// SPDX-License-Identifier: GPL-2.0+ /* Copyright (c) 2015 Broadcom Corporation All Rights Reserved */ /* * Created on: Dec 2015 * Author: yuval.raviv@broadcom.com */ #ifndef __PHY_DRV_H__ #define __PHY_DRV_H__ #include "bus_drv.h" #include "dt_access.h" #define PHY_CAP_10_HALF (1 << 0) #define PHY_CAP_10_FULL (1 << 1) #define PHY_CAP_100_HALF (1 << 2) #define PHY_CAP_100_FULL (1 << 3) #define PHY_CAP_1000_HALF (1 << 4) #define PHY_CAP_1000_FULL (1 << 5) #define PHY_CAP_2500 (1 << 6) #define PHY_CAP_5000 (1 << 7) #define PHY_CAP_10000 (1 << 8) #define PHY_CAP_AUTONEG (1 << 9) #define PHY_CAP_PAUSE (1 << 10) #define PHY_CAP_PAUSE_ASYM (1 << 11) #define PHY_CAP_REPEATER (1 << 12) #define PHY_CAP_LAST (1 << 13) #define PHY_CAP_ALL (PHY_CAP_LAST -1) typedef enum { PHY_SPEED_UNKNOWN, PHY_SPEED_AUTO = PHY_SPEED_UNKNOWN, PHY_SPEED_10, PHY_SPEED_100, PHY_SPEED_1000, PHY_SPEED_2500, PHY_SPEED_5000, PHY_SPEED_10000, } phy_speed_t; typedef enum { PHY_DUPLEX_UNKNOWN, PHY_DUPLEX_HALF, PHY_DUPLEX_FULL, } phy_duplex_t; typedef enum { PHY_MII_TYPE_UNKNOWN, PHY_MII_TYPE_MII, PHY_MII_TYPE_TMII, PHY_MII_TYPE_GMII, PHY_MII_TYPE_RGMII, PHY_MII_TYPE_SGMII, PHY_MII_TYPE_HSGMII, PHY_MII_TYPE_XFI, PHY_MII_TYPE_SERDES, PHY_MII_TYPE_LAST, } phy_mii_type_t; static inline const char *phy_mii_type(phy_mii_type_t interface) { switch (interface) { case PHY_MII_TYPE_UNKNOWN: return ""; case PHY_MII_TYPE_MII: return "mii"; case PHY_MII_TYPE_TMII: return "tmii"; case PHY_MII_TYPE_GMII: return "gmii"; case PHY_MII_TYPE_RGMII: return "rgmii"; case PHY_MII_TYPE_SGMII: return "sgmii"; case PHY_MII_TYPE_HSGMII: return "hsgmii"; case PHY_MII_TYPE_XFI: return "xfi"; case PHY_MII_TYPE_SERDES: return "serdes"; default: return "unknown"; } } typedef enum { PHY_TYPE_UNKNOWN, PHY_TYPE_6858_EGPHY, PHY_TYPE_6846_EGPHY, PHY_TYPE_6856_SGMII, PHY_TYPE_EXT1, PHY_TYPE_EXT2, PHY_TYPE_EXT3, PHY_TYPE_LPORT_SERDES, PHY_TYPE_53125, PHY_TYPE_PON, PHY_TYPE_SF2_GPHY, PHY_TYPE_SF2_CL45_PHY, PHY_TYPE_SF2_SERDES, PHY_TYPE_I2C_PHY, PHY_TYPE_XGAE, PHY_TYPE_CROSSBAR, PHY_TYPE_MAC2MAC, PHY_TYPE_G9991, PHY_TYPE_63146_EGPHY, PHY_TYPE_MAX, } phy_type_t; typedef void (*link_change_cb_t)(void *ctx); /* Phy device */ typedef struct phy_dev_s { struct phy_drv_s *phy_drv; bus_drv_t *bus_drv; phy_mii_type_t mii_type; link_change_cb_t link_change_cb; void *link_change_ctx; uint32_t addr; // contains phy address only uint32_t meta_id; // contains extra phyId info from board param void *priv; int link; phy_speed_t speed; phy_duplex_t duplex; int caps_mask; int pause_rx; int pause_tx; int delay_rx; int delay_tx; int swap_pair; int flag; int loopback_save; int reset_gpio; int reset_gpio_active_hi; int idle_stuffing; void *macsec_dev; /* For cascaded PHY */ void *sw_port; struct phy_dev_s *cascade_next; struct phy_dev_s *cascade_prev; dt_handle_t dt_handle; dt_gpio_desc reset_gpiod; } phy_dev_t; #define PHY_FLAG_NOT_PRESENTED (1<<0) /* for SFP module indicating not inserted */ #define PHY_FLAG_POWER_SET_ENABLED (1<<1) #define PHY_FLAG_DYNAMIC (1<<2) #define PHY_FLAG_CABLE_DIAG_ENABLED (1<<3) #define PHY_FLAG_TO_EXTSW (1<<4) #define PHY_FLAG_CABLE_DIAG_INITED (1<<5) #define CAPS_TYPE_ADVERTISE 0 #define CAPS_TYPE_SUPPORTED 1 #define CAPS_TYPE_LP_ADVERTISED 2 typedef enum { INTER_PHY_TYPE_100MBASE_X, INTER_PHY_TYPE_1GBASE_X, INTER_PHY_TYPE_2P5GBASE_X, INTER_PHY_TYPE_2P5GBASE_R, INTER_PHY_TYPE_2P5GIDLE, INTER_PHY_TYPE_5GBASE_R, INTER_PHY_TYPE_5GBASE_X, INTER_PHY_TYPE_5GIDLE, INTER_PHY_TYPE_10GBASE_R, INTER_PHY_TYPE_SGMII, INTER_PHY_TYPE_USXGMII, INTER_PHY_TYPE_UNKNOWN, } inter_phy_type_t; static uint32_t inter_phy_supported_speed_caps[] = { [INTER_PHY_TYPE_100MBASE_X] = PHY_CAP_100_FULL, [INTER_PHY_TYPE_1GBASE_X] = PHY_CAP_1000_FULL, [INTER_PHY_TYPE_2P5GBASE_X] = PHY_CAP_2500, [INTER_PHY_TYPE_2P5GBASE_R] = PHY_CAP_2500, [INTER_PHY_TYPE_2P5GIDLE] = PHY_CAP_2500, [INTER_PHY_TYPE_5GBASE_R] = PHY_CAP_5000, [INTER_PHY_TYPE_5GBASE_X] = PHY_CAP_5000, [INTER_PHY_TYPE_5GIDLE] = PHY_CAP_5000, [INTER_PHY_TYPE_10GBASE_R] = PHY_CAP_10000, [INTER_PHY_TYPE_SGMII] = PHY_CAP_100_FULL | PHY_CAP_1000_FULL, [INTER_PHY_TYPE_USXGMII] = PHY_CAP_100_FULL | PHY_CAP_1000_FULL | PHY_CAP_2500 | PHY_CAP_5000, PHY_CAP_10000, }; #define INTER_PHY_TYPE_100MBASE_X_M (1<cascade_prev || phy->cascade_next) #define cascade_phy_get_next(phy) ((phy->cascade_next && !(phy->cascade_next->flag & PHY_FLAG_NOT_PRESENTED))? phy->cascade_next : NULL) #define cascade_phy_get_prev(phy) (phy->cascade_prev? phy->cascade_prev : NULL) static inline phy_dev_t *cascade_phy_get_first(phy_dev_t *phy_dev) { phy_dev_t *phy; for(phy=phy_dev; phy->cascade_prev; phy=phy->cascade_prev); return phy; } static inline phy_dev_t *cascade_phy_get_last(phy_dev_t *phy_dev) { phy_dev_t *phy; if (!phy_dev) return NULL; for(phy=phy_dev; phy->cascade_next && !(phy->cascade_next->flag & PHY_FLAG_NOT_PRESENTED); phy=phy->cascade_next); return phy; } static inline int phy_bus_read(phy_dev_t *phy_dev, uint16_t reg, uint16_t *val) { if (!phy_dev->bus_drv) return 0; return phy_dev->bus_drv->c22_read(phy_dev->addr, reg, val); } static inline int phy_bus_write(phy_dev_t *phy_dev, uint16_t reg, uint16_t val) { if (!phy_dev->bus_drv) return 0; return phy_dev->bus_drv->c22_write(phy_dev->addr, reg, val); } static inline int phy_bus_c45_read(phy_dev_t *phy_dev, uint16_t dev, uint16_t reg, uint16_t *val) { if (!phy_dev->bus_drv) return 0; return phy_dev->bus_drv->c45_read(phy_dev->addr, dev, reg, val); } static inline int phy_bus_c45_write(phy_dev_t *phy_dev, uint16_t dev, uint16_t reg, uint16_t val) { if (!phy_dev->bus_drv) return 0; return phy_dev->bus_drv->c45_write(phy_dev->addr, dev, reg, val); } static inline int phy_dev_read(phy_dev_t *phy_dev, uint16_t reg, uint16_t *val) { if (phy_dev->phy_drv->read) return phy_dev->phy_drv->read(phy_dev, reg, val); else return phy_bus_read(phy_dev, reg, val); } static inline int phy_dev_write(phy_dev_t *phy_dev, uint16_t reg, uint16_t val) { if (phy_dev->phy_drv->write) return phy_dev->phy_drv->write(phy_dev, reg, val); else return phy_bus_write(phy_dev, reg, val); } static inline int phy_dev_isolate_phy(phy_dev_t *phy_dev, int isolate) { if (!phy_dev->phy_drv->isolate_phy) return 0; return phy_dev->phy_drv->isolate_phy(phy_dev, isolate); } static inline int phy_dev_super_isolate_phy(phy_dev_t *phy_dev, int isolate) { if (!phy_dev->phy_drv->super_isolate_phy) return 0; return phy_dev->phy_drv->super_isolate_phy(phy_dev, isolate); } static inline int phy_dev_pair_swap_set(phy_dev_t *phy_dev, int enable) { if (!phy_dev->phy_drv->pair_swap_set) return 0; return phy_dev->phy_drv->pair_swap_set(phy_dev, enable); } static inline int phy_dev_power_get(phy_dev_t *phy_dev, int *enable) { *enable = 0; if (!phy_dev->phy_drv->power_get) return 0; return phy_dev->phy_drv->power_get(phy_dev, enable); } static inline int phy_dev_power_set(phy_dev_t *phy_dev, int enable) { if (is_cascade_phy(phy_dev)) { phy_dev_t *cascade; for (cascade = cascade_phy_get_first(phy_dev); cascade; cascade = cascade_phy_get_next(cascade)) { //printk("phy %s:%d power_set=%d\n", (phy_dev->phy_drv) ? cascade->phy_drv->name : NULL, cascade->addr, enable); if (enable) cascade->flag |= PHY_FLAG_POWER_SET_ENABLED; else cascade->flag &= ~PHY_FLAG_POWER_SET_ENABLED; if (cascade->phy_drv->power_set) cascade->phy_drv->power_set(cascade, enable); } return 0; } //printk("phy %s:%d power_set=%d\n", (phy_dev->phy_drv) ? phy_dev->phy_drv->name : NULL, phy_dev->addr, enable); if (enable) phy_dev->flag |= PHY_FLAG_POWER_SET_ENABLED; else phy_dev->flag &= ~PHY_FLAG_POWER_SET_ENABLED; if (!phy_dev->phy_drv->power_set) return 0; return phy_dev->phy_drv->power_set(phy_dev, enable); } static inline int phy_dev_apd_get(phy_dev_t *phy_dev, int *enable) { *enable = 0; if (!phy_dev->phy_drv->apd_get) return 0; return phy_dev->phy_drv->apd_get(phy_dev, enable); } static inline int phy_dev_apd_set(phy_dev_t *phy_dev, int enable) { if (!phy_dev->phy_drv->apd_set) return 0; return phy_dev->phy_drv->apd_set(phy_dev, enable); } static inline int phy_dev_cable_diag_run(phy_dev_t *phy_dev, int *result, int *pair_len) { if (!phy_dev->phy_drv->cable_diag_run) return -1; return phy_dev->phy_drv->cable_diag_run(phy_dev, result, pair_len); } static inline int phy_dev_cable_diag_is_supported(phy_dev_t *phy_dev) { return phy_dev->phy_drv->cable_diag_run != NULL; } static inline int phy_dev_cable_diag_is_enabled(phy_dev_t *phy_dev) { return (phy_dev->flag & PHY_FLAG_CABLE_DIAG_ENABLED) > 0; } static inline int phy_dev_cable_diag_set(phy_dev_t *phy_dev, int enable) { if (!phy_dev->phy_drv->cable_diag_run) return -1; phy_dev->flag &= ~PHY_FLAG_CABLE_DIAG_ENABLED; phy_dev->flag |= enable? PHY_FLAG_CABLE_DIAG_ENABLED: 0; if (!phy_dev->phy_drv->cable_diag_set) return 0; return phy_dev->phy_drv->cable_diag_set(phy_dev, enable); } static inline int phy_dev_cable_diag_get(phy_dev_t *phy_dev, int *enable) { if (!phy_dev->phy_drv->cable_diag_run) return -1; *enable = (phy_dev->flag & PHY_FLAG_CABLE_DIAG_ENABLED) > 0; return 0; } static inline int phy_dev_eee_get(phy_dev_t *phy_dev, int *enable) { *enable = 0; if (!phy_dev->phy_drv->eee_get) return 0; return phy_dev->phy_drv->eee_get(phy_dev, enable); } static inline int phy_dev_eee_set(phy_dev_t *phy_dev, int enable) { if (!phy_dev->phy_drv->eee_set) return 0; return phy_dev->phy_drv->eee_set(phy_dev, enable); } static inline int phy_dev_eee_resolution_get(phy_dev_t *phy_dev, int *enable) { *enable = 0; if (!phy_dev->phy_drv->eee_resolution_get) return 0; return phy_dev->phy_drv->eee_resolution_get(phy_dev, enable); } static inline int phy_dev_read_status(phy_dev_t *phy_dev) { int ret = 0; #if !defined(DSL_DEVICES) phy_speed_t speed = phy_dev->speed; #endif if (!phy_dev->phy_drv->read_status) goto Exit; if ((ret = phy_dev->phy_drv->read_status(phy_dev))) goto Exit; #if !defined(DSL_DEVICES) /* DSL product has revert chain direction due to dyanmic module support */ if (phy_dev->speed == speed) goto Exit; if (!phy_dev->cascade_prev) goto Exit; if (!phy_dev->cascade_prev->phy_drv->speed_set) goto Exit; if ((ret = phy_dev->cascade_prev->phy_drv->speed_set(phy_dev->cascade_prev, phy_dev->speed, phy_dev->duplex))) goto Exit; #endif #if defined(DSL_DEVICES) // administratively force link down if ethernet phy is not in power enable state if (phy_dev->phy_drv->phy_type != PHY_TYPE_PON && !(phy_dev->flag & PHY_FLAG_POWER_SET_ENABLED) && !(phy_dev->flag & PHY_FLAG_TO_EXTSW)) { phy_dev->link = 0; } #endif Exit: return ret; } static inline int phy_dev_config_speed_get(phy_dev_t *phy_dev, phy_speed_t *speed, phy_duplex_t *duplex) { if (!phy_dev->phy_drv->config_speed_get) return 0; return phy_dev->phy_drv->config_speed_get(phy_dev, speed, duplex); } static inline int phy_dev_speed_set(phy_dev_t *phy_dev, phy_speed_t speed, phy_duplex_t duplex) { if (!phy_dev->phy_drv->speed_set) return 0; return phy_dev->phy_drv->speed_set(phy_dev, speed, duplex); } static inline int phy_dev_auto_mdix_set(phy_dev_t *phy_dev, int enable) { if (!phy_dev->phy_drv->auto_mdix_set) return -1; return phy_dev->phy_drv->auto_mdix_set(phy_dev, enable); } static inline int phy_dev_auto_mdix_get(phy_dev_t *phy_dev, int *enable) { if (!phy_dev->phy_drv->auto_mdix_get) return -1; return phy_dev->phy_drv->auto_mdix_get(phy_dev, enable); } static inline int phy_dev_wirespeed_set(phy_dev_t *phy_dev, int enable) { if (!phy_dev->phy_drv->wirespeed_set) return -1; return phy_dev->phy_drv->wirespeed_set(phy_dev, enable); } static inline int phy_dev_wirespeed_get(phy_dev_t *phy_dev, int *enable) { if (!phy_dev->phy_drv->wirespeed_get) return -1; return phy_dev->phy_drv->wirespeed_get(phy_dev, enable); } static inline void phy_dev_status_propagate(phy_dev_t *end_phy) { phy_dev_t *phy_dev; for (phy_dev = end_phy->cascade_prev; phy_dev; phy_dev = phy_dev->cascade_prev) { phy_dev_speed_set(phy_dev, end_phy->speed, end_phy->duplex); phy_dev->link = end_phy->link; phy_dev->speed = end_phy->speed; phy_dev->duplex = end_phy->duplex; } } /* For propagating status toward end PHY for status only without changing configuration during the link up due to the status call-back is done in internal PHY */ static inline void phy_dev_status_reverse_propagate(phy_dev_t *end_phy) { phy_dev_t *phy_dev; for (phy_dev = end_phy->cascade_next; phy_dev && !(phy_dev->flag & PHY_FLAG_NOT_PRESENTED); phy_dev = phy_dev->cascade_next) { phy_dev->link = end_phy->link; phy_dev->speed = end_phy->speed; phy_dev->duplex = end_phy->duplex; } } static inline int phy_dev_caps_set(phy_dev_t *phy_dev, uint32_t caps) { if (!phy_dev->phy_drv->caps_set) return 0; if (phy_dev->caps_mask) caps &= phy_dev->caps_mask; return phy_dev->phy_drv->caps_set(phy_dev, caps); } static inline int phy_dev_caps_get(phy_dev_t *phy_dev, int caps_type, uint32_t *caps) { int ret; *caps = 0; if (!phy_dev->phy_drv->caps_get) return 0; ret = phy_dev->phy_drv->caps_get(phy_dev, caps_type, caps); if (caps_type == CAPS_TYPE_SUPPORTED && phy_dev->caps_mask) *caps &= phy_dev->caps_mask; return ret; } static inline int phy_dev_inter_phy_types_get(phy_dev_t *phy_dev, inter_phy_types_dir_t if_dir, uint32_t *types) { *types = INTER_PHY_TYPES_UNKNOWN_M; if (!phy_dev->phy_drv->inter_phy_types_get) return 0; return phy_dev->phy_drv->inter_phy_types_get(phy_dev, if_dir, types); } static inline int phy_dev_inter_phy_types_set(phy_dev_t *phy_dev, inter_phy_types_dir_t if_dir, uint32_t types) { if (!phy_dev->phy_drv->inter_phy_types_set) return 0; return phy_dev->phy_drv->inter_phy_types_set(phy_dev, if_dir, types); } static inline phy_speed_t phy_caps_to_max_speed(uint32_t caps) { int i; static int speed[] = {PHY_CAP_10000, PHY_SPEED_10000, PHY_CAP_5000, PHY_SPEED_5000, PHY_CAP_2500, PHY_SPEED_2500, PHY_CAP_1000_FULL, PHY_SPEED_1000, PHY_CAP_1000_HALF, PHY_SPEED_1000, PHY_CAP_100_FULL, PHY_SPEED_100, PHY_CAP_100_HALF, PHY_SPEED_100, PHY_CAP_10_FULL, PHY_SPEED_10, PHY_CAP_10_HALF, PHY_SPEED_10}; for (i=0; i>= 1; return cap; } static inline int phy_dev_phyid_get(phy_dev_t *phy_dev, uint32_t *phyid) { *phyid = 0; if (!phy_dev->phy_drv->phyid_get) return 0; return phy_dev->phy_drv->phyid_get(phy_dev, phyid); } static inline int phy_dev_init(phy_dev_t *first_phy) { int rc = 0; phy_dev_t *phy_dev; for (phy_dev = cascade_phy_get_first(first_phy); phy_dev; phy_dev = phy_dev->cascade_next) { phy_dev->link = 0; phy_dev->speed = PHY_SPEED_UNKNOWN; phy_dev->duplex = PHY_DUPLEX_UNKNOWN; if (phy_dev->phy_drv->init != NULL) rc |= phy_dev->phy_drv->init(phy_dev); if (phy_dev->phy_drv->phy_type != PHY_TYPE_CROSSBAR) { if (phy_dev->phy_drv->phy_type != PHY_TYPE_SF2_SERDES && phy_dev->phy_drv->phy_type != PHY_TYPE_SF2_CL45_PHY) rc |= phy_dev_apd_set(phy_dev, 1); rc |= phy_dev_eee_set(phy_dev, 1); } } return rc; } static inline int phy_dev_leds_init(phy_dev_t *first_phy, void *leds_info) { int rc = 0; phy_dev_t *phy_dev; for (phy_dev = cascade_phy_get_first(first_phy); phy_dev; phy_dev = phy_dev->cascade_next) { if (phy_dev->phy_drv->leds_init != NULL) rc |= phy_dev->phy_drv->leds_init(phy_dev, leds_info); } return rc; } static inline int cascade_phy_dev_isolate_phy(phy_dev_t *phy_dev, int isolate) { if (is_cascade_phy(phy_dev)) { int rc = 0; phy_dev_t *cascade; for (cascade = cascade_phy_get_first(phy_dev); cascade; cascade = cascade_phy_get_next(cascade)) rc |= phy_dev_isolate_phy(cascade, isolate); return rc; } return phy_dev_isolate_phy(phy_dev, isolate); } static inline int cascade_phy_dev_apd_set(phy_dev_t *phy_dev, int enable) { if (is_cascade_phy(phy_dev)) { int rc = 0; phy_dev_t *cascade; for (cascade = cascade_phy_get_first(phy_dev); cascade; cascade = cascade_phy_get_next(cascade)) rc |= phy_dev_apd_set(cascade, enable); return rc; } return phy_dev_apd_set(phy_dev, enable); } static inline int cascade_phy_dev_eee_set(phy_dev_t *phy_dev, int enable) { if (is_cascade_phy(phy_dev)) { int rc = 0; phy_dev_t *cascade; for (cascade = cascade_phy_get_first(phy_dev); cascade; cascade = cascade_phy_get_next(cascade)) rc |= phy_dev_eee_set(cascade, enable); return rc; } return phy_dev_eee_set(phy_dev, enable); } static inline int cascade_phy_dev_eee_get(phy_dev_t *phy_dev, int *enable) { int val; if (is_cascade_phy(phy_dev)) { int rc = 0; phy_dev_t *cascade; *enable = 0; for (cascade = cascade_phy_get_first(phy_dev); cascade; cascade = cascade_phy_get_next(cascade)) { rc |= phy_dev_eee_get(cascade, &val); *enable |= val; } return rc; } return phy_dev_eee_get(phy_dev, enable); } static inline int cascade_phy_dev_eee_resolution_get(phy_dev_t *phy_dev, int *enable) { int val; if (is_cascade_phy(phy_dev)) { int rc = 0; phy_dev_t *cascade; *enable = 0; for (cascade = cascade_phy_get_first(phy_dev); cascade; cascade = cascade_phy_get_next(cascade)) { rc |= phy_dev_eee_resolution_get(cascade, &val); *enable |= val; } return rc; } return phy_dev_eee_resolution_get(phy_dev, enable); } static inline int cascade_phy_dev_speed_set(phy_dev_t *phy_dev, phy_speed_t speed, phy_duplex_t duplex) { if (is_cascade_phy(phy_dev)) { int rc = 0; phy_dev_t *cascade; for (cascade = cascade_phy_get_first(phy_dev); cascade; cascade = cascade_phy_get_next(cascade)) { rc |= phy_dev_speed_set(cascade, speed, duplex); } return rc; } return phy_dev_speed_set(phy_dev, speed, duplex); } // find minimum caps static inline int cascade_phy_dev_caps_get(phy_dev_t *phy_dev, int caps_type, uint32_t *caps) { int rc = 0; phy_dev_t *cascade; uint32_t cascade_caps; uint32_t inter_phy_types, inter_phy_types_last; uint32_t inter_phy_supported_speed_caps; *caps = 0; inter_phy_types = inter_phy_types_last = 0; if (is_cascade_phy(phy_dev)) { for (cascade = cascade_phy_get_first(phy_dev), *caps = 0; cascade; cascade = cascade_phy_get_next(cascade)) { if (cascade->phy_drv->caps_get) { rc |= cascade->phy_drv->caps_get(cascade, caps_type, &cascade_caps); if (cascade_caps) { *caps = (*caps) ? ((*caps & cascade_caps) | (cascade_caps & (PHY_CAP_PAUSE|PHY_CAP_PAUSE_ASYM))) : cascade_caps; if (!inter_phy_types_last) { rc |= phy_dev_inter_phy_types_get(cascade, INTER_PHY_TYPE_DOWN, &inter_phy_types); inter_phy_types_last = inter_phy_types; } else { rc |= phy_dev_inter_phy_types_get(cascade, INTER_PHY_TYPE_UP, &inter_phy_types); inter_phy_types &= inter_phy_types_last; } rc |= get_inter_phy_supported_speed_caps(inter_phy_types, &inter_phy_supported_speed_caps); *caps = (*caps & inter_phy_supported_speed_caps) | (*caps & PHY_CAP_AUTONEG); } } } return rc; } return phy_dev_caps_get(phy_dev, caps_type, caps); } static inline int cascade_phy_dev_caps_set(phy_dev_t *phy_dev, uint32_t caps) { if (is_cascade_phy(phy_dev)) { int rc = 0; phy_dev_t *cascade; for (cascade = cascade_phy_get_first(phy_dev); cascade; cascade = cascade_phy_get_next(cascade)) rc |= phy_dev_caps_set(cascade, caps); return rc; } return phy_dev_caps_set(phy_dev, caps); } /* Return if the phy_speed_t is covered by the PHY Speed CAP */ static inline int phy_dev_cap_speed_match(uint32_t caps, phy_speed_t speed) { switch (speed) { case PHY_SPEED_10000: return ((caps & PHY_CAP_10000)>0); case PHY_SPEED_5000: return ((caps & PHY_CAP_5000)>0); case PHY_SPEED_2500: return ((caps & PHY_CAP_2500)>0); case PHY_SPEED_1000: return ((caps & PHY_CAP_1000_FULL)>0); case PHY_SPEED_100: return ((caps & PHY_CAP_100_FULL)>0); case PHY_SPEED_10: return ((caps & PHY_CAP_10_FULL)>0); default: break; } return 0; } static inline phy_speed_t cascade_phy_max_speed_get(phy_dev_t *phy_dev) { uint32_t caps; if (cascade_phy_dev_caps_get(phy_dev, CAPS_TYPE_SUPPORTED, &caps)) return 0; return phy_caps_to_max_speed(caps); } static inline phy_speed_t phy_max_speed_get(phy_dev_t *phy_dev) { uint32_t caps; if (phy_dev_caps_get(phy_dev, CAPS_TYPE_SUPPORTED, &caps)) return 0; return phy_caps_to_max_speed(caps); } static inline int cascade_phy_dev_power_set(phy_dev_t *phy_dev, int enable) { // current phy_dev_power_set() already handle cascade return phy_dev_power_set(phy_dev, enable); } /* Get last non dynamic PHY */ static inline phy_dev_t *cascade_phy_get_last_active(phy_dev_t *phy_dev) { phy_dev_t *phy = cascade_phy_get_last(phy_dev); if (phy && (phy->flag & PHY_FLAG_DYNAMIC)) phy = phy->cascade_prev; return phy; } static inline void _phy_register_polling_timer(phy_dev_t *phy, link_change_cb_t cb, int _register) { phy_dev_t *end_phy = cascade_phy_get_last_active(phy); if (_register) phy_dev_link_change_register(end_phy, cb, end_phy); else phy_dev_link_change_unregister(end_phy); } #define phy_register_polling_timer(phy, cb) _phy_register_polling_timer(phy, cb, 1) #define phy_unregister_polling_timer(phy) _phy_register_polling_timer(phy, 0, 0) static inline int phy_drv_dev_add(phy_dev_t *phy_dev) { if (phy_dev->phy_drv->initialized) return 0; if (!phy_dev->phy_drv->dev_add) return 0; return phy_dev->phy_drv->dev_add(phy_dev); } static inline int phy_drv_dev_del(phy_dev_t *phy_dev) { if (phy_dev->phy_drv->initialized) return 0; if (!phy_dev->phy_drv->dev_del) return 0; return phy_dev->phy_drv->dev_del(phy_dev); } static inline int phy_drv_init(phy_drv_t *phy_drv) { if (phy_drv->initialized) return 0; if (!phy_drv->drv_init) return 0; return phy_drv->drv_init(phy_drv); } static inline int phy_loopback_set(phy_dev_t *phy_dev, int enable, phy_speed_t speed) { phy_drv_t *phy_drv = phy_dev->phy_drv; if(!phy_drv->loopback_set) return 0; return phy_drv->loopback_set(phy_dev, enable, speed); } static inline int phy_loopback_get(phy_dev_t *phy_dev, int *enable, phy_speed_t *speed) { phy_drv_t *phy_drv = phy_dev->phy_drv; if(!phy_drv->loopback_get) return 0; return phy_drv->loopback_get(phy_dev, enable, speed); } static inline int phy_speed_2_mbps(phy_speed_t speed) { int i; int speeds[] = {PHY_SPEED_10000, 10000, PHY_SPEED_5000, 5000, PHY_SPEED_2500, 2500, PHY_SPEED_1000, 1000, PHY_SPEED_100, 100, PHY_SPEED_10, 10}; for (i=0; iphy_drv->macsec_oper) return 0; return phy_dev->phy_drv->macsec_oper(phy_dev, data); } #endif