#include <linux/ioport.h>
#include <linux/switch.h>
+#include <linux/mii.h>
#include <rt305x_regs.h>
#include <rt305x_esw_platform.h>
#define RT305X_ESW_REG_POA 0x80
#define RT305X_ESW_REG_FPA 0x84
#define RT305X_ESW_REG_SOCPC 0x8c
-#define RT305X_ESW_REG_POC1 0x90
-#define RT305X_ESW_REG_POC2 0x94
-#define RT305X_ESW_REG_POC3 0x98
+#define RT305X_ESW_REG_POC0 0x90
+#define RT305X_ESW_REG_POC1 0x94
+#define RT305X_ESW_REG_POC2 0x98
#define RT305X_ESW_REG_SGC 0x9c
#define RT305X_ESW_REG_STRT 0xa0
#define RT305X_ESW_REG_PCR0 0xc0
#define RT305X_ESW_SOCPC_DISBC2CPU_S 16
#define RT305X_ESW_SOCPC_CRC_PADDING BIT(25)
-#define RT305X_ESW_POC1_EN_BP_S 0
-#define RT305X_ESW_POC1_EN_FC_S 8
-#define RT305X_ESW_POC1_DIS_RMC2CPU_S 16
-#define RT305X_ESW_POC1_DIS_PORT_M 0x7f
-#define RT305X_ESW_POC1_DIS_PORT_S 23
+#define RT305X_ESW_POC0_EN_BP_S 0
+#define RT305X_ESW_POC0_EN_FC_S 8
+#define RT305X_ESW_POC0_DIS_RMC2CPU_S 16
+#define RT305X_ESW_POC0_DIS_PORT_M 0x7f
+#define RT305X_ESW_POC0_DIS_PORT_S 23
-#define RT305X_ESW_POC3_UNTAG_EN_M 0xff
-#define RT305X_ESW_POC3_UNTAG_EN_S 0
-#define RT305X_ESW_POC3_ENAGING_S 8
-#define RT305X_ESW_POC3_DIS_UC_PAUSE_S 16
+#define RT305X_ESW_POC2_UNTAG_EN_M 0xff
+#define RT305X_ESW_POC2_UNTAG_EN_S 0
+#define RT305X_ESW_POC2_ENAGING_S 8
+#define RT305X_ESW_POC2_DIS_UC_PAUSE_S 16
#define RT305X_ESW_SGC2_DOUBLE_TAG_M 0x7f
#define RT305X_ESW_SGC2_DOUBLE_TAG_S 0
/* Port attributes. */
RT305X_ESW_ATTR_PORT_DISABLE,
RT305X_ESW_ATTR_PORT_DOUBLETAG,
- RT305X_ESW_ATTR_PORT_EN_VLAN,
RT305X_ESW_ATTR_PORT_UNTAG,
RT305X_ESW_ATTR_PORT_LED,
RT305X_ESW_ATTR_PORT_LAN,
bool disable;
bool doubletag;
bool untag;
- bool en_vlan;
u8 led;
u16 pvid;
};
(msc & RT305X_ESW_VMSC_MSC_M) << s);
}
+static unsigned
+rt305x_esw_get_port_disable(struct rt305x_esw *esw)
+{
+ unsigned reg;
+ reg = rt305x_esw_rr(esw, RT305X_ESW_REG_POC0);
+ return (reg >> RT305X_ESW_POC0_DIS_PORT_S) &
+ RT305X_ESW_POC0_DIS_PORT_M;
+}
+
+static void
+rt305x_esw_set_port_disable(struct rt305x_esw *esw, unsigned disable_mask)
+{
+ unsigned old_mask;
+ unsigned enable_mask;
+ unsigned changed;
+ int i;
+
+ old_mask = rt305x_esw_get_port_disable(esw);
+ changed = old_mask ^ disable_mask;
+ enable_mask = old_mask & disable_mask;
+
+ /* enable before writing to MII */
+ rt305x_esw_rmw(esw, RT305X_ESW_REG_POC0,
+ (RT305X_ESW_POC0_DIS_PORT_M <<
+ RT305X_ESW_POC0_DIS_PORT_S),
+ enable_mask << RT305X_ESW_POC0_DIS_PORT_S);
+
+ for (i = 0; i < RT305X_ESW_NUM_LEDS; i++) {
+ if (!(changed & (1 << i)))
+ continue;
+ if (disable_mask & (1 << i)) {
+ /* disable */
+ rt305x_mii_write(esw, i, MII_BMCR,
+ BMCR_PDOWN);
+ } else {
+ /* enable */
+ rt305x_mii_write(esw, i, MII_BMCR,
+ BMCR_FULLDPLX |
+ BMCR_ANENABLE |
+ BMCR_ANRESTART |
+ BMCR_SPEED100);
+ }
+ }
+
+ /* disable after writing to MII */
+ rt305x_esw_rmw(esw, RT305X_ESW_REG_POC0,
+ (RT305X_ESW_POC0_DIS_PORT_M <<
+ RT305X_ESW_POC0_DIS_PORT_S),
+ disable_mask << RT305X_ESW_POC0_DIS_PORT_S);
+}
+
static int
rt305x_esw_apply_config(struct switch_dev *dev);
rt305x_esw_hw_init(struct rt305x_esw *esw)
{
int i;
- u8 port_map = 0;
+ u8 port_disable = 0;
+ u8 port_map = RT305X_ESW_PMAP_LLLLLL;
/* vodoo from original driver */
rt305x_esw_wr(esw, 0xC8A07850, RT305X_ESW_REG_FCT0);
/* Enable Back Pressure, and Flow Control */
rt305x_esw_wr(esw,
- ((RT305X_ESW_PORTS_ALL << RT305X_ESW_POC1_EN_BP_S) |
- (RT305X_ESW_PORTS_ALL << RT305X_ESW_POC1_EN_FC_S)),
- RT305X_ESW_REG_POC1);
+ ((RT305X_ESW_PORTS_ALL << RT305X_ESW_POC0_EN_BP_S) |
+ (RT305X_ESW_PORTS_ALL << RT305X_ESW_POC0_EN_FC_S)),
+ RT305X_ESW_REG_POC0);
/* Enable Aging, and VLAN TAG removal */
rt305x_esw_wr(esw,
- ((RT305X_ESW_PORTS_ALL << RT305X_ESW_POC3_ENAGING_S) |
- (RT305X_ESW_PORTS_NOCPU << RT305X_ESW_POC3_UNTAG_EN_S)),
- RT305X_ESW_REG_POC3);
+ ((RT305X_ESW_PORTS_ALL << RT305X_ESW_POC2_ENAGING_S) |
+ (RT305X_ESW_PORTS_NOCPU << RT305X_ESW_POC2_UNTAG_EN_S)),
+ RT305X_ESW_REG_POC2);
rt305x_esw_wr(esw, esw->pdata->reg_initval_fct2, RT305X_ESW_REG_FCT2);
rt305x_esw_wr(esw, 0x00000005, RT305X_ESW_REG_P3LED);
rt305x_esw_wr(esw, 0x00000005, RT305X_ESW_REG_P4LED);
+ /* Copy disabled port configuration from bootloader setup */
+ port_disable = rt305x_esw_get_port_disable(esw);
+ for (i = 0; i < 6; i++)
+ esw->ports[i].disable = (port_disable & (1 << i)) != 0;
+
rt305x_mii_write(esw, 0, 31, 0x8000);
for (i = 0; i < 5; i++) {
- /* TX10 waveform coefficient */
- rt305x_mii_write(esw, i, 0, 0x3100);
+ if (esw->ports[i].disable) {
+ rt305x_mii_write(esw, i, MII_BMCR, BMCR_PDOWN);
+ } else {
+ rt305x_mii_write(esw, i, MII_BMCR,
+ BMCR_FULLDPLX |
+ BMCR_ANENABLE |
+ BMCR_SPEED100);
+ }
/* TX10 waveform coefficient */
rt305x_mii_write(esw, i, 26, 0x1601);
/* TX100/TX10 AD/DA current bias */
/* select local register */
rt305x_mii_write(esw, 0, 31, 0x8000);
- /* Set up logical config and apply. */
- for (i = 0; i < RT305X_ESW_NUM_VLANS; i++) {
- esw->vlans[i].vid = RT305X_ESW_VLAN_NONE;
- esw->vlans[i].ports = RT305X_ESW_PORTS_NONE;
- }
-
- for (i = 0; i < RT305X_ESW_NUM_PORTS; i++) {
- esw->ports[i].pvid = 1;
- esw->ports[i].en_vlan = 1;
- esw->ports[i].untag = i != RT305X_ESW_PORT6;
- }
-
switch (esw->pdata->vlan_config) {
- case RT305X_ESW_VLAN_CONFIG_BYPASS:
case RT305X_ESW_VLAN_CONFIG_NONE:
port_map = RT305X_ESW_PMAP_LLLLLL;
- esw->global_vlan_enable = 0;
break;
-
case RT305X_ESW_VLAN_CONFIG_LLLLW:
port_map = RT305X_ESW_PMAP_LLLLWL;
- esw->global_vlan_enable = 1;
- esw->vlans[0].vid = 1;
- esw->vlans[1].vid = 2;
- esw->ports[4].pvid = 2;
- esw->ports[5].disable = 1;
- esw->vlans[0].ports =
- BIT(RT305X_ESW_PORT0) | BIT(RT305X_ESW_PORT1) |
- BIT(RT305X_ESW_PORT2) | BIT(RT305X_ESW_PORT3) |
- BIT(RT305X_ESW_PORT6);
- esw->vlans[1].ports =
- BIT(RT305X_ESW_PORT4) | BIT(RT305X_ESW_PORT6);
break;
-
case RT305X_ESW_VLAN_CONFIG_WLLLL:
port_map = RT305X_ESW_PMAP_WLLLLL;
- esw->global_vlan_enable = 1;
- esw->vlans[0].vid = 1;
- esw->vlans[1].vid = 2;
- esw->ports[0].pvid = 2;
- esw->ports[5].disable = 1;
- esw->vlans[0].ports =
- BIT(RT305X_ESW_PORT1) | BIT(RT305X_ESW_PORT2) |
- BIT(RT305X_ESW_PORT3) | BIT(RT305X_ESW_PORT4) |
- BIT(RT305X_ESW_PORT6);
- esw->vlans[1].ports =
- BIT(RT305X_ESW_PORT0) | BIT(RT305X_ESW_PORT6);
break;
-
default:
BUG();
}
RT305X_ESW_SGC2_LAN_PMAP_M << RT305X_ESW_SGC2_LAN_PMAP_S,
port_map << RT305X_ESW_SGC2_LAN_PMAP_S);
+ /* Apply the empty config. */
rt305x_esw_apply_config(&esw->swdev);
}
disable |= esw->ports[i].disable << i;
if (esw->global_vlan_enable) {
doubletag |= esw->ports[i].doubletag << i;
- en_vlan |= esw->ports[i].en_vlan << i;
+ en_vlan |= 1 << i;
untag |= esw->ports[i].untag << i;
pvid = esw->ports[i].pvid;
} else {
RT305X_ESW_REG_P0LED + 4*i);
}
- rt305x_esw_rmw(esw, RT305X_ESW_REG_POC1,
- RT305X_ESW_POC1_DIS_PORT_M << RT305X_ESW_POC1_DIS_PORT_S,
- disable << RT305X_ESW_POC1_DIS_PORT_S);
+ rt305x_esw_set_port_disable(esw, disable);
rt305x_esw_rmw(esw, RT305X_ESW_REG_SGC2,
(RT305X_ESW_SGC2_DOUBLE_TAG_M <<
RT305X_ESW_SGC2_DOUBLE_TAG_S),
rt305x_esw_rmw(esw, RT305X_ESW_REG_PFC1,
RT305X_ESW_PFC1_EN_VLAN_M << RT305X_ESW_PFC1_EN_VLAN_S,
en_vlan << RT305X_ESW_PFC1_EN_VLAN_S);
- rt305x_esw_rmw(esw, RT305X_ESW_REG_POC3,
- RT305X_ESW_POC3_UNTAG_EN_M << RT305X_ESW_POC3_UNTAG_EN_S,
- untag << RT305X_ESW_POC3_UNTAG_EN_S);
+ rt305x_esw_rmw(esw, RT305X_ESW_REG_POC2,
+ RT305X_ESW_POC2_UNTAG_EN_M << RT305X_ESW_POC2_UNTAG_EN_S,
+ untag << RT305X_ESW_POC2_UNTAG_EN_S);
if (!esw->global_vlan_enable) {
/*
switch (attr->id) {
case RT305X_ESW_ATTR_PORT_DISABLE:
- reg = RT305X_ESW_REG_POC1;
- shift = RT305X_ESW_POC1_DIS_PORT_S;
+ reg = RT305X_ESW_REG_POC0;
+ shift = RT305X_ESW_POC0_DIS_PORT_S;
break;
case RT305X_ESW_ATTR_PORT_DOUBLETAG:
reg = RT305X_ESW_REG_SGC2;
shift = RT305X_ESW_SGC2_DOUBLE_TAG_S;
break;
- case RT305X_ESW_ATTR_PORT_EN_VLAN:
- reg = RT305X_ESW_REG_PFC1;
- shift = RT305X_ESW_PFC1_EN_VLAN_S;
- break;
case RT305X_ESW_ATTR_PORT_UNTAG:
- reg = RT305X_ESW_REG_POC3;
- shift = RT305X_ESW_POC3_UNTAG_EN_S;
+ reg = RT305X_ESW_REG_POC2;
+ shift = RT305X_ESW_POC2_UNTAG_EN_S;
break;
case RT305X_ESW_ATTR_PORT_LAN:
reg = RT305X_ESW_REG_SGC2;
case RT305X_ESW_ATTR_PORT_DOUBLETAG:
esw->ports[idx].doubletag = val->value.i;
break;
- case RT305X_ESW_ATTR_PORT_EN_VLAN:
- esw->ports[idx].en_vlan = val->value.i;
- break;
case RT305X_ESW_ATTR_PORT_UNTAG:
esw->ports[idx].untag = val->value.i;
break;
rt305x_esw_get_vlan_ports(struct switch_dev *dev, struct switch_val *val)
{
struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
- u32 vmsc, poc3;
+ u32 vmsc, poc2;
int vlan_idx = -1;
int i;
return -EINVAL;
vmsc = rt305x_esw_get_vmsc(esw, vlan_idx);
- poc3 = rt305x_esw_rr(esw, RT305X_ESW_REG_POC3);
+ poc2 = rt305x_esw_rr(esw, RT305X_ESW_REG_POC2);
for (i = 0; i < RT305X_ESW_NUM_PORTS; i++) {
struct switch_port *p;
p = &val->value.ports[val->len++];
p->id = i;
- if (poc3 & (port_mask << RT305X_ESW_POC3_UNTAG_EN_S))
+ if (poc2 & (port_mask << RT305X_ESW_POC2_UNTAG_EN_S))
p->flags = 0;
else
p->flags = 1 << SWITCH_PORT_FLAG_TAGGED;
.get = rt305x_esw_get_port_bool,
.set = rt305x_esw_set_port_bool,
},
- {
- .type = SWITCH_TYPE_INT,
- .name = "en_vlan",
- .description = "VLAN enabled (1:enabled)",
- .max = 1,
- .id = RT305X_ESW_ATTR_PORT_EN_VLAN,
- .get = rt305x_esw_get_port_bool,
- .set = rt305x_esw_set_port_bool,
- },
{
.type = SWITCH_TYPE_INT,
.name = "untag",