blob: 17b54441b0e904655d276ca11a78f2d9fdbe719a [file] [log] [blame]
Vivien Didelot18abed22016-11-04 03:23:26 +01001/*
2 * Marvell 88E6xxx Switch Port Registers support
3 *
4 * Copyright (c) 2008 Marvell Semiconductor
5 *
6 * Copyright (c) 2016 Vivien Didelot <vivien.didelot@savoirfairelinux.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 */
13
14#include "mv88e6xxx.h"
15#include "port.h"
16
17int mv88e6xxx_port_read(struct mv88e6xxx_chip *chip, int port, int reg,
18 u16 *val)
19{
20 int addr = chip->info->port_base_addr + port;
21
22 return mv88e6xxx_read(chip, addr, reg, val);
23}
24
25int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg,
26 u16 val)
27{
28 int addr = chip->info->port_base_addr + port;
29
30 return mv88e6xxx_write(chip, addr, reg, val);
31}
Vivien Didelote28def332016-11-04 03:23:27 +010032
Vivien Didelot08ef7f12016-11-04 03:23:32 +010033/* Offset 0x01: MAC (or PCS or Physical) Control Register
34 *
35 * Link, Duplex and Flow Control have one force bit, one value bit.
36 */
37
38int mv88e6xxx_port_set_link(struct mv88e6xxx_chip *chip, int port, int link)
39{
40 u16 reg;
41 int err;
42
43 err = mv88e6xxx_port_read(chip, port, PORT_PCS_CTRL, &reg);
44 if (err)
45 return err;
46
47 reg &= ~(PORT_PCS_CTRL_FORCE_LINK | PORT_PCS_CTRL_LINK_UP);
48
49 switch (link) {
50 case LINK_FORCED_DOWN:
51 reg |= PORT_PCS_CTRL_FORCE_LINK;
52 break;
53 case LINK_FORCED_UP:
54 reg |= PORT_PCS_CTRL_FORCE_LINK | PORT_PCS_CTRL_LINK_UP;
55 break;
56 case LINK_UNFORCED:
57 /* normal link detection */
58 break;
59 default:
60 return -EINVAL;
61 }
62
63 err = mv88e6xxx_port_write(chip, port, PORT_PCS_CTRL, reg);
64 if (err)
65 return err;
66
67 netdev_dbg(chip->ds->ports[port].netdev, "%s link %s\n",
68 reg & PORT_PCS_CTRL_FORCE_LINK ? "Force" : "Unforce",
69 reg & PORT_PCS_CTRL_LINK_UP ? "up" : "down");
70
71 return 0;
72}
73
Vivien Didelot7f1ae072016-11-04 03:23:33 +010074int mv88e6xxx_port_set_duplex(struct mv88e6xxx_chip *chip, int port, int dup)
75{
76 u16 reg;
77 int err;
78
79 err = mv88e6xxx_port_read(chip, port, PORT_PCS_CTRL, &reg);
80 if (err)
81 return err;
82
83 reg &= ~(PORT_PCS_CTRL_FORCE_DUPLEX | PORT_PCS_CTRL_DUPLEX_FULL);
84
85 switch (dup) {
86 case DUPLEX_HALF:
87 reg |= PORT_PCS_CTRL_FORCE_DUPLEX;
88 break;
89 case DUPLEX_FULL:
90 reg |= PORT_PCS_CTRL_FORCE_DUPLEX | PORT_PCS_CTRL_DUPLEX_FULL;
91 break;
92 case DUPLEX_UNFORCED:
93 /* normal duplex detection */
94 break;
95 default:
96 return -EINVAL;
97 }
98
99 err = mv88e6xxx_port_write(chip, port, PORT_PCS_CTRL, reg);
100 if (err)
101 return err;
102
103 netdev_dbg(chip->ds->ports[port].netdev, "%s %s duplex\n",
104 reg & PORT_PCS_CTRL_FORCE_DUPLEX ? "Force" : "Unforce",
105 reg & PORT_PCS_CTRL_DUPLEX_FULL ? "full" : "half");
106
107 return 0;
108}
109
Vivien Didelote28def332016-11-04 03:23:27 +0100110/* Offset 0x04: Port Control Register */
111
112static const char * const mv88e6xxx_port_state_names[] = {
113 [PORT_CONTROL_STATE_DISABLED] = "Disabled",
114 [PORT_CONTROL_STATE_BLOCKING] = "Blocking/Listening",
115 [PORT_CONTROL_STATE_LEARNING] = "Learning",
116 [PORT_CONTROL_STATE_FORWARDING] = "Forwarding",
117};
118
119int mv88e6xxx_port_set_state(struct mv88e6xxx_chip *chip, int port, u8 state)
120{
121 u16 reg;
122 int err;
123
124 err = mv88e6xxx_port_read(chip, port, PORT_CONTROL, &reg);
125 if (err)
126 return err;
127
128 reg &= ~PORT_CONTROL_STATE_MASK;
129 reg |= state;
130
131 err = mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg);
132 if (err)
133 return err;
134
135 netdev_dbg(chip->ds->ports[port].netdev, "PortState set to %s\n",
136 mv88e6xxx_port_state_names[state]);
137
138 return 0;
139}
Vivien Didelot5a7921f2016-11-04 03:23:28 +0100140
Vivien Didelotb4e48c52016-11-04 03:23:29 +0100141/* Offset 0x05: Port Control 1 */
142
Vivien Didelot5a7921f2016-11-04 03:23:28 +0100143/* Offset 0x06: Port Based VLAN Map */
144
145int mv88e6xxx_port_set_vlan_map(struct mv88e6xxx_chip *chip, int port, u16 map)
146{
147 const u16 mask = GENMASK(mv88e6xxx_num_ports(chip) - 1, 0);
148 u16 reg;
149 int err;
150
151 err = mv88e6xxx_port_read(chip, port, PORT_BASE_VLAN, &reg);
152 if (err)
153 return err;
154
155 reg &= ~mask;
156 reg |= map & mask;
157
158 err = mv88e6xxx_port_write(chip, port, PORT_BASE_VLAN, reg);
159 if (err)
160 return err;
161
162 netdev_dbg(chip->ds->ports[port].netdev, "VLANTable set to %.3x\n",
163 map);
164
165 return 0;
166}
Vivien Didelotb4e48c52016-11-04 03:23:29 +0100167
168int mv88e6xxx_port_get_fid(struct mv88e6xxx_chip *chip, int port, u16 *fid)
169{
170 const u16 upper_mask = (mv88e6xxx_num_databases(chip) - 1) >> 4;
171 u16 reg;
172 int err;
173
174 /* Port's default FID lower 4 bits are located in reg 0x06, offset 12 */
175 err = mv88e6xxx_port_read(chip, port, PORT_BASE_VLAN, &reg);
176 if (err)
177 return err;
178
179 *fid = (reg & 0xf000) >> 12;
180
181 /* Port's default FID upper bits are located in reg 0x05, offset 0 */
182 if (upper_mask) {
183 err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_1, &reg);
184 if (err)
185 return err;
186
187 *fid |= (reg & upper_mask) << 4;
188 }
189
190 return 0;
191}
192
193int mv88e6xxx_port_set_fid(struct mv88e6xxx_chip *chip, int port, u16 fid)
194{
195 const u16 upper_mask = (mv88e6xxx_num_databases(chip) - 1) >> 4;
196 u16 reg;
197 int err;
198
199 if (fid >= mv88e6xxx_num_databases(chip))
200 return -EINVAL;
201
202 /* Port's default FID lower 4 bits are located in reg 0x06, offset 12 */
203 err = mv88e6xxx_port_read(chip, port, PORT_BASE_VLAN, &reg);
204 if (err)
205 return err;
206
207 reg &= 0x0fff;
208 reg |= (fid & 0x000f) << 12;
209
210 err = mv88e6xxx_port_write(chip, port, PORT_BASE_VLAN, reg);
211 if (err)
212 return err;
213
214 /* Port's default FID upper bits are located in reg 0x05, offset 0 */
215 if (upper_mask) {
216 err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_1, &reg);
217 if (err)
218 return err;
219
220 reg &= ~upper_mask;
221 reg |= (fid >> 4) & upper_mask;
222
223 err = mv88e6xxx_port_write(chip, port, PORT_CONTROL_1, reg);
224 if (err)
225 return err;
226 }
227
228 netdev_dbg(chip->ds->ports[port].netdev, "FID set to %u\n", fid);
229
230 return 0;
231}
Vivien Didelot77064f32016-11-04 03:23:30 +0100232
233/* Offset 0x07: Default Port VLAN ID & Priority */
234
235int mv88e6xxx_port_get_pvid(struct mv88e6xxx_chip *chip, int port, u16 *pvid)
236{
237 u16 reg;
238 int err;
239
240 err = mv88e6xxx_port_read(chip, port, PORT_DEFAULT_VLAN, &reg);
241 if (err)
242 return err;
243
244 *pvid = reg & PORT_DEFAULT_VLAN_MASK;
245
246 return 0;
247}
248
249int mv88e6xxx_port_set_pvid(struct mv88e6xxx_chip *chip, int port, u16 pvid)
250{
251 u16 reg;
252 int err;
253
254 err = mv88e6xxx_port_read(chip, port, PORT_DEFAULT_VLAN, &reg);
255 if (err)
256 return err;
257
258 reg &= ~PORT_DEFAULT_VLAN_MASK;
259 reg |= pvid & PORT_DEFAULT_VLAN_MASK;
260
261 err = mv88e6xxx_port_write(chip, port, PORT_DEFAULT_VLAN, reg);
262 if (err)
263 return err;
264
265 netdev_dbg(chip->ds->ports[port].netdev, "DefaultVID set to %u\n",
266 pvid);
267
268 return 0;
269}
Vivien Didelot385a0992016-11-04 03:23:31 +0100270
271/* Offset 0x08: Port Control 2 Register */
272
273static const char * const mv88e6xxx_port_8021q_mode_names[] = {
274 [PORT_CONTROL_2_8021Q_DISABLED] = "Disabled",
275 [PORT_CONTROL_2_8021Q_FALLBACK] = "Fallback",
276 [PORT_CONTROL_2_8021Q_CHECK] = "Check",
277 [PORT_CONTROL_2_8021Q_SECURE] = "Secure",
278};
279
280int mv88e6xxx_port_set_8021q_mode(struct mv88e6xxx_chip *chip, int port,
281 u16 mode)
282{
283 u16 reg;
284 int err;
285
286 err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_2, &reg);
287 if (err)
288 return err;
289
290 reg &= ~PORT_CONTROL_2_8021Q_MASK;
291 reg |= mode & PORT_CONTROL_2_8021Q_MASK;
292
293 err = mv88e6xxx_port_write(chip, port, PORT_CONTROL_2, reg);
294 if (err)
295 return err;
296
297 netdev_dbg(chip->ds->ports[port].netdev, "802.1QMode set to %s\n",
298 mv88e6xxx_port_8021q_mode_names[mode]);
299
300 return 0;
301}