blob: 8c0168be859e03f138b8010a64844e1f7ed71112 [file] [log] [blame]
Heiko Schocher41b64a82020-02-06 09:48:16 +01001// 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
Heiko Schocher41b64a82020-02-06 09:48:16 +010011#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
20struct qe_uec_mdio_priv {
21 struct ucc_mii_mng *base;
22};
23
24static int
25qe_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
58static int
59qe_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
86static const struct mdio_ops qe_uec_mdio_ops = {
87 .read = qe_uec_mdio_read,
88 .write = qe_uec_mdio_write,
89};
90
91static 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
Johan Jonker8d5d8e02023-03-13 01:32:04 +010099 priv->base = dev_read_addr_ptr(dev);
Heiko Schocher41b64a82020-02-06 09:48:16 +0100100 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
150static const struct udevice_id qe_uec_mdio_ids[] = {
151 { .compatible = "fsl,ucc-mdio" },
152 { }
153};
154
155U_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,
Simon Glass8a2b47f2020-12-03 16:55:17 -0700161 .priv_auto = sizeof(struct qe_uec_mdio_priv),
Heiko Schocher41b64a82020-02-06 09:48:16 +0100162};