Commit 4b20a07e authored by Oleksij Rempel's avatar Oleksij Rempel Committed by David S. Miller

net: dsa: microchip: ksz8795: add support for ksz88xx chips

We add support for the ksz8863 and ksz8873 chips which are
using the same register patterns but other offsets as the
ksz8795.
Signed-off-by: default avatarMichael Grzeschik <m.grzeschik@pengutronix.de>
Signed-off-by: default avatarOleksij Rempel <o.rempel@pengutronix.de>
Reviewed-by: default avatarAndrew Lunn <andrew@lunn.ch>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 9f73e112
......@@ -72,9 +72,60 @@ static const u8 ksz8795_shifts[] = {
[DYNAMIC_MAC_SRC_PORT] = 24,
};
static const struct {
static const u8 ksz8863_regs[] = {
[REG_IND_CTRL_0] = 0x79,
[REG_IND_DATA_8] = 0x7B,
[REG_IND_DATA_CHECK] = 0x7B,
[REG_IND_DATA_HI] = 0x7C,
[REG_IND_DATA_LO] = 0x80,
[REG_IND_MIB_CHECK] = 0x80,
[P_FORCE_CTRL] = 0x0C,
[P_LINK_STATUS] = 0x0E,
[P_LOCAL_CTRL] = 0x0C,
[P_NEG_RESTART_CTRL] = 0x0D,
[P_REMOTE_STATUS] = 0x0E,
[P_SPEED_STATUS] = 0x0F,
[S_TAIL_TAG_CTRL] = 0x03,
};
static const u32 ksz8863_masks[] = {
[PORT_802_1P_REMAPPING] = BIT(3),
[SW_TAIL_TAG_ENABLE] = BIT(6),
[MIB_COUNTER_OVERFLOW] = BIT(7),
[MIB_COUNTER_VALID] = BIT(6),
[VLAN_TABLE_FID] = GENMASK(15, 12),
[VLAN_TABLE_MEMBERSHIP] = GENMASK(18, 16),
[VLAN_TABLE_VALID] = BIT(19),
[STATIC_MAC_TABLE_VALID] = BIT(19),
[STATIC_MAC_TABLE_USE_FID] = BIT(21),
[STATIC_MAC_TABLE_FID] = GENMASK(29, 26),
[STATIC_MAC_TABLE_OVERRIDE] = BIT(20),
[STATIC_MAC_TABLE_FWD_PORTS] = GENMASK(18, 16),
[DYNAMIC_MAC_TABLE_ENTRIES_H] = GENMASK(5, 0),
[DYNAMIC_MAC_TABLE_MAC_EMPTY] = BIT(7),
[DYNAMIC_MAC_TABLE_NOT_READY] = BIT(7),
[DYNAMIC_MAC_TABLE_ENTRIES] = GENMASK(31, 28),
[DYNAMIC_MAC_TABLE_FID] = GENMASK(19, 16),
[DYNAMIC_MAC_TABLE_SRC_PORT] = GENMASK(21, 20),
[DYNAMIC_MAC_TABLE_TIMESTAMP] = GENMASK(23, 22),
};
static u8 ksz8863_shifts[] = {
[VLAN_TABLE_MEMBERSHIP_S] = 16,
[STATIC_MAC_FWD_PORTS] = 16,
[STATIC_MAC_FID] = 22,
[DYNAMIC_MAC_ENTRIES_H] = 3,
[DYNAMIC_MAC_ENTRIES] = 24,
[DYNAMIC_MAC_FID] = 16,
[DYNAMIC_MAC_TIMESTAMP] = 24,
[DYNAMIC_MAC_SRC_PORT] = 20,
};
struct mib_names {
char string[ETH_GSTRING_LEN];
} mib_names[] = {
};
static const struct mib_names ksz87xx_mib_names[] = {
{ "rx_hi" },
{ "rx_undersize" },
{ "rx_fragments" },
......@@ -113,6 +164,48 @@ static const struct {
{ "tx_discards" },
};
static const struct mib_names ksz88xx_mib_names[] = {
{ "rx" },
{ "rx_hi" },
{ "rx_undersize" },
{ "rx_fragments" },
{ "rx_oversize" },
{ "rx_jabbers" },
{ "rx_symbol_err" },
{ "rx_crc_err" },
{ "rx_align_err" },
{ "rx_mac_ctrl" },
{ "rx_pause" },
{ "rx_bcast" },
{ "rx_mcast" },
{ "rx_ucast" },
{ "rx_64_or_less" },
{ "rx_65_127" },
{ "rx_128_255" },
{ "rx_256_511" },
{ "rx_512_1023" },
{ "rx_1024_1522" },
{ "tx" },
{ "tx_hi" },
{ "tx_late_col" },
{ "tx_pause" },
{ "tx_bcast" },
{ "tx_mcast" },
{ "tx_ucast" },
{ "tx_deferred" },
{ "tx_total_col" },
{ "tx_exc_col" },
{ "tx_single_col" },
{ "tx_mult_col" },
{ "rx_discards" },
{ "tx_discards" },
};
static bool ksz_is_ksz88x3(struct ksz_device *dev)
{
return dev->chip_id == 0x8830;
}
static void ksz_cfg(struct ksz_device *dev, u32 addr, u8 bits, bool set)
{
regmap_update_bits(dev->regmap[0], addr, bits, set ? bits : 0);
......@@ -127,10 +220,18 @@ static void ksz_port_cfg(struct ksz_device *dev, int port, int offset, u8 bits,
static int ksz8_reset_switch(struct ksz_device *dev)
{
/* reset switch */
ksz_write8(dev, REG_POWER_MANAGEMENT_1,
SW_SOFTWARE_POWER_DOWN << SW_POWER_MANAGEMENT_MODE_S);
ksz_write8(dev, REG_POWER_MANAGEMENT_1, 0);
if (ksz_is_ksz88x3(dev)) {
/* reset switch */
ksz_cfg(dev, KSZ8863_REG_SW_RESET,
KSZ8863_GLOBAL_SOFTWARE_RESET | KSZ8863_PCS_RESET, true);
ksz_cfg(dev, KSZ8863_REG_SW_RESET,
KSZ8863_GLOBAL_SOFTWARE_RESET | KSZ8863_PCS_RESET, false);
} else {
/* reset switch */
ksz_write8(dev, REG_POWER_MANAGEMENT_1,
SW_SOFTWARE_POWER_DOWN << SW_POWER_MANAGEMENT_MODE_S);
ksz_write8(dev, REG_POWER_MANAGEMENT_1, 0);
}
return 0;
}
......@@ -204,8 +305,8 @@ static void ksz8_r_mib_cnt(struct ksz_device *dev, int port, u16 addr, u64 *cnt)
mutex_unlock(&dev->alu_mutex);
}
static void ksz8_r_mib_pkt(struct ksz_device *dev, int port, u16 addr,
u64 *dropped, u64 *cnt)
static void ksz8795_r_mib_pkt(struct ksz_device *dev, int port, u16 addr,
u64 *dropped, u64 *cnt)
{
struct ksz8 *ksz8 = dev->priv;
const u32 *masks;
......@@ -219,8 +320,8 @@ static void ksz8_r_mib_pkt(struct ksz_device *dev, int port, u16 addr,
regs = ksz8->regs;
addr -= dev->reg_mib_cnt;
ctrl_addr = (KS_MIB_TOTAL_RX_1 - KS_MIB_TOTAL_RX_0) * port;
ctrl_addr += addr + KS_MIB_TOTAL_RX_0;
ctrl_addr = (KSZ8795_MIB_TOTAL_RX_1 - KSZ8795_MIB_TOTAL_RX_0) * port;
ctrl_addr += addr + KSZ8795_MIB_TOTAL_RX_0;
ctrl_addr |= IND_ACC_TABLE(TABLE_MIB | TABLE_READ);
mutex_lock(&dev->alu_mutex);
......@@ -257,8 +358,52 @@ static void ksz8_r_mib_pkt(struct ksz_device *dev, int port, u16 addr,
mutex_unlock(&dev->alu_mutex);
}
static void ksz8863_r_mib_pkt(struct ksz_device *dev, int port, u16 addr,
u64 *dropped, u64 *cnt)
{
struct ksz8 *ksz8 = dev->priv;
const u8 *regs = ksz8->regs;
u32 *last = (u32 *)dropped;
u16 ctrl_addr;
u32 data;
u32 cur;
addr -= dev->reg_mib_cnt;
ctrl_addr = addr ? KSZ8863_MIB_PACKET_DROPPED_TX_0 :
KSZ8863_MIB_PACKET_DROPPED_RX_0;
ctrl_addr += port;
ctrl_addr |= IND_ACC_TABLE(TABLE_MIB | TABLE_READ);
mutex_lock(&dev->alu_mutex);
ksz_write16(dev, regs[REG_IND_CTRL_0], ctrl_addr);
ksz_read32(dev, regs[REG_IND_DATA_LO], &data);
mutex_unlock(&dev->alu_mutex);
data &= MIB_PACKET_DROPPED;
cur = last[addr];
if (data != cur) {
last[addr] = data;
if (data < cur)
data += MIB_PACKET_DROPPED + 1;
data -= cur;
*cnt += data;
}
}
static void ksz8_r_mib_pkt(struct ksz_device *dev, int port, u16 addr,
u64 *dropped, u64 *cnt)
{
if (ksz_is_ksz88x3(dev))
ksz8863_r_mib_pkt(dev, port, addr, dropped, cnt);
else
ksz8795_r_mib_pkt(dev, port, addr, dropped, cnt);
}
static void ksz8_freeze_mib(struct ksz_device *dev, int port, bool freeze)
{
if (ksz_is_ksz88x3(dev))
return;
/* enable the port for flush/freeze function */
if (freeze)
ksz_cfg(dev, REG_SW_CTRL_6, BIT(port), true);
......@@ -272,11 +417,14 @@ static void ksz8_freeze_mib(struct ksz_device *dev, int port, bool freeze)
static void ksz8_port_init_cnt(struct ksz_device *dev, int port)
{
struct ksz_port_mib *mib = &dev->ports[port].mib;
u64 *dropped;
/* flush all enabled port MIB counters */
ksz_cfg(dev, REG_SW_CTRL_6, BIT(port), true);
ksz_cfg(dev, REG_SW_CTRL_6, SW_MIB_COUNTER_FLUSH, true);
ksz_cfg(dev, REG_SW_CTRL_6, BIT(port), false);
if (!ksz_is_ksz88x3(dev)) {
/* flush all enabled port MIB counters */
ksz_cfg(dev, REG_SW_CTRL_6, BIT(port), true);
ksz_cfg(dev, REG_SW_CTRL_6, SW_MIB_COUNTER_FLUSH, true);
ksz_cfg(dev, REG_SW_CTRL_6, BIT(port), false);
}
mib->cnt_ptr = 0;
......@@ -287,10 +435,13 @@ static void ksz8_port_init_cnt(struct ksz_device *dev, int port)
++mib->cnt_ptr;
}
/* last one in storage */
dropped = &mib->counters[dev->mib_cnt];
/* Some ports may not have MIB counters after SWITCH_COUNTER_NUM. */
while (mib->cnt_ptr < dev->mib_cnt) {
dev->dev_ops->r_mib_pkt(dev, port, mib->cnt_ptr,
NULL, &mib->counters[mib->cnt_ptr]);
dropped, &mib->counters[mib->cnt_ptr]);
++mib->cnt_ptr;
}
mib->cnt_ptr = 0;
......@@ -588,8 +739,13 @@ static void ksz8_r_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 *val)
data |= PHY_LOOPBACK;
if (ctrl & PORT_FORCE_100_MBIT)
data |= PHY_SPEED_100MBIT;
if (!(ctrl & PORT_AUTO_NEG_DISABLE))
data |= PHY_AUTO_NEG_ENABLE;
if (ksz_is_ksz88x3(dev)) {
if ((ctrl & PORT_AUTO_NEG_ENABLE))
data |= PHY_AUTO_NEG_ENABLE;
} else {
if (!(ctrl & PORT_AUTO_NEG_DISABLE))
data |= PHY_AUTO_NEG_ENABLE;
}
if (restart & PORT_POWER_DOWN)
data |= PHY_POWER_DOWN;
if (restart & PORT_AUTO_NEG_RESTART)
......@@ -623,7 +779,10 @@ static void ksz8_r_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 *val)
data = KSZ8795_ID_HI;
break;
case PHY_REG_ID_2:
data = KSZ8795_ID_LO;
if (ksz_is_ksz88x3(dev))
data = KSZ8863_ID_LO;
else
data = KSZ8795_ID_LO;
break;
case PHY_REG_AUTO_NEGOTIATION:
ksz_pread8(dev, p, regs[P_LOCAL_CTRL], &ctrl);
......@@ -686,14 +845,22 @@ static void ksz8_w_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 val)
ksz_pwrite8(dev, p, regs[P_SPEED_STATUS], data);
ksz_pread8(dev, p, regs[P_FORCE_CTRL], &ctrl);
data = ctrl;
if (!(val & PHY_AUTO_NEG_ENABLE))
data |= PORT_AUTO_NEG_DISABLE;
else
data &= ~PORT_AUTO_NEG_DISABLE;
if (ksz_is_ksz88x3(dev)) {
if ((val & PHY_AUTO_NEG_ENABLE))
data |= PORT_AUTO_NEG_ENABLE;
else
data &= ~PORT_AUTO_NEG_ENABLE;
} else {
if (!(val & PHY_AUTO_NEG_ENABLE))
data |= PORT_AUTO_NEG_DISABLE;
else
data &= ~PORT_AUTO_NEG_DISABLE;
/* Fiber port does not support auto-negotiation. */
if (dev->ports[p].fiber)
data |= PORT_AUTO_NEG_DISABLE;
}
/* Fiber port does not support auto-negotiation. */
if (dev->ports[p].fiber)
data |= PORT_AUTO_NEG_DISABLE;
if (val & PHY_SPEED_100MBIT)
data |= PORT_FORCE_100_MBIT;
else
......@@ -767,7 +934,11 @@ static enum dsa_tag_protocol ksz8_get_tag_protocol(struct dsa_switch *ds,
int port,
enum dsa_tag_protocol mp)
{
return DSA_TAG_PROTO_KSZ8795;
struct ksz_device *dev = ds->priv;
/* ksz88x3 uses the same tag schema as KSZ9893 */
return ksz_is_ksz88x3(dev) ?
DSA_TAG_PROTO_KSZ9893 : DSA_TAG_PROTO_KSZ8795;
}
static void ksz8_get_strings(struct dsa_switch *ds, int port,
......@@ -777,8 +948,8 @@ static void ksz8_get_strings(struct dsa_switch *ds, int port,
int i;
for (i = 0; i < dev->mib_cnt; i++) {
memcpy(buf + i * ETH_GSTRING_LEN, mib_names[i].string,
ETH_GSTRING_LEN);
memcpy(buf + i * ETH_GSTRING_LEN,
dev->mib_names[i].string, ETH_GSTRING_LEN);
}
}
......@@ -904,6 +1075,9 @@ static int ksz8_port_vlan_filtering(struct dsa_switch *ds, int port, bool flag,
{
struct ksz_device *dev = ds->priv;
if (ksz_is_ksz88x3(dev))
return -ENOTSUPP;
ksz_cfg(dev, S_MIRROR_CTRL, SW_VLAN_ENABLE, flag);
return 0;
......@@ -918,6 +1092,9 @@ static int ksz8_port_vlan_add(struct dsa_switch *ds, int port,
u16 data, new_pvid = 0;
u8 fid, member, valid;
if (ksz_is_ksz88x3(dev))
return -ENOTSUPP;
ksz_port_cfg(dev, port, P_TAG_CTRL, PORT_REMOVE_TAG, untagged);
ksz8_r_vlan_table(dev, vlan->vid, &data);
......@@ -958,6 +1135,9 @@ static int ksz8_port_vlan_del(struct dsa_switch *ds, int port,
u16 data, pvid, new_pvid = 0;
u8 fid, member, valid;
if (ksz_is_ksz88x3(dev))
return -ENOTSUPP;
ksz_pread16(dev, port, REG_PORT_CTRL_VID, &pvid);
pvid = pvid & 0xFFF;
......@@ -1091,7 +1271,8 @@ static void ksz8_port_setup(struct ksz_device *dev, int port, bool cpu_port)
/* enable broadcast storm limit */
ksz_port_cfg(dev, port, P_BCAST_STORM_CTRL, PORT_BROADCAST_STORM, true);
ksz8795_set_prio_queue(dev, port, 4);
if (!ksz_is_ksz88x3(dev))
ksz8795_set_prio_queue(dev, port, 4);
/* disable DiffServ priority */
ksz_port_cfg(dev, port, P_PRIO_CTRL, PORT_DIFFSERV_ENABLE, false);
......@@ -1104,7 +1285,8 @@ static void ksz8_port_setup(struct ksz_device *dev, int port, bool cpu_port)
ksz_port_cfg(dev, port, P_PRIO_CTRL, PORT_802_1P_ENABLE, true);
if (cpu_port) {
ksz8795_cpu_interface_select(dev, port);
if (!ksz_is_ksz88x3(dev))
ksz8795_cpu_interface_select(dev, port);
member = dev->port_mask;
} else {
......@@ -1156,9 +1338,11 @@ static void ksz8_config_cpu_port(struct dsa_switch *ds)
p = &dev->ports[i];
if (!p->on)
continue;
ksz_pread8(dev, i, regs[P_REMOTE_STATUS], &remote);
if (remote & PORT_FIBER_MODE)
p->fiber = 1;
if (!ksz_is_ksz88x3(dev)) {
ksz_pread8(dev, i, regs[P_REMOTE_STATUS], &remote);
if (remote & PORT_FIBER_MODE)
p->fiber = 1;
}
if (p->fiber)
ksz_port_cfg(dev, i, P_STP_CTRL, PORT_FORCE_FLOW_CTRL,
true);
......@@ -1277,19 +1461,30 @@ static int ksz8_switch_detect(struct ksz_device *dev)
id1 = id16 >> 8;
id2 = id16 & SW_CHIP_ID_M;
if (id1 != FAMILY_ID ||
(id2 != CHIP_ID_94 && id2 != CHIP_ID_95))
return -ENODEV;
if (id2 == CHIP_ID_95) {
u8 val;
switch (id1) {
case KSZ87_FAMILY_ID:
if ((id2 != CHIP_ID_94 && id2 != CHIP_ID_95))
return -ENODEV;
if (id2 == CHIP_ID_95) {
u8 val;
id2 = 0x95;
ksz_read8(dev, REG_PORT_1_STATUS_0, &val);
if (val & PORT_FIBER_MODE)
id2 = 0x65;
} else if (id2 == CHIP_ID_94) {
id2 = 0x94;
id2 = 0x95;
ksz_read8(dev, REG_PORT_STATUS_0, &val);
if (val & PORT_FIBER_MODE)
id2 = 0x65;
} else if (id2 == CHIP_ID_94) {
id2 = 0x94;
}
break;
case KSZ88_FAMILY_ID:
if (id2 != CHIP_ID_63)
return -ENODEV;
break;
default:
dev_err(dev->dev, "invalid family id: %d\n", id1);
return -ENODEV;
}
id16 &= ~0xff;
id16 |= id2;
......@@ -1350,6 +1545,15 @@ static const struct ksz_chip_data ksz8_switch_chips[] = {
.cpu_ports = 0x10, /* can be configured as cpu port */
.port_cnt = 5, /* total cpu and user ports */
},
{
.chip_id = 0x8830,
.dev_name = "KSZ8863/KSZ8873",
.num_vlans = 16,
.num_alus = 0,
.num_statics = 8,
.cpu_ports = 0x4, /* can be configured as cpu port */
.port_cnt = 3,
},
};
static int ksz8_switch_init(struct ksz_device *dev)
......@@ -1382,12 +1586,21 @@ static int ksz8_switch_init(struct ksz_device *dev)
if (!dev->cpu_ports)
return -ENODEV;
ksz8->regs = ksz8795_regs;
ksz8->masks = ksz8795_masks;
ksz8->shifts = ksz8795_shifts;
if (ksz_is_ksz88x3(dev)) {
ksz8->regs = ksz8863_regs;
ksz8->masks = ksz8863_masks;
ksz8->shifts = ksz8863_shifts;
dev->mib_cnt = ARRAY_SIZE(ksz88xx_mib_names);
dev->mib_names = ksz88xx_mib_names;
} else {
ksz8->regs = ksz8795_regs;
ksz8->masks = ksz8795_masks;
ksz8->shifts = ksz8795_shifts;
dev->mib_cnt = ARRAY_SIZE(ksz87xx_mib_names);
dev->mib_names = ksz87xx_mib_names;
}
dev->reg_mib_cnt = KSZ8795_COUNTER_NUM;
dev->mib_cnt = ARRAY_SIZE(mib_names);
dev->reg_mib_cnt = MIB_COUNTER_NUM;
dev->ports = devm_kzalloc(dev->dev,
dev->port_cnt * sizeof(struct ksz_port),
......
......@@ -16,7 +16,8 @@
#define REG_CHIP_ID0 0x00
#define FAMILY_ID 0x87
#define KSZ87_FAMILY_ID 0x87
#define KSZ88_FAMILY_ID 0x88
#define REG_CHIP_ID1 0x01
......@@ -28,6 +29,12 @@
#define CHIP_ID_94 0x60
#define CHIP_ID_95 0x90
#define CHIP_ID_63 0x30
#define KSZ8863_REG_SW_RESET 0x43
#define KSZ8863_GLOBAL_SOFTWARE_RESET BIT(4)
#define KSZ8863_PCS_RESET BIT(0)
#define REG_SW_CTRL_0 0x02
......@@ -267,6 +274,7 @@
#define REG_PORT_3_CTRL_9 0x3C
#define REG_PORT_4_CTRL_9 0x4C
#define PORT_AUTO_NEG_ENABLE BIT(7)
#define PORT_AUTO_NEG_DISABLE BIT(7)
#define PORT_FORCE_100_MBIT BIT(6)
#define PORT_FORCE_FULL_DUPLEX BIT(5)
......@@ -800,6 +808,7 @@
#define KSZ8795_ID_HI 0x0022
#define KSZ8795_ID_LO 0x1550
#define KSZ8863_ID_LO 0x1430
#define KSZ8795_SW_ID 0x8795
......@@ -830,7 +839,7 @@
#define KS_PRIO_IN_REG 4
#define KSZ8795_COUNTER_NUM 0x20
#define MIB_COUNTER_NUM 0x20
/* Common names used by other drivers */
......@@ -876,26 +885,13 @@
#define MIB_COUNTER_VALUE 0x3FFFFFFF
#define KS_MIB_TOTAL_RX_0 0x100
#define KS_MIB_TOTAL_TX_0 0x101
#define KS_MIB_PACKET_DROPPED_RX_0 0x102
#define KS_MIB_PACKET_DROPPED_TX_0 0x103
#define KS_MIB_TOTAL_RX_1 0x104
#define KS_MIB_TOTAL_TX_1 0x105
#define KS_MIB_PACKET_DROPPED_TX_1 0x106
#define KS_MIB_PACKET_DROPPED_RX_1 0x107
#define KS_MIB_TOTAL_RX_2 0x108
#define KS_MIB_TOTAL_TX_2 0x109
#define KS_MIB_PACKET_DROPPED_TX_2 0x10A
#define KS_MIB_PACKET_DROPPED_RX_2 0x10B
#define KS_MIB_TOTAL_RX_3 0x10C
#define KS_MIB_TOTAL_TX_3 0x10D
#define KS_MIB_PACKET_DROPPED_TX_3 0x10E
#define KS_MIB_PACKET_DROPPED_RX_3 0x10F
#define KS_MIB_TOTAL_RX_4 0x110
#define KS_MIB_TOTAL_TX_4 0x111
#define KS_MIB_PACKET_DROPPED_TX_4 0x112
#define KS_MIB_PACKET_DROPPED_RX_4 0x113
#define KSZ8795_MIB_TOTAL_RX_0 0x100
#define KSZ8795_MIB_TOTAL_TX_0 0x101
#define KSZ8795_MIB_TOTAL_RX_1 0x104
#define KSZ8795_MIB_TOTAL_TX_1 0x105
#define KSZ8863_MIB_PACKET_DROPPED_TX_0 0x100
#define KSZ8863_MIB_PACKET_DROPPED_RX_0 0x105
#define MIB_PACKET_DROPPED 0x0000FFFF
......
......@@ -71,6 +71,7 @@ struct ksz_device {
int port_cnt;
int reg_mib_cnt;
int mib_cnt;
const struct mib_names *mib_names;
phy_interface_t compat_interface;
u32 regs_size;
bool phy_errata_9477;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment