1 // SPDX-License-Identifier: GPL-2.0-only
2 // Copyright (C) 2015 Broadcom Corporation
4 #include <linux/delay.h>
6 #include <linux/module.h>
8 #include <linux/phy/phy.h>
9 #include <linux/platform_device.h>
11 #define PCIE_CFG_OFFSET 0x00
12 #define PCIE1_PHY_IDDQ_SHIFT 10
13 #define PCIE0_PHY_IDDQ_SHIFT 2
15 enum cygnus_pcie_phy_id
{
21 struct cygnus_pcie_phy_core
;
24 * struct cygnus_pcie_phy - Cygnus PCIe PHY device
25 * @core: pointer to the Cygnus PCIe PHY core control
26 * @id: internal ID to identify the Cygnus PCIe PHY
27 * @phy: pointer to the kernel PHY device
29 struct cygnus_pcie_phy
{
30 struct cygnus_pcie_phy_core
*core
;
31 enum cygnus_pcie_phy_id id
;
36 * struct cygnus_pcie_phy_core - Cygnus PCIe PHY core control
37 * @dev: pointer to device
38 * @base: base register
39 * @lock: mutex to protect access to individual PHYs
40 * @phys: pointer to Cygnus PHY device
42 struct cygnus_pcie_phy_core
{
46 struct cygnus_pcie_phy phys
[MAX_NUM_PHYS
];
49 static int cygnus_pcie_power_config(struct cygnus_pcie_phy
*phy
, bool enable
)
51 struct cygnus_pcie_phy_core
*core
= phy
->core
;
55 mutex_lock(&core
->lock
);
58 case CYGNUS_PHY_PCIE0
:
59 shift
= PCIE0_PHY_IDDQ_SHIFT
;
62 case CYGNUS_PHY_PCIE1
:
63 shift
= PCIE1_PHY_IDDQ_SHIFT
;
67 mutex_unlock(&core
->lock
);
68 dev_err(core
->dev
, "PCIe PHY %d invalid\n", phy
->id
);
73 val
= readl(core
->base
+ PCIE_CFG_OFFSET
);
75 writel(val
, core
->base
+ PCIE_CFG_OFFSET
);
77 * Wait 50 ms for the PCIe Serdes to stabilize after the analog
78 * front end is brought up
82 val
= readl(core
->base
+ PCIE_CFG_OFFSET
);
84 writel(val
, core
->base
+ PCIE_CFG_OFFSET
);
87 mutex_unlock(&core
->lock
);
88 dev_dbg(core
->dev
, "PCIe PHY %d %s\n", phy
->id
,
89 enable
? "enabled" : "disabled");
93 static int cygnus_pcie_phy_power_on(struct phy
*p
)
95 struct cygnus_pcie_phy
*phy
= phy_get_drvdata(p
);
97 return cygnus_pcie_power_config(phy
, true);
100 static int cygnus_pcie_phy_power_off(struct phy
*p
)
102 struct cygnus_pcie_phy
*phy
= phy_get_drvdata(p
);
104 return cygnus_pcie_power_config(phy
, false);
107 static const struct phy_ops cygnus_pcie_phy_ops
= {
108 .power_on
= cygnus_pcie_phy_power_on
,
109 .power_off
= cygnus_pcie_phy_power_off
,
110 .owner
= THIS_MODULE
,
113 static int cygnus_pcie_phy_probe(struct platform_device
*pdev
)
115 struct device
*dev
= &pdev
->dev
;
116 struct device_node
*node
= dev
->of_node
;
117 struct cygnus_pcie_phy_core
*core
;
118 struct phy_provider
*provider
;
121 if (of_get_child_count(node
) == 0) {
122 dev_err(dev
, "PHY no child node\n");
126 core
= devm_kzalloc(dev
, sizeof(*core
), GFP_KERNEL
);
132 core
->base
= devm_platform_ioremap_resource(pdev
, 0);
133 if (IS_ERR(core
->base
))
134 return PTR_ERR(core
->base
);
136 mutex_init(&core
->lock
);
138 for_each_available_child_of_node_scoped(node
, child
) {
140 struct cygnus_pcie_phy
*p
;
142 if (of_property_read_u32(child
, "reg", &id
)) {
143 dev_err(dev
, "missing reg property for %pOFn\n",
148 if (id
>= MAX_NUM_PHYS
) {
149 dev_err(dev
, "invalid PHY id: %u\n", id
);
153 if (core
->phys
[id
].phy
) {
154 dev_err(dev
, "duplicated PHY id: %u\n", id
);
159 p
->phy
= devm_phy_create(dev
, child
, &cygnus_pcie_phy_ops
);
160 if (IS_ERR(p
->phy
)) {
161 dev_err(dev
, "failed to create PHY\n");
162 return PTR_ERR(p
->phy
);
167 phy_set_drvdata(p
->phy
, p
);
171 dev_set_drvdata(dev
, core
);
173 provider
= devm_of_phy_provider_register(dev
, of_phy_simple_xlate
);
174 if (IS_ERR(provider
)) {
175 dev_err(dev
, "failed to register PHY provider\n");
176 return PTR_ERR(provider
);
179 dev_dbg(dev
, "registered %u PCIe PHY(s)\n", cnt
);
184 static const struct of_device_id cygnus_pcie_phy_match_table
[] = {
185 { .compatible
= "brcm,cygnus-pcie-phy" },
188 MODULE_DEVICE_TABLE(of
, cygnus_pcie_phy_match_table
);
190 static struct platform_driver cygnus_pcie_phy_driver
= {
192 .name
= "cygnus-pcie-phy",
193 .of_match_table
= cygnus_pcie_phy_match_table
,
195 .probe
= cygnus_pcie_phy_probe
,
197 module_platform_driver(cygnus_pcie_phy_driver
);
199 MODULE_AUTHOR("Ray Jui <rjui@broadcom.com>");
200 MODULE_DESCRIPTION("Broadcom Cygnus PCIe PHY driver");
201 MODULE_LICENSE("GPL v2");