lkml.org 
[lkml]   [2015]   [Mar]   [5]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH v9 2/3] staging: fsl-mc: Freescale Management Complex (fsl-mc) bus driver
    Date
    Platform device driver that sets up the basic bus infrastructure
    for the fsl-mc bus type, including support for adding/removing
    fsl-mc devices, register/unregister of fsl-mc drivers, and bus
    match support to bind devices to drivers.

    Signed-off-by: J. German Rivera <German.Rivera@freescale.com>
    Signed-off-by: Stuart Yoder <stuart.yoder@freescale.com>
    ---
    Changes in v9:
    - Fixed compilation errors that escaped after move to staging

    Changes addressed in v8:
    - Addressed comments from Greg Kroah-Hartman:
    * Moved files to drivers/staging to wait for child drivers of the fsl-mc bus
    to be posted upstream:
    drivers/bus/fsl-mc/Kconfig -> drivers/staging/fsl-mc/bus/Kconfig
    drivers/bus/fsl-mc/Makefile -> drivers/staging/fsl-mc/bus/Makefile
    drivers/bus/fsl-mc/mc-bus.c -> drivers/staging/fsl-mc/bus/mc-bus.c
    include/linux/fsl/mc-private.h -> drivers/staging/fsl-mc/include/mc-private.h
    include/linux/fsl/mc.h -> drivers/staging/fsl-mc/include/mc.h

    - Addressed comments from Alex Graf:
    * Removed incorrect comment from __fsl_mc_driver_register()

    Changes in v7:
    - Refactored MC bus data structures
    - Removed creation of fsl_mc_io object from fsl_mc_add_device()
    - Addressed comments from Alex Graf:
    * Use the 'ranges' property for the 'fsl,qoriq-mc' node as a way to
    translate MC-returned addresses (bus relative addresses) to
    system physical addresses.

    Changes addressed in v6:
    - Fixed new checkpatch warnings

    Changes addressed in v5:
    - Addressed comments from Alex Graf:
    * Renamed dprc_get_container_id() call as dpmng_get_container_id().
    * Added TODO comment to use the 'ranges' property of the
    fsl-mc DT node.

    Changes addressed in v4:
    - Addressed comments from Alex Graf:
    * Added missing scope limitations and more descriptive help
    text for the FSL_MC_BUS config option.
    - Fixed bug related to setting fsl_mc_bus_type.dev_root too
    late.

    Changes in v3:
    - Addressed changes from Kim Phillips:
    * Renamed files:
    drivers/bus/fsl-mc/fsl_mc_bus.c -> drivers/bus/fsl-mc/mc-bus.c
    include/linux/fsl_mc.h -> include/linux/fsl/mc.h
    include/linux/fsl_mc_private.h -> include/linux/fsl/mc-private.h

    - Addressed comments from Timur Tabi:
    * Changed all functions that had goto out/error when no common cleanup
    was done, to just have multiple return points.
    * Replaced error cleanup boolean flags with multiple exit points.

    Changes in v2:
    - Addressed comment from Joe Perches:
    * Changed pr_debug to dev_dbg in fsl_mc_bus_match

    - Addressed comments from Kim Phillips and Alex Graf:
    * Changed version check to allow the driver to run with MC
    firmware that has major version number greater than or equal
    to the driver's major version number.
    * Removed minor version check

    - Removed unused variable parent_dev in fsl_mc_device_remove

    drivers/staging/Kconfig | 2 +
    drivers/staging/Makefile | 1 +
    drivers/staging/fsl-mc/Kconfig | 1 +
    drivers/staging/fsl-mc/Makefile | 2 +
    drivers/staging/fsl-mc/bus/Kconfig | 24 +
    drivers/staging/fsl-mc/bus/Makefile | 14 +
    drivers/staging/fsl-mc/bus/mc-bus.c | 755 ++++++++++++++++++++++++++++
    drivers/staging/fsl-mc/include/mc-private.h | 67 +++
    drivers/staging/fsl-mc/include/mc.h | 136 +++++
    9 files changed, 1002 insertions(+)
    create mode 100644 drivers/staging/fsl-mc/Kconfig
    create mode 100644 drivers/staging/fsl-mc/Makefile
    create mode 100644 drivers/staging/fsl-mc/bus/Kconfig
    create mode 100644 drivers/staging/fsl-mc/bus/Makefile
    create mode 100644 drivers/staging/fsl-mc/bus/mc-bus.c
    create mode 100644 drivers/staging/fsl-mc/include/mc-private.h
    create mode 100644 drivers/staging/fsl-mc/include/mc.h

    diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
    index 45baa83..a313265 100644
    --- a/drivers/staging/Kconfig
    +++ b/drivers/staging/Kconfig
    @@ -108,4 +108,6 @@ source "drivers/staging/fbtft/Kconfig"

    source "drivers/staging/i2o/Kconfig"

    +source "drivers/staging/fsl-mc/Kconfig"
    +
    endif # STAGING
    diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
    index 2916079..f93c033 100644
    --- a/drivers/staging/Makefile
    +++ b/drivers/staging/Makefile
    @@ -46,3 +46,4 @@ obj-$(CONFIG_UNISYSSPAR) += unisys/
    obj-$(CONFIG_COMMON_CLK_XLNX_CLKWZRD) += clocking-wizard/
    obj-$(CONFIG_FB_TFT) += fbtft/
    obj-$(CONFIG_I2O) += i2o/
    +obj-$(CONFIG_FSL_MC_BUS) += fsl-mc/
    diff --git a/drivers/staging/fsl-mc/Kconfig b/drivers/staging/fsl-mc/Kconfig
    new file mode 100644
    index 0000000..32df07b
    --- /dev/null
    +++ b/drivers/staging/fsl-mc/Kconfig
    @@ -0,0 +1 @@
    +source "drivers/staging/fsl-mc/bus/Kconfig"
    diff --git a/drivers/staging/fsl-mc/Makefile b/drivers/staging/fsl-mc/Makefile
    new file mode 100644
    index 0000000..9c6a001
    --- /dev/null
    +++ b/drivers/staging/fsl-mc/Makefile
    @@ -0,0 +1,2 @@
    +# Freescale Management Complex (MC) bus drivers
    +obj-$(CONFIG_FSL_MC_BUS) += bus/
    diff --git a/drivers/staging/fsl-mc/bus/Kconfig b/drivers/staging/fsl-mc/bus/Kconfig
    new file mode 100644
    index 0000000..0d779d9
    --- /dev/null
    +++ b/drivers/staging/fsl-mc/bus/Kconfig
    @@ -0,0 +1,24 @@
    +#
    +# Freescale Management Complex (MC) bus drivers
    +#
    +# Copyright (C) 2014 Freescale Semiconductor, Inc.
    +#
    +# This file is released under the GPLv2
    +#
    +
    +config FSL_MC_BUS
    + tristate "Freescale Management Complex (MC) bus driver"
    + depends on OF && ARM64
    + help
    + Driver to enable the bus infrastructure for the Freescale
    + QorIQ Management Complex (fsl-mc). The fsl-mc is a hardware
    + module of the QorIQ LS2 SoCs, that does resource management
    + for hardware building-blocks in the SoC that can be used
    + to dynamically create networking hardware objects such as
    + network interfaces (NICs), crypto accelerator instances,
    + or L2 switches.
    +
    + Only enable this option when building the kernel for
    + Freescale QorQIQ LS2xxxx SoCs.
    +
    +
    diff --git a/drivers/staging/fsl-mc/bus/Makefile b/drivers/staging/fsl-mc/bus/Makefile
    new file mode 100644
    index 0000000..decd339
    --- /dev/null
    +++ b/drivers/staging/fsl-mc/bus/Makefile
    @@ -0,0 +1,14 @@
    +#
    +# Freescale Management Complex (MC) bus drivers
    +#
    +# Copyright (C) 2014 Freescale Semiconductor, Inc.
    +#
    +# This file is released under the GPLv2
    +#
    +obj-$(CONFIG_FSL_MC_BUS) += mc-bus-driver.o
    +
    +mc-bus-driver-objs := mc-bus.o \
    + mc-sys.o \
    + dprc.o \
    + dpmng.o
    +
    diff --git a/drivers/staging/fsl-mc/bus/mc-bus.c b/drivers/staging/fsl-mc/bus/mc-bus.c
    new file mode 100644
    index 0000000..ce1de52
    --- /dev/null
    +++ b/drivers/staging/fsl-mc/bus/mc-bus.c
    @@ -0,0 +1,755 @@
    +/*
    + * Freescale Management Complex (MC) bus driver
    + *
    + * Copyright (C) 2014 Freescale Semiconductor, Inc.
    + * Author: German Rivera <German.Rivera@freescale.com>
    + *
    + * This file is licensed under the terms of the GNU General Public
    + * License version 2. This program is licensed "as is" without any
    + * warranty of any kind, whether express or implied.
    + */
    +
    +#include "../include/mc-private.h"
    +#include <linux/module.h>
    +#include <linux/of_device.h>
    +#include <linux/of_address.h>
    +#include <linux/ioport.h>
    +#include <linux/slab.h>
    +#include <linux/limits.h>
    +#include "../include/dpmng.h"
    +#include "../include/mc-sys.h"
    +#include "dprc-cmd.h"
    +
    +static struct kmem_cache *mc_dev_cache;
    +
    +/**
    + * fsl_mc_bus_match - device to driver matching callback
    + * @dev: the MC object device structure to match against
    + * @drv: the device driver to search for matching MC object device id
    + * structures
    + *
    + * Returns 1 on success, 0 otherwise.
    + */
    +static int fsl_mc_bus_match(struct device *dev, struct device_driver *drv)
    +{
    + const struct fsl_mc_device_match_id *id;
    + struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
    + struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(drv);
    + bool found = false;
    +
    + if (WARN_ON(!fsl_mc_bus_type.dev_root))
    + goto out;
    +
    + if (!mc_drv->match_id_table)
    + goto out;
    +
    + /*
    + * If the object is not 'plugged' don't match.
    + * Only exception is the root DPRC, which is a special case.
    + */
    + if ((mc_dev->obj_desc.state & DPRC_OBJ_STATE_PLUGGED) == 0 &&
    + &mc_dev->dev != fsl_mc_bus_type.dev_root)
    + goto out;
    +
    + /*
    + * Traverse the match_id table of the given driver, trying to find
    + * a matching for the given MC object device.
    + */
    + for (id = mc_drv->match_id_table; id->vendor != 0x0; id++) {
    + if (id->vendor == mc_dev->obj_desc.vendor &&
    + strcmp(id->obj_type, mc_dev->obj_desc.type) == 0 &&
    + id->ver_major == mc_dev->obj_desc.ver_major &&
    + id->ver_minor == mc_dev->obj_desc.ver_minor) {
    + found = true;
    + break;
    + }
    + }
    +
    +out:
    + dev_dbg(dev, "%smatched\n", found ? "" : "not ");
    + return found;
    +}
    +
    +/**
    + * fsl_mc_bus_uevent - callback invoked when a device is added
    + */
    +static int fsl_mc_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
    +{
    + pr_debug("%s invoked\n", __func__);
    + return 0;
    +}
    +
    +struct bus_type fsl_mc_bus_type = {
    + .name = "fsl-mc",
    + .match = fsl_mc_bus_match,
    + .uevent = fsl_mc_bus_uevent,
    +};
    +EXPORT_SYMBOL_GPL(fsl_mc_bus_type);
    +
    +static int fsl_mc_driver_probe(struct device *dev)
    +{
    + struct fsl_mc_driver *mc_drv;
    + struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
    + int error;
    +
    + if (WARN_ON(!dev->driver))
    + return -EINVAL;
    +
    + mc_drv = to_fsl_mc_driver(dev->driver);
    + if (WARN_ON(!mc_drv->probe))
    + return -EINVAL;
    +
    + error = mc_drv->probe(mc_dev);
    + if (error < 0) {
    + dev_err(dev, "MC object device probe callback failed: %d\n",
    + error);
    + return error;
    + }
    +
    + return 0;
    +}
    +
    +static int fsl_mc_driver_remove(struct device *dev)
    +{
    + struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(dev->driver);
    + struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
    + int error;
    +
    + if (WARN_ON(!dev->driver))
    + return -EINVAL;
    +
    + error = mc_drv->remove(mc_dev);
    + if (error < 0) {
    + dev_err(dev,
    + "MC object device remove callback failed: %d\n",
    + error);
    + return error;
    + }
    +
    + return 0;
    +}
    +
    +static void fsl_mc_driver_shutdown(struct device *dev)
    +{
    + struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(dev->driver);
    + struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
    +
    + mc_drv->shutdown(mc_dev);
    +}
    +
    +/**
    + * __fsl_mc_driver_register - registers a child device driver with the
    + * MC bus
    + *
    + * This function is implicitly invoked from the registration function of
    + * fsl_mc device drivers, which is generated by the
    + * module_fsl_mc_driver() macro.
    + */
    +int __fsl_mc_driver_register(struct fsl_mc_driver *mc_driver,
    + struct module *owner)
    +{
    + int error;
    +
    + mc_driver->driver.owner = owner;
    + mc_driver->driver.bus = &fsl_mc_bus_type;
    +
    + if (mc_driver->probe)
    + mc_driver->driver.probe = fsl_mc_driver_probe;
    +
    + if (mc_driver->remove)
    + mc_driver->driver.remove = fsl_mc_driver_remove;
    +
    + if (mc_driver->shutdown)
    + mc_driver->driver.shutdown = fsl_mc_driver_shutdown;
    +
    + error = driver_register(&mc_driver->driver);
    + if (error < 0) {
    + pr_err("driver_register() failed for %s: %d\n",
    + mc_driver->driver.name, error);
    + return error;
    + }
    +
    + pr_info("MC object device driver %s registered\n",
    + mc_driver->driver.name);
    + return 0;
    +}
    +EXPORT_SYMBOL_GPL(__fsl_mc_driver_register);
    +
    +/**
    + * fsl_mc_driver_unregister - unregisters a device driver from the
    + * MC bus
    + */
    +void fsl_mc_driver_unregister(struct fsl_mc_driver *mc_driver)
    +{
    + driver_unregister(&mc_driver->driver);
    +}
    +EXPORT_SYMBOL_GPL(fsl_mc_driver_unregister);
    +
    +static int get_dprc_icid(struct fsl_mc_io *mc_io,
    + int container_id, uint16_t *icid)
    +{
    + uint16_t dprc_handle;
    + struct dprc_attributes attr;
    + int error;
    +
    + error = dprc_open(mc_io, container_id, &dprc_handle);
    + if (error < 0) {
    + pr_err("dprc_open() failed: %d\n", error);
    + return error;
    + }
    +
    + memset(&attr, 0, sizeof(attr));
    + error = dprc_get_attributes(mc_io, dprc_handle, &attr);
    + if (error < 0) {
    + pr_err("dprc_get_attributes() failed: %d\n", error);
    + goto common_cleanup;
    + }
    +
    + *icid = attr.icid;
    + error = 0;
    +
    +common_cleanup:
    + (void)dprc_close(mc_io, dprc_handle);
    + return error;
    +}
    +
    +static int translate_mc_addr(uint64_t mc_addr, phys_addr_t *phys_addr)
    +{
    + int i;
    + struct fsl_mc *mc = dev_get_drvdata(fsl_mc_bus_type.dev_root->parent);
    +
    + if (mc->num_translation_ranges == 0) {
    + /*
    + * Do identity mapping:
    + */
    + *phys_addr = mc_addr;
    + return 0;
    + }
    +
    + for (i = 0; i < mc->num_translation_ranges; i++) {
    + struct fsl_mc_addr_translation_range *range =
    + &mc->translation_ranges[i];
    +
    + if (mc_addr >= range->start_mc_addr &&
    + mc_addr < range->end_mc_addr) {
    + *phys_addr = range->start_phys_addr +
    + (mc_addr - range->start_mc_addr);
    + return 0;
    + }
    + }
    +
    + return -EFAULT;
    +}
    +
    +static int fsl_mc_device_get_mmio_regions(struct fsl_mc_device *mc_dev,
    + struct fsl_mc_device *mc_bus_dev)
    +{
    + int i;
    + int error;
    + struct resource *regions;
    + struct dprc_obj_desc *obj_desc = &mc_dev->obj_desc;
    + struct device *parent_dev = mc_dev->dev.parent;
    +
    + regions = kmalloc_array(obj_desc->region_count,
    + sizeof(regions[0]), GFP_KERNEL);
    + if (!regions)
    + return -ENOMEM;
    +
    + for (i = 0; i < obj_desc->region_count; i++) {
    + struct dprc_region_desc region_desc;
    +
    + error = dprc_get_obj_region(mc_bus_dev->mc_io,
    + mc_bus_dev->mc_handle,
    + obj_desc->type,
    + obj_desc->id, i, &region_desc);
    + if (error < 0) {
    + dev_err(parent_dev,
    + "dprc_get_obj_region() failed: %d\n", error);
    + goto error_cleanup_regions;
    + }
    +
    + WARN_ON(region_desc.base_paddr == 0x0);
    + WARN_ON(region_desc.size == 0);
    + error = translate_mc_addr(region_desc.base_paddr,
    + &regions[i].start);
    + if (error < 0) {
    + dev_err(parent_dev,
    + "Invalid MC address: %#llx\n",
    + region_desc.base_paddr);
    + goto error_cleanup_regions;
    + }
    +
    + regions[i].end = regions[i].start + region_desc.size - 1;
    + regions[i].name = "fsl-mc object MMIO region";
    + regions[i].flags = IORESOURCE_IO;
    + }
    +
    + mc_dev->regions = regions;
    + return 0;
    +
    +error_cleanup_regions:
    + kfree(regions);
    + return error;
    +}
    +
    +/**
    + * Add a newly discovered MC object device to be visible in Linux
    + */
    +int fsl_mc_device_add(struct dprc_obj_desc *obj_desc,
    + struct fsl_mc_io *mc_io,
    + struct device *parent_dev,
    + struct fsl_mc_device **new_mc_dev)
    +{
    + int error;
    + struct fsl_mc_device *mc_dev = NULL;
    + struct fsl_mc_bus *mc_bus = NULL;
    + struct fsl_mc_device *parent_mc_dev;
    +
    + if (parent_dev->bus == &fsl_mc_bus_type)
    + parent_mc_dev = to_fsl_mc_device(parent_dev);
    + else
    + parent_mc_dev = NULL;
    +
    + if (strcmp(obj_desc->type, "dprc") == 0) {
    + /*
    + * Allocate an MC bus device object:
    + */
    + mc_bus = devm_kzalloc(parent_dev, sizeof(*mc_bus), GFP_KERNEL);
    + if (!mc_bus)
    + return -ENOMEM;
    +
    + mc_dev = &mc_bus->mc_dev;
    + } else {
    + /*
    + * Allocate a regular fsl_mc_device object:
    + */
    + mc_dev = kmem_cache_zalloc(mc_dev_cache, GFP_KERNEL);
    + if (!mc_dev)
    + return -ENOMEM;
    + }
    +
    + mc_dev->obj_desc = *obj_desc;
    + mc_dev->mc_io = mc_io;
    + device_initialize(&mc_dev->dev);
    + mc_dev->dev.parent = parent_dev;
    + mc_dev->dev.bus = &fsl_mc_bus_type;
    + dev_set_name(&mc_dev->dev, "%s.%x", obj_desc->type, obj_desc->id);
    +
    + if (strcmp(obj_desc->type, "dprc") == 0) {
    + struct fsl_mc_io *mc_io2;
    +
    + mc_dev->flags |= FSL_MC_IS_DPRC;
    +
    + /*
    + * To get the DPRC's ICID, we need to open the DPRC
    + * in get_dprc_icid(). For child DPRCs, we do so using the
    + * parent DPRC's MC portal instead of the child DPRC's MC
    + * portal, in case the child DPRC is already opened with
    + * its own portal (e.g., the DPRC used by AIOP).
    + *
    + * NOTE: There cannot be more than one active open for a
    + * given MC object, using the same MC portal.
    + */
    + if (parent_mc_dev) {
    + /*
    + * device being added is a child DPRC device
    + */
    + mc_io2 = parent_mc_dev->mc_io;
    + } else {
    + /*
    + * device being added is the root DPRC device
    + */
    + if (WARN_ON(!mc_io)) {
    + error = -EINVAL;
    + goto error_cleanup_dev;
    + }
    +
    + mc_io2 = mc_io;
    +
    + if (!fsl_mc_bus_type.dev_root)
    + fsl_mc_bus_type.dev_root = &mc_dev->dev;
    + }
    +
    + error = get_dprc_icid(mc_io2, obj_desc->id, &mc_dev->icid);
    + if (error < 0)
    + goto error_cleanup_dev;
    + } else {
    + /*
    + * A non-DPRC MC object device has to be a child of another
    + * MC object (specifically a DPRC object)
    + */
    + mc_dev->icid = parent_mc_dev->icid;
    + mc_dev->dma_mask = FSL_MC_DEFAULT_DMA_MASK;
    + mc_dev->dev.dma_mask = &mc_dev->dma_mask;
    + }
    +
    + /*
    + * Get MMIO regions for the device from the MC:
    + *
    + * NOTE: the root DPRC is a special case as its MMIO region is
    + * obtained from the device tree
    + */
    + if (parent_mc_dev && obj_desc->region_count != 0) {
    + error = fsl_mc_device_get_mmio_regions(mc_dev,
    + parent_mc_dev);
    + if (error < 0)
    + goto error_cleanup_dev;
    + }
    +
    + /*
    + * The device-specific probe callback will get invoked by device_add()
    + */
    + error = device_add(&mc_dev->dev);
    + if (error < 0) {
    + dev_err(parent_dev,
    + "device_add() failed for device %s: %d\n",
    + dev_name(&mc_dev->dev), error);
    + goto error_cleanup_dev;
    + }
    +
    + (void)get_device(&mc_dev->dev);
    + dev_dbg(parent_dev, "Added MC object device %s\n",
    + dev_name(&mc_dev->dev));
    +
    + *new_mc_dev = mc_dev;
    + return 0;
    +
    +error_cleanup_dev:
    + kfree(mc_dev->regions);
    + if (mc_bus)
    + devm_kfree(parent_dev, mc_bus);
    + else
    + kmem_cache_free(mc_dev_cache, mc_dev);
    +
    + return error;
    +}
    +EXPORT_SYMBOL_GPL(fsl_mc_device_add);
    +
    +/**
    + * fsl_mc_device_remove - Remove a MC object device from being visible to
    + * Linux
    + *
    + * @mc_dev: Pointer to a MC object device object
    + */
    +void fsl_mc_device_remove(struct fsl_mc_device *mc_dev)
    +{
    + struct fsl_mc_bus *mc_bus = NULL;
    +
    + kfree(mc_dev->regions);
    +
    + /*
    + * The device-specific remove callback will get invoked by device_del()
    + */
    + device_del(&mc_dev->dev);
    + put_device(&mc_dev->dev);
    +
    + if (strcmp(mc_dev->obj_desc.type, "dprc") == 0) {
    + struct fsl_mc_io *mc_io = mc_dev->mc_io;
    +
    + mc_bus = to_fsl_mc_bus(mc_dev);
    + fsl_destroy_mc_io(mc_io);
    + if (&mc_dev->dev == fsl_mc_bus_type.dev_root)
    + fsl_mc_bus_type.dev_root = NULL;
    + }
    +
    + mc_dev->mc_io = NULL;
    + if (mc_bus)
    + devm_kfree(mc_dev->dev.parent, mc_bus);
    + else
    + kmem_cache_free(mc_dev_cache, mc_dev);
    +}
    +EXPORT_SYMBOL_GPL(fsl_mc_device_remove);
    +
    +static int parse_mc_ranges(struct device *dev,
    + int *paddr_cells,
    + int *mc_addr_cells,
    + int *mc_size_cells,
    + const __be32 **ranges_start,
    + uint8_t *num_ranges)
    +{
    + const __be32 *prop;
    + int range_tuple_cell_count;
    + int ranges_len;
    + int tuple_len;
    + struct device_node *mc_node = dev->of_node;
    +
    + *ranges_start = of_get_property(mc_node, "ranges", &ranges_len);
    + if (!(*ranges_start) || !ranges_len) {
    + dev_warn(dev,
    + "missing or empty ranges property for device tree node '%s'\n",
    + mc_node->name);
    +
    + *num_ranges = 0;
    + return 0;
    + }
    +
    + *paddr_cells = of_n_addr_cells(mc_node);
    +
    + prop = of_get_property(mc_node, "#address-cells", NULL);
    + if (prop)
    + *mc_addr_cells = be32_to_cpup(prop);
    + else
    + *mc_addr_cells = *paddr_cells;
    +
    + prop = of_get_property(mc_node, "#size-cells", NULL);
    + if (prop)
    + *mc_size_cells = be32_to_cpup(prop);
    + else
    + *mc_size_cells = of_n_size_cells(mc_node);
    +
    + range_tuple_cell_count = *paddr_cells + *mc_addr_cells +
    + *mc_size_cells;
    +
    + tuple_len = range_tuple_cell_count * sizeof(__be32);
    + if (ranges_len % tuple_len != 0) {
    + dev_err(dev, "malformed ranges property '%s'\n", mc_node->name);
    + return -EINVAL;
    + }
    +
    + *num_ranges = ranges_len / tuple_len;
    + return 0;
    +}
    +
    +static int get_mc_addr_translation_ranges(struct device *dev,
    + struct fsl_mc_addr_translation_range
    + **ranges,
    + uint8_t *num_ranges)
    +{
    + int error;
    + int paddr_cells;
    + int mc_addr_cells;
    + int mc_size_cells;
    + int i;
    + const __be32 *ranges_start;
    + const __be32 *cell;
    +
    + error = parse_mc_ranges(dev,
    + &paddr_cells,
    + &mc_addr_cells,
    + &mc_size_cells,
    + &ranges_start,
    + num_ranges);
    + if (error < 0)
    + return error;
    +
    + if (!(*num_ranges)) {
    + /*
    + * Missing or empty ranges property ("ranges;") for the
    + * 'fsl,qoriq-mc' node. In this case, identity mapping
    + * will be used.
    + */
    + *ranges = NULL;
    + return 0;
    + }
    +
    + *ranges = devm_kcalloc(dev, *num_ranges,
    + sizeof(struct fsl_mc_addr_translation_range),
    + GFP_KERNEL);
    + if (!(*ranges))
    + return -ENOMEM;
    +
    + cell = ranges_start;
    + for (i = 0; i < *num_ranges; ++i) {
    + struct fsl_mc_addr_translation_range *range = &(*ranges)[i];
    +
    + range->start_mc_addr = of_read_number(cell, mc_addr_cells);
    + cell += mc_addr_cells;
    + range->start_phys_addr = of_read_number(cell, paddr_cells);
    + cell += paddr_cells;
    + range->end_mc_addr = range->start_mc_addr +
    + of_read_number(cell, mc_size_cells);
    +
    + cell += mc_size_cells;
    + }
    +
    + return 0;
    +}
    +
    +/**
    + * fsl_mc_bus_probe - callback invoked when the root MC bus is being
    + * added
    + */
    +static int fsl_mc_bus_probe(struct platform_device *pdev)
    +{
    + struct dprc_obj_desc obj_desc;
    + int error;
    + struct fsl_mc *mc;
    + struct fsl_mc_device *mc_bus_dev = NULL;
    + struct fsl_mc_io *mc_io = NULL;
    + int container_id;
    + phys_addr_t mc_portal_phys_addr;
    + uint32_t mc_portal_size;
    + struct mc_version mc_version;
    + struct resource res;
    +
    + dev_info(&pdev->dev, "Root MC bus device probed");
    +
    + mc = devm_kzalloc(&pdev->dev, sizeof(*mc), GFP_KERNEL);
    + if (!mc)
    + return -ENOMEM;
    +
    + platform_set_drvdata(pdev, mc);
    +
    + /*
    + * Get physical address of MC portal for the root DPRC:
    + */
    + error = of_address_to_resource(pdev->dev.of_node, 0, &res);
    + if (error < 0) {
    + dev_err(&pdev->dev,
    + "of_address_to_resource() failed for %s\n",
    + pdev->dev.of_node->full_name);
    + return error;
    + }
    +
    + mc_portal_phys_addr = res.start;
    + mc_portal_size = resource_size(&res);
    + error = fsl_create_mc_io(&pdev->dev, mc_portal_phys_addr,
    + mc_portal_size, 0, &mc_io);
    + if (error < 0)
    + return error;
    +
    + error = mc_get_version(mc_io, &mc_version);
    + if (error != 0) {
    + dev_err(&pdev->dev,
    + "mc_get_version() failed with error %d\n", error);
    + goto error_cleanup_mc_io;
    + }
    +
    + dev_info(&pdev->dev,
    + "Freescale Management Complex Firmware version: %u.%u.%u\n",
    + mc_version.major, mc_version.minor, mc_version.revision);
    +
    + if (mc_version.major < MC_VER_MAJOR) {
    + dev_err(&pdev->dev,
    + "ERROR: MC firmware version not supported by driver (driver version: %u.%u)\n",
    + MC_VER_MAJOR, MC_VER_MINOR);
    + error = -ENOTSUPP;
    + goto error_cleanup_mc_io;
    + }
    +
    + if (mc_version.major > MC_VER_MAJOR) {
    + dev_warn(&pdev->dev,
    + "WARNING: driver may not support newer MC firmware features (driver version: %u.%u)\n",
    + MC_VER_MAJOR, MC_VER_MINOR);
    + }
    +
    + error = get_mc_addr_translation_ranges(&pdev->dev,
    + &mc->translation_ranges,
    + &mc->num_translation_ranges);
    + if (error < 0)
    + goto error_cleanup_mc_io;
    +
    + error = dpmng_get_container_id(mc_io, &container_id);
    + if (error < 0) {
    + dev_err(&pdev->dev,
    + "dpmng_get_container_id() failed: %d\n", error);
    + goto error_cleanup_mc_io;
    + }
    +
    + obj_desc.vendor = FSL_MC_VENDOR_FREESCALE;
    + strcpy(obj_desc.type, "dprc");
    + obj_desc.id = container_id;
    + obj_desc.ver_major = DPRC_VER_MAJOR;
    + obj_desc.ver_minor = DPRC_VER_MINOR;
    + obj_desc.region_count = 0;
    +
    + error = fsl_mc_device_add(&obj_desc, mc_io, &pdev->dev, &mc_bus_dev);
    + if (error < 0)
    + goto error_cleanup_mc_io;
    +
    + mc->root_mc_bus_dev = mc_bus_dev;
    + return 0;
    +
    +error_cleanup_mc_io:
    + fsl_destroy_mc_io(mc_io);
    + return error;
    +}
    +
    +/**
    + * fsl_mc_bus_remove - callback invoked when the root MC bus is being
    + * removed
    + */
    +static int fsl_mc_bus_remove(struct platform_device *pdev)
    +{
    + struct fsl_mc *mc = platform_get_drvdata(pdev);
    +
    + if (WARN_ON(&mc->root_mc_bus_dev->dev != fsl_mc_bus_type.dev_root))
    + return -EINVAL;
    +
    + fsl_mc_device_remove(mc->root_mc_bus_dev);
    + dev_info(&pdev->dev, "Root MC bus device removed");
    + return 0;
    +}
    +
    +static const struct of_device_id fsl_mc_bus_match_table[] = {
    + {.compatible = "fsl,qoriq-mc",},
    + {},
    +};
    +
    +MODULE_DEVICE_TABLE(of, fsl_mc_bus_match_table);
    +
    +static struct platform_driver fsl_mc_bus_driver = {
    + .driver = {
    + .name = "fsl_mc_bus",
    + .owner = THIS_MODULE,
    + .pm = NULL,
    + .of_match_table = fsl_mc_bus_match_table,
    + },
    + .probe = fsl_mc_bus_probe,
    + .remove = fsl_mc_bus_remove,
    +};
    +
    +static int __init fsl_mc_bus_driver_init(void)
    +{
    + int error;
    +
    + mc_dev_cache = kmem_cache_create("fsl_mc_device",
    + sizeof(struct fsl_mc_device), 0, 0,
    + NULL);
    + if (!mc_dev_cache) {
    + pr_err("Could not create fsl_mc_device cache\n");
    + return -ENOMEM;
    + }
    +
    + error = bus_register(&fsl_mc_bus_type);
    + if (error < 0) {
    + pr_err("fsl-mc bus type registration failed: %d\n", error);
    + goto error_cleanup_cache;
    + }
    +
    + pr_info("fsl-mc bus type registered\n");
    +
    + error = platform_driver_register(&fsl_mc_bus_driver);
    + if (error < 0) {
    + pr_err("platform_driver_register() failed: %d\n", error);
    + goto error_cleanup_bus;
    + }
    +
    + return 0;
    +
    +error_cleanup_bus:
    + bus_unregister(&fsl_mc_bus_type);
    +
    +error_cleanup_cache:
    + kmem_cache_destroy(mc_dev_cache);
    + return error;
    +}
    +
    +postcore_initcall(fsl_mc_bus_driver_init);
    +
    +static void __exit fsl_mc_bus_driver_exit(void)
    +{
    + if (WARN_ON(!mc_dev_cache))
    + return;
    +
    + platform_driver_unregister(&fsl_mc_bus_driver);
    + bus_unregister(&fsl_mc_bus_type);
    + kmem_cache_destroy(mc_dev_cache);
    + pr_info("MC bus unregistered\n");
    +}
    +
    +module_exit(fsl_mc_bus_driver_exit);
    +
    +MODULE_AUTHOR("Freescale Semiconductor Inc.");
    +MODULE_DESCRIPTION("Freescale Management Complex (MC) bus driver");
    +MODULE_LICENSE("GPL");
    diff --git a/drivers/staging/fsl-mc/include/mc-private.h b/drivers/staging/fsl-mc/include/mc-private.h
    new file mode 100644
    index 0000000..5c957c4
    --- /dev/null
    +++ b/drivers/staging/fsl-mc/include/mc-private.h
    @@ -0,0 +1,67 @@
    +/*
    + * Freescale Management Complex (MC) bus private declarations
    + *
    + * Copyright (C) 2014 Freescale Semiconductor, Inc.
    + * Author: German Rivera <German.Rivera@freescale.com>
    + *
    + * This file is licensed under the terms of the GNU General Public
    + * License version 2. This program is licensed "as is" without any
    + * warranty of any kind, whether express or implied.
    + */
    +#ifndef _FSL_MC_PRIVATE_H_
    +#define _FSL_MC_PRIVATE_H_
    +
    +#include "../include/mc.h"
    +#include <linux/mutex.h>
    +#include <linux/stringify.h>
    +
    +#define FSL_MC_DEVICE_MATCH(_mc_dev, _obj_desc) \
    + (strcmp((_mc_dev)->obj_desc.type, (_obj_desc)->type) == 0 && \
    + (_mc_dev)->obj_desc.id == (_obj_desc)->id)
    +
    +/**
    + * struct fsl_mc - Private data of a "fsl,qoriq-mc" platform device
    + * @root_mc_bus_dev: MC object device representing the root DPRC
    + * @addr_translation_ranges: array of bus to system address translation ranges
    + */
    +struct fsl_mc {
    + struct fsl_mc_device *root_mc_bus_dev;
    + uint8_t num_translation_ranges;
    + struct fsl_mc_addr_translation_range *translation_ranges;
    +};
    +
    +/**
    + * struct fsl_mc_bus - logical bus that corresponds to a physical DPRC
    + * @mc_dev: fsl-mc device for the bus device itself.
    + * @scan_mutex: Serializes bus scanning
    + */
    +struct fsl_mc_bus {
    + struct fsl_mc_device mc_dev;
    + struct mutex scan_mutex; /* serializes bus scanning */
    +};
    +
    +#define to_fsl_mc_bus(_mc_dev) \
    + container_of(_mc_dev, struct fsl_mc_bus, mc_dev)
    +
    +/**
    + * struct fsl_mc_addr_translation_range - bus to system address translation
    + * range
    + * @start_mc_addr: Start MC address of the range being translated
    + * @end_mc_addr: MC address of the first byte after the range (last MC
    + * address of the range is end_mc_addr - 1)
    + * @start_phys_addr: system physical address corresponding to start_mc_addr
    + */
    +struct fsl_mc_addr_translation_range {
    + uint64_t start_mc_addr;
    + uint64_t end_mc_addr;
    + phys_addr_t start_phys_addr;
    +};
    +
    +int __must_check fsl_mc_device_add(struct dprc_obj_desc *obj_desc,
    + struct fsl_mc_io *mc_io,
    + struct device *parent_dev,
    + struct fsl_mc_device **new_mc_dev);
    +
    +void fsl_mc_device_remove(struct fsl_mc_device *mc_dev);
    +
    +#endif /* _FSL_MC_PRIVATE_H_ */
    diff --git a/drivers/staging/fsl-mc/include/mc.h b/drivers/staging/fsl-mc/include/mc.h
    new file mode 100644
    index 0000000..5cd237b
    --- /dev/null
    +++ b/drivers/staging/fsl-mc/include/mc.h
    @@ -0,0 +1,136 @@
    +/*
    + * Freescale Management Complex (MC) bus public interface
    + *
    + * Copyright (C) 2014 Freescale Semiconductor, Inc.
    + * Author: German Rivera <German.Rivera@freescale.com>
    + *
    + * This file is licensed under the terms of the GNU General Public
    + * License version 2. This program is licensed "as is" without any
    + * warranty of any kind, whether express or implied.
    + */
    +#ifndef _FSL_MC_H_
    +#define _FSL_MC_H_
    +
    +#include <linux/device.h>
    +#include <linux/mod_devicetable.h>
    +#include <linux/list.h>
    +#include "../include/dprc.h"
    +
    +#define FSL_MC_VENDOR_FREESCALE 0x1957
    +
    +struct fsl_mc_device;
    +struct fsl_mc_io;
    +
    +/**
    + * struct fsl_mc_driver - MC object device driver object
    + * @driver: Generic device driver
    + * @match_id_table: table of supported device matching Ids
    + * @probe: Function called when a device is added
    + * @remove: Function called when a device is removed
    + * @shutdown: Function called at shutdown time to quiesce the device
    + * @suspend: Function called when a device is stopped
    + * @resume: Function called when a device is resumed
    + *
    + * Generic DPAA device driver object for device drivers that are registered
    + * with a DPRC bus. This structure is to be embedded in each device-specific
    + * driver structure.
    + */
    +struct fsl_mc_driver {
    + struct device_driver driver;
    + const struct fsl_mc_device_match_id *match_id_table;
    + int (*probe)(struct fsl_mc_device *dev);
    + int (*remove)(struct fsl_mc_device *dev);
    + void (*shutdown)(struct fsl_mc_device *dev);
    + int (*suspend)(struct fsl_mc_device *dev, pm_message_t state);
    + int (*resume)(struct fsl_mc_device *dev);
    +};
    +
    +#define to_fsl_mc_driver(_drv) \
    + container_of(_drv, struct fsl_mc_driver, driver)
    +
    +/**
    + * struct fsl_mc_device_match_id - MC object device Id entry for driver matching
    + * @vendor: vendor ID
    + * @obj_type: MC object type
    + * @ver_major: MC object version major number
    + * @ver_minor: MC object version minor number
    + *
    + * Type of entries in the "device Id" table for MC object devices supported by
    + * a MC object device driver. The last entry of the table has vendor set to 0x0
    + */
    +struct fsl_mc_device_match_id {
    + uint16_t vendor;
    + const char obj_type[16];
    + uint32_t ver_major;
    + uint32_t ver_minor;
    +};
    +
    +/**
    + * Bit masks for a MC object device (struct fsl_mc_device) flags
    + */
    +#define FSL_MC_IS_DPRC 0x0001
    +
    +/**
    + * Default DMA mask for devices on a fsl-mc bus
    + */
    +#define FSL_MC_DEFAULT_DMA_MASK (~0ULL)
    +
    +/**
    + * struct fsl_mc_device - MC object device object
    + * @dev: Linux driver model device object
    + * @dma_mask: Default DMA mask
    + * @flags: MC object device flags
    + * @icid: Isolation context ID for the device
    + * @mc_handle: MC handle for the corresponding MC object opened
    + * @mc_io: Pointer to MC IO object assigned to this device or
    + * NULL if none.
    + * @obj_desc: MC description of the DPAA device
    + * @regions: pointer to array of MMIO region entries
    + *
    + * Generic device object for MC object devices that are "attached" to a
    + * MC bus.
    + *
    + * NOTES:
    + * - For a non-DPRC object its icid is the same as its parent DPRC's icid.
    + * - The SMMU notifier callback gets invoked after device_add() has been
    + * called for an MC object device, but before the device-specific probe
    + * callback gets called.
    + */
    +struct fsl_mc_device {
    + struct device dev;
    + uint64_t dma_mask;
    + uint16_t flags;
    + uint16_t icid;
    + uint16_t mc_handle;
    + struct fsl_mc_io *mc_io;
    + struct dprc_obj_desc obj_desc;
    + struct resource *regions;
    +};
    +
    +#define to_fsl_mc_device(_dev) \
    + container_of(_dev, struct fsl_mc_device, dev)
    +
    +/*
    + * module_fsl_mc_driver() - Helper macro for drivers that don't do
    + * anything special in module init/exit. This eliminates a lot of
    + * boilerplate. Each module may only use this macro once, and
    + * calling it replaces module_init() and module_exit()
    + */
    +#define module_fsl_mc_driver(__fsl_mc_driver) \
    + module_driver(__fsl_mc_driver, fsl_mc_driver_register, \
    + fsl_mc_driver_unregister)
    +
    +/*
    + * Macro to avoid include chaining to get THIS_MODULE
    + */
    +#define fsl_mc_driver_register(drv) \
    + __fsl_mc_driver_register(drv, THIS_MODULE)
    +
    +int __must_check __fsl_mc_driver_register(struct fsl_mc_driver *fsl_mc_driver,
    + struct module *owner);
    +
    +void fsl_mc_driver_unregister(struct fsl_mc_driver *driver);
    +
    +extern struct bus_type fsl_mc_bus_type;
    +
    +#endif /* _FSL_MC_H_ */
    --
    2.3.0


    \
     
     \ /
      Last update: 2015-03-06 02:41    [W:3.319 / U:0.020 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site