1/* Ethernet Physical Receiver model. 2 3 Copyright (C) 2010-2023 Free Software Foundation, Inc. 4 Contributed by Analog Devices, Inc. 5 6 This file is part of simulators. 7 8 This program is free software; you can redistribute it and/or modify 9 it under the terms of the GNU General Public License as published by 10 the Free Software Foundation; either version 3 of the License, or 11 (at your option) any later version. 12 13 This program is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 GNU General Public License for more details. 17 18 You should have received a copy of the GNU General Public License 19 along with this program. If not, see <http://www.gnu.org/licenses/>. */ 20 21/* This must come before any other includes. */ 22#include "defs.h" 23 24#include "sim-main.h" 25#include "devices.h" 26 27#if defined (HAVE_LINUX_MII_H) && defined (HAVE_LINUX_TYPES_H) 28 29/* Workaround old/broken linux headers. */ 30#include <linux/types.h> 31#include <linux/mii.h> 32 33#define REG_PHY_SIZE 0x20 34 35struct eth_phy 36{ 37 bu32 base; 38 bu16 regs[REG_PHY_SIZE]; 39}; 40#define reg_base() offsetof(struct eth_phy, regs[0]) 41#define reg_offset(reg) (offsetof(struct eth_phy, reg) - reg_base()) 42#define reg_idx(reg) (reg_offset (reg) / 4) 43 44static const char * const reg_names[] = 45{ 46 [MII_BMCR ] = "MII_BMCR", 47 [MII_BMSR ] = "MII_BMSR", 48 [MII_PHYSID1 ] = "MII_PHYSID1", 49 [MII_PHYSID2 ] = "MII_PHYSID2", 50 [MII_ADVERTISE ] = "MII_ADVERTISE", 51 [MII_LPA ] = "MII_LPA", 52 [MII_EXPANSION ] = "MII_EXPANSION", 53#ifdef MII_CTRL1000 54 [MII_CTRL1000 ] = "MII_CTRL1000", 55#endif 56#ifdef MII_STAT1000 57 [MII_STAT1000 ] = "MII_STAT1000", 58#endif 59#ifdef MII_ESTATUS 60 [MII_ESTATUS ] = "MII_ESTATUS", 61#endif 62 [MII_DCOUNTER ] = "MII_DCOUNTER", 63 [MII_FCSCOUNTER ] = "MII_FCSCOUNTER", 64 [MII_NWAYTEST ] = "MII_NWAYTEST", 65 [MII_RERRCOUNTER] = "MII_RERRCOUNTER", 66 [MII_SREVISION ] = "MII_SREVISION", 67 [MII_RESV1 ] = "MII_RESV1", 68 [MII_LBRERROR ] = "MII_LBRERROR", 69 [MII_PHYADDR ] = "MII_PHYADDR", 70 [MII_RESV2 ] = "MII_RESV2", 71 [MII_TPISTATUS ] = "MII_TPISTATUS", 72 [MII_NCONFIG ] = "MII_NCONFIG", 73}; 74#define mmr_name(off) (reg_names[off] ? : "<INV>") 75#define mmr_off reg_off 76 77static unsigned 78eth_phy_io_write_buffer (struct hw *me, const void *source, 79 int space, address_word addr, unsigned nr_bytes) 80{ 81 struct eth_phy *phy = hw_data (me); 82 bu16 reg_off; 83 bu16 value; 84 bu16 *valuep; 85 86 value = dv_load_2 (source); 87 88 reg_off = addr - phy->base; 89 valuep = (void *)((uintptr_t)phy + reg_base() + reg_off); 90 91 HW_TRACE_WRITE (); 92 93 switch (reg_off) 94 { 95 case MII_BMCR: 96 *valuep = value; 97 break; 98 case MII_PHYSID1: 99 case MII_PHYSID2: 100 /* Discard writes to these. */ 101 break; 102 default: 103 /* XXX: Discard writes to unknown regs ? */ 104 *valuep = value; 105 break; 106 } 107 108 return nr_bytes; 109} 110 111static unsigned 112eth_phy_io_read_buffer (struct hw *me, void *dest, 113 int space, address_word addr, unsigned nr_bytes) 114{ 115 struct eth_phy *phy = hw_data (me); 116 bu16 reg_off; 117 bu16 *valuep; 118 119 reg_off = addr - phy->base; 120 valuep = (void *)((uintptr_t)phy + reg_base() + reg_off); 121 122 HW_TRACE_READ (); 123 124 switch (reg_off) 125 { 126 case MII_BMCR: 127 dv_store_2 (dest, *valuep); 128 break; 129 case MII_BMSR: 130 /* XXX: Let people control this ? */ 131 *valuep = BMSR_100FULL | BMSR_100HALF | BMSR_10FULL | BMSR_10HALF | 132 BMSR_ANEGCOMPLETE | BMSR_ANEGCAPABLE | BMSR_LSTATUS; 133 dv_store_2 (dest, *valuep); 134 break; 135 case MII_LPA: 136 /* XXX: Let people control this ? */ 137 *valuep = LPA_100FULL | LPA_100HALF | LPA_10FULL | LPA_10HALF; 138 dv_store_2 (dest, *valuep); 139 break; 140 default: 141 dv_store_2 (dest, *valuep); 142 break; 143 } 144 145 return nr_bytes; 146} 147 148static void 149attach_eth_phy_regs (struct hw *me, struct eth_phy *phy) 150{ 151 address_word attach_address; 152 int attach_space; 153 unsigned attach_size; 154 reg_property_spec reg; 155 156 if (hw_find_property (me, "reg") == NULL) 157 hw_abort (me, "Missing \"reg\" property"); 158 159 if (!hw_find_reg_array_property (me, "reg", 0, ®)) 160 hw_abort (me, "\"reg\" property must contain three addr/size entries"); 161 162 hw_unit_address_to_attach_address (hw_parent (me), 163 ®.address, 164 &attach_space, &attach_address, me); 165 hw_unit_size_to_attach_size (hw_parent (me), ®.size, &attach_size, me); 166 167 if (attach_size != REG_PHY_SIZE) 168 hw_abort (me, "\"reg\" size must be %#x", REG_PHY_SIZE); 169 170 hw_attach_address (hw_parent (me), 171 0, attach_space, attach_address, attach_size, me); 172 173 phy->base = attach_address; 174} 175 176static void 177eth_phy_finish (struct hw *me) 178{ 179 struct eth_phy *phy; 180 181 phy = HW_ZALLOC (me, struct eth_phy); 182 183 set_hw_data (me, phy); 184 set_hw_io_read_buffer (me, eth_phy_io_read_buffer); 185 set_hw_io_write_buffer (me, eth_phy_io_write_buffer); 186 187 attach_eth_phy_regs (me, phy); 188 189 /* Initialize the PHY. */ 190 phy->regs[MII_PHYSID1] = 0; /* Unassigned Vendor */ 191 phy->regs[MII_PHYSID2] = 0xAD; /* Product */ 192} 193 194#else 195 196static void 197eth_phy_finish (struct hw *me) 198{ 199 HW_TRACE ((me, "No linux/mii.h support found")); 200} 201 202#endif 203 204const struct hw_descriptor dv_eth_phy_descriptor[] = 205{ 206 {"eth_phy", eth_phy_finish,}, 207 {NULL, NULL}, 208}; 209