PCI: generic, thunder: Use generic ECAM API
Use functions provided by drivers/pci/ecam.h for mapping the config space in drivers/pci/host/pci-host-common.c, and update its users to use 'struct pci_config_window' and 'struct pci_ecam_ops'. The changes are mostly to use 'struct pci_config_window' in place of 'struct gen_pci'. Some of the fields of gen_pci were only used temporarily and can be eliminated by using local variables or function arguments, these are not carried over to struct pci_config_window. pci-thunder-ecam.c and pci-thunder-pem.c are the only users of the pci_host_common_probe function and the gen_pci structure; these have been updated to use the new API as well. The patch does not introduce any functional changes other than a very minor one: with the new code, on 64-bit platforms, we do just a single ioremap for the whole config space. Signed-off-by: Jayachandran C <jchandra@broadcom.com> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
This commit is contained in:
parent
35ff9477d8
commit
1958e7173d
7 changed files with 113 additions and 237 deletions
|
@ -59,4 +59,9 @@ void __iomem *pci_ecam_map_bus(struct pci_bus *bus, unsigned int devfn,
|
||||||
/* default ECAM ops */
|
/* default ECAM ops */
|
||||||
extern struct pci_ecam_ops pci_generic_ecam_ops;
|
extern struct pci_ecam_ops pci_generic_ecam_ops;
|
||||||
|
|
||||||
|
#ifdef CONFIG_PCI_HOST_GENERIC
|
||||||
|
/* for DT-based PCI controllers that support ECAM */
|
||||||
|
int pci_host_common_probe(struct platform_device *pdev,
|
||||||
|
struct pci_ecam_ops *ops);
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -77,6 +77,7 @@ config PCI_RCAR_GEN2_PCIE
|
||||||
|
|
||||||
config PCI_HOST_COMMON
|
config PCI_HOST_COMMON
|
||||||
bool
|
bool
|
||||||
|
select PCI_ECAM
|
||||||
|
|
||||||
config PCI_HOST_GENERIC
|
config PCI_HOST_GENERIC
|
||||||
bool "Generic PCI host controller"
|
bool "Generic PCI host controller"
|
||||||
|
|
|
@ -22,27 +22,21 @@
|
||||||
#include <linux/of_pci.h>
|
#include <linux/of_pci.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
|
||||||
#include "pci-host-common.h"
|
#include "../ecam.h"
|
||||||
|
|
||||||
static void gen_pci_release_of_pci_ranges(struct gen_pci *pci)
|
static int gen_pci_parse_request_of_pci_ranges(struct device *dev,
|
||||||
{
|
struct list_head *resources, struct resource **bus_range)
|
||||||
pci_free_resource_list(&pci->resources);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int gen_pci_parse_request_of_pci_ranges(struct gen_pci *pci)
|
|
||||||
{
|
{
|
||||||
int err, res_valid = 0;
|
int err, res_valid = 0;
|
||||||
struct device *dev = pci->host.dev.parent;
|
|
||||||
struct device_node *np = dev->of_node;
|
struct device_node *np = dev->of_node;
|
||||||
resource_size_t iobase;
|
resource_size_t iobase;
|
||||||
struct resource_entry *win;
|
struct resource_entry *win;
|
||||||
|
|
||||||
err = of_pci_get_host_bridge_resources(np, 0, 0xff, &pci->resources,
|
err = of_pci_get_host_bridge_resources(np, 0, 0xff, resources, &iobase);
|
||||||
&iobase);
|
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
resource_list_for_each_entry(win, &pci->resources) {
|
resource_list_for_each_entry(win, resources) {
|
||||||
struct resource *parent, *res = win->res;
|
struct resource *parent, *res = win->res;
|
||||||
|
|
||||||
switch (resource_type(res)) {
|
switch (resource_type(res)) {
|
||||||
|
@ -60,7 +54,7 @@ static int gen_pci_parse_request_of_pci_ranges(struct gen_pci *pci)
|
||||||
res_valid |= !(res->flags & IORESOURCE_PREFETCH);
|
res_valid |= !(res->flags & IORESOURCE_PREFETCH);
|
||||||
break;
|
break;
|
||||||
case IORESOURCE_BUS:
|
case IORESOURCE_BUS:
|
||||||
pci->cfg.bus_range = res;
|
*bus_range = res;
|
||||||
default:
|
default:
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -79,65 +73,60 @@ static int gen_pci_parse_request_of_pci_ranges(struct gen_pci *pci)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
out_release_res:
|
out_release_res:
|
||||||
gen_pci_release_of_pci_ranges(pci);
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int gen_pci_parse_map_cfg_windows(struct gen_pci *pci)
|
static void gen_pci_unmap_cfg(void *ptr)
|
||||||
|
{
|
||||||
|
pci_ecam_free((struct pci_config_window *)ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct pci_config_window *gen_pci_init(struct device *dev,
|
||||||
|
struct list_head *resources, struct pci_ecam_ops *ops)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
u8 bus_max;
|
struct resource cfgres;
|
||||||
resource_size_t busn;
|
struct resource *bus_range = NULL;
|
||||||
struct resource *bus_range;
|
struct pci_config_window *cfg;
|
||||||
struct device *dev = pci->host.dev.parent;
|
|
||||||
struct device_node *np = dev->of_node;
|
|
||||||
u32 sz = 1 << pci->cfg.ops->bus_shift;
|
|
||||||
|
|
||||||
err = of_address_to_resource(np, 0, &pci->cfg.res);
|
/* Parse our PCI ranges and request their resources */
|
||||||
|
err = gen_pci_parse_request_of_pci_ranges(dev, resources, &bus_range);
|
||||||
|
if (err)
|
||||||
|
goto err_out;
|
||||||
|
|
||||||
|
err = of_address_to_resource(dev->of_node, 0, &cfgres);
|
||||||
if (err) {
|
if (err) {
|
||||||
dev_err(dev, "missing \"reg\" property\n");
|
dev_err(dev, "missing \"reg\" property\n");
|
||||||
return err;
|
goto err_out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Limit the bus-range to fit within reg */
|
cfg = pci_ecam_create(dev, &cfgres, bus_range, ops);
|
||||||
bus_max = pci->cfg.bus_range->start +
|
if (IS_ERR(cfg)) {
|
||||||
(resource_size(&pci->cfg.res) >> pci->cfg.ops->bus_shift) - 1;
|
err = PTR_ERR(cfg);
|
||||||
pci->cfg.bus_range->end = min_t(resource_size_t,
|
goto err_out;
|
||||||
pci->cfg.bus_range->end, bus_max);
|
|
||||||
|
|
||||||
pci->cfg.win = devm_kcalloc(dev, resource_size(pci->cfg.bus_range),
|
|
||||||
sizeof(*pci->cfg.win), GFP_KERNEL);
|
|
||||||
if (!pci->cfg.win)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
/* 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;
|
|
||||||
|
|
||||||
pci->cfg.win[idx] = devm_ioremap(dev,
|
|
||||||
pci->cfg.res.start + idx * sz,
|
|
||||||
sz);
|
|
||||||
if (!pci->cfg.win[idx])
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
err = devm_add_action(dev, gen_pci_unmap_cfg, cfg);
|
||||||
|
if (err) {
|
||||||
|
gen_pci_unmap_cfg(cfg);
|
||||||
|
goto err_out;
|
||||||
|
}
|
||||||
|
return cfg;
|
||||||
|
|
||||||
|
err_out:
|
||||||
|
pci_free_resource_list(resources);
|
||||||
|
return ERR_PTR(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
int pci_host_common_probe(struct platform_device *pdev,
|
int pci_host_common_probe(struct platform_device *pdev,
|
||||||
struct gen_pci *pci)
|
struct pci_ecam_ops *ops)
|
||||||
{
|
{
|
||||||
int err;
|
|
||||||
const char *type;
|
const char *type;
|
||||||
struct device *dev = &pdev->dev;
|
struct device *dev = &pdev->dev;
|
||||||
struct device_node *np = dev->of_node;
|
struct device_node *np = dev->of_node;
|
||||||
struct pci_bus *bus, *child;
|
struct pci_bus *bus, *child;
|
||||||
|
struct pci_config_window *cfg;
|
||||||
|
struct list_head resources;
|
||||||
|
|
||||||
type = of_get_property(np, "device_type", NULL);
|
type = of_get_property(np, "device_type", NULL);
|
||||||
if (!type || strcmp(type, "pci")) {
|
if (!type || strcmp(type, "pci")) {
|
||||||
|
@ -147,29 +136,18 @@ int pci_host_common_probe(struct platform_device *pdev,
|
||||||
|
|
||||||
of_pci_check_probe_only();
|
of_pci_check_probe_only();
|
||||||
|
|
||||||
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 */
|
/* Parse and map our Configuration Space windows */
|
||||||
err = gen_pci_parse_map_cfg_windows(pci);
|
INIT_LIST_HEAD(&resources);
|
||||||
if (err) {
|
cfg = gen_pci_init(dev, &resources, ops);
|
||||||
gen_pci_release_of_pci_ranges(pci);
|
if (IS_ERR(cfg))
|
||||||
return err;
|
return PTR_ERR(cfg);
|
||||||
}
|
|
||||||
|
|
||||||
/* Do not reassign resources if probe only */
|
/* Do not reassign resources if probe only */
|
||||||
if (!pci_has_flag(PCI_PROBE_ONLY))
|
if (!pci_has_flag(PCI_PROBE_ONLY))
|
||||||
pci_add_flags(PCI_REASSIGN_ALL_RSRC | PCI_REASSIGN_ALL_BUS);
|
pci_add_flags(PCI_REASSIGN_ALL_RSRC | PCI_REASSIGN_ALL_BUS);
|
||||||
|
|
||||||
|
bus = pci_scan_root_bus(dev, cfg->busr.start, &ops->pci_ops, cfg,
|
||||||
bus = pci_scan_root_bus(dev, pci->cfg.bus_range->start,
|
&resources);
|
||||||
&pci->cfg.ops->ops, pci, &pci->resources);
|
|
||||||
if (!bus) {
|
if (!bus) {
|
||||||
dev_err(dev, "Scanning rootbus failed");
|
dev_err(dev, "Scanning rootbus failed");
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
|
@ -1,47 +0,0 @@
|
||||||
/*
|
|
||||||
* 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/>.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2014 ARM Limited
|
|
||||||
*
|
|
||||||
* Author: Will Deacon <will.deacon@arm.com>
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _PCI_HOST_COMMON_H
|
|
||||||
#define _PCI_HOST_COMMON_H
|
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
|
||||||
#include <linux/platform_device.h>
|
|
||||||
|
|
||||||
struct gen_pci_cfg_bus_ops {
|
|
||||||
u32 bus_shift;
|
|
||||||
struct pci_ops ops;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct gen_pci_cfg_windows {
|
|
||||||
struct resource res;
|
|
||||||
struct resource *bus_range;
|
|
||||||
void __iomem **win;
|
|
||||||
|
|
||||||
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;
|
|
||||||
};
|
|
||||||
|
|
||||||
int pci_host_common_probe(struct platform_device *pdev,
|
|
||||||
struct gen_pci *pci);
|
|
||||||
|
|
||||||
#endif /* _PCI_HOST_COMMON_H */
|
|
|
@ -25,41 +25,12 @@
|
||||||
#include <linux/of_pci.h>
|
#include <linux/of_pci.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
|
||||||
#include "pci-host-common.h"
|
#include "../ecam.h"
|
||||||
|
|
||||||
static void __iomem *gen_pci_map_cfg_bus_cam(struct pci_bus *bus,
|
static struct pci_ecam_ops gen_pci_cfg_cam_bus_ops = {
|
||||||
unsigned int devfn,
|
|
||||||
int where)
|
|
||||||
{
|
|
||||||
struct gen_pci *pci = bus->sysdata;
|
|
||||||
resource_size_t idx = bus->number - pci->cfg.bus_range->start;
|
|
||||||
|
|
||||||
return pci->cfg.win[idx] + ((devfn << 8) | where);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct gen_pci_cfg_bus_ops gen_pci_cfg_cam_bus_ops = {
|
|
||||||
.bus_shift = 16,
|
.bus_shift = 16,
|
||||||
.ops = {
|
.pci_ops = {
|
||||||
.map_bus = gen_pci_map_cfg_bus_cam,
|
.map_bus = pci_ecam_map_bus,
|
||||||
.read = pci_generic_config_read,
|
|
||||||
.write = pci_generic_config_write,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static void __iomem *gen_pci_map_cfg_bus_ecam(struct pci_bus *bus,
|
|
||||||
unsigned int devfn,
|
|
||||||
int where)
|
|
||||||
{
|
|
||||||
struct gen_pci *pci = bus->sysdata;
|
|
||||||
resource_size_t idx = bus->number - pci->cfg.bus_range->start;
|
|
||||||
|
|
||||||
return pci->cfg.win[idx] + ((devfn << 12) | where);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct gen_pci_cfg_bus_ops gen_pci_cfg_ecam_bus_ops = {
|
|
||||||
.bus_shift = 20,
|
|
||||||
.ops = {
|
|
||||||
.map_bus = gen_pci_map_cfg_bus_ecam,
|
|
||||||
.read = pci_generic_config_read,
|
.read = pci_generic_config_read,
|
||||||
.write = pci_generic_config_write,
|
.write = pci_generic_config_write,
|
||||||
}
|
}
|
||||||
|
@ -70,25 +41,22 @@ static const struct of_device_id gen_pci_of_match[] = {
|
||||||
.data = &gen_pci_cfg_cam_bus_ops },
|
.data = &gen_pci_cfg_cam_bus_ops },
|
||||||
|
|
||||||
{ .compatible = "pci-host-ecam-generic",
|
{ .compatible = "pci-host-ecam-generic",
|
||||||
.data = &gen_pci_cfg_ecam_bus_ops },
|
.data = &pci_generic_ecam_ops },
|
||||||
|
|
||||||
{ },
|
{ },
|
||||||
};
|
};
|
||||||
|
|
||||||
MODULE_DEVICE_TABLE(of, gen_pci_of_match);
|
MODULE_DEVICE_TABLE(of, gen_pci_of_match);
|
||||||
|
|
||||||
static int gen_pci_probe(struct platform_device *pdev)
|
static int gen_pci_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct device *dev = &pdev->dev;
|
|
||||||
const struct of_device_id *of_id;
|
const struct of_device_id *of_id;
|
||||||
struct gen_pci *pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL);
|
struct pci_ecam_ops *ops;
|
||||||
|
|
||||||
if (!pci)
|
of_id = of_match_node(gen_pci_of_match, pdev->dev.of_node);
|
||||||
return -ENOMEM;
|
ops = (struct pci_ecam_ops *)of_id->data;
|
||||||
|
|
||||||
of_id = of_match_node(gen_pci_of_match, dev->of_node);
|
return pci_host_common_probe(pdev, ops);
|
||||||
pci->cfg.ops = (struct gen_pci_cfg_bus_ops *)of_id->data;
|
|
||||||
|
|
||||||
return pci_host_common_probe(pdev, pci);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct platform_driver gen_pci_driver = {
|
static struct platform_driver gen_pci_driver = {
|
||||||
|
|
|
@ -13,18 +13,7 @@
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
|
||||||
#include "pci-host-common.h"
|
#include "../ecam.h"
|
||||||
|
|
||||||
/* Mapping is standard ECAM */
|
|
||||||
static void __iomem *thunder_ecam_map_bus(struct pci_bus *bus,
|
|
||||||
unsigned int devfn,
|
|
||||||
int where)
|
|
||||||
{
|
|
||||||
struct gen_pci *pci = bus->sysdata;
|
|
||||||
resource_size_t idx = bus->number - pci->cfg.bus_range->start;
|
|
||||||
|
|
||||||
return pci->cfg.win[idx] + ((devfn << 12) | where);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void set_val(u32 v, int where, int size, u32 *val)
|
static void set_val(u32 v, int where, int size, u32 *val)
|
||||||
{
|
{
|
||||||
|
@ -99,7 +88,7 @@ static int handle_ea_bar(u32 e0, int bar, struct pci_bus *bus,
|
||||||
static int thunder_ecam_p2_config_read(struct pci_bus *bus, unsigned int devfn,
|
static int thunder_ecam_p2_config_read(struct pci_bus *bus, unsigned int devfn,
|
||||||
int where, int size, u32 *val)
|
int where, int size, u32 *val)
|
||||||
{
|
{
|
||||||
struct gen_pci *pci = bus->sysdata;
|
struct pci_config_window *cfg = bus->sysdata;
|
||||||
int where_a = where & ~3;
|
int where_a = where & ~3;
|
||||||
void __iomem *addr;
|
void __iomem *addr;
|
||||||
u32 node_bits;
|
u32 node_bits;
|
||||||
|
@ -129,7 +118,7 @@ static int thunder_ecam_p2_config_read(struct pci_bus *bus, unsigned int devfn,
|
||||||
* the config space access window. Since we are working with
|
* the config space access window. Since we are working with
|
||||||
* the high-order 32 bits, shift everything down by 32 bits.
|
* the high-order 32 bits, shift everything down by 32 bits.
|
||||||
*/
|
*/
|
||||||
node_bits = (pci->cfg.res.start >> 32) & (1 << 12);
|
node_bits = (cfg->res.start >> 32) & (1 << 12);
|
||||||
|
|
||||||
v |= node_bits;
|
v |= node_bits;
|
||||||
set_val(v, where, size, val);
|
set_val(v, where, size, val);
|
||||||
|
@ -358,36 +347,24 @@ static int thunder_ecam_config_write(struct pci_bus *bus, unsigned int devfn,
|
||||||
return pci_generic_config_write(bus, devfn, where, size, val);
|
return pci_generic_config_write(bus, devfn, where, size, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct gen_pci_cfg_bus_ops thunder_ecam_bus_ops = {
|
static struct pci_ecam_ops pci_thunder_ecam_ops = {
|
||||||
.bus_shift = 20,
|
.bus_shift = 20,
|
||||||
.ops = {
|
.pci_ops = {
|
||||||
.map_bus = thunder_ecam_map_bus,
|
.map_bus = pci_ecam_map_bus,
|
||||||
.read = thunder_ecam_config_read,
|
.read = thunder_ecam_config_read,
|
||||||
.write = thunder_ecam_config_write,
|
.write = thunder_ecam_config_write,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct of_device_id thunder_ecam_of_match[] = {
|
static const struct of_device_id thunder_ecam_of_match[] = {
|
||||||
{ .compatible = "cavium,pci-host-thunder-ecam",
|
{ .compatible = "cavium,pci-host-thunder-ecam" },
|
||||||
.data = &thunder_ecam_bus_ops },
|
|
||||||
|
|
||||||
{ },
|
{ },
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, thunder_ecam_of_match);
|
MODULE_DEVICE_TABLE(of, thunder_ecam_of_match);
|
||||||
|
|
||||||
static int thunder_ecam_probe(struct platform_device *pdev)
|
static int thunder_ecam_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct device *dev = &pdev->dev;
|
return pci_host_common_probe(pdev, &pci_thunder_ecam_ops);
|
||||||
const struct of_device_id *of_id;
|
|
||||||
struct gen_pci *pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL);
|
|
||||||
|
|
||||||
if (!pci)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
of_id = of_match_node(thunder_ecam_of_match, dev->of_node);
|
|
||||||
pci->cfg.ops = (struct gen_pci_cfg_bus_ops *)of_id->data;
|
|
||||||
|
|
||||||
return pci_host_common_probe(pdev, pci);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct platform_driver thunder_ecam_driver = {
|
static struct platform_driver thunder_ecam_driver = {
|
||||||
|
|
|
@ -20,34 +20,22 @@
|
||||||
#include <linux/of_pci.h>
|
#include <linux/of_pci.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
|
||||||
#include "pci-host-common.h"
|
#include "../ecam.h"
|
||||||
|
|
||||||
#define PEM_CFG_WR 0x28
|
#define PEM_CFG_WR 0x28
|
||||||
#define PEM_CFG_RD 0x30
|
#define PEM_CFG_RD 0x30
|
||||||
|
|
||||||
struct thunder_pem_pci {
|
struct thunder_pem_pci {
|
||||||
struct gen_pci gen_pci;
|
|
||||||
u32 ea_entry[3];
|
u32 ea_entry[3];
|
||||||
void __iomem *pem_reg_base;
|
void __iomem *pem_reg_base;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void __iomem *thunder_pem_map_bus(struct pci_bus *bus,
|
|
||||||
unsigned int devfn, int where)
|
|
||||||
{
|
|
||||||
struct gen_pci *pci = bus->sysdata;
|
|
||||||
resource_size_t idx = bus->number - pci->cfg.bus_range->start;
|
|
||||||
|
|
||||||
return pci->cfg.win[idx] + ((devfn << 16) | where);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int thunder_pem_bridge_read(struct pci_bus *bus, unsigned int devfn,
|
static int thunder_pem_bridge_read(struct pci_bus *bus, unsigned int devfn,
|
||||||
int where, int size, u32 *val)
|
int where, int size, u32 *val)
|
||||||
{
|
{
|
||||||
u64 read_val;
|
u64 read_val;
|
||||||
struct thunder_pem_pci *pem_pci;
|
struct pci_config_window *cfg = bus->sysdata;
|
||||||
struct gen_pci *pci = bus->sysdata;
|
struct thunder_pem_pci *pem_pci = (struct thunder_pem_pci *)cfg->priv;
|
||||||
|
|
||||||
pem_pci = container_of(pci, struct thunder_pem_pci, gen_pci);
|
|
||||||
|
|
||||||
if (devfn != 0 || where >= 2048) {
|
if (devfn != 0 || where >= 2048) {
|
||||||
*val = ~0;
|
*val = ~0;
|
||||||
|
@ -132,17 +120,17 @@ static int thunder_pem_bridge_read(struct pci_bus *bus, unsigned int devfn,
|
||||||
static int thunder_pem_config_read(struct pci_bus *bus, unsigned int devfn,
|
static int thunder_pem_config_read(struct pci_bus *bus, unsigned int devfn,
|
||||||
int where, int size, u32 *val)
|
int where, int size, u32 *val)
|
||||||
{
|
{
|
||||||
struct gen_pci *pci = bus->sysdata;
|
struct pci_config_window *cfg = bus->sysdata;
|
||||||
|
|
||||||
if (bus->number < pci->cfg.bus_range->start ||
|
if (bus->number < cfg->busr.start ||
|
||||||
bus->number > pci->cfg.bus_range->end)
|
bus->number > cfg->busr.end)
|
||||||
return PCIBIOS_DEVICE_NOT_FOUND;
|
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The first device on the bus is the PEM PCIe bridge.
|
* The first device on the bus is the PEM PCIe bridge.
|
||||||
* Special case its config access.
|
* Special case its config access.
|
||||||
*/
|
*/
|
||||||
if (bus->number == pci->cfg.bus_range->start)
|
if (bus->number == cfg->busr.start)
|
||||||
return thunder_pem_bridge_read(bus, devfn, where, size, val);
|
return thunder_pem_bridge_read(bus, devfn, where, size, val);
|
||||||
|
|
||||||
return pci_generic_config_read(bus, devfn, where, size, val);
|
return pci_generic_config_read(bus, devfn, where, size, val);
|
||||||
|
@ -187,12 +175,11 @@ static u32 thunder_pem_bridge_w1c_bits(int where)
|
||||||
static int thunder_pem_bridge_write(struct pci_bus *bus, unsigned int devfn,
|
static int thunder_pem_bridge_write(struct pci_bus *bus, unsigned int devfn,
|
||||||
int where, int size, u32 val)
|
int where, int size, u32 val)
|
||||||
{
|
{
|
||||||
struct gen_pci *pci = bus->sysdata;
|
struct pci_config_window *cfg = bus->sysdata;
|
||||||
struct thunder_pem_pci *pem_pci;
|
struct thunder_pem_pci *pem_pci = (struct thunder_pem_pci *)cfg->priv;
|
||||||
u64 write_val, read_val;
|
u64 write_val, read_val;
|
||||||
u32 mask = 0;
|
u32 mask = 0;
|
||||||
|
|
||||||
pem_pci = container_of(pci, struct thunder_pem_pci, gen_pci);
|
|
||||||
|
|
||||||
if (devfn != 0 || where >= 2048)
|
if (devfn != 0 || where >= 2048)
|
||||||
return PCIBIOS_DEVICE_NOT_FOUND;
|
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||||
|
@ -256,53 +243,38 @@ static int thunder_pem_bridge_write(struct pci_bus *bus, unsigned int devfn,
|
||||||
static int thunder_pem_config_write(struct pci_bus *bus, unsigned int devfn,
|
static int thunder_pem_config_write(struct pci_bus *bus, unsigned int devfn,
|
||||||
int where, int size, u32 val)
|
int where, int size, u32 val)
|
||||||
{
|
{
|
||||||
struct gen_pci *pci = bus->sysdata;
|
struct pci_config_window *cfg = bus->sysdata;
|
||||||
|
|
||||||
if (bus->number < pci->cfg.bus_range->start ||
|
if (bus->number < cfg->busr.start ||
|
||||||
bus->number > pci->cfg.bus_range->end)
|
bus->number > cfg->busr.end)
|
||||||
return PCIBIOS_DEVICE_NOT_FOUND;
|
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||||
/*
|
/*
|
||||||
* The first device on the bus is the PEM PCIe bridge.
|
* The first device on the bus is the PEM PCIe bridge.
|
||||||
* Special case its config access.
|
* Special case its config access.
|
||||||
*/
|
*/
|
||||||
if (bus->number == pci->cfg.bus_range->start)
|
if (bus->number == cfg->busr.start)
|
||||||
return thunder_pem_bridge_write(bus, devfn, where, size, val);
|
return thunder_pem_bridge_write(bus, devfn, where, size, val);
|
||||||
|
|
||||||
|
|
||||||
return pci_generic_config_write(bus, devfn, where, size, val);
|
return pci_generic_config_write(bus, devfn, where, size, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct gen_pci_cfg_bus_ops thunder_pem_bus_ops = {
|
static int thunder_pem_init(struct device *dev, struct pci_config_window *cfg)
|
||||||
.bus_shift = 24,
|
|
||||||
.ops = {
|
|
||||||
.map_bus = thunder_pem_map_bus,
|
|
||||||
.read = thunder_pem_config_read,
|
|
||||||
.write = thunder_pem_config_write,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct of_device_id thunder_pem_of_match[] = {
|
|
||||||
{ .compatible = "cavium,pci-host-thunder-pem",
|
|
||||||
.data = &thunder_pem_bus_ops },
|
|
||||||
|
|
||||||
{ },
|
|
||||||
};
|
|
||||||
MODULE_DEVICE_TABLE(of, thunder_pem_of_match);
|
|
||||||
|
|
||||||
static int thunder_pem_probe(struct platform_device *pdev)
|
|
||||||
{
|
{
|
||||||
struct device *dev = &pdev->dev;
|
|
||||||
const struct of_device_id *of_id;
|
|
||||||
resource_size_t bar4_start;
|
resource_size_t bar4_start;
|
||||||
struct resource *res_pem;
|
struct resource *res_pem;
|
||||||
struct thunder_pem_pci *pem_pci;
|
struct thunder_pem_pci *pem_pci;
|
||||||
|
struct platform_device *pdev;
|
||||||
|
|
||||||
|
/* Only OF support for now */
|
||||||
|
if (!dev->of_node)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
pem_pci = devm_kzalloc(dev, sizeof(*pem_pci), GFP_KERNEL);
|
pem_pci = devm_kzalloc(dev, sizeof(*pem_pci), GFP_KERNEL);
|
||||||
if (!pem_pci)
|
if (!pem_pci)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
of_id = of_match_node(thunder_pem_of_match, dev->of_node);
|
pdev = to_platform_device(dev);
|
||||||
pem_pci->gen_pci.cfg.ops = (struct gen_pci_cfg_bus_ops *)of_id->data;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The second register range is the PEM bridge to the PCIe
|
* The second register range is the PEM bridge to the PCIe
|
||||||
|
@ -330,7 +302,29 @@ static int thunder_pem_probe(struct platform_device *pdev)
|
||||||
pem_pci->ea_entry[1] = (u32)(res_pem->end - bar4_start) & ~3u;
|
pem_pci->ea_entry[1] = (u32)(res_pem->end - bar4_start) & ~3u;
|
||||||
pem_pci->ea_entry[2] = (u32)(bar4_start >> 32);
|
pem_pci->ea_entry[2] = (u32)(bar4_start >> 32);
|
||||||
|
|
||||||
return pci_host_common_probe(pdev, &pem_pci->gen_pci);
|
cfg->priv = pem_pci;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct pci_ecam_ops pci_thunder_pem_ops = {
|
||||||
|
.bus_shift = 24,
|
||||||
|
.init = thunder_pem_init,
|
||||||
|
.pci_ops = {
|
||||||
|
.map_bus = pci_ecam_map_bus,
|
||||||
|
.read = thunder_pem_config_read,
|
||||||
|
.write = thunder_pem_config_write,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct of_device_id thunder_pem_of_match[] = {
|
||||||
|
{ .compatible = "cavium,pci-host-thunder-pem" },
|
||||||
|
{ },
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, thunder_pem_of_match);
|
||||||
|
|
||||||
|
static int thunder_pem_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
return pci_host_common_probe(pdev, &pci_thunder_pem_ops);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct platform_driver thunder_pem_driver = {
|
static struct platform_driver thunder_pem_driver = {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue