diff options
author | Stuart Hodgson <smhodgson@solarflare.com> | 2012-05-01 13:50:43 -0400 |
---|---|---|
committer | Ben Hutchings <bhutchings@solarflare.com> | 2012-05-09 22:10:46 -0400 |
commit | c087bd2cfdaf334d7d0c32bd1fcc1a23d5b88973 (patch) | |
tree | 9b0edadd759f4210ad890a686880f85e31f0bc2c | |
parent | 41c3cb6d20f0252308e9796fa4f3dacb4960de91 (diff) |
sfc: Added support for new ethtool APIs for obtaining module eeprom
Currently allows for SFP+ eeprom to be returned using the ethtool API.
This can be extended in future to handle different eeprom formats
and sizes
Signed-off-by: Stuart Hodgson <smhodgson@solarflare.com>
[bwh: Drop redundant validation, comment, whitespace]
Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
-rw-r--r-- | drivers/net/ethernet/sfc/ethtool.c | 35 | ||||
-rw-r--r-- | drivers/net/ethernet/sfc/mcdi_phy.c | 76 | ||||
-rw-r--r-- | drivers/net/ethernet/sfc/net_driver.h | 5 |
3 files changed, 116 insertions, 0 deletions
diff --git a/drivers/net/ethernet/sfc/ethtool.c b/drivers/net/ethernet/sfc/ethtool.c index f22f45f515a8..b0a4558de248 100644 --- a/drivers/net/ethernet/sfc/ethtool.c +++ b/drivers/net/ethernet/sfc/ethtool.c | |||
@@ -1108,6 +1108,39 @@ static int efx_ethtool_set_rxfh_indir(struct net_device *net_dev, | |||
1108 | return 0; | 1108 | return 0; |
1109 | } | 1109 | } |
1110 | 1110 | ||
1111 | static int efx_ethtool_get_module_eeprom(struct net_device *net_dev, | ||
1112 | struct ethtool_eeprom *ee, | ||
1113 | u8 *data) | ||
1114 | { | ||
1115 | struct efx_nic *efx = netdev_priv(net_dev); | ||
1116 | int ret; | ||
1117 | |||
1118 | if (!efx->phy_op || !efx->phy_op->get_module_eeprom) | ||
1119 | return -EOPNOTSUPP; | ||
1120 | |||
1121 | mutex_lock(&efx->mac_lock); | ||
1122 | ret = efx->phy_op->get_module_eeprom(efx, ee, data); | ||
1123 | mutex_unlock(&efx->mac_lock); | ||
1124 | |||
1125 | return ret; | ||
1126 | } | ||
1127 | |||
1128 | static int efx_ethtool_get_module_info(struct net_device *net_dev, | ||
1129 | struct ethtool_modinfo *modinfo) | ||
1130 | { | ||
1131 | struct efx_nic *efx = netdev_priv(net_dev); | ||
1132 | int ret; | ||
1133 | |||
1134 | if (!efx->phy_op || !efx->phy_op->get_module_info) | ||
1135 | return -EOPNOTSUPP; | ||
1136 | |||
1137 | mutex_lock(&efx->mac_lock); | ||
1138 | ret = efx->phy_op->get_module_info(efx, modinfo); | ||
1139 | mutex_unlock(&efx->mac_lock); | ||
1140 | |||
1141 | return ret; | ||
1142 | } | ||
1143 | |||
1111 | const struct ethtool_ops efx_ethtool_ops = { | 1144 | const struct ethtool_ops efx_ethtool_ops = { |
1112 | .get_settings = efx_ethtool_get_settings, | 1145 | .get_settings = efx_ethtool_get_settings, |
1113 | .set_settings = efx_ethtool_set_settings, | 1146 | .set_settings = efx_ethtool_set_settings, |
@@ -1137,4 +1170,6 @@ const struct ethtool_ops efx_ethtool_ops = { | |||
1137 | .get_rxfh_indir_size = efx_ethtool_get_rxfh_indir_size, | 1170 | .get_rxfh_indir_size = efx_ethtool_get_rxfh_indir_size, |
1138 | .get_rxfh_indir = efx_ethtool_get_rxfh_indir, | 1171 | .get_rxfh_indir = efx_ethtool_get_rxfh_indir, |
1139 | .set_rxfh_indir = efx_ethtool_set_rxfh_indir, | 1172 | .set_rxfh_indir = efx_ethtool_set_rxfh_indir, |
1173 | .get_module_info = efx_ethtool_get_module_info, | ||
1174 | .get_module_eeprom = efx_ethtool_get_module_eeprom, | ||
1140 | }; | 1175 | }; |
diff --git a/drivers/net/ethernet/sfc/mcdi_phy.c b/drivers/net/ethernet/sfc/mcdi_phy.c index 7bcad899a936..13cb40fe90c1 100644 --- a/drivers/net/ethernet/sfc/mcdi_phy.c +++ b/drivers/net/ethernet/sfc/mcdi_phy.c | |||
@@ -739,6 +739,80 @@ static const char *efx_mcdi_phy_test_name(struct efx_nic *efx, | |||
739 | return NULL; | 739 | return NULL; |
740 | } | 740 | } |
741 | 741 | ||
742 | #define SFP_PAGE_SIZE 128 | ||
743 | #define SFP_NUM_PAGES 2 | ||
744 | static int efx_mcdi_phy_get_module_eeprom(struct efx_nic *efx, | ||
745 | struct ethtool_eeprom *ee, u8 *data) | ||
746 | { | ||
747 | u8 outbuf[MC_CMD_GET_PHY_MEDIA_INFO_OUT_LENMAX]; | ||
748 | u8 inbuf[MC_CMD_GET_PHY_MEDIA_INFO_IN_LEN]; | ||
749 | size_t outlen; | ||
750 | int rc; | ||
751 | unsigned int payload_len; | ||
752 | unsigned int space_remaining = ee->len; | ||
753 | unsigned int page; | ||
754 | unsigned int page_off; | ||
755 | unsigned int to_copy; | ||
756 | u8 *user_data = data; | ||
757 | |||
758 | BUILD_BUG_ON(SFP_PAGE_SIZE * SFP_NUM_PAGES != ETH_MODULE_SFF_8079_LEN); | ||
759 | |||
760 | page_off = ee->offset % SFP_PAGE_SIZE; | ||
761 | page = ee->offset / SFP_PAGE_SIZE; | ||
762 | |||
763 | while (space_remaining && (page < SFP_NUM_PAGES)) { | ||
764 | MCDI_SET_DWORD(inbuf, GET_PHY_MEDIA_INFO_IN_PAGE, page); | ||
765 | |||
766 | rc = efx_mcdi_rpc(efx, MC_CMD_GET_PHY_MEDIA_INFO, | ||
767 | inbuf, sizeof(inbuf), | ||
768 | outbuf, sizeof(outbuf), | ||
769 | &outlen); | ||
770 | if (rc) | ||
771 | return rc; | ||
772 | |||
773 | if (outlen < (MC_CMD_GET_PHY_MEDIA_INFO_OUT_DATA_OFST + | ||
774 | SFP_PAGE_SIZE)) | ||
775 | return -EIO; | ||
776 | |||
777 | payload_len = MCDI_DWORD(outbuf, | ||
778 | GET_PHY_MEDIA_INFO_OUT_DATALEN); | ||
779 | if (payload_len != SFP_PAGE_SIZE) | ||
780 | return -EIO; | ||
781 | |||
782 | /* Copy as much as we can into data */ | ||
783 | payload_len -= page_off; | ||
784 | to_copy = (space_remaining < payload_len) ? | ||
785 | space_remaining : payload_len; | ||
786 | |||
787 | memcpy(user_data, | ||
788 | outbuf + page_off + | ||
789 | MC_CMD_GET_PHY_MEDIA_INFO_OUT_DATA_OFST, | ||
790 | to_copy); | ||
791 | |||
792 | space_remaining -= to_copy; | ||
793 | user_data += to_copy; | ||
794 | page_off = 0; | ||
795 | page++; | ||
796 | } | ||
797 | |||
798 | return 0; | ||
799 | } | ||
800 | |||
801 | static int efx_mcdi_phy_get_module_info(struct efx_nic *efx, | ||
802 | struct ethtool_modinfo *modinfo) | ||
803 | { | ||
804 | struct efx_mcdi_phy_data *phy_cfg = efx->phy_data; | ||
805 | |||
806 | switch (phy_cfg->media) { | ||
807 | case MC_CMD_MEDIA_SFP_PLUS: | ||
808 | modinfo->type = ETH_MODULE_SFF_8079; | ||
809 | modinfo->eeprom_len = ETH_MODULE_SFF_8079_LEN; | ||
810 | return 0; | ||
811 | default: | ||
812 | return -EOPNOTSUPP; | ||
813 | } | ||
814 | } | ||
815 | |||
742 | const struct efx_phy_operations efx_mcdi_phy_ops = { | 816 | const struct efx_phy_operations efx_mcdi_phy_ops = { |
743 | .probe = efx_mcdi_phy_probe, | 817 | .probe = efx_mcdi_phy_probe, |
744 | .init = efx_port_dummy_op_int, | 818 | .init = efx_port_dummy_op_int, |
@@ -751,4 +825,6 @@ const struct efx_phy_operations efx_mcdi_phy_ops = { | |||
751 | .test_alive = efx_mcdi_phy_test_alive, | 825 | .test_alive = efx_mcdi_phy_test_alive, |
752 | .run_tests = efx_mcdi_phy_run_tests, | 826 | .run_tests = efx_mcdi_phy_run_tests, |
753 | .test_name = efx_mcdi_phy_test_name, | 827 | .test_name = efx_mcdi_phy_test_name, |
828 | .get_module_eeprom = efx_mcdi_phy_get_module_eeprom, | ||
829 | .get_module_info = efx_mcdi_phy_get_module_info, | ||
754 | }; | 830 | }; |
diff --git a/drivers/net/ethernet/sfc/net_driver.h b/drivers/net/ethernet/sfc/net_driver.h index eaca447e2a2b..0e575359af17 100644 --- a/drivers/net/ethernet/sfc/net_driver.h +++ b/drivers/net/ethernet/sfc/net_driver.h | |||
@@ -519,6 +519,11 @@ struct efx_phy_operations { | |||
519 | int (*test_alive) (struct efx_nic *efx); | 519 | int (*test_alive) (struct efx_nic *efx); |
520 | const char *(*test_name) (struct efx_nic *efx, unsigned int index); | 520 | const char *(*test_name) (struct efx_nic *efx, unsigned int index); |
521 | int (*run_tests) (struct efx_nic *efx, int *results, unsigned flags); | 521 | int (*run_tests) (struct efx_nic *efx, int *results, unsigned flags); |
522 | int (*get_module_eeprom) (struct efx_nic *efx, | ||
523 | struct ethtool_eeprom *ee, | ||
524 | u8 *data); | ||
525 | int (*get_module_info) (struct efx_nic *efx, | ||
526 | struct ethtool_modinfo *modinfo); | ||
522 | }; | 527 | }; |
523 | 528 | ||
524 | /** | 529 | /** |