196136Sobrien/* Ethernet Physical Receiver model.
2150234Skan
3150234Skan   Copyright (C) 2010-2023 Free Software Foundation, Inc.
4150234Skan   Contributed by Analog Devices, Inc.
596340Sobrien
6132751Skan   This file is part of simulators.
7132751Skan
896340Sobrien   This program is free software; you can redistribute it and/or modify
9132751Skan   it under the terms of the GNU General Public License as published by
1096340Sobrien   the Free Software Foundation; either version 3 of the License, or
1196340Sobrien   (at your option) any later version.
12132751Skan
1396340Sobrien   This program is distributed in the hope that it will be useful,
1496340Sobrien   but WITHOUT ANY WARRANTY; without even the implied warranty of
15132751Skan   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1696340Sobrien   GNU General Public License for more details.
1796340Sobrien
18132751Skan   You should have received a copy of the GNU General Public License
1996340Sobrien   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
2096340Sobrien
21132751Skan/* This must come before any other includes.  */
2296340Sobrien#include "defs.h"
2396340Sobrien
24132751Skan#include "sim-main.h"
25246857Sdim#include "devices.h"
2696340Sobrien
27132751Skan#if defined (HAVE_LINUX_MII_H) && defined (HAVE_LINUX_TYPES_H)
2896340Sobrien
2996340Sobrien/* Workaround old/broken linux headers.  */
30132751Skan#include <linux/types.h>
3196340Sobrien#include <linux/mii.h>
3296340Sobrien
33132751Skan#define REG_PHY_SIZE 0x20
3496340Sobrien
3596340Sobrienstruct eth_phy
36132751Skan{
37140864Skan  bu32 base;
3896340Sobrien  bu16 regs[REG_PHY_SIZE];
39169718Skan};
40169718Skan#define reg_base()      offsetof(struct eth_phy, regs[0])
41169718Skan#define reg_offset(reg) (offsetof(struct eth_phy, reg) - reg_base())
42132751Skan#define reg_idx(reg)    (reg_offset (reg) / 4)
4396340Sobrien
4496340Sobrienstatic const char * const reg_names[] =
45132751Skan{
4696340Sobrien  [MII_BMCR       ] = "MII_BMCR",
4796340Sobrien  [MII_BMSR       ] = "MII_BMSR",
48132751Skan  [MII_PHYSID1    ] = "MII_PHYSID1",
49132751Skan  [MII_PHYSID2    ] = "MII_PHYSID2",
5096340Sobrien  [MII_ADVERTISE  ] = "MII_ADVERTISE",
51132751Skan  [MII_LPA        ] = "MII_LPA",
5296340Sobrien  [MII_EXPANSION  ] = "MII_EXPANSION",
5396340Sobrien#ifdef MII_CTRL1000
54132751Skan  [MII_CTRL1000   ] = "MII_CTRL1000",
5596340Sobrien#endif
5696340Sobrien#ifdef MII_STAT1000
57132751Skan  [MII_STAT1000   ] = "MII_STAT1000",
5896340Sobrien#endif
5996340Sobrien#ifdef MII_ESTATUS
60132751Skan  [MII_ESTATUS    ] = "MII_ESTATUS",
6196340Sobrien#endif
6296340Sobrien  [MII_DCOUNTER   ] = "MII_DCOUNTER",
63132751Skan  [MII_FCSCOUNTER ] = "MII_FCSCOUNTER",
64132751Skan  [MII_NWAYTEST   ] = "MII_NWAYTEST",
6596340Sobrien  [MII_RERRCOUNTER] = "MII_RERRCOUNTER",
66132751Skan  [MII_SREVISION  ] = "MII_SREVISION",
6796340Sobrien  [MII_RESV1      ] = "MII_RESV1",
6896340Sobrien  [MII_LBRERROR   ] = "MII_LBRERROR",
69132751Skan  [MII_PHYADDR    ] = "MII_PHYADDR",
70246857Sdim  [MII_RESV2      ] = "MII_RESV2",
7196340Sobrien  [MII_TPISTATUS  ] = "MII_TPISTATUS",
72132751Skan  [MII_NCONFIG    ] = "MII_NCONFIG",
7396340Sobrien};
7496340Sobrien#define mmr_name(off) (reg_names[off] ? : "<INV>")
75132751Skan#define mmr_off reg_off
76132751Skan
7796340Sobrienstatic unsigned
78169718Skaneth_phy_io_write_buffer (struct hw *me, const void *source,
79169718Skan			 int space, address_word addr, unsigned nr_bytes)
8096340Sobrien{
81132751Skan  struct eth_phy *phy = hw_data (me);
8296340Sobrien  bu16 reg_off;
8396340Sobrien  bu16 value;
84132751Skan  bu16 *valuep;
8596340Sobrien
8696340Sobrien  value = dv_load_2 (source);
87132751Skan
8896340Sobrien  reg_off = addr - phy->base;
8996340Sobrien  valuep = (void *)((uintptr_t)phy + reg_base() + reg_off);
90132751Skan
91132751Skan  HW_TRACE_WRITE ();
92132751Skan
93132751Skan  switch (reg_off)
9496340Sobrien    {
9596340Sobrien    case MII_BMCR:
96132751Skan      *valuep = value;
97140861Skan      break;
9896340Sobrien    case MII_PHYSID1:
99132751Skan    case MII_PHYSID2:
10096340Sobrien      /* Discard writes to these.  */
10196340Sobrien      break;
102132751Skan    default:
103246857Sdim      /* XXX: Discard writes to unknown regs ?  */
10496340Sobrien      *valuep = value;
105132751Skan      break;
10696340Sobrien    }
10796340Sobrien
108132751Skan  return nr_bytes;
109132751Skan}
110132751Skan
111132751Skanstatic unsigned
11296340Sobrieneth_phy_io_read_buffer (struct hw *me, void *dest,
11396340Sobrien			int space, address_word addr, unsigned nr_bytes)
114132751Skan{
115144140Sdas  struct eth_phy *phy = hw_data (me);
11696340Sobrien  bu16 reg_off;
117132751Skan  bu16 *valuep;
118132751Skan
119132751Skan  reg_off = addr - phy->base;
120169718Skan  valuep = (void *)((uintptr_t)phy + reg_base() + reg_off);
121169718Skan
122169718Skan  HW_TRACE_READ ();
123132751Skan
12496340Sobrien  switch (reg_off)
12596340Sobrien    {
126169718Skan    case MII_BMCR:
127169718Skan      dv_store_2 (dest, *valuep);
128169718Skan      break;
12996340Sobrien    case MII_BMSR:
130132751Skan      /* XXX: Let people control this ?  */
13196340Sobrien      *valuep = BMSR_100FULL | BMSR_100HALF | BMSR_10FULL | BMSR_10HALF |
13296340Sobrien		BMSR_ANEGCOMPLETE | BMSR_ANEGCAPABLE | BMSR_LSTATUS;
133132751Skan      dv_store_2 (dest, *valuep);
13496340Sobrien      break;
13596340Sobrien    case MII_LPA:
136132751Skan      /* XXX: Let people control this ?  */
137246857Sdim      *valuep = LPA_100FULL | LPA_100HALF | LPA_10FULL | LPA_10HALF;
13896340Sobrien      dv_store_2 (dest, *valuep);
139132751Skan      break;
140169718Skan    default:
14196340Sobrien      dv_store_2 (dest, *valuep);
142132751Skan      break;
143169718Skan    }
14496340Sobrien
145132751Skan  return nr_bytes;
146169718Skan}
14796340Sobrien
148132751Skanstatic void
149132751Skanattach_eth_phy_regs (struct hw *me, struct eth_phy *phy)
150111116Skan{
151169718Skan  address_word attach_address;
152169718Skan  int attach_space;
153169718Skan  unsigned attach_size;
154132751Skan  reg_property_spec reg;
155132751Skan
156132751Skan  if (hw_find_property (me, "reg") == NULL)
157132751Skan    hw_abort (me, "Missing \"reg\" property");
15896340Sobrien
15996340Sobrien  if (!hw_find_reg_array_property (me, "reg", 0, &reg))
160132751Skan    hw_abort (me, "\"reg\" property must contain three addr/size entries");
16196340Sobrien
16296340Sobrien  hw_unit_address_to_attach_address (hw_parent (me),
163132751Skan				     &reg.address,
16496340Sobrien				     &attach_space, &attach_address, me);
16596340Sobrien  hw_unit_size_to_attach_size (hw_parent (me), &reg.size, &attach_size, me);
166132751Skan
16796340Sobrien  if (attach_size != REG_PHY_SIZE)
16896340Sobrien    hw_abort (me, "\"reg\" size must be %#x", REG_PHY_SIZE);
169132751Skan
17096340Sobrien  hw_attach_address (hw_parent (me),
17196340Sobrien		     0, attach_space, attach_address, attach_size, me);
172132751Skan
17396340Sobrien  phy->base = attach_address;
17496340Sobrien}
175169718Skan
176132751Skanstatic void
177132751Skaneth_phy_finish (struct hw *me)
178169718Skan{
179169718Skan  struct eth_phy *phy;
180169718Skan
181132751Skan  phy = HW_ZALLOC (me, struct eth_phy);
18296340Sobrien
18396340Sobrien  set_hw_data (me, phy);
184132751Skan  set_hw_io_read_buffer (me, eth_phy_io_read_buffer);
185144140Sdas  set_hw_io_write_buffer (me, eth_phy_io_write_buffer);
18696340Sobrien
187132751Skan  attach_eth_phy_regs (me, phy);
188132751Skan
189132751Skan  /* Initialize the PHY.  */
190132751Skan  phy->regs[MII_PHYSID1] = 0;    /* Unassigned Vendor */
191132751Skan  phy->regs[MII_PHYSID2] = 0xAD; /* Product */
192132751Skan}
193162553Skan
194162553Skan#else
195162553Skan
196162553Skanstatic void
197162553Skaneth_phy_finish (struct hw *me)
198162553Skan{
199162553Skan  HW_TRACE ((me, "No linux/mii.h support found"));
200162553Skan}
201162553Skan
202162553Skan#endif
203162553Skan
204162553Skanconst struct hw_descriptor dv_eth_phy_descriptor[] =
205162553Skan{
206162553Skan  {"eth_phy", eth_phy_finish,},
207162553Skan  {NULL, NULL},
208132751Skan};
209132751Skan