Merge tag 'wireless-drivers-for-davem-2017-04-03' of git://git.kernel.org/pub/scm...
[linux-2.6-block.git] / drivers / net / dsa / mv88e6xxx / global1_atu.c
1 /*
2  * Marvell 88E6xxx Address Translation Unit (ATU) support
3  *
4  * Copyright (c) 2008 Marvell Semiconductor
5  * Copyright (c) 2017 Savoir-faire Linux, Inc.
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  */
12
13 #include "mv88e6xxx.h"
14 #include "global1.h"
15
16 /* Offset 0x01: ATU FID Register */
17
18 static int mv88e6xxx_g1_atu_fid_write(struct mv88e6xxx_chip *chip, u16 fid)
19 {
20         return mv88e6xxx_g1_write(chip, GLOBAL_ATU_FID, fid & 0xfff);
21 }
22
23 /* Offset 0x0A: ATU Control Register */
24
25 int mv88e6xxx_g1_atu_set_learn2all(struct mv88e6xxx_chip *chip, bool learn2all)
26 {
27         u16 val;
28         int err;
29
30         err = mv88e6xxx_g1_read(chip, GLOBAL_ATU_CONTROL, &val);
31         if (err)
32                 return err;
33
34         if (learn2all)
35                 val |= GLOBAL_ATU_CONTROL_LEARN2ALL;
36         else
37                 val &= ~GLOBAL_ATU_CONTROL_LEARN2ALL;
38
39         return mv88e6xxx_g1_write(chip, GLOBAL_ATU_CONTROL, val);
40 }
41
42 int mv88e6xxx_g1_atu_set_age_time(struct mv88e6xxx_chip *chip,
43                                   unsigned int msecs)
44 {
45         const unsigned int coeff = chip->info->age_time_coeff;
46         const unsigned int min = 0x01 * coeff;
47         const unsigned int max = 0xff * coeff;
48         u8 age_time;
49         u16 val;
50         int err;
51
52         if (msecs < min || msecs > max)
53                 return -ERANGE;
54
55         /* Round to nearest multiple of coeff */
56         age_time = (msecs + coeff / 2) / coeff;
57
58         err = mv88e6xxx_g1_read(chip, GLOBAL_ATU_CONTROL, &val);
59         if (err)
60                 return err;
61
62         /* AgeTime is 11:4 bits */
63         val &= ~0xff0;
64         val |= age_time << 4;
65
66         err = mv88e6xxx_g1_write(chip, GLOBAL_ATU_CONTROL, val);
67         if (err)
68                 return err;
69
70         dev_dbg(chip->dev, "AgeTime set to 0x%02x (%d ms)\n", age_time,
71                 age_time * coeff);
72
73         return 0;
74 }
75
76 /* Offset 0x0B: ATU Operation Register */
77
78 static int mv88e6xxx_g1_atu_op_wait(struct mv88e6xxx_chip *chip)
79 {
80         return mv88e6xxx_g1_wait(chip, GLOBAL_ATU_OP, GLOBAL_ATU_OP_BUSY);
81 }
82
83 static int mv88e6xxx_g1_atu_op(struct mv88e6xxx_chip *chip, u16 fid, u16 op)
84 {
85         u16 val;
86         int err;
87
88         /* FID bits are dispatched all around gradually as more are supported */
89         if (mv88e6xxx_num_databases(chip) > 256) {
90                 err = mv88e6xxx_g1_atu_fid_write(chip, fid);
91                 if (err)
92                         return err;
93         } else {
94                 if (mv88e6xxx_num_databases(chip) > 16) {
95                         /* ATU DBNum[7:4] are located in ATU Control 15:12 */
96                         err = mv88e6xxx_g1_read(chip, GLOBAL_ATU_CONTROL, &val);
97                         if (err)
98                                 return err;
99
100                         val = (val & 0x0fff) | ((fid << 8) & 0xf000);
101                         err = mv88e6xxx_g1_write(chip, GLOBAL_ATU_CONTROL, val);
102                         if (err)
103                                 return err;
104                 }
105
106                 /* ATU DBNum[3:0] are located in ATU Operation 3:0 */
107                 op |= fid & 0xf;
108         }
109
110         err = mv88e6xxx_g1_write(chip, GLOBAL_ATU_OP, op);
111         if (err)
112                 return err;
113
114         return mv88e6xxx_g1_atu_op_wait(chip);
115 }
116
117 /* Offset 0x0C: ATU Data Register */
118
119 static int mv88e6xxx_g1_atu_data_read(struct mv88e6xxx_chip *chip,
120                                       struct mv88e6xxx_atu_entry *entry)
121 {
122         u16 val;
123         int err;
124
125         err = mv88e6xxx_g1_read(chip, GLOBAL_ATU_DATA, &val);
126         if (err)
127                 return err;
128
129         entry->state = val & 0xf;
130         if (entry->state != GLOBAL_ATU_DATA_STATE_UNUSED) {
131                 entry->trunk = !!(val & GLOBAL_ATU_DATA_TRUNK);
132                 entry->portvec = (val >> 4) & mv88e6xxx_port_mask(chip);
133         }
134
135         return 0;
136 }
137
138 static int mv88e6xxx_g1_atu_data_write(struct mv88e6xxx_chip *chip,
139                                        struct mv88e6xxx_atu_entry *entry)
140 {
141         u16 data = entry->state & 0xf;
142
143         if (entry->state != GLOBAL_ATU_DATA_STATE_UNUSED) {
144                 if (entry->trunk)
145                         data |= GLOBAL_ATU_DATA_TRUNK;
146
147                 data |= (entry->portvec & mv88e6xxx_port_mask(chip)) << 4;
148         }
149
150         return mv88e6xxx_g1_write(chip, GLOBAL_ATU_DATA, data);
151 }
152
153 /* Offset 0x0D: ATU MAC Address Register Bytes 0 & 1
154  * Offset 0x0E: ATU MAC Address Register Bytes 2 & 3
155  * Offset 0x0F: ATU MAC Address Register Bytes 4 & 5
156  */
157
158 static int mv88e6xxx_g1_atu_mac_read(struct mv88e6xxx_chip *chip,
159                                      struct mv88e6xxx_atu_entry *entry)
160 {
161         u16 val;
162         int i, err;
163
164         for (i = 0; i < 3; i++) {
165                 err = mv88e6xxx_g1_read(chip, GLOBAL_ATU_MAC_01 + i, &val);
166                 if (err)
167                         return err;
168
169                 entry->mac[i * 2] = val >> 8;
170                 entry->mac[i * 2 + 1] = val & 0xff;
171         }
172
173         return 0;
174 }
175
176 static int mv88e6xxx_g1_atu_mac_write(struct mv88e6xxx_chip *chip,
177                                       struct mv88e6xxx_atu_entry *entry)
178 {
179         u16 val;
180         int i, err;
181
182         for (i = 0; i < 3; i++) {
183                 val = (entry->mac[i * 2] << 8) | entry->mac[i * 2 + 1];
184                 err = mv88e6xxx_g1_write(chip, GLOBAL_ATU_MAC_01 + i, val);
185                 if (err)
186                         return err;
187         }
188
189         return 0;
190 }
191
192 /* Address Translation Unit operations */
193
194 int mv88e6xxx_g1_atu_getnext(struct mv88e6xxx_chip *chip, u16 fid,
195                              struct mv88e6xxx_atu_entry *entry)
196 {
197         int err;
198
199         err = mv88e6xxx_g1_atu_op_wait(chip);
200         if (err)
201                 return err;
202
203         /* Write the MAC address to iterate from only once */
204         if (entry->state == GLOBAL_ATU_DATA_STATE_UNUSED) {
205                 err = mv88e6xxx_g1_atu_mac_write(chip, entry);
206                 if (err)
207                         return err;
208         }
209
210         err = mv88e6xxx_g1_atu_op(chip, fid, GLOBAL_ATU_OP_GET_NEXT_DB);
211         if (err)
212                 return err;
213
214         err = mv88e6xxx_g1_atu_data_read(chip, entry);
215         if (err)
216                 return err;
217
218         return mv88e6xxx_g1_atu_mac_read(chip, entry);
219 }
220
221 int mv88e6xxx_g1_atu_loadpurge(struct mv88e6xxx_chip *chip, u16 fid,
222                                struct mv88e6xxx_atu_entry *entry)
223 {
224         int err;
225
226         err = mv88e6xxx_g1_atu_op_wait(chip);
227         if (err)
228                 return err;
229
230         err = mv88e6xxx_g1_atu_mac_write(chip, entry);
231         if (err)
232                 return err;
233
234         err = mv88e6xxx_g1_atu_data_write(chip, entry);
235         if (err)
236                 return err;
237
238         return mv88e6xxx_g1_atu_op(chip, fid, GLOBAL_ATU_OP_LOAD_DB);
239 }
240
241 static int mv88e6xxx_g1_atu_flushmove(struct mv88e6xxx_chip *chip, u16 fid,
242                                       struct mv88e6xxx_atu_entry *entry,
243                                       bool all)
244 {
245         u16 op;
246         int err;
247
248         err = mv88e6xxx_g1_atu_op_wait(chip);
249         if (err)
250                 return err;
251
252         err = mv88e6xxx_g1_atu_data_write(chip, entry);
253         if (err)
254                 return err;
255
256         /* Flush/Move all or non-static entries from all or a given database */
257         if (all && fid)
258                 op = GLOBAL_ATU_OP_FLUSH_MOVE_ALL_DB;
259         else if (fid)
260                 op = GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC_DB;
261         else if (all)
262                 op = GLOBAL_ATU_OP_FLUSH_MOVE_ALL;
263         else
264                 op = GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC;
265
266         return mv88e6xxx_g1_atu_op(chip, fid, op);
267 }
268
269 int mv88e6xxx_g1_atu_flush(struct mv88e6xxx_chip *chip, u16 fid, bool all)
270 {
271         struct mv88e6xxx_atu_entry entry = {
272                 .state = 0, /* Null EntryState means Flush */
273         };
274
275         return mv88e6xxx_g1_atu_flushmove(chip, fid, &entry, all);
276 }
277
278 static int mv88e6xxx_g1_atu_move(struct mv88e6xxx_chip *chip, u16 fid,
279                                  int from_port, int to_port, bool all)
280 {
281         struct mv88e6xxx_atu_entry entry = { 0 };
282         unsigned long mask;
283         int shift;
284
285         if (!chip->info->atu_move_port_mask)
286                 return -EOPNOTSUPP;
287
288         mask = chip->info->atu_move_port_mask;
289         shift = bitmap_weight(&mask, 16);
290
291         entry.state = 0xf, /* Full EntryState means Move */
292         entry.portvec = from_port & mask;
293         entry.portvec |= (to_port & mask) << shift;
294
295         return mv88e6xxx_g1_atu_flushmove(chip, fid, &entry, all);
296 }
297
298 int mv88e6xxx_g1_atu_remove(struct mv88e6xxx_chip *chip, u16 fid, int port,
299                             bool all)
300 {
301         int from_port = port;
302         int to_port = chip->info->atu_move_port_mask;
303
304         return mv88e6xxx_g1_atu_move(chip, fid, from_port, to_port, all);
305 }