1 /*- 2 * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com> 3 * All rights reserved. 4 * 5 * This software was developed by SRI International and the University of 6 * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237) 7 * ("CTSRD"), as part of the DARPA CRASH research programme. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31 #include <sys/cdefs.h> 32 __FBSDID("$FreeBSD$"); 33 34 /* 35 * Micrel KSZ9021 Gigabit Ethernet Transceiver 36 */ 37 38 #include <sys/param.h> 39 #include <sys/systm.h> 40 #include <sys/kernel.h> 41 #include <sys/socket.h> 42 #include <sys/errno.h> 43 #include <sys/module.h> 44 #include <sys/bus.h> 45 #include <sys/malloc.h> 46 47 #include <machine/bus.h> 48 49 #include <net/if.h> 50 #include <net/if_media.h> 51 52 #include <dev/mii/mii.h> 53 #include <dev/mii/miivar.h> 54 #include "miidevs.h" 55 56 #include "miibus_if.h" 57 58 #include <dev/fdt/fdt_common.h> 59 #include <dev/ofw/openfirm.h> 60 #include <dev/ofw/ofw_bus.h> 61 #include <dev/ofw/ofw_bus_subr.h> 62 63 #define MII_KSZPHY_EXTREG 0x0b 64 #define KSZPHY_EXTREG_WRITE (1 << 15) 65 #define MII_KSZPHY_EXTREG_WRITE 0x0c 66 #define MII_KSZPHY_EXTREG_READ 0x0d 67 #define MII_KSZPHY_CLK_CONTROL_PAD_SKEW 0x104 68 #define MII_KSZPHY_RX_DATA_PAD_SKEW 0x105 69 #define MII_KSZPHY_TX_DATA_PAD_SKEW 0x106 70 71 #define PS_TO_REG(p) ((p) / 200) 72 73 static int micphy_probe(device_t); 74 static int micphy_attach(device_t); 75 static int micphy_service(struct mii_softc *, struct mii_data *, int); 76 77 static device_method_t micphy_methods[] = { 78 /* device interface */ 79 DEVMETHOD(device_probe, micphy_probe), 80 DEVMETHOD(device_attach, micphy_attach), 81 DEVMETHOD(device_detach, mii_phy_detach), 82 DEVMETHOD(device_shutdown, bus_generic_shutdown), 83 DEVMETHOD_END 84 }; 85 86 static devclass_t micphy_devclass; 87 88 static driver_t micphy_driver = { 89 "micphy", 90 micphy_methods, 91 sizeof(struct mii_softc) 92 }; 93 94 DRIVER_MODULE(micphy, miibus, micphy_driver, micphy_devclass, 0, 0); 95 96 static const struct mii_phydesc micphys[] = { 97 MII_PHY_DESC(MICREL, KSZ9021), 98 MII_PHY_END 99 }; 100 101 static const struct mii_phy_funcs micphy_funcs = { 102 micphy_service, 103 ukphy_status, 104 mii_phy_reset 105 }; 106 107 static void 108 micphy_write(struct mii_softc *sc, uint32_t reg, uint32_t val) 109 { 110 111 PHY_WRITE(sc, MII_KSZPHY_EXTREG, KSZPHY_EXTREG_WRITE | reg); 112 PHY_WRITE(sc, MII_KSZPHY_EXTREG_WRITE, val); 113 } 114 115 static int 116 ksz9021_load_values(struct mii_softc *sc, phandle_t node, uint32_t reg, 117 char *field1, char *field2, 118 char *field3, char *field4) 119 { 120 pcell_t dts_value[1]; 121 int len; 122 int val; 123 124 val = 0; 125 126 if ((len = OF_getproplen(node, field1)) > 0) { 127 OF_getencprop(node, field1, dts_value, len); 128 val = PS_TO_REG(dts_value[0]); 129 } 130 131 if ((len = OF_getproplen(node, field2)) > 0) { 132 OF_getencprop(node, field2, dts_value, len); 133 val |= PS_TO_REG(dts_value[0]) << 4; 134 } 135 136 if ((len = OF_getproplen(node, field3)) > 0) { 137 OF_getencprop(node, field3, dts_value, len); 138 val |= PS_TO_REG(dts_value[0]) << 8; 139 } 140 141 if ((len = OF_getproplen(node, field4)) > 0) { 142 OF_getencprop(node, field4, dts_value, len); 143 val |= PS_TO_REG(dts_value[0]) << 12; 144 } 145 146 micphy_write(sc, reg, val); 147 148 return (0); 149 } 150 151 static int 152 micphy_probe(device_t dev) 153 { 154 155 return (mii_phy_dev_probe(dev, micphys, BUS_PROBE_DEFAULT)); 156 } 157 158 static int 159 micphy_attach(device_t dev) 160 { 161 struct mii_softc *sc; 162 phandle_t node; 163 device_t miibus; 164 device_t parent; 165 166 sc = device_get_softc(dev); 167 168 mii_phy_dev_attach(dev, MIIF_NOMANPAUSE, &micphy_funcs, 1); 169 mii_phy_setmedia(sc); 170 171 miibus = device_get_parent(dev); 172 parent = device_get_parent(miibus); 173 174 if ((node = ofw_bus_get_node(parent)) == -1) 175 return (ENXIO); 176 177 ksz9021_load_values(sc, node, MII_KSZPHY_CLK_CONTROL_PAD_SKEW, 178 "txen-skew-ps", "txc-skew-ps", 179 "rxdv-skew-ps", "rxc-skew-ps"); 180 181 ksz9021_load_values(sc, node, MII_KSZPHY_RX_DATA_PAD_SKEW, 182 "rxd0-skew-ps", "rxd1-skew-ps", 183 "rxd2-skew-ps", "rxd3-skew-ps"); 184 185 ksz9021_load_values(sc, node, MII_KSZPHY_TX_DATA_PAD_SKEW, 186 "txd0-skew-ps", "txd1-skew-ps", 187 "txd2-skew-ps", "txd3-skew-ps"); 188 189 return (0); 190 } 191 192 static int 193 micphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd) 194 { 195 196 switch (cmd) { 197 case MII_POLLSTAT: 198 break; 199 200 case MII_MEDIACHG: 201 mii_phy_setmedia(sc); 202 break; 203 204 case MII_TICK: 205 if (mii_phy_tick(sc) == EJUSTRETURN) 206 return (0); 207 break; 208 } 209 210 /* Update the media status. */ 211 PHY_STATUS(sc); 212 213 /* Callback if something changed. */ 214 mii_phy_update(sc, cmd); 215 return (0); 216 } 217