summaryrefslogtreecommitdiffstats
path: root/drivers/net/dsa/mv88e6xxx/global1_vtu.c
blob: c3f55be873b389638ba1d7355bb7b8e317682048 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
/*
 * Marvell 88E6xxx VLAN [Spanning Tree] Translation Unit (VTU [STU]) support
 *
 * Copyright (c) 2008 Marvell Semiconductor
 * Copyright (c) 2015 CMC Electronics, Inc.
 * Copyright (c) 2017 Savoir-faire Linux, Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 */

#include "mv88e6xxx.h"
#include "global1.h"

/* Offset 0x02: VTU FID Register */

int mv88e6xxx_g1_vtu_fid_read(struct mv88e6xxx_chip *chip,
			      struct mv88e6xxx_vtu_entry *entry)
{
	u16 val;
	int err;

	err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_FID, &val);
	if (err)
		return err;

	entry->fid = val & GLOBAL_VTU_FID_MASK;

	return 0;
}

int mv88e6xxx_g1_vtu_fid_write(struct mv88e6xxx_chip *chip,
			       struct mv88e6xxx_vtu_entry *entry)
{
	u16 val = entry->fid & GLOBAL_VTU_FID_MASK;

	return mv88e6xxx_g1_write(chip, GLOBAL_VTU_FID, val);
}

/* Offset 0x03: VTU SID Register */

int mv88e6xxx_g1_vtu_sid_read(struct mv88e6xxx_chip *chip,
			      struct mv88e6xxx_vtu_entry *entry)
{
	u16 val;
	int err;

	err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_SID, &val);
	if (err)
		return err;

	entry->sid = val & GLOBAL_VTU_SID_MASK;

	return 0;
}

int mv88e6xxx_g1_vtu_sid_write(struct mv88e6xxx_chip *chip,
			       struct mv88e6xxx_vtu_entry *entry)
{
	u16 val = entry->sid & GLOBAL_VTU_SID_MASK;

	return mv88e6xxx_g1_write(chip, GLOBAL_VTU_SID, val);
}

/* Offset 0x05: VTU Operation Register */

int mv88e6xxx_g1_vtu_op_wait(struct mv88e6xxx_chip *chip)
{
	return mv88e6xxx_g1_wait(chip, GLOBAL_VTU_OP, GLOBAL_VTU_OP_BUSY);
}

int mv88e6xxx_g1_vtu_op(struct mv88e6xxx_chip *chip, u16 op)
{
	int err;

	err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_OP, op);
	if (err)
		return err;

	return mv88e6xxx_g1_vtu_op_wait(chip);
}

/* Offset 0x06: VTU VID Register */

int mv88e6xxx_g1_vtu_vid_read(struct mv88e6xxx_chip *chip,
			      struct mv88e6xxx_vtu_entry *entry)
{
	u16 val;
	int err;

	err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_VID, &val);
	if (err)
		return err;

	entry->vid = val & 0xfff;
	entry->valid = !!(val & GLOBAL_VTU_VID_VALID);

	return 0;
}

int mv88e6xxx_g1_vtu_vid_write(struct mv88e6xxx_chip *chip,
			       struct mv88e6xxx_vtu_entry *entry)
{
	u16 val = entry->vid & 0xfff;

	if (entry->valid)
		val |= GLOBAL_VTU_VID_VALID;

	return mv88e6xxx_g1_write(chip, GLOBAL_VTU_VID, val);
}

/* VLAN Translation Unit Operations */

int mv88e6xxx_g1_vtu_getnext(struct mv88e6xxx_chip *chip,
			     struct mv88e6xxx_vtu_entry *entry)
{
	int err;

	err = mv88e6xxx_g1_vtu_op_wait(chip);
	if (err)
		return err;

	/* To get the next higher active VID, the VTU GetNext operation can be
	 * started again without setting the VID registers since it already
	 * contains the last VID.
	 *
	 * To save a few hardware accesses and abstract this to the caller,
	 * write the VID only once, when the entry is given as invalid.
	 */
	if (!entry->valid) {
		err = mv88e6xxx_g1_vtu_vid_write(chip, entry);
		if (err)
			return err;
	}

	err = mv88e6xxx_g1_vtu_op(chip, GLOBAL_VTU_OP_VTU_GET_NEXT);
	if (err)
		return err;

	return mv88e6xxx_g1_vtu_vid_read(chip, entry);
}

int mv88e6xxx_g1_vtu_flush(struct mv88e6xxx_chip *chip)
{
	int err;

	err = mv88e6xxx_g1_vtu_op_wait(chip);
	if (err)
		return err;

	return mv88e6xxx_g1_vtu_op(chip, GLOBAL_VTU_OP_FLUSH_ALL);
}