2 * Marvell 88E6xxx Address Translation Unit (ATU) support
4 * Copyright (c) 2008 Marvell Semiconductor
5 * Copyright (c) 2017 Savoir-faire Linux, Inc.
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.
12 #include <linux/interrupt.h>
13 #include <linux/irqdomain.h>
18 /* Offset 0x01: ATU FID Register */
20 static int mv88e6xxx_g1_atu_fid_write(struct mv88e6xxx_chip
*chip
, u16 fid
)
22 return mv88e6xxx_g1_write(chip
, MV88E6352_G1_ATU_FID
, fid
& 0xfff);
25 /* Offset 0x0A: ATU Control Register */
27 int mv88e6xxx_g1_atu_set_learn2all(struct mv88e6xxx_chip
*chip
, bool learn2all
)
32 err
= mv88e6xxx_g1_read(chip
, MV88E6XXX_G1_ATU_CTL
, &val
);
37 val
|= MV88E6XXX_G1_ATU_CTL_LEARN2ALL
;
39 val
&= ~MV88E6XXX_G1_ATU_CTL_LEARN2ALL
;
41 return mv88e6xxx_g1_write(chip
, MV88E6XXX_G1_ATU_CTL
, val
);
44 int mv88e6xxx_g1_atu_set_age_time(struct mv88e6xxx_chip
*chip
,
47 const unsigned int coeff
= chip
->info
->age_time_coeff
;
48 const unsigned int min
= 0x01 * coeff
;
49 const unsigned int max
= 0xff * coeff
;
54 if (msecs
< min
|| msecs
> max
)
57 /* Round to nearest multiple of coeff */
58 age_time
= (msecs
+ coeff
/ 2) / coeff
;
60 err
= mv88e6xxx_g1_read(chip
, MV88E6XXX_G1_ATU_CTL
, &val
);
64 /* AgeTime is 11:4 bits */
68 err
= mv88e6xxx_g1_write(chip
, MV88E6XXX_G1_ATU_CTL
, val
);
72 dev_dbg(chip
->dev
, "AgeTime set to 0x%02x (%d ms)\n", age_time
,
78 /* Offset 0x0B: ATU Operation Register */
80 static int mv88e6xxx_g1_atu_op_wait(struct mv88e6xxx_chip
*chip
)
82 return mv88e6xxx_g1_wait(chip
, MV88E6XXX_G1_ATU_OP
,
83 MV88E6XXX_G1_ATU_OP_BUSY
);
86 static int mv88e6xxx_g1_atu_op(struct mv88e6xxx_chip
*chip
, u16 fid
, u16 op
)
91 /* FID bits are dispatched all around gradually as more are supported */
92 if (mv88e6xxx_num_databases(chip
) > 256) {
93 err
= mv88e6xxx_g1_atu_fid_write(chip
, fid
);
97 if (mv88e6xxx_num_databases(chip
) > 16) {
98 /* ATU DBNum[7:4] are located in ATU Control 15:12 */
99 err
= mv88e6xxx_g1_read(chip
, MV88E6XXX_G1_ATU_CTL
,
104 val
= (val
& 0x0fff) | ((fid
<< 8) & 0xf000);
105 err
= mv88e6xxx_g1_write(chip
, MV88E6XXX_G1_ATU_CTL
,
111 /* ATU DBNum[3:0] are located in ATU Operation 3:0 */
115 err
= mv88e6xxx_g1_write(chip
, MV88E6XXX_G1_ATU_OP
,
116 MV88E6XXX_G1_ATU_OP_BUSY
| op
);
120 return mv88e6xxx_g1_atu_op_wait(chip
);
123 /* Offset 0x0C: ATU Data Register */
125 static int mv88e6xxx_g1_atu_data_read(struct mv88e6xxx_chip
*chip
,
126 struct mv88e6xxx_atu_entry
*entry
)
131 err
= mv88e6xxx_g1_read(chip
, MV88E6XXX_G1_ATU_DATA
, &val
);
135 entry
->state
= val
& 0xf;
136 if (entry
->state
!= MV88E6XXX_G1_ATU_DATA_STATE_UNUSED
) {
137 entry
->trunk
= !!(val
& MV88E6XXX_G1_ATU_DATA_TRUNK
);
138 entry
->portvec
= (val
>> 4) & mv88e6xxx_port_mask(chip
);
144 static int mv88e6xxx_g1_atu_data_write(struct mv88e6xxx_chip
*chip
,
145 struct mv88e6xxx_atu_entry
*entry
)
147 u16 data
= entry
->state
& 0xf;
149 if (entry
->state
!= MV88E6XXX_G1_ATU_DATA_STATE_UNUSED
) {
151 data
|= MV88E6XXX_G1_ATU_DATA_TRUNK
;
153 data
|= (entry
->portvec
& mv88e6xxx_port_mask(chip
)) << 4;
156 return mv88e6xxx_g1_write(chip
, MV88E6XXX_G1_ATU_DATA
, data
);
159 /* Offset 0x0D: ATU MAC Address Register Bytes 0 & 1
160 * Offset 0x0E: ATU MAC Address Register Bytes 2 & 3
161 * Offset 0x0F: ATU MAC Address Register Bytes 4 & 5
164 static int mv88e6xxx_g1_atu_mac_read(struct mv88e6xxx_chip
*chip
,
165 struct mv88e6xxx_atu_entry
*entry
)
170 for (i
= 0; i
< 3; i
++) {
171 err
= mv88e6xxx_g1_read(chip
, MV88E6XXX_G1_ATU_MAC01
+ i
, &val
);
175 entry
->mac
[i
* 2] = val
>> 8;
176 entry
->mac
[i
* 2 + 1] = val
& 0xff;
182 static int mv88e6xxx_g1_atu_mac_write(struct mv88e6xxx_chip
*chip
,
183 struct mv88e6xxx_atu_entry
*entry
)
188 for (i
= 0; i
< 3; i
++) {
189 val
= (entry
->mac
[i
* 2] << 8) | entry
->mac
[i
* 2 + 1];
190 err
= mv88e6xxx_g1_write(chip
, MV88E6XXX_G1_ATU_MAC01
+ i
, val
);
198 /* Address Translation Unit operations */
200 int mv88e6xxx_g1_atu_getnext(struct mv88e6xxx_chip
*chip
, u16 fid
,
201 struct mv88e6xxx_atu_entry
*entry
)
205 err
= mv88e6xxx_g1_atu_op_wait(chip
);
209 /* Write the MAC address to iterate from only once */
210 if (entry
->state
== MV88E6XXX_G1_ATU_DATA_STATE_UNUSED
) {
211 err
= mv88e6xxx_g1_atu_mac_write(chip
, entry
);
216 err
= mv88e6xxx_g1_atu_op(chip
, fid
, MV88E6XXX_G1_ATU_OP_GET_NEXT_DB
);
220 err
= mv88e6xxx_g1_atu_data_read(chip
, entry
);
224 return mv88e6xxx_g1_atu_mac_read(chip
, entry
);
227 int mv88e6xxx_g1_atu_loadpurge(struct mv88e6xxx_chip
*chip
, u16 fid
,
228 struct mv88e6xxx_atu_entry
*entry
)
232 err
= mv88e6xxx_g1_atu_op_wait(chip
);
236 err
= mv88e6xxx_g1_atu_mac_write(chip
, entry
);
240 err
= mv88e6xxx_g1_atu_data_write(chip
, entry
);
244 return mv88e6xxx_g1_atu_op(chip
, fid
, MV88E6XXX_G1_ATU_OP_LOAD_DB
);
247 static int mv88e6xxx_g1_atu_flushmove(struct mv88e6xxx_chip
*chip
, u16 fid
,
248 struct mv88e6xxx_atu_entry
*entry
,
254 err
= mv88e6xxx_g1_atu_op_wait(chip
);
258 err
= mv88e6xxx_g1_atu_data_write(chip
, entry
);
262 /* Flush/Move all or non-static entries from all or a given database */
264 op
= MV88E6XXX_G1_ATU_OP_FLUSH_MOVE_ALL_DB
;
266 op
= MV88E6XXX_G1_ATU_OP_FLUSH_MOVE_NON_STATIC_DB
;
268 op
= MV88E6XXX_G1_ATU_OP_FLUSH_MOVE_ALL
;
270 op
= MV88E6XXX_G1_ATU_OP_FLUSH_MOVE_NON_STATIC
;
272 return mv88e6xxx_g1_atu_op(chip
, fid
, op
);
275 int mv88e6xxx_g1_atu_flush(struct mv88e6xxx_chip
*chip
, u16 fid
, bool all
)
277 struct mv88e6xxx_atu_entry entry
= {
278 .state
= 0, /* Null EntryState means Flush */
281 return mv88e6xxx_g1_atu_flushmove(chip
, fid
, &entry
, all
);
284 static int mv88e6xxx_g1_atu_move(struct mv88e6xxx_chip
*chip
, u16 fid
,
285 int from_port
, int to_port
, bool all
)
287 struct mv88e6xxx_atu_entry entry
= { 0 };
291 if (!chip
->info
->atu_move_port_mask
)
294 mask
= chip
->info
->atu_move_port_mask
;
295 shift
= bitmap_weight(&mask
, 16);
297 entry
.state
= 0xf, /* Full EntryState means Move */
298 entry
.portvec
= from_port
& mask
;
299 entry
.portvec
|= (to_port
& mask
) << shift
;
301 return mv88e6xxx_g1_atu_flushmove(chip
, fid
, &entry
, all
);
304 int mv88e6xxx_g1_atu_remove(struct mv88e6xxx_chip
*chip
, u16 fid
, int port
,
307 int from_port
= port
;
308 int to_port
= chip
->info
->atu_move_port_mask
;
310 return mv88e6xxx_g1_atu_move(chip
, fid
, from_port
, to_port
, all
);
313 static irqreturn_t
mv88e6xxx_g1_atu_prob_irq_thread_fn(int irq
, void *dev_id
)
315 struct mv88e6xxx_chip
*chip
= dev_id
;
316 struct mv88e6xxx_atu_entry entry
;
321 mutex_lock(&chip
->reg_lock
);
323 err
= mv88e6xxx_g1_atu_op(chip
, 0,
324 MV88E6XXX_G1_ATU_OP_GET_CLR_VIOLATION
);
328 err
= mv88e6xxx_g1_read(chip
, MV88E6XXX_G1_ATU_OP
, &val
);
332 err
= mv88e6xxx_g1_atu_data_read(chip
, &entry
);
336 err
= mv88e6xxx_g1_atu_mac_read(chip
, &entry
);
342 if (val
& MV88E6XXX_G1_ATU_OP_AGE_OUT_VIOLATION
) {
343 dev_err_ratelimited(chip
->dev
,
344 "ATU age out violation for %pM\n",
348 if (val
& MV88E6XXX_G1_ATU_OP_MEMBER_VIOLATION
) {
349 dev_err_ratelimited(chip
->dev
,
350 "ATU member violation for %pM portvec %x spid %d\n",
351 entry
.mac
, entry
.portvec
, spid
);
352 chip
->ports
[spid
].atu_member_violation
++;
355 if (val
& MV88E6XXX_G1_ATU_OP_MISS_VIOLATION
) {
356 dev_err_ratelimited(chip
->dev
,
357 "ATU miss violation for %pM portvec %x spid %d\n",
358 entry
.mac
, entry
.portvec
, spid
);
359 chip
->ports
[spid
].atu_miss_violation
++;
362 if (val
& MV88E6XXX_G1_ATU_OP_FULL_VIOLATION
) {
363 dev_err_ratelimited(chip
->dev
,
364 "ATU full violation for %pM portvec %x spid %d\n",
365 entry
.mac
, entry
.portvec
, spid
);
366 chip
->ports
[spid
].atu_full_violation
++;
368 mutex_unlock(&chip
->reg_lock
);
373 mutex_unlock(&chip
->reg_lock
);
375 dev_err(chip
->dev
, "ATU problem: error %d while handling interrupt\n",
380 int mv88e6xxx_g1_atu_prob_irq_setup(struct mv88e6xxx_chip
*chip
)
384 chip
->atu_prob_irq
= irq_find_mapping(chip
->g1_irq
.domain
,
385 MV88E6XXX_G1_STS_IRQ_ATU_PROB
);
386 if (chip
->atu_prob_irq
< 0)
387 return chip
->atu_prob_irq
;
389 err
= request_threaded_irq(chip
->atu_prob_irq
, NULL
,
390 mv88e6xxx_g1_atu_prob_irq_thread_fn
,
391 IRQF_ONESHOT
, "mv88e6xxx-g1-atu-prob",
394 irq_dispose_mapping(chip
->atu_prob_irq
);
399 void mv88e6xxx_g1_atu_prob_irq_free(struct mv88e6xxx_chip
*chip
)
401 free_irq(chip
->atu_prob_irq
, chip
);
402 irq_dispose_mapping(chip
->atu_prob_irq
);