enetc: Introduce basic PF and VF ENETC ethernet drivers
[linux-2.6-microblaze.git] / drivers / net / ethernet / freescale / enetc / enetc_cbdr.c
1 // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
2 /* Copyright 2017-2019 NXP */
3
4 #include "enetc.h"
5
6 static void enetc_clean_cbdr(struct enetc_si *si)
7 {
8         struct enetc_cbdr *ring = &si->cbd_ring;
9         struct enetc_cbd *dest_cbd;
10         int i, status;
11
12         i = ring->next_to_clean;
13
14         while (enetc_rd_reg(ring->cir) != i) {
15                 dest_cbd = ENETC_CBD(*ring, i);
16                 status = dest_cbd->status_flags & ENETC_CBD_STATUS_MASK;
17                 if (status)
18                         dev_warn(&si->pdev->dev, "CMD err %04x for cmd %04x\n",
19                                  status, dest_cbd->cmd);
20
21                 memset(dest_cbd, 0, sizeof(*dest_cbd));
22
23                 i = (i + 1) % ring->bd_count;
24         }
25
26         ring->next_to_clean = i;
27 }
28
29 static int enetc_cbd_unused(struct enetc_cbdr *r)
30 {
31         return (r->next_to_clean - r->next_to_use - 1 + r->bd_count) %
32                 r->bd_count;
33 }
34
35 static int enetc_send_cmd(struct enetc_si *si, struct enetc_cbd *cbd)
36 {
37         struct enetc_cbdr *ring = &si->cbd_ring;
38         int timeout = ENETC_CBDR_TIMEOUT;
39         struct enetc_cbd *dest_cbd;
40         int i;
41
42         if (unlikely(!ring->bd_base))
43                 return -EIO;
44
45         if (unlikely(!enetc_cbd_unused(ring)))
46                 enetc_clean_cbdr(si);
47
48         i = ring->next_to_use;
49         dest_cbd = ENETC_CBD(*ring, i);
50
51         /* copy command to the ring */
52         *dest_cbd = *cbd;
53         i = (i + 1) % ring->bd_count;
54
55         ring->next_to_use = i;
56         /* let H/W know BD ring has been updated */
57         enetc_wr_reg(ring->pir, i);
58
59         do {
60                 if (enetc_rd_reg(ring->cir) == i)
61                         break;
62                 udelay(10); /* cannot sleep, rtnl_lock() */
63                 timeout -= 10;
64         } while (timeout);
65
66         if (!timeout)
67                 return -EBUSY;
68
69         enetc_clean_cbdr(si);
70
71         return 0;
72 }
73
74 int enetc_clear_mac_flt_entry(struct enetc_si *si, int index)
75 {
76         struct enetc_cbd cbd;
77
78         memset(&cbd, 0, sizeof(cbd));
79
80         cbd.cls = 1;
81         cbd.status_flags = ENETC_CBD_FLAGS_SF;
82         cbd.index = cpu_to_le16(index);
83
84         return enetc_send_cmd(si, &cbd);
85 }
86
87 int enetc_set_mac_flt_entry(struct enetc_si *si, int index,
88                             char *mac_addr, int si_map)
89 {
90         struct enetc_cbd cbd;
91         u32 upper;
92         u16 lower;
93
94         memset(&cbd, 0, sizeof(cbd));
95
96         /* fill up the "set" descriptor */
97         cbd.cls = 1;
98         cbd.status_flags = ENETC_CBD_FLAGS_SF;
99         cbd.index = cpu_to_le16(index);
100         cbd.opt[3] = cpu_to_le32(si_map);
101         /* enable entry */
102         cbd.opt[0] = cpu_to_le32(BIT(31));
103
104         upper = *(const u32 *)mac_addr;
105         lower = *(const u16 *)(mac_addr + 4);
106         cbd.addr[0] = cpu_to_le32(upper);
107         cbd.addr[1] = cpu_to_le32(lower);
108
109         return enetc_send_cmd(si, &cbd);
110 }