1+ #ifndef KLIB_LAN8720_HPP
2+ #define KLIB_LAN8720_HPP
3+
4+ #include < cstdint>
5+
6+ #include < klib/io/bus/ethernet.hpp>
7+
8+ namespace klib ::hardware::ethernet::phy {
9+ template <typename Miim, uint8_t PhyAddress = 0x0 >
10+ class lan8720a {
11+ public:
12+ // the phy address. Note the last 4 bits are the revision
13+ // number and might differ based on the hardware
14+ constexpr static uint32_t phy_id = 0x7c0f0 ;
15+
16+ /* *
17+ * @brief Available speeds on the lan8720a when configuring
18+ *
19+ */
20+ enum class speed {
21+ automatic,
22+ mbps_10,
23+ mbps_100,
24+ };
25+
26+ protected:
27+ /* *
28+ * @brief Available registers on the lan8720a
29+ *
30+ */
31+ enum class cmd {
32+ // basic registers
33+ basic_control = 0x00 ,
34+ basic_status = 0x01 ,
35+
36+ // extended registers
37+ phy_id_0 = 0x02 ,
38+ phy_id_1 = 0x03 ,
39+ auto_advertisement = 0x04 ,
40+ auto_link = 0x05 ,
41+ auto_expansion = 0x06 ,
42+
43+ // vendor specific registers
44+ mode_control_status = 0x11 ,
45+ special_modes = 0x12 ,
46+ symbol_error_counter = 0x1a ,
47+ control_status_indication = 0x1b ,
48+ interrupt_source = 0x1d ,
49+ interrupt_mask = 0x1e ,
50+ special_control_status = 0x1f
51+ };
52+
53+ /* *
54+ * @brief Helper function to write to a register on the phy
55+ *
56+ * @param reg
57+ * @param value
58+ */
59+ static void write (cmd reg, const uint16_t value) {
60+ Miim::write (PhyAddress, static_cast <uint8_t >(reg), value);
61+ }
62+
63+ /* *
64+ * @brief Helper function to read from the phy
65+ *
66+ * @param reg
67+ * @return uint16_t
68+ */
69+ static uint16_t read (cmd reg) {
70+ return Miim::read (PhyAddress, static_cast <uint8_t >(reg));
71+ }
72+
73+ public:
74+ /* *
75+ * @brief Read the phy id from the Miim interface
76+ *
77+ * @return uint32_t
78+ */
79+ static uint32_t id () {
80+ // return the combined top and bottom id register
81+ return (read (cmd::phy_id_0) << 16 ) | read (cmd::phy_id_1);
82+ }
83+
84+ /* *
85+ * @brief Init the LAN8720A
86+ *
87+ * @tparam Speed
88+ * @tparam FullDuplex
89+ * @tparam Loopback
90+ * @tparam Isolate
91+ * @return status
92+ */
93+ template <speed Speed = speed::automatic, bool FullDuplex = true , bool Loopback = false , bool Isolate = false >
94+ static bool init () {
95+ // do a software reset
96+ write (cmd::basic_control, (0x1 << 15 ));
97+
98+ // wait until the reset is done or a 250ms timeout
99+ const auto time = klib::io::systick<>::get_runtime ();
100+
101+ // wait until the software reset is done or a timeout
102+ while (true ) {
103+ // check if the software reset is done
104+ if (!(read (cmd::basic_control) & (0x1 << 15 ))) {
105+ break ;
106+ }
107+
108+ // check for a timeout
109+ if ((klib::io::systick<>::get_runtime () - time) >= klib::time::ms (500 )) {
110+ // lan8720 did not reset within our timeout time
111+ return false ;
112+ }
113+ }
114+
115+ // make sure we have the correct phy (we ignore the last
116+ // 4 bits, the revision bits)
117+ if ((id () & (~0xf )) != phy_id) {
118+ // not a lan8720a phy
119+ return false ;
120+ }
121+
122+ // write the configuration to the phy
123+ if constexpr (Speed == speed::automatic) {
124+ // in automatic mode we ignore the full duplex mode
125+ // (also overrides the speed)
126+ write (cmd::basic_control, (
127+ (0x1 << 12 ) | (Loopback << 14 ) | (Isolate << 10 )
128+ ));
129+ }
130+ else {
131+ write (cmd::basic_control, (
132+ ((Speed == speed::mbps_100) << 13 ) | (Loopback << 14 ) |
133+ (Isolate << 10 ) | (FullDuplex << 8 )
134+ ));
135+ }
136+
137+ // return we successfully configured the lan8720a
138+ return true ;
139+ }
140+
141+ /* *
142+ * @brief Returns if the link is up
143+ *
144+ * @return true
145+ * @return false
146+ */
147+ static bool has_link () {
148+ return read (cmd::basic_status) & (0x1 << 2 );
149+ }
150+
151+ /* *
152+ * @brief Returns the link configuration
153+ *
154+ * @note Only valid if a linked
155+ *
156+ * @return link_config
157+ */
158+ static klib::io::ethernet::link_config get_link_config () {
159+ // read the special control status
160+ const uint16_t val = read (cmd::special_control_status);
161+
162+ // get the link speed from the
163+ const klib::io::ethernet::link_config ret = {
164+ .link_speed = (
165+ (val & (0x1 << 2 )) ?
166+ klib::io::ethernet::speed::mbps_10 :
167+ klib::io::ethernet::speed::mbps_100
168+ ),
169+ .full_duplex = static_cast <bool >(val & (0x1 << 4 ))
170+ };
171+
172+ return ret;
173+ }
174+ };
175+ }
176+
177+ #endif
0 commit comments