From patchwork Tue Aug 12 16:41:35 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Liviu Dudau X-Patchwork-Id: 35299 Return-Path: X-Original-To: linaro@patches.linaro.org Delivered-To: linaro@patches.linaro.org Received: from mail-qg0-f72.google.com (mail-qg0-f72.google.com [209.85.192.72]) by ip-10-151-82-157.ec2.internal (Postfix) with ESMTPS id A6C26203C5 for ; Tue, 12 Aug 2014 16:42:29 +0000 (UTC) Received: by mail-qg0-f72.google.com with SMTP id q107sf29078784qgd.11 for ; Tue, 12 Aug 2014 09:42:29 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:delivered-to:from:to:cc:subject :date:message-id:sender:precedence:list-id:x-original-sender :x-original-authentication-results:mailing-list:list-post:list-help :list-archive:list-unsubscribe; bh=er2bxtnSWjjG7clm2RmUqAXrXZbJNNhQC46UPU6vALc=; b=Iz5uak52sYLHB0jYCdJqRMIDdK5pgk01a8lESh4drPvc8r7JMJ+xIv6OvtYZZtiMm3 8W+1dnnrxte7n/gFFDIWuCWK7+sBjb3WAUKRu1IsgqP0RIuWCAG/CUdstGpS0gWevu4e ukdv9uHV0jO3g8asNTLtwFCcs/TxDMuTX8jxflgJIOtez+a/lxgFiRJa7wi8WMd6uDof XZT0jx8vXZ3F5S3K2LDFVHHNGcAKGcdac3rOtZy6aSuXUg+6hFS61cQKI99wBaQ6Nskp 9BvrY5whC9w2aOp4la0w1ciq8X/GIruyGaLwwTm5QH7kDIpYvI3UkASfEttRGat7L+gb 6SJw== X-Gm-Message-State: ALoCoQnvIHAA+EW0Cn+A/KdEHgJESr/LQ7SJUtJpRP7NaJkj8Ci6DHRynptqaMs3vXlsFi0CetHJ X-Received: by 10.236.157.193 with SMTP id o41mr17658652yhk.6.1407861749540; Tue, 12 Aug 2014 09:42:29 -0700 (PDT) MIME-Version: 1.0 X-BeenThere: patchwork-forward@linaro.org Received: by 10.140.97.134 with SMTP id m6ls277900qge.8.gmail; Tue, 12 Aug 2014 09:42:29 -0700 (PDT) X-Received: by 10.52.146.176 with SMTP id td16mr1641978vdb.81.1407861749424; Tue, 12 Aug 2014 09:42:29 -0700 (PDT) Received: from mail-vc0-f177.google.com (mail-vc0-f177.google.com [209.85.220.177]) by mx.google.com with ESMTPS id sf9si8551113vcb.80.2014.08.12.09.42.29 for (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Tue, 12 Aug 2014 09:42:29 -0700 (PDT) Received-SPF: pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.220.177 as permitted sender) client-ip=209.85.220.177; Received: by mail-vc0-f177.google.com with SMTP id hy4so13633433vcb.36 for ; Tue, 12 Aug 2014 09:42:29 -0700 (PDT) X-Received: by 10.221.5.137 with SMTP id og9mr5193704vcb.18.1407861749315; Tue, 12 Aug 2014 09:42:29 -0700 (PDT) X-Forwarded-To: patchwork-forward@linaro.org X-Forwarded-For: patch@linaro.org patchwork-forward@linaro.org Delivered-To: patch@linaro.org Received: by 10.221.37.5 with SMTP id tc5csp263733vcb; Tue, 12 Aug 2014 09:42:28 -0700 (PDT) X-Received: by 10.70.134.72 with SMTP id pi8mr1701176pdb.167.1407861748105; Tue, 12 Aug 2014 09:42:28 -0700 (PDT) Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id kr6si16458631pab.60.2014.08.12.09.42.27 for ; Tue, 12 Aug 2014 09:42:28 -0700 (PDT) Received-SPF: none (google.com: linux-kernel-owner@vger.kernel.org does not designate permitted sender hosts) client-ip=209.132.180.67; Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753871AbaHLQm0 (ORCPT + 26 others); Tue, 12 Aug 2014 12:42:26 -0400 Received: from fw-tnat.cambridge.arm.com ([217.140.96.21]:50835 "EHLO cam-smtp0.cambridge.arm.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1753213AbaHLQmY (ORCPT ); Tue, 12 Aug 2014 12:42:24 -0400 Received: from e106497-lin.cambridge.arm.com (e106497-lin.cambridge.arm.com [10.1.195.53]) by cam-smtp0.cambridge.arm.com (8.13.8/8.13.8) with ESMTP id s7CGfZwD013360; Tue, 12 Aug 2014 17:41:35 +0100 From: Liviu Dudau To: Bjorn Helgaas , Catalin Marinas , Will Deacon , Arnd Bergmann , Jingoo Han , Kukjin Kim , Suravee Suthikulanit , linux-pci Cc: LKML , LAKML Subject: [PATCH] drivers: pci: convert generic host controller to DT host bridge creation API Date: Tue, 12 Aug 2014 17:41:35 +0100 Message-Id: <1407861695-25549-1-git-send-email-Liviu.Dudau@arm.com> X-Mailer: git-send-email 2.0.4 Sender: linux-kernel-owner@vger.kernel.org Precedence: list List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-Removed-Original-Auth: Dkim didn't pass. X-Original-Sender: liviu.dudau@arm.com X-Original-Authentication-Results: mx.google.com; spf=pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.220.177 as permitted sender) smtp.mail=patch+caf_=patchwork-forward=linaro.org@linaro.org Mailing-list: list patchwork-forward@linaro.org; contact patchwork-forward+owners@linaro.org X-Google-Group-Id: 836684582541 List-Post: , List-Help: , List-Archive: List-Unsubscribe: , From: Lorenzo Pieralisi In order to consolidate DT configuration for PCI host controllers in the kernel, a new API was introduced that allows creating a host bridge and its PCI bus from DT, removing duplicated code present in the majority of pci host driver implementations. This patch converts the existing PCI generic host controller driver to the new API. Most of the code parsing ranges and creating resources is now delegated to of_create_pci_host_bridge() API which also triggers a scan of the root bus. The setup hook passed by the host controller code to the generic DT layer completes the initialization by performing resource filtering (ie it checks that at least one non-prefetchable memory resource is present), remapping IO and configuration regions and initializing the required PCI flags. Cc: Will Deacon Signed-off-by: Rob Herring [ported to v9, fixed some bugs and refactored the code] Signed-off-by: Lorenzo Pieralisi Reviewed-by: Liviu Dudau --- drivers/pci/host/pci-host-generic.c | 244 ++++++++++-------------------------- 1 file changed, 66 insertions(+), 178 deletions(-) diff --git a/drivers/pci/host/pci-host-generic.c b/drivers/pci/host/pci-host-generic.c index 44fe6aa..24b519f 100644 --- a/drivers/pci/host/pci-host-generic.c +++ b/drivers/pci/host/pci-host-generic.c @@ -32,25 +32,21 @@ struct gen_pci_cfg_bus_ops { struct gen_pci_cfg_windows { struct resource res; - struct resource bus_range; void __iomem **win; const struct gen_pci_cfg_bus_ops *ops; }; struct gen_pci { - struct pci_host_bridge host; struct gen_pci_cfg_windows cfg; - struct list_head resources; }; static void __iomem *gen_pci_map_cfg_bus_cam(struct pci_bus *bus, unsigned int devfn, int where) { - struct pci_sys_data *sys = bus->sysdata; - struct gen_pci *pci = sys->private_data; - resource_size_t idx = bus->number - pci->cfg.bus_range.start; + struct gen_pci *pci = bus->sysdata; + resource_size_t idx = bus->number - bus->busn_res.start; return pci->cfg.win[idx] + ((devfn << 8) | where); } @@ -64,9 +60,8 @@ static void __iomem *gen_pci_map_cfg_bus_ecam(struct pci_bus *bus, unsigned int devfn, int where) { - struct pci_sys_data *sys = bus->sysdata; - struct gen_pci *pci = sys->private_data; - resource_size_t idx = bus->number - pci->cfg.bus_range.start; + struct gen_pci *pci = bus->sysdata; + resource_size_t idx = bus->number - bus->busn_res.start; return pci->cfg.win[idx] + ((devfn << 12) | where); } @@ -80,8 +75,7 @@ static int gen_pci_config_read(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 *val) { void __iomem *addr; - struct pci_sys_data *sys = bus->sysdata; - struct gen_pci *pci = sys->private_data; + struct gen_pci *pci = bus->sysdata; addr = pci->cfg.ops->map_bus(bus, devfn, where); @@ -103,8 +97,7 @@ static int gen_pci_config_write(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 val) { void __iomem *addr; - struct pci_sys_data *sys = bus->sysdata; - struct gen_pci *pci = sys->private_data; + struct gen_pci *pci = bus->sysdata; addr = pci->cfg.ops->map_bus(bus, devfn, where); @@ -138,162 +131,40 @@ static const struct of_device_id gen_pci_of_match[] = { }; MODULE_DEVICE_TABLE(of, gen_pci_of_match); -static int gen_pci_calc_io_offset(struct device *dev, - struct of_pci_range *range, - struct resource *res, - resource_size_t *offset) -{ - static atomic_t wins = ATOMIC_INIT(0); - int err, idx, max_win; - unsigned int window; - - if (!PAGE_ALIGNED(range->cpu_addr)) - return -EINVAL; - - max_win = (IO_SPACE_LIMIT + 1) / SZ_64K; - idx = atomic_inc_return(&wins); - if (idx > max_win) - return -ENOSPC; - - window = (idx - 1) * SZ_64K; - err = pci_ioremap_io(window, range->cpu_addr); - if (err) - return err; - - of_pci_range_to_resource(range, dev->of_node, res); - res->start = window; - res->end = res->start + range->size - 1; - *offset = window - range->pci_addr; - return 0; -} - -static int gen_pci_calc_mem_offset(struct device *dev, - struct of_pci_range *range, - struct resource *res, - resource_size_t *offset) -{ - of_pci_range_to_resource(range, dev->of_node, res); - *offset = range->cpu_addr - range->pci_addr; - return 0; -} - -static void gen_pci_release_of_pci_ranges(struct gen_pci *pci) -{ - struct pci_host_bridge_window *win; - - list_for_each_entry(win, &pci->resources, list) - release_resource(win->res); - - pci_free_resource_list(&pci->resources); -} - -static int gen_pci_parse_request_of_pci_ranges(struct gen_pci *pci) -{ - struct of_pci_range range; - struct of_pci_range_parser parser; - int err, res_valid = 0; - struct device *dev = pci->host.dev.parent; - struct device_node *np = dev->of_node; - - if (of_pci_range_parser_init(&parser, np)) { - dev_err(dev, "missing \"ranges\" property\n"); - return -EINVAL; - } - - for_each_of_pci_range(&parser, &range) { - struct resource *parent, *res; - resource_size_t offset; - u32 restype = range.flags & IORESOURCE_TYPE_BITS; - - res = devm_kmalloc(dev, sizeof(*res), GFP_KERNEL); - if (!res) { - err = -ENOMEM; - goto out_release_res; - } - - switch (restype) { - case IORESOURCE_IO: - parent = &ioport_resource; - err = gen_pci_calc_io_offset(dev, &range, res, &offset); - break; - case IORESOURCE_MEM: - parent = &iomem_resource; - err = gen_pci_calc_mem_offset(dev, &range, res, &offset); - res_valid |= !(res->flags & IORESOURCE_PREFETCH || err); - break; - default: - err = -EINVAL; - continue; - } - - if (err) { - dev_warn(dev, - "error %d: failed to add resource [type 0x%x, %lld bytes]\n", - err, restype, range.size); - continue; - } - - err = request_resource(parent, res); - if (err) - goto out_release_res; - - pci_add_resource_offset(&pci->resources, res, offset); - } - - if (!res_valid) { - dev_err(dev, "non-prefetchable memory resource required\n"); - err = -EINVAL; - goto out_release_res; - } - - return 0; - -out_release_res: - gen_pci_release_of_pci_ranges(pci); - return err; -} - -static int gen_pci_parse_map_cfg_windows(struct gen_pci *pci) +static int gen_pci_parse_map_cfg_windows(struct pci_host_bridge *host) { int err; u8 bus_max; resource_size_t busn; - struct resource *bus_range; - struct device *dev = pci->host.dev.parent; + struct pci_bus *bus = host->bus; + struct gen_pci *pci = bus->sysdata; + struct resource *bus_range = &bus->busn_res; + struct device *dev = host->dev.parent; struct device_node *np = dev->of_node; - if (of_pci_parse_bus_range(np, &pci->cfg.bus_range)) - pci->cfg.bus_range = (struct resource) { - .name = np->name, - .start = 0, - .end = 0xff, - .flags = IORESOURCE_BUS, - }; - err = of_address_to_resource(np, 0, &pci->cfg.res); if (err) { dev_err(dev, "missing \"reg\" property\n"); return err; } - pci->cfg.win = devm_kcalloc(dev, resource_size(&pci->cfg.bus_range), + /* Limit the bus-range to fit within reg */ + bus_max = bus_range->start + + (resource_size(&pci->cfg.res) >> pci->cfg.ops->bus_shift) - 1; + pci_bus_update_busn_res_end(bus, min_t(resource_size_t, + bus_range->end, bus_max)); + + pci->cfg.win = devm_kcalloc(dev, resource_size(bus_range), sizeof(*pci->cfg.win), GFP_KERNEL); if (!pci->cfg.win) return -ENOMEM; - /* Limit the bus-range to fit within reg */ - bus_max = pci->cfg.bus_range.start + - (resource_size(&pci->cfg.res) >> pci->cfg.ops->bus_shift) - 1; - pci->cfg.bus_range.end = min_t(resource_size_t, pci->cfg.bus_range.end, - bus_max); - /* Map our Configuration Space windows */ if (!devm_request_mem_region(dev, pci->cfg.res.start, resource_size(&pci->cfg.res), "Configuration Space")) return -ENOMEM; - bus_range = &pci->cfg.bus_range; for (busn = bus_range->start; busn <= bus_range->end; ++busn) { u32 idx = busn - bus_range->start; u32 sz = 1 << pci->cfg.ops->bus_shift; @@ -305,34 +176,66 @@ static int gen_pci_parse_map_cfg_windows(struct gen_pci *pci) return -ENOMEM; } - /* Register bus resource */ - pci_add_resource(&pci->resources, bus_range); return 0; } -static int gen_pci_setup(int nr, struct pci_sys_data *sys) +static int gen_pci_setup(struct pci_host_bridge *host, + resource_size_t io_cpuaddr) { - struct gen_pci *pci = sys->private_data; - list_splice_init(&pci->resources, &sys->resources); - return 1; + int err; + struct pci_host_bridge_window *window; + u32 restype, prefetchable; + struct device *dev = host->dev.parent; + struct resource *iores = NULL; + bool res_valid = false; + + list_for_each_entry(window, &host->windows, list) { + restype = window->res->flags & IORESOURCE_TYPE_BITS; + prefetchable = window->res->flags & IORESOURCE_PREFETCH; + + /* + * Require at least one non-prefetchable MEM resource + */ + if ((restype == IORESOURCE_MEM) && !prefetchable) + res_valid = true; + + if (restype == IORESOURCE_IO) + iores = window->res; + } + + if (!res_valid) { + dev_err(dev, "non-prefetchable memory resource required\n"); + return -EINVAL; + } + + if (iores) { + if (!PAGE_ALIGNED(io_cpuaddr)) + return -EINVAL; + + err = pci_remap_iospace(iores, io_cpuaddr); + if (err) + return err; + } + + /* Parse and map our Configuration Space windows */ + err = gen_pci_parse_map_cfg_windows(host); + if (err) + return err; + + pci_add_flags(PCI_ENABLE_PROC_DOMAINS); + pci_add_flags(PCI_REASSIGN_ALL_BUS | PCI_REASSIGN_ALL_RSRC); + + return 0; } static int gen_pci_probe(struct platform_device *pdev) { - int err; const char *type; const struct of_device_id *of_id; const int *prop; struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; struct gen_pci *pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); - struct hw_pci hw = { - .nr_controllers = 1, - .private_data = (void **)&pci, - .setup = gen_pci_setup, - .map_irq = of_irq_parse_and_map_pci, - .ops = &gen_pci_ops, - }; if (!pci) return -ENOMEM; @@ -353,24 +256,9 @@ static int gen_pci_probe(struct platform_device *pdev) of_id = of_match_node(gen_pci_of_match, np); pci->cfg.ops = of_id->data; - pci->host.dev.parent = dev; - INIT_LIST_HEAD(&pci->host.windows); - INIT_LIST_HEAD(&pci->resources); - /* Parse our PCI ranges and request their resources */ - err = gen_pci_parse_request_of_pci_ranges(pci); - if (err) - return err; - - /* Parse and map our Configuration Space windows */ - err = gen_pci_parse_map_cfg_windows(pci); - if (err) { - gen_pci_release_of_pci_ranges(pci); - return err; - } - - pci_common_init_dev(dev, &hw); - return 0; + return of_create_pci_host_bridge(dev, 0, 0xff, &gen_pci_ops, + gen_pci_setup, pci); } static struct platform_driver gen_pci_driver = {