net: dsa: mv88e6xxx: add port 802.1Q mode setter
[linux-2.6-block.git] / drivers / net / dsa / mv88e6xxx / port.c
CommitLineData
18abed21
VD
1/*
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}
e28def33
VD
32
33/* Offset 0x04: Port Control Register */
34
35static const char * const mv88e6xxx_port_state_names[] = {
36 [PORT_CONTROL_STATE_DISABLED] = "Disabled",
37 [PORT_CONTROL_STATE_BLOCKING] = "Blocking/Listening",
38 [PORT_CONTROL_STATE_LEARNING] = "Learning",
39 [PORT_CONTROL_STATE_FORWARDING] = "Forwarding",
40};
41
42int mv88e6xxx_port_set_state(struct mv88e6xxx_chip *chip, int port, u8 state)
43{
44 u16 reg;
45 int err;
46
47 err = mv88e6xxx_port_read(chip, port, PORT_CONTROL, &reg);
48 if (err)
49 return err;
50
51 reg &= ~PORT_CONTROL_STATE_MASK;
52 reg |= state;
53
54 err = mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg);
55 if (err)
56 return err;
57
58 netdev_dbg(chip->ds->ports[port].netdev, "PortState set to %s\n",
59 mv88e6xxx_port_state_names[state]);
60
61 return 0;
62}
5a7921f4 63
b4e48c50
VD
64/* Offset 0x05: Port Control 1 */
65
5a7921f4
VD
66/* Offset 0x06: Port Based VLAN Map */
67
68int mv88e6xxx_port_set_vlan_map(struct mv88e6xxx_chip *chip, int port, u16 map)
69{
70 const u16 mask = GENMASK(mv88e6xxx_num_ports(chip) - 1, 0);
71 u16 reg;
72 int err;
73
74 err = mv88e6xxx_port_read(chip, port, PORT_BASE_VLAN, &reg);
75 if (err)
76 return err;
77
78 reg &= ~mask;
79 reg |= map & mask;
80
81 err = mv88e6xxx_port_write(chip, port, PORT_BASE_VLAN, reg);
82 if (err)
83 return err;
84
85 netdev_dbg(chip->ds->ports[port].netdev, "VLANTable set to %.3x\n",
86 map);
87
88 return 0;
89}
b4e48c50
VD
90
91int mv88e6xxx_port_get_fid(struct mv88e6xxx_chip *chip, int port, u16 *fid)
92{
93 const u16 upper_mask = (mv88e6xxx_num_databases(chip) - 1) >> 4;
94 u16 reg;
95 int err;
96
97 /* Port's default FID lower 4 bits are located in reg 0x06, offset 12 */
98 err = mv88e6xxx_port_read(chip, port, PORT_BASE_VLAN, &reg);
99 if (err)
100 return err;
101
102 *fid = (reg & 0xf000) >> 12;
103
104 /* Port's default FID upper bits are located in reg 0x05, offset 0 */
105 if (upper_mask) {
106 err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_1, &reg);
107 if (err)
108 return err;
109
110 *fid |= (reg & upper_mask) << 4;
111 }
112
113 return 0;
114}
115
116int mv88e6xxx_port_set_fid(struct mv88e6xxx_chip *chip, int port, u16 fid)
117{
118 const u16 upper_mask = (mv88e6xxx_num_databases(chip) - 1) >> 4;
119 u16 reg;
120 int err;
121
122 if (fid >= mv88e6xxx_num_databases(chip))
123 return -EINVAL;
124
125 /* Port's default FID lower 4 bits are located in reg 0x06, offset 12 */
126 err = mv88e6xxx_port_read(chip, port, PORT_BASE_VLAN, &reg);
127 if (err)
128 return err;
129
130 reg &= 0x0fff;
131 reg |= (fid & 0x000f) << 12;
132
133 err = mv88e6xxx_port_write(chip, port, PORT_BASE_VLAN, reg);
134 if (err)
135 return err;
136
137 /* Port's default FID upper bits are located in reg 0x05, offset 0 */
138 if (upper_mask) {
139 err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_1, &reg);
140 if (err)
141 return err;
142
143 reg &= ~upper_mask;
144 reg |= (fid >> 4) & upper_mask;
145
146 err = mv88e6xxx_port_write(chip, port, PORT_CONTROL_1, reg);
147 if (err)
148 return err;
149 }
150
151 netdev_dbg(chip->ds->ports[port].netdev, "FID set to %u\n", fid);
152
153 return 0;
154}
77064f37
VD
155
156/* Offset 0x07: Default Port VLAN ID & Priority */
157
158int mv88e6xxx_port_get_pvid(struct mv88e6xxx_chip *chip, int port, u16 *pvid)
159{
160 u16 reg;
161 int err;
162
163 err = mv88e6xxx_port_read(chip, port, PORT_DEFAULT_VLAN, &reg);
164 if (err)
165 return err;
166
167 *pvid = reg & PORT_DEFAULT_VLAN_MASK;
168
169 return 0;
170}
171
172int mv88e6xxx_port_set_pvid(struct mv88e6xxx_chip *chip, int port, u16 pvid)
173{
174 u16 reg;
175 int err;
176
177 err = mv88e6xxx_port_read(chip, port, PORT_DEFAULT_VLAN, &reg);
178 if (err)
179 return err;
180
181 reg &= ~PORT_DEFAULT_VLAN_MASK;
182 reg |= pvid & PORT_DEFAULT_VLAN_MASK;
183
184 err = mv88e6xxx_port_write(chip, port, PORT_DEFAULT_VLAN, reg);
185 if (err)
186 return err;
187
188 netdev_dbg(chip->ds->ports[port].netdev, "DefaultVID set to %u\n",
189 pvid);
190
191 return 0;
192}
385a0995
VD
193
194/* Offset 0x08: Port Control 2 Register */
195
196static const char * const mv88e6xxx_port_8021q_mode_names[] = {
197 [PORT_CONTROL_2_8021Q_DISABLED] = "Disabled",
198 [PORT_CONTROL_2_8021Q_FALLBACK] = "Fallback",
199 [PORT_CONTROL_2_8021Q_CHECK] = "Check",
200 [PORT_CONTROL_2_8021Q_SECURE] = "Secure",
201};
202
203int mv88e6xxx_port_set_8021q_mode(struct mv88e6xxx_chip *chip, int port,
204 u16 mode)
205{
206 u16 reg;
207 int err;
208
209 err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_2, &reg);
210 if (err)
211 return err;
212
213 reg &= ~PORT_CONTROL_2_8021Q_MASK;
214 reg |= mode & PORT_CONTROL_2_8021Q_MASK;
215
216 err = mv88e6xxx_port_write(chip, port, PORT_CONTROL_2, reg);
217 if (err)
218 return err;
219
220 netdev_dbg(chip->ds->ports[port].netdev, "802.1QMode set to %s\n",
221 mv88e6xxx_port_8021q_mode_names[mode]);
222
223 return 0;
224}