Commit ea6828b8 authored by Dhananjay Phadke's avatar Dhananjay Phadke Committed by David S. Miller

netxen: improve pci memory access

o Access on card memory through memory controller (agent)
  rather than moving small pci window around. Clean up the
  code for moving windows around.

o Restrict memory accesss to 64 bit, currently only firmware
  download uses this.
Signed-off-by: default avatarDhananjay Phadke <dhananjay@netxen.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent f78c0850
...@@ -552,8 +552,8 @@ struct netxen_hardware_context { ...@@ -552,8 +552,8 @@ struct netxen_hardware_context {
int qdr_sn_window; int qdr_sn_window;
int ddr_mn_window; int ddr_mn_window;
unsigned long mn_win_crb; u32 mn_win_crb;
unsigned long ms_win_crb; u32 ms_win_crb;
u8 cut_through; u8 cut_through;
u8 revision_id; u8 revision_id;
......
...@@ -1279,25 +1279,6 @@ netxen_nic_hw_read_wx_2M(struct netxen_adapter *adapter, ulong off) ...@@ -1279,25 +1279,6 @@ netxen_nic_hw_read_wx_2M(struct netxen_adapter *adapter, ulong off)
return data; return data;
} }
/*
* check memory access boundary.
* used by test agent. support ddr access only for now
*/
static unsigned long
netxen_nic_pci_mem_bound_check(struct netxen_adapter *adapter,
unsigned long long addr, int size)
{
if (!ADDR_IN_RANGE(addr,
NETXEN_ADDR_DDR_NET, NETXEN_ADDR_DDR_NET_MAX) ||
!ADDR_IN_RANGE(addr+size-1,
NETXEN_ADDR_DDR_NET, NETXEN_ADDR_DDR_NET_MAX) ||
((size != 1) && (size != 2) && (size != 4) && (size != 8))) {
return 0;
}
return 1;
}
static int netxen_pci_set_window_warning_count; static int netxen_pci_set_window_warning_count;
static unsigned long static unsigned long
...@@ -1424,10 +1405,8 @@ netxen_nic_pci_set_window_2M(struct netxen_adapter *adapter, ...@@ -1424,10 +1405,8 @@ netxen_nic_pci_set_window_2M(struct netxen_adapter *adapter,
/* DDR network side */ /* DDR network side */
window = MN_WIN(addr); window = MN_WIN(addr);
adapter->ahw.ddr_mn_window = window; adapter->ahw.ddr_mn_window = window;
NXWR32(adapter, adapter->ahw.mn_win_crb | NETXEN_PCI_CRBSPACE, NXWR32(adapter, adapter->ahw.mn_win_crb, window);
window); win_read = NXRD32(adapter, adapter->ahw.mn_win_crb);
win_read = NXRD32(adapter,
adapter->ahw.mn_win_crb | NETXEN_PCI_CRBSPACE);
if ((win_read << 17) != window) { if ((win_read << 17) != window) {
printk(KERN_INFO "Written MNwin (0x%x) != " printk(KERN_INFO "Written MNwin (0x%x) != "
"Read MNwin (0x%x)\n", window, win_read); "Read MNwin (0x%x)\n", window, win_read);
...@@ -1442,10 +1421,8 @@ netxen_nic_pci_set_window_2M(struct netxen_adapter *adapter, ...@@ -1442,10 +1421,8 @@ netxen_nic_pci_set_window_2M(struct netxen_adapter *adapter,
window = OCM_WIN(addr); window = OCM_WIN(addr);
adapter->ahw.ddr_mn_window = window; adapter->ahw.ddr_mn_window = window;
NXWR32(adapter, adapter->ahw.mn_win_crb | NETXEN_PCI_CRBSPACE, NXWR32(adapter, adapter->ahw.mn_win_crb, window);
window); win_read = NXRD32(adapter, adapter->ahw.mn_win_crb);
win_read = NXRD32(adapter,
adapter->ahw.mn_win_crb | NETXEN_PCI_CRBSPACE);
if ((win_read >> 7) != window) { if ((win_read >> 7) != window) {
printk(KERN_INFO "%s: Written OCMwin (0x%x) != " printk(KERN_INFO "%s: Written OCMwin (0x%x) != "
"Read OCMwin (0x%x)\n", "Read OCMwin (0x%x)\n",
...@@ -1458,10 +1435,8 @@ netxen_nic_pci_set_window_2M(struct netxen_adapter *adapter, ...@@ -1458,10 +1435,8 @@ netxen_nic_pci_set_window_2M(struct netxen_adapter *adapter,
/* QDR network side */ /* QDR network side */
window = MS_WIN(addr); window = MS_WIN(addr);
adapter->ahw.qdr_sn_window = window; adapter->ahw.qdr_sn_window = window;
NXWR32(adapter, adapter->ahw.ms_win_crb | NETXEN_PCI_CRBSPACE, NXWR32(adapter, adapter->ahw.ms_win_crb, window);
window); win_read = NXRD32(adapter, adapter->ahw.ms_win_crb);
win_read = NXRD32(adapter,
adapter->ahw.ms_win_crb | NETXEN_PCI_CRBSPACE);
if (win_read != window) { if (win_read != window) {
printk(KERN_INFO "%s: Written MSwin (0x%x) != " printk(KERN_INFO "%s: Written MSwin (0x%x) != "
"Read MSwin (0x%x)\n", "Read MSwin (0x%x)\n",
...@@ -1484,177 +1459,6 @@ netxen_nic_pci_set_window_2M(struct netxen_adapter *adapter, ...@@ -1484,177 +1459,6 @@ netxen_nic_pci_set_window_2M(struct netxen_adapter *adapter,
return addr; return addr;
} }
static int netxen_nic_pci_is_same_window(struct netxen_adapter *adapter,
unsigned long long addr)
{
int window;
unsigned long long qdr_max;
if (NX_IS_REVISION_P2(adapter->ahw.revision_id))
qdr_max = NETXEN_ADDR_QDR_NET_MAX_P2;
else
qdr_max = NETXEN_ADDR_QDR_NET_MAX_P3;
if (ADDR_IN_RANGE(addr,
NETXEN_ADDR_DDR_NET, NETXEN_ADDR_DDR_NET_MAX)) {
/* DDR network side */
BUG(); /* MN access can not come here */
} else if (ADDR_IN_RANGE(addr,
NETXEN_ADDR_OCM0, NETXEN_ADDR_OCM0_MAX)) {
return 1;
} else if (ADDR_IN_RANGE(addr,
NETXEN_ADDR_OCM1, NETXEN_ADDR_OCM1_MAX)) {
return 1;
} else if (ADDR_IN_RANGE(addr, NETXEN_ADDR_QDR_NET, qdr_max)) {
/* QDR network side */
window = ((addr - NETXEN_ADDR_QDR_NET) >> 22) & 0x3f;
if (adapter->ahw.qdr_sn_window == window)
return 1;
}
return 0;
}
static int netxen_nic_pci_mem_read_direct(struct netxen_adapter *adapter,
u64 off, void *data, int size)
{
unsigned long flags;
void __iomem *addr, *mem_ptr = NULL;
int ret = 0;
u64 start;
unsigned long mem_base;
unsigned long mem_page;
write_lock_irqsave(&adapter->adapter_lock, flags);
/*
* If attempting to access unknown address or straddle hw windows,
* do not access.
*/
start = adapter->pci_set_window(adapter, off);
if ((start == -1UL) ||
(netxen_nic_pci_is_same_window(adapter, off+size-1) == 0)) {
write_unlock_irqrestore(&adapter->adapter_lock, flags);
printk(KERN_ERR "%s out of bound pci memory access. "
"offset is 0x%llx\n", netxen_nic_driver_name,
(unsigned long long)off);
return -1;
}
addr = pci_base_offset(adapter, start);
if (!addr) {
write_unlock_irqrestore(&adapter->adapter_lock, flags);
mem_base = pci_resource_start(adapter->pdev, 0);
mem_page = start & PAGE_MASK;
/* Map two pages whenever user tries to access addresses in two
consecutive pages.
*/
if (mem_page != ((start + size - 1) & PAGE_MASK))
mem_ptr = ioremap(mem_base + mem_page, PAGE_SIZE * 2);
else
mem_ptr = ioremap(mem_base + mem_page, PAGE_SIZE);
if (mem_ptr == NULL) {
*(uint8_t *)data = 0;
return -1;
}
addr = mem_ptr;
addr += start & (PAGE_SIZE - 1);
write_lock_irqsave(&adapter->adapter_lock, flags);
}
switch (size) {
case 1:
*(uint8_t *)data = readb(addr);
break;
case 2:
*(uint16_t *)data = readw(addr);
break;
case 4:
*(uint32_t *)data = readl(addr);
break;
case 8:
*(uint64_t *)data = readq(addr);
break;
default:
ret = -1;
break;
}
write_unlock_irqrestore(&adapter->adapter_lock, flags);
if (mem_ptr)
iounmap(mem_ptr);
return ret;
}
static int
netxen_nic_pci_mem_write_direct(struct netxen_adapter *adapter, u64 off,
void *data, int size)
{
unsigned long flags;
void __iomem *addr, *mem_ptr = NULL;
int ret = 0;
u64 start;
unsigned long mem_base;
unsigned long mem_page;
write_lock_irqsave(&adapter->adapter_lock, flags);
/*
* If attempting to access unknown address or straddle hw windows,
* do not access.
*/
start = adapter->pci_set_window(adapter, off);
if ((start == -1UL) ||
(netxen_nic_pci_is_same_window(adapter, off+size-1) == 0)) {
write_unlock_irqrestore(&adapter->adapter_lock, flags);
printk(KERN_ERR "%s out of bound pci memory access. "
"offset is 0x%llx\n", netxen_nic_driver_name,
(unsigned long long)off);
return -1;
}
addr = pci_base_offset(adapter, start);
if (!addr) {
write_unlock_irqrestore(&adapter->adapter_lock, flags);
mem_base = pci_resource_start(adapter->pdev, 0);
mem_page = start & PAGE_MASK;
/* Map two pages whenever user tries to access addresses in two
* consecutive pages.
*/
if (mem_page != ((start + size - 1) & PAGE_MASK))
mem_ptr = ioremap(mem_base + mem_page, PAGE_SIZE*2);
else
mem_ptr = ioremap(mem_base + mem_page, PAGE_SIZE);
if (mem_ptr == NULL)
return -1;
addr = mem_ptr;
addr += start & (PAGE_SIZE - 1);
write_lock_irqsave(&adapter->adapter_lock, flags);
}
switch (size) {
case 1:
writeb(*(uint8_t *)data, addr);
break;
case 2:
writew(*(uint16_t *)data, addr);
break;
case 4:
writel(*(uint32_t *)data, addr);
break;
case 8:
writeq(*(uint64_t *)data, addr);
break;
default:
ret = -1;
break;
}
write_unlock_irqrestore(&adapter->adapter_lock, flags);
if (mem_ptr)
iounmap(mem_ptr);
return ret;
}
#define MAX_CTL_CHECK 1000 #define MAX_CTL_CHECK 1000
static int static int
...@@ -1667,19 +1471,28 @@ netxen_nic_pci_mem_write_128M(struct netxen_adapter *adapter, ...@@ -1667,19 +1471,28 @@ netxen_nic_pci_mem_write_128M(struct netxen_adapter *adapter,
uint64_t off8, tmpw, word[2] = {0, 0}; uint64_t off8, tmpw, word[2] = {0, 0};
void __iomem *mem_crb; void __iomem *mem_crb;
/* if (size != 8)
* If not MN, go check for MS or invalid. return -EIO;
*/
if (netxen_nic_pci_mem_bound_check(adapter, off, size) == 0) if (ADDR_IN_RANGE(off, NETXEN_ADDR_QDR_NET,
return netxen_nic_pci_mem_write_direct(adapter, NETXEN_ADDR_QDR_NET_MAX_P2)) {
off, data, size); mem_crb = pci_base_offset(adapter, NETXEN_CRB_QDR_NET);
goto correct;
}
if (ADDR_IN_RANGE(off, NETXEN_ADDR_DDR_NET, NETXEN_ADDR_DDR_NET_MAX)) {
mem_crb = pci_base_offset(adapter, NETXEN_CRB_DDR_NET);
goto correct;
}
return -EIO;
correct:
off8 = off & 0xfffffff8; off8 = off & 0xfffffff8;
off0 = off & 0x7; off0 = off & 0x7;
sz[0] = (size < (8 - off0)) ? size : (8 - off0); sz[0] = (size < (8 - off0)) ? size : (8 - off0);
sz[1] = size - sz[0]; sz[1] = size - sz[0];
loop = ((off0 + size - 1) >> 3) + 1; loop = ((off0 + size - 1) >> 3) + 1;
mem_crb = pci_base_offset(adapter, NETXEN_CRB_DDR_NET);
if ((size != 8) || (off0 != 0)) { if ((size != 8) || (off0 != 0)) {
for (i = 0; i < loop; i++) { for (i = 0; i < loop; i++) {
...@@ -1760,20 +1573,29 @@ netxen_nic_pci_mem_read_128M(struct netxen_adapter *adapter, ...@@ -1760,20 +1573,29 @@ netxen_nic_pci_mem_read_128M(struct netxen_adapter *adapter,
uint64_t off8, val, word[2] = {0, 0}; uint64_t off8, val, word[2] = {0, 0};
void __iomem *mem_crb; void __iomem *mem_crb;
if (size != 8)
return -EIO;
/* if (ADDR_IN_RANGE(off, NETXEN_ADDR_QDR_NET,
* If not MN, go check for MS or invalid. NETXEN_ADDR_QDR_NET_MAX_P2)) {
*/ mem_crb = pci_base_offset(adapter, NETXEN_CRB_QDR_NET);
if (netxen_nic_pci_mem_bound_check(adapter, off, size) == 0) goto correct;
return netxen_nic_pci_mem_read_direct(adapter, off, data, size); }
if (ADDR_IN_RANGE(off, NETXEN_ADDR_DDR_NET, NETXEN_ADDR_DDR_NET_MAX)) {
mem_crb = pci_base_offset(adapter, NETXEN_CRB_DDR_NET);
goto correct;
}
return -EIO;
correct:
off8 = off & 0xfffffff8; off8 = off & 0xfffffff8;
off0[0] = off & 0x7; off0[0] = off & 0x7;
off0[1] = 0; off0[1] = 0;
sz[0] = (size < (8 - off0[0])) ? size : (8 - off0[0]); sz[0] = (size < (8 - off0[0])) ? size : (8 - off0[0]);
sz[1] = size - sz[0]; sz[1] = size - sz[0];
loop = ((off0[0] + size - 1) >> 3) + 1; loop = ((off0[0] + size - 1) >> 3) + 1;
mem_crb = pci_base_offset(adapter, NETXEN_CRB_DDR_NET);
write_lock_irqsave(&adapter->adapter_lock, flags); write_lock_irqsave(&adapter->adapter_lock, flags);
netxen_nic_pci_change_crbwindow_128M(adapter, 0); netxen_nic_pci_change_crbwindow_128M(adapter, 0);
...@@ -1847,20 +1669,26 @@ netxen_nic_pci_mem_write_2M(struct netxen_adapter *adapter, ...@@ -1847,20 +1669,26 @@ netxen_nic_pci_mem_write_2M(struct netxen_adapter *adapter,
{ {
int i, j, ret = 0, loop, sz[2], off0; int i, j, ret = 0, loop, sz[2], off0;
uint32_t temp; uint32_t temp;
uint64_t off8, mem_crb, tmpw, word[2] = {0, 0}; uint64_t off8, tmpw, word[2] = {0, 0};
void __iomem *mem_crb;
/* if (size != 8)
* If not MN, go check for MS or invalid. return -EIO;
*/
if (off >= NETXEN_ADDR_QDR_NET && off <= NETXEN_ADDR_QDR_NET_MAX_P3) if (ADDR_IN_RANGE(off, NETXEN_ADDR_QDR_NET,
mem_crb = NETXEN_CRB_QDR_NET; NETXEN_ADDR_QDR_NET_MAX_P3)) {
else { mem_crb = netxen_get_ioaddr(adapter, NETXEN_CRB_QDR_NET);
mem_crb = NETXEN_CRB_DDR_NET; goto correct;
if (netxen_nic_pci_mem_bound_check(adapter, off, size) == 0)
return netxen_nic_pci_mem_write_direct(adapter,
off, data, size);
} }
if (ADDR_IN_RANGE(off, NETXEN_ADDR_DDR_NET, NETXEN_ADDR_DDR_NET_MAX)) {
mem_crb = netxen_get_ioaddr(adapter, NETXEN_CRB_DDR_NET);
goto correct;
}
return -EIO;
correct:
off8 = off & 0xfffffff8; off8 = off & 0xfffffff8;
off0 = off & 0x7; off0 = off & 0x7;
sz[0] = (size < (8 - off0)) ? size : (8 - off0); sz[0] = (size < (8 - off0)) ? size : (8 - off0);
...@@ -1906,21 +1734,18 @@ netxen_nic_pci_mem_write_2M(struct netxen_adapter *adapter, ...@@ -1906,21 +1734,18 @@ netxen_nic_pci_mem_write_2M(struct netxen_adapter *adapter,
*/ */
for (i = 0; i < loop; i++) { for (i = 0; i < loop; i++) {
temp = off8 + (i << 3); writel(off8 + (i << 3), mem_crb+MIU_TEST_AGT_ADDR_LO);
NXWR32(adapter, mem_crb+MIU_TEST_AGT_ADDR_LO, temp); writel(0, mem_crb+MIU_TEST_AGT_ADDR_HI);
temp = 0; writel(word[i] & 0xffffffff, mem_crb+MIU_TEST_AGT_WRDATA_LO);
NXWR32(adapter, mem_crb+MIU_TEST_AGT_ADDR_HI, temp); writel((word[i] >> 32) & 0xffffffff,
temp = word[i] & 0xffffffff; mem_crb+MIU_TEST_AGT_WRDATA_HI);
NXWR32(adapter, mem_crb+MIU_TEST_AGT_WRDATA_LO, temp); writel((MIU_TA_CTL_ENABLE | MIU_TA_CTL_WRITE),
temp = (word[i] >> 32) & 0xffffffff; mem_crb+MIU_TEST_AGT_CTRL);
NXWR32(adapter, mem_crb+MIU_TEST_AGT_WRDATA_HI, temp); writel(MIU_TA_CTL_START | MIU_TA_CTL_ENABLE | MIU_TA_CTL_WRITE,
temp = MIU_TA_CTL_ENABLE | MIU_TA_CTL_WRITE; mem_crb+MIU_TEST_AGT_CTRL);
NXWR32(adapter, mem_crb+MIU_TEST_AGT_CTRL, temp);
temp = MIU_TA_CTL_START | MIU_TA_CTL_ENABLE | MIU_TA_CTL_WRITE;
NXWR32(adapter, mem_crb+MIU_TEST_AGT_CTRL, temp);
for (j = 0; j < MAX_CTL_CHECK; j++) { for (j = 0; j < MAX_CTL_CHECK; j++) {
temp = NXRD32(adapter, mem_crb + MIU_TEST_AGT_CTRL); temp = readl(mem_crb + MIU_TEST_AGT_CTRL);
if ((temp & MIU_TA_CTL_BUSY) == 0) if ((temp & MIU_TA_CTL_BUSY) == 0)
break; break;
} }
...@@ -1947,21 +1772,26 @@ netxen_nic_pci_mem_read_2M(struct netxen_adapter *adapter, ...@@ -1947,21 +1772,26 @@ netxen_nic_pci_mem_read_2M(struct netxen_adapter *adapter,
{ {
int i, j = 0, k, start, end, loop, sz[2], off0[2]; int i, j = 0, k, start, end, loop, sz[2], off0[2];
uint32_t temp; uint32_t temp;
uint64_t off8, val, mem_crb, word[2] = {0, 0}; uint64_t off8, val, word[2] = {0, 0};
void __iomem *mem_crb;
/* if (size != 8)
* If not MN, go check for MS or invalid. return -EIO;
*/
if (off >= NETXEN_ADDR_QDR_NET && off <= NETXEN_ADDR_QDR_NET_MAX_P3) if (ADDR_IN_RANGE(off, NETXEN_ADDR_QDR_NET,
mem_crb = NETXEN_CRB_QDR_NET; NETXEN_ADDR_QDR_NET_MAX_P3)) {
else { mem_crb = netxen_get_ioaddr(adapter, NETXEN_CRB_QDR_NET);
mem_crb = NETXEN_CRB_DDR_NET; goto correct;
if (netxen_nic_pci_mem_bound_check(adapter, off, size) == 0)
return netxen_nic_pci_mem_read_direct(adapter,
off, data, size);
} }
if (ADDR_IN_RANGE(off, NETXEN_ADDR_DDR_NET, NETXEN_ADDR_DDR_NET_MAX)) {
mem_crb = netxen_get_ioaddr(adapter, NETXEN_CRB_DDR_NET);
goto correct;
}
return -EIO;
correct:
off8 = off & 0xfffffff8; off8 = off & 0xfffffff8;
off0[0] = off & 0x7; off0[0] = off & 0x7;
off0[1] = 0; off0[1] = 0;
...@@ -1976,17 +1806,14 @@ netxen_nic_pci_mem_read_2M(struct netxen_adapter *adapter, ...@@ -1976,17 +1806,14 @@ netxen_nic_pci_mem_read_2M(struct netxen_adapter *adapter,
*/ */
for (i = 0; i < loop; i++) { for (i = 0; i < loop; i++) {
temp = off8 + (i << 3); writel(off8 + (i << 3), mem_crb + MIU_TEST_AGT_ADDR_LO);
NXWR32(adapter, mem_crb + MIU_TEST_AGT_ADDR_LO, temp); writel(0, mem_crb + MIU_TEST_AGT_ADDR_HI);
temp = 0; writel(MIU_TA_CTL_ENABLE, mem_crb + MIU_TEST_AGT_CTRL);
NXWR32(adapter, mem_crb + MIU_TEST_AGT_ADDR_HI, temp); writel(MIU_TA_CTL_START | MIU_TA_CTL_ENABLE,
temp = MIU_TA_CTL_ENABLE; mem_crb + MIU_TEST_AGT_CTRL);
NXWR32(adapter, mem_crb + MIU_TEST_AGT_CTRL, temp);
temp = MIU_TA_CTL_START | MIU_TA_CTL_ENABLE;
NXWR32(adapter, mem_crb + MIU_TEST_AGT_CTRL, temp);
for (j = 0; j < MAX_CTL_CHECK; j++) { for (j = 0; j < MAX_CTL_CHECK; j++) {
temp = NXRD32(adapter, mem_crb + MIU_TEST_AGT_CTRL); temp = readl(mem_crb + MIU_TEST_AGT_CTRL);
if ((temp & MIU_TA_CTL_BUSY) == 0) if ((temp & MIU_TA_CTL_BUSY) == 0)
break; break;
} }
...@@ -2001,8 +1828,7 @@ netxen_nic_pci_mem_read_2M(struct netxen_adapter *adapter, ...@@ -2001,8 +1828,7 @@ netxen_nic_pci_mem_read_2M(struct netxen_adapter *adapter,
start = off0[i] >> 2; start = off0[i] >> 2;
end = (off0[i] + sz[i] - 1) >> 2; end = (off0[i] + sz[i] - 1) >> 2;
for (k = start; k <= end; k++) { for (k = start; k <= end; k++) {
temp = NXRD32(adapter, temp = readl(mem_crb + MIU_TEST_AGT_RDDATA(k));
mem_crb + MIU_TEST_AGT_RDDATA(k));
word[i] |= ((uint64_t)temp << (32 * k)); word[i] |= ((uint64_t)temp << (32 * k));
} }
} }
......
...@@ -643,9 +643,10 @@ netxen_setup_pci_map(struct netxen_adapter *adapter) ...@@ -643,9 +643,10 @@ netxen_setup_pci_map(struct netxen_adapter *adapter)
adapter->ahw.ddr_mn_window = 0; adapter->ahw.ddr_mn_window = 0;
adapter->ahw.qdr_sn_window = 0; adapter->ahw.qdr_sn_window = 0;
adapter->ahw.mn_win_crb = 0x100000 + PCIX_MN_WINDOW + adapter->ahw.mn_win_crb = NETXEN_PCI_CRBSPACE +
(pci_func * 0x20); 0x100000 + PCIX_MN_WINDOW + (pci_func * 0x20);
adapter->ahw.ms_win_crb = 0x100000 + PCIX_SN_WINDOW; adapter->ahw.ms_win_crb = NETXEN_PCI_CRBSPACE +
0x100000 + PCIX_SN_WINDOW;
if (pci_func < 4) if (pci_func < 4)
adapter->ahw.ms_win_crb += (pci_func * 0x20); adapter->ahw.ms_win_crb += (pci_func * 0x20);
else else
......
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