/******************************************************************************* * file ksz8051xnl.c * author Michael J. Scott * version V1.0.0. * date 05-APRIL-2014 * brief This is a Phy Driver for Micrel KSZ8051xNL * for Rowley TCP/IP Library * ****************************************************************************** Copyright (c) 2014 Michael J. Scott Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR OR COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include #include "libnet/ctl_net_api.h" #include "libnet/ctl_net_hw.h" #include "libnet/ctl_phy_mii.h" #include "libnet/ksz8051xnl.h" // KS8051xnl vendor-specific registers #define PHY_RECR 0x15 #define PHY_INTCTRL 0x1B #define PHY_EXT_SR 0x1E #define PHY_100PHY 0x1F typedef CTL_PHY_STATE_t (*CTL_PHY_VET_FN_t)(CTL_NET_INTERFACE_t *net, unsigned isFirstPass); static CTL_PHY_STATE_t ksz8051xnl_phy_task_do_error_state(CTL_NET_INTERFACE_t *net, unsigned firstPass); static CTL_PHY_STATE_t ksz8051xnl_phy_task_do_no_link_state(CTL_NET_INTERFACE_t *net, unsigned firstPass); static CTL_PHY_STATE_t ksz8051xnl_phy_task_do_negotiating_state(CTL_NET_INTERFACE_t *net, unsigned firstPass); static CTL_PHY_STATE_t ksz8051xnl_phy_task_do_linked_state(CTL_NET_INTERFACE_t *net, unsigned firstPass); static const CTL_PHY_VET_FN_t stateHandler[] = { ksz8051xnl_phy_task_do_error_state, ksz8051xnl_phy_task_do_no_link_state, ksz8051xnl_phy_task_do_negotiating_state, ksz8051xnl_phy_task_do_linked_state, }; static CTL_STATUS_t ksz8051xnl_phy_init(CTL_NET_INTERFACE_t *net) { unsigned long id; CTL_STATUS_t stat; // Initialize flags. net->phy.operating_mode = 0; net->phy.state = CTL_PHY_STATE_ERROR; // Ensure correct PHY. stat = ctl_phy_read_id(net, &id); if (stat < CTL_NO_ERROR) return stat; if (id != KSZ8051XNL_PHY_ID) return CTL_PHY_INCORRECT_ID; // Reset PHY and and configure link protocol. stat = ctl_phy_reset(net); if (stat < CTL_NO_ERROR) return stat; stat = ctl_phy_reset(net); if (stat < CTL_NO_ERROR) return stat; stat = ctl_phy_configure_link(net); if (stat < CTL_NO_ERROR) return stat; // PHY is set up, kick off state machine. net->phy.state = CTL_PHY_STATE_INITIALIZE; stat = ctl_mac_mii_write(net, PHY_BMCR, 0x1000); // Done. return CTL_NO_ERROR; } static CTL_PHY_STATE_t ksz8051xnl_phy_task_do_error_state(CTL_NET_INTERFACE_t *net, unsigned firstPass) { return CTL_PHY_STATE_ERROR; // we never got past initialization } static CTL_PHY_STATE_t ksz8051xnl_phy_task_do_no_link_state(CTL_NET_INTERFACE_t *net, unsigned firstPass) { if (!firstPass) { CTL_STATUS_t stat = ctl_mac_mii_deferred_read_result(net); if (stat >= CTL_NO_ERROR) { if (stat & BMSR_LINK_STATUS) return CTL_PHY_STATE_NEGOTIATING; } } if (ctl_mac_mii_deferred_read(net, PHY_BMSR) < CTL_NO_ERROR) return CTL_PHY_STATE_ERROR; else return CTL_PHY_STATE_NO_LINK; } static CTL_PHY_STATE_t ksz8051xnl_phy_task_do_negotiating_state(CTL_NET_INTERFACE_t *net, unsigned firstPass) { if (!firstPass) { CTL_STATUS_t bmcr = ctl_mac_mii_read(net, PHY_BMCR); if( bmcr & 0x8000 ) return CTL_PHY_STATE_NEGOTIATING; CTL_STATUS_t bmsr = ctl_mac_mii_read(net, PHY_EXT_SR); if (bmsr >= CTL_NO_ERROR) { unsigned linkStatus; linkStatus = (bmsr) & 7; switch (linkStatus) { default: case 0: // Still in auto-negotiation case 3: // Reserved state case 7: // PHY/MII isolate break; case 1: // 10base-T HDX net->phy.operating_mode |= CTL_PHY_FLAG_10M; net->phy.operating_mode &= ~CTL_PHY_FLAG_100M; net->phy.operating_mode &= ~CTL_PHY_FLAG_FULL_DUPLEX; if (ctl_mac_mii_write(net, PHY_BMCR, 0) < CTL_NO_ERROR) return CTL_PHY_STATE_ERROR; else return CTL_PHY_STATE_LINKED; case 2: // 100base-TX HDX net->phy.operating_mode &= ~CTL_PHY_FLAG_10M; net->phy.operating_mode |= CTL_PHY_FLAG_100M; net->phy.operating_mode &= ~CTL_PHY_FLAG_FULL_DUPLEX; if (ctl_mac_mii_write(net, PHY_BMCR, BMCR_SPEED_SELECTION_LSB) < CTL_NO_ERROR) return CTL_PHY_STATE_ERROR; else return CTL_PHY_STATE_LINKED; case 5: // 10base-T FDX net->phy.operating_mode |= CTL_PHY_FLAG_10M; net->phy.operating_mode &= ~CTL_PHY_FLAG_100M; net->phy.operating_mode |= CTL_PHY_FLAG_FULL_DUPLEX; if (ctl_mac_mii_write(net, PHY_BMCR, BMCR_DUPLEX_MODE) < CTL_NO_ERROR) return CTL_PHY_STATE_ERROR; else return CTL_PHY_STATE_LINKED; case 6: // 100base-TX FDX net->phy.operating_mode &= ~CTL_PHY_FLAG_10M; net->phy.operating_mode |= CTL_PHY_FLAG_100M; net->phy.operating_mode |= CTL_PHY_FLAG_FULL_DUPLEX; if (ctl_mac_mii_write(net, PHY_BMCR, BMCR_DUPLEX_MODE | BMCR_SPEED_SELECTION_LSB) < CTL_NO_ERROR) return CTL_PHY_STATE_ERROR; else return CTL_PHY_STATE_LINKED; } } } if (ctl_mac_mii_deferred_read(net, PHY_100PHY) < CTL_NO_ERROR) return CTL_PHY_STATE_ERROR; else return CTL_PHY_STATE_NEGOTIATING; } static CTL_PHY_STATE_t ksz8051xnl_phy_task_do_linked_state(CTL_NET_INTERFACE_t *net, unsigned firstPass) { if (!firstPass) { CTL_STATUS_t lastResult = ctl_mac_mii_deferred_read_result(net); if (lastResult >= CTL_NO_ERROR) { if ((lastResult & BMSR_LINK_STATUS) == 0) return CTL_PHY_STATE_NO_LINK; // drop back } } if (ctl_mac_mii_deferred_read(net, PHY_BMSR) < CTL_NO_ERROR) return CTL_PHY_STATE_ERROR; else return CTL_PHY_STATE_LINKED; } static void ksz8051xnl_phy_update(CTL_NET_INTERFACE_t *net) { CTL_PHY_STATE_t nextState; CTL_STATUS_t stat; // Call the state function with the "first pass" flag deasserted. if (net->phy.state == CTL_PHY_STATE_INITIALIZE) { if (stat = ctl_mac_mii_deferred_read(net, PHY_BMSR) < CTL_NO_ERROR) net->phy.state = CTL_PHY_STATE_ERROR; else net->phy.state = CTL_PHY_STATE_NO_LINK; return; } // Call the state function with the "first pass" flag deasserted. nextState = stateHandler[net->phy.state](net, 0); // If the state function returned a new state, then call the new // state function with the "first pass" flag set. if (nextState != net->phy.state) { // First pass will only ever transition to error state, // and the PHY stays in that state until the MAC is reset. net->phy.state = stateHandler[nextState](net, 1); } } void ksz8051xnl_phy_init_driver(CTL_NET_PHY_DRIVER_t *driver) { driver->name = "Micrel KSZ8052xNL"; driver->init_fn = ksz8051xnl_phy_init; driver->update_fn = ksz8051xnl_phy_update; }