Commit bbf9d17d authored by J. German Rivera's avatar J. German Rivera Committed by Greg Kroah-Hartman

staging: fsl-mc: Freescale Management Complex (fsl-mc) bus driver

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: default avatarJ. German Rivera <German.Rivera@freescale.com>
Signed-off-by: default avatarStuart Yoder <stuart.yoder@freescale.com>
Acked-by: default avatarAlexander Graf <agraf@suse.de>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 31c88965
...@@ -110,4 +110,6 @@ source "drivers/staging/fbtft/Kconfig" ...@@ -110,4 +110,6 @@ source "drivers/staging/fbtft/Kconfig"
source "drivers/staging/i2o/Kconfig" source "drivers/staging/i2o/Kconfig"
source "drivers/staging/fsl-mc/Kconfig"
endif # STAGING endif # STAGING
...@@ -47,3 +47,4 @@ obj-$(CONFIG_UNISYSSPAR) += unisys/ ...@@ -47,3 +47,4 @@ obj-$(CONFIG_UNISYSSPAR) += unisys/
obj-$(CONFIG_COMMON_CLK_XLNX_CLKWZRD) += clocking-wizard/ obj-$(CONFIG_COMMON_CLK_XLNX_CLKWZRD) += clocking-wizard/
obj-$(CONFIG_FB_TFT) += fbtft/ obj-$(CONFIG_FB_TFT) += fbtft/
obj-$(CONFIG_I2O) += i2o/ obj-$(CONFIG_I2O) += i2o/
obj-$(CONFIG_FSL_MC_BUS) += fsl-mc/
source "drivers/staging/fsl-mc/bus/Kconfig"
# Freescale Management Complex (MC) bus drivers
obj-$(CONFIG_FSL_MC_BUS) += bus/
#
# 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.
#
# 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
/*
* 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");
/*
* 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_ */
/*
* 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_ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment