diff options
author | Paul M Stillwell Jr <paul.m.stillwell.jr@intel.com> | 2014-06-04 01:35:54 -0400 |
---|---|---|
committer | Jeff Kirsher <jeffrey.t.kirsher@intel.com> | 2014-06-26 07:44:57 -0400 |
commit | 3ba3faeb62220411284551a6443395ce7960b17d (patch) | |
tree | 63892bf8f4377bb19930813a83ae57e9326c8176 /drivers/net | |
parent | 7a208e83fcedc0b845facc17d05ead0b9b73a967 (diff) |
i40e/i40evf: Big endian fixes for handling HMC
Fix HMC handling for big endian architectures.
Change-ID: Id8c46fc341815d47bfe0af8b819f0ab9a1e9e515
Signed-off-by: Paul M Stillwell Jr <paul.m.stillwell.jr@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/ethernet/intel/i40e/i40e_lan_hmc.c | 247 | ||||
-rw-r--r-- | drivers/net/ethernet/intel/i40e/i40e_lan_hmc.h | 28 | ||||
-rw-r--r-- | drivers/net/ethernet/intel/i40evf/i40e_lan_hmc.h | 28 |
3 files changed, 236 insertions, 67 deletions
diff --git a/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.c b/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.c index 870ab1ee072c..5a603a5e9aa8 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.c +++ b/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.c | |||
@@ -747,6 +747,194 @@ static struct i40e_context_ele i40e_hmc_rxq_ce_info[] = { | |||
747 | }; | 747 | }; |
748 | 748 | ||
749 | /** | 749 | /** |
750 | * i40e_write_byte - replace HMC context byte | ||
751 | * @hmc_bits: pointer to the HMC memory | ||
752 | * @ce_info: a description of the struct to be read from | ||
753 | * @src: the struct to be read from | ||
754 | **/ | ||
755 | static void i40e_write_byte(u8 *hmc_bits, | ||
756 | struct i40e_context_ele *ce_info, | ||
757 | u8 *src) | ||
758 | { | ||
759 | u8 src_byte, dest_byte, mask; | ||
760 | u8 *from, *dest; | ||
761 | u16 shift_width; | ||
762 | |||
763 | /* copy from the next struct field */ | ||
764 | from = src + ce_info->offset; | ||
765 | |||
766 | /* prepare the bits and mask */ | ||
767 | shift_width = ce_info->lsb % 8; | ||
768 | mask = ((u8)1 << ce_info->width) - 1; | ||
769 | |||
770 | src_byte = *from; | ||
771 | src_byte &= mask; | ||
772 | |||
773 | /* shift to correct alignment */ | ||
774 | mask <<= shift_width; | ||
775 | src_byte <<= shift_width; | ||
776 | |||
777 | /* get the current bits from the target bit string */ | ||
778 | dest = hmc_bits + (ce_info->lsb / 8); | ||
779 | |||
780 | memcpy(&dest_byte, dest, sizeof(dest_byte)); | ||
781 | |||
782 | dest_byte &= ~mask; /* get the bits not changing */ | ||
783 | dest_byte |= src_byte; /* add in the new bits */ | ||
784 | |||
785 | /* put it all back */ | ||
786 | memcpy(dest, &dest_byte, sizeof(dest_byte)); | ||
787 | } | ||
788 | |||
789 | /** | ||
790 | * i40e_write_word - replace HMC context word | ||
791 | * @hmc_bits: pointer to the HMC memory | ||
792 | * @ce_info: a description of the struct to be read from | ||
793 | * @src: the struct to be read from | ||
794 | **/ | ||
795 | static void i40e_write_word(u8 *hmc_bits, | ||
796 | struct i40e_context_ele *ce_info, | ||
797 | u8 *src) | ||
798 | { | ||
799 | u16 src_word, mask; | ||
800 | u8 *from, *dest; | ||
801 | u16 shift_width; | ||
802 | __le16 dest_word; | ||
803 | |||
804 | /* copy from the next struct field */ | ||
805 | from = src + ce_info->offset; | ||
806 | |||
807 | /* prepare the bits and mask */ | ||
808 | shift_width = ce_info->lsb % 8; | ||
809 | mask = ((u16)1 << ce_info->width) - 1; | ||
810 | |||
811 | /* don't swizzle the bits until after the mask because the mask bits | ||
812 | * will be in a different bit position on big endian machines | ||
813 | */ | ||
814 | src_word = *(u16 *)from; | ||
815 | src_word &= mask; | ||
816 | |||
817 | /* shift to correct alignment */ | ||
818 | mask <<= shift_width; | ||
819 | src_word <<= shift_width; | ||
820 | |||
821 | /* get the current bits from the target bit string */ | ||
822 | dest = hmc_bits + (ce_info->lsb / 8); | ||
823 | |||
824 | memcpy(&dest_word, dest, sizeof(dest_word)); | ||
825 | |||
826 | dest_word &= ~(cpu_to_le16(mask)); /* get the bits not changing */ | ||
827 | dest_word |= cpu_to_le16(src_word); /* add in the new bits */ | ||
828 | |||
829 | /* put it all back */ | ||
830 | memcpy(dest, &dest_word, sizeof(dest_word)); | ||
831 | } | ||
832 | |||
833 | /** | ||
834 | * i40e_write_dword - replace HMC context dword | ||
835 | * @hmc_bits: pointer to the HMC memory | ||
836 | * @ce_info: a description of the struct to be read from | ||
837 | * @src: the struct to be read from | ||
838 | **/ | ||
839 | static void i40e_write_dword(u8 *hmc_bits, | ||
840 | struct i40e_context_ele *ce_info, | ||
841 | u8 *src) | ||
842 | { | ||
843 | u32 src_dword, mask; | ||
844 | u8 *from, *dest; | ||
845 | u16 shift_width; | ||
846 | __le32 dest_dword; | ||
847 | |||
848 | /* copy from the next struct field */ | ||
849 | from = src + ce_info->offset; | ||
850 | |||
851 | /* prepare the bits and mask */ | ||
852 | shift_width = ce_info->lsb % 8; | ||
853 | |||
854 | /* if the field width is exactly 32 on an x86 machine, then the shift | ||
855 | * operation will not work because the SHL instructions count is masked | ||
856 | * to 5 bits so the shift will do nothing | ||
857 | */ | ||
858 | if (ce_info->width < 32) | ||
859 | mask = ((u32)1 << ce_info->width) - 1; | ||
860 | else | ||
861 | mask = -1; | ||
862 | |||
863 | /* don't swizzle the bits until after the mask because the mask bits | ||
864 | * will be in a different bit position on big endian machines | ||
865 | */ | ||
866 | src_dword = *(u32 *)from; | ||
867 | src_dword &= mask; | ||
868 | |||
869 | /* shift to correct alignment */ | ||
870 | mask <<= shift_width; | ||
871 | src_dword <<= shift_width; | ||
872 | |||
873 | /* get the current bits from the target bit string */ | ||
874 | dest = hmc_bits + (ce_info->lsb / 8); | ||
875 | |||
876 | memcpy(&dest_dword, dest, sizeof(dest_dword)); | ||
877 | |||
878 | dest_dword &= ~(cpu_to_le32(mask)); /* get the bits not changing */ | ||
879 | dest_dword |= cpu_to_le32(src_dword); /* add in the new bits */ | ||
880 | |||
881 | /* put it all back */ | ||
882 | memcpy(dest, &dest_dword, sizeof(dest_dword)); | ||
883 | } | ||
884 | |||
885 | /** | ||
886 | * i40e_write_qword - replace HMC context qword | ||
887 | * @hmc_bits: pointer to the HMC memory | ||
888 | * @ce_info: a description of the struct to be read from | ||
889 | * @src: the struct to be read from | ||
890 | **/ | ||
891 | static void i40e_write_qword(u8 *hmc_bits, | ||
892 | struct i40e_context_ele *ce_info, | ||
893 | u8 *src) | ||
894 | { | ||
895 | u64 src_qword, mask; | ||
896 | u8 *from, *dest; | ||
897 | u16 shift_width; | ||
898 | __le64 dest_qword; | ||
899 | |||
900 | /* copy from the next struct field */ | ||
901 | from = src + ce_info->offset; | ||
902 | |||
903 | /* prepare the bits and mask */ | ||
904 | shift_width = ce_info->lsb % 8; | ||
905 | |||
906 | /* if the field width is exactly 64 on an x86 machine, then the shift | ||
907 | * operation will not work because the SHL instructions count is masked | ||
908 | * to 6 bits so the shift will do nothing | ||
909 | */ | ||
910 | if (ce_info->width < 64) | ||
911 | mask = ((u64)1 << ce_info->width) - 1; | ||
912 | else | ||
913 | mask = -1; | ||
914 | |||
915 | /* don't swizzle the bits until after the mask because the mask bits | ||
916 | * will be in a different bit position on big endian machines | ||
917 | */ | ||
918 | src_qword = *(u64 *)from; | ||
919 | src_qword &= mask; | ||
920 | |||
921 | /* shift to correct alignment */ | ||
922 | mask <<= shift_width; | ||
923 | src_qword <<= shift_width; | ||
924 | |||
925 | /* get the current bits from the target bit string */ | ||
926 | dest = hmc_bits + (ce_info->lsb / 8); | ||
927 | |||
928 | memcpy(&dest_qword, dest, sizeof(dest_qword)); | ||
929 | |||
930 | dest_qword &= ~(cpu_to_le64(mask)); /* get the bits not changing */ | ||
931 | dest_qword |= cpu_to_le64(src_qword); /* add in the new bits */ | ||
932 | |||
933 | /* put it all back */ | ||
934 | memcpy(dest, &dest_qword, sizeof(dest_qword)); | ||
935 | } | ||
936 | |||
937 | /** | ||
750 | * i40e_clear_hmc_context - zero out the HMC context bits | 938 | * i40e_clear_hmc_context - zero out the HMC context bits |
751 | * @hw: the hardware struct | 939 | * @hw: the hardware struct |
752 | * @context_bytes: pointer to the context bit array (DMA memory) | 940 | * @context_bytes: pointer to the context bit array (DMA memory) |
@@ -772,71 +960,28 @@ static i40e_status i40e_set_hmc_context(u8 *context_bytes, | |||
772 | struct i40e_context_ele *ce_info, | 960 | struct i40e_context_ele *ce_info, |
773 | u8 *dest) | 961 | u8 *dest) |
774 | { | 962 | { |
775 | u16 shift_width; | ||
776 | u64 bitfield; | ||
777 | u8 hi_byte; | ||
778 | u8 hi_mask; | ||
779 | u64 t_bits; | ||
780 | u64 mask; | ||
781 | u8 *p; | ||
782 | int f; | 963 | int f; |
783 | 964 | ||
784 | for (f = 0; ce_info[f].width != 0; f++) { | 965 | for (f = 0; ce_info[f].width != 0; f++) { |
785 | /* clear out the field */ | ||
786 | bitfield = 0; | ||
787 | 966 | ||
788 | /* copy from the next struct field */ | 967 | /* we have to deal with each element of the HMC using the |
789 | p = dest + ce_info[f].offset; | 968 | * correct size so that we are correct regardless of the |
969 | * endianness of the machine | ||
970 | */ | ||
790 | switch (ce_info[f].size_of) { | 971 | switch (ce_info[f].size_of) { |
791 | case 1: | 972 | case 1: |
792 | bitfield = *p; | 973 | i40e_write_byte(context_bytes, &ce_info[f], dest); |
793 | break; | 974 | break; |
794 | case 2: | 975 | case 2: |
795 | bitfield = cpu_to_le16(*(u16 *)p); | 976 | i40e_write_word(context_bytes, &ce_info[f], dest); |
796 | break; | 977 | break; |
797 | case 4: | 978 | case 4: |
798 | bitfield = cpu_to_le32(*(u32 *)p); | 979 | i40e_write_dword(context_bytes, &ce_info[f], dest); |
799 | break; | 980 | break; |
800 | case 8: | 981 | case 8: |
801 | bitfield = cpu_to_le64(*(u64 *)p); | 982 | i40e_write_qword(context_bytes, &ce_info[f], dest); |
802 | break; | 983 | break; |
803 | } | 984 | } |
804 | |||
805 | /* prepare the bits and mask */ | ||
806 | shift_width = ce_info[f].lsb % 8; | ||
807 | mask = ((u64)1 << ce_info[f].width) - 1; | ||
808 | |||
809 | /* save upper bytes for special case */ | ||
810 | hi_mask = (u8)((mask >> 56) & 0xff); | ||
811 | hi_byte = (u8)((bitfield >> 56) & 0xff); | ||
812 | |||
813 | /* shift to correct alignment */ | ||
814 | mask <<= shift_width; | ||
815 | bitfield <<= shift_width; | ||
816 | |||
817 | /* get the current bits from the target bit string */ | ||
818 | p = context_bytes + (ce_info[f].lsb / 8); | ||
819 | memcpy(&t_bits, p, sizeof(u64)); | ||
820 | |||
821 | t_bits &= ~mask; /* get the bits not changing */ | ||
822 | t_bits |= bitfield; /* add in the new bits */ | ||
823 | |||
824 | /* put it all back */ | ||
825 | memcpy(p, &t_bits, sizeof(u64)); | ||
826 | |||
827 | /* deal with the special case if needed | ||
828 | * example: 62 bit field that starts in bit 5 of first byte | ||
829 | * will overlap 3 bits into byte 9 | ||
830 | */ | ||
831 | if ((shift_width + ce_info[f].width) > 64) { | ||
832 | u8 byte; | ||
833 | |||
834 | hi_mask >>= (8 - shift_width); | ||
835 | hi_byte >>= (8 - shift_width); | ||
836 | byte = p[8] & ~hi_mask; /* get the bits not changing */ | ||
837 | byte |= hi_byte; /* add in the new bits */ | ||
838 | p[8] = byte; /* put it back */ | ||
839 | } | ||
840 | } | 985 | } |
841 | 986 | ||
842 | return 0; | 987 | return 0; |
diff --git a/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.h b/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.h index eb65fe23c4a7..e74128db5be5 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.h +++ b/drivers/net/ethernet/intel/i40e/i40e_lan_hmc.h | |||
@@ -32,16 +32,22 @@ struct i40e_hw; | |||
32 | 32 | ||
33 | /* HMC element context information */ | 33 | /* HMC element context information */ |
34 | 34 | ||
35 | /* Rx queue context data */ | 35 | /* Rx queue context data |
36 | * | ||
37 | * The sizes of the variables may be larger than needed due to crossing byte | ||
38 | * boundaries. If we do not have the width of the variable set to the correct | ||
39 | * size then we could end up shifting bits off the top of the variable when the | ||
40 | * variable is at the top of a byte and crosses over into the next byte. | ||
41 | */ | ||
36 | struct i40e_hmc_obj_rxq { | 42 | struct i40e_hmc_obj_rxq { |
37 | u16 head; | 43 | u16 head; |
38 | u8 cpuid; | 44 | u16 cpuid; /* bigger than needed, see above for reason */ |
39 | u64 base; | 45 | u64 base; |
40 | u16 qlen; | 46 | u16 qlen; |
41 | #define I40E_RXQ_CTX_DBUFF_SHIFT 7 | 47 | #define I40E_RXQ_CTX_DBUFF_SHIFT 7 |
42 | u8 dbuff; | 48 | u16 dbuff; /* bigger than needed, see above for reason */ |
43 | #define I40E_RXQ_CTX_HBUFF_SHIFT 6 | 49 | #define I40E_RXQ_CTX_HBUFF_SHIFT 6 |
44 | u8 hbuff; | 50 | u16 hbuff; /* bigger than needed, see above for reason */ |
45 | u8 dtype; | 51 | u8 dtype; |
46 | u8 dsize; | 52 | u8 dsize; |
47 | u8 crcstrip; | 53 | u8 crcstrip; |
@@ -50,16 +56,22 @@ struct i40e_hmc_obj_rxq { | |||
50 | u8 hsplit_0; | 56 | u8 hsplit_0; |
51 | u8 hsplit_1; | 57 | u8 hsplit_1; |
52 | u8 showiv; | 58 | u8 showiv; |
53 | u16 rxmax; | 59 | u32 rxmax; /* bigger than needed, see above for reason */ |
54 | u8 tphrdesc_ena; | 60 | u8 tphrdesc_ena; |
55 | u8 tphwdesc_ena; | 61 | u8 tphwdesc_ena; |
56 | u8 tphdata_ena; | 62 | u8 tphdata_ena; |
57 | u8 tphhead_ena; | 63 | u8 tphhead_ena; |
58 | u8 lrxqthresh; | 64 | u16 lrxqthresh; /* bigger than needed, see above for reason */ |
59 | u8 prefena; /* NOTE: normally must be set to 1 at init */ | 65 | u8 prefena; /* NOTE: normally must be set to 1 at init */ |
60 | }; | 66 | }; |
61 | 67 | ||
62 | /* Tx queue context data */ | 68 | /* Tx queue context data |
69 | * | ||
70 | * The sizes of the variables may be larger than needed due to crossing byte | ||
71 | * boundaries. If we do not have the width of the variable set to the correct | ||
72 | * size then we could end up shifting bits off the top of the variable when the | ||
73 | * variable is at the top of a byte and crosses over into the next byte. | ||
74 | */ | ||
63 | struct i40e_hmc_obj_txq { | 75 | struct i40e_hmc_obj_txq { |
64 | u16 head; | 76 | u16 head; |
65 | u8 new_context; | 77 | u8 new_context; |
@@ -69,7 +81,7 @@ struct i40e_hmc_obj_txq { | |||
69 | u8 fd_ena; | 81 | u8 fd_ena; |
70 | u8 alt_vlan_ena; | 82 | u8 alt_vlan_ena; |
71 | u16 thead_wb; | 83 | u16 thead_wb; |
72 | u16 cpuid; | 84 | u8 cpuid; |
73 | u8 head_wb_ena; | 85 | u8 head_wb_ena; |
74 | u16 qlen; | 86 | u16 qlen; |
75 | u8 tphrdesc_ena; | 87 | u8 tphrdesc_ena; |
diff --git a/drivers/net/ethernet/intel/i40evf/i40e_lan_hmc.h b/drivers/net/ethernet/intel/i40evf/i40e_lan_hmc.h index d6f762241537..a5d79877354c 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_lan_hmc.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_lan_hmc.h | |||
@@ -32,16 +32,22 @@ struct i40e_hw; | |||
32 | 32 | ||
33 | /* HMC element context information */ | 33 | /* HMC element context information */ |
34 | 34 | ||
35 | /* Rx queue context data */ | 35 | /* Rx queue context data |
36 | * | ||
37 | * The sizes of the variables may be larger than needed due to crossing byte | ||
38 | * boundaries. If we do not have the width of the variable set to the correct | ||
39 | * size then we could end up shifting bits off the top of the variable when the | ||
40 | * variable is at the top of a byte and crosses over into the next byte. | ||
41 | */ | ||
36 | struct i40e_hmc_obj_rxq { | 42 | struct i40e_hmc_obj_rxq { |
37 | u16 head; | 43 | u16 head; |
38 | u8 cpuid; | 44 | u16 cpuid; /* bigger than needed, see above for reason */ |
39 | u64 base; | 45 | u64 base; |
40 | u16 qlen; | 46 | u16 qlen; |
41 | #define I40E_RXQ_CTX_DBUFF_SHIFT 7 | 47 | #define I40E_RXQ_CTX_DBUFF_SHIFT 7 |
42 | u8 dbuff; | 48 | u16 dbuff; /* bigger than needed, see above for reason */ |
43 | #define I40E_RXQ_CTX_HBUFF_SHIFT 6 | 49 | #define I40E_RXQ_CTX_HBUFF_SHIFT 6 |
44 | u8 hbuff; | 50 | u16 hbuff; /* bigger than needed, see above for reason */ |
45 | u8 dtype; | 51 | u8 dtype; |
46 | u8 dsize; | 52 | u8 dsize; |
47 | u8 crcstrip; | 53 | u8 crcstrip; |
@@ -50,16 +56,22 @@ struct i40e_hmc_obj_rxq { | |||
50 | u8 hsplit_0; | 56 | u8 hsplit_0; |
51 | u8 hsplit_1; | 57 | u8 hsplit_1; |
52 | u8 showiv; | 58 | u8 showiv; |
53 | u16 rxmax; | 59 | u32 rxmax; /* bigger than needed, see above for reason */ |
54 | u8 tphrdesc_ena; | 60 | u8 tphrdesc_ena; |
55 | u8 tphwdesc_ena; | 61 | u8 tphwdesc_ena; |
56 | u8 tphdata_ena; | 62 | u8 tphdata_ena; |
57 | u8 tphhead_ena; | 63 | u8 tphhead_ena; |
58 | u8 lrxqthresh; | 64 | u16 lrxqthresh; /* bigger than needed, see above for reason */ |
59 | u8 prefena; /* NOTE: normally must be set to 1 at init */ | 65 | u8 prefena; /* NOTE: normally must be set to 1 at init */ |
60 | }; | 66 | }; |
61 | 67 | ||
62 | /* Tx queue context data */ | 68 | /* Tx queue context data |
69 | * | ||
70 | * The sizes of the variables may be larger than needed due to crossing byte | ||
71 | * boundaries. If we do not have the width of the variable set to the correct | ||
72 | * size then we could end up shifting bits off the top of the variable when the | ||
73 | * variable is at the top of a byte and crosses over into the next byte. | ||
74 | */ | ||
63 | struct i40e_hmc_obj_txq { | 75 | struct i40e_hmc_obj_txq { |
64 | u16 head; | 76 | u16 head; |
65 | u8 new_context; | 77 | u8 new_context; |
@@ -69,7 +81,7 @@ struct i40e_hmc_obj_txq { | |||
69 | u8 fd_ena; | 81 | u8 fd_ena; |
70 | u8 alt_vlan_ena; | 82 | u8 alt_vlan_ena; |
71 | u16 thead_wb; | 83 | u16 thead_wb; |
72 | u16 cpuid; | 84 | u8 cpuid; |
73 | u8 head_wb_ena; | 85 | u8 head_wb_ena; |
74 | u16 qlen; | 86 | u16 qlen; |
75 | u8 tphrdesc_ena; | 87 | u8 tphrdesc_ena; |