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