OSDN Git Service

43656ad79d8adc4178c8b3adbbbc5826c7566d3e
[immortalwrt/immortalwrt.git] / target / linux / generic / backport-5.15 / 766-10-net-dsa-qca8k-add-support-for-mgmt-read-write-in-Eth.patch
1 From 5950c7c0a68c915b336c70f79388626e2d576ab7 Mon Sep 17 00:00:00 2001
2 From: Ansuel Smith <ansuelsmth@gmail.com>
3 Date: Wed, 2 Feb 2022 01:03:29 +0100
4 Subject: [PATCH 10/16] net: dsa: qca8k: add support for mgmt read/write in
5  Ethernet packet
6
7 Add qca8k side support for mgmt read/write in Ethernet packet.
8 qca8k supports some specially crafted Ethernet packet that can be used
9 for mgmt read/write instead of the legacy method uart/internal mdio.
10 This add support for the qca8k side to craft the packet and enqueue it.
11 Each port and the qca8k_priv have a special struct to put data in it.
12 The completion API is used to wait for the packet to be received back
13 with the requested data.
14
15 The various steps are:
16 1. Craft the special packet with the qca hdr set to mgmt read/write
17    mode.
18 2. Set the lock in the dedicated mgmt struct.
19 3. Increment the seq number and set it in the mgmt pkt
20 4. Reinit the completion.
21 5. Enqueue the packet.
22 6. Wait the packet to be received.
23 7. Use the data set by the tagger to complete the mdio operation.
24
25 If the completion timeouts or the ack value is not true, the legacy
26 mdio way is used.
27
28 It has to be considered that in the initial setup mdio is still used and
29 mdio is still used until DSA is ready to accept and tag packet.
30
31 tag_proto_connect() is used to fill the required handler for the tagger
32 to correctly parse and elaborate the special Ethernet mdio packet.
33
34 Locking is added to qca8k_master_change() to make sure no mgmt Ethernet
35 are in progress.
36
37 Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
38 Signed-off-by: David S. Miller <davem@davemloft.net>
39 ---
40  drivers/net/dsa/qca8k.c | 225 ++++++++++++++++++++++++++++++++++++++++
41  drivers/net/dsa/qca8k.h |  13 +++
42  2 files changed, 238 insertions(+)
43
44 --- a/drivers/net/dsa/qca8k.c
45 +++ b/drivers/net/dsa/qca8k.c
46 @@ -20,6 +20,7 @@
47  #include <linux/phylink.h>
48  #include <linux/gpio/consumer.h>
49  #include <linux/etherdevice.h>
50 +#include <linux/dsa/tag_qca.h>
51  
52  #include "qca8k.h"
53  
54 @@ -170,6 +171,194 @@ qca8k_rmw(struct qca8k_priv *priv, u32 r
55         return regmap_update_bits(priv->regmap, reg, mask, write_val);
56  }
57  
58 +static void qca8k_rw_reg_ack_handler(struct dsa_switch *ds, struct sk_buff *skb)
59 +{
60 +       struct qca8k_mgmt_eth_data *mgmt_eth_data;
61 +       struct qca8k_priv *priv = ds->priv;
62 +       struct qca_mgmt_ethhdr *mgmt_ethhdr;
63 +       u8 len, cmd;
64 +
65 +       mgmt_ethhdr = (struct qca_mgmt_ethhdr *)skb_mac_header(skb);
66 +       mgmt_eth_data = &priv->mgmt_eth_data;
67 +
68 +       cmd = FIELD_GET(QCA_HDR_MGMT_CMD, mgmt_ethhdr->command);
69 +       len = FIELD_GET(QCA_HDR_MGMT_LENGTH, mgmt_ethhdr->command);
70 +
71 +       /* Make sure the seq match the requested packet */
72 +       if (mgmt_ethhdr->seq == mgmt_eth_data->seq)
73 +               mgmt_eth_data->ack = true;
74 +
75 +       if (cmd == MDIO_READ) {
76 +               mgmt_eth_data->data[0] = mgmt_ethhdr->mdio_data;
77 +
78 +               /* Get the rest of the 12 byte of data */
79 +               if (len > QCA_HDR_MGMT_DATA1_LEN)
80 +                       memcpy(mgmt_eth_data->data + 1, skb->data,
81 +                              QCA_HDR_MGMT_DATA2_LEN);
82 +       }
83 +
84 +       complete(&mgmt_eth_data->rw_done);
85 +}
86 +
87 +static struct sk_buff *qca8k_alloc_mdio_header(enum mdio_cmd cmd, u32 reg, u32 *val,
88 +                                              int priority)
89 +{
90 +       struct qca_mgmt_ethhdr *mgmt_ethhdr;
91 +       struct sk_buff *skb;
92 +       u16 hdr;
93 +
94 +       skb = dev_alloc_skb(QCA_HDR_MGMT_PKT_LEN);
95 +       if (!skb)
96 +               return NULL;
97 +
98 +       skb_reset_mac_header(skb);
99 +       skb_set_network_header(skb, skb->len);
100 +
101 +       mgmt_ethhdr = skb_push(skb, QCA_HDR_MGMT_HEADER_LEN + QCA_HDR_LEN);
102 +
103 +       hdr = FIELD_PREP(QCA_HDR_XMIT_VERSION, QCA_HDR_VERSION);
104 +       hdr |= FIELD_PREP(QCA_HDR_XMIT_PRIORITY, priority);
105 +       hdr |= QCA_HDR_XMIT_FROM_CPU;
106 +       hdr |= FIELD_PREP(QCA_HDR_XMIT_DP_BIT, BIT(0));
107 +       hdr |= FIELD_PREP(QCA_HDR_XMIT_CONTROL, QCA_HDR_XMIT_TYPE_RW_REG);
108 +
109 +       mgmt_ethhdr->command = FIELD_PREP(QCA_HDR_MGMT_ADDR, reg);
110 +       mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_LENGTH, 4);
111 +       mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_CMD, cmd);
112 +       mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_CHECK_CODE,
113 +                                          QCA_HDR_MGMT_CHECK_CODE_VAL);
114 +
115 +       if (cmd == MDIO_WRITE)
116 +               mgmt_ethhdr->mdio_data = *val;
117 +
118 +       mgmt_ethhdr->hdr = htons(hdr);
119 +
120 +       skb_put_zero(skb, QCA_HDR_MGMT_DATA2_LEN + QCA_HDR_MGMT_PADDING_LEN);
121 +
122 +       return skb;
123 +}
124 +
125 +static void qca8k_mdio_header_fill_seq_num(struct sk_buff *skb, u32 seq_num)
126 +{
127 +       struct qca_mgmt_ethhdr *mgmt_ethhdr;
128 +
129 +       mgmt_ethhdr = (struct qca_mgmt_ethhdr *)skb->data;
130 +       mgmt_ethhdr->seq = FIELD_PREP(QCA_HDR_MGMT_SEQ_NUM, seq_num);
131 +}
132 +
133 +static int qca8k_read_eth(struct qca8k_priv *priv, u32 reg, u32 *val)
134 +{
135 +       struct qca8k_mgmt_eth_data *mgmt_eth_data = &priv->mgmt_eth_data;
136 +       struct sk_buff *skb;
137 +       bool ack;
138 +       int ret;
139 +
140 +       skb = qca8k_alloc_mdio_header(MDIO_READ, reg, NULL,
141 +                                     QCA8K_ETHERNET_MDIO_PRIORITY);
142 +       if (!skb)
143 +               return -ENOMEM;
144 +
145 +       mutex_lock(&mgmt_eth_data->mutex);
146 +
147 +       /* Check mgmt_master if is operational */
148 +       if (!priv->mgmt_master) {
149 +               kfree_skb(skb);
150 +               mutex_unlock(&mgmt_eth_data->mutex);
151 +               return -EINVAL;
152 +       }
153 +
154 +       skb->dev = priv->mgmt_master;
155 +
156 +       reinit_completion(&mgmt_eth_data->rw_done);
157 +
158 +       /* Increment seq_num and set it in the mdio pkt */
159 +       mgmt_eth_data->seq++;
160 +       qca8k_mdio_header_fill_seq_num(skb, mgmt_eth_data->seq);
161 +       mgmt_eth_data->ack = false;
162 +
163 +       dev_queue_xmit(skb);
164 +
165 +       ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done,
166 +                                         msecs_to_jiffies(QCA8K_ETHERNET_TIMEOUT));
167 +
168 +       *val = mgmt_eth_data->data[0];
169 +       ack = mgmt_eth_data->ack;
170 +
171 +       mutex_unlock(&mgmt_eth_data->mutex);
172 +
173 +       if (ret <= 0)
174 +               return -ETIMEDOUT;
175 +
176 +       if (!ack)
177 +               return -EINVAL;
178 +
179 +       return 0;
180 +}
181 +
182 +static int qca8k_write_eth(struct qca8k_priv *priv, u32 reg, u32 val)
183 +{
184 +       struct qca8k_mgmt_eth_data *mgmt_eth_data = &priv->mgmt_eth_data;
185 +       struct sk_buff *skb;
186 +       bool ack;
187 +       int ret;
188 +
189 +       skb = qca8k_alloc_mdio_header(MDIO_WRITE, reg, &val,
190 +                                     QCA8K_ETHERNET_MDIO_PRIORITY);
191 +       if (!skb)
192 +               return -ENOMEM;
193 +
194 +       mutex_lock(&mgmt_eth_data->mutex);
195 +
196 +       /* Check mgmt_master if is operational */
197 +       if (!priv->mgmt_master) {
198 +               kfree_skb(skb);
199 +               mutex_unlock(&mgmt_eth_data->mutex);
200 +               return -EINVAL;
201 +       }
202 +
203 +       skb->dev = priv->mgmt_master;
204 +
205 +       reinit_completion(&mgmt_eth_data->rw_done);
206 +
207 +       /* Increment seq_num and set it in the mdio pkt */
208 +       mgmt_eth_data->seq++;
209 +       qca8k_mdio_header_fill_seq_num(skb, mgmt_eth_data->seq);
210 +       mgmt_eth_data->ack = false;
211 +
212 +       dev_queue_xmit(skb);
213 +
214 +       ret = wait_for_completion_timeout(&mgmt_eth_data->rw_done,
215 +                                         msecs_to_jiffies(QCA8K_ETHERNET_TIMEOUT));
216 +
217 +       ack = mgmt_eth_data->ack;
218 +
219 +       mutex_unlock(&mgmt_eth_data->mutex);
220 +
221 +       if (ret <= 0)
222 +               return -ETIMEDOUT;
223 +
224 +       if (!ack)
225 +               return -EINVAL;
226 +
227 +       return 0;
228 +}
229 +
230 +static int
231 +qca8k_regmap_update_bits_eth(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val)
232 +{
233 +       u32 val = 0;
234 +       int ret;
235 +
236 +       ret = qca8k_read_eth(priv, reg, &val);
237 +       if (ret)
238 +               return ret;
239 +
240 +       val &= ~mask;
241 +       val |= write_val;
242 +
243 +       return qca8k_write_eth(priv, reg, val);
244 +}
245 +
246  static int
247  qca8k_regmap_read(void *ctx, uint32_t reg, uint32_t *val)
248  {
249 @@ -178,6 +367,9 @@ qca8k_regmap_read(void *ctx, uint32_t re
250         u16 r1, r2, page;
251         int ret;
252  
253 +       if (!qca8k_read_eth(priv, reg, val))
254 +               return 0;
255 +
256         qca8k_split_addr(reg, &r1, &r2, &page);
257  
258         mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
259 @@ -201,6 +393,9 @@ qca8k_regmap_write(void *ctx, uint32_t r
260         u16 r1, r2, page;
261         int ret;
262  
263 +       if (!qca8k_write_eth(priv, reg, val))
264 +               return 0;
265 +
266         qca8k_split_addr(reg, &r1, &r2, &page);
267  
268         mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
269 @@ -225,6 +420,9 @@ qca8k_regmap_update_bits(void *ctx, uint
270         u32 val;
271         int ret;
272  
273 +       if (!qca8k_regmap_update_bits_eth(priv, reg, mask, write_val))
274 +               return 0;
275 +
276         qca8k_split_addr(reg, &r1, &r2, &page);
277  
278         mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
279 @@ -2393,7 +2591,30 @@ qca8k_master_change(struct dsa_switch *d
280         if (dp->index != 0)
281                 return;
282  
283 +       mutex_lock(&priv->mgmt_eth_data.mutex);
284 +
285         priv->mgmt_master = operational ? (struct net_device *)master : NULL;
286 +
287 +       mutex_unlock(&priv->mgmt_eth_data.mutex);
288 +}
289 +
290 +static int qca8k_connect_tag_protocol(struct dsa_switch *ds,
291 +                                     enum dsa_tag_protocol proto)
292 +{
293 +       struct qca_tagger_data *tagger_data;
294 +
295 +       switch (proto) {
296 +       case DSA_TAG_PROTO_QCA:
297 +               tagger_data = ds->tagger_data;
298 +
299 +               tagger_data->rw_reg_ack_handler = qca8k_rw_reg_ack_handler;
300 +
301 +               break;
302 +       default:
303 +               return -EOPNOTSUPP;
304 +       }
305 +
306 +       return 0;
307  }
308  
309  static const struct dsa_switch_ops qca8k_switch_ops = {
310 @@ -2432,6 +2653,7 @@ static const struct dsa_switch_ops qca8k
311         .port_lag_join          = qca8k_port_lag_join,
312         .port_lag_leave         = qca8k_port_lag_leave,
313         .master_state_change    = qca8k_master_change,
314 +       .connect_tag_protocol   = qca8k_connect_tag_protocol,
315  };
316  
317  static int qca8k_read_switch_id(struct qca8k_priv *priv)
318 @@ -2511,6 +2733,9 @@ qca8k_sw_probe(struct mdio_device *mdiod
319         if (!priv->ds)
320                 return -ENOMEM;
321  
322 +       mutex_init(&priv->mgmt_eth_data.mutex);
323 +       init_completion(&priv->mgmt_eth_data.rw_done);
324 +
325         priv->ds->dev = &mdiodev->dev;
326         priv->ds->num_ports = QCA8K_NUM_PORTS;
327         priv->ds->priv = priv;
328 --- a/drivers/net/dsa/qca8k.h
329 +++ b/drivers/net/dsa/qca8k.h
330 @@ -11,6 +11,10 @@
331  #include <linux/delay.h>
332  #include <linux/regmap.h>
333  #include <linux/gpio.h>
334 +#include <linux/dsa/tag_qca.h>
335 +
336 +#define QCA8K_ETHERNET_MDIO_PRIORITY                   7
337 +#define QCA8K_ETHERNET_TIMEOUT                         100
338  
339  #define QCA8K_NUM_PORTS                                        7
340  #define QCA8K_NUM_CPU_PORTS                            2
341 @@ -328,6 +332,14 @@ enum {
342         QCA8K_CPU_PORT6,
343  };
344  
345 +struct qca8k_mgmt_eth_data {
346 +       struct completion rw_done;
347 +       struct mutex mutex; /* Enforce one mdio read/write at time */
348 +       bool ack;
349 +       u32 seq;
350 +       u32 data[4];
351 +};
352 +
353  struct qca8k_ports_config {
354         bool sgmii_rx_clk_falling_edge;
355         bool sgmii_tx_clk_falling_edge;
356 @@ -354,6 +366,7 @@ struct qca8k_priv {
357         struct gpio_desc *reset_gpio;
358         unsigned int port_mtu[QCA8K_NUM_PORTS];
359         struct net_device *mgmt_master; /* Track if mdio/mib Ethernet is available */
360 +       struct qca8k_mgmt_eth_data mgmt_eth_data;
361  };
362  
363  struct qca8k_mib_desc {