1 /* $NetBSD: mvphy.c,v 1.8 2008/11/17 03:04:27 dyoung Exp $ */
4 * Copyright (c) 2006 Sam Leffler, Errno Consulting
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * Driver for Marvell 88E6060 10/100 5-port PHY switch.
33 #include <sys/cdefs.h>
34 __KERNEL_RCSID(0, "$NetBSD: mvphy.c,v 1.8 2008/11/17 03:04:27 dyoung Exp $");
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/kernel.h>
39 #include <sys/device.h>
40 #include <sys/socket.h>
41 #include <sys/errno.h>
44 #include <net/if_media.h>
46 #include <dev/mii/mii.h>
47 #include <dev/mii/miivar.h>
48 #include <dev/mii/miidevs.h>
50 #include <dev/mii/mvphyreg.h>
52 #define MV_PORT(sc) ((sc)->mii_phy - 16) /* PHY # to switch port */
53 #define MV_CPU_PORT 5 /* port # of CPU port */
55 #define MV_READ(p, phy, r) \
56 (*(p)->mii_pdata->mii_readreg)(device_parent((p)->mii_dev), \
58 #define MV_WRITE(p, phy, r, v) \
59 (*(p)->mii_pdata->mii_writereg)(device_parent((p)->mii_dev), \
63 #define MV_ATUCTRL_ATU_SIZE_DEFAULT 2 /* 1024 entry database */
64 #define MV_ATUCTRL_AGE_TIME_DEFAULT 19 /* 19 * 16 = 304 seconds */
67 * Register manipulation macros that expect bit field defines
68 * to follow the convention that an _S suffix is appended for
69 * a shift count, while the field mask has no suffix.
71 #define SM(_v, _f) (((_v) << _f##_S) & _f)
72 #define MS(_v, _f) (((_v) & _f) >> _f##_S)
74 static int mvphymatch(device_t
, cfdata_t
, void *);
75 static void mvphyattach(device_t
, device_t
, void *);
77 CFATTACH_DECL_NEW(mvphy
, sizeof(struct mii_softc
),
78 mvphymatch
, mvphyattach
, mii_phy_detach
, mii_phy_activate
);
80 static int mvphy_service(struct mii_softc
*, struct mii_data
*, int);
81 static void mvphy_status(struct mii_softc
*);
82 static void mvphy_reset(struct mii_softc
*sc
);
84 static const struct mii_phy_funcs mvphy_funcs
= {
85 mvphy_service
, mvphy_status
, mvphy_reset
,
88 static const struct mii_phydesc mvphys
[] = {
89 { MII_OUI_xxMARVELL
, MII_MODEL_xxMARVELL_E6060
,
90 MII_STR_xxMARVELL_E6060
},
97 * On AP30/AR5312 the switch is configured in one of two ways:
98 * as a ROUTER or as a BRIDGE. The ROUTER config sets up ports
99 * 0-3 as LAN ports, port 4 as the WAN port, and port 5 connects
100 * to the MAC in the 5312. The BRIDGE config sets up ports
101 * 0-4 as LAN ports with port 5 connected to the MAC in the 5312.
104 uint16_t switchPortAddr
;/* switch port associated with PHY */
105 uint16_t vlanSetting
; /* VLAN table setting for PHY */
106 uint32_t portControl
; /* switch port control setting for PHY */
108 static const struct mvPhyConfig dumbConfig
[] = {
109 { 0x18, 0x2e, /* PHY port 0 = LAN port 0 */
110 MV_PORT_CONTROL_PORT_STATE_FORWARDING
},
111 { 0x19, 0x2d, /* PHY port 1 = LAN port 1 */
112 MV_PORT_CONTROL_PORT_STATE_FORWARDING
},
113 { 0x1a, 0x2b, /* PHY port 2 = LAN port 2 */
114 MV_PORT_CONTROL_PORT_STATE_FORWARDING
},
115 { 0x1b, 0x27, /* PHY port 3 = LAN port 3 */
116 MV_PORT_CONTROL_PORT_STATE_FORWARDING
},
117 { 0x1c, 0x25, /* PHY port 4 = LAN port 4 */
118 MV_PORT_CONTROL_PORT_STATE_FORWARDING
},
119 { 0x1d, 0x1f, /* PHY port 5 = CPU port */
120 MV_PORT_CONTROL_PORT_STATE_FORWARDING
}
122 static const struct mvPhyConfig routerConfig
[] = {
123 { 0x18, 0x2e, /* PHY port 0 = LAN port 0 */
124 MV_PORT_CONTROL_PORT_STATE_FORWARDING
},
125 { 0x19, 0x2d, /* PHY port 1 = LAN port 1 */
126 MV_PORT_CONTROL_PORT_STATE_FORWARDING
},
127 { 0x1a, 0x2b, /* PHY port 2 = LAN port 2 */
128 MV_PORT_CONTROL_PORT_STATE_FORWARDING
},
129 { 0x1b, 0x27, /* PHY port 3 = LAN port 3 */
130 MV_PORT_CONTROL_PORT_STATE_FORWARDING
},
131 { 0x1c, 0x1020, /* PHY port 4 = WAN port */
132 MV_PORT_CONTROL_PORT_STATE_FORWARDING
},
133 /* NB: 0x0f =>'s send only to LAN ports */
134 { 0x1d, 0x0f, /* PHY port 5 = CPU port */
135 MV_PORT_CONTROL_PORT_STATE_FORWARDING
137 | MV_PORT_CONTROL_INGRESS_TRAILER
138 | MV_PORT_CONTROL_EGRESS_MODE
142 static const struct mvPhyConfig bridgeConfig
[] = {
143 { 0x18, 0x3e, /* PHY port 0 = LAN port 0 */
144 MV_PORT_CONTROL_PORT_STATE_FORWARDING
},
145 { 0x19, 0x3d, /* PHY port 1 = LAN port 1 */
146 MV_PORT_CONTROL_PORT_STATE_FORWARDING
},
147 { 0x1a, 0x3b, /* PHY port 2 = LAN port 2 */
148 MV_PORT_CONTROL_PORT_STATE_FORWARDING
},
149 { 0x1b, 0x37, /* PHY port 3 = LAN port 3 */
150 MV_PORT_CONTROL_PORT_STATE_FORWARDING
},
151 { 0x1c, 0x37, /* PHY port 4 = LAN port 4 */
152 MV_PORT_CONTROL_PORT_STATE_FORWARDING
},
153 /* NB: 0x1f =>'s send to all ports */
154 { 0x1d, 0x1f, /* PHY port 5 = CPU port */
155 MV_PORT_CONTROL_PORT_STATE_FORWARDING
157 | MV_PORT_CONTROL_INGRESS_TRAILER
158 | MV_PORT_CONTROL_EGRESS_MODE
163 static void mvphy_switchconfig(struct mii_softc
*, int);
164 static void mvphy_flushatu(struct mii_softc
*);
167 mvphymatch(device_t parent
, cfdata_t match
, void *aux
)
169 struct mii_attach_args
*ma
= aux
;
171 if (mii_phy_match(ma
, mvphys
) != NULL
)
178 mvphyattach(device_t parent
, device_t self
, void *aux
)
180 struct mii_softc
*sc
= device_private(self
);
181 struct mii_attach_args
*ma
= aux
;
182 struct mii_data
*mii
= ma
->mii_data
;
183 const struct mii_phydesc
*mpd
;
185 mpd
= mii_phy_match(ma
, mvphys
);
186 aprint_naive(": Media interface\n");
187 aprint_normal(": %s, rev. %d\n", mpd
->mpd_name
, MII_REV(ma
->mii_id2
));
190 sc
->mii_inst
= mii
->mii_instance
;
191 sc
->mii_phy
= ma
->mii_phyno
;
192 sc
->mii_funcs
= &mvphy_funcs
;
194 sc
->mii_flags
= ma
->mii_flags
;
195 sc
->mii_anegticks
= MII_ANEGTICKS
;
197 if (MV_PORT(sc
) == 0) { /* NB: only when attaching first PHY */
199 * Set the global switch settings and configure the
200 * CPU port since it does not probe as a visible PHY.
202 MV_WRITE(sc
, MII_MV_SWITCH_GLOBAL_ADDR
, MV_ATU_CONTROL
,
203 SM(MV_ATUCTRL_AGE_TIME_DEFAULT
, MV_ATUCTRL_AGE_TIME
)
204 | SM(MV_ATUCTRL_ATU_SIZE_DEFAULT
, MV_ATUCTRL_ATU_SIZE
));
205 mvphy_switchconfig(sc
, MV_CPU_PORT
);
209 sc
->mii_capabilities
= PHY_READ(sc
, MII_BMSR
) & ma
->mii_capmask
;
210 aprint_normal_dev(self
, "");
211 if ((sc
->mii_capabilities
& BMSR_MEDIAMASK
) == 0)
212 aprint_error("no media present");
214 mii_phy_add_media(sc
);
219 mvphy_service(struct mii_softc
*sc
, struct mii_data
*mii
, int cmd
)
221 struct ifmedia_entry
*ife
= mii
->mii_media
.ifm_cur
;
226 * If we're not polling our PHY instance, just return.
228 if (IFM_INST(ife
->ifm_media
) != sc
->mii_inst
)
234 * If the media indicates a different PHY instance,
237 if (IFM_INST(ife
->ifm_media
) != sc
->mii_inst
) {
243 * If the interface is not up, don't do anything.
245 if ((mii
->mii_ifp
->if_flags
& IFF_UP
) == 0)
248 mii_phy_setmedia(sc
);
253 * If we're not currently selected, just return.
255 if (IFM_INST(ife
->ifm_media
) != sc
->mii_inst
)
258 if (mii_phy_tick(sc
) == EJUSTRETURN
)
267 /* Update the media status. */
270 /* Callback if something changed. */
271 mii_phy_update(sc
, cmd
);
276 mvphy_status(struct mii_softc
*sc
)
278 struct mii_data
*mii
= sc
->mii_pdata
;
281 mii
->mii_media_status
= IFM_AVALID
;
282 mii
->mii_media_active
= IFM_ETHER
;
284 hwstatus
= PHY_READ(sc
, MII_MV_PHY_SPECIFIC_STATUS
);
285 if (hwstatus
& MV_STATUS_REAL_TIME_LINK_UP
) {
286 mii
->mii_media_status
|= IFM_ACTIVE
;
287 if (hwstatus
& MV_STATUS_RESOLVED_SPEED_100
)
288 mii
->mii_media_active
|= IFM_100_TX
;
290 mii
->mii_media_active
|= IFM_10_T
;
291 if (hwstatus
& MV_STATUS_RESOLVED_DUPLEX_FULL
)
292 mii
->mii_media_active
|= IFM_FDX
;
294 mii
->mii_media_active
|= IFM_NONE
;
295 /* XXX flush ATU only on link down transition */
301 mvphy_reset(struct mii_softc
*sc
)
304 /* XXX handle fixed media config */
305 PHY_WRITE(sc
, MII_BMCR
, BMCR_RESET
| BMCR_AUTOEN
);
306 mvphy_switchconfig(sc
, MV_PORT(sc
));
310 * Configure switch for the specified port.
313 mvphy_switchconfig(struct mii_softc
*sc
, int port
)
315 /* XXX router vs bridge */
316 /*const struct mvPhyConfig *conf = &routerConfig[port];*/
317 /*const struct mvPhyConfig *conf = &bridgeConfig[port];*/
318 const struct mvPhyConfig
*conf
= &dumbConfig
[port
];
320 MV_WRITE(sc
, conf
->switchPortAddr
, MV_PORT_BASED_VLAN_MAP
,
322 /* XXX administrative control of port enable? */
323 MV_WRITE(sc
, conf
->switchPortAddr
, MV_PORT_CONTROL
, conf
->portControl
);
324 MV_WRITE(sc
, conf
->switchPortAddr
, MV_PORT_ASSOCIATION_VECTOR
, 1<<port
);
328 * Flush the Address Translation Unit (ATU).
331 mvphy_flushatu(struct mii_softc
*sc
)
336 /* wait for any previous request to complete */
337 /* XXX if busy defer to tick */
339 for (i
= 0; i
< 1000; i
++) {
340 status
= MV_READ(sc
, MII_MV_SWITCH_GLOBAL_ADDR
,
342 if (MV_ATU_IS_BUSY(status
))
346 MV_WRITE(sc
, MII_MV_SWITCH_GLOBAL_ADDR
, MV_ATU_OPERATION
,
347 MV_ATU_OP_FLUSH_ALL
| MV_ATU_BUSY
);
349 aprint_error_dev(sc->mii_dev, "timeout waiting for ATU flush\n");*/