|
| 1 | +/* |
| 2 | + * SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD |
| 3 | + * |
| 4 | + * SPDX-License-Identifier: Apache-2.0 |
| 5 | + */ |
| 6 | + |
| 7 | +#include <string.h> |
| 8 | +#include <stdlib.h> |
| 9 | +#include <sys/cdefs.h> |
| 10 | +#include "esp_log.h" |
| 11 | +#include "esp_check.h" |
| 12 | +#include "esp_eth_phy_802_3.h" |
| 13 | + |
| 14 | +static const char *TAG = "jl1101"; |
| 15 | + |
| 16 | +/***************Vendor Specific Register***************/ |
| 17 | + |
| 18 | +/** |
| 19 | + * @brief PSMR(Power Saving Mode Register) |
| 20 | + * |
| 21 | + */ |
| 22 | +typedef union { |
| 23 | + struct { |
| 24 | + uint16_t reserved : 15; /* Reserved */ |
| 25 | + uint16_t en_pwr_save : 1; /* Enable power saving mode */ |
| 26 | + }; |
| 27 | + uint16_t val; |
| 28 | +} psmr_reg_t; |
| 29 | + |
| 30 | +/** |
| 31 | + * @brief PSR(Page Select Register) |
| 32 | + * |
| 33 | + */ |
| 34 | +typedef union { |
| 35 | + struct { |
| 36 | + uint16_t page_select : 8; /* Select register page, default is 0 */ |
| 37 | + uint16_t reserved : 8; /* Reserved */ |
| 38 | + }; |
| 39 | + uint16_t val; |
| 40 | +} psr_reg_t; |
| 41 | +#define ETH_PHY_PSR_REG_ADDR (0x1F) |
| 42 | + |
| 43 | +typedef struct { |
| 44 | + phy_802_3_t phy_802_3; |
| 45 | +} phy_jl1101_t; |
| 46 | + |
| 47 | +static esp_err_t jl1101_page_select(phy_jl1101_t *jl1101, uint32_t page) |
| 48 | +{ |
| 49 | + esp_err_t ret = ESP_OK; |
| 50 | + esp_eth_mediator_t *eth = jl1101->phy_802_3.eth; |
| 51 | + psr_reg_t psr = { |
| 52 | + .page_select = page |
| 53 | + }; |
| 54 | + ESP_GOTO_ON_ERROR(eth->phy_reg_write(eth, jl1101->phy_802_3.addr, ETH_PHY_PSR_REG_ADDR, psr.val), err, TAG, "write PSR failed"); |
| 55 | + return ESP_OK; |
| 56 | +err: |
| 57 | + return ret; |
| 58 | +} |
| 59 | + |
| 60 | +static esp_err_t jl1101_update_link_duplex_speed(phy_jl1101_t *jl1101) |
| 61 | +{ |
| 62 | + esp_err_t ret = ESP_OK; |
| 63 | + esp_eth_mediator_t *eth = jl1101->phy_802_3.eth; |
| 64 | + uint32_t addr = jl1101->phy_802_3.addr; |
| 65 | + eth_speed_t speed = ETH_SPEED_10M; |
| 66 | + eth_duplex_t duplex = ETH_DUPLEX_HALF; |
| 67 | + bmcr_reg_t bmcr; |
| 68 | + bmsr_reg_t bmsr; |
| 69 | + uint32_t peer_pause_ability = false; |
| 70 | + anlpar_reg_t anlpar; |
| 71 | + ESP_GOTO_ON_ERROR(jl1101_page_select(jl1101, 0), err, TAG, "select page 0 failed"); |
| 72 | + ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, addr, ETH_PHY_BMSR_REG_ADDR, &(bmsr.val)), err, TAG, "read BMSR failed"); |
| 73 | + ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, addr, ETH_PHY_ANLPAR_REG_ADDR, &(anlpar.val)), err, TAG, "read ANLPAR failed"); |
| 74 | + eth_link_t link = bmsr.link_status ? ETH_LINK_UP : ETH_LINK_DOWN; |
| 75 | + /* check if link status changed */ |
| 76 | + if (jl1101->phy_802_3.link_status != link) { |
| 77 | + /* when link up, read negotiation result */ |
| 78 | + if (link == ETH_LINK_UP) { |
| 79 | + ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed"); |
| 80 | + if (bmcr.speed_select) { |
| 81 | + speed = ETH_SPEED_100M; |
| 82 | + } else { |
| 83 | + speed = ETH_SPEED_10M; |
| 84 | + } |
| 85 | + if (bmcr.duplex_mode) { |
| 86 | + duplex = ETH_DUPLEX_FULL; |
| 87 | + } else { |
| 88 | + duplex = ETH_DUPLEX_HALF; |
| 89 | + } |
| 90 | + ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_SPEED, (void *)speed), err, TAG, "change speed failed"); |
| 91 | + ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_DUPLEX, (void *)duplex), err, TAG, "change duplex failed"); |
| 92 | + /* if we're in duplex mode, and peer has the flow control ability */ |
| 93 | + if (duplex == ETH_DUPLEX_FULL && anlpar.symmetric_pause) { |
| 94 | + peer_pause_ability = 1; |
| 95 | + } else { |
| 96 | + peer_pause_ability = 0; |
| 97 | + } |
| 98 | + ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_PAUSE, (void *)peer_pause_ability), err, TAG, "change pause ability failed"); |
| 99 | + } |
| 100 | + ESP_GOTO_ON_ERROR(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)link), err, TAG, "change link failed"); |
| 101 | + jl1101->phy_802_3.link_status = link; |
| 102 | + } |
| 103 | + return ESP_OK; |
| 104 | +err: |
| 105 | + return ret; |
| 106 | +} |
| 107 | + |
| 108 | +static esp_err_t jl1101_get_link(esp_eth_phy_t *phy) |
| 109 | +{ |
| 110 | + esp_err_t ret = ESP_OK; |
| 111 | + phy_jl1101_t *jl1101 = __containerof(esp_eth_phy_into_phy_802_3(phy), phy_jl1101_t, phy_802_3); |
| 112 | + /* Updata information about link, speed, duplex */ |
| 113 | + ESP_GOTO_ON_ERROR(jl1101_update_link_duplex_speed(jl1101), err, TAG, "update link duplex speed failed"); |
| 114 | + return ESP_OK; |
| 115 | +err: |
| 116 | + return ret; |
| 117 | +} |
| 118 | + |
| 119 | +static esp_err_t jl1101_autonego_ctrl(esp_eth_phy_t *phy, eth_phy_autoneg_cmd_t cmd, bool *autonego_en_stat) |
| 120 | +{ |
| 121 | + esp_err_t ret = ESP_OK; |
| 122 | + phy_802_3_t *phy_802_3 = esp_eth_phy_into_phy_802_3(phy); |
| 123 | + esp_eth_mediator_t *eth = phy_802_3->eth; |
| 124 | + if (cmd == ESP_ETH_PHY_AUTONEGO_EN) { |
| 125 | + bmcr_reg_t bmcr; |
| 126 | + ESP_GOTO_ON_ERROR(eth->phy_reg_read(eth, phy_802_3->addr, ETH_PHY_BMCR_REG_ADDR, &(bmcr.val)), err, TAG, "read BMCR failed"); |
| 127 | + ESP_GOTO_ON_FALSE(bmcr.en_loopback == 0, ESP_ERR_INVALID_STATE, err, TAG, "Autonegotiation can't be enabled while in loopback operation"); |
| 128 | + } |
| 129 | + return esp_eth_phy_802_3_autonego_ctrl(phy_802_3, cmd, autonego_en_stat); |
| 130 | +err: |
| 131 | + return ret; |
| 132 | +} |
| 133 | + |
| 134 | +static esp_err_t jl1101_loopback(esp_eth_phy_t *phy, bool enable) |
| 135 | +{ |
| 136 | + esp_err_t ret = ESP_OK; |
| 137 | + phy_802_3_t *phy_802_3 = esp_eth_phy_into_phy_802_3(phy); |
| 138 | + bool auto_nego_en; |
| 139 | + ESP_GOTO_ON_ERROR(jl1101_autonego_ctrl(phy, ESP_ETH_PHY_AUTONEGO_G_STAT, &auto_nego_en), err, TAG, "get status of autonegotiation failed"); |
| 140 | + ESP_GOTO_ON_FALSE(!(auto_nego_en && enable), ESP_ERR_INVALID_STATE, err, TAG, "Unable to set loopback while autonegotiation is enabled. Disable it to use loopback"); |
| 141 | + return esp_eth_phy_802_3_loopback(phy_802_3, enable); |
| 142 | +err: |
| 143 | + return ret; |
| 144 | +} |
| 145 | + |
| 146 | +static esp_err_t jl1101_init(esp_eth_phy_t *phy) |
| 147 | +{ |
| 148 | + esp_err_t ret = ESP_OK; |
| 149 | + phy_802_3_t *phy_802_3 = esp_eth_phy_into_phy_802_3(phy); |
| 150 | + |
| 151 | + /* Basic PHY init */ |
| 152 | + ESP_GOTO_ON_ERROR(esp_eth_phy_802_3_basic_phy_init(phy_802_3), err, TAG, "failed to init PHY"); |
| 153 | + |
| 154 | + /* Check PHY ID */ |
| 155 | + uint32_t oui; |
| 156 | + uint8_t model; |
| 157 | + ESP_GOTO_ON_ERROR(esp_eth_phy_802_3_read_oui(phy_802_3, &oui), err, TAG, "read OUI failed"); |
| 158 | + ESP_GOTO_ON_ERROR(esp_eth_phy_802_3_read_manufac_info(phy_802_3, &model, NULL), err, TAG, "read manufacturer's info failed"); |
| 159 | + ESP_GOTO_ON_FALSE(oui == 0x24DF10 && model == 0x2, ESP_FAIL, err, TAG, "wrong chip ID"); |
| 160 | + |
| 161 | + return ESP_OK; |
| 162 | +err: |
| 163 | + return ret; |
| 164 | +} |
| 165 | + |
| 166 | +esp_eth_phy_t *esp_eth_phy_new_jl1101(const eth_phy_config_t *config) |
| 167 | +{ |
| 168 | + esp_eth_phy_t *ret = NULL; |
| 169 | + phy_jl1101_t *jl1101 = calloc(1, sizeof(phy_jl1101_t)); |
| 170 | + ESP_GOTO_ON_FALSE(jl1101, NULL, err, TAG, "calloc jl1101 failed"); |
| 171 | + ESP_GOTO_ON_FALSE(esp_eth_phy_802_3_obj_config_init(&jl1101->phy_802_3, config) == ESP_OK, |
| 172 | + NULL, err, TAG, "configuration initialization of PHY 802.3 failed"); |
| 173 | + |
| 174 | + // redefine functions which need to be customized for sake of jl1101 |
| 175 | + jl1101->phy_802_3.parent.init = jl1101_init; |
| 176 | + jl1101->phy_802_3.parent.get_link = jl1101_get_link; |
| 177 | + jl1101->phy_802_3.parent.autonego_ctrl = jl1101_autonego_ctrl; |
| 178 | + jl1101->phy_802_3.parent.loopback = jl1101_loopback; |
| 179 | + |
| 180 | + return &jl1101->phy_802_3.parent; |
| 181 | +err: |
| 182 | + if (jl1101 != NULL) { |
| 183 | + free(jl1101); |
| 184 | + } |
| 185 | + return ret; |
| 186 | +} |
0 commit comments