4f0d95f456106a2479528860e07a8e3f272c22f2
[openwrt/openwrt.git] /
1 From a2550d3ce53c68f54042bc5e468c4d07491ffe0e Mon Sep 17 00:00:00 2001
2 From: Christian Marangi <ansuelsmth@gmail.com>
3 Date: Wed, 12 Oct 2022 19:18:36 +0200
4 Subject: [PATCH 1/2] net: dsa: qca8k: fix inband mgmt for big-endian systems
5
6 The header and the data of the skb for the inband mgmt requires
7 to be in little-endian. This is problematic for big-endian system
8 as the mgmt header is written in the cpu byte order.
9
10 Fix this by converting each value for the mgmt header and data to
11 little-endian, and convert to cpu byte order the mgmt header and
12 data sent by the switch.
13
14 Fixes: 5950c7c0a68c ("net: dsa: qca8k: add support for mgmt read/write in Ethernet packet")
15 Tested-by: Pawel Dembicki <paweldembicki@gmail.com>
16 Tested-by: Lech Perczak <lech.perczak@gmail.com>
17 Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
18 Reviewed-by: Lech Perczak <lech.perczak@gmail.com>
19 Signed-off-by: David S. Miller <davem@davemloft.net>
20 ---
21 drivers/net/dsa/qca/qca8k-8xxx.c | 63 ++++++++++++++++++++++++--------
22 include/linux/dsa/tag_qca.h | 6 +--
23 2 files changed, 50 insertions(+), 19 deletions(-)
24
25 --- a/drivers/net/dsa/qca/qca8k-8xxx.c
26 +++ b/drivers/net/dsa/qca/qca8k-8xxx.c
27 @@ -137,27 +137,42 @@ static void qca8k_rw_reg_ack_handler(str
28 struct qca8k_mgmt_eth_data *mgmt_eth_data;
29 struct qca8k_priv *priv = ds->priv;
30 struct qca_mgmt_ethhdr *mgmt_ethhdr;
31 + u32 command;
32 u8 len, cmd;
33 + int i;
34
35 mgmt_ethhdr = (struct qca_mgmt_ethhdr *)skb_mac_header(skb);
36 mgmt_eth_data = &priv->mgmt_eth_data;
37
38 - cmd = FIELD_GET(QCA_HDR_MGMT_CMD, mgmt_ethhdr->command);
39 - len = FIELD_GET(QCA_HDR_MGMT_LENGTH, mgmt_ethhdr->command);
40 + command = get_unaligned_le32(&mgmt_ethhdr->command);
41 + cmd = FIELD_GET(QCA_HDR_MGMT_CMD, command);
42 + len = FIELD_GET(QCA_HDR_MGMT_LENGTH, command);
43
44 /* Make sure the seq match the requested packet */
45 - if (mgmt_ethhdr->seq == mgmt_eth_data->seq)
46 + if (get_unaligned_le32(&mgmt_ethhdr->seq) == mgmt_eth_data->seq)
47 mgmt_eth_data->ack = true;
48
49 if (cmd == MDIO_READ) {
50 - mgmt_eth_data->data[0] = mgmt_ethhdr->mdio_data;
51 + u32 *val = mgmt_eth_data->data;
52 +
53 + *val = get_unaligned_le32(&mgmt_ethhdr->mdio_data);
54
55 /* Get the rest of the 12 byte of data.
56 * The read/write function will extract the requested data.
57 */
58 - if (len > QCA_HDR_MGMT_DATA1_LEN)
59 - memcpy(mgmt_eth_data->data + 1, skb->data,
60 - QCA_HDR_MGMT_DATA2_LEN);
61 + if (len > QCA_HDR_MGMT_DATA1_LEN) {
62 + __le32 *data2 = (__le32 *)skb->data;
63 + int data_len = min_t(int, QCA_HDR_MGMT_DATA2_LEN,
64 + len - QCA_HDR_MGMT_DATA1_LEN);
65 +
66 + val++;
67 +
68 + for (i = sizeof(u32); i <= data_len; i += sizeof(u32)) {
69 + *val = get_unaligned_le32(data2);
70 + val++;
71 + data2++;
72 + }
73 + }
74 }
75
76 complete(&mgmt_eth_data->rw_done);
77 @@ -169,8 +184,10 @@ static struct sk_buff *qca8k_alloc_mdio_
78 struct qca_mgmt_ethhdr *mgmt_ethhdr;
79 unsigned int real_len;
80 struct sk_buff *skb;
81 - u32 *data2;
82 + __le32 *data2;
83 + u32 command;
84 u16 hdr;
85 + int i;
86
87 skb = dev_alloc_skb(QCA_HDR_MGMT_PKT_LEN);
88 if (!skb)
89 @@ -199,20 +216,32 @@ static struct sk_buff *qca8k_alloc_mdio_
90 hdr |= FIELD_PREP(QCA_HDR_XMIT_DP_BIT, BIT(0));
91 hdr |= FIELD_PREP(QCA_HDR_XMIT_CONTROL, QCA_HDR_XMIT_TYPE_RW_REG);
92
93 - mgmt_ethhdr->command = FIELD_PREP(QCA_HDR_MGMT_ADDR, reg);
94 - mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_LENGTH, real_len);
95 - mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_CMD, cmd);
96 - mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_CHECK_CODE,
97 + command = FIELD_PREP(QCA_HDR_MGMT_ADDR, reg);
98 + command |= FIELD_PREP(QCA_HDR_MGMT_LENGTH, real_len);
99 + command |= FIELD_PREP(QCA_HDR_MGMT_CMD, cmd);
100 + command |= FIELD_PREP(QCA_HDR_MGMT_CHECK_CODE,
101 QCA_HDR_MGMT_CHECK_CODE_VAL);
102
103 + put_unaligned_le32(command, &mgmt_ethhdr->command);
104 +
105 if (cmd == MDIO_WRITE)
106 - mgmt_ethhdr->mdio_data = *val;
107 + put_unaligned_le32(*val, &mgmt_ethhdr->mdio_data);
108
109 mgmt_ethhdr->hdr = htons(hdr);
110
111 data2 = skb_put_zero(skb, QCA_HDR_MGMT_DATA2_LEN + QCA_HDR_MGMT_PADDING_LEN);
112 - if (cmd == MDIO_WRITE && len > QCA_HDR_MGMT_DATA1_LEN)
113 - memcpy(data2, val + 1, len - QCA_HDR_MGMT_DATA1_LEN);
114 + if (cmd == MDIO_WRITE && len > QCA_HDR_MGMT_DATA1_LEN) {
115 + int data_len = min_t(int, QCA_HDR_MGMT_DATA2_LEN,
116 + len - QCA_HDR_MGMT_DATA1_LEN);
117 +
118 + val++;
119 +
120 + for (i = sizeof(u32); i <= data_len; i += sizeof(u32)) {
121 + put_unaligned_le32(*val, data2);
122 + data2++;
123 + val++;
124 + }
125 + }
126
127 return skb;
128 }
129 @@ -220,9 +249,11 @@ static struct sk_buff *qca8k_alloc_mdio_
130 static void qca8k_mdio_header_fill_seq_num(struct sk_buff *skb, u32 seq_num)
131 {
132 struct qca_mgmt_ethhdr *mgmt_ethhdr;
133 + u32 seq;
134
135 + seq = FIELD_PREP(QCA_HDR_MGMT_SEQ_NUM, seq_num);
136 mgmt_ethhdr = (struct qca_mgmt_ethhdr *)skb->data;
137 - mgmt_ethhdr->seq = FIELD_PREP(QCA_HDR_MGMT_SEQ_NUM, seq_num);
138 + put_unaligned_le32(seq, &mgmt_ethhdr->seq);
139 }
140
141 static int qca8k_read_eth(struct qca8k_priv *priv, u32 reg, u32 *val, int len)
142 --- a/include/linux/dsa/tag_qca.h
143 +++ b/include/linux/dsa/tag_qca.h
144 @@ -56,9 +56,9 @@
145
146 /* Special struct emulating a Ethernet header */
147 struct qca_mgmt_ethhdr {
148 - u32 command; /* command bit 31:0 */
149 - u32 seq; /* seq 63:32 */
150 - u32 mdio_data; /* first 4byte mdio */
151 + __le32 command; /* command bit 31:0 */
152 + __le32 seq; /* seq 63:32 */
153 + __le32 mdio_data; /* first 4byte mdio */
154 __be16 hdr; /* qca hdr */
155 } __packed;
156