Commit 1d99534beffea9b8e54a2cf31776cff68c4fc676
Committed by
Joe Hershberger
1 parent
120b5ef287
Exists in
smarc_8mq_lf_v2020.04
and in
11 other branches
drivers: net: add NXP ENETC MDIO driver
Adds a driver for the MDIO interface currently integrated in LS1028A SoC. This MDIO interface is shared by multiple ethernet interfaces and is presented as a stand-alone PCI function on the SoC ECAM. Ethernet has a functional dependency on MDIO, for simplicity there is a single config option for both. Signed-off-by: Alex Marginean <alexm.osslist@gmail.com> Reviewed-by: Bin Meng <bmeng.cn@gmail.com> Acked-by: Joe Hershberger <joe.hershberger@ni.com>
Showing 5 changed files with 229 additions and 2 deletions Side-by-side Diff
drivers/net/Kconfig
drivers/net/Makefile
drivers/net/fsl_enetc.c
... | ... | @@ -10,6 +10,7 @@ |
10 | 10 | #include <memalign.h> |
11 | 11 | #include <asm/io.h> |
12 | 12 | #include <pci.h> |
13 | +#include <miiphy.h> | |
13 | 14 | |
14 | 15 | #include "fsl_enetc.h" |
15 | 16 | |
... | ... | @@ -38,6 +39,59 @@ |
38 | 39 | return 0; |
39 | 40 | } |
40 | 41 | |
42 | +/* Configure the actual/external ethernet PHY, if one is found */ | |
43 | +static void enetc_start_phy(struct udevice *dev) | |
44 | +{ | |
45 | + struct enetc_priv *priv = dev_get_priv(dev); | |
46 | + struct udevice *miidev; | |
47 | + struct phy_device *phy; | |
48 | + u32 phandle, phy_id; | |
49 | + ofnode phy_node; | |
50 | + int supported; | |
51 | + | |
52 | + if (!ofnode_valid(dev->node)) { | |
53 | + enetc_dbg(dev, "no enetc ofnode found, skipping PHY set-up\n"); | |
54 | + return; | |
55 | + } | |
56 | + | |
57 | + if (ofnode_read_u32(dev->node, "phy-handle", &phandle)) { | |
58 | + enetc_dbg(dev, "phy-handle not found, skipping PHY set-up\n"); | |
59 | + return; | |
60 | + } | |
61 | + | |
62 | + phy_node = ofnode_get_by_phandle(phandle); | |
63 | + if (!ofnode_valid(phy_node)) { | |
64 | + enetc_dbg(dev, "invalid phy node, skipping PHY set-up\n"); | |
65 | + return; | |
66 | + } | |
67 | + enetc_dbg(dev, "phy node: %s\n", ofnode_get_name(phy_node)); | |
68 | + | |
69 | + if (ofnode_read_u32(phy_node, "reg", &phy_id)) { | |
70 | + enetc_dbg(dev, | |
71 | + "missing reg in PHY node, skipping PHY set-up\n"); | |
72 | + return; | |
73 | + } | |
74 | + | |
75 | + if (uclass_get_device_by_ofnode(UCLASS_MDIO, | |
76 | + ofnode_get_parent(phy_node), | |
77 | + &miidev)) { | |
78 | + enetc_dbg(dev, "can't find MDIO bus for node %s\n", | |
79 | + ofnode_get_name(ofnode_get_parent(phy_node))); | |
80 | + return; | |
81 | + } | |
82 | + | |
83 | + phy = dm_mdio_phy_connect(miidev, phy_id, dev, priv->if_type); | |
84 | + if (!phy) { | |
85 | + enetc_dbg(dev, "dm_mdio_phy_connect returned null\n"); | |
86 | + return; | |
87 | + } | |
88 | + | |
89 | + supported = GENMASK(6, 0); /* speeds up to 1G & AN */ | |
90 | + phy->advertising = phy->supported & supported; | |
91 | + phy_config(phy); | |
92 | + phy_startup(phy); | |
93 | +} | |
94 | + | |
41 | 95 | /* |
42 | 96 | * Probe ENETC driver: |
43 | 97 | * - initialize port and station interface BARs |
... | ... | @@ -248,6 +302,9 @@ |
248 | 302 | /* setup Tx/Rx buffer descriptors */ |
249 | 303 | enetc_setup_tx_bdr(dev); |
250 | 304 | enetc_setup_rx_bdr(dev); |
305 | + | |
306 | + priv->if_type = PHY_INTERFACE_MODE_NONE; | |
307 | + enetc_start_phy(dev); | |
251 | 308 | |
252 | 309 | return 0; |
253 | 310 | } |
drivers/net/fsl_enetc.h
... | ... | @@ -11,6 +11,7 @@ |
11 | 11 | |
12 | 12 | /* PCI function IDs */ |
13 | 13 | #define PCI_DEVICE_ID_ENETC_ETH 0xE100 |
14 | +#define PCI_DEVICE_ID_ENETC_MDIO 0xEE01 | |
14 | 15 | |
15 | 16 | /* ENETC Ethernet controller registers */ |
16 | 17 | /* Station interface register offsets */ |
... | ... | @@ -143,6 +144,8 @@ |
143 | 144 | /* Rx/Tx buffer descriptor rings info */ |
144 | 145 | struct bd_ring tx_bdr; |
145 | 146 | struct bd_ring rx_bdr; |
147 | + | |
148 | + int if_type; | |
146 | 149 | }; |
147 | 150 | |
148 | 151 | /* register accessors */ |
... | ... | @@ -164,6 +167,24 @@ |
164 | 167 | enetc_read(priv, ENETC_BDR(t, n, off)) |
165 | 168 | #define enetc_bdr_write(priv, t, n, off, val) \ |
166 | 169 | enetc_write(priv, ENETC_BDR(t, n, off), val) |
170 | + | |
171 | +/* ENETC external MDIO registers */ | |
172 | +#define ENETC_MDIO_BASE 0x1c00 | |
173 | +#define ENETC_MDIO_CFG 0x00 | |
174 | +#define ENETC_EMDIO_CFG_C22 0x00809508 | |
175 | +#define ENETC_EMDIO_CFG_C45 0x00809548 | |
176 | +#define ENETC_EMDIO_CFG_RD_ER BIT(1) | |
177 | +#define ENETC_EMDIO_CFG_BSY BIT(0) | |
178 | +#define ENETC_MDIO_CTL 0x04 | |
179 | +#define ENETC_MDIO_CTL_READ BIT(15) | |
180 | +#define ENETC_MDIO_DATA 0x08 | |
181 | +#define ENETC_MDIO_STAT 0x0c | |
182 | + | |
183 | +#define ENETC_MDIO_READ_ERR 0xffff | |
184 | + | |
185 | +struct enetc_mdio_priv { | |
186 | + void *regs_base; | |
187 | +}; | |
167 | 188 | |
168 | 189 | #endif /* _ENETC_H */ |
drivers/net/fsl_enetc_mdio.c
1 | +// SPDX-License-Identifier: GPL-2.0+ | |
2 | +/* | |
3 | + * ENETC ethernet controller driver | |
4 | + * Copyright 2019 NXP | |
5 | + */ | |
6 | + | |
7 | +#include <common.h> | |
8 | +#include <dm.h> | |
9 | +#include <errno.h> | |
10 | +#include <pci.h> | |
11 | +#include <miiphy.h> | |
12 | +#include <asm/io.h> | |
13 | +#include <asm/processor.h> | |
14 | +#include <miiphy.h> | |
15 | + | |
16 | +#include "fsl_enetc.h" | |
17 | + | |
18 | +static void enetc_mdio_wait_bsy(struct enetc_mdio_priv *priv) | |
19 | +{ | |
20 | + while (enetc_read(priv, ENETC_MDIO_CFG) & ENETC_EMDIO_CFG_BSY) | |
21 | + cpu_relax(); | |
22 | +} | |
23 | + | |
24 | +static int enetc_mdio_read_priv(struct enetc_mdio_priv *priv, int addr, | |
25 | + int devad, int reg) | |
26 | +{ | |
27 | + if (devad == MDIO_DEVAD_NONE) | |
28 | + enetc_write(priv, ENETC_MDIO_CFG, ENETC_EMDIO_CFG_C22); | |
29 | + else | |
30 | + enetc_write(priv, ENETC_MDIO_CFG, ENETC_EMDIO_CFG_C45); | |
31 | + enetc_mdio_wait_bsy(priv); | |
32 | + | |
33 | + if (devad == MDIO_DEVAD_NONE) { | |
34 | + enetc_write(priv, ENETC_MDIO_CTL, ENETC_MDIO_CTL_READ | | |
35 | + (addr << 5) | reg); | |
36 | + } else { | |
37 | + enetc_write(priv, ENETC_MDIO_CTL, (addr << 5) + devad); | |
38 | + enetc_mdio_wait_bsy(priv); | |
39 | + | |
40 | + enetc_write(priv, ENETC_MDIO_STAT, reg); | |
41 | + enetc_mdio_wait_bsy(priv); | |
42 | + | |
43 | + enetc_write(priv, ENETC_MDIO_CTL, ENETC_MDIO_CTL_READ | | |
44 | + (addr << 5) | devad); | |
45 | + } | |
46 | + | |
47 | + enetc_mdio_wait_bsy(priv); | |
48 | + if (enetc_read(priv, ENETC_MDIO_CFG) & ENETC_EMDIO_CFG_RD_ER) | |
49 | + return ENETC_MDIO_READ_ERR; | |
50 | + | |
51 | + return enetc_read(priv, ENETC_MDIO_DATA); | |
52 | +} | |
53 | + | |
54 | +static int enetc_mdio_write_priv(struct enetc_mdio_priv *priv, int addr, | |
55 | + int devad, int reg, u16 val) | |
56 | +{ | |
57 | + if (devad == MDIO_DEVAD_NONE) | |
58 | + enetc_write(priv, ENETC_MDIO_CFG, ENETC_EMDIO_CFG_C22); | |
59 | + else | |
60 | + enetc_write(priv, ENETC_MDIO_CFG, ENETC_EMDIO_CFG_C45); | |
61 | + enetc_mdio_wait_bsy(priv); | |
62 | + | |
63 | + if (devad != MDIO_DEVAD_NONE) { | |
64 | + enetc_write(priv, ENETC_MDIO_CTL, (addr << 5) + devad); | |
65 | + enetc_write(priv, ENETC_MDIO_STAT, reg); | |
66 | + } else { | |
67 | + enetc_write(priv, ENETC_MDIO_CTL, (addr << 5) + reg); | |
68 | + } | |
69 | + enetc_mdio_wait_bsy(priv); | |
70 | + | |
71 | + enetc_write(priv, ENETC_MDIO_DATA, val); | |
72 | + enetc_mdio_wait_bsy(priv); | |
73 | + | |
74 | + return 0; | |
75 | +} | |
76 | + | |
77 | +/* DM wrappers */ | |
78 | +static int dm_enetc_mdio_read(struct udevice *dev, int addr, int devad, int reg) | |
79 | +{ | |
80 | + struct enetc_mdio_priv *priv = dev_get_priv(dev); | |
81 | + | |
82 | + return enetc_mdio_read_priv(priv, addr, devad, reg); | |
83 | +} | |
84 | + | |
85 | +static int dm_enetc_mdio_write(struct udevice *dev, int addr, int devad, | |
86 | + int reg, u16 val) | |
87 | +{ | |
88 | + struct enetc_mdio_priv *priv = dev_get_priv(dev); | |
89 | + | |
90 | + return enetc_mdio_write_priv(priv, addr, devad, reg, val); | |
91 | +} | |
92 | + | |
93 | +static const struct mdio_ops enetc_mdio_ops = { | |
94 | + .read = dm_enetc_mdio_read, | |
95 | + .write = dm_enetc_mdio_write, | |
96 | +}; | |
97 | + | |
98 | +static int enetc_mdio_bind(struct udevice *dev) | |
99 | +{ | |
100 | + char name[16]; | |
101 | + static int eth_num_devices; | |
102 | + | |
103 | + /* | |
104 | + * prefer using PCI function numbers to number interfaces, but these | |
105 | + * are only available if dts nodes are present. For PCI they are | |
106 | + * optional, handle that case too. Just in case some nodes are present | |
107 | + * and some are not, use different naming scheme - enetc-N based on | |
108 | + * PCI function # and enetc#N based on interface count | |
109 | + */ | |
110 | + if (ofnode_valid(dev->node)) | |
111 | + sprintf(name, "emdio-%u", PCI_FUNC(pci_get_devfn(dev))); | |
112 | + else | |
113 | + sprintf(name, "emdio#%u", eth_num_devices++); | |
114 | + device_set_name(dev, name); | |
115 | + | |
116 | + return 0; | |
117 | +} | |
118 | + | |
119 | +static int enetc_mdio_probe(struct udevice *dev) | |
120 | +{ | |
121 | + struct enetc_mdio_priv *priv = dev_get_priv(dev); | |
122 | + | |
123 | + priv->regs_base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0, 0); | |
124 | + if (!priv->regs_base) { | |
125 | + enetc_dbg(dev, "failed to map BAR0\n"); | |
126 | + return -EINVAL; | |
127 | + } | |
128 | + | |
129 | + priv->regs_base += ENETC_MDIO_BASE; | |
130 | + | |
131 | + dm_pci_clrset_config16(dev, PCI_COMMAND, 0, PCI_COMMAND_MEMORY); | |
132 | + | |
133 | + return 0; | |
134 | +} | |
135 | + | |
136 | +U_BOOT_DRIVER(enetc_mdio) = { | |
137 | + .name = "enetc_mdio", | |
138 | + .id = UCLASS_MDIO, | |
139 | + .bind = enetc_mdio_bind, | |
140 | + .probe = enetc_mdio_probe, | |
141 | + .ops = &enetc_mdio_ops, | |
142 | + .priv_auto_alloc_size = sizeof(struct enetc_mdio_priv), | |
143 | +}; | |
144 | + | |
145 | +static struct pci_device_id enetc_mdio_ids[] = { | |
146 | + { PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, PCI_DEVICE_ID_ENETC_MDIO) }, | |
147 | +}; | |
148 | + | |
149 | +U_BOOT_PCI_DEVICE(enetc_mdio, enetc_mdio_ids); |