SatCat5
cfgbus_mdio.cc
1 // Copyright 2021-2023 The Aerospace Corporation.
3 // This file is a part of SatCat5, licensed under CERN-OHL-W v2 or later.
5 
6 #include <satcat5/cfgbus_core.h>
7 #include <satcat5/cfgbus_mdio.h>
8 #include <satcat5/interrupts.h>
9 #include <satcat5/log.h>
10 #include <satcat5/utils.h>
11 
12 namespace cfg = satcat5::cfg;
13 namespace log = satcat5::log;
14 namespace util = satcat5::util;
15 
16 // Bit masks for the command register:
17 static const u32 HWREG_OPWR = (1u << 26);
18 static const u32 HWREG_OPRD = (2u << 26);
19 inline u32 HWREG_PADDR(unsigned x) {return (x & 0x1F) << 21;}
20 inline u32 HWREG_RADDR(unsigned x) {return (x & 0x1F) << 16;}
21 inline u32 HWREG_WDATA(u16 x) {return x & 0xFFFF;}
22 
23 // Shortcuts for direct register access (REG < 0x20)
24 inline u32 HW_DIR_WRITE(unsigned p, unsigned r, unsigned d)
25  {return HWREG_OPWR | HWREG_PADDR(p) | HWREG_RADDR(r) | HWREG_WDATA(d);}
26 inline u32 HW_DIR_READ(unsigned p, unsigned r)
27  {return HWREG_OPRD | HWREG_PADDR(p) | HWREG_RADDR(r);}
28 
29 // Bit masks for the status register:
30 static const u32 HWSTATUS_WRFULL = (1u << 31);
31 static const u32 HWSTATUS_RVALID = (1u << 30);
32 static const u32 HWSTATUS_RDATA = (0xFFFF);
33 
35  cfg::ConfigBus* cfg, unsigned devaddr, unsigned regaddr)
36  : m_ctrl_reg(cfg->get_register(devaddr, regaddr))
37  , m_addr_rdcount(0)
38  , m_addr_rdidx(0)
39 {
40  // No other initialization at this time.
41 }
42 
44 {
45  // Read status until FIFO is empty.
46  while (hw_rd_status() & HWSTATUS_RVALID) {}
47 }
48 
49 bool cfg::Mdio::direct_write(unsigned phy, unsigned reg, unsigned data)
50 {
51  // Construct and attempt to queue write the command.
52  return hw_wr_command(HW_DIR_WRITE(phy, reg, data));
53 }
54 
55 bool cfg::Mdio::direct_read(unsigned phy, unsigned reg, unsigned ref,
56  cfg::MdioEventListener* callback)
57 {
58  // Attempt to add this command to he hardware queue...
59  if (can_read() && hw_wr_command(HW_DIR_READ(phy, reg))) {
60  // Store new callback parameters.
61  satcat5::irq::AtomicLock lock("MDIO");
62  unsigned wridx = util::modulo_add_uns(
63  m_addr_rdidx + m_addr_rdcount, SATCAT5_MDIO_BUFFSIZE);
64  m_callback[wridx] = callback;
65  m_addr_buff[wridx] = (u16)ref;
66  ++m_addr_rdcount; // Increment pending read counter
67  return true; // Success (accept metadata)
68  } else {
69  return false; // Failure (discard metadata)
70  }
71 }
72 
73 // Always use this method to read status register.
74 // (Otherwise we can accidentally discard received data.)
75 u32 cfg::Mdio::hw_rd_status()
76 {
77  // Read the status register.
78  u32 status = *m_ctrl_reg;
79 
80  // Handle received messages.
81  u16 regaddr = 0, regval = (u16)(status & HWSTATUS_RDATA);
82  cfg::MdioEventListener* callback = 0;
83  if ((m_addr_rdcount > 0) && (status & HWSTATUS_RVALID)) {
84  // Pop register address and callback off the queue.
85  satcat5::irq::AtomicLock lock("MDIO");
86  regaddr = m_addr_buff[m_addr_rdidx];
87  callback = m_callback[m_addr_rdidx];
88  m_addr_rdidx = util::modulo_add_uns(m_addr_rdidx + 1, SATCAT5_MDIO_BUFFSIZE);
89  --m_addr_rdcount;
90  }
91 
92  // Notify callback object, if applicable.
93  if (callback) callback->mdio_done(regaddr, regval);
94 
95  return status;
96 }
97 
98 bool cfg::Mdio::hw_wr_command(u32 cmd)
99 {
100  // Confirm FIFO isn't already full...
101  u32 status = hw_rd_status();
102  if (status & HWSTATUS_WRFULL) {
103  return false; // Abort
104  } else {
105  *m_ctrl_reg = cmd;
106  return true; // Success!
107  }
108 }
109 
110 void cfg::MdioLogger::mdio_done(u16 regaddr, u16 regval)
111 {
112  log::Log(log::INFO, "MDIO read").write(regaddr).write(regval);
113 }
114 
116 // Thin wrappers for indirect register access on a specific PHY device.
118 
119 bool cfg::MdioGenericMmd::write(unsigned reg, unsigned data)
120 {
121  if (reg < 0x20) { // Direct write
122  return m_mdio->direct_write(m_phy, reg, data);
123  } else { // Indirect write
124  return m_mdio->direct_write(m_phy, 0x0D, 0x001F)
125  && m_mdio->direct_write(m_phy, 0x0E, reg)
126  && m_mdio->direct_write(m_phy, 0x0D, 0x401F)
127  && m_mdio->direct_write(m_phy, 0x0E, data);
128  }
129 }
130 
132 {
133  if (reg < 0x20) { // Direct read
134  return m_mdio->direct_read(m_phy, reg, reg, callback);
135  } else { // Indirect read
136  return m_mdio->can_read() // Check before we queue writes
137  && m_mdio->direct_write(m_phy, 0x0D, 0x001F)
138  && m_mdio->direct_write(m_phy, 0x0E, reg)
139  && m_mdio->direct_write(m_phy, 0x0D, 0x401F)
140  && m_mdio->direct_read (m_phy, 0x0E, reg, callback);
141  }
142 }
143 
144 bool cfg::MdioMarvell::write(unsigned reg, unsigned data)
145 {
146  unsigned page = (reg >> 8);
147  return m_mdio->direct_write(m_phy, 0x16, page)
148  && m_mdio->direct_write(m_phy, reg, data);
149 }
150 
152 {
153  unsigned page = (reg >> 8);
154  return m_mdio->can_read() // Check before we queue writes
155  && m_mdio->direct_write(m_phy, 0x16, page)
156  && m_mdio->direct_read (m_phy, reg, reg, callback);
157 }
ConfigBus core definitions.
Generic ConfigBus API.
Definition: cfgbus_core.h:124
Prototype for the cfg::Mdio callback interface.
Definition: cfgbus_mdio.h:22
bool write(unsigned reg, unsigned data) override
Read and write methods allow indirect access.
Definition: cfgbus_mdio.cc:119
bool read(unsigned reg, satcat5::cfg::MdioEventListener *callback) override
Read and write methods allow indirect access.
Definition: cfgbus_mdio.cc:131
bool direct_write(unsigned phy, unsigned reg, unsigned data)
Direct write to designated MDIO register.
Definition: cfgbus_mdio.cc:49
Mdio(satcat5::cfg::ConfigBus *cfg, unsigned devaddr, unsigned regaddr=satcat5::cfg::REGADDR_ANY)
Constructor ties this driver to a ConfigBus address.
Definition: cfgbus_mdio.cc:34
bool direct_read(unsigned phy, unsigned reg, unsigned ref, satcat5::cfg::MdioEventListener *callback)
Direct read from designated MDIO register.
Definition: cfgbus_mdio.cc:55
void poll_always()
Child class MUST override this method.
Definition: cfgbus_mdio.cc:43
bool write(unsigned reg, unsigned data) override
Read and write methods allow indirect access.
Definition: cfgbus_mdio.cc:144
bool read(unsigned reg, satcat5::cfg::MdioEventListener *callback) override
Read and write methods allow indirect access.
Definition: cfgbus_mdio.cc:151
Automatic lock or mutex.
The Log class creates and formats one log message.
Definition: log.h:195
Log & write(const char *str)
Formatting methods for various data types.
Definition: log.cc:198
Diagnostic logging to UART and/or Ethernet ports.
Platform-agnostic API for interrupt management.
Miscellaneous mathematical utility functions.