From mboxrd@z Thu Jan 1 00:00:00 1970 Return-path: Received: from metis.ext.pengutronix.de ([2001:6f8:1178:4:290:27ff:fe1d:cc33]) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1YXl9m-00069x-8s for barebox@lists.infradead.org; Tue, 17 Mar 2015 06:39:18 +0000 From: Sascha Hauer Date: Tue, 17 Mar 2015 07:38:48 +0100 Message-Id: <1426574331-22797-5-git-send-email-s.hauer@pengutronix.de> In-Reply-To: <1426574331-22797-1-git-send-email-s.hauer@pengutronix.de> References: <1426574331-22797-1-git-send-email-s.hauer@pengutronix.de> List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Sender: "barebox" Errors-To: barebox-bounces+u.kleine-koenig=pengutronix.de@lists.infradead.org Subject: [PATCH 4/7] pci: Add pcie-designware driver To: Barebox List Based on the corresponding kernel driver, only small changes to make it work on barebox. Signed-off-by: Sascha Hauer --- drivers/pci/Kconfig | 3 + drivers/pci/Makefile | 1 + drivers/pci/pcie-designware.c | 564 ++++++++++++++++++++++++++++++++++++++++++ drivers/pci/pcie-designware.h | 71 ++++++ 4 files changed, 639 insertions(+) create mode 100644 drivers/pci/pcie-designware.c create mode 100644 drivers/pci/pcie-designware.h diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index 0e9308e..bd24ce0 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -24,6 +24,9 @@ config PCI_DEBUG When in doubt, say N. +config PCIE_DW + bool + config PCI_MVEBU bool "Marvell EBU PCIe driver" depends on ARCH_MVEBU diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index b93b28a..3240abd 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -9,3 +9,4 @@ CPPFLAGS += $(ccflags-y) obj-$(CONFIG_PCI_MVEBU) += pci-mvebu.o pci-mvebu-phy.o obj-$(CONFIG_PCI_TEGRA) += pci-tegra.o +obj-$(CONFIG_PCIE_DW) += pcie-designware.o diff --git a/drivers/pci/pcie-designware.c b/drivers/pci/pcie-designware.c new file mode 100644 index 0000000..4edaede --- /dev/null +++ b/drivers/pci/pcie-designware.c @@ -0,0 +1,564 @@ +/* + * Synopsys Designware PCIe host controller driver + * + * Copyright (C) 2013 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Jingoo Han + * + * 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. + */ + + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pcie-designware.h" + +/* Synopsis specific PCIE configuration registers */ +#define PCIE_PORT_LINK_CONTROL 0x710 +#define PORT_LINK_MODE_MASK (0x3f << 16) +#define PORT_LINK_MODE_1_LANES (0x1 << 16) +#define PORT_LINK_MODE_2_LANES (0x3 << 16) +#define PORT_LINK_MODE_4_LANES (0x7 << 16) + +#define PCIE_LINK_WIDTH_SPEED_CONTROL 0x80C +#define PORT_LOGIC_SPEED_CHANGE (0x1 << 17) +#define PORT_LOGIC_LINK_WIDTH_MASK (0x1ff << 8) +#define PORT_LOGIC_LINK_WIDTH_1_LANES (0x1 << 8) +#define PORT_LOGIC_LINK_WIDTH_2_LANES (0x2 << 8) +#define PORT_LOGIC_LINK_WIDTH_4_LANES (0x4 << 8) + +#define PCIE_MSI_ADDR_LO 0x820 +#define PCIE_MSI_ADDR_HI 0x824 +#define PCIE_MSI_INTR0_ENABLE 0x828 +#define PCIE_MSI_INTR0_MASK 0x82C +#define PCIE_MSI_INTR0_STATUS 0x830 + +#define PCIE_ATU_VIEWPORT 0x900 +#define PCIE_ATU_REGION_INBOUND (0x1 << 31) +#define PCIE_ATU_REGION_OUTBOUND (0x0 << 31) +#define PCIE_ATU_REGION_INDEX1 (0x1 << 0) +#define PCIE_ATU_REGION_INDEX0 (0x0 << 0) +#define PCIE_ATU_CR1 0x904 +#define PCIE_ATU_TYPE_MEM (0x0 << 0) +#define PCIE_ATU_TYPE_IO (0x2 << 0) +#define PCIE_ATU_TYPE_CFG0 (0x4 << 0) +#define PCIE_ATU_TYPE_CFG1 (0x5 << 0) +#define PCIE_ATU_CR2 0x908 +#define PCIE_ATU_ENABLE (0x1 << 31) +#define PCIE_ATU_BAR_MODE_ENABLE (0x1 << 30) +#define PCIE_ATU_LOWER_BASE 0x90C +#define PCIE_ATU_UPPER_BASE 0x910 +#define PCIE_ATU_LIMIT 0x914 +#define PCIE_ATU_LOWER_TARGET 0x918 +#define PCIE_ATU_BUS(x) (((x) & 0xff) << 24) +#define PCIE_ATU_DEV(x) (((x) & 0x1f) << 19) +#define PCIE_ATU_FUNC(x) (((x) & 0x7) << 16) +#define PCIE_ATU_UPPER_TARGET 0x91C + +static unsigned long global_io_offset; + +int dw_pcie_cfg_read(void __iomem *addr, int where, int size, u32 *val) +{ + *val = readl(addr); + + if (size == 1) + *val = (*val >> (8 * (where & 3))) & 0xff; + else if (size == 2) + *val = (*val >> (8 * (where & 3))) & 0xffff; + else if (size != 4) + return PCIBIOS_BAD_REGISTER_NUMBER; + + return PCIBIOS_SUCCESSFUL; +} + +int dw_pcie_cfg_write(void __iomem *addr, int where, int size, u32 val) +{ + if (size == 4) + writel(val, addr); + else if (size == 2) + writew(val, addr + (where & 2)); + else if (size == 1) + writeb(val, addr + (where & 3)); + else + return PCIBIOS_BAD_REGISTER_NUMBER; + + return PCIBIOS_SUCCESSFUL; +} + +static inline void dw_pcie_readl_rc(struct pcie_port *pp, u32 reg, u32 *val) +{ + if (pp->ops->readl_rc) + pp->ops->readl_rc(pp, pp->dbi_base + reg, val); + else + *val = readl(pp->dbi_base + reg); +} + +static inline void dw_pcie_writel_rc(struct pcie_port *pp, u32 val, u32 reg) +{ + if (pp->ops->writel_rc) + pp->ops->writel_rc(pp, val, pp->dbi_base + reg); + else + writel(val, pp->dbi_base + reg); +} + +#include + +static int dw_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, + u32 *val) +{ + int ret; + + if (pp->ops->rd_own_conf) + ret = pp->ops->rd_own_conf(pp, where, size, val); + else + ret = dw_pcie_cfg_read(pp->dbi_base + (where & ~0x3), where, + size, val); + + return ret; +} + +static int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size, + u32 val) +{ + int ret; + + if (pp->ops->wr_own_conf) + ret = pp->ops->wr_own_conf(pp, where, size, val); + else + ret = dw_pcie_cfg_write(pp->dbi_base + (where & ~0x3), where, + size, val); + + return ret; +} + +int dw_pcie_link_up(struct pcie_port *pp) +{ + if (pp->ops->link_up) + return pp->ops->link_up(pp); + else + return 0; +} + +static inline struct pcie_port *host_to_pcie(struct pci_controller *host) +{ + return container_of(host, struct pcie_port, pci); +} + +static void dw_pcie_set_local_bus_nr(struct pci_controller *host, int busno) +{ + struct pcie_port *pp = host_to_pcie(host); + + pp->root_bus_nr = busno; +} + +static struct pci_ops dw_pcie_ops; + +int __init dw_pcie_host_init(struct pcie_port *pp) +{ + struct device_node *np = pp->dev->device_node; + struct of_pci_range range; + struct of_pci_range_parser parser; + struct resource *cfg_res; + u32 val, na, ns; + const __be32 *addrp; + int index; + + /* Find the address cell size and the number of cells in order to get + * the untranslated address. + */ + of_property_read_u32(np, "#address-cells", &na); + ns = of_n_size_cells(np); + + cfg_res = dev_get_resource_by_name(pp->dev, IORESOURCE_MEM, "config"); + if (cfg_res) { + pp->cfg0_size = resource_size(cfg_res)/2; + pp->cfg1_size = resource_size(cfg_res)/2; + pp->cfg0_base = cfg_res->start; + pp->cfg1_base = cfg_res->start + pp->cfg0_size; + + /* Find the untranslated configuration space address */ + index = of_property_match_string(np, "reg-names", "config"); + addrp = of_get_address(np, index, NULL, NULL); + pp->cfg0_mod_base = of_read_number(addrp, ns); + pp->cfg1_mod_base = pp->cfg0_mod_base + pp->cfg0_size; + } else { + dev_err(pp->dev, "missing *config* reg space\n"); + } + + if (of_pci_range_parser_init(&parser, np)) { + dev_err(pp->dev, "missing ranges property\n"); + return -EINVAL; + } + + /* Get the I/O and memory ranges from DT */ + for_each_of_pci_range(&parser, &range) { + unsigned long restype = range.flags & IORESOURCE_TYPE_BITS; + + if (restype == IORESOURCE_IO) { + of_pci_range_to_resource(&range, np, &pp->io); + pp->io.name = "I/O"; + pp->io.start = range.pci_addr + global_io_offset; + pp->io.end = range.pci_addr + range.size + global_io_offset - 1; + pp->io_size = resource_size(&pp->io); + pp->io_bus_addr = range.pci_addr; + pp->io_base = range.cpu_addr; + + /* Find the untranslated IO space address */ + pp->io_mod_base = of_read_number(parser.range - + parser.np + na, ns); + } + if (restype == IORESOURCE_MEM) { + of_pci_range_to_resource(&range, np, &pp->mem); + pp->mem.name = "MEM"; + pp->mem_size = resource_size(&pp->mem); + pp->mem_bus_addr = range.pci_addr; + + /* Find the untranslated MEM space address */ + pp->mem_mod_base = of_read_number(parser.range - + parser.np + na, ns); + } + if (restype == 0) { + of_pci_range_to_resource(&range, np, &pp->cfg); + pp->cfg0_size = resource_size(&pp->cfg)/2; + pp->cfg1_size = resource_size(&pp->cfg)/2; + pp->cfg0_base = pp->cfg.start; + pp->cfg1_base = pp->cfg.start + pp->cfg0_size; + + /* Find the untranslated configuration space address */ + pp->cfg0_mod_base = of_read_number(parser.range - + parser.np + na, ns); + pp->cfg1_mod_base = pp->cfg0_mod_base + + pp->cfg0_size; + } + } + + if (!pp->dbi_base) + pp->dbi_base = (void __force *)pp->cfg.start; + + pp->mem_base = pp->mem.start; + + if (!pp->va_cfg0_base) + pp->va_cfg0_base = (void __force *)(u32)pp->cfg0_base; + + if (!pp->va_cfg1_base) + pp->va_cfg1_base = (void __force *)(u32)pp->cfg1_base; + + if (of_property_read_u32(np, "num-lanes", &pp->lanes)) { + dev_err(pp->dev, "Failed to parse the number of lanes\n"); + return -EINVAL; + } + + if (pp->ops->host_init) + pp->ops->host_init(pp); + + dw_pcie_wr_own_conf(pp, PCI_BASE_ADDRESS_0, 4, 0); + + /* program correct class for RC */ + dw_pcie_wr_own_conf(pp, PCI_CLASS_DEVICE, 2, PCI_CLASS_BRIDGE_PCI); + + dw_pcie_rd_own_conf(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, 4, &val); + val |= PORT_LOGIC_SPEED_CHANGE; + dw_pcie_wr_own_conf(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, 4, val); + + pp->pci.parent = pp->dev; + pp->pci.pci_ops = &dw_pcie_ops; + pp->pci.set_busno = dw_pcie_set_local_bus_nr; + pp->pci.mem_resource = &pp->mem; + pp->pci.io_resource = &pp->io; + + register_pci_controller(&pp->pci); + + return 0; +} + +static void dw_pcie_prog_viewport_cfg0(struct pcie_port *pp, u32 busdev) +{ + /* Program viewport 0 : OUTBOUND : CFG0 */ + dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX0, + PCIE_ATU_VIEWPORT); + dw_pcie_writel_rc(pp, pp->cfg0_mod_base, PCIE_ATU_LOWER_BASE); + dw_pcie_writel_rc(pp, (pp->cfg0_mod_base >> 32), PCIE_ATU_UPPER_BASE); + dw_pcie_writel_rc(pp, pp->cfg0_mod_base + pp->cfg0_size - 1, + PCIE_ATU_LIMIT); + dw_pcie_writel_rc(pp, busdev, PCIE_ATU_LOWER_TARGET); + dw_pcie_writel_rc(pp, 0, PCIE_ATU_UPPER_TARGET); + dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_CFG0, PCIE_ATU_CR1); + dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2); +} + +static void dw_pcie_prog_viewport_cfg1(struct pcie_port *pp, u32 busdev) +{ + /* Program viewport 1 : OUTBOUND : CFG1 */ + dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX1, + PCIE_ATU_VIEWPORT); + dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_CFG1, PCIE_ATU_CR1); + dw_pcie_writel_rc(pp, pp->cfg1_mod_base, PCIE_ATU_LOWER_BASE); + dw_pcie_writel_rc(pp, (pp->cfg1_mod_base >> 32), PCIE_ATU_UPPER_BASE); + dw_pcie_writel_rc(pp, pp->cfg1_mod_base + pp->cfg1_size - 1, + PCIE_ATU_LIMIT); + dw_pcie_writel_rc(pp, busdev, PCIE_ATU_LOWER_TARGET); + dw_pcie_writel_rc(pp, 0, PCIE_ATU_UPPER_TARGET); + dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2); +} + +static void dw_pcie_prog_viewport_mem_outbound(struct pcie_port *pp) +{ + /* Program viewport 0 : OUTBOUND : MEM */ + dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX0, + PCIE_ATU_VIEWPORT); + dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_MEM, PCIE_ATU_CR1); + dw_pcie_writel_rc(pp, pp->mem_mod_base, PCIE_ATU_LOWER_BASE); + dw_pcie_writel_rc(pp, (pp->mem_mod_base >> 32), PCIE_ATU_UPPER_BASE); + dw_pcie_writel_rc(pp, pp->mem_mod_base + pp->mem_size - 1, + PCIE_ATU_LIMIT); + dw_pcie_writel_rc(pp, pp->mem_bus_addr, PCIE_ATU_LOWER_TARGET); + dw_pcie_writel_rc(pp, upper_32_bits(pp->mem_bus_addr), + PCIE_ATU_UPPER_TARGET); + dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2); +} + +static void dw_pcie_prog_viewport_io_outbound(struct pcie_port *pp) +{ + /* Program viewport 1 : OUTBOUND : IO */ + dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX1, + PCIE_ATU_VIEWPORT); + dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_IO, PCIE_ATU_CR1); + dw_pcie_writel_rc(pp, pp->io_mod_base, PCIE_ATU_LOWER_BASE); + dw_pcie_writel_rc(pp, (pp->io_mod_base >> 32), PCIE_ATU_UPPER_BASE); + dw_pcie_writel_rc(pp, pp->io_mod_base + pp->io_size - 1, + PCIE_ATU_LIMIT); + dw_pcie_writel_rc(pp, pp->io_bus_addr, PCIE_ATU_LOWER_TARGET); + dw_pcie_writel_rc(pp, upper_32_bits(pp->io_bus_addr), + PCIE_ATU_UPPER_TARGET); + dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2); +} + +struct pci_bus *get_parent_bus(struct pci_bus *bus) +{ + struct pci_dev *bridge = container_of(bus->parent, struct pci_dev, dev); + + return bridge->bus; +} + +static int dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus, + u32 devfn, int where, int size, u32 *val) +{ + int ret = PCIBIOS_SUCCESSFUL; + u32 address, busdev; + + busdev = PCIE_ATU_BUS(bus->number) | PCIE_ATU_DEV(PCI_SLOT(devfn)) | + PCIE_ATU_FUNC(PCI_FUNC(devfn)); + address = where & ~0x3; + + if (get_parent_bus(bus)->number == pp->root_bus_nr) { + dw_pcie_prog_viewport_cfg0(pp, busdev); + ret = dw_pcie_cfg_read(pp->va_cfg0_base + address, where, size, + val); + dw_pcie_prog_viewport_mem_outbound(pp); + } else { + dw_pcie_prog_viewport_cfg1(pp, busdev); + ret = dw_pcie_cfg_read(pp->va_cfg1_base + address, where, size, + val); + dw_pcie_prog_viewport_io_outbound(pp); + } + + return ret; +} + +static int dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus, + u32 devfn, int where, int size, u32 val) +{ + int ret = PCIBIOS_SUCCESSFUL; + u32 address, busdev; + + busdev = PCIE_ATU_BUS(bus->number) | PCIE_ATU_DEV(PCI_SLOT(devfn)) | + PCIE_ATU_FUNC(PCI_FUNC(devfn)); + address = where & ~0x3; + + if (get_parent_bus(bus)->number == pp->root_bus_nr) { + dw_pcie_prog_viewport_cfg0(pp, busdev); + ret = dw_pcie_cfg_write(pp->va_cfg0_base + address, where, size, + val); + dw_pcie_prog_viewport_mem_outbound(pp); + } else { + dw_pcie_prog_viewport_cfg1(pp, busdev); + ret = dw_pcie_cfg_write(pp->va_cfg1_base + address, where, size, + val); + dw_pcie_prog_viewport_io_outbound(pp); + } + + return ret; +} + +static int dw_pcie_valid_config(struct pcie_port *pp, + struct pci_bus *bus, int dev) +{ + /* If there is no link, then there is no device */ + if (bus->number != pp->root_bus_nr) { + if (!dw_pcie_link_up(pp)) + return 0; + } + + /* access only one slot on each root port */ + if (bus->number == pp->root_bus_nr && dev > 0) + return 0; + + /* + * do not read more than one device on the bus directly attached + * to RC's (Virtual Bridge's) DS side. + */ + if (bus->primary == pp->root_bus_nr && dev > 0) + return 0; + + return 1; +} + +static int dw_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, + int size, u32 *val) +{ + struct pcie_port *pp = host_to_pcie(bus->host); + int ret; + + *val = 0xffffffff; + + if (dw_pcie_valid_config(pp, bus, PCI_SLOT(devfn)) == 0) + return PCIBIOS_DEVICE_NOT_FOUND; + + data_abort_mask(); + + if (bus->number != pp->root_bus_nr) + if (pp->ops->rd_other_conf) + ret = pp->ops->rd_other_conf(pp, bus, devfn, + where, size, val); + else + ret = dw_pcie_rd_other_conf(pp, bus, devfn, + where, size, val); + else + ret = dw_pcie_rd_own_conf(pp, where, size, val); + + if (data_abort_unmask()) + return PCIBIOS_DEVICE_NOT_FOUND; + + return ret; +} + +static int dw_pcie_wr_conf(struct pci_bus *bus, u32 devfn, + int where, int size, u32 val) +{ + struct pcie_port *pp = host_to_pcie(bus->host); + int ret; + + if (dw_pcie_valid_config(pp, bus, PCI_SLOT(devfn)) == 0) + return PCIBIOS_DEVICE_NOT_FOUND; + + data_abort_mask(); + + if (bus->number != pp->root_bus_nr) + if (pp->ops->wr_other_conf) + ret = pp->ops->wr_other_conf(pp, bus, devfn, + where, size, val); + else + ret = dw_pcie_wr_other_conf(pp, bus, devfn, + where, size, val); + else + ret = dw_pcie_wr_own_conf(pp, where, size, val); + + if (data_abort_unmask()) + return PCIBIOS_DEVICE_NOT_FOUND; + + return ret; +} + +static int dw_pcie_res_start(struct pci_bus *bus, resource_size_t res_addr) +{ + return res_addr; +} + +static struct pci_ops dw_pcie_ops = { + .read = dw_pcie_rd_conf, + .write = dw_pcie_wr_conf, + .res_start = dw_pcie_res_start, +}; + +void dw_pcie_setup_rc(struct pcie_port *pp) +{ + u32 val; + u32 membase; + u32 memlimit; + + /* set the number of lanes */ + dw_pcie_readl_rc(pp, PCIE_PORT_LINK_CONTROL, &val); + val &= ~PORT_LINK_MODE_MASK; + switch (pp->lanes) { + case 1: + val |= PORT_LINK_MODE_1_LANES; + break; + case 2: + val |= PORT_LINK_MODE_2_LANES; + break; + case 4: + val |= PORT_LINK_MODE_4_LANES; + break; + } + dw_pcie_writel_rc(pp, val, PCIE_PORT_LINK_CONTROL); + + /* set link width speed control register */ + dw_pcie_readl_rc(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, &val); + val &= ~PORT_LOGIC_LINK_WIDTH_MASK; + switch (pp->lanes) { + case 1: + val |= PORT_LOGIC_LINK_WIDTH_1_LANES; + break; + case 2: + val |= PORT_LOGIC_LINK_WIDTH_2_LANES; + break; + case 4: + val |= PORT_LOGIC_LINK_WIDTH_4_LANES; + break; + } + dw_pcie_writel_rc(pp, val, PCIE_LINK_WIDTH_SPEED_CONTROL); + + /* setup RC BARs */ + dw_pcie_writel_rc(pp, 0x00000004, PCI_BASE_ADDRESS_0); + dw_pcie_writel_rc(pp, 0x00000000, PCI_BASE_ADDRESS_1); + + /* setup bus numbers */ + dw_pcie_readl_rc(pp, PCI_PRIMARY_BUS, &val); + val &= 0xff000000; + val |= 0x00010100; + dw_pcie_writel_rc(pp, val, PCI_PRIMARY_BUS); + + /* setup memory base, memory limit */ + membase = ((u32)pp->mem_base & 0xfff00000) >> 16; + memlimit = (pp->mem_size + (u32)pp->mem_base) & 0xfff00000; + val = memlimit | membase; + dw_pcie_writel_rc(pp, val, PCI_MEMORY_BASE); + + /* setup command register */ + dw_pcie_readl_rc(pp, PCI_COMMAND, &val); + val &= 0xffff0000; + val |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY | + PCI_COMMAND_MASTER | PCI_COMMAND_SERR; + dw_pcie_writel_rc(pp, val, PCI_COMMAND); +} + +MODULE_AUTHOR("Jingoo Han "); +MODULE_DESCRIPTION("Designware PCIe host controller driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pci/pcie-designware.h b/drivers/pci/pcie-designware.h new file mode 100644 index 0000000..8d0330a --- /dev/null +++ b/drivers/pci/pcie-designware.h @@ -0,0 +1,71 @@ +/* + * Synopsys Designware PCIe host controller driver + * + * Copyright (C) 2013 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Jingoo Han + * + * 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. + */ + +#ifndef _PCIE_DESIGNWARE_H +#define _PCIE_DESIGNWARE_H + +struct pcie_port { + struct device_d *dev; + u8 root_bus_nr; + void __iomem *dbi_base; + u64 cfg0_base; + u64 cfg0_mod_base; + void __iomem *va_cfg0_base; + u32 cfg0_size; + u64 cfg1_base; + u64 cfg1_mod_base; + void __iomem *va_cfg1_base; + u32 cfg1_size; + u64 io_base; + u64 io_mod_base; + phys_addr_t io_bus_addr; + u32 io_size; + u64 mem_base; + u64 mem_mod_base; + phys_addr_t mem_bus_addr; + u32 mem_size; + struct resource cfg; + struct resource io; + struct resource mem; + struct resource busn; + int irq; + u32 lanes; + struct pcie_host_ops *ops; + struct pci_controller pci; +}; + +struct pcie_host_ops { + void (*readl_rc)(struct pcie_port *pp, + void __iomem *dbi_base, u32 *val); + void (*writel_rc)(struct pcie_port *pp, + u32 val, void __iomem *dbi_base); + int (*rd_own_conf)(struct pcie_port *pp, int where, int size, u32 *val); + int (*wr_own_conf)(struct pcie_port *pp, int where, int size, u32 val); + int (*rd_other_conf)(struct pcie_port *pp, struct pci_bus *bus, + unsigned int devfn, int where, int size, u32 *val); + int (*wr_other_conf)(struct pcie_port *pp, struct pci_bus *bus, + unsigned int devfn, int where, int size, u32 val); + int (*link_up)(struct pcie_port *pp); + void (*host_init)(struct pcie_port *pp); + void (*scan_bus)(struct pcie_port *pp); +}; + +int dw_pcie_cfg_read(void __iomem *addr, int where, int size, u32 *val); +int dw_pcie_cfg_write(void __iomem *addr, int where, int size, u32 val); +irqreturn_t dw_handle_msi_irq(struct pcie_port *pp); +void dw_pcie_msi_init(struct pcie_port *pp); +int dw_pcie_link_up(struct pcie_port *pp); +void dw_pcie_setup_rc(struct pcie_port *pp); +int dw_pcie_host_init(struct pcie_port *pp); + +#endif /* _PCIE_DESIGNWARE_H */ -- 2.1.4 _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox