generic: 5.15: backport qca8k fixup patches for Big-Endian systems
[openwrt/openwrt.git] / target / linux / generic / backport-5.15 / 776-v6.1-01-net-dsa-qca8k-fix-inband-mgmt-for-big-endian-systems.patch
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 diff --git a/drivers/net/dsa/qca/qca8k-8xxx.c b/drivers/net/dsa/qca/qca8k-8xxx.c
26 index 5669c92c93f7..644338ca0510 100644
27 --- a/drivers/net/dsa/qca/qca8k-8xxx.c
28 +++ b/drivers/net/dsa/qca/qca8k-8xxx.c
29 @@ -137,27 +137,42 @@ static void qca8k_rw_reg_ack_handler(struct dsa_switch *ds, struct sk_buff *skb)
30 struct qca8k_mgmt_eth_data *mgmt_eth_data;
31 struct qca8k_priv *priv = ds->priv;
32 struct qca_mgmt_ethhdr *mgmt_ethhdr;
33 + u32 command;
34 u8 len, cmd;
35 + int i;
36
37 mgmt_ethhdr = (struct qca_mgmt_ethhdr *)skb_mac_header(skb);
38 mgmt_eth_data = &priv->mgmt_eth_data;
39
40 - cmd = FIELD_GET(QCA_HDR_MGMT_CMD, mgmt_ethhdr->command);
41 - len = FIELD_GET(QCA_HDR_MGMT_LENGTH, mgmt_ethhdr->command);
42 + command = get_unaligned_le32(&mgmt_ethhdr->command);
43 + cmd = FIELD_GET(QCA_HDR_MGMT_CMD, command);
44 + len = FIELD_GET(QCA_HDR_MGMT_LENGTH, command);
45
46 /* Make sure the seq match the requested packet */
47 - if (mgmt_ethhdr->seq == mgmt_eth_data->seq)
48 + if (get_unaligned_le32(&mgmt_ethhdr->seq) == mgmt_eth_data->seq)
49 mgmt_eth_data->ack = true;
50
51 if (cmd == MDIO_READ) {
52 - mgmt_eth_data->data[0] = mgmt_ethhdr->mdio_data;
53 + u32 *val = mgmt_eth_data->data;
54 +
55 + *val = get_unaligned_le32(&mgmt_ethhdr->mdio_data);
56
57 /* Get the rest of the 12 byte of data.
58 * The read/write function will extract the requested data.
59 */
60 - if (len > QCA_HDR_MGMT_DATA1_LEN)
61 - memcpy(mgmt_eth_data->data + 1, skb->data,
62 - QCA_HDR_MGMT_DATA2_LEN);
63 + if (len > QCA_HDR_MGMT_DATA1_LEN) {
64 + __le32 *data2 = (__le32 *)skb->data;
65 + int data_len = min_t(int, QCA_HDR_MGMT_DATA2_LEN,
66 + len - QCA_HDR_MGMT_DATA1_LEN);
67 +
68 + val++;
69 +
70 + for (i = sizeof(u32); i <= data_len; i += sizeof(u32)) {
71 + *val = get_unaligned_le32(data2);
72 + val++;
73 + data2++;
74 + }
75 + }
76 }
77
78 complete(&mgmt_eth_data->rw_done);
79 @@ -169,8 +184,10 @@ static struct sk_buff *qca8k_alloc_mdio_header(enum mdio_cmd cmd, u32 reg, u32 *
80 struct qca_mgmt_ethhdr *mgmt_ethhdr;
81 unsigned int real_len;
82 struct sk_buff *skb;
83 - u32 *data2;
84 + __le32 *data2;
85 + u32 command;
86 u16 hdr;
87 + int i;
88
89 skb = dev_alloc_skb(QCA_HDR_MGMT_PKT_LEN);
90 if (!skb)
91 @@ -199,20 +216,32 @@ static struct sk_buff *qca8k_alloc_mdio_header(enum mdio_cmd cmd, u32 reg, u32 *
92 hdr |= FIELD_PREP(QCA_HDR_XMIT_DP_BIT, BIT(0));
93 hdr |= FIELD_PREP(QCA_HDR_XMIT_CONTROL, QCA_HDR_XMIT_TYPE_RW_REG);
94
95 - mgmt_ethhdr->command = FIELD_PREP(QCA_HDR_MGMT_ADDR, reg);
96 - mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_LENGTH, real_len);
97 - mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_CMD, cmd);
98 - mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_CHECK_CODE,
99 + command = FIELD_PREP(QCA_HDR_MGMT_ADDR, reg);
100 + command |= FIELD_PREP(QCA_HDR_MGMT_LENGTH, real_len);
101 + command |= FIELD_PREP(QCA_HDR_MGMT_CMD, cmd);
102 + command |= FIELD_PREP(QCA_HDR_MGMT_CHECK_CODE,
103 QCA_HDR_MGMT_CHECK_CODE_VAL);
104
105 + put_unaligned_le32(command, &mgmt_ethhdr->command);
106 +
107 if (cmd == MDIO_WRITE)
108 - mgmt_ethhdr->mdio_data = *val;
109 + put_unaligned_le32(*val, &mgmt_ethhdr->mdio_data);
110
111 mgmt_ethhdr->hdr = htons(hdr);
112
113 data2 = skb_put_zero(skb, QCA_HDR_MGMT_DATA2_LEN + QCA_HDR_MGMT_PADDING_LEN);
114 - if (cmd == MDIO_WRITE && len > QCA_HDR_MGMT_DATA1_LEN)
115 - memcpy(data2, val + 1, len - QCA_HDR_MGMT_DATA1_LEN);
116 + if (cmd == MDIO_WRITE && len > QCA_HDR_MGMT_DATA1_LEN) {
117 + int data_len = min_t(int, QCA_HDR_MGMT_DATA2_LEN,
118 + len - QCA_HDR_MGMT_DATA1_LEN);
119 +
120 + val++;
121 +
122 + for (i = sizeof(u32); i <= data_len; i += sizeof(u32)) {
123 + put_unaligned_le32(*val, data2);
124 + data2++;
125 + val++;
126 + }
127 + }
128
129 return skb;
130 }
131 @@ -220,9 +249,11 @@ static struct sk_buff *qca8k_alloc_mdio_header(enum mdio_cmd cmd, u32 reg, u32 *
132 static void qca8k_mdio_header_fill_seq_num(struct sk_buff *skb, u32 seq_num)
133 {
134 struct qca_mgmt_ethhdr *mgmt_ethhdr;
135 + u32 seq;
136
137 + seq = FIELD_PREP(QCA_HDR_MGMT_SEQ_NUM, seq_num);
138 mgmt_ethhdr = (struct qca_mgmt_ethhdr *)skb->data;
139 - mgmt_ethhdr->seq = FIELD_PREP(QCA_HDR_MGMT_SEQ_NUM, seq_num);
140 + put_unaligned_le32(seq, &mgmt_ethhdr->seq);
141 }
142
143 static int qca8k_read_eth(struct qca8k_priv *priv, u32 reg, u32 *val, int len)
144 diff --git a/include/linux/dsa/tag_qca.h b/include/linux/dsa/tag_qca.h
145 index 50be7cbd93a5..0e176da1e43f 100644
146 --- a/include/linux/dsa/tag_qca.h
147 +++ b/include/linux/dsa/tag_qca.h
148 @@ -61,9 +61,9 @@ struct sk_buff;
149
150 /* Special struct emulating a Ethernet header */
151 struct qca_mgmt_ethhdr {
152 - u32 command; /* command bit 31:0 */
153 - u32 seq; /* seq 63:32 */
154 - u32 mdio_data; /* first 4byte mdio */
155 + __le32 command; /* command bit 31:0 */
156 + __le32 seq; /* seq 63:32 */
157 + __le32 mdio_data; /* first 4byte mdio */
158 __be16 hdr; /* qca hdr */
159 } __packed;
160
161 --
162 2.37.2
163