aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRay Jui <rjui@broadcom.com>2015-09-21 13:17:18 -0400
committerKishon Vijay Abraham I <kishon@ti.com>2015-10-06 10:51:48 -0400
commit882fed73420e85c0b06447c036e3096862fc73ba (patch)
tree783cff0ea83dd4fd857403b80aadc2b09614b3f2
parent9d030e170608aeb287ef56ecb40d9aa4cfa7d086 (diff)
phy: cygnus: pcie: Add Cygnus PCIe PHY support
This patch adds the PCIe PHY support for the Broadcom PCIe RC interface on Cygnus Signed-off-by: Ray Jui <rjui@broadcom.com> Reviewed-by: Arun Parameswaran <aparames@broadcom.com> Reviewed-by: JD (Jiandong) Zheng <jdzheng@broadcom.com> Reviewed-by: Scott Branden <sbranden@broadcom.com> Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
-rw-r--r--drivers/phy/Kconfig9
-rw-r--r--drivers/phy/Makefile1
-rw-r--r--drivers/phy/phy-bcm-cygnus-pcie.c213
3 files changed, 223 insertions, 0 deletions
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index ec436c1e3d7e..7eb5859dd035 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -380,4 +380,13 @@ config PHY_BRCMSTB_SATA
380 Enable this to support the SATA3 PHY on 28nm Broadcom STB SoCs. 380 Enable this to support the SATA3 PHY on 28nm Broadcom STB SoCs.
381 Likely useful only with CONFIG_SATA_BRCMSTB enabled. 381 Likely useful only with CONFIG_SATA_BRCMSTB enabled.
382 382
383config PHY_CYGNUS_PCIE
384 tristate "Broadcom Cygnus PCIe PHY driver"
385 depends on OF && (ARCH_BCM_CYGNUS || COMPILE_TEST)
386 select GENERIC_PHY
387 default ARCH_BCM_CYGNUS
388 help
389 Enable this to support the Broadcom Cygnus PCIe PHY.
390 If unsure, say N.
391
383endmenu 392endmenu
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index a7cc629451ea..075db1a81aa5 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -47,3 +47,4 @@ obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-14nm.o
47obj-$(CONFIG_PHY_TUSB1210) += phy-tusb1210.o 47obj-$(CONFIG_PHY_TUSB1210) += phy-tusb1210.o
48obj-$(CONFIG_PHY_BRCMSTB_SATA) += phy-brcmstb-sata.o 48obj-$(CONFIG_PHY_BRCMSTB_SATA) += phy-brcmstb-sata.o
49obj-$(CONFIG_PHY_PISTACHIO_USB) += phy-pistachio-usb.o 49obj-$(CONFIG_PHY_PISTACHIO_USB) += phy-pistachio-usb.o
50obj-$(CONFIG_PHY_CYGNUS_PCIE) += phy-bcm-cygnus-pcie.o
diff --git a/drivers/phy/phy-bcm-cygnus-pcie.c b/drivers/phy/phy-bcm-cygnus-pcie.c
new file mode 100644
index 000000000000..7ad72b7d2b98
--- /dev/null
+++ b/drivers/phy/phy-bcm-cygnus-pcie.c
@@ -0,0 +1,213 @@
1/*
2 * Copyright (C) 2015 Broadcom Corporation
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation version 2.
7 *
8 * This program is distributed "as is" WITHOUT ANY WARRANTY of any
9 * kind, whether express or implied; without even the implied warranty
10 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13
14#include <linux/delay.h>
15#include <linux/io.h>
16#include <linux/module.h>
17#include <linux/of.h>
18#include <linux/phy/phy.h>
19#include <linux/platform_device.h>
20
21#define PCIE_CFG_OFFSET 0x00
22#define PCIE1_PHY_IDDQ_SHIFT 10
23#define PCIE0_PHY_IDDQ_SHIFT 2
24
25enum cygnus_pcie_phy_id {
26 CYGNUS_PHY_PCIE0 = 0,
27 CYGNUS_PHY_PCIE1,
28 MAX_NUM_PHYS,
29};
30
31struct cygnus_pcie_phy_core;
32
33/**
34 * struct cygnus_pcie_phy - Cygnus PCIe PHY device
35 * @core: pointer to the Cygnus PCIe PHY core control
36 * @id: internal ID to identify the Cygnus PCIe PHY
37 * @phy: pointer to the kernel PHY device
38 */
39struct cygnus_pcie_phy {
40 struct cygnus_pcie_phy_core *core;
41 enum cygnus_pcie_phy_id id;
42 struct phy *phy;
43};
44
45/**
46 * struct cygnus_pcie_phy_core - Cygnus PCIe PHY core control
47 * @dev: pointer to device
48 * @base: base register
49 * @lock: mutex to protect access to individual PHYs
50 * @phys: pointer to Cygnus PHY device
51 */
52struct cygnus_pcie_phy_core {
53 struct device *dev;
54 void __iomem *base;
55 struct mutex lock;
56 struct cygnus_pcie_phy phys[MAX_NUM_PHYS];
57};
58
59static int cygnus_pcie_power_config(struct cygnus_pcie_phy *phy, bool enable)
60{
61 struct cygnus_pcie_phy_core *core = phy->core;
62 unsigned shift;
63 u32 val;
64
65 mutex_lock(&core->lock);
66
67 switch (phy->id) {
68 case CYGNUS_PHY_PCIE0:
69 shift = PCIE0_PHY_IDDQ_SHIFT;
70 break;
71
72 case CYGNUS_PHY_PCIE1:
73 shift = PCIE1_PHY_IDDQ_SHIFT;
74 break;
75
76 default:
77 mutex_unlock(&core->lock);
78 dev_err(core->dev, "PCIe PHY %d invalid\n", phy->id);
79 return -EINVAL;
80 }
81
82 if (enable) {
83 val = readl(core->base + PCIE_CFG_OFFSET);
84 val &= ~BIT(shift);
85 writel(val, core->base + PCIE_CFG_OFFSET);
86 /*
87 * Wait 50 ms for the PCIe Serdes to stabilize after the analog
88 * front end is brought up
89 */
90 msleep(50);
91 } else {
92 val = readl(core->base + PCIE_CFG_OFFSET);
93 val |= BIT(shift);
94 writel(val, core->base + PCIE_CFG_OFFSET);
95 }
96
97 mutex_unlock(&core->lock);
98 dev_dbg(core->dev, "PCIe PHY %d %s\n", phy->id,
99 enable ? "enabled" : "disabled");
100 return 0;
101}
102
103static int cygnus_pcie_phy_power_on(struct phy *p)
104{
105 struct cygnus_pcie_phy *phy = phy_get_drvdata(p);
106
107 return cygnus_pcie_power_config(phy, true);
108}
109
110static int cygnus_pcie_phy_power_off(struct phy *p)
111{
112 struct cygnus_pcie_phy *phy = phy_get_drvdata(p);
113
114 return cygnus_pcie_power_config(phy, false);
115}
116
117static struct phy_ops cygnus_pcie_phy_ops = {
118 .power_on = cygnus_pcie_phy_power_on,
119 .power_off = cygnus_pcie_phy_power_off,
120 .owner = THIS_MODULE,
121};
122
123static int cygnus_pcie_phy_probe(struct platform_device *pdev)
124{
125 struct device *dev = &pdev->dev;
126 struct device_node *node = dev->of_node, *child;
127 struct cygnus_pcie_phy_core *core;
128 struct phy_provider *provider;
129 struct resource *res;
130 unsigned cnt = 0;
131
132 if (of_get_child_count(node) == 0) {
133 dev_err(dev, "PHY no child node\n");
134 return -ENODEV;
135 }
136
137 core = devm_kzalloc(dev, sizeof(*core), GFP_KERNEL);
138 if (!core)
139 return -ENOMEM;
140
141 core->dev = dev;
142
143 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
144 core->base = devm_ioremap_resource(dev, res);
145 if (IS_ERR(core->base))
146 return PTR_ERR(core->base);
147
148 mutex_init(&core->lock);
149
150 for_each_available_child_of_node(node, child) {
151 unsigned int id;
152 struct cygnus_pcie_phy *p;
153
154 if (of_property_read_u32(child, "reg", &id)) {
155 dev_err(dev, "missing reg property for %s\n",
156 child->name);
157 return -EINVAL;
158 }
159
160 if (id >= MAX_NUM_PHYS) {
161 dev_err(dev, "invalid PHY id: %u\n", id);
162 return -EINVAL;
163 }
164
165 if (core->phys[id].phy) {
166 dev_err(dev, "duplicated PHY id: %u\n", id);
167 return -EINVAL;
168 }
169
170 p = &core->phys[id];
171 p->phy = devm_phy_create(dev, child, &cygnus_pcie_phy_ops);
172 if (IS_ERR(p->phy)) {
173 dev_err(dev, "failed to create PHY\n");
174 return PTR_ERR(p->phy);
175 }
176
177 p->core = core;
178 p->id = id;
179 phy_set_drvdata(p->phy, p);
180 cnt++;
181 }
182
183 dev_set_drvdata(dev, core);
184
185 provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
186 if (IS_ERR(provider)) {
187 dev_err(dev, "failed to register PHY provider\n");
188 return PTR_ERR(provider);
189 }
190
191 dev_dbg(dev, "registered %u PCIe PHY(s)\n", cnt);
192
193 return 0;
194}
195
196static const struct of_device_id cygnus_pcie_phy_match_table[] = {
197 { .compatible = "brcm,cygnus-pcie-phy" },
198 { /* sentinel */ }
199};
200MODULE_DEVICE_TABLE(of, cygnus_pcie_phy_match_table);
201
202static struct platform_driver cygnus_pcie_phy_driver = {
203 .driver = {
204 .name = "cygnus-pcie-phy",
205 .of_match_table = cygnus_pcie_phy_match_table,
206 },
207 .probe = cygnus_pcie_phy_probe,
208};
209module_platform_driver(cygnus_pcie_phy_driver);
210
211MODULE_AUTHOR("Ray Jui <rjui@broadcom.com>");
212MODULE_DESCRIPTION("Broadcom Cygnus PCIe PHY driver");
213MODULE_LICENSE("GPL v2");