diff options
Diffstat (limited to 'drivers/pci/host/pci-ftpci100.c')
-rw-r--r-- | drivers/pci/host/pci-ftpci100.c | 143 |
1 files changed, 107 insertions, 36 deletions
diff --git a/drivers/pci/host/pci-ftpci100.c b/drivers/pci/host/pci-ftpci100.c index d26501c4145a..5162dffc102b 100644 --- a/drivers/pci/host/pci-ftpci100.c +++ b/drivers/pci/host/pci-ftpci100.c @@ -25,6 +25,7 @@ #include <linux/irqchip/chained_irq.h> #include <linux/bitops.h> #include <linux/irq.h> +#include <linux/clk.h> /* * Special configuration registers directly in the first few words @@ -37,6 +38,7 @@ #define PCI_CONFIG 0x28 /* PCI configuration command register */ #define PCI_DATA 0x2C +#define FARADAY_PCI_STATUS_CMD 0x04 /* Status and command */ #define FARADAY_PCI_PMC 0x40 /* Power management control */ #define FARADAY_PCI_PMCSR 0x44 /* Power management status */ #define FARADAY_PCI_CTRL1 0x48 /* Control register 1 */ @@ -45,6 +47,8 @@ #define FARADAY_PCI_MEM2_BASE_SIZE 0x54 /* Memory base and size #2 */ #define FARADAY_PCI_MEM3_BASE_SIZE 0x58 /* Memory base and size #3 */ +#define PCI_STATUS_66MHZ_CAPABLE BIT(21) + /* Bits 31..28 gives INTD..INTA status */ #define PCI_CTRL2_INTSTS_SHIFT 28 #define PCI_CTRL2_INTMASK_CMDERR BIT(27) @@ -117,6 +121,7 @@ struct faraday_pci { void __iomem *base; struct irq_domain *irqdomain; struct pci_bus *bus; + struct clk *bus_clk; }; static int faraday_res_to_memcfg(resource_size_t mem_base, @@ -178,12 +183,11 @@ static int faraday_res_to_memcfg(resource_size_t mem_base, return 0; } -static int faraday_pci_read_config(struct pci_bus *bus, unsigned int fn, - int config, int size, u32 *value) +static int faraday_raw_pci_read_config(struct faraday_pci *p, int bus_number, + unsigned int fn, int config, int size, + u32 *value) { - struct faraday_pci *p = bus->sysdata; - - writel(PCI_CONF_BUS(bus->number) | + writel(PCI_CONF_BUS(bus_number) | PCI_CONF_DEVICE(PCI_SLOT(fn)) | PCI_CONF_FUNCTION(PCI_FUNC(fn)) | PCI_CONF_WHERE(config) | @@ -197,24 +201,28 @@ static int faraday_pci_read_config(struct pci_bus *bus, unsigned int fn, else if (size == 2) *value = (*value >> (8 * (config & 3))) & 0xFFFF; + return PCIBIOS_SUCCESSFUL; +} + +static int faraday_pci_read_config(struct pci_bus *bus, unsigned int fn, + int config, int size, u32 *value) +{ + struct faraday_pci *p = bus->sysdata; + dev_dbg(&bus->dev, "[read] slt: %.2d, fnc: %d, cnf: 0x%.2X, val (%d bytes): 0x%.8X\n", PCI_SLOT(fn), PCI_FUNC(fn), config, size, *value); - return PCIBIOS_SUCCESSFUL; + return faraday_raw_pci_read_config(p, bus->number, fn, config, size, value); } -static int faraday_pci_write_config(struct pci_bus *bus, unsigned int fn, - int config, int size, u32 value) +static int faraday_raw_pci_write_config(struct faraday_pci *p, int bus_number, + unsigned int fn, int config, int size, + u32 value) { - struct faraday_pci *p = bus->sysdata; int ret = PCIBIOS_SUCCESSFUL; - dev_dbg(&bus->dev, - "[write] slt: %.2d, fnc: %d, cnf: 0x%.2X, val (%d bytes): 0x%.8X\n", - PCI_SLOT(fn), PCI_FUNC(fn), config, size, value); - - writel(PCI_CONF_BUS(bus->number) | + writel(PCI_CONF_BUS(bus_number) | PCI_CONF_DEVICE(PCI_SLOT(fn)) | PCI_CONF_FUNCTION(PCI_FUNC(fn)) | PCI_CONF_WHERE(config) | @@ -238,6 +246,19 @@ static int faraday_pci_write_config(struct pci_bus *bus, unsigned int fn, return ret; } +static int faraday_pci_write_config(struct pci_bus *bus, unsigned int fn, + int config, int size, u32 value) +{ + struct faraday_pci *p = bus->sysdata; + + dev_dbg(&bus->dev, + "[write] slt: %.2d, fnc: %d, cnf: 0x%.2X, val (%d bytes): 0x%.8X\n", + PCI_SLOT(fn), PCI_FUNC(fn), config, size, value); + + return faraday_raw_pci_write_config(p, bus->number, fn, config, size, + value); +} + static struct pci_ops faraday_pci_ops = { .read = faraday_pci_read_config, .write = faraday_pci_write_config, @@ -248,10 +269,10 @@ static void faraday_pci_ack_irq(struct irq_data *d) struct faraday_pci *p = irq_data_get_irq_chip_data(d); unsigned int reg; - faraday_pci_read_config(p->bus, 0, FARADAY_PCI_CTRL2, 4, ®); + faraday_raw_pci_read_config(p, 0, 0, FARADAY_PCI_CTRL2, 4, ®); reg &= ~(0xF << PCI_CTRL2_INTSTS_SHIFT); reg |= BIT(irqd_to_hwirq(d) + PCI_CTRL2_INTSTS_SHIFT); - faraday_pci_write_config(p->bus, 0, FARADAY_PCI_CTRL2, 4, reg); + faraday_raw_pci_write_config(p, 0, 0, FARADAY_PCI_CTRL2, 4, reg); } static void faraday_pci_mask_irq(struct irq_data *d) @@ -259,10 +280,10 @@ static void faraday_pci_mask_irq(struct irq_data *d) struct faraday_pci *p = irq_data_get_irq_chip_data(d); unsigned int reg; - faraday_pci_read_config(p->bus, 0, FARADAY_PCI_CTRL2, 4, ®); + faraday_raw_pci_read_config(p, 0, 0, FARADAY_PCI_CTRL2, 4, ®); reg &= ~((0xF << PCI_CTRL2_INTSTS_SHIFT) | BIT(irqd_to_hwirq(d) + PCI_CTRL2_INTMASK_SHIFT)); - faraday_pci_write_config(p->bus, 0, FARADAY_PCI_CTRL2, 4, reg); + faraday_raw_pci_write_config(p, 0, 0, FARADAY_PCI_CTRL2, 4, reg); } static void faraday_pci_unmask_irq(struct irq_data *d) @@ -270,10 +291,10 @@ static void faraday_pci_unmask_irq(struct irq_data *d) struct faraday_pci *p = irq_data_get_irq_chip_data(d); unsigned int reg; - faraday_pci_read_config(p->bus, 0, FARADAY_PCI_CTRL2, 4, ®); + faraday_raw_pci_read_config(p, 0, 0, FARADAY_PCI_CTRL2, 4, ®); reg &= ~(0xF << PCI_CTRL2_INTSTS_SHIFT); reg |= BIT(irqd_to_hwirq(d) + PCI_CTRL2_INTMASK_SHIFT); - faraday_pci_write_config(p->bus, 0, FARADAY_PCI_CTRL2, 4, reg); + faraday_raw_pci_write_config(p, 0, 0, FARADAY_PCI_CTRL2, 4, reg); } static void faraday_pci_irq_handler(struct irq_desc *desc) @@ -282,7 +303,7 @@ static void faraday_pci_irq_handler(struct irq_desc *desc) struct irq_chip *irqchip = irq_desc_get_chip(desc); unsigned int irq_stat, reg, i; - faraday_pci_read_config(p->bus, 0, FARADAY_PCI_CTRL2, 4, ®); + faraday_raw_pci_read_config(p, 0, 0, FARADAY_PCI_CTRL2, 4, ®); irq_stat = reg >> PCI_CTRL2_INTSTS_SHIFT; chained_irq_enter(irqchip, desc); @@ -403,8 +424,8 @@ static int faraday_pci_parse_map_dma_ranges(struct faraday_pci *p, dev_info(dev, "DMA MEM%d BASE: 0x%016llx -> 0x%016llx config %08x\n", i + 1, range.pci_addr, end, val); if (i <= 2) { - faraday_pci_write_config(p->bus, 0, confreg[i], - 4, val); + faraday_raw_pci_write_config(p, 0, 0, confreg[i], + 4, val); } else { dev_err(dev, "ignore extraneous dma-range %d\n", i); break; @@ -428,11 +449,14 @@ static int faraday_pci_probe(struct platform_device *pdev) struct resource *mem; struct resource *io; struct pci_host_bridge *host; + struct clk *clk; + unsigned char max_bus_speed = PCI_SPEED_33MHz; + unsigned char cur_bus_speed = PCI_SPEED_33MHz; int ret; u32 val; LIST_HEAD(res); - host = pci_alloc_host_bridge(sizeof(*p)); + host = devm_pci_alloc_host_bridge(dev, sizeof(*p)); if (!host) return -ENOMEM; @@ -440,10 +464,30 @@ static int faraday_pci_probe(struct platform_device *pdev) host->ops = &faraday_pci_ops; host->busnr = 0; host->msi = NULL; + host->map_irq = of_irq_parse_and_map_pci; + host->swizzle_irq = pci_common_swizzle; p = pci_host_bridge_priv(host); host->sysdata = p; p->dev = dev; + /* Retrieve and enable optional clocks */ + clk = devm_clk_get(dev, "PCLK"); + if (IS_ERR(clk)) + return PTR_ERR(clk); + ret = clk_prepare_enable(clk); + if (ret) { + dev_err(dev, "could not prepare PCLK\n"); + return ret; + } + p->bus_clk = devm_clk_get(dev, "PCICLK"); + if (IS_ERR(p->bus_clk)) + return PTR_ERR(clk); + ret = clk_prepare_enable(p->bus_clk); + if (ret) { + dev_err(dev, "could not prepare PCICLK\n"); + return ret; + } + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); p->base = devm_ioremap_resource(dev, regs); if (IS_ERR(p->base)) @@ -496,17 +540,8 @@ static int faraday_pci_probe(struct platform_device *pdev) val |= PCI_COMMAND_MEMORY; val |= PCI_COMMAND_MASTER; writel(val, p->base + PCI_CTRL); - - list_splice_init(&res, &host->windows); - ret = pci_register_host_bridge(host); - if (ret) { - dev_err(dev, "failed to register host: %d\n", ret); - return ret; - } - p->bus = host->bus; - /* Mask and clear all interrupts */ - faraday_pci_write_config(p->bus, 0, FARADAY_PCI_CTRL2 + 2, 2, 0xF000); + faraday_raw_pci_write_config(p, 0, 0, FARADAY_PCI_CTRL2 + 2, 2, 0xF000); if (variant->cascaded_irq) { ret = faraday_pci_setup_cascaded_irq(p); if (ret) { @@ -515,12 +550,48 @@ static int faraday_pci_probe(struct platform_device *pdev) } } + /* Check bus clock if we can gear up to 66 MHz */ + if (!IS_ERR(p->bus_clk)) { + unsigned long rate; + u32 val; + + faraday_raw_pci_read_config(p, 0, 0, + FARADAY_PCI_STATUS_CMD, 4, &val); + rate = clk_get_rate(p->bus_clk); + + if ((rate == 33000000) && (val & PCI_STATUS_66MHZ_CAPABLE)) { + dev_info(dev, "33MHz bus is 66MHz capable\n"); + max_bus_speed = PCI_SPEED_66MHz; + ret = clk_set_rate(p->bus_clk, 66000000); + if (ret) + dev_err(dev, "failed to set bus clock\n"); + } else { + dev_info(dev, "33MHz only bus\n"); + max_bus_speed = PCI_SPEED_33MHz; + } + + /* Bumping the clock may fail so read back the rate */ + rate = clk_get_rate(p->bus_clk); + if (rate == 33000000) + cur_bus_speed = PCI_SPEED_33MHz; + if (rate == 66000000) + cur_bus_speed = PCI_SPEED_66MHz; + } + ret = faraday_pci_parse_map_dma_ranges(p, dev->of_node); if (ret) return ret; - pci_scan_child_bus(p->bus); - pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci); + list_splice_init(&res, &host->windows); + ret = pci_scan_root_bus_bridge(host); + if (ret) { + dev_err(dev, "failed to scan host: %d\n", ret); + return ret; + } + p->bus = host->bus; + p->bus->max_bus_speed = max_bus_speed; + p->bus->cur_bus_speed = cur_bus_speed; + pci_bus_assign_resources(p->bus); pci_bus_add_devices(p->bus); pci_free_resource_list(&res); |