xref: /openbmc/linux/drivers/phy/marvell/phy-armada38x-comphy.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
114dc100bSRussell King // SPDX-License-Identifier: GPL-2.0
214dc100bSRussell King /*
314dc100bSRussell King  * Copyright (C) 2018 Russell King, Deep Blue Solutions Ltd.
414dc100bSRussell King  *
514dc100bSRussell King  * Partly derived from CP110 comphy driver by Antoine Tenart
614dc100bSRussell King  * <antoine.tenart@bootlin.com>
714dc100bSRussell King  */
814dc100bSRussell King #include <linux/delay.h>
914dc100bSRussell King #include <linux/iopoll.h>
1014dc100bSRussell King #include <linux/module.h>
11*7559e757SRob Herring #include <linux/of.h>
1214dc100bSRussell King #include <linux/phy/phy.h>
1314dc100bSRussell King #include <linux/phy.h>
1414dc100bSRussell King #include <linux/platform_device.h>
1514dc100bSRussell King 
1614dc100bSRussell King #define MAX_A38X_COMPHY	6
1714dc100bSRussell King #define MAX_A38X_PORTS	3
1814dc100bSRussell King 
1914dc100bSRussell King #define COMPHY_CFG1		0x00
2014dc100bSRussell King #define  COMPHY_CFG1_GEN_TX(x)		((x) << 26)
2114dc100bSRussell King #define  COMPHY_CFG1_GEN_TX_MSK		COMPHY_CFG1_GEN_TX(15)
2214dc100bSRussell King #define  COMPHY_CFG1_GEN_RX(x)		((x) << 22)
2314dc100bSRussell King #define  COMPHY_CFG1_GEN_RX_MSK		COMPHY_CFG1_GEN_RX(15)
2414dc100bSRussell King #define  GEN_SGMII_1_25GBPS		6
2514dc100bSRussell King #define  GEN_SGMII_3_125GBPS		8
2614dc100bSRussell King 
2714dc100bSRussell King #define COMPHY_STAT1		0x18
2814dc100bSRussell King #define  COMPHY_STAT1_PLL_RDY_TX	BIT(3)
2914dc100bSRussell King #define  COMPHY_STAT1_PLL_RDY_RX	BIT(2)
3014dc100bSRussell King 
3114dc100bSRussell King #define COMPHY_SELECTOR		0xfc
3214dc100bSRussell King 
3314dc100bSRussell King struct a38x_comphy;
3414dc100bSRussell King 
3514dc100bSRussell King struct a38x_comphy_lane {
3614dc100bSRussell King 	void __iomem *base;
3714dc100bSRussell King 	struct a38x_comphy *priv;
3814dc100bSRussell King 	unsigned int n;
3914dc100bSRussell King 
4014dc100bSRussell King 	int port;
4114dc100bSRussell King };
4214dc100bSRussell King 
4314dc100bSRussell King struct a38x_comphy {
4414dc100bSRussell King 	void __iomem *base;
451dea06cdSRussell King 	void __iomem *conf;
4614dc100bSRussell King 	struct device *dev;
4714dc100bSRussell King 	struct a38x_comphy_lane lane[MAX_A38X_COMPHY];
4814dc100bSRussell King };
4914dc100bSRussell King 
5014dc100bSRussell King static const u8 gbe_mux[MAX_A38X_COMPHY][MAX_A38X_PORTS] = {
5114dc100bSRussell King 	{ 0, 0, 0 },
5214dc100bSRussell King 	{ 4, 5, 0 },
5314dc100bSRussell King 	{ 0, 4, 0 },
5414dc100bSRussell King 	{ 0, 0, 4 },
5514dc100bSRussell King 	{ 0, 3, 0 },
5614dc100bSRussell King 	{ 0, 0, 3 },
5714dc100bSRussell King };
5814dc100bSRussell King 
a38x_set_conf(struct a38x_comphy_lane * lane,bool enable)591dea06cdSRussell King static void a38x_set_conf(struct a38x_comphy_lane *lane, bool enable)
601dea06cdSRussell King {
611dea06cdSRussell King 	struct a38x_comphy *priv = lane->priv;
621dea06cdSRussell King 	u32 conf;
631dea06cdSRussell King 
641dea06cdSRussell King 	if (priv->conf) {
651dea06cdSRussell King 		conf = readl_relaxed(priv->conf);
661dea06cdSRussell King 		if (enable)
671dea06cdSRussell King 			conf |= BIT(lane->port);
681dea06cdSRussell King 		else
691dea06cdSRussell King 			conf &= ~BIT(lane->port);
701dea06cdSRussell King 		writel(conf, priv->conf);
711dea06cdSRussell King 	}
721dea06cdSRussell King }
731dea06cdSRussell King 
a38x_comphy_set_reg(struct a38x_comphy_lane * lane,unsigned int offset,u32 mask,u32 value)7414dc100bSRussell King static void a38x_comphy_set_reg(struct a38x_comphy_lane *lane,
7514dc100bSRussell King 				unsigned int offset, u32 mask, u32 value)
7614dc100bSRussell King {
7714dc100bSRussell King 	u32 val;
7814dc100bSRussell King 
7914dc100bSRussell King 	val = readl_relaxed(lane->base + offset) & ~mask;
8014dc100bSRussell King 	writel(val | value, lane->base + offset);
8114dc100bSRussell King }
8214dc100bSRussell King 
a38x_comphy_set_speed(struct a38x_comphy_lane * lane,unsigned int gen_tx,unsigned int gen_rx)8314dc100bSRussell King static void a38x_comphy_set_speed(struct a38x_comphy_lane *lane,
8414dc100bSRussell King 				  unsigned int gen_tx, unsigned int gen_rx)
8514dc100bSRussell King {
8614dc100bSRussell King 	a38x_comphy_set_reg(lane, COMPHY_CFG1,
8714dc100bSRussell King 			    COMPHY_CFG1_GEN_TX_MSK | COMPHY_CFG1_GEN_RX_MSK,
8814dc100bSRussell King 			    COMPHY_CFG1_GEN_TX(gen_tx) |
8914dc100bSRussell King 		            COMPHY_CFG1_GEN_RX(gen_rx));
9014dc100bSRussell King }
9114dc100bSRussell King 
a38x_comphy_poll(struct a38x_comphy_lane * lane,unsigned int offset,u32 mask,u32 value)9214dc100bSRussell King static int a38x_comphy_poll(struct a38x_comphy_lane *lane,
9314dc100bSRussell King 			    unsigned int offset, u32 mask, u32 value)
9414dc100bSRussell King {
9514dc100bSRussell King 	u32 val;
9614dc100bSRussell King 	int ret;
9714dc100bSRussell King 
9814dc100bSRussell King 	ret = readl_relaxed_poll_timeout_atomic(lane->base + offset, val,
9914dc100bSRussell King 						(val & mask) == value,
10014dc100bSRussell King 						1000, 150000);
10114dc100bSRussell King 
10214dc100bSRussell King 	if (ret)
10314dc100bSRussell King 		dev_err(lane->priv->dev,
10414dc100bSRussell King 			"comphy%u: timed out waiting for status\n", lane->n);
10514dc100bSRussell King 
10614dc100bSRussell King 	return ret;
10714dc100bSRussell King }
10814dc100bSRussell King 
10914dc100bSRussell King /*
11014dc100bSRussell King  * We only support changing the speed for comphys configured for GBE.
11114dc100bSRussell King  * Since that is all we do, we only poll for PLL ready status.
11214dc100bSRussell King  */
a38x_comphy_set_mode(struct phy * phy,enum phy_mode mode,int sub)11314dc100bSRussell King static int a38x_comphy_set_mode(struct phy *phy, enum phy_mode mode, int sub)
11414dc100bSRussell King {
11514dc100bSRussell King 	struct a38x_comphy_lane *lane = phy_get_drvdata(phy);
11614dc100bSRussell King 	unsigned int gen;
1171dea06cdSRussell King 	int ret;
11814dc100bSRussell King 
11914dc100bSRussell King 	if (mode != PHY_MODE_ETHERNET)
12014dc100bSRussell King 		return -EINVAL;
12114dc100bSRussell King 
12214dc100bSRussell King 	switch (sub) {
12314dc100bSRussell King 	case PHY_INTERFACE_MODE_SGMII:
12414dc100bSRussell King 	case PHY_INTERFACE_MODE_1000BASEX:
12514dc100bSRussell King 		gen = GEN_SGMII_1_25GBPS;
12614dc100bSRussell King 		break;
12714dc100bSRussell King 
12814dc100bSRussell King 	case PHY_INTERFACE_MODE_2500BASEX:
12914dc100bSRussell King 		gen = GEN_SGMII_3_125GBPS;
13014dc100bSRussell King 		break;
13114dc100bSRussell King 
13214dc100bSRussell King 	default:
13314dc100bSRussell King 		return -EINVAL;
13414dc100bSRussell King 	}
13514dc100bSRussell King 
1361dea06cdSRussell King 	a38x_set_conf(lane, false);
1371dea06cdSRussell King 
13814dc100bSRussell King 	a38x_comphy_set_speed(lane, gen, gen);
13914dc100bSRussell King 
1401dea06cdSRussell King 	ret = a38x_comphy_poll(lane, COMPHY_STAT1,
14114dc100bSRussell King 			       COMPHY_STAT1_PLL_RDY_TX |
14214dc100bSRussell King 			       COMPHY_STAT1_PLL_RDY_RX,
14314dc100bSRussell King 			       COMPHY_STAT1_PLL_RDY_TX |
14414dc100bSRussell King 			       COMPHY_STAT1_PLL_RDY_RX);
1451dea06cdSRussell King 
1461dea06cdSRussell King 	if (ret == 0)
1471dea06cdSRussell King 		a38x_set_conf(lane, true);
1481dea06cdSRussell King 
1491dea06cdSRussell King 	return ret;
15014dc100bSRussell King }
15114dc100bSRussell King 
15214dc100bSRussell King static const struct phy_ops a38x_comphy_ops = {
15314dc100bSRussell King 	.set_mode	= a38x_comphy_set_mode,
15414dc100bSRussell King 	.owner		= THIS_MODULE,
15514dc100bSRussell King };
15614dc100bSRussell King 
a38x_comphy_xlate(struct device * dev,struct of_phandle_args * args)15714dc100bSRussell King static struct phy *a38x_comphy_xlate(struct device *dev,
15814dc100bSRussell King 				     struct of_phandle_args *args)
15914dc100bSRussell King {
16014dc100bSRussell King 	struct a38x_comphy_lane *lane;
16114dc100bSRussell King 	struct phy *phy;
16214dc100bSRussell King 	u32 val;
16314dc100bSRussell King 
16414dc100bSRussell King 	if (WARN_ON(args->args[0] >= MAX_A38X_PORTS))
16514dc100bSRussell King 		return ERR_PTR(-EINVAL);
16614dc100bSRussell King 
16714dc100bSRussell King 	phy = of_phy_simple_xlate(dev, args);
16814dc100bSRussell King 	if (IS_ERR(phy))
16914dc100bSRussell King 		return phy;
17014dc100bSRussell King 
17114dc100bSRussell King 	lane = phy_get_drvdata(phy);
17214dc100bSRussell King 	if (lane->port >= 0)
17314dc100bSRussell King 		return ERR_PTR(-EBUSY);
17414dc100bSRussell King 
17514dc100bSRussell King 	lane->port = args->args[0];
17614dc100bSRussell King 
17714dc100bSRussell King 	val = readl_relaxed(lane->priv->base + COMPHY_SELECTOR);
17814dc100bSRussell King 	val = (val >> (4 * lane->n)) & 0xf;
17914dc100bSRussell King 
18014dc100bSRussell King 	if (!gbe_mux[lane->n][lane->port] ||
18114dc100bSRussell King 	    val != gbe_mux[lane->n][lane->port]) {
18214dc100bSRussell King 		dev_warn(lane->priv->dev,
18314dc100bSRussell King 			 "comphy%u: not configured for GBE\n", lane->n);
18414dc100bSRussell King 		phy = ERR_PTR(-EINVAL);
18514dc100bSRussell King 	}
18614dc100bSRussell King 
18714dc100bSRussell King 	return phy;
18814dc100bSRussell King }
18914dc100bSRussell King 
a38x_comphy_probe(struct platform_device * pdev)19014dc100bSRussell King static int a38x_comphy_probe(struct platform_device *pdev)
19114dc100bSRussell King {
19214dc100bSRussell King 	struct phy_provider *provider;
19314dc100bSRussell King 	struct device_node *child;
19414dc100bSRussell King 	struct a38x_comphy *priv;
19514dc100bSRussell King 	struct resource *res;
19614dc100bSRussell King 	void __iomem *base;
19714dc100bSRussell King 
19814dc100bSRussell King 	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
19914dc100bSRussell King 	if (!priv)
20014dc100bSRussell King 		return -ENOMEM;
20114dc100bSRussell King 
2021dea06cdSRussell King 	base = devm_platform_ioremap_resource(pdev, 0);
20314dc100bSRussell King 	if (IS_ERR(base))
20414dc100bSRussell King 		return PTR_ERR(base);
20514dc100bSRussell King 
20614dc100bSRussell King 	priv->dev = &pdev->dev;
20714dc100bSRussell King 	priv->base = base;
20814dc100bSRussell King 
2091dea06cdSRussell King 	/* Optional */
2101dea06cdSRussell King 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "conf");
2111dea06cdSRussell King 	if (res) {
2121dea06cdSRussell King 		priv->conf = devm_ioremap_resource(&pdev->dev, res);
2131dea06cdSRussell King 		if (IS_ERR(priv->conf))
2141dea06cdSRussell King 			return PTR_ERR(priv->conf);
2151dea06cdSRussell King 	}
2161dea06cdSRussell King 
21714dc100bSRussell King 	for_each_available_child_of_node(pdev->dev.of_node, child) {
21814dc100bSRussell King 		struct phy *phy;
21914dc100bSRussell King 		int ret;
22014dc100bSRussell King 		u32 val;
22114dc100bSRussell King 
22214dc100bSRussell King 		ret = of_property_read_u32(child, "reg", &val);
22314dc100bSRussell King 		if (ret < 0) {
22414dc100bSRussell King 			dev_err(&pdev->dev, "missing 'reg' property (%d)\n",
22514dc100bSRussell King 				ret);
22614dc100bSRussell King 			continue;
22714dc100bSRussell King 		}
22814dc100bSRussell King 
22914dc100bSRussell King 		if (val >= MAX_A38X_COMPHY || priv->lane[val].base) {
23014dc100bSRussell King 			dev_err(&pdev->dev, "invalid 'reg' property\n");
23114dc100bSRussell King 			continue;
23214dc100bSRussell King 		}
23314dc100bSRussell King 
23414dc100bSRussell King 		phy = devm_phy_create(&pdev->dev, child, &a38x_comphy_ops);
2358d160f6bSNishka Dasgupta 		if (IS_ERR(phy)) {
2368d160f6bSNishka Dasgupta 			of_node_put(child);
23714dc100bSRussell King 			return PTR_ERR(phy);
2388d160f6bSNishka Dasgupta 		}
23914dc100bSRussell King 
24014dc100bSRussell King 		priv->lane[val].base = base + 0x28 * val;
24114dc100bSRussell King 		priv->lane[val].priv = priv;
24214dc100bSRussell King 		priv->lane[val].n = val;
24314dc100bSRussell King 		priv->lane[val].port = -1;
24414dc100bSRussell King 		phy_set_drvdata(phy, &priv->lane[val]);
24514dc100bSRussell King 	}
24614dc100bSRussell King 
24714dc100bSRussell King 	dev_set_drvdata(&pdev->dev, priv);
24814dc100bSRussell King 
24914dc100bSRussell King 	provider = devm_of_phy_provider_register(&pdev->dev, a38x_comphy_xlate);
25014dc100bSRussell King 
25114dc100bSRussell King 	return PTR_ERR_OR_ZERO(provider);
25214dc100bSRussell King }
25314dc100bSRussell King 
25414dc100bSRussell King static const struct of_device_id a38x_comphy_of_match_table[] = {
25514dc100bSRussell King 	{ .compatible = "marvell,armada-380-comphy" },
25614dc100bSRussell King 	{ },
25714dc100bSRussell King };
25814dc100bSRussell King MODULE_DEVICE_TABLE(of, a38x_comphy_of_match_table);
25914dc100bSRussell King 
26014dc100bSRussell King static struct platform_driver a38x_comphy_driver = {
26114dc100bSRussell King 	.probe	= a38x_comphy_probe,
26214dc100bSRussell King 	.driver	= {
26314dc100bSRussell King 		.name = "armada-38x-comphy",
26414dc100bSRussell King 		.of_match_table = a38x_comphy_of_match_table,
26514dc100bSRussell King 	},
26614dc100bSRussell King };
26714dc100bSRussell King module_platform_driver(a38x_comphy_driver);
26814dc100bSRussell King 
26914dc100bSRussell King MODULE_AUTHOR("Russell King <rmk+kernel@armlinux.org.uk>");
27014dc100bSRussell King MODULE_DESCRIPTION("Common PHY driver for Armada 38x SoCs");
27114dc100bSRussell King MODULE_LICENSE("GPL v2");
272