1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * QE UEC ethernet phy controller driver
4  *
5  * based on phy parts of drivers/qe/uec.c and drivers/qe/uec_phy.c
6  * from NXP
7  *
8  * Copyright (C) 2020 Heiko Schocher <hs@denx.de>
9  */
10 
11 #include <dm.h>
12 #include <errno.h>
13 #include <miiphy.h>
14 #include <phy.h>
15 #include <asm/io.h>
16 #include <linux/ioport.h>
17 
18 #include "dm_qe_uec.h"
19 
20 struct qe_uec_mdio_priv {
21 	struct ucc_mii_mng *base;
22 };
23 
24 static int
qe_uec_mdio_read(struct udevice * dev,int addr,int devad,int reg)25 qe_uec_mdio_read(struct udevice *dev, int addr, int devad, int reg)
26 {
27 	struct qe_uec_mdio_priv *priv = dev_get_priv(dev);
28 	struct ucc_mii_mng *regs = priv->base;
29 	u32 tmp_reg;
30 	u16 value;
31 
32 	debug("%s: regs: %p addr: %x devad: %x reg: %x\n", __func__, regs,
33 	      addr, devad, reg);
34 	/* Setting up the MII management Address Register */
35 	tmp_reg = ((u32)addr << MIIMADD_PHY_ADDRESS_SHIFT) | reg;
36 	out_be32(&regs->miimadd, tmp_reg);
37 
38 	/* clear MII management command cycle */
39 	out_be32(&regs->miimcom, 0);
40 	sync();
41 
42 	/* Perform an MII management read cycle */
43 	out_be32(&regs->miimcom, MIIMCOM_READ_CYCLE);
44 
45 	/* Wait till MII management write is complete */
46 	while ((in_be32(&regs->miimind)) &
47 	       (MIIMIND_NOT_VALID | MIIMIND_BUSY))
48 		;
49 
50 	/* Read MII management status  */
51 	value = (u16)in_be32(&regs->miimstat);
52 	if (value == 0xffff)
53 		return -EINVAL;
54 
55 	return value;
56 };
57 
58 static int
qe_uec_mdio_write(struct udevice * dev,int addr,int devad,int reg,u16 value)59 qe_uec_mdio_write(struct udevice *dev, int addr, int devad, int reg,
60 		  u16 value)
61 {
62 	struct qe_uec_mdio_priv *priv = dev_get_priv(dev);
63 	struct ucc_mii_mng *regs = priv->base;
64 	u32 tmp_reg;
65 
66 	debug("%s: regs: %p addr: %x devad: %x reg: %x val: %x\n", __func__,
67 	      regs, addr, devad, reg, value);
68 
69 	/* Stop the MII management read cycle */
70 	out_be32(&regs->miimcom, 0);
71 	/* Setting up the MII management Address Register */
72 	tmp_reg = ((u32)addr << MIIMADD_PHY_ADDRESS_SHIFT) | reg;
73 	out_be32(&regs->miimadd, tmp_reg);
74 
75 	/* Setting up the MII management Control Register with the value */
76 	out_be32(&regs->miimcon, (u32)value);
77 	sync();
78 
79 	/* Wait till MII management write is complete */
80 	while ((in_be32(&regs->miimind)) & MIIMIND_BUSY)
81 		;
82 
83 	return 0;
84 };
85 
86 static const struct mdio_ops qe_uec_mdio_ops = {
87 	.read = qe_uec_mdio_read,
88 	.write = qe_uec_mdio_write,
89 };
90 
qe_uec_mdio_probe(struct udevice * dev)91 static int qe_uec_mdio_probe(struct udevice *dev)
92 {
93 	struct qe_uec_mdio_priv *priv = dev_get_priv(dev);
94 	fdt_size_t base;
95 	ofnode node;
96 	u32 num = 0;
97 	int ret = -ENODEV;
98 
99 	priv->base = dev_read_addr_ptr(dev);
100 	base = (fdt_size_t)priv->base;
101 
102 	/*
103 	 * idea from linux:
104 	 * drivers/net/ethernet/freescale/fsl_pq_mdio.c
105 	 *
106 	 * Find the UCC node that controls the given MDIO node
107 	 *
108 	 * For some reason, the QE MDIO nodes are not children of the UCC
109 	 * devices that control them.  Therefore, we need to scan all UCC
110 	 * nodes looking for the one that encompases the given MDIO node.
111 	 * We do this by comparing physical addresses.  The 'start' and
112 	 * 'end' addresses of the MDIO node are passed, and the correct
113 	 * UCC node will cover the entire address range.
114 	 */
115 	node = ofnode_by_compatible(ofnode_null(), "ucc_geth");
116 	while (ofnode_valid(node)) {
117 		fdt_size_t size;
118 		fdt_addr_t addr;
119 
120 		addr = ofnode_get_addr_index(node, 0);
121 		ret = ofnode_get_addr_size_index(node, 0, &size);
122 
123 		if (addr == FDT_ADDR_T_NONE) {
124 			node = ofnode_by_compatible(node, "ucc_geth");
125 			continue;
126 		}
127 
128 		/* check if priv->base in start end */
129 		if (base > addr && base < (addr + size)) {
130 			ret = ofnode_read_u32(node, "cell-index", &num);
131 			if (ret)
132 				ret = ofnode_read_u32(node, "device-id",
133 						      &num);
134 			break;
135 		}
136 		node = ofnode_by_compatible(node, "ucc_geth");
137 	}
138 
139 	if (ret) {
140 		printf("%s: no cell-index nor device-id found!", __func__);
141 		return ret;
142 	}
143 
144 	/* Setup MII master clock source */
145 	qe_set_mii_clk_src(num - 1);
146 
147 	return 0;
148 }
149 
150 static const struct udevice_id qe_uec_mdio_ids[] = {
151 	{ .compatible = "fsl,ucc-mdio" },
152 	{ }
153 };
154 
155 U_BOOT_DRIVER(mvmdio) = {
156 	.name			= "qe_uec_mdio",
157 	.id			= UCLASS_MDIO,
158 	.of_match		= qe_uec_mdio_ids,
159 	.probe			= qe_uec_mdio_probe,
160 	.ops			= &qe_uec_mdio_ops,
161 	.priv_auto	= sizeof(struct qe_uec_mdio_priv),
162 };
163