diff options
author | Peter Robinson <pbrobinson@gmail.com> | 2016-09-17 18:00:13 +0100 |
---|---|---|
committer | Peter Robinson <pbrobinson@gmail.com> | 2016-09-17 18:00:13 +0100 |
commit | 3164e2db5f8ae9db4ae6ff9c055b974d11cb7f79 (patch) | |
tree | 74ab38143454bc43f928e5a82ac7ab827dde5a93 /arm64-pcie-quirks-xgene.patch | |
parent | dffeb9c7c27b5c7fbc61552f39797d11f9aede35 (diff) | |
download | kernel-3164e2db5f8ae9db4ae6ff9c055b974d11cb7f79.tar.gz kernel-3164e2db5f8ae9db4ae6ff9c055b974d11cb7f79.tar.xz kernel-3164e2db5f8ae9db4ae6ff9c055b974d11cb7f79.zip |
Update ARM64 pci-e quicks to latest upstream, update x-gene quirks patch
Diffstat (limited to 'arm64-pcie-quirks-xgene.patch')
-rw-r--r-- | arm64-pcie-quirks-xgene.patch | 508 |
1 files changed, 0 insertions, 508 deletions
diff --git a/arm64-pcie-quirks-xgene.patch b/arm64-pcie-quirks-xgene.patch deleted file mode 100644 index 9defc1763..000000000 --- a/arm64-pcie-quirks-xgene.patch +++ /dev/null @@ -1,508 +0,0 @@ -From 965e95a91066290f6555546f066a6e2aaba1199e Mon Sep 17 00:00:00 2001 -From: Peter Robinson <pbrobinson@gmail.com> -Date: Tue, 5 Jul 2016 23:49:39 +0100 -Subject: [PATCH] Some platforms may not be fully compliant with generic set of - PCI config accessors. For these cases we implement the way to overwrite - accessors set. Algorithm traverses available quirk list, matches against - <oem_id, oem_table_id, domain, bus number> tuple and returns corresponding - PCI config ops. oem_id and oem_table_id come from MCFG table standard header. - All quirks can be defined using DECLARE_ACPI_MCFG_FIXUP() macro and kept self - contained. Example: - -/* Custom PCI config ops */ -static struct pci_generic_ecam_ops foo_pci_ops = { - .bus_shift = 24, - .pci_ops = { - .map_bus = pci_ecam_map_bus, - .read = foo_ecam_config_read, - .write = foo_ecam_config_write, - } -}; - -DECLARE_ACPI_MCFG_FIXUP(&foo_pci_ops, <oem_id_str>, <oem_table_id>, <domain_nr>, <bus_nr>); - -Signed-off-by: Tomasz Nowicki <tn@semihalf.com> -Signed-off-by: Dongdong Liu <liudongdong3@huawei.com> ---- - drivers/acpi/pci_mcfg.c | 41 ++++++++++++++++++++++++++++++++++++--- - include/asm-generic/vmlinux.lds.h | 7 +++++++ - include/linux/pci-acpi.h | 20 +++++++++++++++++++ - 3 files changed, 65 insertions(+), 3 deletions(-) - -diff --git a/drivers/acpi/pci_mcfg.c b/drivers/acpi/pci_mcfg.c -index b5b376e..a5c9067 100644 ---- a/drivers/acpi/pci_mcfg.c -+++ b/drivers/acpi/pci_mcfg.c -@@ -22,6 +22,10 @@ - #include <linux/kernel.h> - #include <linux/pci.h> - #include <linux/pci-acpi.h> -+#include <linux/pci-ecam.h> -+ -+/* Root pointer to the mapped MCFG table */ -+static struct acpi_table_mcfg *mcfg_table; - - /* Structure to hold entries from the MCFG table */ - struct mcfg_entry { -@@ -35,6 +39,38 @@ struct mcfg_entry { - /* List to save MCFG entries */ - static LIST_HEAD(pci_mcfg_list); - -+extern struct pci_cfg_fixup __start_acpi_mcfg_fixups[]; -+extern struct pci_cfg_fixup __end_acpi_mcfg_fixups[]; -+ -+struct pci_ecam_ops *pci_mcfg_get_ops(struct acpi_pci_root *root) -+{ -+ int bus_num = root->secondary.start; -+ int domain = root->segment; -+ struct pci_cfg_fixup *f; -+ -+ if (!mcfg_table) -+ return &pci_generic_ecam_ops; -+ -+ /* -+ * Match against platform specific quirks and return corresponding -+ * CAM ops. -+ * -+ * First match against PCI topology <domain:bus> then use OEM ID and -+ * OEM revision from MCFG table standard header. -+ */ -+ for (f = __start_acpi_mcfg_fixups; f < __end_acpi_mcfg_fixups; f++) { -+ if ((f->domain == domain || f->domain == PCI_MCFG_DOMAIN_ANY) && -+ (f->bus_num == bus_num || f->bus_num == PCI_MCFG_BUS_ANY) && -+ (!strncmp(f->oem_id, mcfg_table->header.oem_id, -+ ACPI_OEM_ID_SIZE)) && -+ (!strncmp(f->oem_table_id, mcfg_table->header.oem_table_id, -+ ACPI_OEM_TABLE_ID_SIZE))) -+ return f->ops; -+ } -+ /* No quirks, use ECAM */ -+ return &pci_generic_ecam_ops; -+} -+ - phys_addr_t pci_mcfg_lookup(u16 seg, struct resource *bus_res) - { - struct mcfg_entry *e; -@@ -54,7 +90,6 @@ phys_addr_t pci_mcfg_lookup(u16 seg, struct resource *bus_res) - - static __init int pci_mcfg_parse(struct acpi_table_header *header) - { -- struct acpi_table_mcfg *mcfg; - struct acpi_mcfg_allocation *mptr; - struct mcfg_entry *e, *arr; - int i, n; -@@ -64,8 +99,8 @@ static __init int pci_mcfg_parse(struct acpi_table_header *header) - - n = (header->length - sizeof(struct acpi_table_mcfg)) / - sizeof(struct acpi_mcfg_allocation); -- mcfg = (struct acpi_table_mcfg *)header; -- mptr = (struct acpi_mcfg_allocation *) &mcfg[1]; -+ mcfg_table = (struct acpi_table_mcfg *)header; -+ mptr = (struct acpi_mcfg_allocation *) &mcfg_table[1]; - - arr = kcalloc(n, sizeof(*arr), GFP_KERNEL); - if (!arr) -diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h -index 2456397..c49bd36 100644 ---- a/include/asm-generic/vmlinux.lds.h -+++ b/include/asm-generic/vmlinux.lds.h -@@ -308,6 +308,13 @@ - VMLINUX_SYMBOL(__end_pci_fixups_suspend_late) = .; \ - } \ - \ -+ /* ACPI MCFG quirks */ \ -+ .acpi_fixup : AT(ADDR(.acpi_fixup) - LOAD_OFFSET) { \ -+ VMLINUX_SYMBOL(__start_acpi_mcfg_fixups) = .; \ -+ *(.acpi_fixup_mcfg) \ -+ VMLINUX_SYMBOL(__end_acpi_mcfg_fixups) = .; \ -+ } \ -+ \ - /* Built-in firmware blobs */ \ - .builtin_fw : AT(ADDR(.builtin_fw) - LOAD_OFFSET) { \ - VMLINUX_SYMBOL(__start_builtin_fw) = .; \ -diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h -index 7d63a66..c8a6559 100644 ---- a/include/linux/pci-acpi.h -+++ b/include/linux/pci-acpi.h -@@ -25,6 +25,7 @@ static inline acpi_status pci_acpi_remove_pm_notifier(struct acpi_device *dev) - extern phys_addr_t acpi_pci_root_get_mcfg_addr(acpi_handle handle); - - extern phys_addr_t pci_mcfg_lookup(u16 domain, struct resource *bus_res); -+extern struct pci_ecam_ops *pci_mcfg_get_ops(struct acpi_pci_root *root); - - static inline acpi_handle acpi_find_root_bridge_handle(struct pci_dev *pdev) - { -@@ -72,6 +73,25 @@ struct acpi_pci_root_ops { - int (*prepare_resources)(struct acpi_pci_root_info *info); - }; - -+struct pci_cfg_fixup { -+ struct pci_ecam_ops *ops; -+ char *oem_id; -+ char *oem_table_id; -+ int domain; -+ int bus_num; -+}; -+ -+#define PCI_MCFG_DOMAIN_ANY -1 -+#define PCI_MCFG_BUS_ANY -1 -+ -+/* Designate a routine to fix up buggy MCFG */ -+#define DECLARE_ACPI_MCFG_FIXUP(ops, oem_id, oem_table_id, dom, bus) \ -+ static const struct pci_cfg_fixup \ -+ __mcfg_fixup_##oem_id##oem_table_id##dom##bus \ -+ __used __attribute__((__section__(".acpi_fixup_mcfg"), \ -+ aligned((sizeof(void *))))) = \ -+ { ops, oem_id, oem_table_id, dom, bus }; -+ - extern int acpi_pci_probe_root_resources(struct acpi_pci_root_info *info); - extern struct pci_bus *acpi_pci_root_create(struct acpi_pci_root *root, - struct acpi_pci_root_ops *ops, --- -2.9.2 - -From 817d09d7650319a827f00bd3b4c9b407d3977ba0 Mon Sep 17 00:00:00 2001 -From: Peter Robinson <pbrobinson@gmail.com> -Date: Tue, 5 Jul 2016 23:52:46 +0100 -Subject: [PATCH] pci_generic_ecam_ops is used by default. Since there are - platforms which have non-compliant ECAM space we need to overwrite these - accessors prior to PCI buses enumeration. In order to do that we call - pci_mcfg_get_ops to retrieve pci_ecam_ops structure so that we can use proper - PCI config space accessors and bus_shift. - -pci_generic_ecam_ops is still used for platforms free from quirks. - -Signed-off-by: Tomasz Nowicki <tn@semihalf.com> ---- - arch/arm64/kernel/pci.c | 7 ++++--- - 1 file changed, 4 insertions(+), 3 deletions(-) - -diff --git a/arch/arm64/kernel/pci.c b/arch/arm64/kernel/pci.c -index acf3872..ec513f1 100644 ---- a/arch/arm64/kernel/pci.c -+++ b/arch/arm64/kernel/pci.c -@@ -126,6 +126,7 @@ pci_acpi_setup_ecam_mapping(struct acpi_pci_root *root) - struct pci_config_window *cfg; - struct resource cfgres; - unsigned int bsz; -+ struct pci_ecam_ops *ops; - - /* Use address from _CBA if present, otherwise lookup MCFG */ - if (!root->mcfg_addr) -@@ -137,12 +138,12 @@ pci_acpi_setup_ecam_mapping(struct acpi_pci_root *root) - return NULL; - } - -- bsz = 1 << pci_generic_ecam_ops.bus_shift; -+ ops = pci_mcfg_get_ops(root); -+ bsz = 1 << ops->bus_shift; - cfgres.start = root->mcfg_addr + bus_res->start * bsz; - cfgres.end = cfgres.start + resource_size(bus_res) * bsz - 1; - cfgres.flags = IORESOURCE_MEM; -- cfg = pci_ecam_create(&root->device->dev, &cfgres, bus_res, -- &pci_generic_ecam_ops); -+ cfg = pci_ecam_create(&root->device->dev, &cfgres, bus_res, ops); - if (IS_ERR(cfg)) { - dev_err(&root->device->dev, "%04x:%pR error %ld mapping ECAM\n", - seg, bus_res, PTR_ERR(cfg)); --- -2.9.2 - -From ac5cff2e2304a1969e39e967567aa41cade1839f Mon Sep 17 00:00:00 2001 -From: Peter Robinson <pbrobinson@gmail.com> -Date: Tue, 5 Jul 2016 23:53:59 +0100 -Subject: [PATCH] The ECAM quirk matching criteria per the discussion on - https://lkml.org/lkml/2016/6/13/944 includes: OEM ID, OEM Table ID and OEM - Revision. So this patch adds OEM Table ID into the check to match platform - specific ECAM quirks as well. - -This patch also improve strncmp check using strlen and -min_t to ignore the padding spaces in OEM ID and OEM -Table ID. - -Signed-off-by: Duc Dang <dhdang@apm.com> ---- - drivers/acpi/pci_mcfg.c | 7 +++++-- - include/linux/pci-acpi.h | 7 ++++--- - 2 files changed, 9 insertions(+), 5 deletions(-) - -diff --git a/drivers/acpi/pci_mcfg.c b/drivers/acpi/pci_mcfg.c -index a5c9067..5137d16 100644 ---- a/drivers/acpi/pci_mcfg.c -+++ b/drivers/acpi/pci_mcfg.c -@@ -62,9 +62,12 @@ struct pci_ecam_ops *pci_mcfg_get_ops(struct acpi_pci_root *root) - if ((f->domain == domain || f->domain == PCI_MCFG_DOMAIN_ANY) && - (f->bus_num == bus_num || f->bus_num == PCI_MCFG_BUS_ANY) && - (!strncmp(f->oem_id, mcfg_table->header.oem_id, -- ACPI_OEM_ID_SIZE)) && -+ min_t(size_t, strlen(f->oem_id), -+ ACPI_OEM_ID_SIZE))) && - (!strncmp(f->oem_table_id, mcfg_table->header.oem_table_id, -- ACPI_OEM_TABLE_ID_SIZE))) -+ min_t(size_t, strlen(f->oem_table_id), -+ ACPI_OEM_TABLE_ID_SIZE))) && -+ (f->oem_revision == mcfg_table->header.oem_revision)) - return f->ops; - } - /* No quirks, use ECAM */ -diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h -index c8a6559..5148c8d 100644 ---- a/include/linux/pci-acpi.h -+++ b/include/linux/pci-acpi.h -@@ -77,6 +77,7 @@ struct pci_cfg_fixup { - struct pci_ecam_ops *ops; - char *oem_id; - char *oem_table_id; -+ u32 oem_revision; - int domain; - int bus_num; - }; -@@ -85,12 +86,12 @@ struct pci_cfg_fixup { - #define PCI_MCFG_BUS_ANY -1 - - /* Designate a routine to fix up buggy MCFG */ --#define DECLARE_ACPI_MCFG_FIXUP(ops, oem_id, oem_table_id, dom, bus) \ -+#define DECLARE_ACPI_MCFG_FIXUP(ops, oem_id, oem_table_id, rev, dom, bus) \ - static const struct pci_cfg_fixup \ -- __mcfg_fixup_##oem_id##oem_table_id##dom##bus \ -+ __mcfg_fixup_##oem_id##oem_table_id##rev##dom##bus \ - __used __attribute__((__section__(".acpi_fixup_mcfg"), \ - aligned((sizeof(void *))))) = \ -- { ops, oem_id, oem_table_id, dom, bus }; -+ { ops, oem_id, oem_table_id, rev, dom, bus }; - - extern int acpi_pci_probe_root_resources(struct acpi_pci_root_info *info); - extern struct pci_bus *acpi_pci_root_create(struct acpi_pci_root *root, --- -2.9.2 - -From b9c1592c6b615da0c26168c5c3e0f8fc256a23ca Mon Sep 17 00:00:00 2001 -From: Peter Robinson <pbrobinson@gmail.com> -Date: Tue, 5 Jul 2016 23:55:11 +0100 -Subject: [PATCH] X-Gene PCIe controller does not fully support ECAM. This - patch adds required ECAM fixup to allow X-Gene PCIe controller to be - functional in ACPI boot mode. - -Signed-off-by: Duc Dang <dhdang@apm.com> ---- - drivers/pci/host/Makefile | 2 +- - drivers/pci/host/pci-xgene-ecam.c | 194 ++++++++++++++++++++++++++++++++++++++ - 2 files changed, 195 insertions(+), 1 deletion(-) - create mode 100644 drivers/pci/host/pci-xgene-ecam.c - -diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile -index 8843410..af4f505 100644 ---- a/drivers/pci/host/Makefile -+++ b/drivers/pci/host/Makefile -@@ -15,7 +15,7 @@ obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o - obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o - obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o - obj-$(CONFIG_PCIE_XILINX_NWL) += pcie-xilinx-nwl.o --obj-$(CONFIG_PCI_XGENE) += pci-xgene.o -+obj-$(CONFIG_PCI_XGENE) += pci-xgene.o pci-xgene-ecam.o - obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o - obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o - obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o -diff --git a/drivers/pci/host/pci-xgene-ecam.c b/drivers/pci/host/pci-xgene-ecam.c -new file mode 100644 -index 0000000..1bea63f ---- /dev/null -+++ b/drivers/pci/host/pci-xgene-ecam.c -@@ -0,0 +1,194 @@ -+/* -+ * APM X-Gene PCIe ECAM fixup driver -+ * -+ * Copyright (c) 2016, Applied Micro Circuits Corporation -+ * Author: -+ * Duc Dang <dhdang@apm.com> -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program. If not, see <http://www.gnu.org/licenses/>. -+ */ -+ -+#include <linux/kernel.h> -+#include <linux/module.h> -+#include <linux/of_address.h> -+#include <linux/of_pci.h> -+#include <linux/pci-acpi.h> -+#include <linux/platform_device.h> -+#include <linux/pci-ecam.h> -+ -+#ifdef CONFIG_ACPI -+#define RTDID 0x160 -+#define ROOT_CAP_AND_CTRL 0x5C -+ -+/* PCIe IP version */ -+#define XGENE_PCIE_IP_VER_UNKN 0 -+#define XGENE_PCIE_IP_VER_1 1 -+ -+#define APM_OEM_ID "APM" -+#define APM_XGENE_OEM_TABLE_ID "XGENE" -+#define APM_XGENE_OEM_REV 0x00000002 -+ -+struct xgene_pcie_acpi_root { -+ void __iomem *csr_base; -+ u32 version; -+}; -+ -+static acpi_status xgene_pcie_find_csr_base(struct acpi_resource *acpi_res, -+ void *data) -+{ -+ struct xgene_pcie_acpi_root *root = data; -+ struct acpi_resource_fixed_memory32 *fixed32; -+ -+ if (acpi_res->type == ACPI_RESOURCE_TYPE_FIXED_MEMORY32) { -+ fixed32 = &acpi_res->data.fixed_memory32; -+ root->csr_base = ioremap(fixed32->address, -+ fixed32->address_length); -+ return AE_CTRL_TERMINATE; -+ } -+ -+ return AE_OK; -+} -+ -+static int xgene_pcie_ecam_init(struct pci_config_window *cfg) -+{ -+ struct xgene_pcie_acpi_root *xgene_root; -+ struct device *dev = cfg->parent; -+ struct acpi_device *adev = to_acpi_device(dev); -+ acpi_handle handle = acpi_device_handle(adev); -+ -+ xgene_root = devm_kzalloc(dev, sizeof(*xgene_root), GFP_KERNEL); -+ if (!xgene_root) -+ return -ENOMEM; -+ -+ acpi_walk_resources(handle, METHOD_NAME__CRS, -+ xgene_pcie_find_csr_base, xgene_root); -+ -+ if (!xgene_root->csr_base) { -+ kfree(xgene_root); -+ return -ENODEV; -+ } -+ -+ xgene_root->version = XGENE_PCIE_IP_VER_1; -+ -+ cfg->priv = xgene_root; -+ -+ return 0; -+} -+ -+/* -+ * For Configuration request, RTDID register is used as Bus Number, -+ * Device Number and Function number of the header fields. -+ */ -+static void xgene_pcie_set_rtdid_reg(struct pci_bus *bus, uint devfn) -+{ -+ struct pci_config_window *cfg = bus->sysdata; -+ struct xgene_pcie_acpi_root *port = cfg->priv; -+ unsigned int b, d, f; -+ u32 rtdid_val = 0; -+ -+ b = bus->number; -+ d = PCI_SLOT(devfn); -+ f = PCI_FUNC(devfn); -+ -+ if (!pci_is_root_bus(bus)) -+ rtdid_val = (b << 8) | (d << 3) | f; -+ -+ writel(rtdid_val, port->csr_base + RTDID); -+ /* read the register back to ensure flush */ -+ readl(port->csr_base + RTDID); -+} -+ -+/* -+ * X-Gene PCIe port uses BAR0-BAR1 of RC's configuration space as -+ * the translation from PCI bus to native BUS. Entire DDR region -+ * is mapped into PCIe space using these registers, so it can be -+ * reached by DMA from EP devices. The BAR0/1 of bridge should be -+ * hidden during enumeration to avoid the sizing and resource allocation -+ * by PCIe core. -+ */ -+static bool xgene_pcie_hide_rc_bars(struct pci_bus *bus, int offset) -+{ -+ if (pci_is_root_bus(bus) && ((offset == PCI_BASE_ADDRESS_0) || -+ (offset == PCI_BASE_ADDRESS_1))) -+ return true; -+ -+ return false; -+} -+ -+void __iomem *xgene_pcie_ecam_map_bus(struct pci_bus *bus, -+ unsigned int devfn, int where) -+{ -+ struct pci_config_window *cfg = bus->sysdata; -+ unsigned int busn = bus->number; -+ void __iomem *base; -+ -+ if (busn < cfg->busr.start || busn > cfg->busr.end) -+ return NULL; -+ -+ if ((pci_is_root_bus(bus) && devfn != 0) || -+ xgene_pcie_hide_rc_bars(bus, where)) -+ return NULL; -+ -+ xgene_pcie_set_rtdid_reg(bus, devfn); -+ -+ if (busn > cfg->busr.start) -+ base = cfg->win + (1 << cfg->ops->bus_shift); -+ else -+ base = cfg->win; -+ -+ return base + where; -+} -+ -+static int xgene_pcie_config_read32(struct pci_bus *bus, unsigned int devfn, -+ int where, int size, u32 *val) -+{ -+ struct pci_config_window *cfg = bus->sysdata; -+ struct xgene_pcie_acpi_root *port = cfg->priv; -+ -+ if (pci_generic_config_read32(bus, devfn, where & ~0x3, 4, val) != -+ PCIBIOS_SUCCESSFUL) -+ return PCIBIOS_DEVICE_NOT_FOUND; -+ -+ /* -+ * The v1 controller has a bug in its Configuration Request -+ * Retry Status (CRS) logic: when CRS is enabled and we read the -+ * Vendor and Device ID of a non-existent device, the controller -+ * fabricates return data of 0xFFFF0001 ("device exists but is not -+ * ready") instead of 0xFFFFFFFF ("device does not exist"). This -+ * causes the PCI core to retry the read until it times out. -+ * Avoid this by not claiming to support CRS. -+ */ -+ if (pci_is_root_bus(bus) && (port->version == XGENE_PCIE_IP_VER_1) && -+ ((where & ~0x3) == ROOT_CAP_AND_CTRL)) -+ *val &= ~(PCI_EXP_RTCAP_CRSVIS << 16); -+ -+ if (size <= 2) -+ *val = (*val >> (8 * (where & 3))) & ((1 << (size * 8)) - 1); -+ -+ return PCIBIOS_SUCCESSFUL; -+} -+ -+static struct pci_ecam_ops xgene_pcie_ecam_ops = { -+ .bus_shift = 16, -+ .init = xgene_pcie_ecam_init, -+ .pci_ops = { -+ .map_bus = xgene_pcie_ecam_map_bus, -+ .read = xgene_pcie_config_read32, -+ .write = pci_generic_config_write, -+ } -+}; -+ -+DECLARE_ACPI_MCFG_FIXUP(&xgene_pcie_ecam_ops, APM_OEM_ID, -+ APM_XGENE_OEM_TABLE_ID, APM_XGENE_OEM_REV, -+ PCI_MCFG_DOMAIN_ANY, PCI_MCFG_BUS_ANY); -+#endif --- -2.9.2 - |