diff options
author | Biwen Li <biwen.li@nxp.com> | 2018-11-02 11:21:57 +0800 |
---|---|---|
committer | Hauke Mehrtens <hauke@hauke-m.de> | 2018-12-18 20:17:23 +0100 |
commit | 0a827ebd2fa3c7dc210aff0a30d0dc1d536ed89c (patch) | |
tree | b80223fffb0814b6db33f48dd3aaa047cd3e0299 /target/linux/layerscape/patches-4.14/708-mc-bus-support-layerscape.patch | |
parent | 7bd6969acce763a7117b33a6647b8c5dbbf2b0aa (diff) | |
download | upstream-0a827ebd2fa3c7dc210aff0a30d0dc1d536ed89c.tar.gz upstream-0a827ebd2fa3c7dc210aff0a30d0dc1d536ed89c.tar.bz2 upstream-0a827ebd2fa3c7dc210aff0a30d0dc1d536ed89c.zip |
layerscape: upgrade kernel to 4.14
This patch is to upgrade kernel to 4.14 for layerscape.
patches-4.14 for layerscape included two categories.
- NXP Layerscape SDK kernel-4.14 patches
All patches on tag LSDK-18.09-V4.14 were ported to OpenWrt
kernel. Since there were hundreds patches, we had to make
an all-in-one patch for each IP/feature.
See below links for LSDK kernel.
https://lsdk.github.io/components.html
https://source.codeaurora.org/external/qoriq/qoriq-components/linux
- Non-LSDK kernel patches
Other patches which were not in LSDK were just put in patches-4.14.
Kept below patches from patches-4.9.
303-dts-layerscape-add-traverse-ls1043.patch
821-add-esdhc-vsel-to-ls1043.patch
822-rgmii-fixed-link.patch
Renamed and rebase them as below in patches-4.14,
303-add-DTS-for-Traverse-LS1043-Boards.patch
712-sdk-dpaa-rgmii-fixed-link.patch
824-mmc-sdhci-of-esdhc-add-voltage-switch-support-for-ls.patch
Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
Signed-off-by: Biwen Li <biwen.li@nxp.com>
Diffstat (limited to 'target/linux/layerscape/patches-4.14/708-mc-bus-support-layerscape.patch')
-rw-r--r-- | target/linux/layerscape/patches-4.14/708-mc-bus-support-layerscape.patch | 12062 |
1 files changed, 12062 insertions, 0 deletions
diff --git a/target/linux/layerscape/patches-4.14/708-mc-bus-support-layerscape.patch b/target/linux/layerscape/patches-4.14/708-mc-bus-support-layerscape.patch new file mode 100644 index 0000000000..8d2ba1a193 --- /dev/null +++ b/target/linux/layerscape/patches-4.14/708-mc-bus-support-layerscape.patch @@ -0,0 +1,12062 @@ +From 2aaf8e8caef3ec4c2c155421f62f983892c49387 Mon Sep 17 00:00:00 2001 +From: Biwen Li <biwen.li@nxp.com> +Date: Fri, 16 Nov 2018 12:20:04 +0800 +Subject: [PATCH 13/39] mc-bus: support layerscape +This is an integrated patch of mc-bus for layerscape + +Signed-off-by: Bharat Bhushan <Bharat.Bhushan@nxp.com> +Signed-off-by: Bogdan Purcareata <bogdan.purcareata@nxp.com> +Signed-off-by: Cristian Sovaiala <cristian.sovaiala@freescale.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +Signed-off-by: Guanhua Gao <guanhua.gao@nxp.com> +Signed-off-by: Horia Geantă <horia.geanta@nxp.com> +Signed-off-by: Ioana Ciornei <ioana.ciornei@nxp.com> +Signed-off-by: Ioana Radulescu <ruxandra.radulescu@nxp.com> +Signed-off-by: J. German Rivera <German.Rivera@freescale.com> +Signed-off-by: Laurentiu Tudor <laurentiu.tudor@nxp.com> +Signed-off-by: Lijun Pan <Lijun.Pan@freescale.com> +Signed-off-by: Nipun Gupta <nipun.gupta@nxp.com> +Signed-off-by: Radu Alexe <radu.alexe@nxp.com> +Signed-off-by: Razvan Stefanescu <razvan.stefanescu@nxp.com> +Signed-off-by: Stuart Yoder <stuart.yoder@nxp.com> +Signed-off-by: Stuart Yoder <stuyoder@gmail.com> +Signed-off-by: Biwen Li <biwen.li@nxp.com> +--- + drivers/bus/Kconfig | 2 + + drivers/bus/Makefile | 4 + + drivers/bus/fsl-mc/Kconfig | 23 + + drivers/bus/fsl-mc/Makefile | 21 + + .../{staging/fsl-mc/bus => bus/fsl-mc}/dpbp.c | 97 +- + .../fsl-mc/bus => bus/fsl-mc}/dpcon.c | 103 +- + drivers/bus/fsl-mc/dpmcp.c | 99 ++ + .../fsl-mc/bus => bus/fsl-mc}/dprc-driver.c | 96 +- + .../{staging/fsl-mc/bus => bus/fsl-mc}/dprc.c | 288 +---- + .../bus => bus/fsl-mc}/fsl-mc-allocator.c | 112 +- + .../fsl-mc/bus => bus/fsl-mc}/fsl-mc-bus.c | 305 ++++- + .../fsl-mc/bus => bus/fsl-mc}/fsl-mc-msi.c | 16 +- + drivers/bus/fsl-mc/fsl-mc-private.h | 223 ++++ + drivers/bus/fsl-mc/fsl-mc-restool.c | 219 ++++ + .../fsl-mc/bus => bus/fsl-mc}/mc-io.c | 36 +- + .../fsl-mc/bus => bus/fsl-mc}/mc-sys.c | 33 +- + drivers/irqchip/Kconfig | 6 + + drivers/irqchip/Makefile | 1 + + drivers/irqchip/irq-gic-v3-its-fsl-mc-msi.c | 98 ++ + drivers/staging/fsl-dpaa2/ethernet/README | 2 +- + .../staging/fsl-dpaa2/ethernet/dpaa2-eth.c | 2 +- + .../staging/fsl-dpaa2/ethernet/dpaa2-eth.h | 3 +- + drivers/staging/fsl-dpaa2/ethernet/dpni.c | 2 +- + drivers/staging/fsl-mc/TODO | 18 - + drivers/staging/fsl-mc/bus/Kconfig | 16 +- + drivers/staging/fsl-mc/bus/Makefile | 13 - + drivers/staging/fsl-mc/bus/dpio/dpio-driver.c | 2 +- + .../staging/fsl-mc/bus/dpio/dpio-service.c | 2 +- + drivers/staging/fsl-mc/bus/dpio/dpio.c | 14 +- + drivers/staging/fsl-mc/bus/dpmcp-cmd.h | 56 - + drivers/staging/fsl-mc/bus/dpmcp.h | 60 - + drivers/staging/fsl-mc/bus/dpmng-cmd.h | 58 - + drivers/staging/fsl-mc/bus/dprc-cmd.h | 451 -------- + drivers/staging/fsl-mc/bus/dprc.h | 268 ----- + .../fsl-mc/bus/irq-gic-v3-its-fsl-mc-msi.c | 1 + + include/linux/fsl/mc.h | 1020 +++++++++++++++++ + include/uapi/linux/fsl_mc.h | 31 + + 37 files changed, 2255 insertions(+), 1546 deletions(-) + create mode 100644 drivers/bus/fsl-mc/Kconfig + create mode 100644 drivers/bus/fsl-mc/Makefile + rename drivers/{staging/fsl-mc/bus => bus/fsl-mc}/dpbp.c (67%) + rename drivers/{staging/fsl-mc/bus => bus/fsl-mc}/dpcon.c (70%) + create mode 100644 drivers/bus/fsl-mc/dpmcp.c + rename drivers/{staging/fsl-mc/bus => bus/fsl-mc}/dprc-driver.c (93%) + rename drivers/{staging/fsl-mc/bus => bus/fsl-mc}/dprc.c (68%) + rename drivers/{staging/fsl-mc/bus => bus/fsl-mc}/fsl-mc-allocator.c (84%) + rename drivers/{staging/fsl-mc/bus => bus/fsl-mc}/fsl-mc-bus.c (76%) + rename drivers/{staging/fsl-mc/bus => bus/fsl-mc}/fsl-mc-msi.c (96%) + create mode 100644 drivers/bus/fsl-mc/fsl-mc-private.h + create mode 100644 drivers/bus/fsl-mc/fsl-mc-restool.c + rename drivers/{staging/fsl-mc/bus => bus/fsl-mc}/mc-io.c (89%) + rename drivers/{staging/fsl-mc/bus => bus/fsl-mc}/mc-sys.c (90%) + create mode 100644 drivers/irqchip/irq-gic-v3-its-fsl-mc-msi.c + delete mode 100644 drivers/staging/fsl-mc/TODO + delete mode 100644 drivers/staging/fsl-mc/bus/dpmcp-cmd.h + delete mode 100644 drivers/staging/fsl-mc/bus/dpmcp.h + delete mode 100644 drivers/staging/fsl-mc/bus/dpmng-cmd.h + delete mode 100644 drivers/staging/fsl-mc/bus/dprc-cmd.h + delete mode 100644 drivers/staging/fsl-mc/bus/dprc.h + create mode 100644 include/linux/fsl/mc.h + create mode 100644 include/uapi/linux/fsl_mc.h + +--- a/drivers/bus/Kconfig ++++ b/drivers/bus/Kconfig +@@ -184,4 +184,6 @@ config DA8XX_MSTPRI + configuration. Allows to adjust the priorities of all master + peripherals. + ++source "drivers/bus/fsl-mc/Kconfig" ++ + endmenu +--- a/drivers/bus/Makefile ++++ b/drivers/bus/Makefile +@@ -8,6 +8,10 @@ obj-$(CONFIG_ARM_CCI) += arm-cci.o + obj-$(CONFIG_ARM_CCN) += arm-ccn.o + + obj-$(CONFIG_BRCMSTB_GISB_ARB) += brcmstb_gisb.o ++ ++# DPAA2 fsl-mc bus ++obj-$(CONFIG_FSL_MC_BUS) += fsl-mc/ ++ + obj-$(CONFIG_IMX_WEIM) += imx-weim.o + obj-$(CONFIG_MIPS_CDMM) += mips_cdmm.o + obj-$(CONFIG_MVEBU_MBUS) += mvebu-mbus.o +--- /dev/null ++++ b/drivers/bus/fsl-mc/Kconfig +@@ -0,0 +1,23 @@ ++# SPDX-License-Identifier: GPL-2.0 ++# ++# DPAA2 fsl-mc bus ++# ++# Copyright (C) 2014-2016 Freescale Semiconductor, Inc. ++# ++ ++config FSL_MC_BUS ++ bool "QorIQ DPAA2 fsl-mc bus driver" ++ depends on OF && (ARCH_LAYERSCAPE || (COMPILE_TEST && (ARM || ARM64 || X86 || PPC))) ++ select GENERIC_MSI_IRQ_DOMAIN ++ help ++ Driver to enable the bus infrastructure for the QorIQ DPAA2 ++ architecture. The fsl-mc bus driver handles discovery of ++ DPAA2 objects (which are represented as Linux devices) and ++ binding objects to drivers. ++ ++config FSL_MC_RESTOOL ++ bool "Management Complex (MC) restool support" ++ depends on FSL_MC_BUS ++ help ++ Provides kernel support for the Management Complex resource ++ manager user-space tool - restool. +--- /dev/null ++++ b/drivers/bus/fsl-mc/Makefile +@@ -0,0 +1,21 @@ ++# SPDX-License-Identifier: GPL-2.0 ++# ++# Freescale Management Complex (MC) bus drivers ++# ++# Copyright (C) 2014 Freescale Semiconductor, Inc. ++# ++obj-$(CONFIG_FSL_MC_BUS) += mc-bus-driver.o ++ ++mc-bus-driver-objs := fsl-mc-bus.o \ ++ mc-sys.o \ ++ mc-io.o \ ++ dpbp.o \ ++ dpcon.o \ ++ dprc.o \ ++ dprc-driver.o \ ++ fsl-mc-allocator.o \ ++ fsl-mc-msi.o \ ++ dpmcp.o ++ ++# MC restool kernel support ++obj-$(CONFIG_FSL_MC_RESTOOL) += fsl-mc-restool.o +--- a/drivers/staging/fsl-mc/bus/dpbp.c ++++ /dev/null +@@ -1,253 +0,0 @@ +-// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +-/* +- * Copyright 2013-2016 Freescale Semiconductor Inc. +- * +- */ +-#include <linux/kernel.h> +-#include "../include/mc.h" +-#include "../include/dpbp.h" +- +-#include "dpbp-cmd.h" +- +-/** +- * dpbp_open() - Open a control session for the specified object. +- * @mc_io: Pointer to MC portal's I/O object +- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' +- * @dpbp_id: DPBP unique ID +- * @token: Returned token; use in subsequent API calls +- * +- * This function can be used to open a control session for an +- * already created object; an object may have been declared in +- * the DPL or by calling the dpbp_create function. +- * This function returns a unique authentication token, +- * associated with the specific object ID and the specific MC +- * portal; this token must be used in all subsequent commands for +- * this specific object +- * +- * Return: '0' on Success; Error code otherwise. +- */ +-int dpbp_open(struct fsl_mc_io *mc_io, +- u32 cmd_flags, +- int dpbp_id, +- u16 *token) +-{ +- struct mc_command cmd = { 0 }; +- struct dpbp_cmd_open *cmd_params; +- int err; +- +- /* prepare command */ +- cmd.header = mc_encode_cmd_header(DPBP_CMDID_OPEN, +- cmd_flags, 0); +- cmd_params = (struct dpbp_cmd_open *)cmd.params; +- cmd_params->dpbp_id = cpu_to_le32(dpbp_id); +- +- /* send command to mc*/ +- err = mc_send_command(mc_io, &cmd); +- if (err) +- return err; +- +- /* retrieve response parameters */ +- *token = mc_cmd_hdr_read_token(&cmd); +- +- return err; +-} +-EXPORT_SYMBOL(dpbp_open); +- +-/** +- * dpbp_close() - Close the control session of the object +- * @mc_io: Pointer to MC portal's I/O object +- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' +- * @token: Token of DPBP object +- * +- * After this function is called, no further operations are +- * allowed on the object without opening a new control session. +- * +- * Return: '0' on Success; Error code otherwise. +- */ +-int dpbp_close(struct fsl_mc_io *mc_io, +- u32 cmd_flags, +- u16 token) +-{ +- struct mc_command cmd = { 0 }; +- +- /* prepare command */ +- cmd.header = mc_encode_cmd_header(DPBP_CMDID_CLOSE, cmd_flags, +- token); +- +- /* send command to mc*/ +- return mc_send_command(mc_io, &cmd); +-} +-EXPORT_SYMBOL(dpbp_close); +- +-/** +- * dpbp_enable() - Enable the DPBP. +- * @mc_io: Pointer to MC portal's I/O object +- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' +- * @token: Token of DPBP object +- * +- * Return: '0' on Success; Error code otherwise. +- */ +-int dpbp_enable(struct fsl_mc_io *mc_io, +- u32 cmd_flags, +- u16 token) +-{ +- struct mc_command cmd = { 0 }; +- +- /* prepare command */ +- cmd.header = mc_encode_cmd_header(DPBP_CMDID_ENABLE, cmd_flags, +- token); +- +- /* send command to mc*/ +- return mc_send_command(mc_io, &cmd); +-} +-EXPORT_SYMBOL(dpbp_enable); +- +-/** +- * dpbp_disable() - Disable the DPBP. +- * @mc_io: Pointer to MC portal's I/O object +- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' +- * @token: Token of DPBP object +- * +- * Return: '0' on Success; Error code otherwise. +- */ +-int dpbp_disable(struct fsl_mc_io *mc_io, +- u32 cmd_flags, +- u16 token) +-{ +- struct mc_command cmd = { 0 }; +- +- /* prepare command */ +- cmd.header = mc_encode_cmd_header(DPBP_CMDID_DISABLE, +- cmd_flags, token); +- +- /* send command to mc*/ +- return mc_send_command(mc_io, &cmd); +-} +-EXPORT_SYMBOL(dpbp_disable); +- +-/** +- * dpbp_is_enabled() - Check if the DPBP is enabled. +- * @mc_io: Pointer to MC portal's I/O object +- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' +- * @token: Token of DPBP object +- * @en: Returns '1' if object is enabled; '0' otherwise +- * +- * Return: '0' on Success; Error code otherwise. +- */ +-int dpbp_is_enabled(struct fsl_mc_io *mc_io, +- u32 cmd_flags, +- u16 token, +- int *en) +-{ +- struct mc_command cmd = { 0 }; +- struct dpbp_rsp_is_enabled *rsp_params; +- int err; +- /* prepare command */ +- cmd.header = mc_encode_cmd_header(DPBP_CMDID_IS_ENABLED, cmd_flags, +- token); +- +- /* send command to mc*/ +- err = mc_send_command(mc_io, &cmd); +- if (err) +- return err; +- +- /* retrieve response parameters */ +- rsp_params = (struct dpbp_rsp_is_enabled *)cmd.params; +- *en = rsp_params->enabled & DPBP_ENABLE; +- +- return 0; +-} +-EXPORT_SYMBOL(dpbp_is_enabled); +- +-/** +- * dpbp_reset() - Reset the DPBP, returns the object to initial state. +- * @mc_io: Pointer to MC portal's I/O object +- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' +- * @token: Token of DPBP object +- * +- * Return: '0' on Success; Error code otherwise. +- */ +-int dpbp_reset(struct fsl_mc_io *mc_io, +- u32 cmd_flags, +- u16 token) +-{ +- struct mc_command cmd = { 0 }; +- +- /* prepare command */ +- cmd.header = mc_encode_cmd_header(DPBP_CMDID_RESET, +- cmd_flags, token); +- +- /* send command to mc*/ +- return mc_send_command(mc_io, &cmd); +-} +-EXPORT_SYMBOL(dpbp_reset); +- +-/** +- * dpbp_get_attributes - Retrieve DPBP attributes. +- * +- * @mc_io: Pointer to MC portal's I/O object +- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' +- * @token: Token of DPBP object +- * @attr: Returned object's attributes +- * +- * Return: '0' on Success; Error code otherwise. +- */ +-int dpbp_get_attributes(struct fsl_mc_io *mc_io, +- u32 cmd_flags, +- u16 token, +- struct dpbp_attr *attr) +-{ +- struct mc_command cmd = { 0 }; +- struct dpbp_rsp_get_attributes *rsp_params; +- int err; +- +- /* prepare command */ +- cmd.header = mc_encode_cmd_header(DPBP_CMDID_GET_ATTR, +- cmd_flags, token); +- +- /* send command to mc*/ +- err = mc_send_command(mc_io, &cmd); +- if (err) +- return err; +- +- /* retrieve response parameters */ +- rsp_params = (struct dpbp_rsp_get_attributes *)cmd.params; +- attr->bpid = le16_to_cpu(rsp_params->bpid); +- attr->id = le32_to_cpu(rsp_params->id); +- +- return 0; +-} +-EXPORT_SYMBOL(dpbp_get_attributes); +- +-/** +- * dpbp_get_api_version - Get Data Path Buffer Pool API version +- * @mc_io: Pointer to Mc portal's I/O object +- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' +- * @major_ver: Major version of Buffer Pool API +- * @minor_ver: Minor version of Buffer Pool API +- * +- * Return: '0' on Success; Error code otherwise. +- */ +-int dpbp_get_api_version(struct fsl_mc_io *mc_io, +- u32 cmd_flags, +- u16 *major_ver, +- u16 *minor_ver) +-{ +- struct mc_command cmd = { 0 }; +- int err; +- +- /* prepare command */ +- cmd.header = mc_encode_cmd_header(DPBP_CMDID_GET_API_VERSION, +- cmd_flags, 0); +- +- /* send command to mc */ +- err = mc_send_command(mc_io, &cmd); +- if (err) +- return err; +- +- /* retrieve response parameters */ +- mc_cmd_read_api_version(&cmd, major_ver, minor_ver); +- +- return 0; +-} +-EXPORT_SYMBOL(dpbp_get_api_version); +--- /dev/null ++++ b/drivers/bus/fsl-mc/dpbp.c +@@ -0,0 +1,186 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) ++/* ++ * Copyright 2013-2016 Freescale Semiconductor Inc. ++ * ++ */ ++#include <linux/kernel.h> ++#include <linux/fsl/mc.h> ++#include <linux/fsl/mc.h> ++ ++#include "fsl-mc-private.h" ++ ++/** ++ * dpbp_open() - Open a control session for the specified object. ++ * @mc_io: Pointer to MC portal's I/O object ++ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' ++ * @dpbp_id: DPBP unique ID ++ * @token: Returned token; use in subsequent API calls ++ * ++ * This function can be used to open a control session for an ++ * already created object; an object may have been declared in ++ * the DPL or by calling the dpbp_create function. ++ * This function returns a unique authentication token, ++ * associated with the specific object ID and the specific MC ++ * portal; this token must be used in all subsequent commands for ++ * this specific object ++ * ++ * Return: '0' on Success; Error code otherwise. ++ */ ++int dpbp_open(struct fsl_mc_io *mc_io, ++ u32 cmd_flags, ++ int dpbp_id, ++ u16 *token) ++{ ++ struct fsl_mc_command cmd = { 0 }; ++ struct dpbp_cmd_open *cmd_params; ++ int err; ++ ++ /* prepare command */ ++ cmd.header = mc_encode_cmd_header(DPBP_CMDID_OPEN, ++ cmd_flags, 0); ++ cmd_params = (struct dpbp_cmd_open *)cmd.params; ++ cmd_params->dpbp_id = cpu_to_le32(dpbp_id); ++ ++ /* send command to mc*/ ++ err = mc_send_command(mc_io, &cmd); ++ if (err) ++ return err; ++ ++ /* retrieve response parameters */ ++ *token = mc_cmd_hdr_read_token(&cmd); ++ ++ return err; ++} ++EXPORT_SYMBOL_GPL(dpbp_open); ++ ++/** ++ * dpbp_close() - Close the control session of the object ++ * @mc_io: Pointer to MC portal's I/O object ++ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' ++ * @token: Token of DPBP object ++ * ++ * After this function is called, no further operations are ++ * allowed on the object without opening a new control session. ++ * ++ * Return: '0' on Success; Error code otherwise. ++ */ ++int dpbp_close(struct fsl_mc_io *mc_io, ++ u32 cmd_flags, ++ u16 token) ++{ ++ struct fsl_mc_command cmd = { 0 }; ++ ++ /* prepare command */ ++ cmd.header = mc_encode_cmd_header(DPBP_CMDID_CLOSE, cmd_flags, ++ token); ++ ++ /* send command to mc*/ ++ return mc_send_command(mc_io, &cmd); ++} ++EXPORT_SYMBOL_GPL(dpbp_close); ++ ++/** ++ * dpbp_enable() - Enable the DPBP. ++ * @mc_io: Pointer to MC portal's I/O object ++ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' ++ * @token: Token of DPBP object ++ * ++ * Return: '0' on Success; Error code otherwise. ++ */ ++int dpbp_enable(struct fsl_mc_io *mc_io, ++ u32 cmd_flags, ++ u16 token) ++{ ++ struct fsl_mc_command cmd = { 0 }; ++ ++ /* prepare command */ ++ cmd.header = mc_encode_cmd_header(DPBP_CMDID_ENABLE, cmd_flags, ++ token); ++ ++ /* send command to mc*/ ++ return mc_send_command(mc_io, &cmd); ++} ++EXPORT_SYMBOL_GPL(dpbp_enable); ++ ++/** ++ * dpbp_disable() - Disable the DPBP. ++ * @mc_io: Pointer to MC portal's I/O object ++ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' ++ * @token: Token of DPBP object ++ * ++ * Return: '0' on Success; Error code otherwise. ++ */ ++int dpbp_disable(struct fsl_mc_io *mc_io, ++ u32 cmd_flags, ++ u16 token) ++{ ++ struct fsl_mc_command cmd = { 0 }; ++ ++ /* prepare command */ ++ cmd.header = mc_encode_cmd_header(DPBP_CMDID_DISABLE, ++ cmd_flags, token); ++ ++ /* send command to mc*/ ++ return mc_send_command(mc_io, &cmd); ++} ++EXPORT_SYMBOL_GPL(dpbp_disable); ++ ++/** ++ * dpbp_reset() - Reset the DPBP, returns the object to initial state. ++ * @mc_io: Pointer to MC portal's I/O object ++ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' ++ * @token: Token of DPBP object ++ * ++ * Return: '0' on Success; Error code otherwise. ++ */ ++int dpbp_reset(struct fsl_mc_io *mc_io, ++ u32 cmd_flags, ++ u16 token) ++{ ++ struct fsl_mc_command cmd = { 0 }; ++ ++ /* prepare command */ ++ cmd.header = mc_encode_cmd_header(DPBP_CMDID_RESET, ++ cmd_flags, token); ++ ++ /* send command to mc*/ ++ return mc_send_command(mc_io, &cmd); ++} ++EXPORT_SYMBOL_GPL(dpbp_reset); ++ ++/** ++ * dpbp_get_attributes - Retrieve DPBP attributes. ++ * ++ * @mc_io: Pointer to MC portal's I/O object ++ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' ++ * @token: Token of DPBP object ++ * @attr: Returned object's attributes ++ * ++ * Return: '0' on Success; Error code otherwise. ++ */ ++int dpbp_get_attributes(struct fsl_mc_io *mc_io, ++ u32 cmd_flags, ++ u16 token, ++ struct dpbp_attr *attr) ++{ ++ struct fsl_mc_command cmd = { 0 }; ++ struct dpbp_rsp_get_attributes *rsp_params; ++ int err; ++ ++ /* prepare command */ ++ cmd.header = mc_encode_cmd_header(DPBP_CMDID_GET_ATTR, ++ cmd_flags, token); ++ ++ /* send command to mc*/ ++ err = mc_send_command(mc_io, &cmd); ++ if (err) ++ return err; ++ ++ /* retrieve response parameters */ ++ rsp_params = (struct dpbp_rsp_get_attributes *)cmd.params; ++ attr->bpid = le16_to_cpu(rsp_params->bpid); ++ attr->id = le32_to_cpu(rsp_params->id); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(dpbp_get_attributes); +--- a/drivers/staging/fsl-mc/bus/dpcon.c ++++ /dev/null +@@ -1,291 +0,0 @@ +-// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +-/* +- * Copyright 2013-2016 Freescale Semiconductor Inc. +- * +- */ +-#include <linux/kernel.h> +-#include "../include/mc.h" +-#include "../include/dpcon.h" +- +-#include "dpcon-cmd.h" +- +-/** +- * dpcon_open() - Open a control session for the specified object +- * @mc_io: Pointer to MC portal's I/O object +- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' +- * @dpcon_id: DPCON unique ID +- * @token: Returned token; use in subsequent API calls +- * +- * This function can be used to open a control session for an +- * already created object; an object may have been declared in +- * the DPL or by calling the dpcon_create() function. +- * This function returns a unique authentication token, +- * associated with the specific object ID and the specific MC +- * portal; this token must be used in all subsequent commands for +- * this specific object. +- * +- * Return: '0' on Success; Error code otherwise. +- */ +-int dpcon_open(struct fsl_mc_io *mc_io, +- u32 cmd_flags, +- int dpcon_id, +- u16 *token) +-{ +- struct mc_command cmd = { 0 }; +- struct dpcon_cmd_open *dpcon_cmd; +- int err; +- +- /* prepare command */ +- cmd.header = mc_encode_cmd_header(DPCON_CMDID_OPEN, +- cmd_flags, +- 0); +- dpcon_cmd = (struct dpcon_cmd_open *)cmd.params; +- dpcon_cmd->dpcon_id = cpu_to_le32(dpcon_id); +- +- /* send command to mc*/ +- err = mc_send_command(mc_io, &cmd); +- if (err) +- return err; +- +- /* retrieve response parameters */ +- *token = mc_cmd_hdr_read_token(&cmd); +- +- return 0; +-} +-EXPORT_SYMBOL(dpcon_open); +- +-/** +- * dpcon_close() - Close the control session of the object +- * @mc_io: Pointer to MC portal's I/O object +- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' +- * @token: Token of DPCON object +- * +- * After this function is called, no further operations are +- * allowed on the object without opening a new control session. +- * +- * Return: '0' on Success; Error code otherwise. +- */ +-int dpcon_close(struct fsl_mc_io *mc_io, +- u32 cmd_flags, +- u16 token) +-{ +- struct mc_command cmd = { 0 }; +- +- /* prepare command */ +- cmd.header = mc_encode_cmd_header(DPCON_CMDID_CLOSE, +- cmd_flags, +- token); +- +- /* send command to mc*/ +- return mc_send_command(mc_io, &cmd); +-} +-EXPORT_SYMBOL(dpcon_close); +- +-/** +- * dpcon_enable() - Enable the DPCON +- * @mc_io: Pointer to MC portal's I/O object +- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' +- * @token: Token of DPCON object +- * +- * Return: '0' on Success; Error code otherwise +- */ +-int dpcon_enable(struct fsl_mc_io *mc_io, +- u32 cmd_flags, +- u16 token) +-{ +- struct mc_command cmd = { 0 }; +- +- /* prepare command */ +- cmd.header = mc_encode_cmd_header(DPCON_CMDID_ENABLE, +- cmd_flags, +- token); +- +- /* send command to mc*/ +- return mc_send_command(mc_io, &cmd); +-} +-EXPORT_SYMBOL(dpcon_enable); +- +-/** +- * dpcon_disable() - Disable the DPCON +- * @mc_io: Pointer to MC portal's I/O object +- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' +- * @token: Token of DPCON object +- * +- * Return: '0' on Success; Error code otherwise +- */ +-int dpcon_disable(struct fsl_mc_io *mc_io, +- u32 cmd_flags, +- u16 token) +-{ +- struct mc_command cmd = { 0 }; +- +- /* prepare command */ +- cmd.header = mc_encode_cmd_header(DPCON_CMDID_DISABLE, +- cmd_flags, +- token); +- +- /* send command to mc*/ +- return mc_send_command(mc_io, &cmd); +-} +-EXPORT_SYMBOL(dpcon_disable); +- +-/** +- * dpcon_is_enabled() - Check if the DPCON is enabled. +- * @mc_io: Pointer to MC portal's I/O object +- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' +- * @token: Token of DPCON object +- * @en: Returns '1' if object is enabled; '0' otherwise +- * +- * Return: '0' on Success; Error code otherwise. +- */ +-int dpcon_is_enabled(struct fsl_mc_io *mc_io, +- u32 cmd_flags, +- u16 token, +- int *en) +-{ +- struct mc_command cmd = { 0 }; +- struct dpcon_rsp_is_enabled *dpcon_rsp; +- int err; +- +- /* prepare command */ +- cmd.header = mc_encode_cmd_header(DPCON_CMDID_IS_ENABLED, +- cmd_flags, +- token); +- +- /* send command to mc*/ +- err = mc_send_command(mc_io, &cmd); +- if (err) +- return err; +- +- /* retrieve response parameters */ +- dpcon_rsp = (struct dpcon_rsp_is_enabled *)cmd.params; +- *en = dpcon_rsp->enabled & DPCON_ENABLE; +- +- return 0; +-} +-EXPORT_SYMBOL(dpcon_is_enabled); +- +-/** +- * dpcon_reset() - Reset the DPCON, returns the object to initial state. +- * @mc_io: Pointer to MC portal's I/O object +- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' +- * @token: Token of DPCON object +- * +- * Return: '0' on Success; Error code otherwise. +- */ +-int dpcon_reset(struct fsl_mc_io *mc_io, +- u32 cmd_flags, +- u16 token) +-{ +- struct mc_command cmd = { 0 }; +- +- /* prepare command */ +- cmd.header = mc_encode_cmd_header(DPCON_CMDID_RESET, +- cmd_flags, token); +- +- /* send command to mc*/ +- return mc_send_command(mc_io, &cmd); +-} +-EXPORT_SYMBOL(dpcon_reset); +- +-/** +- * dpcon_get_attributes() - Retrieve DPCON attributes. +- * @mc_io: Pointer to MC portal's I/O object +- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' +- * @token: Token of DPCON object +- * @attr: Object's attributes +- * +- * Return: '0' on Success; Error code otherwise. +- */ +-int dpcon_get_attributes(struct fsl_mc_io *mc_io, +- u32 cmd_flags, +- u16 token, +- struct dpcon_attr *attr) +-{ +- struct mc_command cmd = { 0 }; +- struct dpcon_rsp_get_attr *dpcon_rsp; +- int err; +- +- /* prepare command */ +- cmd.header = mc_encode_cmd_header(DPCON_CMDID_GET_ATTR, +- cmd_flags, +- token); +- +- /* send command to mc*/ +- err = mc_send_command(mc_io, &cmd); +- if (err) +- return err; +- +- /* retrieve response parameters */ +- dpcon_rsp = (struct dpcon_rsp_get_attr *)cmd.params; +- attr->id = le32_to_cpu(dpcon_rsp->id); +- attr->qbman_ch_id = le16_to_cpu(dpcon_rsp->qbman_ch_id); +- attr->num_priorities = dpcon_rsp->num_priorities; +- +- return 0; +-} +-EXPORT_SYMBOL(dpcon_get_attributes); +- +-/** +- * dpcon_set_notification() - Set DPCON notification destination +- * @mc_io: Pointer to MC portal's I/O object +- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' +- * @token: Token of DPCON object +- * @cfg: Notification parameters +- * +- * Return: '0' on Success; Error code otherwise +- */ +-int dpcon_set_notification(struct fsl_mc_io *mc_io, +- u32 cmd_flags, +- u16 token, +- struct dpcon_notification_cfg *cfg) +-{ +- struct mc_command cmd = { 0 }; +- struct dpcon_cmd_set_notification *dpcon_cmd; +- +- /* prepare command */ +- cmd.header = mc_encode_cmd_header(DPCON_CMDID_SET_NOTIFICATION, +- cmd_flags, +- token); +- dpcon_cmd = (struct dpcon_cmd_set_notification *)cmd.params; +- dpcon_cmd->dpio_id = cpu_to_le32(cfg->dpio_id); +- dpcon_cmd->priority = cfg->priority; +- dpcon_cmd->user_ctx = cpu_to_le64(cfg->user_ctx); +- +- /* send command to mc*/ +- return mc_send_command(mc_io, &cmd); +-} +-EXPORT_SYMBOL(dpcon_set_notification); +- +-/** +- * dpcon_get_api_version - Get Data Path Concentrator API version +- * @mc_io: Pointer to MC portal's DPCON object +- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' +- * @major_ver: Major version of DPCON API +- * @minor_ver: Minor version of DPCON API +- * +- * Return: '0' on Success; Error code otherwise +- */ +-int dpcon_get_api_version(struct fsl_mc_io *mc_io, +- u32 cmd_flags, +- u16 *major_ver, +- u16 *minor_ver) +-{ +- struct mc_command cmd = { 0 }; +- int err; +- +- /* prepare command */ +- cmd.header = mc_encode_cmd_header(DPCON_CMDID_GET_API_VERSION, +- cmd_flags, 0); +- +- /* send command to mc */ +- err = mc_send_command(mc_io, &cmd); +- if (err) +- return err; +- +- /* retrieve response parameters */ +- mc_cmd_read_api_version(&cmd, major_ver, minor_ver); +- +- return 0; +-} +-EXPORT_SYMBOL(dpcon_get_api_version); +--- /dev/null ++++ b/drivers/bus/fsl-mc/dpcon.c +@@ -0,0 +1,222 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) ++/* ++ * Copyright 2013-2016 Freescale Semiconductor Inc. ++ * ++ */ ++#include <linux/kernel.h> ++#include <linux/fsl/mc.h> ++#include <linux/fsl/mc.h> ++ ++#include "fsl-mc-private.h" ++ ++/** ++ * dpcon_open() - Open a control session for the specified object ++ * @mc_io: Pointer to MC portal's I/O object ++ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' ++ * @dpcon_id: DPCON unique ID ++ * @token: Returned token; use in subsequent API calls ++ * ++ * This function can be used to open a control session for an ++ * already created object; an object may have been declared in ++ * the DPL or by calling the dpcon_create() function. ++ * This function returns a unique authentication token, ++ * associated with the specific object ID and the specific MC ++ * portal; this token must be used in all subsequent commands for ++ * this specific object. ++ * ++ * Return: '0' on Success; Error code otherwise. ++ */ ++int dpcon_open(struct fsl_mc_io *mc_io, ++ u32 cmd_flags, ++ int dpcon_id, ++ u16 *token) ++{ ++ struct fsl_mc_command cmd = { 0 }; ++ struct dpcon_cmd_open *dpcon_cmd; ++ int err; ++ ++ /* prepare command */ ++ cmd.header = mc_encode_cmd_header(DPCON_CMDID_OPEN, ++ cmd_flags, ++ 0); ++ dpcon_cmd = (struct dpcon_cmd_open *)cmd.params; ++ dpcon_cmd->dpcon_id = cpu_to_le32(dpcon_id); ++ ++ /* send command to mc*/ ++ err = mc_send_command(mc_io, &cmd); ++ if (err) ++ return err; ++ ++ /* retrieve response parameters */ ++ *token = mc_cmd_hdr_read_token(&cmd); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(dpcon_open); ++ ++/** ++ * dpcon_close() - Close the control session of the object ++ * @mc_io: Pointer to MC portal's I/O object ++ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' ++ * @token: Token of DPCON object ++ * ++ * After this function is called, no further operations are ++ * allowed on the object without opening a new control session. ++ * ++ * Return: '0' on Success; Error code otherwise. ++ */ ++int dpcon_close(struct fsl_mc_io *mc_io, ++ u32 cmd_flags, ++ u16 token) ++{ ++ struct fsl_mc_command cmd = { 0 }; ++ ++ /* prepare command */ ++ cmd.header = mc_encode_cmd_header(DPCON_CMDID_CLOSE, ++ cmd_flags, ++ token); ++ ++ /* send command to mc*/ ++ return mc_send_command(mc_io, &cmd); ++} ++EXPORT_SYMBOL_GPL(dpcon_close); ++ ++/** ++ * dpcon_enable() - Enable the DPCON ++ * @mc_io: Pointer to MC portal's I/O object ++ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' ++ * @token: Token of DPCON object ++ * ++ * Return: '0' on Success; Error code otherwise ++ */ ++int dpcon_enable(struct fsl_mc_io *mc_io, ++ u32 cmd_flags, ++ u16 token) ++{ ++ struct fsl_mc_command cmd = { 0 }; ++ ++ /* prepare command */ ++ cmd.header = mc_encode_cmd_header(DPCON_CMDID_ENABLE, ++ cmd_flags, ++ token); ++ ++ /* send command to mc*/ ++ return mc_send_command(mc_io, &cmd); ++} ++EXPORT_SYMBOL_GPL(dpcon_enable); ++ ++/** ++ * dpcon_disable() - Disable the DPCON ++ * @mc_io: Pointer to MC portal's I/O object ++ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' ++ * @token: Token of DPCON object ++ * ++ * Return: '0' on Success; Error code otherwise ++ */ ++int dpcon_disable(struct fsl_mc_io *mc_io, ++ u32 cmd_flags, ++ u16 token) ++{ ++ struct fsl_mc_command cmd = { 0 }; ++ ++ /* prepare command */ ++ cmd.header = mc_encode_cmd_header(DPCON_CMDID_DISABLE, ++ cmd_flags, ++ token); ++ ++ /* send command to mc*/ ++ return mc_send_command(mc_io, &cmd); ++} ++EXPORT_SYMBOL_GPL(dpcon_disable); ++ ++/** ++ * dpcon_reset() - Reset the DPCON, returns the object to initial state. ++ * @mc_io: Pointer to MC portal's I/O object ++ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' ++ * @token: Token of DPCON object ++ * ++ * Return: '0' on Success; Error code otherwise. ++ */ ++int dpcon_reset(struct fsl_mc_io *mc_io, ++ u32 cmd_flags, ++ u16 token) ++{ ++ struct fsl_mc_command cmd = { 0 }; ++ ++ /* prepare command */ ++ cmd.header = mc_encode_cmd_header(DPCON_CMDID_RESET, ++ cmd_flags, token); ++ ++ /* send command to mc*/ ++ return mc_send_command(mc_io, &cmd); ++} ++EXPORT_SYMBOL_GPL(dpcon_reset); ++ ++/** ++ * dpcon_get_attributes() - Retrieve DPCON attributes. ++ * @mc_io: Pointer to MC portal's I/O object ++ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' ++ * @token: Token of DPCON object ++ * @attr: Object's attributes ++ * ++ * Return: '0' on Success; Error code otherwise. ++ */ ++int dpcon_get_attributes(struct fsl_mc_io *mc_io, ++ u32 cmd_flags, ++ u16 token, ++ struct dpcon_attr *attr) ++{ ++ struct fsl_mc_command cmd = { 0 }; ++ struct dpcon_rsp_get_attr *dpcon_rsp; ++ int err; ++ ++ /* prepare command */ ++ cmd.header = mc_encode_cmd_header(DPCON_CMDID_GET_ATTR, ++ cmd_flags, ++ token); ++ ++ /* send command to mc*/ ++ err = mc_send_command(mc_io, &cmd); ++ if (err) ++ return err; ++ ++ /* retrieve response parameters */ ++ dpcon_rsp = (struct dpcon_rsp_get_attr *)cmd.params; ++ attr->id = le32_to_cpu(dpcon_rsp->id); ++ attr->qbman_ch_id = le16_to_cpu(dpcon_rsp->qbman_ch_id); ++ attr->num_priorities = dpcon_rsp->num_priorities; ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(dpcon_get_attributes); ++ ++/** ++ * dpcon_set_notification() - Set DPCON notification destination ++ * @mc_io: Pointer to MC portal's I/O object ++ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' ++ * @token: Token of DPCON object ++ * @cfg: Notification parameters ++ * ++ * Return: '0' on Success; Error code otherwise ++ */ ++int dpcon_set_notification(struct fsl_mc_io *mc_io, ++ u32 cmd_flags, ++ u16 token, ++ struct dpcon_notification_cfg *cfg) ++{ ++ struct fsl_mc_command cmd = { 0 }; ++ struct dpcon_cmd_set_notification *dpcon_cmd; ++ ++ /* prepare command */ ++ cmd.header = mc_encode_cmd_header(DPCON_CMDID_SET_NOTIFICATION, ++ cmd_flags, ++ token); ++ dpcon_cmd = (struct dpcon_cmd_set_notification *)cmd.params; ++ dpcon_cmd->dpio_id = cpu_to_le32(cfg->dpio_id); ++ dpcon_cmd->priority = cfg->priority; ++ dpcon_cmd->user_ctx = cpu_to_le64(cfg->user_ctx); ++ ++ /* send command to mc*/ ++ return mc_send_command(mc_io, &cmd); ++} ++EXPORT_SYMBOL_GPL(dpcon_set_notification); +--- /dev/null ++++ b/drivers/bus/fsl-mc/dpmcp.c +@@ -0,0 +1,99 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) ++/* ++ * Copyright 2013-2016 Freescale Semiconductor Inc. ++ * ++ */ ++#include <linux/kernel.h> ++#include <linux/fsl/mc.h> ++ ++#include "fsl-mc-private.h" ++ ++/** ++ * dpmcp_open() - Open a control session for the specified object. ++ * @mc_io: Pointer to MC portal's I/O object ++ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' ++ * @dpmcp_id: DPMCP unique ID ++ * @token: Returned token; use in subsequent API calls ++ * ++ * This function can be used to open a control session for an ++ * already created object; an object may have been declared in ++ * the DPL or by calling the dpmcp_create function. ++ * This function returns a unique authentication token, ++ * associated with the specific object ID and the specific MC ++ * portal; this token must be used in all subsequent commands for ++ * this specific object ++ * ++ * Return: '0' on Success; Error code otherwise. ++ */ ++int dpmcp_open(struct fsl_mc_io *mc_io, ++ u32 cmd_flags, ++ int dpmcp_id, ++ u16 *token) ++{ ++ struct fsl_mc_command cmd = { 0 }; ++ struct dpmcp_cmd_open *cmd_params; ++ int err; ++ ++ /* prepare command */ ++ cmd.header = mc_encode_cmd_header(DPMCP_CMDID_OPEN, ++ cmd_flags, 0); ++ cmd_params = (struct dpmcp_cmd_open *)cmd.params; ++ cmd_params->dpmcp_id = cpu_to_le32(dpmcp_id); ++ ++ /* send command to mc*/ ++ err = mc_send_command(mc_io, &cmd); ++ if (err) ++ return err; ++ ++ /* retrieve response parameters */ ++ *token = mc_cmd_hdr_read_token(&cmd); ++ ++ return err; ++} ++ ++/** ++ * dpmcp_close() - Close the control session of the object ++ * @mc_io: Pointer to MC portal's I/O object ++ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' ++ * @token: Token of DPMCP object ++ * ++ * After this function is called, no further operations are ++ * allowed on the object without opening a new control session. ++ * ++ * Return: '0' on Success; Error code otherwise. ++ */ ++int dpmcp_close(struct fsl_mc_io *mc_io, ++ u32 cmd_flags, ++ u16 token) ++{ ++ struct fsl_mc_command cmd = { 0 }; ++ ++ /* prepare command */ ++ cmd.header = mc_encode_cmd_header(DPMCP_CMDID_CLOSE, ++ cmd_flags, token); ++ ++ /* send command to mc*/ ++ return mc_send_command(mc_io, &cmd); ++} ++ ++/** ++ * dpmcp_reset() - Reset the DPMCP, returns the object to initial state. ++ * @mc_io: Pointer to MC portal's I/O object ++ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' ++ * @token: Token of DPMCP object ++ * ++ * Return: '0' on Success; Error code otherwise. ++ */ ++int dpmcp_reset(struct fsl_mc_io *mc_io, ++ u32 cmd_flags, ++ u16 token) ++{ ++ struct fsl_mc_command cmd = { 0 }; ++ ++ /* prepare command */ ++ cmd.header = mc_encode_cmd_header(DPMCP_CMDID_RESET, ++ cmd_flags, token); ++ ++ /* send command to mc*/ ++ return mc_send_command(mc_io, &cmd); ++} +--- a/drivers/staging/fsl-mc/bus/dprc-driver.c ++++ /dev/null +@@ -1,813 +0,0 @@ +-// SPDX-License-Identifier: GPL-2.0 +-/* +- * Freescale data path resource container (DPRC) driver +- * +- * Copyright (C) 2014-2016 Freescale Semiconductor, Inc. +- * Author: German Rivera <German.Rivera@freescale.com> +- * +- */ +- +-#include <linux/module.h> +-#include <linux/slab.h> +-#include <linux/interrupt.h> +-#include <linux/msi.h> +-#include "../include/mc.h" +- +-#include "dprc-cmd.h" +-#include "fsl-mc-private.h" +- +-#define FSL_MC_DPRC_DRIVER_NAME "fsl_mc_dprc" +- +-struct fsl_mc_child_objs { +- int child_count; +- struct fsl_mc_obj_desc *child_array; +-}; +- +-static bool fsl_mc_device_match(struct fsl_mc_device *mc_dev, +- struct fsl_mc_obj_desc *obj_desc) +-{ +- return mc_dev->obj_desc.id == obj_desc->id && +- strcmp(mc_dev->obj_desc.type, obj_desc->type) == 0; +- +-} +- +-static int __fsl_mc_device_remove_if_not_in_mc(struct device *dev, void *data) +-{ +- int i; +- struct fsl_mc_child_objs *objs; +- struct fsl_mc_device *mc_dev; +- +- WARN_ON(!dev); +- WARN_ON(!data); +- mc_dev = to_fsl_mc_device(dev); +- objs = data; +- +- for (i = 0; i < objs->child_count; i++) { +- struct fsl_mc_obj_desc *obj_desc = &objs->child_array[i]; +- +- if (strlen(obj_desc->type) != 0 && +- fsl_mc_device_match(mc_dev, obj_desc)) +- break; +- } +- +- if (i == objs->child_count) +- fsl_mc_device_remove(mc_dev); +- +- return 0; +-} +- +-static int __fsl_mc_device_remove(struct device *dev, void *data) +-{ +- WARN_ON(!dev); +- WARN_ON(data); +- fsl_mc_device_remove(to_fsl_mc_device(dev)); +- return 0; +-} +- +-/** +- * dprc_remove_devices - Removes devices for objects removed from a DPRC +- * +- * @mc_bus_dev: pointer to the fsl-mc device that represents a DPRC object +- * @obj_desc_array: array of object descriptors for child objects currently +- * present in the DPRC in the MC. +- * @num_child_objects_in_mc: number of entries in obj_desc_array +- * +- * Synchronizes the state of the Linux bus driver with the actual state of +- * the MC by removing devices that represent MC objects that have +- * been dynamically removed in the physical DPRC. +- */ +-static void dprc_remove_devices(struct fsl_mc_device *mc_bus_dev, +- struct fsl_mc_obj_desc *obj_desc_array, +- int num_child_objects_in_mc) +-{ +- if (num_child_objects_in_mc != 0) { +- /* +- * Remove child objects that are in the DPRC in Linux, +- * but not in the MC: +- */ +- struct fsl_mc_child_objs objs; +- +- objs.child_count = num_child_objects_in_mc; +- objs.child_array = obj_desc_array; +- device_for_each_child(&mc_bus_dev->dev, &objs, +- __fsl_mc_device_remove_if_not_in_mc); +- } else { +- /* +- * There are no child objects for this DPRC in the MC. +- * So, remove all the child devices from Linux: +- */ +- device_for_each_child(&mc_bus_dev->dev, NULL, +- __fsl_mc_device_remove); +- } +-} +- +-static int __fsl_mc_device_match(struct device *dev, void *data) +-{ +- struct fsl_mc_obj_desc *obj_desc = data; +- struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); +- +- return fsl_mc_device_match(mc_dev, obj_desc); +-} +- +-static struct fsl_mc_device *fsl_mc_device_lookup(struct fsl_mc_obj_desc +- *obj_desc, +- struct fsl_mc_device +- *mc_bus_dev) +-{ +- struct device *dev; +- +- dev = device_find_child(&mc_bus_dev->dev, obj_desc, +- __fsl_mc_device_match); +- +- return dev ? to_fsl_mc_device(dev) : NULL; +-} +- +-/** +- * check_plugged_state_change - Check change in an MC object's plugged state +- * +- * @mc_dev: pointer to the fsl-mc device for a given MC object +- * @obj_desc: pointer to the MC object's descriptor in the MC +- * +- * If the plugged state has changed from unplugged to plugged, the fsl-mc +- * device is bound to the corresponding device driver. +- * If the plugged state has changed from plugged to unplugged, the fsl-mc +- * device is unbound from the corresponding device driver. +- */ +-static void check_plugged_state_change(struct fsl_mc_device *mc_dev, +- struct fsl_mc_obj_desc *obj_desc) +-{ +- int error; +- u32 plugged_flag_at_mc = +- obj_desc->state & FSL_MC_OBJ_STATE_PLUGGED; +- +- if (plugged_flag_at_mc != +- (mc_dev->obj_desc.state & FSL_MC_OBJ_STATE_PLUGGED)) { +- if (plugged_flag_at_mc) { +- mc_dev->obj_desc.state |= FSL_MC_OBJ_STATE_PLUGGED; +- error = device_attach(&mc_dev->dev); +- if (error < 0) { +- dev_err(&mc_dev->dev, +- "device_attach() failed: %d\n", +- error); +- } +- } else { +- mc_dev->obj_desc.state &= ~FSL_MC_OBJ_STATE_PLUGGED; +- device_release_driver(&mc_dev->dev); +- } +- } +-} +- +-/** +- * dprc_add_new_devices - Adds devices to the logical bus for a DPRC +- * +- * @mc_bus_dev: pointer to the fsl-mc device that represents a DPRC object +- * @obj_desc_array: array of device descriptors for child devices currently +- * present in the physical DPRC. +- * @num_child_objects_in_mc: number of entries in obj_desc_array +- * +- * Synchronizes the state of the Linux bus driver with the actual +- * state of the MC by adding objects that have been newly discovered +- * in the physical DPRC. +- */ +-static void dprc_add_new_devices(struct fsl_mc_device *mc_bus_dev, +- struct fsl_mc_obj_desc *obj_desc_array, +- int num_child_objects_in_mc) +-{ +- int error; +- int i; +- +- for (i = 0; i < num_child_objects_in_mc; i++) { +- struct fsl_mc_device *child_dev; +- struct fsl_mc_obj_desc *obj_desc = &obj_desc_array[i]; +- +- if (strlen(obj_desc->type) == 0) +- continue; +- +- /* +- * Check if device is already known to Linux: +- */ +- child_dev = fsl_mc_device_lookup(obj_desc, mc_bus_dev); +- if (child_dev) { +- check_plugged_state_change(child_dev, obj_desc); +- put_device(&child_dev->dev); +- continue; +- } +- +- error = fsl_mc_device_add(obj_desc, NULL, &mc_bus_dev->dev, +- &child_dev); +- if (error < 0) +- continue; +- } +-} +- +-/** +- * dprc_scan_objects - Discover objects in a DPRC +- * +- * @mc_bus_dev: pointer to the fsl-mc device that represents a DPRC object +- * @total_irq_count: total number of IRQs needed by objects in the DPRC. +- * +- * Detects objects added and removed from a DPRC and synchronizes the +- * state of the Linux bus driver, MC by adding and removing +- * devices accordingly. +- * Two types of devices can be found in a DPRC: allocatable objects (e.g., +- * dpbp, dpmcp) and non-allocatable devices (e.g., dprc, dpni). +- * All allocatable devices needed to be probed before all non-allocatable +- * devices, to ensure that device drivers for non-allocatable +- * devices can allocate any type of allocatable devices. +- * That is, we need to ensure that the corresponding resource pools are +- * populated before they can get allocation requests from probe callbacks +- * of the device drivers for the non-allocatable devices. +- */ +-static int dprc_scan_objects(struct fsl_mc_device *mc_bus_dev, +- unsigned int *total_irq_count) +-{ +- int num_child_objects; +- int dprc_get_obj_failures; +- int error; +- unsigned int irq_count = mc_bus_dev->obj_desc.irq_count; +- struct fsl_mc_obj_desc *child_obj_desc_array = NULL; +- +- error = dprc_get_obj_count(mc_bus_dev->mc_io, +- 0, +- mc_bus_dev->mc_handle, +- &num_child_objects); +- if (error < 0) { +- dev_err(&mc_bus_dev->dev, "dprc_get_obj_count() failed: %d\n", +- error); +- return error; +- } +- +- if (num_child_objects != 0) { +- int i; +- +- child_obj_desc_array = +- devm_kmalloc_array(&mc_bus_dev->dev, num_child_objects, +- sizeof(*child_obj_desc_array), +- GFP_KERNEL); +- if (!child_obj_desc_array) +- return -ENOMEM; +- +- /* +- * Discover objects currently present in the physical DPRC: +- */ +- dprc_get_obj_failures = 0; +- for (i = 0; i < num_child_objects; i++) { +- struct fsl_mc_obj_desc *obj_desc = +- &child_obj_desc_array[i]; +- +- error = dprc_get_obj(mc_bus_dev->mc_io, +- 0, +- mc_bus_dev->mc_handle, +- i, obj_desc); +- if (error < 0) { +- dev_err(&mc_bus_dev->dev, +- "dprc_get_obj(i=%d) failed: %d\n", +- i, error); +- /* +- * Mark the obj entry as "invalid", by using the +- * empty string as obj type: +- */ +- obj_desc->type[0] = '\0'; +- obj_desc->id = error; +- dprc_get_obj_failures++; +- continue; +- } +- +- /* +- * add a quirk for all versions of dpsec < 4.0...none +- * are coherent regardless of what the MC reports. +- */ +- if ((strcmp(obj_desc->type, "dpseci") == 0) && +- (obj_desc->ver_major < 4)) +- obj_desc->flags |= +- FSL_MC_OBJ_FLAG_NO_MEM_SHAREABILITY; +- +- irq_count += obj_desc->irq_count; +- dev_dbg(&mc_bus_dev->dev, +- "Discovered object: type %s, id %d\n", +- obj_desc->type, obj_desc->id); +- } +- +- if (dprc_get_obj_failures != 0) { +- dev_err(&mc_bus_dev->dev, +- "%d out of %d devices could not be retrieved\n", +- dprc_get_obj_failures, num_child_objects); +- } +- } +- +- *total_irq_count = irq_count; +- dprc_remove_devices(mc_bus_dev, child_obj_desc_array, +- num_child_objects); +- +- dprc_add_new_devices(mc_bus_dev, child_obj_desc_array, +- num_child_objects); +- +- if (child_obj_desc_array) +- devm_kfree(&mc_bus_dev->dev, child_obj_desc_array); +- +- return 0; +-} +- +-/** +- * dprc_scan_container - Scans a physical DPRC and synchronizes Linux bus state +- * +- * @mc_bus_dev: pointer to the fsl-mc device that represents a DPRC object +- * +- * Scans the physical DPRC and synchronizes the state of the Linux +- * bus driver with the actual state of the MC by adding and removing +- * devices as appropriate. +- */ +-static int dprc_scan_container(struct fsl_mc_device *mc_bus_dev) +-{ +- int error; +- unsigned int irq_count; +- struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev); +- +- fsl_mc_init_all_resource_pools(mc_bus_dev); +- +- /* +- * Discover objects in the DPRC: +- */ +- mutex_lock(&mc_bus->scan_mutex); +- error = dprc_scan_objects(mc_bus_dev, &irq_count); +- mutex_unlock(&mc_bus->scan_mutex); +- if (error < 0) +- goto error; +- +- if (dev_get_msi_domain(&mc_bus_dev->dev) && !mc_bus->irq_resources) { +- if (irq_count > FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS) { +- dev_warn(&mc_bus_dev->dev, +- "IRQs needed (%u) exceed IRQs preallocated (%u)\n", +- irq_count, FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS); +- } +- +- error = fsl_mc_populate_irq_pool( +- mc_bus, +- FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS); +- if (error < 0) +- goto error; +- } +- +- return 0; +-error: +- fsl_mc_cleanup_all_resource_pools(mc_bus_dev); +- return error; +-} +- +-/** +- * dprc_irq0_handler - Regular ISR for DPRC interrupt 0 +- * +- * @irq: IRQ number of the interrupt being handled +- * @arg: Pointer to device structure +- */ +-static irqreturn_t dprc_irq0_handler(int irq_num, void *arg) +-{ +- return IRQ_WAKE_THREAD; +-} +- +-/** +- * dprc_irq0_handler_thread - Handler thread function for DPRC interrupt 0 +- * +- * @irq: IRQ number of the interrupt being handled +- * @arg: Pointer to device structure +- */ +-static irqreturn_t dprc_irq0_handler_thread(int irq_num, void *arg) +-{ +- int error; +- u32 status; +- struct device *dev = arg; +- struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); +- struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev); +- struct fsl_mc_io *mc_io = mc_dev->mc_io; +- struct msi_desc *msi_desc = mc_dev->irqs[0]->msi_desc; +- +- dev_dbg(dev, "DPRC IRQ %d triggered on CPU %u\n", +- irq_num, smp_processor_id()); +- +- if (WARN_ON(!(mc_dev->flags & FSL_MC_IS_DPRC))) +- return IRQ_HANDLED; +- +- mutex_lock(&mc_bus->scan_mutex); +- if (WARN_ON(!msi_desc || msi_desc->irq != (u32)irq_num)) +- goto out; +- +- status = 0; +- error = dprc_get_irq_status(mc_io, 0, mc_dev->mc_handle, 0, +- &status); +- if (error < 0) { +- dev_err(dev, +- "dprc_get_irq_status() failed: %d\n", error); +- goto out; +- } +- +- error = dprc_clear_irq_status(mc_io, 0, mc_dev->mc_handle, 0, +- status); +- if (error < 0) { +- dev_err(dev, +- "dprc_clear_irq_status() failed: %d\n", error); +- goto out; +- } +- +- if (status & (DPRC_IRQ_EVENT_OBJ_ADDED | +- DPRC_IRQ_EVENT_OBJ_REMOVED | +- DPRC_IRQ_EVENT_CONTAINER_DESTROYED | +- DPRC_IRQ_EVENT_OBJ_DESTROYED | +- DPRC_IRQ_EVENT_OBJ_CREATED)) { +- unsigned int irq_count; +- +- error = dprc_scan_objects(mc_dev, &irq_count); +- if (error < 0) { +- /* +- * If the error is -ENXIO, we ignore it, as it indicates +- * that the object scan was aborted, as we detected that +- * an object was removed from the DPRC in the MC, while +- * we were scanning the DPRC. +- */ +- if (error != -ENXIO) { +- dev_err(dev, "dprc_scan_objects() failed: %d\n", +- error); +- } +- +- goto out; +- } +- +- if (irq_count > FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS) { +- dev_warn(dev, +- "IRQs needed (%u) exceed IRQs preallocated (%u)\n", +- irq_count, FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS); +- } +- } +- +-out: +- mutex_unlock(&mc_bus->scan_mutex); +- return IRQ_HANDLED; +-} +- +-/* +- * Disable and clear interrupt for a given DPRC object +- */ +-static int disable_dprc_irq(struct fsl_mc_device *mc_dev) +-{ +- int error; +- struct fsl_mc_io *mc_io = mc_dev->mc_io; +- +- WARN_ON(mc_dev->obj_desc.irq_count != 1); +- +- /* +- * Disable generation of interrupt, while we configure it: +- */ +- error = dprc_set_irq_enable(mc_io, 0, mc_dev->mc_handle, 0, 0); +- if (error < 0) { +- dev_err(&mc_dev->dev, +- "Disabling DPRC IRQ failed: dprc_set_irq_enable() failed: %d\n", +- error); +- return error; +- } +- +- /* +- * Disable all interrupt causes for the interrupt: +- */ +- error = dprc_set_irq_mask(mc_io, 0, mc_dev->mc_handle, 0, 0x0); +- if (error < 0) { +- dev_err(&mc_dev->dev, +- "Disabling DPRC IRQ failed: dprc_set_irq_mask() failed: %d\n", +- error); +- return error; +- } +- +- /* +- * Clear any leftover interrupts: +- */ +- error = dprc_clear_irq_status(mc_io, 0, mc_dev->mc_handle, 0, ~0x0U); +- if (error < 0) { +- dev_err(&mc_dev->dev, +- "Disabling DPRC IRQ failed: dprc_clear_irq_status() failed: %d\n", +- error); +- return error; +- } +- +- return 0; +-} +- +-static int register_dprc_irq_handler(struct fsl_mc_device *mc_dev) +-{ +- int error; +- struct fsl_mc_device_irq *irq = mc_dev->irqs[0]; +- +- WARN_ON(mc_dev->obj_desc.irq_count != 1); +- +- /* +- * NOTE: devm_request_threaded_irq() invokes the device-specific +- * function that programs the MSI physically in the device +- */ +- error = devm_request_threaded_irq(&mc_dev->dev, +- irq->msi_desc->irq, +- dprc_irq0_handler, +- dprc_irq0_handler_thread, +- IRQF_NO_SUSPEND | IRQF_ONESHOT, +- dev_name(&mc_dev->dev), +- &mc_dev->dev); +- if (error < 0) { +- dev_err(&mc_dev->dev, +- "devm_request_threaded_irq() failed: %d\n", +- error); +- return error; +- } +- +- return 0; +-} +- +-static int enable_dprc_irq(struct fsl_mc_device *mc_dev) +-{ +- int error; +- +- /* +- * Enable all interrupt causes for the interrupt: +- */ +- error = dprc_set_irq_mask(mc_dev->mc_io, 0, mc_dev->mc_handle, 0, +- ~0x0u); +- if (error < 0) { +- dev_err(&mc_dev->dev, +- "Enabling DPRC IRQ failed: dprc_set_irq_mask() failed: %d\n", +- error); +- +- return error; +- } +- +- /* +- * Enable generation of the interrupt: +- */ +- error = dprc_set_irq_enable(mc_dev->mc_io, 0, mc_dev->mc_handle, 0, 1); +- if (error < 0) { +- dev_err(&mc_dev->dev, +- "Enabling DPRC IRQ failed: dprc_set_irq_enable() failed: %d\n", +- error); +- +- return error; +- } +- +- return 0; +-} +- +-/* +- * Setup interrupt for a given DPRC device +- */ +-static int dprc_setup_irq(struct fsl_mc_device *mc_dev) +-{ +- int error; +- +- error = fsl_mc_allocate_irqs(mc_dev); +- if (error < 0) +- return error; +- +- error = disable_dprc_irq(mc_dev); +- if (error < 0) +- goto error_free_irqs; +- +- error = register_dprc_irq_handler(mc_dev); +- if (error < 0) +- goto error_free_irqs; +- +- error = enable_dprc_irq(mc_dev); +- if (error < 0) +- goto error_free_irqs; +- +- return 0; +- +-error_free_irqs: +- fsl_mc_free_irqs(mc_dev); +- return error; +-} +- +-/** +- * dprc_probe - callback invoked when a DPRC is being bound to this driver +- * +- * @mc_dev: Pointer to fsl-mc device representing a DPRC +- * +- * It opens the physical DPRC in the MC. +- * It scans the DPRC to discover the MC objects contained in it. +- * It creates the interrupt pool for the MC bus associated with the DPRC. +- * It configures the interrupts for the DPRC device itself. +- */ +-static int dprc_probe(struct fsl_mc_device *mc_dev) +-{ +- int error; +- size_t region_size; +- struct device *parent_dev = mc_dev->dev.parent; +- struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev); +- bool mc_io_created = false; +- bool msi_domain_set = false; +- u16 major_ver, minor_ver; +- +- if (WARN_ON(strcmp(mc_dev->obj_desc.type, "dprc") != 0)) +- return -EINVAL; +- +- if (WARN_ON(dev_get_msi_domain(&mc_dev->dev))) +- return -EINVAL; +- +- if (!mc_dev->mc_io) { +- /* +- * This is a child DPRC: +- */ +- if (WARN_ON(!dev_is_fsl_mc(parent_dev))) +- return -EINVAL; +- +- if (WARN_ON(mc_dev->obj_desc.region_count == 0)) +- return -EINVAL; +- +- region_size = resource_size(mc_dev->regions); +- +- error = fsl_create_mc_io(&mc_dev->dev, +- mc_dev->regions[0].start, +- region_size, +- NULL, +- FSL_MC_IO_ATOMIC_CONTEXT_PORTAL, +- &mc_dev->mc_io); +- if (error < 0) +- return error; +- +- mc_io_created = true; +- +- /* +- * Inherit parent MSI domain: +- */ +- dev_set_msi_domain(&mc_dev->dev, +- dev_get_msi_domain(parent_dev)); +- msi_domain_set = true; +- } else { +- /* +- * This is a root DPRC +- */ +- struct irq_domain *mc_msi_domain; +- +- if (WARN_ON(dev_is_fsl_mc(parent_dev))) +- return -EINVAL; +- +- error = fsl_mc_find_msi_domain(parent_dev, +- &mc_msi_domain); +- if (error < 0) { +- dev_warn(&mc_dev->dev, +- "WARNING: MC bus without interrupt support\n"); +- } else { +- dev_set_msi_domain(&mc_dev->dev, mc_msi_domain); +- msi_domain_set = true; +- } +- } +- +- error = dprc_open(mc_dev->mc_io, 0, mc_dev->obj_desc.id, +- &mc_dev->mc_handle); +- if (error < 0) { +- dev_err(&mc_dev->dev, "dprc_open() failed: %d\n", error); +- goto error_cleanup_msi_domain; +- } +- +- error = dprc_get_attributes(mc_dev->mc_io, 0, mc_dev->mc_handle, +- &mc_bus->dprc_attr); +- if (error < 0) { +- dev_err(&mc_dev->dev, "dprc_get_attributes() failed: %d\n", +- error); +- goto error_cleanup_open; +- } +- +- error = dprc_get_api_version(mc_dev->mc_io, 0, +- &major_ver, +- &minor_ver); +- if (error < 0) { +- dev_err(&mc_dev->dev, "dprc_get_api_version() failed: %d\n", +- error); +- goto error_cleanup_open; +- } +- +- if (major_ver < DPRC_MIN_VER_MAJOR || +- (major_ver == DPRC_MIN_VER_MAJOR && +- minor_ver < DPRC_MIN_VER_MINOR)) { +- dev_err(&mc_dev->dev, +- "ERROR: DPRC version %d.%d not supported\n", +- major_ver, minor_ver); +- error = -ENOTSUPP; +- goto error_cleanup_open; +- } +- +- mutex_init(&mc_bus->scan_mutex); +- +- /* +- * Discover MC objects in DPRC object: +- */ +- error = dprc_scan_container(mc_dev); +- if (error < 0) +- goto error_cleanup_open; +- +- /* +- * Configure interrupt for the DPRC object associated with this MC bus: +- */ +- error = dprc_setup_irq(mc_dev); +- if (error < 0) +- goto error_cleanup_open; +- +- dev_info(&mc_dev->dev, "DPRC device bound to driver"); +- return 0; +- +-error_cleanup_open: +- (void)dprc_close(mc_dev->mc_io, 0, mc_dev->mc_handle); +- +-error_cleanup_msi_domain: +- if (msi_domain_set) +- dev_set_msi_domain(&mc_dev->dev, NULL); +- +- if (mc_io_created) { +- fsl_destroy_mc_io(mc_dev->mc_io); +- mc_dev->mc_io = NULL; +- } +- +- return error; +-} +- +-/* +- * Tear down interrupt for a given DPRC object +- */ +-static void dprc_teardown_irq(struct fsl_mc_device *mc_dev) +-{ +- struct fsl_mc_device_irq *irq = mc_dev->irqs[0]; +- +- (void)disable_dprc_irq(mc_dev); +- +- devm_free_irq(&mc_dev->dev, irq->msi_desc->irq, &mc_dev->dev); +- +- fsl_mc_free_irqs(mc_dev); +-} +- +-/** +- * dprc_remove - callback invoked when a DPRC is being unbound from this driver +- * +- * @mc_dev: Pointer to fsl-mc device representing the DPRC +- * +- * It removes the DPRC's child objects from Linux (not from the MC) and +- * closes the DPRC device in the MC. +- * It tears down the interrupts that were configured for the DPRC device. +- * It destroys the interrupt pool associated with this MC bus. +- */ +-static int dprc_remove(struct fsl_mc_device *mc_dev) +-{ +- int error; +- struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev); +- +- if (WARN_ON(strcmp(mc_dev->obj_desc.type, "dprc") != 0)) +- return -EINVAL; +- if (WARN_ON(!mc_dev->mc_io)) +- return -EINVAL; +- +- if (WARN_ON(!mc_bus->irq_resources)) +- return -EINVAL; +- +- if (dev_get_msi_domain(&mc_dev->dev)) +- dprc_teardown_irq(mc_dev); +- +- device_for_each_child(&mc_dev->dev, NULL, __fsl_mc_device_remove); +- +- if (dev_get_msi_domain(&mc_dev->dev)) { +- fsl_mc_cleanup_irq_pool(mc_bus); +- dev_set_msi_domain(&mc_dev->dev, NULL); +- } +- +- fsl_mc_cleanup_all_resource_pools(mc_dev); +- +- error = dprc_close(mc_dev->mc_io, 0, mc_dev->mc_handle); +- if (error < 0) +- dev_err(&mc_dev->dev, "dprc_close() failed: %d\n", error); +- +- if (!fsl_mc_is_root_dprc(&mc_dev->dev)) { +- fsl_destroy_mc_io(mc_dev->mc_io); +- mc_dev->mc_io = NULL; +- } +- +- dev_info(&mc_dev->dev, "DPRC device unbound from driver"); +- return 0; +-} +- +-static const struct fsl_mc_device_id match_id_table[] = { +- { +- .vendor = FSL_MC_VENDOR_FREESCALE, +- .obj_type = "dprc"}, +- {.vendor = 0x0}, +-}; +- +-static struct fsl_mc_driver dprc_driver = { +- .driver = { +- .name = FSL_MC_DPRC_DRIVER_NAME, +- .owner = THIS_MODULE, +- .pm = NULL, +- }, +- .match_id_table = match_id_table, +- .probe = dprc_probe, +- .remove = dprc_remove, +-}; +- +-int __init dprc_driver_init(void) +-{ +- return fsl_mc_driver_register(&dprc_driver); +-} +- +-void dprc_driver_exit(void) +-{ +- fsl_mc_driver_unregister(&dprc_driver); +-} +--- /dev/null ++++ b/drivers/bus/fsl-mc/dprc-driver.c +@@ -0,0 +1,815 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Freescale data path resource container (DPRC) driver ++ * ++ * Copyright (C) 2014-2016 Freescale Semiconductor, Inc. ++ * Author: German Rivera <German.Rivera@freescale.com> ++ * ++ */ ++ ++#include <linux/module.h> ++#include <linux/slab.h> ++#include <linux/interrupt.h> ++#include <linux/msi.h> ++#include <linux/fsl/mc.h> ++ ++#include "fsl-mc-private.h" ++ ++#define FSL_MC_DPRC_DRIVER_NAME "fsl_mc_dprc" ++ ++struct fsl_mc_child_objs { ++ int child_count; ++ struct fsl_mc_obj_desc *child_array; ++}; ++ ++static bool fsl_mc_device_match(struct fsl_mc_device *mc_dev, ++ struct fsl_mc_obj_desc *obj_desc) ++{ ++ return mc_dev->obj_desc.id == obj_desc->id && ++ strcmp(mc_dev->obj_desc.type, obj_desc->type) == 0; ++ ++} ++ ++static int __fsl_mc_device_remove_if_not_in_mc(struct device *dev, void *data) ++{ ++ int i; ++ struct fsl_mc_child_objs *objs; ++ struct fsl_mc_device *mc_dev; ++ ++ mc_dev = to_fsl_mc_device(dev); ++ objs = data; ++ ++ for (i = 0; i < objs->child_count; i++) { ++ struct fsl_mc_obj_desc *obj_desc = &objs->child_array[i]; ++ ++ if (strlen(obj_desc->type) != 0 && ++ fsl_mc_device_match(mc_dev, obj_desc)) ++ break; ++ } ++ ++ if (i == objs->child_count) ++ fsl_mc_device_remove(mc_dev); ++ ++ return 0; ++} ++ ++static int __fsl_mc_device_remove(struct device *dev, void *data) ++{ ++ fsl_mc_device_remove(to_fsl_mc_device(dev)); ++ return 0; ++} ++ ++/** ++ * dprc_remove_devices - Removes devices for objects removed from a DPRC ++ * ++ * @mc_bus_dev: pointer to the fsl-mc device that represents a DPRC object ++ * @obj_desc_array: array of object descriptors for child objects currently ++ * present in the DPRC in the MC. ++ * @num_child_objects_in_mc: number of entries in obj_desc_array ++ * ++ * Synchronizes the state of the Linux bus driver with the actual state of ++ * the MC by removing devices that represent MC objects that have ++ * been dynamically removed in the physical DPRC. ++ */ ++static void dprc_remove_devices(struct fsl_mc_device *mc_bus_dev, ++ struct fsl_mc_obj_desc *obj_desc_array, ++ int num_child_objects_in_mc) ++{ ++ if (num_child_objects_in_mc != 0) { ++ /* ++ * Remove child objects that are in the DPRC in Linux, ++ * but not in the MC: ++ */ ++ struct fsl_mc_child_objs objs; ++ ++ objs.child_count = num_child_objects_in_mc; ++ objs.child_array = obj_desc_array; ++ device_for_each_child(&mc_bus_dev->dev, &objs, ++ __fsl_mc_device_remove_if_not_in_mc); ++ } else { ++ /* ++ * There are no child objects for this DPRC in the MC. ++ * So, remove all the child devices from Linux: ++ */ ++ device_for_each_child(&mc_bus_dev->dev, NULL, ++ __fsl_mc_device_remove); ++ } ++} ++ ++static int __fsl_mc_device_match(struct device *dev, void *data) ++{ ++ struct fsl_mc_obj_desc *obj_desc = data; ++ struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); ++ ++ return fsl_mc_device_match(mc_dev, obj_desc); ++} ++ ++static struct fsl_mc_device *fsl_mc_device_lookup(struct fsl_mc_obj_desc ++ *obj_desc, ++ struct fsl_mc_device ++ *mc_bus_dev) ++{ ++ struct device *dev; ++ ++ dev = device_find_child(&mc_bus_dev->dev, obj_desc, ++ __fsl_mc_device_match); ++ ++ return dev ? to_fsl_mc_device(dev) : NULL; ++} ++ ++/** ++ * check_plugged_state_change - Check change in an MC object's plugged state ++ * ++ * @mc_dev: pointer to the fsl-mc device for a given MC object ++ * @obj_desc: pointer to the MC object's descriptor in the MC ++ * ++ * If the plugged state has changed from unplugged to plugged, the fsl-mc ++ * device is bound to the corresponding device driver. ++ * If the plugged state has changed from plugged to unplugged, the fsl-mc ++ * device is unbound from the corresponding device driver. ++ */ ++static void check_plugged_state_change(struct fsl_mc_device *mc_dev, ++ struct fsl_mc_obj_desc *obj_desc) ++{ ++ int error; ++ u32 plugged_flag_at_mc = ++ obj_desc->state & FSL_MC_OBJ_STATE_PLUGGED; ++ ++ if (plugged_flag_at_mc != ++ (mc_dev->obj_desc.state & FSL_MC_OBJ_STATE_PLUGGED)) { ++ if (plugged_flag_at_mc) { ++ mc_dev->obj_desc.state |= FSL_MC_OBJ_STATE_PLUGGED; ++ error = device_attach(&mc_dev->dev); ++ if (error < 0) { ++ dev_err(&mc_dev->dev, ++ "device_attach() failed: %d\n", ++ error); ++ } ++ } else { ++ mc_dev->obj_desc.state &= ~FSL_MC_OBJ_STATE_PLUGGED; ++ device_release_driver(&mc_dev->dev); ++ } ++ } ++} ++ ++/** ++ * dprc_add_new_devices - Adds devices to the logical bus for a DPRC ++ * ++ * @mc_bus_dev: pointer to the fsl-mc device that represents a DPRC object ++ * @driver_override: driver override to apply to new objects found in the ++ * DPRC, or NULL, if none. ++ * @obj_desc_array: array of device descriptors for child devices currently ++ * present in the physical DPRC. ++ * @num_child_objects_in_mc: number of entries in obj_desc_array ++ * ++ * Synchronizes the state of the Linux bus driver with the actual ++ * state of the MC by adding objects that have been newly discovered ++ * in the physical DPRC. ++ */ ++static void dprc_add_new_devices(struct fsl_mc_device *mc_bus_dev, ++ const char *driver_override, ++ struct fsl_mc_obj_desc *obj_desc_array, ++ int num_child_objects_in_mc) ++{ ++ int error; ++ int i; ++ ++ for (i = 0; i < num_child_objects_in_mc; i++) { ++ struct fsl_mc_device *child_dev; ++ struct fsl_mc_obj_desc *obj_desc = &obj_desc_array[i]; ++ ++ if (strlen(obj_desc->type) == 0) ++ continue; ++ ++ /* ++ * Check if device is already known to Linux: ++ */ ++ child_dev = fsl_mc_device_lookup(obj_desc, mc_bus_dev); ++ if (child_dev) { ++ check_plugged_state_change(child_dev, obj_desc); ++ put_device(&child_dev->dev); ++ continue; ++ } ++ ++ error = fsl_mc_device_add(obj_desc, NULL, &mc_bus_dev->dev, ++ driver_override, &child_dev); ++ if (error < 0) ++ continue; ++ } ++} ++ ++/** ++ * dprc_scan_objects - Discover objects in a DPRC ++ * ++ * @mc_bus_dev: pointer to the fsl-mc device that represents a DPRC object ++ * @driver_override: driver override to apply to new objects found in the ++ * DPRC, or NULL, if none. ++ * @total_irq_count: If argument is provided the function populates the ++ * total number of IRQs created by objects in the DPRC. ++ * ++ * Detects objects added and removed from a DPRC and synchronizes the ++ * state of the Linux bus driver, MC by adding and removing ++ * devices accordingly. ++ * Two types of devices can be found in a DPRC: allocatable objects (e.g., ++ * dpbp, dpmcp) and non-allocatable devices (e.g., dprc, dpni). ++ * All allocatable devices needed to be probed before all non-allocatable ++ * devices, to ensure that device drivers for non-allocatable ++ * devices can allocate any type of allocatable devices. ++ * That is, we need to ensure that the corresponding resource pools are ++ * populated before they can get allocation requests from probe callbacks ++ * of the device drivers for the non-allocatable devices. ++ */ ++int dprc_scan_objects(struct fsl_mc_device *mc_bus_dev, ++ const char *driver_override, ++ unsigned int *total_irq_count) ++{ ++ int num_child_objects; ++ int dprc_get_obj_failures; ++ int error; ++ unsigned int irq_count = mc_bus_dev->obj_desc.irq_count; ++ struct fsl_mc_obj_desc *child_obj_desc_array = NULL; ++ struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev); ++ ++ error = dprc_get_obj_count(mc_bus_dev->mc_io, ++ 0, ++ mc_bus_dev->mc_handle, ++ &num_child_objects); ++ if (error < 0) { ++ dev_err(&mc_bus_dev->dev, "dprc_get_obj_count() failed: %d\n", ++ error); ++ return error; ++ } ++ ++ if (num_child_objects != 0) { ++ int i; ++ ++ child_obj_desc_array = ++ devm_kmalloc_array(&mc_bus_dev->dev, num_child_objects, ++ sizeof(*child_obj_desc_array), ++ GFP_KERNEL); ++ if (!child_obj_desc_array) ++ return -ENOMEM; ++ ++ /* ++ * Discover objects currently present in the physical DPRC: ++ */ ++ dprc_get_obj_failures = 0; ++ for (i = 0; i < num_child_objects; i++) { ++ struct fsl_mc_obj_desc *obj_desc = ++ &child_obj_desc_array[i]; ++ ++ error = dprc_get_obj(mc_bus_dev->mc_io, ++ 0, ++ mc_bus_dev->mc_handle, ++ i, obj_desc); ++ if (error < 0) { ++ dev_err(&mc_bus_dev->dev, ++ "dprc_get_obj(i=%d) failed: %d\n", ++ i, error); ++ /* ++ * Mark the obj entry as "invalid", by using the ++ * empty string as obj type: ++ */ ++ obj_desc->type[0] = '\0'; ++ obj_desc->id = error; ++ dprc_get_obj_failures++; ++ continue; ++ } ++ ++ /* ++ * add a quirk for all versions of dpsec < 4.0...none ++ * are coherent regardless of what the MC reports. ++ */ ++ if ((strcmp(obj_desc->type, "dpseci") == 0) && ++ (obj_desc->ver_major < 4)) ++ obj_desc->flags |= ++ FSL_MC_OBJ_FLAG_NO_MEM_SHAREABILITY; ++ ++ irq_count += obj_desc->irq_count; ++ dev_dbg(&mc_bus_dev->dev, ++ "Discovered object: type %s, id %d\n", ++ obj_desc->type, obj_desc->id); ++ } ++ ++ if (dprc_get_obj_failures != 0) { ++ dev_err(&mc_bus_dev->dev, ++ "%d out of %d devices could not be retrieved\n", ++ dprc_get_obj_failures, num_child_objects); ++ } ++ } ++ ++ /* ++ * Allocate IRQ's before binding the scanned devices with their ++ * respective drivers. ++ */ ++ if (dev_get_msi_domain(&mc_bus_dev->dev) && !mc_bus->irq_resources) { ++ if (irq_count > FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS) { ++ dev_warn(&mc_bus_dev->dev, ++ "IRQs needed (%u) exceed IRQs preallocated (%u)\n", ++ irq_count, FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS); ++ } ++ ++ error = fsl_mc_populate_irq_pool(mc_bus, ++ FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS); ++ if (error < 0) ++ return error; ++ } ++ ++ if (total_irq_count) ++ *total_irq_count = irq_count; ++ ++ dprc_remove_devices(mc_bus_dev, child_obj_desc_array, ++ num_child_objects); ++ ++ dprc_add_new_devices(mc_bus_dev, driver_override, child_obj_desc_array, ++ num_child_objects); ++ ++ if (child_obj_desc_array) ++ devm_kfree(&mc_bus_dev->dev, child_obj_desc_array); ++ ++ return 0; ++} ++ ++/** ++ * dprc_scan_container - Scans a physical DPRC and synchronizes Linux bus state ++ * ++ * @mc_bus_dev: pointer to the fsl-mc device that represents a DPRC object ++ * ++ * Scans the physical DPRC and synchronizes the state of the Linux ++ * bus driver with the actual state of the MC by adding and removing ++ * devices as appropriate. ++ */ ++static int dprc_scan_container(struct fsl_mc_device *mc_bus_dev) ++{ ++ int error; ++ struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev); ++ ++ fsl_mc_init_all_resource_pools(mc_bus_dev); ++ ++ /* ++ * Discover objects in the DPRC: ++ */ ++ mutex_lock(&mc_bus->scan_mutex); ++ error = dprc_scan_objects(mc_bus_dev, NULL, NULL); ++ mutex_unlock(&mc_bus->scan_mutex); ++ if (error < 0) { ++ fsl_mc_cleanup_all_resource_pools(mc_bus_dev); ++ return error; ++ } ++ ++ return 0; ++} ++ ++/** ++ * dprc_irq0_handler - Regular ISR for DPRC interrupt 0 ++ * ++ * @irq: IRQ number of the interrupt being handled ++ * @arg: Pointer to device structure ++ */ ++static irqreturn_t dprc_irq0_handler(int irq_num, void *arg) ++{ ++ return IRQ_WAKE_THREAD; ++} ++ ++/** ++ * dprc_irq0_handler_thread - Handler thread function for DPRC interrupt 0 ++ * ++ * @irq: IRQ number of the interrupt being handled ++ * @arg: Pointer to device structure ++ */ ++static irqreturn_t dprc_irq0_handler_thread(int irq_num, void *arg) ++{ ++ int error; ++ u32 status; ++ struct device *dev = arg; ++ struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); ++ struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev); ++ struct fsl_mc_io *mc_io = mc_dev->mc_io; ++ struct msi_desc *msi_desc = mc_dev->irqs[0]->msi_desc; ++ ++ dev_dbg(dev, "DPRC IRQ %d triggered on CPU %u\n", ++ irq_num, smp_processor_id()); ++ ++ if (!(mc_dev->flags & FSL_MC_IS_DPRC)) ++ return IRQ_HANDLED; ++ ++ mutex_lock(&mc_bus->scan_mutex); ++ if (!msi_desc || msi_desc->irq != (u32)irq_num) ++ goto out; ++ ++ status = 0; ++ error = dprc_get_irq_status(mc_io, 0, mc_dev->mc_handle, 0, ++ &status); ++ if (error < 0) { ++ dev_err(dev, ++ "dprc_get_irq_status() failed: %d\n", error); ++ goto out; ++ } ++ ++ error = dprc_clear_irq_status(mc_io, 0, mc_dev->mc_handle, 0, ++ status); ++ if (error < 0) { ++ dev_err(dev, ++ "dprc_clear_irq_status() failed: %d\n", error); ++ goto out; ++ } ++ ++ if (status & (DPRC_IRQ_EVENT_OBJ_ADDED | ++ DPRC_IRQ_EVENT_OBJ_REMOVED | ++ DPRC_IRQ_EVENT_CONTAINER_DESTROYED | ++ DPRC_IRQ_EVENT_OBJ_DESTROYED | ++ DPRC_IRQ_EVENT_OBJ_CREATED)) { ++ unsigned int irq_count; ++ ++ error = dprc_scan_objects(mc_dev, NULL, &irq_count); ++ if (error < 0) { ++ /* ++ * If the error is -ENXIO, we ignore it, as it indicates ++ * that the object scan was aborted, as we detected that ++ * an object was removed from the DPRC in the MC, while ++ * we were scanning the DPRC. ++ */ ++ if (error != -ENXIO) { ++ dev_err(dev, "dprc_scan_objects() failed: %d\n", ++ error); ++ } ++ ++ goto out; ++ } ++ ++ if (irq_count > FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS) { ++ dev_warn(dev, ++ "IRQs needed (%u) exceed IRQs preallocated (%u)\n", ++ irq_count, FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS); ++ } ++ } ++ ++out: ++ mutex_unlock(&mc_bus->scan_mutex); ++ return IRQ_HANDLED; ++} ++ ++/* ++ * Disable and clear interrupt for a given DPRC object ++ */ ++static int disable_dprc_irq(struct fsl_mc_device *mc_dev) ++{ ++ int error; ++ struct fsl_mc_io *mc_io = mc_dev->mc_io; ++ ++ /* ++ * Disable generation of interrupt, while we configure it: ++ */ ++ error = dprc_set_irq_enable(mc_io, 0, mc_dev->mc_handle, 0, 0); ++ if (error < 0) { ++ dev_err(&mc_dev->dev, ++ "Disabling DPRC IRQ failed: dprc_set_irq_enable() failed: %d\n", ++ error); ++ return error; ++ } ++ ++ /* ++ * Disable all interrupt causes for the interrupt: ++ */ ++ error = dprc_set_irq_mask(mc_io, 0, mc_dev->mc_handle, 0, 0x0); ++ if (error < 0) { ++ dev_err(&mc_dev->dev, ++ "Disabling DPRC IRQ failed: dprc_set_irq_mask() failed: %d\n", ++ error); ++ return error; ++ } ++ ++ /* ++ * Clear any leftover interrupts: ++ */ ++ error = dprc_clear_irq_status(mc_io, 0, mc_dev->mc_handle, 0, ~0x0U); ++ if (error < 0) { ++ dev_err(&mc_dev->dev, ++ "Disabling DPRC IRQ failed: dprc_clear_irq_status() failed: %d\n", ++ error); ++ return error; ++ } ++ ++ return 0; ++} ++ ++static int register_dprc_irq_handler(struct fsl_mc_device *mc_dev) ++{ ++ int error; ++ struct fsl_mc_device_irq *irq = mc_dev->irqs[0]; ++ ++ /* ++ * NOTE: devm_request_threaded_irq() invokes the device-specific ++ * function that programs the MSI physically in the device ++ */ ++ error = devm_request_threaded_irq(&mc_dev->dev, ++ irq->msi_desc->irq, ++ dprc_irq0_handler, ++ dprc_irq0_handler_thread, ++ IRQF_NO_SUSPEND | IRQF_ONESHOT, ++ dev_name(&mc_dev->dev), ++ &mc_dev->dev); ++ if (error < 0) { ++ dev_err(&mc_dev->dev, ++ "devm_request_threaded_irq() failed: %d\n", ++ error); ++ return error; ++ } ++ ++ return 0; ++} ++ ++static int enable_dprc_irq(struct fsl_mc_device *mc_dev) ++{ ++ int error; ++ ++ /* ++ * Enable all interrupt causes for the interrupt: ++ */ ++ error = dprc_set_irq_mask(mc_dev->mc_io, 0, mc_dev->mc_handle, 0, ++ ~0x0u); ++ if (error < 0) { ++ dev_err(&mc_dev->dev, ++ "Enabling DPRC IRQ failed: dprc_set_irq_mask() failed: %d\n", ++ error); ++ ++ return error; ++ } ++ ++ /* ++ * Enable generation of the interrupt: ++ */ ++ error = dprc_set_irq_enable(mc_dev->mc_io, 0, mc_dev->mc_handle, 0, 1); ++ if (error < 0) { ++ dev_err(&mc_dev->dev, ++ "Enabling DPRC IRQ failed: dprc_set_irq_enable() failed: %d\n", ++ error); ++ ++ return error; ++ } ++ ++ return 0; ++} ++ ++/* ++ * Setup interrupt for a given DPRC device ++ */ ++static int dprc_setup_irq(struct fsl_mc_device *mc_dev) ++{ ++ int error; ++ ++ error = fsl_mc_allocate_irqs(mc_dev); ++ if (error < 0) ++ return error; ++ ++ error = disable_dprc_irq(mc_dev); ++ if (error < 0) ++ goto error_free_irqs; ++ ++ error = register_dprc_irq_handler(mc_dev); ++ if (error < 0) ++ goto error_free_irqs; ++ ++ error = enable_dprc_irq(mc_dev); ++ if (error < 0) ++ goto error_free_irqs; ++ ++ return 0; ++ ++error_free_irqs: ++ fsl_mc_free_irqs(mc_dev); ++ return error; ++} ++ ++/** ++ * dprc_probe - callback invoked when a DPRC is being bound to this driver ++ * ++ * @mc_dev: Pointer to fsl-mc device representing a DPRC ++ * ++ * It opens the physical DPRC in the MC. ++ * It scans the DPRC to discover the MC objects contained in it. ++ * It creates the interrupt pool for the MC bus associated with the DPRC. ++ * It configures the interrupts for the DPRC device itself. ++ */ ++static int dprc_probe(struct fsl_mc_device *mc_dev) ++{ ++ int error; ++ size_t region_size; ++ struct device *parent_dev = mc_dev->dev.parent; ++ struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev); ++ bool mc_io_created = false; ++ bool msi_domain_set = false; ++ u16 major_ver, minor_ver; ++ ++ if (!is_fsl_mc_bus_dprc(mc_dev)) ++ return -EINVAL; ++ ++ if (dev_get_msi_domain(&mc_dev->dev)) ++ return -EINVAL; ++ ++ if (!mc_dev->mc_io) { ++ /* ++ * This is a child DPRC: ++ */ ++ if (!dev_is_fsl_mc(parent_dev)) ++ return -EINVAL; ++ ++ if (mc_dev->obj_desc.region_count == 0) ++ return -EINVAL; ++ ++ region_size = resource_size(mc_dev->regions); ++ ++ error = fsl_create_mc_io(&mc_dev->dev, ++ mc_dev->regions[0].start, ++ region_size, ++ NULL, ++ FSL_MC_IO_ATOMIC_CONTEXT_PORTAL, ++ &mc_dev->mc_io); ++ if (error < 0) ++ return error; ++ ++ mc_io_created = true; ++ ++ /* ++ * Inherit parent MSI domain: ++ */ ++ dev_set_msi_domain(&mc_dev->dev, ++ dev_get_msi_domain(parent_dev)); ++ msi_domain_set = true; ++ } else { ++ /* ++ * This is a root DPRC ++ */ ++ struct irq_domain *mc_msi_domain; ++ ++ if (dev_is_fsl_mc(parent_dev)) ++ return -EINVAL; ++ ++ error = fsl_mc_find_msi_domain(parent_dev, ++ &mc_msi_domain); ++ if (error < 0) { ++ dev_warn(&mc_dev->dev, ++ "WARNING: MC bus without interrupt support\n"); ++ } else { ++ dev_set_msi_domain(&mc_dev->dev, mc_msi_domain); ++ msi_domain_set = true; ++ } ++ } ++ ++ error = dprc_open(mc_dev->mc_io, 0, mc_dev->obj_desc.id, ++ &mc_dev->mc_handle); ++ if (error < 0) { ++ dev_err(&mc_dev->dev, "dprc_open() failed: %d\n", error); ++ goto error_cleanup_msi_domain; ++ } ++ ++ error = dprc_get_attributes(mc_dev->mc_io, 0, mc_dev->mc_handle, ++ &mc_bus->dprc_attr); ++ if (error < 0) { ++ dev_err(&mc_dev->dev, "dprc_get_attributes() failed: %d\n", ++ error); ++ goto error_cleanup_open; ++ } ++ ++ error = dprc_get_api_version(mc_dev->mc_io, 0, ++ &major_ver, ++ &minor_ver); ++ if (error < 0) { ++ dev_err(&mc_dev->dev, "dprc_get_api_version() failed: %d\n", ++ error); ++ goto error_cleanup_open; ++ } ++ ++ if (major_ver < DPRC_MIN_VER_MAJOR || ++ (major_ver == DPRC_MIN_VER_MAJOR && ++ minor_ver < DPRC_MIN_VER_MINOR)) { ++ dev_err(&mc_dev->dev, ++ "ERROR: DPRC version %d.%d not supported\n", ++ major_ver, minor_ver); ++ error = -ENOTSUPP; ++ goto error_cleanup_open; ++ } ++ ++ mutex_init(&mc_bus->scan_mutex); ++ ++ /* ++ * Discover MC objects in DPRC object: ++ */ ++ error = dprc_scan_container(mc_dev); ++ if (error < 0) ++ goto error_cleanup_open; ++ ++ /* ++ * Configure interrupt for the DPRC object associated with this MC bus: ++ */ ++ error = dprc_setup_irq(mc_dev); ++ if (error < 0) ++ goto error_cleanup_open; ++ ++ dev_info(&mc_dev->dev, "DPRC device bound to driver"); ++ return 0; ++ ++error_cleanup_open: ++ (void)dprc_close(mc_dev->mc_io, 0, mc_dev->mc_handle); ++ ++error_cleanup_msi_domain: ++ if (msi_domain_set) ++ dev_set_msi_domain(&mc_dev->dev, NULL); ++ ++ if (mc_io_created) { ++ fsl_destroy_mc_io(mc_dev->mc_io); ++ mc_dev->mc_io = NULL; ++ } ++ ++ return error; ++} ++ ++/* ++ * Tear down interrupt for a given DPRC object ++ */ ++static void dprc_teardown_irq(struct fsl_mc_device *mc_dev) ++{ ++ struct fsl_mc_device_irq *irq = mc_dev->irqs[0]; ++ ++ (void)disable_dprc_irq(mc_dev); ++ ++ devm_free_irq(&mc_dev->dev, irq->msi_desc->irq, &mc_dev->dev); ++ ++ fsl_mc_free_irqs(mc_dev); ++} ++ ++/** ++ * dprc_remove - callback invoked when a DPRC is being unbound from this driver ++ * ++ * @mc_dev: Pointer to fsl-mc device representing the DPRC ++ * ++ * It removes the DPRC's child objects from Linux (not from the MC) and ++ * closes the DPRC device in the MC. ++ * It tears down the interrupts that were configured for the DPRC device. ++ * It destroys the interrupt pool associated with this MC bus. ++ */ ++static int dprc_remove(struct fsl_mc_device *mc_dev) ++{ ++ int error; ++ struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev); ++ ++ if (!is_fsl_mc_bus_dprc(mc_dev)) ++ return -EINVAL; ++ if (!mc_dev->mc_io) ++ return -EINVAL; ++ ++ if (!mc_bus->irq_resources) ++ return -EINVAL; ++ ++ if (dev_get_msi_domain(&mc_dev->dev)) ++ dprc_teardown_irq(mc_dev); ++ ++ device_for_each_child(&mc_dev->dev, NULL, __fsl_mc_device_remove); ++ ++ if (dev_get_msi_domain(&mc_dev->dev)) { ++ fsl_mc_cleanup_irq_pool(mc_bus); ++ dev_set_msi_domain(&mc_dev->dev, NULL); ++ } ++ ++ fsl_mc_cleanup_all_resource_pools(mc_dev); ++ ++ error = dprc_close(mc_dev->mc_io, 0, mc_dev->mc_handle); ++ if (error < 0) ++ dev_err(&mc_dev->dev, "dprc_close() failed: %d\n", error); ++ ++ if (!fsl_mc_is_root_dprc(&mc_dev->dev)) { ++ fsl_destroy_mc_io(mc_dev->mc_io); ++ mc_dev->mc_io = NULL; ++ } ++ ++ dev_info(&mc_dev->dev, "DPRC device unbound from driver"); ++ return 0; ++} ++ ++static const struct fsl_mc_device_id match_id_table[] = { ++ { ++ .vendor = FSL_MC_VENDOR_FREESCALE, ++ .obj_type = "dprc"}, ++ {.vendor = 0x0}, ++}; ++ ++static struct fsl_mc_driver dprc_driver = { ++ .driver = { ++ .name = FSL_MC_DPRC_DRIVER_NAME, ++ .owner = THIS_MODULE, ++ .pm = NULL, ++ }, ++ .match_id_table = match_id_table, ++ .probe = dprc_probe, ++ .remove = dprc_remove, ++}; ++ ++int __init dprc_driver_init(void) ++{ ++ return fsl_mc_driver_register(&dprc_driver); ++} ++ ++void dprc_driver_exit(void) ++{ ++ fsl_mc_driver_unregister(&dprc_driver); ++} +--- a/drivers/staging/fsl-mc/bus/dprc.c ++++ /dev/null +@@ -1,757 +0,0 @@ +-// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +-/* +- * Copyright 2013-2016 Freescale Semiconductor Inc. +- * +- */ +-#include <linux/kernel.h> +-#include "../include/mc.h" +-#include "dprc.h" +- +-#include "dprc-cmd.h" +- +-/** +- * dprc_open() - Open DPRC object for use +- * @mc_io: Pointer to MC portal's I/O object +- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' +- * @container_id: Container ID to open +- * @token: Returned token of DPRC object +- * +- * Return: '0' on Success; Error code otherwise. +- * +- * @warning Required before any operation on the object. +- */ +-int dprc_open(struct fsl_mc_io *mc_io, +- u32 cmd_flags, +- int container_id, +- u16 *token) +-{ +- struct mc_command cmd = { 0 }; +- struct dprc_cmd_open *cmd_params; +- int err; +- +- /* prepare command */ +- cmd.header = mc_encode_cmd_header(DPRC_CMDID_OPEN, cmd_flags, +- 0); +- cmd_params = (struct dprc_cmd_open *)cmd.params; +- cmd_params->container_id = cpu_to_le32(container_id); +- +- /* send command to mc*/ +- err = mc_send_command(mc_io, &cmd); +- if (err) +- return err; +- +- /* retrieve response parameters */ +- *token = mc_cmd_hdr_read_token(&cmd); +- +- return 0; +-} +-EXPORT_SYMBOL(dprc_open); +- +-/** +- * dprc_close() - Close the control session of the object +- * @mc_io: Pointer to MC portal's I/O object +- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' +- * @token: Token of DPRC object +- * +- * After this function is called, no further operations are +- * allowed on the object without opening a new control session. +- * +- * Return: '0' on Success; Error code otherwise. +- */ +-int dprc_close(struct fsl_mc_io *mc_io, +- u32 cmd_flags, +- u16 token) +-{ +- struct mc_command cmd = { 0 }; +- +- /* prepare command */ +- cmd.header = mc_encode_cmd_header(DPRC_CMDID_CLOSE, cmd_flags, +- token); +- +- /* send command to mc*/ +- return mc_send_command(mc_io, &cmd); +-} +-EXPORT_SYMBOL(dprc_close); +- +-/** +- * dprc_get_irq() - Get IRQ information from the DPRC. +- * @mc_io: Pointer to MC portal's I/O object +- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' +- * @token: Token of DPRC object +- * @irq_index: The interrupt index to configure +- * @type: Interrupt type: 0 represents message interrupt +- * type (both irq_addr and irq_val are valid) +- * @irq_cfg: IRQ attributes +- * +- * Return: '0' on Success; Error code otherwise. +- */ +-int dprc_get_irq(struct fsl_mc_io *mc_io, +- u32 cmd_flags, +- u16 token, +- u8 irq_index, +- int *type, +- struct dprc_irq_cfg *irq_cfg) +-{ +- struct mc_command cmd = { 0 }; +- struct dprc_cmd_get_irq *cmd_params; +- struct dprc_rsp_get_irq *rsp_params; +- int err; +- +- /* prepare command */ +- cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_IRQ, +- cmd_flags, +- token); +- cmd_params = (struct dprc_cmd_get_irq *)cmd.params; +- cmd_params->irq_index = irq_index; +- +- /* send command to mc*/ +- err = mc_send_command(mc_io, &cmd); +- if (err) +- return err; +- +- /* retrieve response parameters */ +- rsp_params = (struct dprc_rsp_get_irq *)cmd.params; +- irq_cfg->val = le32_to_cpu(rsp_params->irq_val); +- irq_cfg->paddr = le64_to_cpu(rsp_params->irq_addr); +- irq_cfg->irq_num = le32_to_cpu(rsp_params->irq_num); +- *type = le32_to_cpu(rsp_params->type); +- +- return 0; +-} +- +-/** +- * dprc_set_irq() - Set IRQ information for the DPRC to trigger an interrupt. +- * @mc_io: Pointer to MC portal's I/O object +- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' +- * @token: Token of DPRC object +- * @irq_index: Identifies the interrupt index to configure +- * @irq_cfg: IRQ configuration +- * +- * Return: '0' on Success; Error code otherwise. +- */ +-int dprc_set_irq(struct fsl_mc_io *mc_io, +- u32 cmd_flags, +- u16 token, +- u8 irq_index, +- struct dprc_irq_cfg *irq_cfg) +-{ +- struct mc_command cmd = { 0 }; +- struct dprc_cmd_set_irq *cmd_params; +- +- /* prepare command */ +- cmd.header = mc_encode_cmd_header(DPRC_CMDID_SET_IRQ, +- cmd_flags, +- token); +- cmd_params = (struct dprc_cmd_set_irq *)cmd.params; +- cmd_params->irq_val = cpu_to_le32(irq_cfg->val); +- cmd_params->irq_index = irq_index; +- cmd_params->irq_addr = cpu_to_le64(irq_cfg->paddr); +- cmd_params->irq_num = cpu_to_le32(irq_cfg->irq_num); +- +- /* send command to mc*/ +- return mc_send_command(mc_io, &cmd); +-} +- +-/** +- * dprc_get_irq_enable() - Get overall interrupt state. +- * @mc_io: Pointer to MC portal's I/O object +- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' +- * @token: Token of DPRC object +- * @irq_index: The interrupt index to configure +- * @en: Returned interrupt state - enable = 1, disable = 0 +- * +- * Return: '0' on Success; Error code otherwise. +- */ +-int dprc_get_irq_enable(struct fsl_mc_io *mc_io, +- u32 cmd_flags, +- u16 token, +- u8 irq_index, +- u8 *en) +-{ +- struct mc_command cmd = { 0 }; +- struct dprc_cmd_get_irq_enable *cmd_params; +- struct dprc_rsp_get_irq_enable *rsp_params; +- int err; +- +- /* prepare command */ +- cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_IRQ_ENABLE, +- cmd_flags, token); +- cmd_params = (struct dprc_cmd_get_irq_enable *)cmd.params; +- cmd_params->irq_index = irq_index; +- +- /* send command to mc*/ +- err = mc_send_command(mc_io, &cmd); +- if (err) +- return err; +- +- /* retrieve response parameters */ +- rsp_params = (struct dprc_rsp_get_irq_enable *)cmd.params; +- *en = rsp_params->enabled & DPRC_ENABLE; +- +- return 0; +-} +- +-/** +- * dprc_set_irq_enable() - Set overall interrupt state. +- * @mc_io: Pointer to MC portal's I/O object +- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' +- * @token: Token of DPRC object +- * @irq_index: The interrupt index to configure +- * @en: Interrupt state - enable = 1, disable = 0 +- * +- * Allows GPP software to control when interrupts are generated. +- * Each interrupt can have up to 32 causes. The enable/disable control's the +- * overall interrupt state. if the interrupt is disabled no causes will cause +- * an interrupt. +- * +- * Return: '0' on Success; Error code otherwise. +- */ +-int dprc_set_irq_enable(struct fsl_mc_io *mc_io, +- u32 cmd_flags, +- u16 token, +- u8 irq_index, +- u8 en) +-{ +- struct mc_command cmd = { 0 }; +- struct dprc_cmd_set_irq_enable *cmd_params; +- +- /* prepare command */ +- cmd.header = mc_encode_cmd_header(DPRC_CMDID_SET_IRQ_ENABLE, +- cmd_flags, token); +- cmd_params = (struct dprc_cmd_set_irq_enable *)cmd.params; +- cmd_params->enable = en & DPRC_ENABLE; +- cmd_params->irq_index = irq_index; +- +- /* send command to mc*/ +- return mc_send_command(mc_io, &cmd); +-} +- +-/** +- * dprc_get_irq_mask() - Get interrupt mask. +- * @mc_io: Pointer to MC portal's I/O object +- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' +- * @token: Token of DPRC object +- * @irq_index: The interrupt index to configure +- * @mask: Returned event mask to trigger interrupt +- * +- * Every interrupt can have up to 32 causes and the interrupt model supports +- * masking/unmasking each cause independently +- * +- * Return: '0' on Success; Error code otherwise. +- */ +-int dprc_get_irq_mask(struct fsl_mc_io *mc_io, +- u32 cmd_flags, +- u16 token, +- u8 irq_index, +- u32 *mask) +-{ +- struct mc_command cmd = { 0 }; +- struct dprc_cmd_get_irq_mask *cmd_params; +- struct dprc_rsp_get_irq_mask *rsp_params; +- int err; +- +- /* prepare command */ +- cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_IRQ_MASK, +- cmd_flags, token); +- cmd_params = (struct dprc_cmd_get_irq_mask *)cmd.params; +- cmd_params->irq_index = irq_index; +- +- /* send command to mc*/ +- err = mc_send_command(mc_io, &cmd); +- if (err) +- return err; +- +- /* retrieve response parameters */ +- rsp_params = (struct dprc_rsp_get_irq_mask *)cmd.params; +- *mask = le32_to_cpu(rsp_params->mask); +- +- return 0; +-} +- +-/** +- * dprc_set_irq_mask() - Set interrupt mask. +- * @mc_io: Pointer to MC portal's I/O object +- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' +- * @token: Token of DPRC object +- * @irq_index: The interrupt index to configure +- * @mask: event mask to trigger interrupt; +- * each bit: +- * 0 = ignore event +- * 1 = consider event for asserting irq +- * +- * Every interrupt can have up to 32 causes and the interrupt model supports +- * masking/unmasking each cause independently +- * +- * Return: '0' on Success; Error code otherwise. +- */ +-int dprc_set_irq_mask(struct fsl_mc_io *mc_io, +- u32 cmd_flags, +- u16 token, +- u8 irq_index, +- u32 mask) +-{ +- struct mc_command cmd = { 0 }; +- struct dprc_cmd_set_irq_mask *cmd_params; +- +- /* prepare command */ +- cmd.header = mc_encode_cmd_header(DPRC_CMDID_SET_IRQ_MASK, +- cmd_flags, token); +- cmd_params = (struct dprc_cmd_set_irq_mask *)cmd.params; +- cmd_params->mask = cpu_to_le32(mask); +- cmd_params->irq_index = irq_index; +- +- /* send command to mc*/ +- return mc_send_command(mc_io, &cmd); +-} +- +-/** +- * dprc_get_irq_status() - Get the current status of any pending interrupts. +- * @mc_io: Pointer to MC portal's I/O object +- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' +- * @token: Token of DPRC object +- * @irq_index: The interrupt index to configure +- * @status: Returned interrupts status - one bit per cause: +- * 0 = no interrupt pending +- * 1 = interrupt pending +- * +- * Return: '0' on Success; Error code otherwise. +- */ +-int dprc_get_irq_status(struct fsl_mc_io *mc_io, +- u32 cmd_flags, +- u16 token, +- u8 irq_index, +- u32 *status) +-{ +- struct mc_command cmd = { 0 }; +- struct dprc_cmd_get_irq_status *cmd_params; +- struct dprc_rsp_get_irq_status *rsp_params; +- int err; +- +- /* prepare command */ +- cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_IRQ_STATUS, +- cmd_flags, token); +- cmd_params = (struct dprc_cmd_get_irq_status *)cmd.params; +- cmd_params->status = cpu_to_le32(*status); +- cmd_params->irq_index = irq_index; +- +- /* send command to mc*/ +- err = mc_send_command(mc_io, &cmd); +- if (err) +- return err; +- +- /* retrieve response parameters */ +- rsp_params = (struct dprc_rsp_get_irq_status *)cmd.params; +- *status = le32_to_cpu(rsp_params->status); +- +- return 0; +-} +- +-/** +- * dprc_clear_irq_status() - Clear a pending interrupt's status +- * @mc_io: Pointer to MC portal's I/O object +- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' +- * @token: Token of DPRC object +- * @irq_index: The interrupt index to configure +- * @status: bits to clear (W1C) - one bit per cause: +- * 0 = don't change +- * 1 = clear status bit +- * +- * Return: '0' on Success; Error code otherwise. +- */ +-int dprc_clear_irq_status(struct fsl_mc_io *mc_io, +- u32 cmd_flags, +- u16 token, +- u8 irq_index, +- u32 status) +-{ +- struct mc_command cmd = { 0 }; +- struct dprc_cmd_clear_irq_status *cmd_params; +- +- /* prepare command */ +- cmd.header = mc_encode_cmd_header(DPRC_CMDID_CLEAR_IRQ_STATUS, +- cmd_flags, token); +- cmd_params = (struct dprc_cmd_clear_irq_status *)cmd.params; +- cmd_params->status = cpu_to_le32(status); +- cmd_params->irq_index = irq_index; +- +- /* send command to mc*/ +- return mc_send_command(mc_io, &cmd); +-} +- +-/** +- * dprc_get_attributes() - Obtains container attributes +- * @mc_io: Pointer to MC portal's I/O object +- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' +- * @token: Token of DPRC object +- * @attributes Returned container attributes +- * +- * Return: '0' on Success; Error code otherwise. +- */ +-int dprc_get_attributes(struct fsl_mc_io *mc_io, +- u32 cmd_flags, +- u16 token, +- struct dprc_attributes *attr) +-{ +- struct mc_command cmd = { 0 }; +- struct dprc_rsp_get_attributes *rsp_params; +- int err; +- +- /* prepare command */ +- cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_ATTR, +- cmd_flags, +- token); +- +- /* send command to mc*/ +- err = mc_send_command(mc_io, &cmd); +- if (err) +- return err; +- +- /* retrieve response parameters */ +- rsp_params = (struct dprc_rsp_get_attributes *)cmd.params; +- attr->container_id = le32_to_cpu(rsp_params->container_id); +- attr->icid = le16_to_cpu(rsp_params->icid); +- attr->options = le32_to_cpu(rsp_params->options); +- attr->portal_id = le32_to_cpu(rsp_params->portal_id); +- +- return 0; +-} +- +-/** +- * dprc_get_obj_count() - Obtains the number of objects in the DPRC +- * @mc_io: Pointer to MC portal's I/O object +- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' +- * @token: Token of DPRC object +- * @obj_count: Number of objects assigned to the DPRC +- * +- * Return: '0' on Success; Error code otherwise. +- */ +-int dprc_get_obj_count(struct fsl_mc_io *mc_io, +- u32 cmd_flags, +- u16 token, +- int *obj_count) +-{ +- struct mc_command cmd = { 0 }; +- struct dprc_rsp_get_obj_count *rsp_params; +- int err; +- +- /* prepare command */ +- cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_OBJ_COUNT, +- cmd_flags, token); +- +- /* send command to mc*/ +- err = mc_send_command(mc_io, &cmd); +- if (err) +- return err; +- +- /* retrieve response parameters */ +- rsp_params = (struct dprc_rsp_get_obj_count *)cmd.params; +- *obj_count = le32_to_cpu(rsp_params->obj_count); +- +- return 0; +-} +-EXPORT_SYMBOL(dprc_get_obj_count); +- +-/** +- * dprc_get_obj() - Get general information on an object +- * @mc_io: Pointer to MC portal's I/O object +- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' +- * @token: Token of DPRC object +- * @obj_index: Index of the object to be queried (< obj_count) +- * @obj_desc: Returns the requested object descriptor +- * +- * The object descriptors are retrieved one by one by incrementing +- * obj_index up to (not including) the value of obj_count returned +- * from dprc_get_obj_count(). dprc_get_obj_count() must +- * be called prior to dprc_get_obj(). +- * +- * Return: '0' on Success; Error code otherwise. +- */ +-int dprc_get_obj(struct fsl_mc_io *mc_io, +- u32 cmd_flags, +- u16 token, +- int obj_index, +- struct fsl_mc_obj_desc *obj_desc) +-{ +- struct mc_command cmd = { 0 }; +- struct dprc_cmd_get_obj *cmd_params; +- struct dprc_rsp_get_obj *rsp_params; +- int err; +- +- /* prepare command */ +- cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_OBJ, +- cmd_flags, +- token); +- cmd_params = (struct dprc_cmd_get_obj *)cmd.params; +- cmd_params->obj_index = cpu_to_le32(obj_index); +- +- /* send command to mc*/ +- err = mc_send_command(mc_io, &cmd); +- if (err) +- return err; +- +- /* retrieve response parameters */ +- rsp_params = (struct dprc_rsp_get_obj *)cmd.params; +- obj_desc->id = le32_to_cpu(rsp_params->id); +- obj_desc->vendor = le16_to_cpu(rsp_params->vendor); +- obj_desc->irq_count = rsp_params->irq_count; +- obj_desc->region_count = rsp_params->region_count; +- obj_desc->state = le32_to_cpu(rsp_params->state); +- obj_desc->ver_major = le16_to_cpu(rsp_params->version_major); +- obj_desc->ver_minor = le16_to_cpu(rsp_params->version_minor); +- obj_desc->flags = le16_to_cpu(rsp_params->flags); +- strncpy(obj_desc->type, rsp_params->type, 16); +- obj_desc->type[15] = '\0'; +- strncpy(obj_desc->label, rsp_params->label, 16); +- obj_desc->label[15] = '\0'; +- return 0; +-} +-EXPORT_SYMBOL(dprc_get_obj); +- +-/** +- * dprc_set_obj_irq() - Set IRQ information for object to trigger an interrupt. +- * @mc_io: Pointer to MC portal's I/O object +- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' +- * @token: Token of DPRC object +- * @obj_type: Type of the object to set its IRQ +- * @obj_id: ID of the object to set its IRQ +- * @irq_index: The interrupt index to configure +- * @irq_cfg: IRQ configuration +- * +- * Return: '0' on Success; Error code otherwise. +- */ +-int dprc_set_obj_irq(struct fsl_mc_io *mc_io, +- u32 cmd_flags, +- u16 token, +- char *obj_type, +- int obj_id, +- u8 irq_index, +- struct dprc_irq_cfg *irq_cfg) +-{ +- struct mc_command cmd = { 0 }; +- struct dprc_cmd_set_obj_irq *cmd_params; +- +- /* prepare command */ +- cmd.header = mc_encode_cmd_header(DPRC_CMDID_SET_OBJ_IRQ, +- cmd_flags, +- token); +- cmd_params = (struct dprc_cmd_set_obj_irq *)cmd.params; +- cmd_params->irq_val = cpu_to_le32(irq_cfg->val); +- cmd_params->irq_index = irq_index; +- cmd_params->irq_addr = cpu_to_le64(irq_cfg->paddr); +- cmd_params->irq_num = cpu_to_le32(irq_cfg->irq_num); +- cmd_params->obj_id = cpu_to_le32(obj_id); +- strncpy(cmd_params->obj_type, obj_type, 16); +- cmd_params->obj_type[15] = '\0'; +- +- /* send command to mc*/ +- return mc_send_command(mc_io, &cmd); +-} +-EXPORT_SYMBOL(dprc_set_obj_irq); +- +-/** +- * dprc_get_obj_irq() - Get IRQ information from object. +- * @mc_io: Pointer to MC portal's I/O object +- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' +- * @token: Token of DPRC object +- * @obj_type: Type od the object to get its IRQ +- * @obj_id: ID of the object to get its IRQ +- * @irq_index: The interrupt index to configure +- * @type: Interrupt type: 0 represents message interrupt +- * type (both irq_addr and irq_val are valid) +- * @irq_cfg: The returned IRQ attributes +- * +- * Return: '0' on Success; Error code otherwise. +- */ +-int dprc_get_obj_irq(struct fsl_mc_io *mc_io, +- u32 cmd_flags, +- u16 token, +- char *obj_type, +- int obj_id, +- u8 irq_index, +- int *type, +- struct dprc_irq_cfg *irq_cfg) +-{ +- struct mc_command cmd = { 0 }; +- struct dprc_cmd_get_obj_irq *cmd_params; +- struct dprc_rsp_get_obj_irq *rsp_params; +- int err; +- +- /* prepare command */ +- cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_OBJ_IRQ, +- cmd_flags, +- token); +- cmd_params = (struct dprc_cmd_get_obj_irq *)cmd.params; +- cmd_params->obj_id = cpu_to_le32(obj_id); +- cmd_params->irq_index = irq_index; +- strncpy(cmd_params->obj_type, obj_type, 16); +- cmd_params->obj_type[15] = '\0'; +- +- /* send command to mc*/ +- err = mc_send_command(mc_io, &cmd); +- if (err) +- return err; +- +- /* retrieve response parameters */ +- rsp_params = (struct dprc_rsp_get_obj_irq *)cmd.params; +- irq_cfg->val = le32_to_cpu(rsp_params->irq_val); +- irq_cfg->paddr = le64_to_cpu(rsp_params->irq_addr); +- irq_cfg->irq_num = le32_to_cpu(rsp_params->irq_num); +- *type = le32_to_cpu(rsp_params->type); +- +- return 0; +-} +-EXPORT_SYMBOL(dprc_get_obj_irq); +- +-/** +- * dprc_get_res_count() - Obtains the number of free resources that are assigned +- * to this container, by pool type +- * @mc_io: Pointer to MC portal's I/O object +- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' +- * @token: Token of DPRC object +- * @type: pool type +- * @res_count: Returned number of free resources of the given +- * resource type that are assigned to this DPRC +- * +- * Return: '0' on Success; Error code otherwise. +- */ +-int dprc_get_res_count(struct fsl_mc_io *mc_io, +- u32 cmd_flags, +- u16 token, +- char *type, +- int *res_count) +-{ +- struct mc_command cmd = { 0 }; +- struct dprc_cmd_get_res_count *cmd_params; +- struct dprc_rsp_get_res_count *rsp_params; +- int err; +- +- /* prepare command */ +- cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_RES_COUNT, +- cmd_flags, token); +- cmd_params = (struct dprc_cmd_get_res_count *)cmd.params; +- strncpy(cmd_params->type, type, 16); +- cmd_params->type[15] = '\0'; +- +- /* send command to mc*/ +- err = mc_send_command(mc_io, &cmd); +- if (err) +- return err; +- +- /* retrieve response parameters */ +- rsp_params = (struct dprc_rsp_get_res_count *)cmd.params; +- *res_count = le32_to_cpu(rsp_params->res_count); +- +- return 0; +-} +-EXPORT_SYMBOL(dprc_get_res_count); +- +-/** +- * dprc_get_obj_region() - Get region information for a specified object. +- * @mc_io: Pointer to MC portal's I/O object +- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' +- * @token: Token of DPRC object +- * @obj_type; Object type as returned in dprc_get_obj() +- * @obj_id: Unique object instance as returned in dprc_get_obj() +- * @region_index: The specific region to query +- * @region_desc: Returns the requested region descriptor +- * +- * Return: '0' on Success; Error code otherwise. +- */ +-int dprc_get_obj_region(struct fsl_mc_io *mc_io, +- u32 cmd_flags, +- u16 token, +- char *obj_type, +- int obj_id, +- u8 region_index, +- struct dprc_region_desc *region_desc) +-{ +- struct mc_command cmd = { 0 }; +- struct dprc_cmd_get_obj_region *cmd_params; +- struct dprc_rsp_get_obj_region *rsp_params; +- int err; +- +- /* prepare command */ +- cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_OBJ_REG, +- cmd_flags, token); +- cmd_params = (struct dprc_cmd_get_obj_region *)cmd.params; +- cmd_params->obj_id = cpu_to_le32(obj_id); +- cmd_params->region_index = region_index; +- strncpy(cmd_params->obj_type, obj_type, 16); +- cmd_params->obj_type[15] = '\0'; +- +- /* send command to mc*/ +- err = mc_send_command(mc_io, &cmd); +- if (err) +- return err; +- +- /* retrieve response parameters */ +- rsp_params = (struct dprc_rsp_get_obj_region *)cmd.params; +- region_desc->base_offset = le64_to_cpu(rsp_params->base_addr); +- region_desc->size = le32_to_cpu(rsp_params->size); +- +- return 0; +-} +-EXPORT_SYMBOL(dprc_get_obj_region); +- +-/** +- * dprc_get_api_version - Get Data Path Resource Container API version +- * @mc_io: Pointer to Mc portal's I/O object +- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' +- * @major_ver: Major version of Data Path Resource Container API +- * @minor_ver: Minor version of Data Path Resource Container API +- * +- * Return: '0' on Success; Error code otherwise. +- */ +-int dprc_get_api_version(struct fsl_mc_io *mc_io, +- u32 cmd_flags, +- u16 *major_ver, +- u16 *minor_ver) +-{ +- struct mc_command cmd = { 0 }; +- int err; +- +- /* prepare command */ +- cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_API_VERSION, +- cmd_flags, 0); +- +- /* send command to mc */ +- err = mc_send_command(mc_io, &cmd); +- if (err) +- return err; +- +- /* retrieve response parameters */ +- mc_cmd_read_api_version(&cmd, major_ver, minor_ver); +- +- return 0; +-} +- +-/** +- * dprc_get_container_id - Get container ID associated with a given portal. +- * @mc_io: Pointer to Mc portal's I/O object +- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' +- * @container_id: Requested container id +- * +- * Return: '0' on Success; Error code otherwise. +- */ +-int dprc_get_container_id(struct fsl_mc_io *mc_io, +- u32 cmd_flags, +- int *container_id) +-{ +- struct mc_command cmd = { 0 }; +- int err; +- +- /* prepare command */ +- cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_CONT_ID, +- cmd_flags, +- 0); +- +- /* send command to mc*/ +- err = mc_send_command(mc_io, &cmd); +- if (err) +- return err; +- +- /* retrieve response parameters */ +- *container_id = (int)mc_cmd_read_object_id(&cmd); +- +- return 0; +-} +--- /dev/null ++++ b/drivers/bus/fsl-mc/dprc.c +@@ -0,0 +1,575 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) ++/* ++ * Copyright 2013-2016 Freescale Semiconductor Inc. ++ * ++ */ ++#include <linux/kernel.h> ++#include <linux/fsl/mc.h> ++ ++#include "fsl-mc-private.h" ++ ++/** ++ * dprc_open() - Open DPRC object for use ++ * @mc_io: Pointer to MC portal's I/O object ++ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' ++ * @container_id: Container ID to open ++ * @token: Returned token of DPRC object ++ * ++ * Return: '0' on Success; Error code otherwise. ++ * ++ * @warning Required before any operation on the object. ++ */ ++int dprc_open(struct fsl_mc_io *mc_io, ++ u32 cmd_flags, ++ int container_id, ++ u16 *token) ++{ ++ struct fsl_mc_command cmd = { 0 }; ++ struct dprc_cmd_open *cmd_params; ++ int err; ++ ++ /* prepare command */ ++ cmd.header = mc_encode_cmd_header(DPRC_CMDID_OPEN, cmd_flags, ++ 0); ++ cmd_params = (struct dprc_cmd_open *)cmd.params; ++ cmd_params->container_id = cpu_to_le32(container_id); ++ ++ /* send command to mc*/ ++ err = mc_send_command(mc_io, &cmd); ++ if (err) ++ return err; ++ ++ /* retrieve response parameters */ ++ *token = mc_cmd_hdr_read_token(&cmd); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(dprc_open); ++ ++/** ++ * dprc_close() - Close the control session of the object ++ * @mc_io: Pointer to MC portal's I/O object ++ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' ++ * @token: Token of DPRC object ++ * ++ * After this function is called, no further operations are ++ * allowed on the object without opening a new control session. ++ * ++ * Return: '0' on Success; Error code otherwise. ++ */ ++int dprc_close(struct fsl_mc_io *mc_io, ++ u32 cmd_flags, ++ u16 token) ++{ ++ struct fsl_mc_command cmd = { 0 }; ++ ++ /* prepare command */ ++ cmd.header = mc_encode_cmd_header(DPRC_CMDID_CLOSE, cmd_flags, ++ token); ++ ++ /* send command to mc*/ ++ return mc_send_command(mc_io, &cmd); ++} ++EXPORT_SYMBOL_GPL(dprc_close); ++ ++/** ++ * dprc_reset_container - Reset child container. ++ * @mc_io: Pointer to MC portal's I/O object ++ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' ++ * @token: Token of DPRC object ++ * @child_container_id: ID of the container to reset ++ * ++ * In case a software context crashes or becomes non-responsive, the parent ++ * may wish to reset its resources container before the software context is ++ * restarted. ++ * ++ * This routine informs all objects assigned to the child container that the ++ * container is being reset, so they may perform any cleanup operations that are ++ * needed. All objects handles that were owned by the child container shall be ++ * closed. ++ * ++ * Note that such request may be submitted even if the child software context ++ * has not crashed, but the resulting object cleanup operations will not be ++ * aware of that. ++ * ++ * Return: '0' on Success; Error code otherwise. ++ */ ++int dprc_reset_container(struct fsl_mc_io *mc_io, ++ u32 cmd_flags, ++ u16 token, ++ int child_container_id) ++{ ++ struct fsl_mc_command cmd = { 0 }; ++ struct dprc_cmd_reset_container *cmd_params; ++ ++ /* prepare command */ ++ cmd.header = mc_encode_cmd_header(DPRC_CMDID_RESET_CONT, ++ cmd_flags, token); ++ cmd_params = (struct dprc_cmd_reset_container *)cmd.params; ++ cmd_params->child_container_id = cpu_to_le32(child_container_id); ++ ++ /* send command to mc*/ ++ return mc_send_command(mc_io, &cmd); ++} ++EXPORT_SYMBOL_GPL(dprc_reset_container); ++ ++/** ++ * dprc_set_irq() - Set IRQ information for the DPRC to trigger an interrupt. ++ * @mc_io: Pointer to MC portal's I/O object ++ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' ++ * @token: Token of DPRC object ++ * @irq_index: Identifies the interrupt index to configure ++ * @irq_cfg: IRQ configuration ++ * ++ * Return: '0' on Success; Error code otherwise. ++ */ ++int dprc_set_irq(struct fsl_mc_io *mc_io, ++ u32 cmd_flags, ++ u16 token, ++ u8 irq_index, ++ struct dprc_irq_cfg *irq_cfg) ++{ ++ struct fsl_mc_command cmd = { 0 }; ++ struct dprc_cmd_set_irq *cmd_params; ++ ++ /* prepare command */ ++ cmd.header = mc_encode_cmd_header(DPRC_CMDID_SET_IRQ, ++ cmd_flags, ++ token); ++ cmd_params = (struct dprc_cmd_set_irq *)cmd.params; ++ cmd_params->irq_val = cpu_to_le32(irq_cfg->val); ++ cmd_params->irq_index = irq_index; ++ cmd_params->irq_addr = cpu_to_le64(irq_cfg->paddr); ++ cmd_params->irq_num = cpu_to_le32(irq_cfg->irq_num); ++ ++ /* send command to mc*/ ++ return mc_send_command(mc_io, &cmd); ++} ++ ++/** ++ * dprc_set_irq_enable() - Set overall interrupt state. ++ * @mc_io: Pointer to MC portal's I/O object ++ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' ++ * @token: Token of DPRC object ++ * @irq_index: The interrupt index to configure ++ * @en: Interrupt state - enable = 1, disable = 0 ++ * ++ * Allows GPP software to control when interrupts are generated. ++ * Each interrupt can have up to 32 causes. The enable/disable control's the ++ * overall interrupt state. if the interrupt is disabled no causes will cause ++ * an interrupt. ++ * ++ * Return: '0' on Success; Error code otherwise. ++ */ ++int dprc_set_irq_enable(struct fsl_mc_io *mc_io, ++ u32 cmd_flags, ++ u16 token, ++ u8 irq_index, ++ u8 en) ++{ ++ struct fsl_mc_command cmd = { 0 }; ++ struct dprc_cmd_set_irq_enable *cmd_params; ++ ++ /* prepare command */ ++ cmd.header = mc_encode_cmd_header(DPRC_CMDID_SET_IRQ_ENABLE, ++ cmd_flags, token); ++ cmd_params = (struct dprc_cmd_set_irq_enable *)cmd.params; ++ cmd_params->enable = en & DPRC_ENABLE; ++ cmd_params->irq_index = irq_index; ++ ++ /* send command to mc*/ ++ return mc_send_command(mc_io, &cmd); ++} ++ ++/** ++ * dprc_set_irq_mask() - Set interrupt mask. ++ * @mc_io: Pointer to MC portal's I/O object ++ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' ++ * @token: Token of DPRC object ++ * @irq_index: The interrupt index to configure ++ * @mask: event mask to trigger interrupt; ++ * each bit: ++ * 0 = ignore event ++ * 1 = consider event for asserting irq ++ * ++ * Every interrupt can have up to 32 causes and the interrupt model supports ++ * masking/unmasking each cause independently ++ * ++ * Return: '0' on Success; Error code otherwise. ++ */ ++int dprc_set_irq_mask(struct fsl_mc_io *mc_io, ++ u32 cmd_flags, ++ u16 token, ++ u8 irq_index, ++ u32 mask) ++{ ++ struct fsl_mc_command cmd = { 0 }; ++ struct dprc_cmd_set_irq_mask *cmd_params; ++ ++ /* prepare command */ ++ cmd.header = mc_encode_cmd_header(DPRC_CMDID_SET_IRQ_MASK, ++ cmd_flags, token); ++ cmd_params = (struct dprc_cmd_set_irq_mask *)cmd.params; ++ cmd_params->mask = cpu_to_le32(mask); ++ cmd_params->irq_index = irq_index; ++ ++ /* send command to mc*/ ++ return mc_send_command(mc_io, &cmd); ++} ++ ++/** ++ * dprc_get_irq_status() - Get the current status of any pending interrupts. ++ * @mc_io: Pointer to MC portal's I/O object ++ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' ++ * @token: Token of DPRC object ++ * @irq_index: The interrupt index to configure ++ * @status: Returned interrupts status - one bit per cause: ++ * 0 = no interrupt pending ++ * 1 = interrupt pending ++ * ++ * Return: '0' on Success; Error code otherwise. ++ */ ++int dprc_get_irq_status(struct fsl_mc_io *mc_io, ++ u32 cmd_flags, ++ u16 token, ++ u8 irq_index, ++ u32 *status) ++{ ++ struct fsl_mc_command cmd = { 0 }; ++ struct dprc_cmd_get_irq_status *cmd_params; ++ struct dprc_rsp_get_irq_status *rsp_params; ++ int err; ++ ++ /* prepare command */ ++ cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_IRQ_STATUS, ++ cmd_flags, token); ++ cmd_params = (struct dprc_cmd_get_irq_status *)cmd.params; ++ cmd_params->status = cpu_to_le32(*status); ++ cmd_params->irq_index = irq_index; ++ ++ /* send command to mc*/ ++ err = mc_send_command(mc_io, &cmd); ++ if (err) ++ return err; ++ ++ /* retrieve response parameters */ ++ rsp_params = (struct dprc_rsp_get_irq_status *)cmd.params; ++ *status = le32_to_cpu(rsp_params->status); ++ ++ return 0; ++} ++ ++/** ++ * dprc_clear_irq_status() - Clear a pending interrupt's status ++ * @mc_io: Pointer to MC portal's I/O object ++ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' ++ * @token: Token of DPRC object ++ * @irq_index: The interrupt index to configure ++ * @status: bits to clear (W1C) - one bit per cause: ++ * 0 = don't change ++ * 1 = clear status bit ++ * ++ * Return: '0' on Success; Error code otherwise. ++ */ ++int dprc_clear_irq_status(struct fsl_mc_io *mc_io, ++ u32 cmd_flags, ++ u16 token, ++ u8 irq_index, ++ u32 status) ++{ ++ struct fsl_mc_command cmd = { 0 }; ++ struct dprc_cmd_clear_irq_status *cmd_params; ++ ++ /* prepare command */ ++ cmd.header = mc_encode_cmd_header(DPRC_CMDID_CLEAR_IRQ_STATUS, ++ cmd_flags, token); ++ cmd_params = (struct dprc_cmd_clear_irq_status *)cmd.params; ++ cmd_params->status = cpu_to_le32(status); ++ cmd_params->irq_index = irq_index; ++ ++ /* send command to mc*/ ++ return mc_send_command(mc_io, &cmd); ++} ++ ++/** ++ * dprc_get_attributes() - Obtains container attributes ++ * @mc_io: Pointer to MC portal's I/O object ++ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' ++ * @token: Token of DPRC object ++ * @attributes Returned container attributes ++ * ++ * Return: '0' on Success; Error code otherwise. ++ */ ++int dprc_get_attributes(struct fsl_mc_io *mc_io, ++ u32 cmd_flags, ++ u16 token, ++ struct dprc_attributes *attr) ++{ ++ struct fsl_mc_command cmd = { 0 }; ++ struct dprc_rsp_get_attributes *rsp_params; ++ int err; ++ ++ /* prepare command */ ++ cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_ATTR, ++ cmd_flags, ++ token); ++ ++ /* send command to mc*/ ++ err = mc_send_command(mc_io, &cmd); ++ if (err) ++ return err; ++ ++ /* retrieve response parameters */ ++ rsp_params = (struct dprc_rsp_get_attributes *)cmd.params; ++ attr->container_id = le32_to_cpu(rsp_params->container_id); ++ attr->icid = le32_to_cpu(rsp_params->icid); ++ attr->options = le32_to_cpu(rsp_params->options); ++ attr->portal_id = le32_to_cpu(rsp_params->portal_id); ++ ++ return 0; ++} ++ ++/** ++ * dprc_get_obj_count() - Obtains the number of objects in the DPRC ++ * @mc_io: Pointer to MC portal's I/O object ++ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' ++ * @token: Token of DPRC object ++ * @obj_count: Number of objects assigned to the DPRC ++ * ++ * Return: '0' on Success; Error code otherwise. ++ */ ++int dprc_get_obj_count(struct fsl_mc_io *mc_io, ++ u32 cmd_flags, ++ u16 token, ++ int *obj_count) ++{ ++ struct fsl_mc_command cmd = { 0 }; ++ struct dprc_rsp_get_obj_count *rsp_params; ++ int err; ++ ++ /* prepare command */ ++ cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_OBJ_COUNT, ++ cmd_flags, token); ++ ++ /* send command to mc*/ ++ err = mc_send_command(mc_io, &cmd); ++ if (err) ++ return err; ++ ++ /* retrieve response parameters */ ++ rsp_params = (struct dprc_rsp_get_obj_count *)cmd.params; ++ *obj_count = le32_to_cpu(rsp_params->obj_count); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(dprc_get_obj_count); ++ ++/** ++ * dprc_get_obj() - Get general information on an object ++ * @mc_io: Pointer to MC portal's I/O object ++ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' ++ * @token: Token of DPRC object ++ * @obj_index: Index of the object to be queried (< obj_count) ++ * @obj_desc: Returns the requested object descriptor ++ * ++ * The object descriptors are retrieved one by one by incrementing ++ * obj_index up to (not including) the value of obj_count returned ++ * from dprc_get_obj_count(). dprc_get_obj_count() must ++ * be called prior to dprc_get_obj(). ++ * ++ * Return: '0' on Success; Error code otherwise. ++ */ ++int dprc_get_obj(struct fsl_mc_io *mc_io, ++ u32 cmd_flags, ++ u16 token, ++ int obj_index, ++ struct fsl_mc_obj_desc *obj_desc) ++{ ++ struct fsl_mc_command cmd = { 0 }; ++ struct dprc_cmd_get_obj *cmd_params; ++ struct dprc_rsp_get_obj *rsp_params; ++ int err; ++ ++ /* prepare command */ ++ cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_OBJ, ++ cmd_flags, ++ token); ++ cmd_params = (struct dprc_cmd_get_obj *)cmd.params; ++ cmd_params->obj_index = cpu_to_le32(obj_index); ++ ++ /* send command to mc*/ ++ err = mc_send_command(mc_io, &cmd); ++ if (err) ++ return err; ++ ++ /* retrieve response parameters */ ++ rsp_params = (struct dprc_rsp_get_obj *)cmd.params; ++ obj_desc->id = le32_to_cpu(rsp_params->id); ++ obj_desc->vendor = le16_to_cpu(rsp_params->vendor); ++ obj_desc->irq_count = rsp_params->irq_count; ++ obj_desc->region_count = rsp_params->region_count; ++ obj_desc->state = le32_to_cpu(rsp_params->state); ++ obj_desc->ver_major = le16_to_cpu(rsp_params->version_major); ++ obj_desc->ver_minor = le16_to_cpu(rsp_params->version_minor); ++ obj_desc->flags = le16_to_cpu(rsp_params->flags); ++ strncpy(obj_desc->type, rsp_params->type, 16); ++ obj_desc->type[15] = '\0'; ++ strncpy(obj_desc->label, rsp_params->label, 16); ++ obj_desc->label[15] = '\0'; ++ return 0; ++} ++EXPORT_SYMBOL_GPL(dprc_get_obj); ++ ++/** ++ * dprc_set_obj_irq() - Set IRQ information for object to trigger an interrupt. ++ * @mc_io: Pointer to MC portal's I/O object ++ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' ++ * @token: Token of DPRC object ++ * @obj_type: Type of the object to set its IRQ ++ * @obj_id: ID of the object to set its IRQ ++ * @irq_index: The interrupt index to configure ++ * @irq_cfg: IRQ configuration ++ * ++ * Return: '0' on Success; Error code otherwise. ++ */ ++int dprc_set_obj_irq(struct fsl_mc_io *mc_io, ++ u32 cmd_flags, ++ u16 token, ++ char *obj_type, ++ int obj_id, ++ u8 irq_index, ++ struct dprc_irq_cfg *irq_cfg) ++{ ++ struct fsl_mc_command cmd = { 0 }; ++ struct dprc_cmd_set_obj_irq *cmd_params; ++ ++ /* prepare command */ ++ cmd.header = mc_encode_cmd_header(DPRC_CMDID_SET_OBJ_IRQ, ++ cmd_flags, ++ token); ++ cmd_params = (struct dprc_cmd_set_obj_irq *)cmd.params; ++ cmd_params->irq_val = cpu_to_le32(irq_cfg->val); ++ cmd_params->irq_index = irq_index; ++ cmd_params->irq_addr = cpu_to_le64(irq_cfg->paddr); ++ cmd_params->irq_num = cpu_to_le32(irq_cfg->irq_num); ++ cmd_params->obj_id = cpu_to_le32(obj_id); ++ strncpy(cmd_params->obj_type, obj_type, 16); ++ cmd_params->obj_type[15] = '\0'; ++ ++ /* send command to mc*/ ++ return mc_send_command(mc_io, &cmd); ++} ++EXPORT_SYMBOL_GPL(dprc_set_obj_irq); ++ ++/** ++ * dprc_get_obj_region() - Get region information for a specified object. ++ * @mc_io: Pointer to MC portal's I/O object ++ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' ++ * @token: Token of DPRC object ++ * @obj_type; Object type as returned in dprc_get_obj() ++ * @obj_id: Unique object instance as returned in dprc_get_obj() ++ * @region_index: The specific region to query ++ * @region_desc: Returns the requested region descriptor ++ * ++ * Return: '0' on Success; Error code otherwise. ++ */ ++int dprc_get_obj_region(struct fsl_mc_io *mc_io, ++ u32 cmd_flags, ++ u16 token, ++ char *obj_type, ++ int obj_id, ++ u8 region_index, ++ struct dprc_region_desc *region_desc) ++{ ++ struct fsl_mc_command cmd = { 0 }; ++ struct dprc_cmd_get_obj_region *cmd_params; ++ struct dprc_rsp_get_obj_region *rsp_params; ++ int err; ++ ++ /* prepare command */ ++ cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_OBJ_REG, ++ cmd_flags, token); ++ cmd_params = (struct dprc_cmd_get_obj_region *)cmd.params; ++ cmd_params->obj_id = cpu_to_le32(obj_id); ++ cmd_params->region_index = region_index; ++ strncpy(cmd_params->obj_type, obj_type, 16); ++ cmd_params->obj_type[15] = '\0'; ++ ++ /* send command to mc*/ ++ err = mc_send_command(mc_io, &cmd); ++ if (err) ++ return err; ++ ++ /* retrieve response parameters */ ++ rsp_params = (struct dprc_rsp_get_obj_region *)cmd.params; ++ region_desc->base_offset = le32_to_cpu(rsp_params->base_addr); ++ region_desc->size = le32_to_cpu(rsp_params->size); ++ region_desc->type = rsp_params->type; ++ region_desc->flags = le32_to_cpu(rsp_params->flags); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(dprc_get_obj_region); ++ ++/** ++ * dprc_get_api_version - Get Data Path Resource Container API version ++ * @mc_io: Pointer to Mc portal's I/O object ++ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' ++ * @major_ver: Major version of Data Path Resource Container API ++ * @minor_ver: Minor version of Data Path Resource Container API ++ * ++ * Return: '0' on Success; Error code otherwise. ++ */ ++int dprc_get_api_version(struct fsl_mc_io *mc_io, ++ u32 cmd_flags, ++ u16 *major_ver, ++ u16 *minor_ver) ++{ ++ struct fsl_mc_command cmd = { 0 }; ++ int err; ++ ++ /* prepare command */ ++ cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_API_VERSION, ++ cmd_flags, 0); ++ ++ /* send command to mc */ ++ err = mc_send_command(mc_io, &cmd); ++ if (err) ++ return err; ++ ++ /* retrieve response parameters */ ++ mc_cmd_read_api_version(&cmd, major_ver, minor_ver); ++ ++ return 0; ++} ++ ++/** ++ * dprc_get_container_id - Get container ID associated with a given portal. ++ * @mc_io: Pointer to Mc portal's I/O object ++ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' ++ * @container_id: Requested container id ++ * ++ * Return: '0' on Success; Error code otherwise. ++ */ ++int dprc_get_container_id(struct fsl_mc_io *mc_io, ++ u32 cmd_flags, ++ int *container_id) ++{ ++ struct fsl_mc_command cmd = { 0 }; ++ int err; ++ ++ /* prepare command */ ++ cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_CONT_ID, ++ cmd_flags, ++ 0); ++ ++ /* send command to mc*/ ++ err = mc_send_command(mc_io, &cmd); ++ if (err) ++ return err; ++ ++ /* retrieve response parameters */ ++ *container_id = (int)mc_cmd_read_object_id(&cmd); ++ ++ return 0; ++} +--- a/drivers/staging/fsl-mc/bus/fsl-mc-allocator.c ++++ /dev/null +@@ -1,663 +0,0 @@ +-// SPDX-License-Identifier: GPL-2.0 +-/* +- * fsl-mc object allocator driver +- * +- * Copyright (C) 2013-2016 Freescale Semiconductor, Inc. +- * +- */ +- +-#include <linux/module.h> +-#include <linux/msi.h> +-#include "../include/mc.h" +- +-#include "fsl-mc-private.h" +- +-static bool __must_check fsl_mc_is_allocatable(const char *obj_type) +-{ +- return strcmp(obj_type, "dpbp") == 0 || +- strcmp(obj_type, "dpmcp") == 0 || +- strcmp(obj_type, "dpcon") == 0; +-} +- +-/** +- * fsl_mc_resource_pool_add_device - add allocatable object to a resource +- * pool of a given fsl-mc bus +- * +- * @mc_bus: pointer to the fsl-mc bus +- * @pool_type: pool type +- * @mc_dev: pointer to allocatable fsl-mc device +- */ +-static int __must_check fsl_mc_resource_pool_add_device(struct fsl_mc_bus +- *mc_bus, +- enum fsl_mc_pool_type +- pool_type, +- struct fsl_mc_device +- *mc_dev) +-{ +- struct fsl_mc_resource_pool *res_pool; +- struct fsl_mc_resource *resource; +- struct fsl_mc_device *mc_bus_dev = &mc_bus->mc_dev; +- int error = -EINVAL; +- +- if (WARN_ON(pool_type < 0 || pool_type >= FSL_MC_NUM_POOL_TYPES)) +- goto out; +- if (WARN_ON(!fsl_mc_is_allocatable(mc_dev->obj_desc.type))) +- goto out; +- if (WARN_ON(mc_dev->resource)) +- goto out; +- +- res_pool = &mc_bus->resource_pools[pool_type]; +- if (WARN_ON(res_pool->type != pool_type)) +- goto out; +- if (WARN_ON(res_pool->mc_bus != mc_bus)) +- goto out; +- +- mutex_lock(&res_pool->mutex); +- +- if (WARN_ON(res_pool->max_count < 0)) +- goto out_unlock; +- if (WARN_ON(res_pool->free_count < 0 || +- res_pool->free_count > res_pool->max_count)) +- goto out_unlock; +- +- resource = devm_kzalloc(&mc_bus_dev->dev, sizeof(*resource), +- GFP_KERNEL); +- if (!resource) { +- error = -ENOMEM; +- dev_err(&mc_bus_dev->dev, +- "Failed to allocate memory for fsl_mc_resource\n"); +- goto out_unlock; +- } +- +- resource->type = pool_type; +- resource->id = mc_dev->obj_desc.id; +- resource->data = mc_dev; +- resource->parent_pool = res_pool; +- INIT_LIST_HEAD(&resource->node); +- list_add_tail(&resource->node, &res_pool->free_list); +- mc_dev->resource = resource; +- res_pool->free_count++; +- res_pool->max_count++; +- error = 0; +-out_unlock: +- mutex_unlock(&res_pool->mutex); +-out: +- return error; +-} +- +-/** +- * fsl_mc_resource_pool_remove_device - remove an allocatable device from a +- * resource pool +- * +- * @mc_dev: pointer to allocatable fsl-mc device +- * +- * It permanently removes an allocatable fsl-mc device from the resource +- * pool. It's an error if the device is in use. +- */ +-static int __must_check fsl_mc_resource_pool_remove_device(struct fsl_mc_device +- *mc_dev) +-{ +- struct fsl_mc_device *mc_bus_dev; +- struct fsl_mc_bus *mc_bus; +- struct fsl_mc_resource_pool *res_pool; +- struct fsl_mc_resource *resource; +- int error = -EINVAL; +- +- if (WARN_ON(!fsl_mc_is_allocatable(mc_dev->obj_desc.type))) +- goto out; +- +- resource = mc_dev->resource; +- if (WARN_ON(!resource || resource->data != mc_dev)) +- goto out; +- +- mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent); +- mc_bus = to_fsl_mc_bus(mc_bus_dev); +- res_pool = resource->parent_pool; +- if (WARN_ON(res_pool != &mc_bus->resource_pools[resource->type])) +- goto out; +- +- mutex_lock(&res_pool->mutex); +- +- if (WARN_ON(res_pool->max_count <= 0)) +- goto out_unlock; +- if (WARN_ON(res_pool->free_count <= 0 || +- res_pool->free_count > res_pool->max_count)) +- goto out_unlock; +- +- /* +- * If the device is currently allocated, its resource is not +- * in the free list and thus, the device cannot be removed. +- */ +- if (list_empty(&resource->node)) { +- error = -EBUSY; +- dev_err(&mc_bus_dev->dev, +- "Device %s cannot be removed from resource pool\n", +- dev_name(&mc_dev->dev)); +- goto out_unlock; +- } +- +- list_del_init(&resource->node); +- res_pool->free_count--; +- res_pool->max_count--; +- +- devm_kfree(&mc_bus_dev->dev, resource); +- mc_dev->resource = NULL; +- error = 0; +-out_unlock: +- mutex_unlock(&res_pool->mutex); +-out: +- return error; +-} +- +-static const char *const fsl_mc_pool_type_strings[] = { +- [FSL_MC_POOL_DPMCP] = "dpmcp", +- [FSL_MC_POOL_DPBP] = "dpbp", +- [FSL_MC_POOL_DPCON] = "dpcon", +- [FSL_MC_POOL_IRQ] = "irq", +-}; +- +-static int __must_check object_type_to_pool_type(const char *object_type, +- enum fsl_mc_pool_type +- *pool_type) +-{ +- unsigned int i; +- +- for (i = 0; i < ARRAY_SIZE(fsl_mc_pool_type_strings); i++) { +- if (strcmp(object_type, fsl_mc_pool_type_strings[i]) == 0) { +- *pool_type = i; +- return 0; +- } +- } +- +- return -EINVAL; +-} +- +-int __must_check fsl_mc_resource_allocate(struct fsl_mc_bus *mc_bus, +- enum fsl_mc_pool_type pool_type, +- struct fsl_mc_resource **new_resource) +-{ +- struct fsl_mc_resource_pool *res_pool; +- struct fsl_mc_resource *resource; +- struct fsl_mc_device *mc_bus_dev = &mc_bus->mc_dev; +- int error = -EINVAL; +- +- BUILD_BUG_ON(ARRAY_SIZE(fsl_mc_pool_type_strings) != +- FSL_MC_NUM_POOL_TYPES); +- +- *new_resource = NULL; +- if (WARN_ON(pool_type < 0 || pool_type >= FSL_MC_NUM_POOL_TYPES)) +- goto out; +- +- res_pool = &mc_bus->resource_pools[pool_type]; +- if (WARN_ON(res_pool->mc_bus != mc_bus)) +- goto out; +- +- mutex_lock(&res_pool->mutex); +- resource = list_first_entry_or_null(&res_pool->free_list, +- struct fsl_mc_resource, node); +- +- if (!resource) { +- WARN_ON(res_pool->free_count != 0); +- error = -ENXIO; +- dev_err(&mc_bus_dev->dev, +- "No more resources of type %s left\n", +- fsl_mc_pool_type_strings[pool_type]); +- goto out_unlock; +- } +- +- if (WARN_ON(resource->type != pool_type)) +- goto out_unlock; +- if (WARN_ON(resource->parent_pool != res_pool)) +- goto out_unlock; +- if (WARN_ON(res_pool->free_count <= 0 || +- res_pool->free_count > res_pool->max_count)) +- goto out_unlock; +- +- list_del_init(&resource->node); +- +- res_pool->free_count--; +- error = 0; +-out_unlock: +- mutex_unlock(&res_pool->mutex); +- *new_resource = resource; +-out: +- return error; +-} +-EXPORT_SYMBOL_GPL(fsl_mc_resource_allocate); +- +-void fsl_mc_resource_free(struct fsl_mc_resource *resource) +-{ +- struct fsl_mc_resource_pool *res_pool; +- +- res_pool = resource->parent_pool; +- if (WARN_ON(resource->type != res_pool->type)) +- return; +- +- mutex_lock(&res_pool->mutex); +- if (WARN_ON(res_pool->free_count < 0 || +- res_pool->free_count >= res_pool->max_count)) +- goto out_unlock; +- +- if (WARN_ON(!list_empty(&resource->node))) +- goto out_unlock; +- +- list_add_tail(&resource->node, &res_pool->free_list); +- res_pool->free_count++; +-out_unlock: +- mutex_unlock(&res_pool->mutex); +-} +-EXPORT_SYMBOL_GPL(fsl_mc_resource_free); +- +-/** +- * fsl_mc_object_allocate - Allocates an fsl-mc object of the given +- * pool type from a given fsl-mc bus instance +- * +- * @mc_dev: fsl-mc device which is used in conjunction with the +- * allocated object +- * @pool_type: pool type +- * @new_mc_dev: pointer to area where the pointer to the allocated device +- * is to be returned +- * +- * Allocatable objects are always used in conjunction with some functional +- * device. This function allocates an object of the specified type from +- * the DPRC containing the functional device. +- * +- * NOTE: pool_type must be different from FSL_MC_POOL_MCP, since MC +- * portals are allocated using fsl_mc_portal_allocate(), instead of +- * this function. +- */ +-int __must_check fsl_mc_object_allocate(struct fsl_mc_device *mc_dev, +- enum fsl_mc_pool_type pool_type, +- struct fsl_mc_device **new_mc_adev) +-{ +- struct fsl_mc_device *mc_bus_dev; +- struct fsl_mc_bus *mc_bus; +- struct fsl_mc_device *mc_adev; +- int error = -EINVAL; +- struct fsl_mc_resource *resource = NULL; +- +- *new_mc_adev = NULL; +- if (WARN_ON(mc_dev->flags & FSL_MC_IS_DPRC)) +- goto error; +- +- if (WARN_ON(!dev_is_fsl_mc(mc_dev->dev.parent))) +- goto error; +- +- if (WARN_ON(pool_type == FSL_MC_POOL_DPMCP)) +- goto error; +- +- mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent); +- mc_bus = to_fsl_mc_bus(mc_bus_dev); +- error = fsl_mc_resource_allocate(mc_bus, pool_type, &resource); +- if (error < 0) +- goto error; +- +- mc_adev = resource->data; +- if (WARN_ON(!mc_adev)) +- goto error; +- +- *new_mc_adev = mc_adev; +- return 0; +-error: +- if (resource) +- fsl_mc_resource_free(resource); +- +- return error; +-} +-EXPORT_SYMBOL_GPL(fsl_mc_object_allocate); +- +-/** +- * fsl_mc_object_free - Returns an fsl-mc object to the resource +- * pool where it came from. +- * @mc_adev: Pointer to the fsl-mc device +- */ +-void fsl_mc_object_free(struct fsl_mc_device *mc_adev) +-{ +- struct fsl_mc_resource *resource; +- +- resource = mc_adev->resource; +- if (WARN_ON(resource->type == FSL_MC_POOL_DPMCP)) +- return; +- if (WARN_ON(resource->data != mc_adev)) +- return; +- +- fsl_mc_resource_free(resource); +-} +-EXPORT_SYMBOL_GPL(fsl_mc_object_free); +- +-/* +- * A DPRC and the devices in the DPRC all share the same GIC-ITS device +- * ID. A block of IRQs is pre-allocated and maintained in a pool +- * from which devices can allocate them when needed. +- */ +- +-/* +- * Initialize the interrupt pool associated with an fsl-mc bus. +- * It allocates a block of IRQs from the GIC-ITS. +- */ +-int fsl_mc_populate_irq_pool(struct fsl_mc_bus *mc_bus, +- unsigned int irq_count) +-{ +- unsigned int i; +- struct msi_desc *msi_desc; +- struct fsl_mc_device_irq *irq_resources; +- struct fsl_mc_device_irq *mc_dev_irq; +- int error; +- struct fsl_mc_device *mc_bus_dev = &mc_bus->mc_dev; +- struct fsl_mc_resource_pool *res_pool = +- &mc_bus->resource_pools[FSL_MC_POOL_IRQ]; +- +- if (WARN_ON(irq_count == 0 || +- irq_count > FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS)) +- return -EINVAL; +- +- error = fsl_mc_msi_domain_alloc_irqs(&mc_bus_dev->dev, irq_count); +- if (error < 0) +- return error; +- +- irq_resources = devm_kzalloc(&mc_bus_dev->dev, +- sizeof(*irq_resources) * irq_count, +- GFP_KERNEL); +- if (!irq_resources) { +- error = -ENOMEM; +- goto cleanup_msi_irqs; +- } +- +- for (i = 0; i < irq_count; i++) { +- mc_dev_irq = &irq_resources[i]; +- +- /* +- * NOTE: This mc_dev_irq's MSI addr/value pair will be set +- * by the fsl_mc_msi_write_msg() callback +- */ +- mc_dev_irq->resource.type = res_pool->type; +- mc_dev_irq->resource.data = mc_dev_irq; +- mc_dev_irq->resource.parent_pool = res_pool; +- INIT_LIST_HEAD(&mc_dev_irq->resource.node); +- list_add_tail(&mc_dev_irq->resource.node, &res_pool->free_list); +- } +- +- for_each_msi_entry(msi_desc, &mc_bus_dev->dev) { +- mc_dev_irq = &irq_resources[msi_desc->fsl_mc.msi_index]; +- mc_dev_irq->msi_desc = msi_desc; +- mc_dev_irq->resource.id = msi_desc->irq; +- } +- +- res_pool->max_count = irq_count; +- res_pool->free_count = irq_count; +- mc_bus->irq_resources = irq_resources; +- return 0; +- +-cleanup_msi_irqs: +- fsl_mc_msi_domain_free_irqs(&mc_bus_dev->dev); +- return error; +-} +-EXPORT_SYMBOL_GPL(fsl_mc_populate_irq_pool); +- +-/** +- * Teardown the interrupt pool associated with an fsl-mc bus. +- * It frees the IRQs that were allocated to the pool, back to the GIC-ITS. +- */ +-void fsl_mc_cleanup_irq_pool(struct fsl_mc_bus *mc_bus) +-{ +- struct fsl_mc_device *mc_bus_dev = &mc_bus->mc_dev; +- struct fsl_mc_resource_pool *res_pool = +- &mc_bus->resource_pools[FSL_MC_POOL_IRQ]; +- +- if (WARN_ON(!mc_bus->irq_resources)) +- return; +- +- if (WARN_ON(res_pool->max_count == 0)) +- return; +- +- if (WARN_ON(res_pool->free_count != res_pool->max_count)) +- return; +- +- INIT_LIST_HEAD(&res_pool->free_list); +- res_pool->max_count = 0; +- res_pool->free_count = 0; +- mc_bus->irq_resources = NULL; +- fsl_mc_msi_domain_free_irqs(&mc_bus_dev->dev); +-} +-EXPORT_SYMBOL_GPL(fsl_mc_cleanup_irq_pool); +- +-/** +- * Allocate the IRQs required by a given fsl-mc device. +- */ +-int __must_check fsl_mc_allocate_irqs(struct fsl_mc_device *mc_dev) +-{ +- int i; +- int irq_count; +- int res_allocated_count = 0; +- int error = -EINVAL; +- struct fsl_mc_device_irq **irqs = NULL; +- struct fsl_mc_bus *mc_bus; +- struct fsl_mc_resource_pool *res_pool; +- +- if (WARN_ON(mc_dev->irqs)) +- return -EINVAL; +- +- irq_count = mc_dev->obj_desc.irq_count; +- if (WARN_ON(irq_count == 0)) +- return -EINVAL; +- +- if (strcmp(mc_dev->obj_desc.type, "dprc") == 0) +- mc_bus = to_fsl_mc_bus(mc_dev); +- else +- mc_bus = to_fsl_mc_bus(to_fsl_mc_device(mc_dev->dev.parent)); +- +- if (WARN_ON(!mc_bus->irq_resources)) +- return -EINVAL; +- +- res_pool = &mc_bus->resource_pools[FSL_MC_POOL_IRQ]; +- if (res_pool->free_count < irq_count) { +- dev_err(&mc_dev->dev, +- "Not able to allocate %u irqs for device\n", irq_count); +- return -ENOSPC; +- } +- +- irqs = devm_kzalloc(&mc_dev->dev, irq_count * sizeof(irqs[0]), +- GFP_KERNEL); +- if (!irqs) +- return -ENOMEM; +- +- for (i = 0; i < irq_count; i++) { +- struct fsl_mc_resource *resource; +- +- error = fsl_mc_resource_allocate(mc_bus, FSL_MC_POOL_IRQ, +- &resource); +- if (error < 0) +- goto error_resource_alloc; +- +- irqs[i] = to_fsl_mc_irq(resource); +- res_allocated_count++; +- +- WARN_ON(irqs[i]->mc_dev); +- irqs[i]->mc_dev = mc_dev; +- irqs[i]->dev_irq_index = i; +- } +- +- mc_dev->irqs = irqs; +- return 0; +- +-error_resource_alloc: +- for (i = 0; i < res_allocated_count; i++) { +- irqs[i]->mc_dev = NULL; +- fsl_mc_resource_free(&irqs[i]->resource); +- } +- +- return error; +-} +-EXPORT_SYMBOL_GPL(fsl_mc_allocate_irqs); +- +-/* +- * Frees the IRQs that were allocated for an fsl-mc device. +- */ +-void fsl_mc_free_irqs(struct fsl_mc_device *mc_dev) +-{ +- int i; +- int irq_count; +- struct fsl_mc_bus *mc_bus; +- struct fsl_mc_device_irq **irqs = mc_dev->irqs; +- +- if (WARN_ON(!irqs)) +- return; +- +- irq_count = mc_dev->obj_desc.irq_count; +- +- if (strcmp(mc_dev->obj_desc.type, "dprc") == 0) +- mc_bus = to_fsl_mc_bus(mc_dev); +- else +- mc_bus = to_fsl_mc_bus(to_fsl_mc_device(mc_dev->dev.parent)); +- +- if (WARN_ON(!mc_bus->irq_resources)) +- return; +- +- for (i = 0; i < irq_count; i++) { +- WARN_ON(!irqs[i]->mc_dev); +- irqs[i]->mc_dev = NULL; +- fsl_mc_resource_free(&irqs[i]->resource); +- } +- +- mc_dev->irqs = NULL; +-} +-EXPORT_SYMBOL_GPL(fsl_mc_free_irqs); +- +-void fsl_mc_init_all_resource_pools(struct fsl_mc_device *mc_bus_dev) +-{ +- int pool_type; +- struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev); +- +- for (pool_type = 0; pool_type < FSL_MC_NUM_POOL_TYPES; pool_type++) { +- struct fsl_mc_resource_pool *res_pool = +- &mc_bus->resource_pools[pool_type]; +- +- res_pool->type = pool_type; +- res_pool->max_count = 0; +- res_pool->free_count = 0; +- res_pool->mc_bus = mc_bus; +- INIT_LIST_HEAD(&res_pool->free_list); +- mutex_init(&res_pool->mutex); +- } +-} +- +-static void fsl_mc_cleanup_resource_pool(struct fsl_mc_device *mc_bus_dev, +- enum fsl_mc_pool_type pool_type) +-{ +- struct fsl_mc_resource *resource; +- struct fsl_mc_resource *next; +- struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev); +- struct fsl_mc_resource_pool *res_pool = +- &mc_bus->resource_pools[pool_type]; +- int free_count = 0; +- +- WARN_ON(res_pool->type != pool_type); +- WARN_ON(res_pool->free_count != res_pool->max_count); +- +- list_for_each_entry_safe(resource, next, &res_pool->free_list, node) { +- free_count++; +- WARN_ON(resource->type != res_pool->type); +- WARN_ON(resource->parent_pool != res_pool); +- devm_kfree(&mc_bus_dev->dev, resource); +- } +- +- WARN_ON(free_count != res_pool->free_count); +-} +- +-void fsl_mc_cleanup_all_resource_pools(struct fsl_mc_device *mc_bus_dev) +-{ +- int pool_type; +- +- for (pool_type = 0; pool_type < FSL_MC_NUM_POOL_TYPES; pool_type++) +- fsl_mc_cleanup_resource_pool(mc_bus_dev, pool_type); +-} +- +-/** +- * fsl_mc_allocator_probe - callback invoked when an allocatable device is +- * being added to the system +- */ +-static int fsl_mc_allocator_probe(struct fsl_mc_device *mc_dev) +-{ +- enum fsl_mc_pool_type pool_type; +- struct fsl_mc_device *mc_bus_dev; +- struct fsl_mc_bus *mc_bus; +- int error; +- +- if (WARN_ON(!fsl_mc_is_allocatable(mc_dev->obj_desc.type))) +- return -EINVAL; +- +- mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent); +- if (WARN_ON(!dev_is_fsl_mc(&mc_bus_dev->dev))) +- return -EINVAL; +- +- mc_bus = to_fsl_mc_bus(mc_bus_dev); +- error = object_type_to_pool_type(mc_dev->obj_desc.type, &pool_type); +- if (error < 0) +- return error; +- +- error = fsl_mc_resource_pool_add_device(mc_bus, pool_type, mc_dev); +- if (error < 0) +- return error; +- +- dev_dbg(&mc_dev->dev, +- "Allocatable fsl-mc device bound to fsl_mc_allocator driver"); +- return 0; +-} +- +-/** +- * fsl_mc_allocator_remove - callback invoked when an allocatable device is +- * being removed from the system +- */ +-static int fsl_mc_allocator_remove(struct fsl_mc_device *mc_dev) +-{ +- int error; +- +- if (WARN_ON(!fsl_mc_is_allocatable(mc_dev->obj_desc.type))) +- return -EINVAL; +- +- if (mc_dev->resource) { +- error = fsl_mc_resource_pool_remove_device(mc_dev); +- if (error < 0) +- return error; +- } +- +- dev_dbg(&mc_dev->dev, +- "Allocatable fsl-mc device unbound from fsl_mc_allocator driver"); +- return 0; +-} +- +-static const struct fsl_mc_device_id match_id_table[] = { +- { +- .vendor = FSL_MC_VENDOR_FREESCALE, +- .obj_type = "dpbp", +- }, +- { +- .vendor = FSL_MC_VENDOR_FREESCALE, +- .obj_type = "dpmcp", +- }, +- { +- .vendor = FSL_MC_VENDOR_FREESCALE, +- .obj_type = "dpcon", +- }, +- {.vendor = 0x0}, +-}; +- +-static struct fsl_mc_driver fsl_mc_allocator_driver = { +- .driver = { +- .name = "fsl_mc_allocator", +- .pm = NULL, +- }, +- .match_id_table = match_id_table, +- .probe = fsl_mc_allocator_probe, +- .remove = fsl_mc_allocator_remove, +-}; +- +-int __init fsl_mc_allocator_driver_init(void) +-{ +- return fsl_mc_driver_register(&fsl_mc_allocator_driver); +-} +- +-void fsl_mc_allocator_driver_exit(void) +-{ +- fsl_mc_driver_unregister(&fsl_mc_allocator_driver); +-} +--- /dev/null ++++ b/drivers/bus/fsl-mc/fsl-mc-allocator.c +@@ -0,0 +1,655 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * fsl-mc object allocator driver ++ * ++ * Copyright (C) 2013-2016 Freescale Semiconductor, Inc. ++ * ++ */ ++ ++#include <linux/module.h> ++#include <linux/msi.h> ++#include <linux/fsl/mc.h> ++ ++#include "fsl-mc-private.h" ++ ++static bool __must_check fsl_mc_is_allocatable(struct fsl_mc_device *mc_dev) ++{ ++ return is_fsl_mc_bus_dpbp(mc_dev) || ++ is_fsl_mc_bus_dpmcp(mc_dev) || ++ is_fsl_mc_bus_dpcon(mc_dev); ++} ++ ++/** ++ * fsl_mc_resource_pool_add_device - add allocatable object to a resource ++ * pool of a given fsl-mc bus ++ * ++ * @mc_bus: pointer to the fsl-mc bus ++ * @pool_type: pool type ++ * @mc_dev: pointer to allocatable fsl-mc device ++ */ ++static int __must_check fsl_mc_resource_pool_add_device(struct fsl_mc_bus ++ *mc_bus, ++ enum fsl_mc_pool_type ++ pool_type, ++ struct fsl_mc_device ++ *mc_dev) ++{ ++ struct fsl_mc_resource_pool *res_pool; ++ struct fsl_mc_resource *resource; ++ struct fsl_mc_device *mc_bus_dev = &mc_bus->mc_dev; ++ int error = -EINVAL; ++ ++ if (pool_type < 0 || pool_type >= FSL_MC_NUM_POOL_TYPES) ++ goto out; ++ if (!fsl_mc_is_allocatable(mc_dev)) ++ goto out; ++ if (mc_dev->resource) ++ goto out; ++ ++ res_pool = &mc_bus->resource_pools[pool_type]; ++ if (res_pool->type != pool_type) ++ goto out; ++ if (res_pool->mc_bus != mc_bus) ++ goto out; ++ ++ mutex_lock(&res_pool->mutex); ++ ++ if (res_pool->max_count < 0) ++ goto out_unlock; ++ if (res_pool->free_count < 0 || ++ res_pool->free_count > res_pool->max_count) ++ goto out_unlock; ++ ++ resource = devm_kzalloc(&mc_bus_dev->dev, sizeof(*resource), ++ GFP_KERNEL); ++ if (!resource) { ++ error = -ENOMEM; ++ dev_err(&mc_bus_dev->dev, ++ "Failed to allocate memory for fsl_mc_resource\n"); ++ goto out_unlock; ++ } ++ ++ resource->type = pool_type; ++ resource->id = mc_dev->obj_desc.id; ++ resource->data = mc_dev; ++ resource->parent_pool = res_pool; ++ INIT_LIST_HEAD(&resource->node); ++ list_add_tail(&resource->node, &res_pool->free_list); ++ mc_dev->resource = resource; ++ res_pool->free_count++; ++ res_pool->max_count++; ++ error = 0; ++out_unlock: ++ mutex_unlock(&res_pool->mutex); ++out: ++ return error; ++} ++ ++/** ++ * fsl_mc_resource_pool_remove_device - remove an allocatable device from a ++ * resource pool ++ * ++ * @mc_dev: pointer to allocatable fsl-mc device ++ * ++ * It permanently removes an allocatable fsl-mc device from the resource ++ * pool. It's an error if the device is in use. ++ */ ++static int __must_check fsl_mc_resource_pool_remove_device(struct fsl_mc_device ++ *mc_dev) ++{ ++ struct fsl_mc_device *mc_bus_dev; ++ struct fsl_mc_bus *mc_bus; ++ struct fsl_mc_resource_pool *res_pool; ++ struct fsl_mc_resource *resource; ++ int error = -EINVAL; ++ ++ if (!fsl_mc_is_allocatable(mc_dev)) ++ goto out; ++ ++ resource = mc_dev->resource; ++ if (!resource || resource->data != mc_dev) ++ goto out; ++ ++ mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent); ++ mc_bus = to_fsl_mc_bus(mc_bus_dev); ++ res_pool = resource->parent_pool; ++ if (res_pool != &mc_bus->resource_pools[resource->type]) ++ goto out; ++ ++ mutex_lock(&res_pool->mutex); ++ ++ if (res_pool->max_count <= 0) ++ goto out_unlock; ++ if (res_pool->free_count <= 0 || ++ res_pool->free_count > res_pool->max_count) ++ goto out_unlock; ++ ++ /* ++ * If the device is currently allocated, its resource is not ++ * in the free list and thus, the device cannot be removed. ++ */ ++ if (list_empty(&resource->node)) { ++ error = -EBUSY; ++ dev_err(&mc_bus_dev->dev, ++ "Device %s cannot be removed from resource pool\n", ++ dev_name(&mc_dev->dev)); ++ goto out_unlock; ++ } ++ ++ list_del_init(&resource->node); ++ res_pool->free_count--; ++ res_pool->max_count--; ++ ++ devm_kfree(&mc_bus_dev->dev, resource); ++ mc_dev->resource = NULL; ++ error = 0; ++out_unlock: ++ mutex_unlock(&res_pool->mutex); ++out: ++ return error; ++} ++ ++static const char *const fsl_mc_pool_type_strings[] = { ++ [FSL_MC_POOL_DPMCP] = "dpmcp", ++ [FSL_MC_POOL_DPBP] = "dpbp", ++ [FSL_MC_POOL_DPCON] = "dpcon", ++ [FSL_MC_POOL_IRQ] = "irq", ++}; ++ ++static int __must_check object_type_to_pool_type(const char *object_type, ++ enum fsl_mc_pool_type ++ *pool_type) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(fsl_mc_pool_type_strings); i++) { ++ if (strcmp(object_type, fsl_mc_pool_type_strings[i]) == 0) { ++ *pool_type = i; ++ return 0; ++ } ++ } ++ ++ return -EINVAL; ++} ++ ++int __must_check fsl_mc_resource_allocate(struct fsl_mc_bus *mc_bus, ++ enum fsl_mc_pool_type pool_type, ++ struct fsl_mc_resource **new_resource) ++{ ++ struct fsl_mc_resource_pool *res_pool; ++ struct fsl_mc_resource *resource; ++ struct fsl_mc_device *mc_bus_dev = &mc_bus->mc_dev; ++ int error = -EINVAL; ++ ++ BUILD_BUG_ON(ARRAY_SIZE(fsl_mc_pool_type_strings) != ++ FSL_MC_NUM_POOL_TYPES); ++ ++ *new_resource = NULL; ++ if (pool_type < 0 || pool_type >= FSL_MC_NUM_POOL_TYPES) ++ goto out; ++ ++ res_pool = &mc_bus->resource_pools[pool_type]; ++ if (res_pool->mc_bus != mc_bus) ++ goto out; ++ ++ mutex_lock(&res_pool->mutex); ++ resource = list_first_entry_or_null(&res_pool->free_list, ++ struct fsl_mc_resource, node); ++ ++ if (!resource) { ++ error = -ENXIO; ++ dev_err(&mc_bus_dev->dev, ++ "No more resources of type %s left\n", ++ fsl_mc_pool_type_strings[pool_type]); ++ goto out_unlock; ++ } ++ ++ if (resource->type != pool_type) ++ goto out_unlock; ++ if (resource->parent_pool != res_pool) ++ goto out_unlock; ++ if (res_pool->free_count <= 0 || ++ res_pool->free_count > res_pool->max_count) ++ goto out_unlock; ++ ++ list_del_init(&resource->node); ++ ++ res_pool->free_count--; ++ error = 0; ++out_unlock: ++ mutex_unlock(&res_pool->mutex); ++ *new_resource = resource; ++out: ++ return error; ++} ++EXPORT_SYMBOL_GPL(fsl_mc_resource_allocate); ++ ++void fsl_mc_resource_free(struct fsl_mc_resource *resource) ++{ ++ struct fsl_mc_resource_pool *res_pool; ++ ++ res_pool = resource->parent_pool; ++ if (resource->type != res_pool->type) ++ return; ++ ++ mutex_lock(&res_pool->mutex); ++ if (res_pool->free_count < 0 || ++ res_pool->free_count >= res_pool->max_count) ++ goto out_unlock; ++ ++ if (!list_empty(&resource->node)) ++ goto out_unlock; ++ ++ list_add_tail(&resource->node, &res_pool->free_list); ++ res_pool->free_count++; ++out_unlock: ++ mutex_unlock(&res_pool->mutex); ++} ++EXPORT_SYMBOL_GPL(fsl_mc_resource_free); ++ ++/** ++ * fsl_mc_object_allocate - Allocates an fsl-mc object of the given ++ * pool type from a given fsl-mc bus instance ++ * ++ * @mc_dev: fsl-mc device which is used in conjunction with the ++ * allocated object ++ * @pool_type: pool type ++ * @new_mc_dev: pointer to area where the pointer to the allocated device ++ * is to be returned ++ * ++ * Allocatable objects are always used in conjunction with some functional ++ * device. This function allocates an object of the specified type from ++ * the DPRC containing the functional device. ++ * ++ * NOTE: pool_type must be different from FSL_MC_POOL_MCP, since MC ++ * portals are allocated using fsl_mc_portal_allocate(), instead of ++ * this function. ++ */ ++int __must_check fsl_mc_object_allocate(struct fsl_mc_device *mc_dev, ++ enum fsl_mc_pool_type pool_type, ++ struct fsl_mc_device **new_mc_adev) ++{ ++ struct fsl_mc_device *mc_bus_dev; ++ struct fsl_mc_bus *mc_bus; ++ struct fsl_mc_device *mc_adev; ++ int error = -EINVAL; ++ struct fsl_mc_resource *resource = NULL; ++ ++ *new_mc_adev = NULL; ++ if (mc_dev->flags & FSL_MC_IS_DPRC) ++ goto error; ++ ++ if (!dev_is_fsl_mc(mc_dev->dev.parent)) ++ goto error; ++ ++ if (pool_type == FSL_MC_POOL_DPMCP) ++ goto error; ++ ++ mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent); ++ mc_bus = to_fsl_mc_bus(mc_bus_dev); ++ error = fsl_mc_resource_allocate(mc_bus, pool_type, &resource); ++ if (error < 0) ++ goto error; ++ ++ mc_adev = resource->data; ++ if (!mc_adev) ++ goto error; ++ ++ *new_mc_adev = mc_adev; ++ return 0; ++error: ++ if (resource) ++ fsl_mc_resource_free(resource); ++ ++ return error; ++} ++EXPORT_SYMBOL_GPL(fsl_mc_object_allocate); ++ ++/** ++ * fsl_mc_object_free - Returns an fsl-mc object to the resource ++ * pool where it came from. ++ * @mc_adev: Pointer to the fsl-mc device ++ */ ++void fsl_mc_object_free(struct fsl_mc_device *mc_adev) ++{ ++ struct fsl_mc_resource *resource; ++ ++ resource = mc_adev->resource; ++ if (resource->type == FSL_MC_POOL_DPMCP) ++ return; ++ if (resource->data != mc_adev) ++ return; ++ ++ fsl_mc_resource_free(resource); ++} ++EXPORT_SYMBOL_GPL(fsl_mc_object_free); ++ ++/* ++ * A DPRC and the devices in the DPRC all share the same GIC-ITS device ++ * ID. A block of IRQs is pre-allocated and maintained in a pool ++ * from which devices can allocate them when needed. ++ */ ++ ++/* ++ * Initialize the interrupt pool associated with an fsl-mc bus. ++ * It allocates a block of IRQs from the GIC-ITS. ++ */ ++int fsl_mc_populate_irq_pool(struct fsl_mc_bus *mc_bus, ++ unsigned int irq_count) ++{ ++ unsigned int i; ++ struct msi_desc *msi_desc; ++ struct fsl_mc_device_irq *irq_resources; ++ struct fsl_mc_device_irq *mc_dev_irq; ++ int error; ++ struct fsl_mc_device *mc_bus_dev = &mc_bus->mc_dev; ++ struct fsl_mc_resource_pool *res_pool = ++ &mc_bus->resource_pools[FSL_MC_POOL_IRQ]; ++ ++ if (irq_count == 0 || ++ irq_count > FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS) ++ return -EINVAL; ++ ++ error = fsl_mc_msi_domain_alloc_irqs(&mc_bus_dev->dev, irq_count); ++ if (error < 0) ++ return error; ++ ++ irq_resources = devm_kzalloc(&mc_bus_dev->dev, ++ sizeof(*irq_resources) * irq_count, ++ GFP_KERNEL); ++ if (!irq_resources) { ++ error = -ENOMEM; ++ goto cleanup_msi_irqs; ++ } ++ ++ for (i = 0; i < irq_count; i++) { ++ mc_dev_irq = &irq_resources[i]; ++ ++ /* ++ * NOTE: This mc_dev_irq's MSI addr/value pair will be set ++ * by the fsl_mc_msi_write_msg() callback ++ */ ++ mc_dev_irq->resource.type = res_pool->type; ++ mc_dev_irq->resource.data = mc_dev_irq; ++ mc_dev_irq->resource.parent_pool = res_pool; ++ INIT_LIST_HEAD(&mc_dev_irq->resource.node); ++ list_add_tail(&mc_dev_irq->resource.node, &res_pool->free_list); ++ } ++ ++ for_each_msi_entry(msi_desc, &mc_bus_dev->dev) { ++ mc_dev_irq = &irq_resources[msi_desc->fsl_mc.msi_index]; ++ mc_dev_irq->msi_desc = msi_desc; ++ mc_dev_irq->resource.id = msi_desc->irq; ++ } ++ ++ res_pool->max_count = irq_count; ++ res_pool->free_count = irq_count; ++ mc_bus->irq_resources = irq_resources; ++ return 0; ++ ++cleanup_msi_irqs: ++ fsl_mc_msi_domain_free_irqs(&mc_bus_dev->dev); ++ return error; ++} ++EXPORT_SYMBOL_GPL(fsl_mc_populate_irq_pool); ++ ++/** ++ * Teardown the interrupt pool associated with an fsl-mc bus. ++ * It frees the IRQs that were allocated to the pool, back to the GIC-ITS. ++ */ ++void fsl_mc_cleanup_irq_pool(struct fsl_mc_bus *mc_bus) ++{ ++ struct fsl_mc_device *mc_bus_dev = &mc_bus->mc_dev; ++ struct fsl_mc_resource_pool *res_pool = ++ &mc_bus->resource_pools[FSL_MC_POOL_IRQ]; ++ ++ if (!mc_bus->irq_resources) ++ return; ++ ++ if (res_pool->max_count == 0) ++ return; ++ ++ if (res_pool->free_count != res_pool->max_count) ++ return; ++ ++ INIT_LIST_HEAD(&res_pool->free_list); ++ res_pool->max_count = 0; ++ res_pool->free_count = 0; ++ mc_bus->irq_resources = NULL; ++ fsl_mc_msi_domain_free_irqs(&mc_bus_dev->dev); ++} ++EXPORT_SYMBOL_GPL(fsl_mc_cleanup_irq_pool); ++ ++/** ++ * Allocate the IRQs required by a given fsl-mc device. ++ */ ++int __must_check fsl_mc_allocate_irqs(struct fsl_mc_device *mc_dev) ++{ ++ int i; ++ int irq_count; ++ int res_allocated_count = 0; ++ int error = -EINVAL; ++ struct fsl_mc_device_irq **irqs = NULL; ++ struct fsl_mc_bus *mc_bus; ++ struct fsl_mc_resource_pool *res_pool; ++ ++ if (mc_dev->irqs) ++ return -EINVAL; ++ ++ irq_count = mc_dev->obj_desc.irq_count; ++ if (irq_count == 0) ++ return -EINVAL; ++ ++ if (is_fsl_mc_bus_dprc(mc_dev)) ++ mc_bus = to_fsl_mc_bus(mc_dev); ++ else ++ mc_bus = to_fsl_mc_bus(to_fsl_mc_device(mc_dev->dev.parent)); ++ ++ if (!mc_bus->irq_resources) ++ return -EINVAL; ++ ++ res_pool = &mc_bus->resource_pools[FSL_MC_POOL_IRQ]; ++ if (res_pool->free_count < irq_count) { ++ dev_err(&mc_dev->dev, ++ "Not able to allocate %u irqs for device\n", irq_count); ++ return -ENOSPC; ++ } ++ ++ irqs = devm_kzalloc(&mc_dev->dev, irq_count * sizeof(irqs[0]), ++ GFP_KERNEL); ++ if (!irqs) ++ return -ENOMEM; ++ ++ for (i = 0; i < irq_count; i++) { ++ struct fsl_mc_resource *resource; ++ ++ error = fsl_mc_resource_allocate(mc_bus, FSL_MC_POOL_IRQ, ++ &resource); ++ if (error < 0) ++ goto error_resource_alloc; ++ ++ irqs[i] = to_fsl_mc_irq(resource); ++ res_allocated_count++; ++ ++ irqs[i]->mc_dev = mc_dev; ++ irqs[i]->dev_irq_index = i; ++ } ++ ++ mc_dev->irqs = irqs; ++ return 0; ++ ++error_resource_alloc: ++ for (i = 0; i < res_allocated_count; i++) { ++ irqs[i]->mc_dev = NULL; ++ fsl_mc_resource_free(&irqs[i]->resource); ++ } ++ ++ return error; ++} ++EXPORT_SYMBOL_GPL(fsl_mc_allocate_irqs); ++ ++/* ++ * Frees the IRQs that were allocated for an fsl-mc device. ++ */ ++void fsl_mc_free_irqs(struct fsl_mc_device *mc_dev) ++{ ++ int i; ++ int irq_count; ++ struct fsl_mc_bus *mc_bus; ++ struct fsl_mc_device_irq **irqs = mc_dev->irqs; ++ ++ if (!irqs) ++ return; ++ ++ irq_count = mc_dev->obj_desc.irq_count; ++ ++ if (is_fsl_mc_bus_dprc(mc_dev)) ++ mc_bus = to_fsl_mc_bus(mc_dev); ++ else ++ mc_bus = to_fsl_mc_bus(to_fsl_mc_device(mc_dev->dev.parent)); ++ ++ if (!mc_bus->irq_resources) ++ return; ++ ++ for (i = 0; i < irq_count; i++) { ++ irqs[i]->mc_dev = NULL; ++ fsl_mc_resource_free(&irqs[i]->resource); ++ } ++ ++ mc_dev->irqs = NULL; ++} ++EXPORT_SYMBOL_GPL(fsl_mc_free_irqs); ++ ++void fsl_mc_init_all_resource_pools(struct fsl_mc_device *mc_bus_dev) ++{ ++ int pool_type; ++ struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev); ++ ++ for (pool_type = 0; pool_type < FSL_MC_NUM_POOL_TYPES; pool_type++) { ++ struct fsl_mc_resource_pool *res_pool = ++ &mc_bus->resource_pools[pool_type]; ++ ++ res_pool->type = pool_type; ++ res_pool->max_count = 0; ++ res_pool->free_count = 0; ++ res_pool->mc_bus = mc_bus; ++ INIT_LIST_HEAD(&res_pool->free_list); ++ mutex_init(&res_pool->mutex); ++ } ++} ++EXPORT_SYMBOL_GPL(fsl_mc_init_all_resource_pools); ++ ++static void fsl_mc_cleanup_resource_pool(struct fsl_mc_device *mc_bus_dev, ++ enum fsl_mc_pool_type pool_type) ++{ ++ struct fsl_mc_resource *resource; ++ struct fsl_mc_resource *next; ++ struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev); ++ struct fsl_mc_resource_pool *res_pool = ++ &mc_bus->resource_pools[pool_type]; ++ int free_count = 0; ++ ++ list_for_each_entry_safe(resource, next, &res_pool->free_list, node) { ++ free_count++; ++ devm_kfree(&mc_bus_dev->dev, resource); ++ } ++} ++ ++void fsl_mc_cleanup_all_resource_pools(struct fsl_mc_device *mc_bus_dev) ++{ ++ int pool_type; ++ ++ for (pool_type = 0; pool_type < FSL_MC_NUM_POOL_TYPES; pool_type++) ++ fsl_mc_cleanup_resource_pool(mc_bus_dev, pool_type); ++} ++EXPORT_SYMBOL_GPL(fsl_mc_cleanup_all_resource_pools); ++ ++/** ++ * fsl_mc_allocator_probe - callback invoked when an allocatable device is ++ * being added to the system ++ */ ++static int fsl_mc_allocator_probe(struct fsl_mc_device *mc_dev) ++{ ++ enum fsl_mc_pool_type pool_type; ++ struct fsl_mc_device *mc_bus_dev; ++ struct fsl_mc_bus *mc_bus; ++ int error; ++ ++ if (!fsl_mc_is_allocatable(mc_dev)) ++ return -EINVAL; ++ ++ mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent); ++ if (!dev_is_fsl_mc(&mc_bus_dev->dev)) ++ return -EINVAL; ++ ++ mc_bus = to_fsl_mc_bus(mc_bus_dev); ++ error = object_type_to_pool_type(mc_dev->obj_desc.type, &pool_type); ++ if (error < 0) ++ return error; ++ ++ error = fsl_mc_resource_pool_add_device(mc_bus, pool_type, mc_dev); ++ if (error < 0) ++ return error; ++ ++ dev_dbg(&mc_dev->dev, ++ "Allocatable fsl-mc device bound to fsl_mc_allocator driver"); ++ return 0; ++} ++ ++/** ++ * fsl_mc_allocator_remove - callback invoked when an allocatable device is ++ * being removed from the system ++ */ ++static int fsl_mc_allocator_remove(struct fsl_mc_device *mc_dev) ++{ ++ int error; ++ ++ if (!fsl_mc_is_allocatable(mc_dev)) ++ return -EINVAL; ++ ++ if (mc_dev->resource) { ++ error = fsl_mc_resource_pool_remove_device(mc_dev); ++ if (error < 0) ++ return error; ++ } ++ ++ dev_dbg(&mc_dev->dev, ++ "Allocatable fsl-mc device unbound from fsl_mc_allocator driver"); ++ return 0; ++} ++ ++static const struct fsl_mc_device_id match_id_table[] = { ++ { ++ .vendor = FSL_MC_VENDOR_FREESCALE, ++ .obj_type = "dpbp", ++ }, ++ { ++ .vendor = FSL_MC_VENDOR_FREESCALE, ++ .obj_type = "dpmcp", ++ }, ++ { ++ .vendor = FSL_MC_VENDOR_FREESCALE, ++ .obj_type = "dpcon", ++ }, ++ {.vendor = 0x0}, ++}; ++ ++static struct fsl_mc_driver fsl_mc_allocator_driver = { ++ .driver = { ++ .name = "fsl_mc_allocator", ++ .pm = NULL, ++ }, ++ .match_id_table = match_id_table, ++ .probe = fsl_mc_allocator_probe, ++ .remove = fsl_mc_allocator_remove, ++}; ++ ++int __init fsl_mc_allocator_driver_init(void) ++{ ++ return fsl_mc_driver_register(&fsl_mc_allocator_driver); ++} ++ ++void fsl_mc_allocator_driver_exit(void) ++{ ++ fsl_mc_driver_unregister(&fsl_mc_allocator_driver); ++} +--- a/drivers/staging/fsl-mc/bus/fsl-mc-bus.c ++++ /dev/null +@@ -1,900 +0,0 @@ +-// SPDX-License-Identifier: GPL-2.0 +-/* +- * Freescale Management Complex (MC) bus driver +- * +- * Copyright (C) 2014-2016 Freescale Semiconductor, Inc. +- * Author: German Rivera <German.Rivera@freescale.com> +- * +- */ +- +-#define pr_fmt(fmt) "fsl-mc: " fmt +- +-#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 <linux/bitops.h> +-#include <linux/msi.h> +-#include <linux/dma-mapping.h> +- +-#include "fsl-mc-private.h" +-#include "dprc-cmd.h" +-#include "dpmng-cmd.h" +- +-/** +- * Default DMA mask for devices on a fsl-mc bus +- */ +-#define FSL_MC_DEFAULT_DMA_MASK (~0ULL) +- +-/** +- * struct fsl_mc - Private data of a "fsl,qoriq-mc" platform device +- * @root_mc_bus_dev: fsl-mc device representing the root DPRC +- * @num_translation_ranges: number of entries in addr_translation_ranges +- * @translation_ranges: array of bus to system address translation ranges +- */ +-struct fsl_mc { +- struct fsl_mc_device *root_mc_bus_dev; +- u8 num_translation_ranges; +- struct fsl_mc_addr_translation_range *translation_ranges; +-}; +- +-/** +- * struct fsl_mc_addr_translation_range - bus to system address translation +- * range +- * @mc_region_type: Type of MC region for the range being translated +- * @start_mc_offset: Start MC offset of the range being translated +- * @end_mc_offset: MC offset of the first byte after the range (last MC +- * offset of the range is end_mc_offset - 1) +- * @start_phys_addr: system physical address corresponding to start_mc_addr +- */ +-struct fsl_mc_addr_translation_range { +- enum dprc_region_type mc_region_type; +- u64 start_mc_offset; +- u64 end_mc_offset; +- phys_addr_t start_phys_addr; +-}; +- +-/** +- * struct mc_version +- * @major: Major version number: incremented on API compatibility changes +- * @minor: Minor version number: incremented on API additions (that are +- * backward compatible); reset when major version is incremented +- * @revision: Internal revision number: incremented on implementation changes +- * and/or bug fixes that have no impact on API +- */ +-struct mc_version { +- u32 major; +- u32 minor; +- u32 revision; +-}; +- +-/** +- * fsl_mc_bus_match - device to driver matching callback +- * @dev: the fsl-mc device to match against +- * @drv: the device driver to search for matching fsl-mc object type +- * 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_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 (!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 & FSL_MC_OBJ_STATE_PLUGGED) == 0 && +- !fsl_mc_is_root_dprc(&mc_dev->dev)) +- goto out; +- +- /* +- * Traverse the match_id table of the given driver, trying to find +- * a matching for the given 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) { +- 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) +-{ +- struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); +- +- if (add_uevent_var(env, "MODALIAS=fsl-mc:v%08Xd%s", +- mc_dev->obj_desc.vendor, +- mc_dev->obj_desc.type)) +- return -ENOMEM; +- +- return 0; +-} +- +-static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, +- char *buf) +-{ +- struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); +- +- return sprintf(buf, "fsl-mc:v%08Xd%s\n", mc_dev->obj_desc.vendor, +- mc_dev->obj_desc.type); +-} +-static DEVICE_ATTR_RO(modalias); +- +-static struct attribute *fsl_mc_dev_attrs[] = { +- &dev_attr_modalias.attr, +- NULL, +-}; +- +-ATTRIBUTE_GROUPS(fsl_mc_dev); +- +-struct bus_type fsl_mc_bus_type = { +- .name = "fsl-mc", +- .match = fsl_mc_bus_match, +- .uevent = fsl_mc_bus_uevent, +- .dev_groups = fsl_mc_dev_groups, +-}; +-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, "%s failed: %d\n", __func__, 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, "%s failed: %d\n", __func__, 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; +- } +- +- 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); +- +-/** +- * mc_get_version() - Retrieves the Management Complex firmware +- * version information +- * @mc_io: Pointer to opaque I/O object +- * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' +- * @mc_ver_info: Returned version information structure +- * +- * Return: '0' on Success; Error code otherwise. +- */ +-static int mc_get_version(struct fsl_mc_io *mc_io, +- u32 cmd_flags, +- struct mc_version *mc_ver_info) +-{ +- struct mc_command cmd = { 0 }; +- struct dpmng_rsp_get_version *rsp_params; +- int err; +- +- /* prepare command */ +- cmd.header = mc_encode_cmd_header(DPMNG_CMDID_GET_VERSION, +- cmd_flags, +- 0); +- +- /* send command to mc*/ +- err = mc_send_command(mc_io, &cmd); +- if (err) +- return err; +- +- /* retrieve response parameters */ +- rsp_params = (struct dpmng_rsp_get_version *)cmd.params; +- mc_ver_info->revision = le32_to_cpu(rsp_params->revision); +- mc_ver_info->major = le32_to_cpu(rsp_params->version_major); +- mc_ver_info->minor = le32_to_cpu(rsp_params->version_minor); +- +- return 0; +-} +- +-/** +- * fsl_mc_get_root_dprc - function to traverse to the root dprc +- */ +-static void fsl_mc_get_root_dprc(struct device *dev, +- struct device **root_dprc_dev) +-{ +- if (WARN_ON(!dev)) { +- *root_dprc_dev = NULL; +- } else if (WARN_ON(!dev_is_fsl_mc(dev))) { +- *root_dprc_dev = NULL; +- } else { +- *root_dprc_dev = dev; +- while (dev_is_fsl_mc((*root_dprc_dev)->parent)) +- *root_dprc_dev = (*root_dprc_dev)->parent; +- } +-} +- +-static int get_dprc_attr(struct fsl_mc_io *mc_io, +- int container_id, struct dprc_attributes *attr) +-{ +- u16 dprc_handle; +- int error; +- +- error = dprc_open(mc_io, 0, container_id, &dprc_handle); +- if (error < 0) { +- dev_err(mc_io->dev, "dprc_open() failed: %d\n", error); +- return error; +- } +- +- memset(attr, 0, sizeof(struct dprc_attributes)); +- error = dprc_get_attributes(mc_io, 0, dprc_handle, attr); +- if (error < 0) { +- dev_err(mc_io->dev, "dprc_get_attributes() failed: %d\n", +- error); +- goto common_cleanup; +- } +- +- error = 0; +- +-common_cleanup: +- (void)dprc_close(mc_io, 0, dprc_handle); +- return error; +-} +- +-static int get_dprc_icid(struct fsl_mc_io *mc_io, +- int container_id, u16 *icid) +-{ +- struct dprc_attributes attr; +- int error; +- +- error = get_dprc_attr(mc_io, container_id, &attr); +- if (error == 0) +- *icid = attr.icid; +- +- return error; +-} +- +-static int translate_mc_addr(struct fsl_mc_device *mc_dev, +- enum dprc_region_type mc_region_type, +- u64 mc_offset, phys_addr_t *phys_addr) +-{ +- int i; +- struct device *root_dprc_dev; +- struct fsl_mc *mc; +- +- fsl_mc_get_root_dprc(&mc_dev->dev, &root_dprc_dev); +- if (WARN_ON(!root_dprc_dev)) +- return -EINVAL; +- mc = dev_get_drvdata(root_dprc_dev->parent); +- +- if (mc->num_translation_ranges == 0) { +- /* +- * Do identity mapping: +- */ +- *phys_addr = mc_offset; +- return 0; +- } +- +- for (i = 0; i < mc->num_translation_ranges; i++) { +- struct fsl_mc_addr_translation_range *range = +- &mc->translation_ranges[i]; +- +- if (mc_region_type == range->mc_region_type && +- mc_offset >= range->start_mc_offset && +- mc_offset < range->end_mc_offset) { +- *phys_addr = range->start_phys_addr + +- (mc_offset - range->start_mc_offset); +- 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 fsl_mc_obj_desc *obj_desc = &mc_dev->obj_desc; +- struct device *parent_dev = mc_dev->dev.parent; +- enum dprc_region_type mc_region_type; +- +- if (strcmp(obj_desc->type, "dprc") == 0 || +- strcmp(obj_desc->type, "dpmcp") == 0) { +- mc_region_type = DPRC_REGION_TYPE_MC_PORTAL; +- } else if (strcmp(obj_desc->type, "dpio") == 0) { +- mc_region_type = DPRC_REGION_TYPE_QBMAN_PORTAL; +- } else { +- /* +- * This function should not have been called for this MC object +- * type, as this object type is not supposed to have MMIO +- * regions +- */ +- WARN_ON(true); +- return -EINVAL; +- } +- +- 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, +- 0, +- mc_bus_dev->mc_handle, +- obj_desc->type, +- obj_desc->id, i, ®ion_desc); +- if (error < 0) { +- dev_err(parent_dev, +- "dprc_get_obj_region() failed: %d\n", error); +- goto error_cleanup_regions; +- } +- +- WARN_ON(region_desc.size == 0); +- error = translate_mc_addr(mc_dev, mc_region_type, +- region_desc.base_offset, +- ®ions[i].start); +- if (error < 0) { +- dev_err(parent_dev, +- "Invalid MC offset: %#x (for %s.%d\'s region %d)\n", +- region_desc.base_offset, +- obj_desc->type, obj_desc->id, i); +- 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; +- if (region_desc.flags & DPRC_REGION_CACHEABLE) +- regions[i].flags |= IORESOURCE_CACHEABLE; +- } +- +- mc_dev->regions = regions; +- return 0; +- +-error_cleanup_regions: +- kfree(regions); +- return error; +-} +- +-/** +- * fsl_mc_is_root_dprc - function to check if a given device is a root dprc +- */ +-bool fsl_mc_is_root_dprc(struct device *dev) +-{ +- struct device *root_dprc_dev; +- +- fsl_mc_get_root_dprc(dev, &root_dprc_dev); +- if (!root_dprc_dev) +- return false; +- return dev == root_dprc_dev; +-} +- +-static void fsl_mc_device_release(struct device *dev) +-{ +- struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); +- +- kfree(mc_dev->regions); +- +- if (strcmp(mc_dev->obj_desc.type, "dprc") == 0) +- kfree(to_fsl_mc_bus(mc_dev)); +- else +- kfree(mc_dev); +-} +- +-/** +- * Add a newly discovered fsl-mc device to be visible in Linux +- */ +-int fsl_mc_device_add(struct fsl_mc_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 (dev_is_fsl_mc(parent_dev)) +- 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 = kzalloc(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 = kzalloc(sizeof(*mc_dev), 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; +- mc_dev->dev.release = fsl_mc_device_release; +- dev_set_name(&mc_dev->dev, "%s.%d", 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; +- } +- +- error = get_dprc_icid(mc_io2, obj_desc->id, &mc_dev->icid); +- if (error < 0) +- goto error_cleanup_dev; +- } else { +- /* +- * A non-DPRC object has to be a child of a DPRC, use the +- * parent's ICID and interrupt domain. +- */ +- 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; +- dev_set_msi_domain(&mc_dev->dev, +- dev_get_msi_domain(&parent_mc_dev->dev)); +- } +- +- /* +- * 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; +- } +- +- /* Objects are coherent, unless 'no shareability' flag set. */ +- if (!(obj_desc->flags & FSL_MC_OBJ_FLAG_NO_MEM_SHAREABILITY)) +- arch_setup_dma_ops(&mc_dev->dev, 0, 0, NULL, true); +- +- /* +- * 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; +- } +- +- dev_dbg(parent_dev, "added %s\n", dev_name(&mc_dev->dev)); +- +- *new_mc_dev = mc_dev; +- return 0; +- +-error_cleanup_dev: +- kfree(mc_dev->regions); +- kfree(mc_bus); +- kfree(mc_dev); +- +- return error; +-} +-EXPORT_SYMBOL_GPL(fsl_mc_device_add); +- +-/** +- * fsl_mc_device_remove - Remove an fsl-mc device from being visible to +- * Linux +- * +- * @mc_dev: Pointer to an fsl-mc device +- */ +-void fsl_mc_device_remove(struct fsl_mc_device *mc_dev) +-{ +- /* +- * The device-specific remove callback will get invoked by device_del() +- */ +- device_del(&mc_dev->dev); +- put_device(&mc_dev->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) +-{ +- 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); +- 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; +- } +- +- return ranges_len / tuple_len; +-} +- +-static int get_mc_addr_translation_ranges(struct device *dev, +- struct fsl_mc_addr_translation_range +- **ranges, +- u8 *num_ranges) +-{ +- int ret; +- int paddr_cells; +- int mc_addr_cells; +- int mc_size_cells; +- int i; +- const __be32 *ranges_start; +- const __be32 *cell; +- +- ret = parse_mc_ranges(dev, +- &paddr_cells, +- &mc_addr_cells, +- &mc_size_cells, +- &ranges_start); +- if (ret < 0) +- return ret; +- +- *num_ranges = ret; +- if (!ret) { +- /* +- * 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->mc_region_type = of_read_number(cell, 1); +- range->start_mc_offset = of_read_number(cell + 1, +- mc_addr_cells - 1); +- cell += mc_addr_cells; +- range->start_phys_addr = of_read_number(cell, paddr_cells); +- cell += paddr_cells; +- range->end_mc_offset = range->start_mc_offset + +- 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 fsl_mc_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; +- u32 mc_portal_size; +- struct mc_version mc_version; +- struct resource res; +- +- 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 %pOF\n", +- pdev->dev.of_node); +- 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, NULL, +- FSL_MC_IO_ATOMIC_CONTEXT_PORTAL, &mc_io); +- if (error < 0) +- return error; +- +- error = mc_get_version(mc_io, 0, &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, "MC firmware version: %u.%u.%u\n", +- mc_version.major, mc_version.minor, mc_version.revision); +- +- error = get_mc_addr_translation_ranges(&pdev->dev, +- &mc->translation_ranges, +- &mc->num_translation_ranges); +- if (error < 0) +- goto error_cleanup_mc_io; +- +- error = dprc_get_container_id(mc_io, 0, &container_id); +- if (error < 0) { +- dev_err(&pdev->dev, +- "dprc_get_container_id() failed: %d\n", error); +- goto error_cleanup_mc_io; +- } +- +- memset(&obj_desc, 0, sizeof(struct fsl_mc_obj_desc)); +- error = dprc_get_api_version(mc_io, 0, +- &obj_desc.ver_major, +- &obj_desc.ver_minor); +- if (error < 0) +- goto error_cleanup_mc_io; +- +- obj_desc.vendor = FSL_MC_VENDOR_FREESCALE; +- strcpy(obj_desc.type, "dprc"); +- obj_desc.id = container_id; +- obj_desc.irq_count = 1; +- 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(!fsl_mc_is_root_dprc(&mc->root_mc_bus_dev->dev))) +- return -EINVAL; +- +- fsl_mc_device_remove(mc->root_mc_bus_dev); +- +- fsl_destroy_mc_io(mc->root_mc_bus_dev->mc_io); +- mc->root_mc_bus_dev->mc_io = NULL; +- +- 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", +- .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; +- +- error = bus_register(&fsl_mc_bus_type); +- if (error < 0) { +- pr_err("bus type registration failed: %d\n", error); +- goto error_cleanup_cache; +- } +- +- error = platform_driver_register(&fsl_mc_bus_driver); +- if (error < 0) { +- pr_err("platform_driver_register() failed: %d\n", error); +- goto error_cleanup_bus; +- } +- +- error = dprc_driver_init(); +- if (error < 0) +- goto error_cleanup_driver; +- +- error = fsl_mc_allocator_driver_init(); +- if (error < 0) +- goto error_cleanup_dprc_driver; +- +- error = its_fsl_mc_msi_init(); +- if (error < 0) +- goto error_cleanup_mc_allocator; +- +- return 0; +- +-error_cleanup_mc_allocator: +- fsl_mc_allocator_driver_exit(); +- +-error_cleanup_dprc_driver: +- dprc_driver_exit(); +- +-error_cleanup_driver: +- platform_driver_unregister(&fsl_mc_bus_driver); +- +-error_cleanup_bus: +- bus_unregister(&fsl_mc_bus_type); +- +-error_cleanup_cache: +- return error; +-} +-postcore_initcall(fsl_mc_bus_driver_init); +--- /dev/null ++++ b/drivers/bus/fsl-mc/fsl-mc-bus.c +@@ -0,0 +1,1139 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Freescale Management Complex (MC) bus driver ++ * ++ * Copyright (C) 2014-2016 Freescale Semiconductor, Inc. ++ * Author: German Rivera <German.Rivera@freescale.com> ++ * ++ */ ++ ++#define pr_fmt(fmt) "fsl-mc: " fmt ++ ++#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 <linux/bitops.h> ++#include <linux/msi.h> ++#include <linux/dma-mapping.h> ++ ++#include "fsl-mc-private.h" ++ ++/** ++ * Default DMA mask for devices on a fsl-mc bus ++ */ ++#define FSL_MC_DEFAULT_DMA_MASK (~0ULL) ++ ++/** ++ * struct fsl_mc - Private data of a "fsl,qoriq-mc" platform device ++ * @root_mc_bus_dev: fsl-mc device representing the root DPRC ++ * @num_translation_ranges: number of entries in addr_translation_ranges ++ * @translation_ranges: array of bus to system address translation ranges ++ */ ++struct fsl_mc { ++ struct fsl_mc_device *root_mc_bus_dev; ++ u8 num_translation_ranges; ++ struct fsl_mc_addr_translation_range *translation_ranges; ++}; ++ ++/** ++ * struct fsl_mc_addr_translation_range - bus to system address translation ++ * range ++ * @mc_region_type: Type of MC region for the range being translated ++ * @start_mc_offset: Start MC offset of the range being translated ++ * @end_mc_offset: MC offset of the first byte after the range (last MC ++ * offset of the range is end_mc_offset - 1) ++ * @start_phys_addr: system physical address corresponding to start_mc_addr ++ */ ++struct fsl_mc_addr_translation_range { ++ enum dprc_region_type mc_region_type; ++ u64 start_mc_offset; ++ u64 end_mc_offset; ++ phys_addr_t start_phys_addr; ++}; ++ ++/** ++ * struct mc_version ++ * @major: Major version number: incremented on API compatibility changes ++ * @minor: Minor version number: incremented on API additions (that are ++ * backward compatible); reset when major version is incremented ++ * @revision: Internal revision number: incremented on implementation changes ++ * and/or bug fixes that have no impact on API ++ */ ++struct mc_version { ++ u32 major; ++ u32 minor; ++ u32 revision; ++}; ++ ++/** ++ * fsl_mc_bus_match - device to driver matching callback ++ * @dev: the fsl-mc device to match against ++ * @drv: the device driver to search for matching fsl-mc object type ++ * 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_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; ++ ++ /* When driver_override is set, only bind to the matching driver */ ++ if (mc_dev->driver_override) { ++ found = !strcmp(mc_dev->driver_override, mc_drv->driver.name); ++ 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 & FSL_MC_OBJ_STATE_PLUGGED) == 0 && ++ !fsl_mc_is_root_dprc(&mc_dev->dev)) ++ goto out; ++ ++ /* ++ * Traverse the match_id table of the given driver, trying to find ++ * a matching for the given 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) { ++ 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) ++{ ++ struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); ++ ++ if (add_uevent_var(env, "MODALIAS=fsl-mc:v%08Xd%s", ++ mc_dev->obj_desc.vendor, ++ mc_dev->obj_desc.type)) ++ return -ENOMEM; ++ ++ return 0; ++} ++ ++static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); ++ ++ return sprintf(buf, "fsl-mc:v%08Xd%s\n", mc_dev->obj_desc.vendor, ++ mc_dev->obj_desc.type); ++} ++static DEVICE_ATTR_RO(modalias); ++ ++static ssize_t rescan_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct fsl_mc_device *root_mc_dev; ++ struct fsl_mc_bus *root_mc_bus; ++ unsigned long val; ++ ++ if (!fsl_mc_is_root_dprc(dev)) ++ return -EINVAL; ++ ++ root_mc_dev = to_fsl_mc_device(dev); ++ root_mc_bus = to_fsl_mc_bus(root_mc_dev); ++ ++ if (kstrtoul(buf, 0, &val) < 0) ++ return -EINVAL; ++ ++ if (val) { ++ mutex_lock(&root_mc_bus->scan_mutex); ++ dprc_scan_objects(root_mc_dev, NULL, NULL); ++ mutex_unlock(&root_mc_bus->scan_mutex); ++ } ++ ++ return count; ++} ++static DEVICE_ATTR_WO(rescan); ++ ++static ssize_t driver_override_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); ++ const char *driver_override, *old = mc_dev->driver_override; ++ char *cp; ++ ++ if (WARN_ON(dev->bus != &fsl_mc_bus_type)) ++ return -EINVAL; ++ ++ if (count >= (PAGE_SIZE - 1)) ++ return -EINVAL; ++ ++ driver_override = kstrndup(buf, count, GFP_KERNEL); ++ if (!driver_override) ++ return -ENOMEM; ++ ++ cp = strchr(driver_override, '\n'); ++ if (cp) ++ *cp = '\0'; ++ ++ if (strlen(driver_override)) { ++ mc_dev->driver_override = driver_override; ++ } else { ++ kfree(driver_override); ++ mc_dev->driver_override = NULL; ++ } ++ ++ kfree(old); ++ ++ return count; ++} ++ ++static ssize_t driver_override_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); ++ ++ return snprintf(buf, PAGE_SIZE, "%s\n", mc_dev->driver_override); ++} ++static DEVICE_ATTR_RW(driver_override); ++ ++static struct attribute *fsl_mc_dev_attrs[] = { ++ &dev_attr_modalias.attr, ++ &dev_attr_rescan.attr, ++ &dev_attr_driver_override.attr, ++ NULL, ++}; ++ ++ATTRIBUTE_GROUPS(fsl_mc_dev); ++ ++static int scan_fsl_mc_bus(struct device *dev, void *data) ++{ ++ struct fsl_mc_device *root_mc_dev; ++ struct fsl_mc_bus *root_mc_bus; ++ ++ if (!fsl_mc_is_root_dprc(dev)) ++ goto exit; ++ ++ root_mc_dev = to_fsl_mc_device(dev); ++ root_mc_bus = to_fsl_mc_bus(root_mc_dev); ++ mutex_lock(&root_mc_bus->scan_mutex); ++ dprc_scan_objects(root_mc_dev, NULL, NULL); ++ mutex_unlock(&root_mc_bus->scan_mutex); ++ ++exit: ++ return 0; ++} ++ ++static ssize_t bus_rescan_store(struct bus_type *bus, ++ const char *buf, size_t count) ++{ ++ unsigned long val; ++ ++ if (kstrtoul(buf, 0, &val) < 0) ++ return -EINVAL; ++ ++ if (val) ++ bus_for_each_dev(bus, NULL, NULL, scan_fsl_mc_bus); ++ ++ return count; ++} ++static BUS_ATTR(rescan, 0220, NULL, bus_rescan_store); ++ ++static struct attribute *fsl_mc_bus_attrs[] = { ++ &bus_attr_rescan.attr, ++ NULL, ++}; ++ ++static const struct attribute_group fsl_mc_bus_group = { ++ .attrs = fsl_mc_bus_attrs, ++}; ++ ++static const struct attribute_group *fsl_mc_bus_groups[] = { ++ &fsl_mc_bus_group, ++ NULL, ++}; ++ ++struct bus_type fsl_mc_bus_type = { ++ .name = "fsl-mc", ++ .match = fsl_mc_bus_match, ++ .uevent = fsl_mc_bus_uevent, ++ .dev_groups = fsl_mc_dev_groups, ++ .bus_groups = fsl_mc_bus_groups, ++}; ++EXPORT_SYMBOL_GPL(fsl_mc_bus_type); ++ ++struct device_type fsl_mc_bus_dprc_type = { ++ .name = "fsl_mc_bus_dprc" ++}; ++ ++struct device_type fsl_mc_bus_dpni_type = { ++ .name = "fsl_mc_bus_dpni" ++}; ++ ++struct device_type fsl_mc_bus_dpio_type = { ++ .name = "fsl_mc_bus_dpio" ++}; ++ ++struct device_type fsl_mc_bus_dpsw_type = { ++ .name = "fsl_mc_bus_dpsw" ++}; ++ ++struct device_type fsl_mc_bus_dpdmux_type = { ++ .name = "fsl_mc_bus_dpdmux" ++}; ++ ++struct device_type fsl_mc_bus_dpbp_type = { ++ .name = "fsl_mc_bus_dpbp" ++}; ++ ++struct device_type fsl_mc_bus_dpcon_type = { ++ .name = "fsl_mc_bus_dpcon" ++}; ++ ++struct device_type fsl_mc_bus_dpmcp_type = { ++ .name = "fsl_mc_bus_dpmcp" ++}; ++ ++struct device_type fsl_mc_bus_dpmac_type = { ++ .name = "fsl_mc_bus_dpmac" ++}; ++ ++struct device_type fsl_mc_bus_dprtc_type = { ++ .name = "fsl_mc_bus_dprtc" ++}; ++ ++struct device_type fsl_mc_bus_dpseci_type = { ++ .name = "fsl_mc_bus_dpseci" ++}; ++ ++struct device_type fsl_mc_bus_dpdcei_type = { ++ .name = "fsl_mc_bus_dpdcei" ++}; ++ ++struct device_type fsl_mc_bus_dpaiop_type = { ++ .name = "fsl_mc_bus_dpaiop" ++}; ++ ++struct device_type fsl_mc_bus_dpci_type = { ++ .name = "fsl_mc_bus_dpci" ++}; ++ ++struct device_type fsl_mc_bus_dpdmai_type = { ++ .name = "fsl_mc_bus_dpdmai" ++}; ++ ++static struct device_type *fsl_mc_get_device_type(const char *type) ++{ ++ static const struct { ++ struct device_type *dev_type; ++ const char *type; ++ } dev_types[] = { ++ { &fsl_mc_bus_dprc_type, "dprc" }, ++ { &fsl_mc_bus_dpni_type, "dpni" }, ++ { &fsl_mc_bus_dpio_type, "dpio" }, ++ { &fsl_mc_bus_dpsw_type, "dpsw" }, ++ { &fsl_mc_bus_dpdmux_type, "dpdmux" }, ++ { &fsl_mc_bus_dpbp_type, "dpbp" }, ++ { &fsl_mc_bus_dpcon_type, "dpcon" }, ++ { &fsl_mc_bus_dpmcp_type, "dpmcp" }, ++ { &fsl_mc_bus_dpmac_type, "dpmac" }, ++ { &fsl_mc_bus_dprtc_type, "dprtc" }, ++ { &fsl_mc_bus_dpseci_type, "dpseci" }, ++ { &fsl_mc_bus_dpdcei_type, "dpdcei" }, ++ { &fsl_mc_bus_dpaiop_type, "dpaiop" }, ++ { &fsl_mc_bus_dpci_type, "dpci" }, ++ { &fsl_mc_bus_dpdmai_type, "dpdmai" }, ++ { NULL, NULL } ++ }; ++ int i; ++ ++ for (i = 0; dev_types[i].dev_type; i++) ++ if (!strcmp(dev_types[i].type, type)) ++ return dev_types[i].dev_type; ++ ++ return NULL; ++} ++ ++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; ++ ++ mc_drv = to_fsl_mc_driver(dev->driver); ++ ++ error = mc_drv->probe(mc_dev); ++ if (error < 0) { ++ if (error != -EPROBE_DEFER) ++ dev_err(dev, "%s failed: %d\n", __func__, 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; ++ ++ error = mc_drv->remove(mc_dev); ++ if (error < 0) { ++ dev_err(dev, "%s failed: %d\n", __func__, 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; ++ } ++ ++ 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); ++ ++/** ++ * mc_get_version() - Retrieves the Management Complex firmware ++ * version information ++ * @mc_io: Pointer to opaque I/O object ++ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' ++ * @mc_ver_info: Returned version information structure ++ * ++ * Return: '0' on Success; Error code otherwise. ++ */ ++static int mc_get_version(struct fsl_mc_io *mc_io, ++ u32 cmd_flags, ++ struct mc_version *mc_ver_info) ++{ ++ struct fsl_mc_command cmd = { 0 }; ++ struct dpmng_rsp_get_version *rsp_params; ++ int err; ++ ++ /* prepare command */ ++ cmd.header = mc_encode_cmd_header(DPMNG_CMDID_GET_VERSION, ++ cmd_flags, ++ 0); ++ ++ /* send command to mc*/ ++ err = mc_send_command(mc_io, &cmd); ++ if (err) ++ return err; ++ ++ /* retrieve response parameters */ ++ rsp_params = (struct dpmng_rsp_get_version *)cmd.params; ++ mc_ver_info->revision = le32_to_cpu(rsp_params->revision); ++ mc_ver_info->major = le32_to_cpu(rsp_params->version_major); ++ mc_ver_info->minor = le32_to_cpu(rsp_params->version_minor); ++ ++ return 0; ++} ++ ++/** ++ * fsl_mc_get_root_dprc - function to traverse to the root dprc ++ */ ++void fsl_mc_get_root_dprc(struct device *dev, ++ struct device **root_dprc_dev) ++{ ++ if (!dev) { ++ *root_dprc_dev = NULL; ++ } else if (!dev_is_fsl_mc(dev)) { ++ *root_dprc_dev = NULL; ++ } else { ++ *root_dprc_dev = dev; ++ while (dev_is_fsl_mc((*root_dprc_dev)->parent)) ++ *root_dprc_dev = (*root_dprc_dev)->parent; ++ } ++} ++EXPORT_SYMBOL_GPL(fsl_mc_get_root_dprc); ++ ++static int get_dprc_attr(struct fsl_mc_io *mc_io, ++ int container_id, struct dprc_attributes *attr) ++{ ++ u16 dprc_handle; ++ int error; ++ ++ error = dprc_open(mc_io, 0, container_id, &dprc_handle); ++ if (error < 0) { ++ dev_err(mc_io->dev, "dprc_open() failed: %d\n", error); ++ return error; ++ } ++ ++ memset(attr, 0, sizeof(struct dprc_attributes)); ++ error = dprc_get_attributes(mc_io, 0, dprc_handle, attr); ++ if (error < 0) { ++ dev_err(mc_io->dev, "dprc_get_attributes() failed: %d\n", ++ error); ++ goto common_cleanup; ++ } ++ ++ error = 0; ++ ++common_cleanup: ++ (void)dprc_close(mc_io, 0, dprc_handle); ++ return error; ++} ++ ++static int get_dprc_icid(struct fsl_mc_io *mc_io, ++ int container_id, u32 *icid) ++{ ++ struct dprc_attributes attr; ++ int error; ++ ++ error = get_dprc_attr(mc_io, container_id, &attr); ++ if (error == 0) ++ *icid = attr.icid; ++ ++ return error; ++} ++ ++static int translate_mc_addr(struct fsl_mc_device *mc_dev, ++ enum dprc_region_type mc_region_type, ++ u64 mc_offset, phys_addr_t *phys_addr) ++{ ++ int i; ++ struct device *root_dprc_dev; ++ struct fsl_mc *mc; ++ ++ fsl_mc_get_root_dprc(&mc_dev->dev, &root_dprc_dev); ++ mc = dev_get_drvdata(root_dprc_dev->parent); ++ ++ if (mc->num_translation_ranges == 0) { ++ /* ++ * Do identity mapping: ++ */ ++ *phys_addr = mc_offset; ++ return 0; ++ } ++ ++ for (i = 0; i < mc->num_translation_ranges; i++) { ++ struct fsl_mc_addr_translation_range *range = ++ &mc->translation_ranges[i]; ++ ++ if (mc_region_type == range->mc_region_type && ++ mc_offset >= range->start_mc_offset && ++ mc_offset < range->end_mc_offset) { ++ *phys_addr = range->start_phys_addr + ++ (mc_offset - range->start_mc_offset); ++ 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 fsl_mc_obj_desc *obj_desc = &mc_dev->obj_desc; ++ struct device *parent_dev = mc_dev->dev.parent; ++ enum dprc_region_type mc_region_type; ++ ++ if (is_fsl_mc_bus_dprc(mc_dev) || ++ is_fsl_mc_bus_dpmcp(mc_dev)) { ++ mc_region_type = DPRC_REGION_TYPE_MC_PORTAL; ++ } else if (is_fsl_mc_bus_dpio(mc_dev)) { ++ mc_region_type = DPRC_REGION_TYPE_QBMAN_PORTAL; ++ } else { ++ /* ++ * This function should not have been called for this MC object ++ * type, as this object type is not supposed to have MMIO ++ * regions ++ */ ++ return -EINVAL; ++ } ++ ++ 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, ++ 0, ++ mc_bus_dev->mc_handle, ++ obj_desc->type, ++ obj_desc->id, i, ®ion_desc); ++ if (error < 0) { ++ dev_err(parent_dev, ++ "dprc_get_obj_region() failed: %d\n", error); ++ goto error_cleanup_regions; ++ } ++ ++ error = translate_mc_addr(mc_dev, mc_region_type, ++ region_desc.base_offset, ++ ®ions[i].start); ++ if (error < 0) { ++ dev_err(parent_dev, ++ "Invalid MC offset: %#x (for %s.%d\'s region %d)\n", ++ region_desc.base_offset, ++ obj_desc->type, obj_desc->id, i); ++ 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; ++ if (region_desc.flags & DPRC_REGION_CACHEABLE) ++ regions[i].flags |= IORESOURCE_CACHEABLE; ++ } ++ ++ mc_dev->regions = regions; ++ return 0; ++ ++error_cleanup_regions: ++ kfree(regions); ++ return error; ++} ++ ++/** ++ * fsl_mc_is_root_dprc - function to check if a given device is a root dprc ++ */ ++bool fsl_mc_is_root_dprc(struct device *dev) ++{ ++ struct device *root_dprc_dev; ++ ++ fsl_mc_get_root_dprc(dev, &root_dprc_dev); ++ if (!root_dprc_dev) ++ return false; ++ return dev == root_dprc_dev; ++} ++ ++static void fsl_mc_device_release(struct device *dev) ++{ ++ struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); ++ ++ kfree(mc_dev->regions); ++ ++ if (is_fsl_mc_bus_dprc(mc_dev)) ++ kfree(to_fsl_mc_bus(mc_dev)); ++ else ++ kfree(mc_dev); ++} ++ ++/** ++ * Add a newly discovered fsl-mc device to be visible in Linux ++ */ ++int fsl_mc_device_add(struct fsl_mc_obj_desc *obj_desc, ++ struct fsl_mc_io *mc_io, ++ struct device *parent_dev, ++ const char *driver_override, ++ 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 (dev_is_fsl_mc(parent_dev)) ++ 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 = kzalloc(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 = kzalloc(sizeof(*mc_dev), GFP_KERNEL); ++ if (!mc_dev) ++ return -ENOMEM; ++ } ++ ++ mc_dev->obj_desc = *obj_desc; ++ mc_dev->mc_io = mc_io; ++ ++ if (driver_override) { ++ /* ++ * We trust driver_override, so we don't need to use ++ * kstrndup() here ++ */ ++ mc_dev->driver_override = kstrdup(driver_override, GFP_KERNEL); ++ if (!mc_dev->driver_override) { ++ error = -ENOMEM; ++ goto error_cleanup_dev; ++ } ++ } ++ ++ device_initialize(&mc_dev->dev); ++ mc_dev->dev.parent = parent_dev; ++ mc_dev->dev.bus = &fsl_mc_bus_type; ++ mc_dev->dev.release = fsl_mc_device_release; ++ mc_dev->dev.type = fsl_mc_get_device_type(obj_desc->type); ++ if (!mc_dev->dev.type) { ++ error = -ENODEV; ++ dev_err(parent_dev, "unknown device type %s\n", obj_desc->type); ++ goto error_cleanup_dev; ++ } ++ dev_set_name(&mc_dev->dev, "%s.%d", 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 (!mc_io) { ++ error = -EINVAL; ++ goto error_cleanup_dev; ++ } ++ ++ mc_io2 = mc_io; ++ } ++ ++ error = get_dprc_icid(mc_io2, obj_desc->id, &mc_dev->icid); ++ if (error < 0) ++ goto error_cleanup_dev; ++ } else { ++ /* ++ * A non-DPRC object has to be a child of a DPRC, use the ++ * parent's ICID and interrupt domain. ++ */ ++ 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; ++ mc_dev->dev.coherent_dma_mask = mc_dev->dma_mask; ++ dev_set_msi_domain(&mc_dev->dev, ++ dev_get_msi_domain(&parent_mc_dev->dev)); ++ } ++ ++ /* ++ * 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; ++ } ++ ++ dev_dbg(parent_dev, "added %s\n", dev_name(&mc_dev->dev)); ++ ++ *new_mc_dev = mc_dev; ++ return 0; ++ ++error_cleanup_dev: ++ kfree(mc_dev->regions); ++ kfree(mc_bus); ++ kfree(mc_dev); ++ ++ return error; ++} ++EXPORT_SYMBOL_GPL(fsl_mc_device_add); ++ ++/** ++ * fsl_mc_device_remove - Remove an fsl-mc device from being visible to ++ * Linux ++ * ++ * @mc_dev: Pointer to an fsl-mc device ++ */ ++void fsl_mc_device_remove(struct fsl_mc_device *mc_dev) ++{ ++ kfree(mc_dev->driver_override); ++ mc_dev->driver_override = NULL; ++ ++ /* ++ * The device-specific remove callback will get invoked by device_del() ++ */ ++ device_del(&mc_dev->dev); ++ put_device(&mc_dev->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) ++{ ++ 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); ++ 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; ++ } ++ ++ return ranges_len / tuple_len; ++} ++ ++static int get_mc_addr_translation_ranges(struct device *dev, ++ struct fsl_mc_addr_translation_range ++ **ranges, ++ u8 *num_ranges) ++{ ++ int ret; ++ int paddr_cells; ++ int mc_addr_cells; ++ int mc_size_cells; ++ int i; ++ const __be32 *ranges_start; ++ const __be32 *cell; ++ ++ ret = parse_mc_ranges(dev, ++ &paddr_cells, ++ &mc_addr_cells, ++ &mc_size_cells, ++ &ranges_start); ++ if (ret < 0) ++ return ret; ++ ++ *num_ranges = ret; ++ if (!ret) { ++ /* ++ * 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->mc_region_type = of_read_number(cell, 1); ++ range->start_mc_offset = of_read_number(cell + 1, ++ mc_addr_cells - 1); ++ cell += mc_addr_cells; ++ range->start_phys_addr = of_read_number(cell, paddr_cells); ++ cell += paddr_cells; ++ range->end_mc_offset = range->start_mc_offset + ++ 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 fsl_mc_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; ++ struct fsl_mc_bus *mc_bus = NULL; ++ int container_id; ++ phys_addr_t mc_portal_phys_addr; ++ u32 mc_portal_size; ++ struct mc_version mc_version; ++ struct resource res; ++ ++ 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 %pOF\n", ++ pdev->dev.of_node); ++ 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, NULL, ++ FSL_MC_IO_ATOMIC_CONTEXT_PORTAL, &mc_io); ++ if (error < 0) ++ return error; ++ ++ error = mc_get_version(mc_io, 0, &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, "MC firmware version: %u.%u.%u\n", ++ mc_version.major, mc_version.minor, mc_version.revision); ++ ++ error = get_mc_addr_translation_ranges(&pdev->dev, ++ &mc->translation_ranges, ++ &mc->num_translation_ranges); ++ if (error < 0) ++ goto error_cleanup_mc_io; ++ ++ error = dprc_get_container_id(mc_io, 0, &container_id); ++ if (error < 0) { ++ dev_err(&pdev->dev, ++ "dprc_get_container_id() failed: %d\n", error); ++ goto error_cleanup_mc_io; ++ } ++ ++ memset(&obj_desc, 0, sizeof(struct fsl_mc_obj_desc)); ++ error = dprc_get_api_version(mc_io, 0, ++ &obj_desc.ver_major, ++ &obj_desc.ver_minor); ++ if (error < 0) ++ goto error_cleanup_mc_io; ++ ++ obj_desc.vendor = FSL_MC_VENDOR_FREESCALE; ++ strcpy(obj_desc.type, "dprc"); ++ obj_desc.id = container_id; ++ obj_desc.irq_count = 1; ++ obj_desc.region_count = 0; ++ ++ error = fsl_mc_device_add(&obj_desc, mc_io, &pdev->dev, NULL, ++ &mc_bus_dev); ++ if (error < 0) ++ goto error_cleanup_mc_io; ++ ++ mc_bus = to_fsl_mc_bus(mc_bus_dev); ++ error = fsl_mc_restool_create_device_file(mc_bus); ++ if (error < 0) ++ goto error_cleanup_device; ++ ++ mc->root_mc_bus_dev = mc_bus_dev; ++ ++ return 0; ++ ++error_cleanup_device: ++ fsl_mc_device_remove(mc_bus_dev); ++ ++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); ++ struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc->root_mc_bus_dev); ++ ++ if (!fsl_mc_is_root_dprc(&mc->root_mc_bus_dev->dev)) ++ return -EINVAL; ++ ++ fsl_mc_restool_remove_device_file(mc_bus); ++ fsl_mc_device_remove(mc->root_mc_bus_dev); ++ ++ fsl_destroy_mc_io(mc->root_mc_bus_dev->mc_io); ++ mc->root_mc_bus_dev->mc_io = NULL; ++ ++ 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", ++ .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; ++ ++ error = bus_register(&fsl_mc_bus_type); ++ if (error < 0) { ++ pr_err("bus type registration failed: %d\n", error); ++ goto error_cleanup_cache; ++ } ++ ++ error = platform_driver_register(&fsl_mc_bus_driver); ++ if (error < 0) { ++ pr_err("platform_driver_register() failed: %d\n", error); ++ goto error_cleanup_bus; ++ } ++ ++ error = dprc_driver_init(); ++ if (error < 0) ++ goto error_cleanup_driver; ++ ++ error = fsl_mc_allocator_driver_init(); ++ if (error < 0) ++ goto error_cleanup_dprc_driver; ++ ++ error = fsl_mc_restool_init(); ++ if (error < 0) ++ goto error_cleanup_mc_allocator; ++ ++ return 0; ++ ++error_cleanup_mc_allocator: ++ fsl_mc_allocator_driver_exit(); ++ ++error_cleanup_dprc_driver: ++ dprc_driver_exit(); ++ ++error_cleanup_driver: ++ platform_driver_unregister(&fsl_mc_bus_driver); ++ ++error_cleanup_bus: ++ bus_unregister(&fsl_mc_bus_type); ++ ++error_cleanup_cache: ++ return error; ++} ++postcore_initcall(fsl_mc_bus_driver_init); +--- a/drivers/staging/fsl-mc/bus/fsl-mc-msi.c ++++ /dev/null +@@ -1,285 +0,0 @@ +-// SPDX-License-Identifier: GPL-2.0 +-/* +- * Freescale Management Complex (MC) bus driver MSI support +- * +- * Copyright (C) 2015-2016 Freescale Semiconductor, Inc. +- * Author: German Rivera <German.Rivera@freescale.com> +- * +- */ +- +-#include <linux/of_device.h> +-#include <linux/of_address.h> +-#include <linux/of_irq.h> +-#include <linux/irq.h> +-#include <linux/irqdomain.h> +-#include <linux/msi.h> +-#include "fsl-mc-private.h" +- +-#ifdef GENERIC_MSI_DOMAIN_OPS +-/* +- * Generate a unique ID identifying the interrupt (only used within the MSI +- * irqdomain. Combine the icid with the interrupt index. +- */ +-static irq_hw_number_t fsl_mc_domain_calc_hwirq(struct fsl_mc_device *dev, +- struct msi_desc *desc) +-{ +- /* +- * Make the base hwirq value for ICID*10000 so it is readable +- * as a decimal value in /proc/interrupts. +- */ +- return (irq_hw_number_t)(desc->fsl_mc.msi_index + (dev->icid * 10000)); +-} +- +-static void fsl_mc_msi_set_desc(msi_alloc_info_t *arg, +- struct msi_desc *desc) +-{ +- arg->desc = desc; +- arg->hwirq = fsl_mc_domain_calc_hwirq(to_fsl_mc_device(desc->dev), +- desc); +-} +-#else +-#define fsl_mc_msi_set_desc NULL +-#endif +- +-static void fsl_mc_msi_update_dom_ops(struct msi_domain_info *info) +-{ +- struct msi_domain_ops *ops = info->ops; +- +- if (WARN_ON(!ops)) +- return; +- +- /* +- * set_desc should not be set by the caller +- */ +- if (!ops->set_desc) +- ops->set_desc = fsl_mc_msi_set_desc; +-} +- +-static void __fsl_mc_msi_write_msg(struct fsl_mc_device *mc_bus_dev, +- struct fsl_mc_device_irq *mc_dev_irq) +-{ +- int error; +- struct fsl_mc_device *owner_mc_dev = mc_dev_irq->mc_dev; +- struct msi_desc *msi_desc = mc_dev_irq->msi_desc; +- struct dprc_irq_cfg irq_cfg; +- +- /* +- * msi_desc->msg.address is 0x0 when this function is invoked in +- * the free_irq() code path. In this case, for the MC, we don't +- * really need to "unprogram" the MSI, so we just return. +- */ +- if (msi_desc->msg.address_lo == 0x0 && msi_desc->msg.address_hi == 0x0) +- return; +- +- if (WARN_ON(!owner_mc_dev)) +- return; +- +- irq_cfg.paddr = ((u64)msi_desc->msg.address_hi << 32) | +- msi_desc->msg.address_lo; +- irq_cfg.val = msi_desc->msg.data; +- irq_cfg.irq_num = msi_desc->irq; +- +- if (owner_mc_dev == mc_bus_dev) { +- /* +- * IRQ is for the mc_bus_dev's DPRC itself +- */ +- error = dprc_set_irq(mc_bus_dev->mc_io, +- MC_CMD_FLAG_INTR_DIS | MC_CMD_FLAG_PRI, +- mc_bus_dev->mc_handle, +- mc_dev_irq->dev_irq_index, +- &irq_cfg); +- if (error < 0) { +- dev_err(&owner_mc_dev->dev, +- "dprc_set_irq() failed: %d\n", error); +- } +- } else { +- /* +- * IRQ is for for a child device of mc_bus_dev +- */ +- error = dprc_set_obj_irq(mc_bus_dev->mc_io, +- MC_CMD_FLAG_INTR_DIS | MC_CMD_FLAG_PRI, +- mc_bus_dev->mc_handle, +- owner_mc_dev->obj_desc.type, +- owner_mc_dev->obj_desc.id, +- mc_dev_irq->dev_irq_index, +- &irq_cfg); +- if (error < 0) { +- dev_err(&owner_mc_dev->dev, +- "dprc_obj_set_irq() failed: %d\n", error); +- } +- } +-} +- +-/* +- * NOTE: This function is invoked with interrupts disabled +- */ +-static void fsl_mc_msi_write_msg(struct irq_data *irq_data, +- struct msi_msg *msg) +-{ +- struct msi_desc *msi_desc = irq_data_get_msi_desc(irq_data); +- struct fsl_mc_device *mc_bus_dev = to_fsl_mc_device(msi_desc->dev); +- struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev); +- struct fsl_mc_device_irq *mc_dev_irq = +- &mc_bus->irq_resources[msi_desc->fsl_mc.msi_index]; +- +- WARN_ON(mc_dev_irq->msi_desc != msi_desc); +- msi_desc->msg = *msg; +- +- /* +- * Program the MSI (paddr, value) pair in the device: +- */ +- __fsl_mc_msi_write_msg(mc_bus_dev, mc_dev_irq); +-} +- +-static void fsl_mc_msi_update_chip_ops(struct msi_domain_info *info) +-{ +- struct irq_chip *chip = info->chip; +- +- if (WARN_ON((!chip))) +- return; +- +- /* +- * irq_write_msi_msg should not be set by the caller +- */ +- if (!chip->irq_write_msi_msg) +- chip->irq_write_msi_msg = fsl_mc_msi_write_msg; +-} +- +-/** +- * fsl_mc_msi_create_irq_domain - Create a fsl-mc MSI interrupt domain +- * @np: Optional device-tree node of the interrupt controller +- * @info: MSI domain info +- * @parent: Parent irq domain +- * +- * Updates the domain and chip ops and creates a fsl-mc MSI +- * interrupt domain. +- * +- * Returns: +- * A domain pointer or NULL in case of failure. +- */ +-struct irq_domain *fsl_mc_msi_create_irq_domain(struct fwnode_handle *fwnode, +- struct msi_domain_info *info, +- struct irq_domain *parent) +-{ +- struct irq_domain *domain; +- +- if (info->flags & MSI_FLAG_USE_DEF_DOM_OPS) +- fsl_mc_msi_update_dom_ops(info); +- if (info->flags & MSI_FLAG_USE_DEF_CHIP_OPS) +- fsl_mc_msi_update_chip_ops(info); +- +- domain = msi_create_irq_domain(fwnode, info, parent); +- if (domain) +- irq_domain_update_bus_token(domain, DOMAIN_BUS_FSL_MC_MSI); +- +- return domain; +-} +- +-int fsl_mc_find_msi_domain(struct device *mc_platform_dev, +- struct irq_domain **mc_msi_domain) +-{ +- struct irq_domain *msi_domain; +- struct device_node *mc_of_node = mc_platform_dev->of_node; +- +- msi_domain = of_msi_get_domain(mc_platform_dev, mc_of_node, +- DOMAIN_BUS_FSL_MC_MSI); +- if (!msi_domain) { +- pr_err("Unable to find fsl-mc MSI domain for %pOF\n", +- mc_of_node); +- +- return -ENOENT; +- } +- +- *mc_msi_domain = msi_domain; +- return 0; +-} +- +-static void fsl_mc_msi_free_descs(struct device *dev) +-{ +- struct msi_desc *desc, *tmp; +- +- list_for_each_entry_safe(desc, tmp, dev_to_msi_list(dev), list) { +- list_del(&desc->list); +- free_msi_entry(desc); +- } +-} +- +-static int fsl_mc_msi_alloc_descs(struct device *dev, unsigned int irq_count) +- +-{ +- unsigned int i; +- int error; +- struct msi_desc *msi_desc; +- +- for (i = 0; i < irq_count; i++) { +- msi_desc = alloc_msi_entry(dev, 1, NULL); +- if (!msi_desc) { +- dev_err(dev, "Failed to allocate msi entry\n"); +- error = -ENOMEM; +- goto cleanup_msi_descs; +- } +- +- msi_desc->fsl_mc.msi_index = i; +- INIT_LIST_HEAD(&msi_desc->list); +- list_add_tail(&msi_desc->list, dev_to_msi_list(dev)); +- } +- +- return 0; +- +-cleanup_msi_descs: +- fsl_mc_msi_free_descs(dev); +- return error; +-} +- +-int fsl_mc_msi_domain_alloc_irqs(struct device *dev, +- unsigned int irq_count) +-{ +- struct irq_domain *msi_domain; +- int error; +- +- if (WARN_ON(!list_empty(dev_to_msi_list(dev)))) +- return -EINVAL; +- +- error = fsl_mc_msi_alloc_descs(dev, irq_count); +- if (error < 0) +- return error; +- +- msi_domain = dev_get_msi_domain(dev); +- if (WARN_ON(!msi_domain)) { +- error = -EINVAL; +- goto cleanup_msi_descs; +- } +- +- /* +- * NOTE: Calling this function will trigger the invocation of the +- * its_fsl_mc_msi_prepare() callback +- */ +- error = msi_domain_alloc_irqs(msi_domain, dev, irq_count); +- +- if (error) { +- dev_err(dev, "Failed to allocate IRQs\n"); +- goto cleanup_msi_descs; +- } +- +- return 0; +- +-cleanup_msi_descs: +- fsl_mc_msi_free_descs(dev); +- return error; +-} +- +-void fsl_mc_msi_domain_free_irqs(struct device *dev) +-{ +- struct irq_domain *msi_domain; +- +- msi_domain = dev_get_msi_domain(dev); +- if (WARN_ON(!msi_domain)) +- return; +- +- msi_domain_free_irqs(msi_domain, dev); +- +- if (WARN_ON(list_empty(dev_to_msi_list(dev)))) +- return; +- +- fsl_mc_msi_free_descs(dev); +-} +--- /dev/null ++++ b/drivers/bus/fsl-mc/fsl-mc-msi.c +@@ -0,0 +1,285 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Freescale Management Complex (MC) bus driver MSI support ++ * ++ * Copyright (C) 2015-2016 Freescale Semiconductor, Inc. ++ * Author: German Rivera <German.Rivera@freescale.com> ++ * ++ */ ++ ++#include <linux/of_device.h> ++#include <linux/of_address.h> ++#include <linux/of_irq.h> ++#include <linux/irq.h> ++#include <linux/irqdomain.h> ++#include <linux/msi.h> ++ ++#include "fsl-mc-private.h" ++ ++#ifdef GENERIC_MSI_DOMAIN_OPS ++/* ++ * Generate a unique ID identifying the interrupt (only used within the MSI ++ * irqdomain. Combine the icid with the interrupt index. ++ */ ++static irq_hw_number_t fsl_mc_domain_calc_hwirq(struct fsl_mc_device *dev, ++ struct msi_desc *desc) ++{ ++ /* ++ * Make the base hwirq value for ICID*10000 so it is readable ++ * as a decimal value in /proc/interrupts. ++ */ ++ return (irq_hw_number_t)(desc->fsl_mc.msi_index + (dev->icid * 10000)); ++} ++ ++static void fsl_mc_msi_set_desc(msi_alloc_info_t *arg, ++ struct msi_desc *desc) ++{ ++ arg->desc = desc; ++ arg->hwirq = fsl_mc_domain_calc_hwirq(to_fsl_mc_device(desc->dev), ++ desc); ++} ++#else ++#define fsl_mc_msi_set_desc NULL ++#endif ++ ++static void fsl_mc_msi_update_dom_ops(struct msi_domain_info *info) ++{ ++ struct msi_domain_ops *ops = info->ops; ++ ++ if (!ops) ++ return; ++ ++ /* ++ * set_desc should not be set by the caller ++ */ ++ if (!ops->set_desc) ++ ops->set_desc = fsl_mc_msi_set_desc; ++} ++ ++static void __fsl_mc_msi_write_msg(struct fsl_mc_device *mc_bus_dev, ++ struct fsl_mc_device_irq *mc_dev_irq) ++{ ++ int error; ++ struct fsl_mc_device *owner_mc_dev = mc_dev_irq->mc_dev; ++ struct msi_desc *msi_desc = mc_dev_irq->msi_desc; ++ struct dprc_irq_cfg irq_cfg; ++ ++ /* ++ * msi_desc->msg.address is 0x0 when this function is invoked in ++ * the free_irq() code path. In this case, for the MC, we don't ++ * really need to "unprogram" the MSI, so we just return. ++ */ ++ if (msi_desc->msg.address_lo == 0x0 && msi_desc->msg.address_hi == 0x0) ++ return; ++ ++ if (!owner_mc_dev) ++ return; ++ ++ irq_cfg.paddr = ((u64)msi_desc->msg.address_hi << 32) | ++ msi_desc->msg.address_lo; ++ irq_cfg.val = msi_desc->msg.data; ++ irq_cfg.irq_num = msi_desc->irq; ++ ++ if (owner_mc_dev == mc_bus_dev) { ++ /* ++ * IRQ is for the mc_bus_dev's DPRC itself ++ */ ++ error = dprc_set_irq(mc_bus_dev->mc_io, ++ MC_CMD_FLAG_INTR_DIS | MC_CMD_FLAG_PRI, ++ mc_bus_dev->mc_handle, ++ mc_dev_irq->dev_irq_index, ++ &irq_cfg); ++ if (error < 0) { ++ dev_err(&owner_mc_dev->dev, ++ "dprc_set_irq() failed: %d\n", error); ++ } ++ } else { ++ /* ++ * IRQ is for for a child device of mc_bus_dev ++ */ ++ error = dprc_set_obj_irq(mc_bus_dev->mc_io, ++ MC_CMD_FLAG_INTR_DIS | MC_CMD_FLAG_PRI, ++ mc_bus_dev->mc_handle, ++ owner_mc_dev->obj_desc.type, ++ owner_mc_dev->obj_desc.id, ++ mc_dev_irq->dev_irq_index, ++ &irq_cfg); ++ if (error < 0) { ++ dev_err(&owner_mc_dev->dev, ++ "dprc_obj_set_irq() failed: %d\n", error); ++ } ++ } ++} ++ ++/* ++ * NOTE: This function is invoked with interrupts disabled ++ */ ++static void fsl_mc_msi_write_msg(struct irq_data *irq_data, ++ struct msi_msg *msg) ++{ ++ struct msi_desc *msi_desc = irq_data_get_msi_desc(irq_data); ++ struct fsl_mc_device *mc_bus_dev = to_fsl_mc_device(msi_desc->dev); ++ struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev); ++ struct fsl_mc_device_irq *mc_dev_irq = ++ &mc_bus->irq_resources[msi_desc->fsl_mc.msi_index]; ++ ++ msi_desc->msg = *msg; ++ ++ /* ++ * Program the MSI (paddr, value) pair in the device: ++ */ ++ __fsl_mc_msi_write_msg(mc_bus_dev, mc_dev_irq); ++} ++ ++static void fsl_mc_msi_update_chip_ops(struct msi_domain_info *info) ++{ ++ struct irq_chip *chip = info->chip; ++ ++ if (!chip) ++ return; ++ ++ /* ++ * irq_write_msi_msg should not be set by the caller ++ */ ++ if (!chip->irq_write_msi_msg) ++ chip->irq_write_msi_msg = fsl_mc_msi_write_msg; ++} ++ ++/** ++ * fsl_mc_msi_create_irq_domain - Create a fsl-mc MSI interrupt domain ++ * @np: Optional device-tree node of the interrupt controller ++ * @info: MSI domain info ++ * @parent: Parent irq domain ++ * ++ * Updates the domain and chip ops and creates a fsl-mc MSI ++ * interrupt domain. ++ * ++ * Returns: ++ * A domain pointer or NULL in case of failure. ++ */ ++struct irq_domain *fsl_mc_msi_create_irq_domain(struct fwnode_handle *fwnode, ++ struct msi_domain_info *info, ++ struct irq_domain *parent) ++{ ++ struct irq_domain *domain; ++ ++ if (info->flags & MSI_FLAG_USE_DEF_DOM_OPS) ++ fsl_mc_msi_update_dom_ops(info); ++ if (info->flags & MSI_FLAG_USE_DEF_CHIP_OPS) ++ fsl_mc_msi_update_chip_ops(info); ++ ++ domain = msi_create_irq_domain(fwnode, info, parent); ++ if (domain) ++ irq_domain_update_bus_token(domain, DOMAIN_BUS_FSL_MC_MSI); ++ ++ return domain; ++} ++ ++int fsl_mc_find_msi_domain(struct device *mc_platform_dev, ++ struct irq_domain **mc_msi_domain) ++{ ++ struct irq_domain *msi_domain; ++ struct device_node *mc_of_node = mc_platform_dev->of_node; ++ ++ msi_domain = of_msi_get_domain(mc_platform_dev, mc_of_node, ++ DOMAIN_BUS_FSL_MC_MSI); ++ if (!msi_domain) { ++ pr_err("Unable to find fsl-mc MSI domain for %pOF\n", ++ mc_of_node); ++ ++ return -ENOENT; ++ } ++ ++ *mc_msi_domain = msi_domain; ++ return 0; ++} ++ ++static void fsl_mc_msi_free_descs(struct device *dev) ++{ ++ struct msi_desc *desc, *tmp; ++ ++ list_for_each_entry_safe(desc, tmp, dev_to_msi_list(dev), list) { ++ list_del(&desc->list); ++ free_msi_entry(desc); ++ } ++} ++ ++static int fsl_mc_msi_alloc_descs(struct device *dev, unsigned int irq_count) ++ ++{ ++ unsigned int i; ++ int error; ++ struct msi_desc *msi_desc; ++ ++ for (i = 0; i < irq_count; i++) { ++ msi_desc = alloc_msi_entry(dev, 1, NULL); ++ if (!msi_desc) { ++ dev_err(dev, "Failed to allocate msi entry\n"); ++ error = -ENOMEM; ++ goto cleanup_msi_descs; ++ } ++ ++ msi_desc->fsl_mc.msi_index = i; ++ INIT_LIST_HEAD(&msi_desc->list); ++ list_add_tail(&msi_desc->list, dev_to_msi_list(dev)); ++ } ++ ++ return 0; ++ ++cleanup_msi_descs: ++ fsl_mc_msi_free_descs(dev); ++ return error; ++} ++ ++int fsl_mc_msi_domain_alloc_irqs(struct device *dev, ++ unsigned int irq_count) ++{ ++ struct irq_domain *msi_domain; ++ int error; ++ ++ if (!list_empty(dev_to_msi_list(dev))) ++ return -EINVAL; ++ ++ error = fsl_mc_msi_alloc_descs(dev, irq_count); ++ if (error < 0) ++ return error; ++ ++ msi_domain = dev_get_msi_domain(dev); ++ if (!msi_domain) { ++ error = -EINVAL; ++ goto cleanup_msi_descs; ++ } ++ ++ /* ++ * NOTE: Calling this function will trigger the invocation of the ++ * its_fsl_mc_msi_prepare() callback ++ */ ++ error = msi_domain_alloc_irqs(msi_domain, dev, irq_count); ++ ++ if (error) { ++ dev_err(dev, "Failed to allocate IRQs\n"); ++ goto cleanup_msi_descs; ++ } ++ ++ return 0; ++ ++cleanup_msi_descs: ++ fsl_mc_msi_free_descs(dev); ++ return error; ++} ++ ++void fsl_mc_msi_domain_free_irqs(struct device *dev) ++{ ++ struct irq_domain *msi_domain; ++ ++ msi_domain = dev_get_msi_domain(dev); ++ if (!msi_domain) ++ return; ++ ++ msi_domain_free_irqs(msi_domain, dev); ++ ++ if (list_empty(dev_to_msi_list(dev))) ++ return; ++ ++ fsl_mc_msi_free_descs(dev); ++} +--- /dev/null ++++ b/drivers/bus/fsl-mc/fsl-mc-private.h +@@ -0,0 +1,223 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * Freescale Management Complex (MC) bus private declarations ++ * ++ * Copyright (C) 2016 Freescale Semiconductor, Inc. ++ * ++ */ ++#ifndef _FSL_MC_PRIVATE_H_ ++#define _FSL_MC_PRIVATE_H_ ++ ++#include <linux/fsl/mc.h> ++#include <linux/mutex.h> ++#include <linux/cdev.h> ++#include <linux/ioctl.h> ++ ++/* ++ * Data Path Management Complex (DPMNG) General API ++ */ ++ ++/* DPMNG command versioning */ ++#define DPMNG_CMD_BASE_VERSION 1 ++#define DPMNG_CMD_ID_OFFSET 4 ++ ++#define DPMNG_CMD(id) (((id) << DPMNG_CMD_ID_OFFSET) | DPMNG_CMD_BASE_VERSION) ++ ++/* DPMNG command IDs */ ++#define DPMNG_CMDID_GET_VERSION DPMNG_CMD(0x831) ++ ++struct dpmng_rsp_get_version { ++ __le32 revision; ++ __le32 version_major; ++ __le32 version_minor; ++}; ++ ++/* ++ * Data Path Management Command Portal (DPMCP) API ++ */ ++ ++/* Minimal supported DPMCP Version */ ++#define DPMCP_MIN_VER_MAJOR 3 ++#define DPMCP_MIN_VER_MINOR 0 ++ ++/* DPMCP command versioning */ ++#define DPMCP_CMD_BASE_VERSION 1 ++#define DPMCP_CMD_ID_OFFSET 4 ++ ++#define DPMCP_CMD(id) (((id) << DPMCP_CMD_ID_OFFSET) | DPMCP_CMD_BASE_VERSION) ++ ++/* DPMCP command IDs */ ++#define DPMCP_CMDID_CLOSE DPMCP_CMD(0x800) ++#define DPMCP_CMDID_OPEN DPMCP_CMD(0x80b) ++#define DPMCP_CMDID_RESET DPMCP_CMD(0x005) ++ ++struct dpmcp_cmd_open { ++ __le32 dpmcp_id; ++}; ++ ++/* ++ * Initialization and runtime control APIs for DPMCP ++ */ ++int dpmcp_open(struct fsl_mc_io *mc_io, ++ u32 cmd_flags, ++ int dpmcp_id, ++ u16 *token); ++ ++int dpmcp_close(struct fsl_mc_io *mc_io, ++ u32 cmd_flags, ++ u16 token); ++ ++int dpmcp_reset(struct fsl_mc_io *mc_io, ++ u32 cmd_flags, ++ u16 token); ++ ++/* ++ * Data Path Buffer Pool (DPBP) API ++ */ ++ ++/* DPBP Version */ ++#define DPBP_VER_MAJOR 3 ++#define DPBP_VER_MINOR 2 ++ ++/* Command versioning */ ++#define DPBP_CMD_BASE_VERSION 1 ++#define DPBP_CMD_ID_OFFSET 4 ++ ++#define DPBP_CMD(id) (((id) << DPBP_CMD_ID_OFFSET) | DPBP_CMD_BASE_VERSION) ++ ++/* Command IDs */ ++#define DPBP_CMDID_CLOSE DPBP_CMD(0x800) ++#define DPBP_CMDID_OPEN DPBP_CMD(0x804) ++ ++#define DPBP_CMDID_ENABLE DPBP_CMD(0x002) ++#define DPBP_CMDID_DISABLE DPBP_CMD(0x003) ++#define DPBP_CMDID_GET_ATTR DPBP_CMD(0x004) ++#define DPBP_CMDID_RESET DPBP_CMD(0x005) ++ ++struct dpbp_cmd_open { ++ __le32 dpbp_id; ++}; ++ ++#define DPBP_ENABLE 0x1 ++ ++struct dpbp_rsp_get_attributes { ++ /* response word 0 */ ++ __le16 pad; ++ __le16 bpid; ++ __le32 id; ++ /* response word 1 */ ++ __le16 version_major; ++ __le16 version_minor; ++}; ++ ++/* ++ * Data Path Concentrator (DPCON) API ++ */ ++ ++/* DPCON Version */ ++#define DPCON_VER_MAJOR 3 ++#define DPCON_VER_MINOR 2 ++ ++/* Command versioning */ ++#define DPCON_CMD_BASE_VERSION 1 ++#define DPCON_CMD_ID_OFFSET 4 ++ ++#define DPCON_CMD(id) (((id) << DPCON_CMD_ID_OFFSET) | DPCON_CMD_BASE_VERSION) ++ ++/* Command IDs */ ++#define DPCON_CMDID_CLOSE DPCON_CMD(0x800) ++#define DPCON_CMDID_OPEN DPCON_CMD(0x808) ++ ++#define DPCON_CMDID_ENABLE DPCON_CMD(0x002) ++#define DPCON_CMDID_DISABLE DPCON_CMD(0x003) ++#define DPCON_CMDID_GET_ATTR DPCON_CMD(0x004) ++#define DPCON_CMDID_RESET DPCON_CMD(0x005) ++ ++#define DPCON_CMDID_SET_NOTIFICATION DPCON_CMD(0x100) ++ ++struct dpcon_cmd_open { ++ __le32 dpcon_id; ++}; ++ ++#define DPCON_ENABLE 1 ++ ++struct dpcon_rsp_get_attr { ++ /* response word 0 */ ++ __le32 id; ++ __le16 qbman_ch_id; ++ u8 num_priorities; ++ u8 pad; ++}; ++ ++struct dpcon_cmd_set_notification { ++ /* cmd word 0 */ ++ __le32 dpio_id; ++ u8 priority; ++ u8 pad[3]; ++ /* cmd word 1 */ ++ __le64 user_ctx; ++}; ++ ++int __must_check fsl_mc_device_add(struct fsl_mc_obj_desc *obj_desc, ++ struct fsl_mc_io *mc_io, ++ struct device *parent_dev, ++ const char *driver_override, ++ struct fsl_mc_device **new_mc_dev); ++ ++int __init dprc_driver_init(void); ++ ++void dprc_driver_exit(void); ++ ++int __init fsl_mc_allocator_driver_init(void); ++ ++void fsl_mc_allocator_driver_exit(void); ++ ++int __must_check fsl_mc_resource_allocate(struct fsl_mc_bus *mc_bus, ++ enum fsl_mc_pool_type pool_type, ++ struct fsl_mc_resource ++ **new_resource); ++ ++void fsl_mc_resource_free(struct fsl_mc_resource *resource); ++ ++int fsl_mc_msi_domain_alloc_irqs(struct device *dev, ++ unsigned int irq_count); ++ ++void fsl_mc_msi_domain_free_irqs(struct device *dev); ++ ++int __must_check fsl_create_mc_io(struct device *dev, ++ phys_addr_t mc_portal_phys_addr, ++ u32 mc_portal_size, ++ struct fsl_mc_device *dpmcp_dev, ++ u32 flags, struct fsl_mc_io **new_mc_io); ++ ++void fsl_destroy_mc_io(struct fsl_mc_io *mc_io); ++ ++bool fsl_mc_is_root_dprc(struct device *dev); ++ ++#ifdef CONFIG_FSL_MC_RESTOOL ++ ++int fsl_mc_restool_create_device_file(struct fsl_mc_bus *mc_bus); ++ ++void fsl_mc_restool_remove_device_file(struct fsl_mc_bus *mc_bus); ++ ++int fsl_mc_restool_init(void); ++ ++#else ++ ++static inline int fsl_mc_restool_create_device_file(struct fsl_mc_bus *mc_bus) ++{ ++ return 0; ++} ++ ++static inline void fsl_mc_restool_remove_device_file(struct fsl_mc_bus *mc_bus) ++{ ++} ++ ++static inline int fsl_mc_restool_init(void) ++{ ++ return 0; ++} ++ ++#endif ++ ++#endif /* _FSL_MC_PRIVATE_H_ */ +--- /dev/null ++++ b/drivers/bus/fsl-mc/fsl-mc-restool.c +@@ -0,0 +1,219 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Management Complex (MC) restool support ++ * ++ * Copyright 2018 NXP ++ * ++ */ ++ ++#include <linux/slab.h> ++#include <linux/cdev.h> ++#include <linux/fs.h> ++#include <linux/uaccess.h> ++ ++#include "fsl-mc-private.h" ++ ++#define FSL_MC_BUS_MAX_MINORS 1 ++ ++static struct class *fsl_mc_bus_class; ++static int fsl_mc_bus_major; ++ ++static int fsl_mc_restool_send_command(unsigned long arg, ++ struct fsl_mc_io *mc_io) ++{ ++ struct fsl_mc_command mc_cmd; ++ int error; ++ ++ error = copy_from_user(&mc_cmd, (void __user *)arg, sizeof(mc_cmd)); ++ if (error) ++ return -EFAULT; ++ ++ error = mc_send_command(mc_io, &mc_cmd); ++ if (error) ++ return error; ++ ++ error = copy_to_user((void __user *)arg, &mc_cmd, sizeof(mc_cmd)); ++ if (error) ++ return -EFAULT; ++ ++ return 0; ++} ++ ++int fsl_mc_restool_init(void) ++{ ++ dev_t dev; ++ int error; ++ ++ fsl_mc_bus_class = class_create(THIS_MODULE, "fsl_mc_bus"); ++ if (IS_ERR(fsl_mc_bus_class)) { ++ error = PTR_ERR(fsl_mc_bus_class); ++ return error; ++ } ++ ++ error = alloc_chrdev_region(&dev, 0, ++ FSL_MC_BUS_MAX_MINORS, ++ "fsl_mc_bus"); ++ if (error < 0) ++ return error; ++ ++ fsl_mc_bus_major = MAJOR(dev); ++ ++ return 0; ++} ++ ++static int fsl_mc_restool_dev_open(struct inode *inode, struct file *filep) ++{ ++ struct fsl_mc_device *root_mc_device; ++ struct fsl_mc_restool *mc_restool; ++ struct fsl_mc_bus *mc_bus; ++ struct fsl_mc_io *dynamic_mc_io; ++ int error; ++ ++ mc_restool = container_of(inode->i_cdev, struct fsl_mc_restool, cdev); ++ mc_bus = container_of(mc_restool, struct fsl_mc_bus, restool_misc); ++ root_mc_device = &mc_bus->mc_dev; ++ ++ mutex_lock(&mc_restool->mutex); ++ ++ if (!mc_restool->local_instance_in_use) { ++ filep->private_data = root_mc_device->mc_io; ++ mc_restool->local_instance_in_use = true; ++ } else { ++ dynamic_mc_io = kzalloc(sizeof(*dynamic_mc_io), GFP_KERNEL); ++ if (!dynamic_mc_io) { ++ error = -ENOMEM; ++ goto error_alloc_mc_io; ++ } ++ ++ error = fsl_mc_portal_allocate(root_mc_device, 0, ++ &dynamic_mc_io); ++ if (error) { ++ pr_err("Could not allocate MC portal\n"); ++ goto error_portal_allocate; ++ } ++ ++ mc_restool->dynamic_instance_count++; ++ filep->private_data = dynamic_mc_io; ++ } ++ ++ mutex_unlock(&mc_restool->mutex); ++ ++ return 0; ++ ++error_portal_allocate: ++ kfree(dynamic_mc_io); ++ ++error_alloc_mc_io: ++ mutex_unlock(&mc_restool->mutex); ++ ++ return error; ++} ++ ++static int fsl_mc_restool_dev_release(struct inode *inode, struct file *filep) ++{ ++ struct fsl_mc_device *root_mc_device; ++ struct fsl_mc_restool *mc_restool; ++ struct fsl_mc_bus *mc_bus; ++ struct fsl_mc_io *mc_io; ++ ++ mc_restool = container_of(inode->i_cdev, struct fsl_mc_restool, cdev); ++ mc_bus = container_of(mc_restool, struct fsl_mc_bus, restool_misc); ++ root_mc_device = &mc_bus->mc_dev; ++ mc_io = filep->private_data; ++ ++ mutex_lock(&mc_restool->mutex); ++ ++ if (WARN_ON(!mc_restool->local_instance_in_use && ++ mc_restool->dynamic_instance_count == 0)) { ++ mutex_unlock(&mc_restool->mutex); ++ return -EINVAL; ++ } ++ ++ if (filep->private_data == root_mc_device->mc_io) { ++ mc_restool->local_instance_in_use = false; ++ } else { ++ fsl_mc_portal_free(mc_io); ++ kfree(mc_io); ++ mc_restool->dynamic_instance_count--; ++ } ++ ++ filep->private_data = NULL; ++ mutex_unlock(&mc_restool->mutex); ++ ++ return 0; ++} ++ ++static long fsl_mc_restool_dev_ioctl(struct file *file, ++ unsigned int cmd, ++ unsigned long arg) ++{ ++ int error; ++ ++ switch (cmd) { ++ case RESTOOL_SEND_MC_COMMAND: ++ error = fsl_mc_restool_send_command(arg, file->private_data); ++ break; ++ default: ++ pr_err("%s: unexpected ioctl call number\n", __func__); ++ error = -EINVAL; ++ } ++ ++ return error; ++} ++ ++static const struct file_operations fsl_mc_restool_dev_fops = { ++ .owner = THIS_MODULE, ++ .open = fsl_mc_restool_dev_open, ++ .release = fsl_mc_restool_dev_release, ++ .unlocked_ioctl = fsl_mc_restool_dev_ioctl, ++}; ++ ++int fsl_mc_restool_create_device_file(struct fsl_mc_bus *mc_bus) ++{ ++ struct fsl_mc_device *mc_dev = &mc_bus->mc_dev; ++ struct fsl_mc_restool *mc_restool = &mc_bus->restool_misc; ++ int error; ++ ++ mc_restool = &mc_bus->restool_misc; ++ mc_restool->dev = MKDEV(fsl_mc_bus_major, 0); ++ cdev_init(&mc_restool->cdev, &fsl_mc_restool_dev_fops); ++ ++ error = cdev_add(&mc_restool->cdev, ++ mc_restool->dev, ++ FSL_MC_BUS_MAX_MINORS); ++ if (error) ++ return error; ++ ++ mc_restool->device = device_create(fsl_mc_bus_class, ++ NULL, ++ mc_restool->dev, ++ NULL, ++ "%s", ++ dev_name(&mc_dev->dev)); ++ if (IS_ERR(mc_restool->device)) { ++ error = PTR_ERR(mc_restool->device); ++ goto error_device_create; ++ } ++ ++ mutex_init(&mc_restool->mutex); ++ ++ return 0; ++ ++error_device_create: ++ cdev_del(&mc_restool->cdev); ++ ++ return error; ++} ++ ++void fsl_mc_restool_remove_device_file(struct fsl_mc_bus *mc_bus) ++{ ++ struct fsl_mc_restool *mc_restool = &mc_bus->restool_misc; ++ ++ if (WARN_ON(mc_restool->local_instance_in_use)) ++ return; ++ ++ if (WARN_ON(mc_restool->dynamic_instance_count != 0)) ++ return; ++ ++ cdev_del(&mc_restool->cdev); ++} +--- a/drivers/staging/fsl-mc/bus/mc-io.c ++++ /dev/null +@@ -1,292 +0,0 @@ +-// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +-/* +- * Copyright 2013-2016 Freescale Semiconductor Inc. +- * +- */ +- +-#include <linux/io.h> +-#include "../include/mc.h" +- +-#include "fsl-mc-private.h" +-#include "dpmcp.h" +-#include "dpmcp-cmd.h" +- +-static int fsl_mc_io_set_dpmcp(struct fsl_mc_io *mc_io, +- struct fsl_mc_device *dpmcp_dev) +-{ +- int error; +- +- if (WARN_ON(!dpmcp_dev)) +- return -EINVAL; +- +- if (WARN_ON(mc_io->dpmcp_dev)) +- return -EINVAL; +- +- if (WARN_ON(dpmcp_dev->mc_io)) +- return -EINVAL; +- +- error = dpmcp_open(mc_io, +- 0, +- dpmcp_dev->obj_desc.id, +- &dpmcp_dev->mc_handle); +- if (error < 0) +- return error; +- +- mc_io->dpmcp_dev = dpmcp_dev; +- dpmcp_dev->mc_io = mc_io; +- return 0; +-} +- +-static void fsl_mc_io_unset_dpmcp(struct fsl_mc_io *mc_io) +-{ +- int error; +- struct fsl_mc_device *dpmcp_dev = mc_io->dpmcp_dev; +- +- if (WARN_ON(!dpmcp_dev)) +- return; +- +- if (WARN_ON(dpmcp_dev->mc_io != mc_io)) +- return; +- +- error = dpmcp_close(mc_io, +- 0, +- dpmcp_dev->mc_handle); +- if (error < 0) { +- dev_err(&dpmcp_dev->dev, "dpmcp_close() failed: %d\n", +- error); +- } +- +- mc_io->dpmcp_dev = NULL; +- dpmcp_dev->mc_io = NULL; +-} +- +-/** +- * Creates an MC I/O object +- * +- * @dev: device to be associated with the MC I/O object +- * @mc_portal_phys_addr: physical address of the MC portal to use +- * @mc_portal_size: size in bytes of the MC portal +- * @dpmcp-dev: Pointer to the DPMCP object associated with this MC I/O +- * object or NULL if none. +- * @flags: flags for the new MC I/O object +- * @new_mc_io: Area to return pointer to newly created MC I/O object +- * +- * Returns '0' on Success; Error code otherwise. +- */ +-int __must_check fsl_create_mc_io(struct device *dev, +- phys_addr_t mc_portal_phys_addr, +- u32 mc_portal_size, +- struct fsl_mc_device *dpmcp_dev, +- u32 flags, struct fsl_mc_io **new_mc_io) +-{ +- int error; +- struct fsl_mc_io *mc_io; +- void __iomem *mc_portal_virt_addr; +- struct resource *res; +- +- mc_io = devm_kzalloc(dev, sizeof(*mc_io), GFP_KERNEL); +- if (!mc_io) +- return -ENOMEM; +- +- mc_io->dev = dev; +- mc_io->flags = flags; +- mc_io->portal_phys_addr = mc_portal_phys_addr; +- mc_io->portal_size = mc_portal_size; +- if (flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL) +- spin_lock_init(&mc_io->spinlock); +- else +- mutex_init(&mc_io->mutex); +- +- res = devm_request_mem_region(dev, +- mc_portal_phys_addr, +- mc_portal_size, +- "mc_portal"); +- if (!res) { +- dev_err(dev, +- "devm_request_mem_region failed for MC portal %pa\n", +- &mc_portal_phys_addr); +- return -EBUSY; +- } +- +- mc_portal_virt_addr = devm_ioremap_nocache(dev, +- mc_portal_phys_addr, +- mc_portal_size); +- if (!mc_portal_virt_addr) { +- dev_err(dev, +- "devm_ioremap_nocache failed for MC portal %pa\n", +- &mc_portal_phys_addr); +- return -ENXIO; +- } +- +- mc_io->portal_virt_addr = mc_portal_virt_addr; +- if (dpmcp_dev) { +- error = fsl_mc_io_set_dpmcp(mc_io, dpmcp_dev); +- if (error < 0) +- goto error_destroy_mc_io; +- } +- +- *new_mc_io = mc_io; +- return 0; +- +-error_destroy_mc_io: +- fsl_destroy_mc_io(mc_io); +- return error; +-} +- +-/** +- * Destroys an MC I/O object +- * +- * @mc_io: MC I/O object to destroy +- */ +-void fsl_destroy_mc_io(struct fsl_mc_io *mc_io) +-{ +- struct fsl_mc_device *dpmcp_dev = mc_io->dpmcp_dev; +- +- if (dpmcp_dev) +- fsl_mc_io_unset_dpmcp(mc_io); +- +- devm_iounmap(mc_io->dev, mc_io->portal_virt_addr); +- devm_release_mem_region(mc_io->dev, +- mc_io->portal_phys_addr, +- mc_io->portal_size); +- +- mc_io->portal_virt_addr = NULL; +- devm_kfree(mc_io->dev, mc_io); +-} +- +-/** +- * fsl_mc_portal_allocate - Allocates an MC portal +- * +- * @mc_dev: MC device for which the MC portal is to be allocated +- * @mc_io_flags: Flags for the fsl_mc_io object that wraps the allocated +- * MC portal. +- * @new_mc_io: Pointer to area where the pointer to the fsl_mc_io object +- * that wraps the allocated MC portal is to be returned +- * +- * This function allocates an MC portal from the device's parent DPRC, +- * from the corresponding MC bus' pool of MC portals and wraps +- * it in a new fsl_mc_io object. If 'mc_dev' is a DPRC itself, the +- * portal is allocated from its own MC bus. +- */ +-int __must_check fsl_mc_portal_allocate(struct fsl_mc_device *mc_dev, +- u16 mc_io_flags, +- struct fsl_mc_io **new_mc_io) +-{ +- struct fsl_mc_device *mc_bus_dev; +- struct fsl_mc_bus *mc_bus; +- phys_addr_t mc_portal_phys_addr; +- size_t mc_portal_size; +- struct fsl_mc_device *dpmcp_dev; +- int error = -EINVAL; +- struct fsl_mc_resource *resource = NULL; +- struct fsl_mc_io *mc_io = NULL; +- +- if (mc_dev->flags & FSL_MC_IS_DPRC) { +- mc_bus_dev = mc_dev; +- } else { +- if (WARN_ON(!dev_is_fsl_mc(mc_dev->dev.parent))) +- return error; +- +- mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent); +- } +- +- mc_bus = to_fsl_mc_bus(mc_bus_dev); +- *new_mc_io = NULL; +- error = fsl_mc_resource_allocate(mc_bus, FSL_MC_POOL_DPMCP, &resource); +- if (error < 0) +- return error; +- +- error = -EINVAL; +- dpmcp_dev = resource->data; +- if (WARN_ON(!dpmcp_dev)) +- goto error_cleanup_resource; +- +- if (dpmcp_dev->obj_desc.ver_major < DPMCP_MIN_VER_MAJOR || +- (dpmcp_dev->obj_desc.ver_major == DPMCP_MIN_VER_MAJOR && +- dpmcp_dev->obj_desc.ver_minor < DPMCP_MIN_VER_MINOR)) { +- dev_err(&dpmcp_dev->dev, +- "ERROR: Version %d.%d of DPMCP not supported.\n", +- dpmcp_dev->obj_desc.ver_major, +- dpmcp_dev->obj_desc.ver_minor); +- error = -ENOTSUPP; +- goto error_cleanup_resource; +- } +- +- if (WARN_ON(dpmcp_dev->obj_desc.region_count == 0)) +- goto error_cleanup_resource; +- +- mc_portal_phys_addr = dpmcp_dev->regions[0].start; +- mc_portal_size = resource_size(dpmcp_dev->regions); +- +- if (WARN_ON(mc_portal_size != mc_bus_dev->mc_io->portal_size)) +- goto error_cleanup_resource; +- +- error = fsl_create_mc_io(&mc_bus_dev->dev, +- mc_portal_phys_addr, +- mc_portal_size, dpmcp_dev, +- mc_io_flags, &mc_io); +- if (error < 0) +- goto error_cleanup_resource; +- +- *new_mc_io = mc_io; +- return 0; +- +-error_cleanup_resource: +- fsl_mc_resource_free(resource); +- return error; +-} +-EXPORT_SYMBOL_GPL(fsl_mc_portal_allocate); +- +-/** +- * fsl_mc_portal_free - Returns an MC portal to the pool of free MC portals +- * of a given MC bus +- * +- * @mc_io: Pointer to the fsl_mc_io object that wraps the MC portal to free +- */ +-void fsl_mc_portal_free(struct fsl_mc_io *mc_io) +-{ +- struct fsl_mc_device *dpmcp_dev; +- struct fsl_mc_resource *resource; +- +- /* +- * Every mc_io obtained by calling fsl_mc_portal_allocate() is supposed +- * to have a DPMCP object associated with. +- */ +- dpmcp_dev = mc_io->dpmcp_dev; +- if (WARN_ON(!dpmcp_dev)) +- return; +- +- resource = dpmcp_dev->resource; +- if (WARN_ON(!resource || resource->type != FSL_MC_POOL_DPMCP)) +- return; +- +- if (WARN_ON(resource->data != dpmcp_dev)) +- return; +- +- fsl_destroy_mc_io(mc_io); +- fsl_mc_resource_free(resource); +-} +-EXPORT_SYMBOL_GPL(fsl_mc_portal_free); +- +-/** +- * fsl_mc_portal_reset - Resets the dpmcp object for a given fsl_mc_io object +- * +- * @mc_io: Pointer to the fsl_mc_io object that wraps the MC portal to free +- */ +-int fsl_mc_portal_reset(struct fsl_mc_io *mc_io) +-{ +- int error; +- struct fsl_mc_device *dpmcp_dev = mc_io->dpmcp_dev; +- +- if (WARN_ON(!dpmcp_dev)) +- return -EINVAL; +- +- error = dpmcp_reset(mc_io, 0, dpmcp_dev->mc_handle); +- if (error < 0) { +- dev_err(&dpmcp_dev->dev, "dpmcp_reset() failed: %d\n", error); +- return error; +- } +- +- return 0; +-} +-EXPORT_SYMBOL_GPL(fsl_mc_portal_reset); +--- /dev/null ++++ b/drivers/bus/fsl-mc/mc-io.c +@@ -0,0 +1,268 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) ++/* ++ * Copyright 2013-2016 Freescale Semiconductor Inc. ++ * ++ */ ++ ++#include <linux/io.h> ++#include <linux/fsl/mc.h> ++ ++#include "fsl-mc-private.h" ++ ++static int fsl_mc_io_set_dpmcp(struct fsl_mc_io *mc_io, ++ struct fsl_mc_device *dpmcp_dev) ++{ ++ int error; ++ ++ if (mc_io->dpmcp_dev) ++ return -EINVAL; ++ ++ if (dpmcp_dev->mc_io) ++ return -EINVAL; ++ ++ error = dpmcp_open(mc_io, ++ 0, ++ dpmcp_dev->obj_desc.id, ++ &dpmcp_dev->mc_handle); ++ if (error < 0) ++ return error; ++ ++ mc_io->dpmcp_dev = dpmcp_dev; ++ dpmcp_dev->mc_io = mc_io; ++ return 0; ++} ++ ++static void fsl_mc_io_unset_dpmcp(struct fsl_mc_io *mc_io) ++{ ++ int error; ++ struct fsl_mc_device *dpmcp_dev = mc_io->dpmcp_dev; ++ ++ error = dpmcp_close(mc_io, ++ 0, ++ dpmcp_dev->mc_handle); ++ if (error < 0) { ++ dev_err(&dpmcp_dev->dev, "dpmcp_close() failed: %d\n", ++ error); ++ } ++ ++ mc_io->dpmcp_dev = NULL; ++ dpmcp_dev->mc_io = NULL; ++} ++ ++/** ++ * Creates an MC I/O object ++ * ++ * @dev: device to be associated with the MC I/O object ++ * @mc_portal_phys_addr: physical address of the MC portal to use ++ * @mc_portal_size: size in bytes of the MC portal ++ * @dpmcp-dev: Pointer to the DPMCP object associated with this MC I/O ++ * object or NULL if none. ++ * @flags: flags for the new MC I/O object ++ * @new_mc_io: Area to return pointer to newly created MC I/O object ++ * ++ * Returns '0' on Success; Error code otherwise. ++ */ ++int __must_check fsl_create_mc_io(struct device *dev, ++ phys_addr_t mc_portal_phys_addr, ++ u32 mc_portal_size, ++ struct fsl_mc_device *dpmcp_dev, ++ u32 flags, struct fsl_mc_io **new_mc_io) ++{ ++ int error; ++ struct fsl_mc_io *mc_io; ++ void __iomem *mc_portal_virt_addr; ++ struct resource *res; ++ ++ mc_io = devm_kzalloc(dev, sizeof(*mc_io), GFP_KERNEL); ++ if (!mc_io) ++ return -ENOMEM; ++ ++ mc_io->dev = dev; ++ mc_io->flags = flags; ++ mc_io->portal_phys_addr = mc_portal_phys_addr; ++ mc_io->portal_size = mc_portal_size; ++ if (flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL) ++ spin_lock_init(&mc_io->spinlock); ++ else ++ mutex_init(&mc_io->mutex); ++ ++ res = devm_request_mem_region(dev, ++ mc_portal_phys_addr, ++ mc_portal_size, ++ "mc_portal"); ++ if (!res) { ++ dev_err(dev, ++ "devm_request_mem_region failed for MC portal %pa\n", ++ &mc_portal_phys_addr); ++ return -EBUSY; ++ } ++ ++ mc_portal_virt_addr = devm_ioremap_nocache(dev, ++ mc_portal_phys_addr, ++ mc_portal_size); ++ if (!mc_portal_virt_addr) { ++ dev_err(dev, ++ "devm_ioremap_nocache failed for MC portal %pa\n", ++ &mc_portal_phys_addr); ++ return -ENXIO; ++ } ++ ++ mc_io->portal_virt_addr = mc_portal_virt_addr; ++ if (dpmcp_dev) { ++ error = fsl_mc_io_set_dpmcp(mc_io, dpmcp_dev); ++ if (error < 0) ++ goto error_destroy_mc_io; ++ } ++ ++ *new_mc_io = mc_io; ++ return 0; ++ ++error_destroy_mc_io: ++ fsl_destroy_mc_io(mc_io); ++ return error; ++} ++ ++/** ++ * Destroys an MC I/O object ++ * ++ * @mc_io: MC I/O object to destroy ++ */ ++void fsl_destroy_mc_io(struct fsl_mc_io *mc_io) ++{ ++ struct fsl_mc_device *dpmcp_dev = mc_io->dpmcp_dev; ++ ++ if (dpmcp_dev) ++ fsl_mc_io_unset_dpmcp(mc_io); ++ ++ devm_iounmap(mc_io->dev, mc_io->portal_virt_addr); ++ devm_release_mem_region(mc_io->dev, ++ mc_io->portal_phys_addr, ++ mc_io->portal_size); ++ ++ mc_io->portal_virt_addr = NULL; ++ devm_kfree(mc_io->dev, mc_io); ++} ++ ++/** ++ * fsl_mc_portal_allocate - Allocates an MC portal ++ * ++ * @mc_dev: MC device for which the MC portal is to be allocated ++ * @mc_io_flags: Flags for the fsl_mc_io object that wraps the allocated ++ * MC portal. ++ * @new_mc_io: Pointer to area where the pointer to the fsl_mc_io object ++ * that wraps the allocated MC portal is to be returned ++ * ++ * This function allocates an MC portal from the device's parent DPRC, ++ * from the corresponding MC bus' pool of MC portals and wraps ++ * it in a new fsl_mc_io object. If 'mc_dev' is a DPRC itself, the ++ * portal is allocated from its own MC bus. ++ */ ++int __must_check fsl_mc_portal_allocate(struct fsl_mc_device *mc_dev, ++ u16 mc_io_flags, ++ struct fsl_mc_io **new_mc_io) ++{ ++ struct fsl_mc_device *mc_bus_dev; ++ struct fsl_mc_bus *mc_bus; ++ phys_addr_t mc_portal_phys_addr; ++ size_t mc_portal_size; ++ struct fsl_mc_device *dpmcp_dev; ++ int error = -EINVAL; ++ struct fsl_mc_resource *resource = NULL; ++ struct fsl_mc_io *mc_io = NULL; ++ ++ if (mc_dev->flags & FSL_MC_IS_DPRC) { ++ mc_bus_dev = mc_dev; ++ } else { ++ if (!dev_is_fsl_mc(mc_dev->dev.parent)) ++ return error; ++ ++ mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent); ++ } ++ ++ mc_bus = to_fsl_mc_bus(mc_bus_dev); ++ *new_mc_io = NULL; ++ error = fsl_mc_resource_allocate(mc_bus, FSL_MC_POOL_DPMCP, &resource); ++ if (error < 0) ++ return error; ++ ++ error = -EINVAL; ++ dpmcp_dev = resource->data; ++ ++ if (dpmcp_dev->obj_desc.ver_major < DPMCP_MIN_VER_MAJOR || ++ (dpmcp_dev->obj_desc.ver_major == DPMCP_MIN_VER_MAJOR && ++ dpmcp_dev->obj_desc.ver_minor < DPMCP_MIN_VER_MINOR)) { ++ dev_err(&dpmcp_dev->dev, ++ "ERROR: Version %d.%d of DPMCP not supported.\n", ++ dpmcp_dev->obj_desc.ver_major, ++ dpmcp_dev->obj_desc.ver_minor); ++ error = -ENOTSUPP; ++ goto error_cleanup_resource; ++ } ++ ++ mc_portal_phys_addr = dpmcp_dev->regions[0].start; ++ mc_portal_size = resource_size(dpmcp_dev->regions); ++ ++ error = fsl_create_mc_io(&mc_bus_dev->dev, ++ mc_portal_phys_addr, ++ mc_portal_size, dpmcp_dev, ++ mc_io_flags, &mc_io); ++ if (error < 0) ++ goto error_cleanup_resource; ++ ++ *new_mc_io = mc_io; ++ return 0; ++ ++error_cleanup_resource: ++ fsl_mc_resource_free(resource); ++ return error; ++} ++EXPORT_SYMBOL_GPL(fsl_mc_portal_allocate); ++ ++/** ++ * fsl_mc_portal_free - Returns an MC portal to the pool of free MC portals ++ * of a given MC bus ++ * ++ * @mc_io: Pointer to the fsl_mc_io object that wraps the MC portal to free ++ */ ++void fsl_mc_portal_free(struct fsl_mc_io *mc_io) ++{ ++ struct fsl_mc_device *dpmcp_dev; ++ struct fsl_mc_resource *resource; ++ ++ /* ++ * Every mc_io obtained by calling fsl_mc_portal_allocate() is supposed ++ * to have a DPMCP object associated with. ++ */ ++ dpmcp_dev = mc_io->dpmcp_dev; ++ ++ resource = dpmcp_dev->resource; ++ if (!resource || resource->type != FSL_MC_POOL_DPMCP) ++ return; ++ ++ if (resource->data != dpmcp_dev) ++ return; ++ ++ fsl_destroy_mc_io(mc_io); ++ fsl_mc_resource_free(resource); ++} ++EXPORT_SYMBOL_GPL(fsl_mc_portal_free); ++ ++/** ++ * fsl_mc_portal_reset - Resets the dpmcp object for a given fsl_mc_io object ++ * ++ * @mc_io: Pointer to the fsl_mc_io object that wraps the MC portal to free ++ */ ++int fsl_mc_portal_reset(struct fsl_mc_io *mc_io) ++{ ++ int error; ++ struct fsl_mc_device *dpmcp_dev = mc_io->dpmcp_dev; ++ ++ error = dpmcp_reset(mc_io, 0, dpmcp_dev->mc_handle); ++ if (error < 0) { ++ dev_err(&dpmcp_dev->dev, "dpmcp_reset() failed: %d\n", error); ++ return error; ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(fsl_mc_portal_reset); +--- a/drivers/staging/fsl-mc/bus/mc-sys.c ++++ /dev/null +@@ -1,297 +0,0 @@ +-// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +-/* +- * Copyright 2013-2016 Freescale Semiconductor Inc. +- * +- * I/O services to send MC commands to the MC hardware +- * +- */ +- +-#include <linux/delay.h> +-#include <linux/slab.h> +-#include <linux/ioport.h> +-#include <linux/device.h> +-#include <linux/io.h> +-#include <linux/io-64-nonatomic-hi-lo.h> +-#include "../include/mc.h" +- +-#include "dpmcp.h" +- +-/** +- * Timeout in milliseconds to wait for the completion of an MC command +- */ +-#define MC_CMD_COMPLETION_TIMEOUT_MS 500 +- +-/* +- * usleep_range() min and max values used to throttle down polling +- * iterations while waiting for MC command completion +- */ +-#define MC_CMD_COMPLETION_POLLING_MIN_SLEEP_USECS 10 +-#define MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS 500 +- +-static enum mc_cmd_status mc_cmd_hdr_read_status(struct mc_command *cmd) +-{ +- struct mc_cmd_header *hdr = (struct mc_cmd_header *)&cmd->header; +- +- return (enum mc_cmd_status)hdr->status; +-} +- +-static u16 mc_cmd_hdr_read_cmdid(struct mc_command *cmd) +-{ +- struct mc_cmd_header *hdr = (struct mc_cmd_header *)&cmd->header; +- u16 cmd_id = le16_to_cpu(hdr->cmd_id); +- +- return cmd_id; +-} +- +-static int mc_status_to_error(enum mc_cmd_status status) +-{ +- static const int mc_status_to_error_map[] = { +- [MC_CMD_STATUS_OK] = 0, +- [MC_CMD_STATUS_AUTH_ERR] = -EACCES, +- [MC_CMD_STATUS_NO_PRIVILEGE] = -EPERM, +- [MC_CMD_STATUS_DMA_ERR] = -EIO, +- [MC_CMD_STATUS_CONFIG_ERR] = -ENXIO, +- [MC_CMD_STATUS_TIMEOUT] = -ETIMEDOUT, +- [MC_CMD_STATUS_NO_RESOURCE] = -ENAVAIL, +- [MC_CMD_STATUS_NO_MEMORY] = -ENOMEM, +- [MC_CMD_STATUS_BUSY] = -EBUSY, +- [MC_CMD_STATUS_UNSUPPORTED_OP] = -ENOTSUPP, +- [MC_CMD_STATUS_INVALID_STATE] = -ENODEV, +- }; +- +- if (WARN_ON((u32)status >= ARRAY_SIZE(mc_status_to_error_map))) +- return -EINVAL; +- +- return mc_status_to_error_map[status]; +-} +- +-static const char *mc_status_to_string(enum mc_cmd_status status) +-{ +- static const char *const status_strings[] = { +- [MC_CMD_STATUS_OK] = "Command completed successfully", +- [MC_CMD_STATUS_READY] = "Command ready to be processed", +- [MC_CMD_STATUS_AUTH_ERR] = "Authentication error", +- [MC_CMD_STATUS_NO_PRIVILEGE] = "No privilege", +- [MC_CMD_STATUS_DMA_ERR] = "DMA or I/O error", +- [MC_CMD_STATUS_CONFIG_ERR] = "Configuration error", +- [MC_CMD_STATUS_TIMEOUT] = "Operation timed out", +- [MC_CMD_STATUS_NO_RESOURCE] = "No resources", +- [MC_CMD_STATUS_NO_MEMORY] = "No memory available", +- [MC_CMD_STATUS_BUSY] = "Device is busy", +- [MC_CMD_STATUS_UNSUPPORTED_OP] = "Unsupported operation", +- [MC_CMD_STATUS_INVALID_STATE] = "Invalid state" +- }; +- +- if ((unsigned int)status >= ARRAY_SIZE(status_strings)) +- return "Unknown MC error"; +- +- return status_strings[status]; +-} +- +-/** +- * mc_write_command - writes a command to a Management Complex (MC) portal +- * +- * @portal: pointer to an MC portal +- * @cmd: pointer to a filled command +- */ +-static inline void mc_write_command(struct mc_command __iomem *portal, +- struct mc_command *cmd) +-{ +- int i; +- +- /* copy command parameters into the portal */ +- for (i = 0; i < MC_CMD_NUM_OF_PARAMS; i++) +- /* +- * Data is already in the expected LE byte-order. Do an +- * extra LE -> CPU conversion so that the CPU -> LE done in +- * the device io write api puts it back in the right order. +- */ +- writeq_relaxed(le64_to_cpu(cmd->params[i]), &portal->params[i]); +- +- /* submit the command by writing the header */ +- writeq(le64_to_cpu(cmd->header), &portal->header); +-} +- +-/** +- * mc_read_response - reads the response for the last MC command from a +- * Management Complex (MC) portal +- * +- * @portal: pointer to an MC portal +- * @resp: pointer to command response buffer +- * +- * Returns MC_CMD_STATUS_OK on Success; Error code otherwise. +- */ +-static inline enum mc_cmd_status mc_read_response(struct mc_command __iomem * +- portal, +- struct mc_command *resp) +-{ +- int i; +- enum mc_cmd_status status; +- +- /* Copy command response header from MC portal: */ +- resp->header = cpu_to_le64(readq_relaxed(&portal->header)); +- status = mc_cmd_hdr_read_status(resp); +- if (status != MC_CMD_STATUS_OK) +- return status; +- +- /* Copy command response data from MC portal: */ +- for (i = 0; i < MC_CMD_NUM_OF_PARAMS; i++) +- /* +- * Data is expected to be in LE byte-order. Do an +- * extra CPU -> LE to revert the LE -> CPU done in +- * the device io read api. +- */ +- resp->params[i] = +- cpu_to_le64(readq_relaxed(&portal->params[i])); +- +- return status; +-} +- +-/** +- * Waits for the completion of an MC command doing preemptible polling. +- * uslepp_range() is called between polling iterations. +- * +- * @mc_io: MC I/O object to be used +- * @cmd: command buffer to receive MC response +- * @mc_status: MC command completion status +- */ +-static int mc_polling_wait_preemptible(struct fsl_mc_io *mc_io, +- struct mc_command *cmd, +- enum mc_cmd_status *mc_status) +-{ +- enum mc_cmd_status status; +- unsigned long jiffies_until_timeout = +- jiffies + msecs_to_jiffies(MC_CMD_COMPLETION_TIMEOUT_MS); +- +- /* +- * Wait for response from the MC hardware: +- */ +- for (;;) { +- status = mc_read_response(mc_io->portal_virt_addr, cmd); +- if (status != MC_CMD_STATUS_READY) +- break; +- +- /* +- * TODO: When MC command completion interrupts are supported +- * call wait function here instead of usleep_range() +- */ +- usleep_range(MC_CMD_COMPLETION_POLLING_MIN_SLEEP_USECS, +- MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS); +- +- if (time_after_eq(jiffies, jiffies_until_timeout)) { +- dev_dbg(mc_io->dev, +- "MC command timed out (portal: %pa, dprc handle: %#x, command: %#x)\n", +- &mc_io->portal_phys_addr, +- (unsigned int)mc_cmd_hdr_read_token(cmd), +- (unsigned int)mc_cmd_hdr_read_cmdid(cmd)); +- +- return -ETIMEDOUT; +- } +- } +- +- *mc_status = status; +- return 0; +-} +- +-/** +- * Waits for the completion of an MC command doing atomic polling. +- * udelay() is called between polling iterations. +- * +- * @mc_io: MC I/O object to be used +- * @cmd: command buffer to receive MC response +- * @mc_status: MC command completion status +- */ +-static int mc_polling_wait_atomic(struct fsl_mc_io *mc_io, +- struct mc_command *cmd, +- enum mc_cmd_status *mc_status) +-{ +- enum mc_cmd_status status; +- unsigned long timeout_usecs = MC_CMD_COMPLETION_TIMEOUT_MS * 1000; +- +- BUILD_BUG_ON((MC_CMD_COMPLETION_TIMEOUT_MS * 1000) % +- MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS != 0); +- +- for (;;) { +- status = mc_read_response(mc_io->portal_virt_addr, cmd); +- if (status != MC_CMD_STATUS_READY) +- break; +- +- udelay(MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS); +- timeout_usecs -= MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS; +- if (timeout_usecs == 0) { +- dev_dbg(mc_io->dev, +- "MC command timed out (portal: %pa, dprc handle: %#x, command: %#x)\n", +- &mc_io->portal_phys_addr, +- (unsigned int)mc_cmd_hdr_read_token(cmd), +- (unsigned int)mc_cmd_hdr_read_cmdid(cmd)); +- +- return -ETIMEDOUT; +- } +- } +- +- *mc_status = status; +- return 0; +-} +- +-/** +- * Sends a command to the MC device using the given MC I/O object +- * +- * @mc_io: MC I/O object to be used +- * @cmd: command to be sent +- * +- * Returns '0' on Success; Error code otherwise. +- */ +-int mc_send_command(struct fsl_mc_io *mc_io, struct mc_command *cmd) +-{ +- int error; +- enum mc_cmd_status status; +- unsigned long irq_flags = 0; +- +- if (WARN_ON(in_irq() && +- !(mc_io->flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL))) +- return -EINVAL; +- +- if (mc_io->flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL) +- spin_lock_irqsave(&mc_io->spinlock, irq_flags); +- else +- mutex_lock(&mc_io->mutex); +- +- /* +- * Send command to the MC hardware: +- */ +- mc_write_command(mc_io->portal_virt_addr, cmd); +- +- /* +- * Wait for response from the MC hardware: +- */ +- if (!(mc_io->flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL)) +- error = mc_polling_wait_preemptible(mc_io, cmd, &status); +- else +- error = mc_polling_wait_atomic(mc_io, cmd, &status); +- +- if (error < 0) +- goto common_exit; +- +- if (status != MC_CMD_STATUS_OK) { +- dev_dbg(mc_io->dev, +- "MC command failed: portal: %pa, dprc handle: %#x, command: %#x, status: %s (%#x)\n", +- &mc_io->portal_phys_addr, +- (unsigned int)mc_cmd_hdr_read_token(cmd), +- (unsigned int)mc_cmd_hdr_read_cmdid(cmd), +- mc_status_to_string(status), +- (unsigned int)status); +- +- error = mc_status_to_error(status); +- goto common_exit; +- } +- +- error = 0; +-common_exit: +- if (mc_io->flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL) +- spin_unlock_irqrestore(&mc_io->spinlock, irq_flags); +- else +- mutex_unlock(&mc_io->mutex); +- +- return error; +-} +-EXPORT_SYMBOL(mc_send_command); +--- /dev/null ++++ b/drivers/bus/fsl-mc/mc-sys.c +@@ -0,0 +1,296 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) ++/* ++ * Copyright 2013-2016 Freescale Semiconductor Inc. ++ * ++ * I/O services to send MC commands to the MC hardware ++ * ++ */ ++ ++#include <linux/delay.h> ++#include <linux/slab.h> ++#include <linux/ioport.h> ++#include <linux/device.h> ++#include <linux/io.h> ++#include <linux/io-64-nonatomic-hi-lo.h> ++#include <linux/fsl/mc.h> ++ ++#include "fsl-mc-private.h" ++ ++/** ++ * Timeout in milliseconds to wait for the completion of an MC command ++ */ ++#define MC_CMD_COMPLETION_TIMEOUT_MS 15000 ++ ++/* ++ * usleep_range() min and max values used to throttle down polling ++ * iterations while waiting for MC command completion ++ */ ++#define MC_CMD_COMPLETION_POLLING_MIN_SLEEP_USECS 10 ++#define MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS 500 ++ ++static enum mc_cmd_status mc_cmd_hdr_read_status(struct fsl_mc_command *cmd) ++{ ++ struct mc_cmd_header *hdr = (struct mc_cmd_header *)&cmd->header; ++ ++ return (enum mc_cmd_status)hdr->status; ++} ++ ++static u16 mc_cmd_hdr_read_cmdid(struct fsl_mc_command *cmd) ++{ ++ struct mc_cmd_header *hdr = (struct mc_cmd_header *)&cmd->header; ++ u16 cmd_id = le16_to_cpu(hdr->cmd_id); ++ ++ return cmd_id; ++} ++ ++static int mc_status_to_error(enum mc_cmd_status status) ++{ ++ static const int mc_status_to_error_map[] = { ++ [MC_CMD_STATUS_OK] = 0, ++ [MC_CMD_STATUS_AUTH_ERR] = -EACCES, ++ [MC_CMD_STATUS_NO_PRIVILEGE] = -EPERM, ++ [MC_CMD_STATUS_DMA_ERR] = -EIO, ++ [MC_CMD_STATUS_CONFIG_ERR] = -ENXIO, ++ [MC_CMD_STATUS_TIMEOUT] = -ETIMEDOUT, ++ [MC_CMD_STATUS_NO_RESOURCE] = -ENAVAIL, ++ [MC_CMD_STATUS_NO_MEMORY] = -ENOMEM, ++ [MC_CMD_STATUS_BUSY] = -EBUSY, ++ [MC_CMD_STATUS_UNSUPPORTED_OP] = -ENOTSUPP, ++ [MC_CMD_STATUS_INVALID_STATE] = -ENODEV, ++ }; ++ ++ if ((u32)status >= ARRAY_SIZE(mc_status_to_error_map)) ++ return -EINVAL; ++ ++ return mc_status_to_error_map[status]; ++} ++ ++static const char *mc_status_to_string(enum mc_cmd_status status) ++{ ++ static const char *const status_strings[] = { ++ [MC_CMD_STATUS_OK] = "Command completed successfully", ++ [MC_CMD_STATUS_READY] = "Command ready to be processed", ++ [MC_CMD_STATUS_AUTH_ERR] = "Authentication error", ++ [MC_CMD_STATUS_NO_PRIVILEGE] = "No privilege", ++ [MC_CMD_STATUS_DMA_ERR] = "DMA or I/O error", ++ [MC_CMD_STATUS_CONFIG_ERR] = "Configuration error", ++ [MC_CMD_STATUS_TIMEOUT] = "Operation timed out", ++ [MC_CMD_STATUS_NO_RESOURCE] = "No resources", ++ [MC_CMD_STATUS_NO_MEMORY] = "No memory available", ++ [MC_CMD_STATUS_BUSY] = "Device is busy", ++ [MC_CMD_STATUS_UNSUPPORTED_OP] = "Unsupported operation", ++ [MC_CMD_STATUS_INVALID_STATE] = "Invalid state" ++ }; ++ ++ if ((unsigned int)status >= ARRAY_SIZE(status_strings)) ++ return "Unknown MC error"; ++ ++ return status_strings[status]; ++} ++ ++/** ++ * mc_write_command - writes a command to a Management Complex (MC) portal ++ * ++ * @portal: pointer to an MC portal ++ * @cmd: pointer to a filled command ++ */ ++static inline void mc_write_command(struct fsl_mc_command __iomem *portal, ++ struct fsl_mc_command *cmd) ++{ ++ int i; ++ ++ /* copy command parameters into the portal */ ++ for (i = 0; i < MC_CMD_NUM_OF_PARAMS; i++) ++ /* ++ * Data is already in the expected LE byte-order. Do an ++ * extra LE -> CPU conversion so that the CPU -> LE done in ++ * the device io write api puts it back in the right order. ++ */ ++ writeq_relaxed(le64_to_cpu(cmd->params[i]), &portal->params[i]); ++ ++ /* submit the command by writing the header */ ++ writeq(le64_to_cpu(cmd->header), &portal->header); ++} ++ ++/** ++ * mc_read_response - reads the response for the last MC command from a ++ * Management Complex (MC) portal ++ * ++ * @portal: pointer to an MC portal ++ * @resp: pointer to command response buffer ++ * ++ * Returns MC_CMD_STATUS_OK on Success; Error code otherwise. ++ */ ++static inline enum mc_cmd_status mc_read_response(struct fsl_mc_command __iomem ++ *portal, ++ struct fsl_mc_command *resp) ++{ ++ int i; ++ enum mc_cmd_status status; ++ ++ /* Copy command response header from MC portal: */ ++ resp->header = cpu_to_le64(readq_relaxed(&portal->header)); ++ status = mc_cmd_hdr_read_status(resp); ++ if (status != MC_CMD_STATUS_OK) ++ return status; ++ ++ /* Copy command response data from MC portal: */ ++ for (i = 0; i < MC_CMD_NUM_OF_PARAMS; i++) ++ /* ++ * Data is expected to be in LE byte-order. Do an ++ * extra CPU -> LE to revert the LE -> CPU done in ++ * the device io read api. ++ */ ++ resp->params[i] = ++ cpu_to_le64(readq_relaxed(&portal->params[i])); ++ ++ return status; ++} ++ ++/** ++ * Waits for the completion of an MC command doing preemptible polling. ++ * uslepp_range() is called between polling iterations. ++ * ++ * @mc_io: MC I/O object to be used ++ * @cmd: command buffer to receive MC response ++ * @mc_status: MC command completion status ++ */ ++static int mc_polling_wait_preemptible(struct fsl_mc_io *mc_io, ++ struct fsl_mc_command *cmd, ++ enum mc_cmd_status *mc_status) ++{ ++ enum mc_cmd_status status; ++ unsigned long jiffies_until_timeout = ++ jiffies + msecs_to_jiffies(MC_CMD_COMPLETION_TIMEOUT_MS); ++ ++ /* ++ * Wait for response from the MC hardware: ++ */ ++ for (;;) { ++ status = mc_read_response(mc_io->portal_virt_addr, cmd); ++ if (status != MC_CMD_STATUS_READY) ++ break; ++ ++ /* ++ * TODO: When MC command completion interrupts are supported ++ * call wait function here instead of usleep_range() ++ */ ++ usleep_range(MC_CMD_COMPLETION_POLLING_MIN_SLEEP_USECS, ++ MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS); ++ ++ if (time_after_eq(jiffies, jiffies_until_timeout)) { ++ dev_dbg(mc_io->dev, ++ "MC command timed out (portal: %pa, dprc handle: %#x, command: %#x)\n", ++ &mc_io->portal_phys_addr, ++ (unsigned int)mc_cmd_hdr_read_token(cmd), ++ (unsigned int)mc_cmd_hdr_read_cmdid(cmd)); ++ ++ return -ETIMEDOUT; ++ } ++ } ++ ++ *mc_status = status; ++ return 0; ++} ++ ++/** ++ * Waits for the completion of an MC command doing atomic polling. ++ * udelay() is called between polling iterations. ++ * ++ * @mc_io: MC I/O object to be used ++ * @cmd: command buffer to receive MC response ++ * @mc_status: MC command completion status ++ */ ++static int mc_polling_wait_atomic(struct fsl_mc_io *mc_io, ++ struct fsl_mc_command *cmd, ++ enum mc_cmd_status *mc_status) ++{ ++ enum mc_cmd_status status; ++ unsigned long timeout_usecs = MC_CMD_COMPLETION_TIMEOUT_MS * 1000; ++ ++ BUILD_BUG_ON((MC_CMD_COMPLETION_TIMEOUT_MS * 1000) % ++ MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS != 0); ++ ++ for (;;) { ++ status = mc_read_response(mc_io->portal_virt_addr, cmd); ++ if (status != MC_CMD_STATUS_READY) ++ break; ++ ++ udelay(MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS); ++ timeout_usecs -= MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS; ++ if (timeout_usecs == 0) { ++ dev_dbg(mc_io->dev, ++ "MC command timed out (portal: %pa, dprc handle: %#x, command: %#x)\n", ++ &mc_io->portal_phys_addr, ++ (unsigned int)mc_cmd_hdr_read_token(cmd), ++ (unsigned int)mc_cmd_hdr_read_cmdid(cmd)); ++ ++ return -ETIMEDOUT; ++ } ++ } ++ ++ *mc_status = status; ++ return 0; ++} ++ ++/** ++ * Sends a command to the MC device using the given MC I/O object ++ * ++ * @mc_io: MC I/O object to be used ++ * @cmd: command to be sent ++ * ++ * Returns '0' on Success; Error code otherwise. ++ */ ++int mc_send_command(struct fsl_mc_io *mc_io, struct fsl_mc_command *cmd) ++{ ++ int error; ++ enum mc_cmd_status status; ++ unsigned long irq_flags = 0; ++ ++ if (in_irq() && !(mc_io->flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL)) ++ return -EINVAL; ++ ++ if (mc_io->flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL) ++ spin_lock_irqsave(&mc_io->spinlock, irq_flags); ++ else ++ mutex_lock(&mc_io->mutex); ++ ++ /* ++ * Send command to the MC hardware: ++ */ ++ mc_write_command(mc_io->portal_virt_addr, cmd); ++ ++ /* ++ * Wait for response from the MC hardware: ++ */ ++ if (!(mc_io->flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL)) ++ error = mc_polling_wait_preemptible(mc_io, cmd, &status); ++ else ++ error = mc_polling_wait_atomic(mc_io, cmd, &status); ++ ++ if (error < 0) ++ goto common_exit; ++ ++ if (status != MC_CMD_STATUS_OK) { ++ dev_dbg(mc_io->dev, ++ "MC command failed: portal: %pa, dprc handle: %#x, command: %#x, status: %s (%#x)\n", ++ &mc_io->portal_phys_addr, ++ (unsigned int)mc_cmd_hdr_read_token(cmd), ++ (unsigned int)mc_cmd_hdr_read_cmdid(cmd), ++ mc_status_to_string(status), ++ (unsigned int)status); ++ ++ error = mc_status_to_error(status); ++ goto common_exit; ++ } ++ ++ error = 0; ++common_exit: ++ if (mc_io->flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL) ++ spin_unlock_irqrestore(&mc_io->spinlock, irq_flags); ++ else ++ mutex_unlock(&mc_io->mutex); ++ ++ return error; ++} ++EXPORT_SYMBOL_GPL(mc_send_command); +--- a/drivers/irqchip/Kconfig ++++ b/drivers/irqchip/Kconfig +@@ -42,6 +42,12 @@ config ARM_GIC_V3_ITS + depends on PCI + depends on PCI_MSI + ++config ARM_GIC_V3_ITS_FSL_MC ++ bool ++ depends on ARM_GIC_V3_ITS ++ depends on FSL_MC_BUS ++ default ARM_GIC_V3_ITS ++ + config ARM_NVIC + bool + select IRQ_DOMAIN +--- a/drivers/irqchip/Makefile ++++ b/drivers/irqchip/Makefile +@@ -30,6 +30,7 @@ obj-$(CONFIG_ARCH_REALVIEW) += irq-gic- + obj-$(CONFIG_ARM_GIC_V2M) += irq-gic-v2m.o + obj-$(CONFIG_ARM_GIC_V3) += irq-gic-v3.o irq-gic-common.o + obj-$(CONFIG_ARM_GIC_V3_ITS) += irq-gic-v3-its.o irq-gic-v3-its-pci-msi.o irq-gic-v3-its-platform-msi.o irq-gic-v4.o ++obj-$(CONFIG_ARM_GIC_V3_ITS_FSL_MC) += irq-gic-v3-its-fsl-mc-msi.o + obj-$(CONFIG_PARTITION_PERCPU) += irq-partition-percpu.o + obj-$(CONFIG_HISILICON_IRQ_MBIGEN) += irq-mbigen.o + obj-$(CONFIG_ARM_NVIC) += irq-nvic.o +--- /dev/null ++++ b/drivers/irqchip/irq-gic-v3-its-fsl-mc-msi.c +@@ -0,0 +1,98 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Freescale Management Complex (MC) bus driver MSI support ++ * ++ * Copyright (C) 2015-2016 Freescale Semiconductor, Inc. ++ * Author: German Rivera <German.Rivera@freescale.com> ++ * ++ */ ++ ++#include <linux/of_device.h> ++#include <linux/of_address.h> ++#include <linux/irq.h> ++#include <linux/msi.h> ++#include <linux/of.h> ++#include <linux/of_irq.h> ++#include <linux/fsl/mc.h> ++ ++static struct irq_chip its_msi_irq_chip = { ++ .name = "ITS-fMSI", ++ .irq_mask = irq_chip_mask_parent, ++ .irq_unmask = irq_chip_unmask_parent, ++ .irq_eoi = irq_chip_eoi_parent, ++ .irq_set_affinity = msi_domain_set_affinity ++}; ++ ++static int its_fsl_mc_msi_prepare(struct irq_domain *msi_domain, ++ struct device *dev, ++ int nvec, msi_alloc_info_t *info) ++{ ++ struct fsl_mc_device *mc_bus_dev; ++ struct msi_domain_info *msi_info; ++ ++ if (!dev_is_fsl_mc(dev)) ++ return -EINVAL; ++ ++ mc_bus_dev = to_fsl_mc_device(dev); ++ if (!(mc_bus_dev->flags & FSL_MC_IS_DPRC)) ++ return -EINVAL; ++ ++ /* ++ * Set the device Id to be passed to the GIC-ITS: ++ * ++ * NOTE: This device id corresponds to the IOMMU stream ID ++ * associated with the DPRC object (ICID). ++ */ ++ info->scratchpad[0].ul = mc_bus_dev->icid; ++ msi_info = msi_get_domain_info(msi_domain->parent); ++ return msi_info->ops->msi_prepare(msi_domain->parent, dev, nvec, info); ++} ++ ++static struct msi_domain_ops its_fsl_mc_msi_ops __ro_after_init = { ++ .msi_prepare = its_fsl_mc_msi_prepare, ++}; ++ ++static struct msi_domain_info its_fsl_mc_msi_domain_info = { ++ .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS), ++ .ops = &its_fsl_mc_msi_ops, ++ .chip = &its_msi_irq_chip, ++}; ++ ++static const struct of_device_id its_device_id[] = { ++ { .compatible = "arm,gic-v3-its", }, ++ {}, ++}; ++ ++static int __init its_fsl_mc_msi_init(void) ++{ ++ struct device_node *np; ++ struct irq_domain *parent; ++ struct irq_domain *mc_msi_domain; ++ ++ for (np = of_find_matching_node(NULL, its_device_id); np; ++ np = of_find_matching_node(np, its_device_id)) { ++ if (!of_property_read_bool(np, "msi-controller")) ++ continue; ++ ++ parent = irq_find_matching_host(np, DOMAIN_BUS_NEXUS); ++ if (!parent || !msi_get_domain_info(parent)) { ++ pr_err("%pOF: unable to locate ITS domain\n", np); ++ continue; ++ } ++ ++ mc_msi_domain = fsl_mc_msi_create_irq_domain( ++ of_node_to_fwnode(np), ++ &its_fsl_mc_msi_domain_info, ++ parent); ++ if (!mc_msi_domain) { ++ pr_err("%pOF: unable to create fsl-mc domain\n", np); ++ continue; ++ } ++ ++ pr_info("fsl-mc MSI: %pOF domain created\n", np); ++ } ++ ++ return 0; ++} ++ ++early_initcall(its_fsl_mc_msi_init); +--- a/drivers/staging/fsl-dpaa2/ethernet/README ++++ b/drivers/staging/fsl-dpaa2/ethernet/README +@@ -36,7 +36,7 @@ are treated as internal resources of oth + + For a more detailed description of the DPAA2 architecture and its object + abstractions see: +- drivers/staging/fsl-mc/README.txt ++ Documentation/networking/dpaa2/overview.rst + + Each Linux net device is built on top of a Datapath Network Interface (DPNI) + object and uses Buffer Pools (DPBPs), I/O Portals (DPIOs) and Concentrators +--- a/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.c ++++ b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.c +@@ -43,7 +43,7 @@ + #include <linux/filter.h> + #include <linux/atomic.h> + #include <net/sock.h> +-#include "../../fsl-mc/include/mc.h" ++#include <linux/fsl/mc.h> + #include "dpaa2-eth.h" + #include "dpaa2-eth-ceetm.h" + +--- a/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.h ++++ b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.h +@@ -36,11 +36,10 @@ + #include <linux/dcbnl.h> + #include <linux/netdevice.h> + #include <linux/if_vlan.h> ++#include <linux/fsl/mc.h> + + #include "../../fsl-mc/include/dpaa2-io.h" + #include "../../fsl-mc/include/dpaa2-fd.h" +-#include "../../fsl-mc/include/dpbp.h" +-#include "../../fsl-mc/include/dpcon.h" + #include "dpni.h" + #include "dpni-cmd.h" + +--- a/drivers/staging/fsl-dpaa2/ethernet/dpni.c ++++ b/drivers/staging/fsl-dpaa2/ethernet/dpni.c +@@ -32,7 +32,7 @@ + */ + #include <linux/kernel.h> + #include <linux/errno.h> +-#include "../../fsl-mc/include/mc.h" ++#include <linux/fsl/mc.h> + #include "dpni.h" + #include "dpni-cmd.h" + +--- a/drivers/staging/fsl-mc/TODO ++++ /dev/null +@@ -1,18 +0,0 @@ +-* Add at least one device driver for a DPAA2 object (child device of the +- fsl-mc bus). Most likely candidate for this is adding DPAA2 Ethernet +- driver support, which depends on drivers for several objects: DPNI, +- DPIO, DPMAC. Other pre-requisites include: +- +- * MC firmware uprev. The MC firmware upon which the fsl-mc +- bus driver and DPAA2 object drivers are based is continuing +- to evolve, so minor updates are needed to keep in sync with binary +- interface changes to the MC. +- +-* Cleanup +- +-Please send any patches to Greg Kroah-Hartman <gregkh@linuxfoundation.org>, +-german.rivera@freescale.com, devel@driverdev.osuosl.org, +-linux-kernel@vger.kernel.org +- +-[1] https://lkml.org/lkml/2015/7/9/93 +-[2] https://lkml.org/lkml/2015/7/7/712 +--- a/drivers/staging/fsl-mc/bus/Kconfig ++++ b/drivers/staging/fsl-mc/bus/Kconfig +@@ -5,16 +5,6 @@ + # Copyright (C) 2014-2016 Freescale Semiconductor, Inc. + # + +-config FSL_MC_BUS +- bool "QorIQ DPAA2 fsl-mc bus driver" +- depends on OF && (ARCH_LAYERSCAPE || (COMPILE_TEST && (ARM || ARM64 || X86_LOCAL_APIC || PPC))) +- select GENERIC_MSI_IRQ_DOMAIN +- help +- Driver to enable the bus infrastructure for the QorIQ DPAA2 +- architecture. The fsl-mc bus driver handles discovery of +- DPAA2 objects (which are represented as Linux devices) and +- binding objects to drivers. +- + config FSL_MC_DPIO + tristate "QorIQ DPAA2 DPIO driver" + depends on FSL_MC_BUS +@@ -24,3 +14,9 @@ config FSL_MC_DPIO + other DPAA2 objects. This driver does not expose the DPIO + objects individually, but groups them under a service layer + API. ++ ++config FSL_QBMAN_DEBUG ++ tristate "Freescale QBMAN Debug APIs" ++ depends on FSL_MC_DPIO ++ help ++ QBMan debug assistant APIs. +--- a/drivers/staging/fsl-mc/bus/Makefile ++++ b/drivers/staging/fsl-mc/bus/Makefile +@@ -4,19 +4,6 @@ + # + # Copyright (C) 2014 Freescale Semiconductor, Inc. + # +-obj-$(CONFIG_FSL_MC_BUS) += mc-bus-driver.o +- +-mc-bus-driver-objs := fsl-mc-bus.o \ +- mc-sys.o \ +- mc-io.o \ +- dprc.o \ +- dprc-driver.o \ +- fsl-mc-allocator.o \ +- fsl-mc-msi.o \ +- irq-gic-v3-its-fsl-mc-msi.o \ +- dpmcp.o \ +- dpbp.o \ +- dpcon.o + + # MC DPIO driver + obj-$(CONFIG_FSL_MC_DPIO) += dpio/ +--- a/drivers/staging/fsl-mc/bus/dpio/dpio-driver.c ++++ b/drivers/staging/fsl-mc/bus/dpio/dpio-driver.c +@@ -14,7 +14,7 @@ + #include <linux/dma-mapping.h> + #include <linux/delay.h> + +-#include "../../include/mc.h" ++#include <linux/fsl/mc.h> + #include "../../include/dpaa2-io.h" + + #include "qbman-portal.h" +--- a/drivers/staging/fsl-mc/bus/dpio/dpio-service.c ++++ b/drivers/staging/fsl-mc/bus/dpio/dpio-service.c +@@ -5,7 +5,7 @@ + * + */ + #include <linux/types.h> +-#include "../../include/mc.h" ++#include <linux/fsl/mc.h> + #include "../../include/dpaa2-io.h" + #include <linux/init.h> + #include <linux/module.h> +--- a/drivers/staging/fsl-mc/bus/dpio/dpio.c ++++ b/drivers/staging/fsl-mc/bus/dpio/dpio.c +@@ -5,7 +5,7 @@ + * + */ + #include <linux/kernel.h> +-#include "../../include/mc.h" ++#include <linux/fsl/mc.h> + + #include "dpio.h" + #include "dpio-cmd.h" +@@ -37,7 +37,7 @@ int dpio_open(struct fsl_mc_io *mc_io, + int dpio_id, + u16 *token) + { +- struct mc_command cmd = { 0 }; ++ struct fsl_mc_command cmd = { 0 }; + struct dpio_cmd_open *dpio_cmd; + int err; + +@@ -70,7 +70,7 @@ int dpio_close(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token) + { +- struct mc_command cmd = { 0 }; ++ struct fsl_mc_command cmd = { 0 }; + + /* prepare command */ + cmd.header = mc_encode_cmd_header(DPIO_CMDID_CLOSE, +@@ -92,7 +92,7 @@ int dpio_enable(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token) + { +- struct mc_command cmd = { 0 }; ++ struct fsl_mc_command cmd = { 0 }; + + /* prepare command */ + cmd.header = mc_encode_cmd_header(DPIO_CMDID_ENABLE, +@@ -114,7 +114,7 @@ int dpio_disable(struct fsl_mc_io *mc_io + u32 cmd_flags, + u16 token) + { +- struct mc_command cmd = { 0 }; ++ struct fsl_mc_command cmd = { 0 }; + + /* prepare command */ + cmd.header = mc_encode_cmd_header(DPIO_CMDID_DISABLE, +@@ -138,7 +138,7 @@ int dpio_get_attributes(struct fsl_mc_io + u16 token, + struct dpio_attr *attr) + { +- struct mc_command cmd = { 0 }; ++ struct fsl_mc_command cmd = { 0 }; + struct dpio_rsp_get_attr *dpio_rsp; + int err; + +@@ -180,7 +180,7 @@ int dpio_get_api_version(struct fsl_mc_i + u16 *major_ver, + u16 *minor_ver) + { +- struct mc_command cmd = { 0 }; ++ struct fsl_mc_command cmd = { 0 }; + int err; + + /* prepare command */ +--- a/drivers/staging/fsl-mc/bus/dpmcp-cmd.h ++++ /dev/null +@@ -1,56 +0,0 @@ +-/* +- * Copyright 2013-2016 Freescale Semiconductor Inc. +- * +- * Redistribution and use in source and binary forms, with or without +- * modification, are permitted provided that the following conditions are met: +- * * Redistributions of source code must retain the above copyright +- * notice, this list of conditions and the following disclaimer. +- * * Redistributions in binary form must reproduce the above copyright +- * notice, this list of conditions and the following disclaimer in the +- * documentation and/or other materials provided with the distribution. +- * * Neither the name of the above-listed copyright holders nor the +- * names of any contributors may be used to endorse or promote products +- * derived from this software without specific prior written permission. +- * +- * ALTERNATIVELY, this software may be distributed under the terms of the +- * GNU General Public License ("GPL") as published by the Free Software +- * Foundation, either version 2 of that License or (at your option) any +- * later version. +- * +- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE +- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +- * POSSIBILITY OF SUCH DAMAGE. +- */ +-#ifndef _FSL_DPMCP_CMD_H +-#define _FSL_DPMCP_CMD_H +- +-/* Minimal supported DPMCP Version */ +-#define DPMCP_MIN_VER_MAJOR 3 +-#define DPMCP_MIN_VER_MINOR 0 +- +-/* Command versioning */ +-#define DPMCP_CMD_BASE_VERSION 1 +-#define DPMCP_CMD_ID_OFFSET 4 +- +-#define DPMCP_CMD(id) (((id) << DPMCP_CMD_ID_OFFSET) | DPMCP_CMD_BASE_VERSION) +- +-/* Command IDs */ +-#define DPMCP_CMDID_CLOSE DPMCP_CMD(0x800) +-#define DPMCP_CMDID_OPEN DPMCP_CMD(0x80b) +-#define DPMCP_CMDID_GET_API_VERSION DPMCP_CMD(0xa0b) +- +-#define DPMCP_CMDID_RESET DPMCP_CMD(0x005) +- +-struct dpmcp_cmd_open { +- __le32 dpmcp_id; +-}; +- +-#endif /* _FSL_DPMCP_CMD_H */ +--- a/drivers/staging/fsl-mc/bus/dpmcp.h ++++ /dev/null +@@ -1,60 +0,0 @@ +-/* +- * Copyright 2013-2016 Freescale Semiconductor Inc. +- * +- * Redistribution and use in source and binary forms, with or without +- * modification, are permitted provided that the following conditions are met: +- * * Redistributions of source code must retain the above copyright +- * notice, this list of conditions and the following disclaimer. +- * * Redistributions in binary form must reproduce the above copyright +- * notice, this list of conditions and the following disclaimer in the +- * documentation and/or other materials provided with the distribution. +- * * Neither the name of the above-listed copyright holders nor the +- * names of any contributors may be used to endorse or promote products +- * derived from this software without specific prior written permission. +- * +- * ALTERNATIVELY, this software may be distributed under the terms of the +- * GNU General Public License ("GPL") as published by the Free Software +- * Foundation, either version 2 of that License or (at your option) any +- * later version. +- * +- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE +- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +- * POSSIBILITY OF SUCH DAMAGE. +- */ +-#ifndef __FSL_DPMCP_H +-#define __FSL_DPMCP_H +- +-/* +- * Data Path Management Command Portal API +- * Contains initialization APIs and runtime control APIs for DPMCP +- */ +- +-struct fsl_mc_io; +- +-int dpmcp_open(struct fsl_mc_io *mc_io, +- u32 cmd_flags, +- int dpmcp_id, +- u16 *token); +- +-int dpmcp_close(struct fsl_mc_io *mc_io, +- u32 cmd_flags, +- u16 token); +- +-int dpmcp_get_api_version(struct fsl_mc_io *mc_io, +- u32 cmd_flags, +- u16 *major_ver, +- u16 *minor_ver); +- +-int dpmcp_reset(struct fsl_mc_io *mc_io, +- u32 cmd_flags, +- u16 token); +- +-#endif /* __FSL_DPMCP_H */ +--- a/drivers/staging/fsl-mc/bus/dpmng-cmd.h ++++ /dev/null +@@ -1,58 +0,0 @@ +-/* +- * Copyright 2013-2016 Freescale Semiconductor Inc. +- * +- * Redistribution and use in source and binary forms, with or without +- * modification, are permitted provided that the following conditions are met: +- * * Redistributions of source code must retain the above copyright +- * notice, this list of conditions and the following disclaimer. +- * * Redistributions in binary form must reproduce the above copyright +- * notice, this list of conditions and the following disclaimer in the +- * documentation and/or other materials provided with the distribution. +- * * Neither the name of the above-listed copyright holders nor the +- * names of any contributors may be used to endorse or promote products +- * derived from this software without specific prior written permission. +- * +- * ALTERNATIVELY, this software may be distributed under the terms of the +- * GNU General Public License ("GPL") as published by the Free Software +- * Foundation, either version 2 of that License or (at your option) any +- * later version. +- * +- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE +- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +- * POSSIBILITY OF SUCH DAMAGE. +- */ +- +-/* +- * dpmng-cmd.h +- * +- * defines portal commands +- * +- */ +- +-#ifndef __FSL_DPMNG_CMD_H +-#define __FSL_DPMNG_CMD_H +- +-/* Command versioning */ +-#define DPMNG_CMD_BASE_VERSION 1 +-#define DPMNG_CMD_ID_OFFSET 4 +- +-#define DPMNG_CMD(id) (((id) << DPMNG_CMD_ID_OFFSET) | DPMNG_CMD_BASE_VERSION) +- +-/* Command IDs */ +-#define DPMNG_CMDID_GET_VERSION DPMNG_CMD(0x831) +- +-struct dpmng_rsp_get_version { +- __le32 revision; +- __le32 version_major; +- __le32 version_minor; +-}; +- +-#endif /* __FSL_DPMNG_CMD_H */ +--- a/drivers/staging/fsl-mc/bus/dprc-cmd.h ++++ /dev/null +@@ -1,451 +0,0 @@ +-/* +- * Copyright 2013-2016 Freescale Semiconductor Inc. +- * +- * Redistribution and use in source and binary forms, with or without +- * modification, are permitted provided that the following conditions are met: +- * * Redistributions of source code must retain the above copyright +- * notice, this list of conditions and the following disclaimer. +- * * Redistributions in binary form must reproduce the above copyright +- * notice, this list of conditions and the following disclaimer in the +- * documentation and/or other materials provided with the distribution. +- * * Neither the name of the above-listed copyright holders nor the +- * names of any contributors may be used to endorse or promote products +- * derived from this software without specific prior written permission. +- * +- * ALTERNATIVELY, this software may be distributed under the terms of the +- * GNU General Public License ("GPL") as published by the Free Software +- * Foundation, either version 2 of that License or (at your option) any +- * later version. +- * +- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE +- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +- * POSSIBILITY OF SUCH DAMAGE. +- */ +- +-/* +- * dprc-cmd.h +- * +- * defines dprc portal commands +- * +- */ +- +-#ifndef _FSL_DPRC_CMD_H +-#define _FSL_DPRC_CMD_H +- +-/* Minimal supported DPRC Version */ +-#define DPRC_MIN_VER_MAJOR 6 +-#define DPRC_MIN_VER_MINOR 0 +- +-/* Command versioning */ +-#define DPRC_CMD_BASE_VERSION 1 +-#define DPRC_CMD_ID_OFFSET 4 +- +-#define DPRC_CMD(id) (((id) << DPRC_CMD_ID_OFFSET) | DPRC_CMD_BASE_VERSION) +- +-/* Command IDs */ +-#define DPRC_CMDID_CLOSE DPRC_CMD(0x800) +-#define DPRC_CMDID_OPEN DPRC_CMD(0x805) +-#define DPRC_CMDID_GET_API_VERSION DPRC_CMD(0xa05) +- +-#define DPRC_CMDID_GET_ATTR DPRC_CMD(0x004) +- +-#define DPRC_CMDID_SET_IRQ DPRC_CMD(0x010) +-#define DPRC_CMDID_GET_IRQ DPRC_CMD(0x011) +-#define DPRC_CMDID_SET_IRQ_ENABLE DPRC_CMD(0x012) +-#define DPRC_CMDID_GET_IRQ_ENABLE DPRC_CMD(0x013) +-#define DPRC_CMDID_SET_IRQ_MASK DPRC_CMD(0x014) +-#define DPRC_CMDID_GET_IRQ_MASK DPRC_CMD(0x015) +-#define DPRC_CMDID_GET_IRQ_STATUS DPRC_CMD(0x016) +-#define DPRC_CMDID_CLEAR_IRQ_STATUS DPRC_CMD(0x017) +- +-#define DPRC_CMDID_GET_CONT_ID DPRC_CMD(0x830) +-#define DPRC_CMDID_GET_OBJ_COUNT DPRC_CMD(0x159) +-#define DPRC_CMDID_GET_OBJ DPRC_CMD(0x15A) +-#define DPRC_CMDID_GET_RES_COUNT DPRC_CMD(0x15B) +-#define DPRC_CMDID_GET_OBJ_REG DPRC_CMD(0x15E) +-#define DPRC_CMDID_SET_OBJ_IRQ DPRC_CMD(0x15F) +-#define DPRC_CMDID_GET_OBJ_IRQ DPRC_CMD(0x160) +- +-struct dprc_cmd_open { +- __le32 container_id; +-}; +- +-struct dprc_cmd_create_container { +- /* cmd word 0 */ +- __le32 options; +- __le16 icid; +- __le16 pad0; +- /* cmd word 1 */ +- __le32 pad1; +- __le32 portal_id; +- /* cmd words 2-3 */ +- u8 label[16]; +-}; +- +-struct dprc_rsp_create_container { +- /* response word 0 */ +- __le64 pad0; +- /* response word 1 */ +- __le32 child_container_id; +- __le32 pad1; +- /* response word 2 */ +- __le64 child_portal_addr; +-}; +- +-struct dprc_cmd_destroy_container { +- __le32 child_container_id; +-}; +- +-struct dprc_cmd_reset_container { +- __le32 child_container_id; +-}; +- +-struct dprc_cmd_set_irq { +- /* cmd word 0 */ +- __le32 irq_val; +- u8 irq_index; +- u8 pad[3]; +- /* cmd word 1 */ +- __le64 irq_addr; +- /* cmd word 2 */ +- __le32 irq_num; +-}; +- +-struct dprc_cmd_get_irq { +- __le32 pad; +- u8 irq_index; +-}; +- +-struct dprc_rsp_get_irq { +- /* response word 0 */ +- __le32 irq_val; +- __le32 pad; +- /* response word 1 */ +- __le64 irq_addr; +- /* response word 2 */ +- __le32 irq_num; +- __le32 type; +-}; +- +-#define DPRC_ENABLE 0x1 +- +-struct dprc_cmd_set_irq_enable { +- u8 enable; +- u8 pad[3]; +- u8 irq_index; +-}; +- +-struct dprc_cmd_get_irq_enable { +- __le32 pad; +- u8 irq_index; +-}; +- +-struct dprc_rsp_get_irq_enable { +- u8 enabled; +-}; +- +-struct dprc_cmd_set_irq_mask { +- __le32 mask; +- u8 irq_index; +-}; +- +-struct dprc_cmd_get_irq_mask { +- __le32 pad; +- u8 irq_index; +-}; +- +-struct dprc_rsp_get_irq_mask { +- __le32 mask; +-}; +- +-struct dprc_cmd_get_irq_status { +- __le32 status; +- u8 irq_index; +-}; +- +-struct dprc_rsp_get_irq_status { +- __le32 status; +-}; +- +-struct dprc_cmd_clear_irq_status { +- __le32 status; +- u8 irq_index; +-}; +- +-struct dprc_rsp_get_attributes { +- /* response word 0 */ +- __le32 container_id; +- __le16 icid; +- __le16 pad; +- /* response word 1 */ +- __le32 options; +- __le32 portal_id; +-}; +- +-struct dprc_cmd_set_res_quota { +- /* cmd word 0 */ +- __le32 child_container_id; +- __le16 quota; +- __le16 pad; +- /* cmd words 1-2 */ +- u8 type[16]; +-}; +- +-struct dprc_cmd_get_res_quota { +- /* cmd word 0 */ +- __le32 child_container_id; +- __le32 pad; +- /* cmd word 1-2 */ +- u8 type[16]; +-}; +- +-struct dprc_rsp_get_res_quota { +- __le32 pad; +- __le16 quota; +-}; +- +-struct dprc_cmd_assign { +- /* cmd word 0 */ +- __le32 container_id; +- __le32 options; +- /* cmd word 1 */ +- __le32 num; +- __le32 id_base_align; +- /* cmd word 2-3 */ +- u8 type[16]; +-}; +- +-struct dprc_cmd_unassign { +- /* cmd word 0 */ +- __le32 child_container_id; +- __le32 options; +- /* cmd word 1 */ +- __le32 num; +- __le32 id_base_align; +- /* cmd word 2-3 */ +- u8 type[16]; +-}; +- +-struct dprc_rsp_get_pool_count { +- __le32 pool_count; +-}; +- +-struct dprc_cmd_get_pool { +- __le32 pool_index; +-}; +- +-struct dprc_rsp_get_pool { +- /* response word 0 */ +- __le64 pad; +- /* response word 1-2 */ +- u8 type[16]; +-}; +- +-struct dprc_rsp_get_obj_count { +- __le32 pad; +- __le32 obj_count; +-}; +- +-struct dprc_cmd_get_obj { +- __le32 obj_index; +-}; +- +-struct dprc_rsp_get_obj { +- /* response word 0 */ +- __le32 pad0; +- __le32 id; +- /* response word 1 */ +- __le16 vendor; +- u8 irq_count; +- u8 region_count; +- __le32 state; +- /* response word 2 */ +- __le16 version_major; +- __le16 version_minor; +- __le16 flags; +- __le16 pad1; +- /* response word 3-4 */ +- u8 type[16]; +- /* response word 5-6 */ +- u8 label[16]; +-}; +- +-struct dprc_cmd_get_obj_desc { +- /* cmd word 0 */ +- __le32 obj_id; +- __le32 pad; +- /* cmd word 1-2 */ +- u8 type[16]; +-}; +- +-struct dprc_rsp_get_obj_desc { +- /* response word 0 */ +- __le32 pad0; +- __le32 id; +- /* response word 1 */ +- __le16 vendor; +- u8 irq_count; +- u8 region_count; +- __le32 state; +- /* response word 2 */ +- __le16 version_major; +- __le16 version_minor; +- __le16 flags; +- __le16 pad1; +- /* response word 3-4 */ +- u8 type[16]; +- /* response word 5-6 */ +- u8 label[16]; +-}; +- +-struct dprc_cmd_get_res_count { +- /* cmd word 0 */ +- __le64 pad; +- /* cmd word 1-2 */ +- u8 type[16]; +-}; +- +-struct dprc_rsp_get_res_count { +- __le32 res_count; +-}; +- +-struct dprc_cmd_get_res_ids { +- /* cmd word 0 */ +- u8 pad0[5]; +- u8 iter_status; +- __le16 pad1; +- /* cmd word 1 */ +- __le32 base_id; +- __le32 last_id; +- /* cmd word 2-3 */ +- u8 type[16]; +-}; +- +-struct dprc_rsp_get_res_ids { +- /* response word 0 */ +- u8 pad0[5]; +- u8 iter_status; +- __le16 pad1; +- /* response word 1 */ +- __le32 base_id; +- __le32 last_id; +-}; +- +-struct dprc_cmd_get_obj_region { +- /* cmd word 0 */ +- __le32 obj_id; +- __le16 pad0; +- u8 region_index; +- u8 pad1; +- /* cmd word 1-2 */ +- __le64 pad2[2]; +- /* cmd word 3-4 */ +- u8 obj_type[16]; +-}; +- +-struct dprc_rsp_get_obj_region { +- /* response word 0 */ +- __le64 pad; +- /* response word 1 */ +- __le64 base_addr; +- /* response word 2 */ +- __le32 size; +-}; +- +-struct dprc_cmd_set_obj_label { +- /* cmd word 0 */ +- __le32 obj_id; +- __le32 pad; +- /* cmd word 1-2 */ +- u8 label[16]; +- /* cmd word 3-4 */ +- u8 obj_type[16]; +-}; +- +-struct dprc_cmd_set_obj_irq { +- /* cmd word 0 */ +- __le32 irq_val; +- u8 irq_index; +- u8 pad[3]; +- /* cmd word 1 */ +- __le64 irq_addr; +- /* cmd word 2 */ +- __le32 irq_num; +- __le32 obj_id; +- /* cmd word 3-4 */ +- u8 obj_type[16]; +-}; +- +-struct dprc_cmd_get_obj_irq { +- /* cmd word 0 */ +- __le32 obj_id; +- u8 irq_index; +- u8 pad[3]; +- /* cmd word 1-2 */ +- u8 obj_type[16]; +-}; +- +-struct dprc_rsp_get_obj_irq { +- /* response word 0 */ +- __le32 irq_val; +- __le32 pad; +- /* response word 1 */ +- __le64 irq_addr; +- /* response word 2 */ +- __le32 irq_num; +- __le32 type; +-}; +- +-struct dprc_cmd_connect { +- /* cmd word 0 */ +- __le32 ep1_id; +- __le32 ep1_interface_id; +- /* cmd word 1 */ +- __le32 ep2_id; +- __le32 ep2_interface_id; +- /* cmd word 2-3 */ +- u8 ep1_type[16]; +- /* cmd word 4 */ +- __le32 max_rate; +- __le32 committed_rate; +- /* cmd word 5-6 */ +- u8 ep2_type[16]; +-}; +- +-struct dprc_cmd_disconnect { +- /* cmd word 0 */ +- __le32 id; +- __le32 interface_id; +- /* cmd word 1-2 */ +- u8 type[16]; +-}; +- +-struct dprc_cmd_get_connection { +- /* cmd word 0 */ +- __le32 ep1_id; +- __le32 ep1_interface_id; +- /* cmd word 1-2 */ +- u8 ep1_type[16]; +-}; +- +-struct dprc_rsp_get_connection { +- /* response word 0-2 */ +- __le64 pad[3]; +- /* response word 3 */ +- __le32 ep2_id; +- __le32 ep2_interface_id; +- /* response word 4-5 */ +- u8 ep2_type[16]; +- /* response word 6 */ +- __le32 state; +-}; +- +-#endif /* _FSL_DPRC_CMD_H */ +--- a/drivers/staging/fsl-mc/bus/dprc.h ++++ /dev/null +@@ -1,268 +0,0 @@ +-/* +- * Copyright 2013-2016 Freescale Semiconductor Inc. +- * +- * Redistribution and use in source and binary forms, with or without +- * modification, are permitted provided that the following conditions are met: +- * * Redistributions of source code must retain the above copyright +- * notice, this list of conditions and the following disclaimer. +- * * Redistributions in binary form must reproduce the above copyright +- * notice, this list of conditions and the following disclaimer in the +- * documentation and/or other materials provided with the distribution. +- * * Neither the name of the above-listed copyright holders nor the +- * names of any contributors may be used to endorse or promote products +- * derived from this software without specific prior written permission. +- * +- * +- * ALTERNATIVELY, this software may be distributed under the terms of the +- * GNU General Public License ("GPL") as published by the Free Software +- * Foundation, either version 2 of that License or (at your option) any +- * later version. +- * +- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE +- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +- * POSSIBILITY OF SUCH DAMAGE. +- */ +-#ifndef _FSL_DPRC_H +-#define _FSL_DPRC_H +- +-/* +- * Data Path Resource Container API +- * Contains DPRC API for managing and querying DPAA resources +- */ +- +-struct fsl_mc_io; +-struct fsl_mc_obj_desc; +- +-int dprc_open(struct fsl_mc_io *mc_io, +- u32 cmd_flags, +- int container_id, +- u16 *token); +- +-int dprc_close(struct fsl_mc_io *mc_io, +- u32 cmd_flags, +- u16 token); +- +-/* IRQ */ +- +-/* IRQ index */ +-#define DPRC_IRQ_INDEX 0 +- +-/* Number of dprc's IRQs */ +-#define DPRC_NUM_OF_IRQS 1 +- +-/* DPRC IRQ events */ +- +-/* IRQ event - Indicates that a new object added to the container */ +-#define DPRC_IRQ_EVENT_OBJ_ADDED 0x00000001 +-/* IRQ event - Indicates that an object was removed from the container */ +-#define DPRC_IRQ_EVENT_OBJ_REMOVED 0x00000002 +-/* IRQ event - Indicates that resources added to the container */ +-#define DPRC_IRQ_EVENT_RES_ADDED 0x00000004 +-/* IRQ event - Indicates that resources removed from the container */ +-#define DPRC_IRQ_EVENT_RES_REMOVED 0x00000008 +-/* +- * IRQ event - Indicates that one of the descendant containers that opened by +- * this container is destroyed +- */ +-#define DPRC_IRQ_EVENT_CONTAINER_DESTROYED 0x00000010 +- +-/* +- * IRQ event - Indicates that on one of the container's opened object is +- * destroyed +- */ +-#define DPRC_IRQ_EVENT_OBJ_DESTROYED 0x00000020 +- +-/* Irq event - Indicates that object is created at the container */ +-#define DPRC_IRQ_EVENT_OBJ_CREATED 0x00000040 +- +-/** +- * struct dprc_irq_cfg - IRQ configuration +- * @paddr: Address that must be written to signal a message-based interrupt +- * @val: Value to write into irq_addr address +- * @irq_num: A user defined number associated with this IRQ +- */ +-struct dprc_irq_cfg { +- phys_addr_t paddr; +- u32 val; +- int irq_num; +-}; +- +-int dprc_set_irq(struct fsl_mc_io *mc_io, +- u32 cmd_flags, +- u16 token, +- u8 irq_index, +- struct dprc_irq_cfg *irq_cfg); +- +-int dprc_get_irq(struct fsl_mc_io *mc_io, +- u32 cmd_flags, +- u16 token, +- u8 irq_index, +- int *type, +- struct dprc_irq_cfg *irq_cfg); +- +-int dprc_set_irq_enable(struct fsl_mc_io *mc_io, +- u32 cmd_flags, +- u16 token, +- u8 irq_index, +- u8 en); +- +-int dprc_get_irq_enable(struct fsl_mc_io *mc_io, +- u32 cmd_flags, +- u16 token, +- u8 irq_index, +- u8 *en); +- +-int dprc_set_irq_mask(struct fsl_mc_io *mc_io, +- u32 cmd_flags, +- u16 token, +- u8 irq_index, +- u32 mask); +- +-int dprc_get_irq_mask(struct fsl_mc_io *mc_io, +- u32 cmd_flags, +- u16 token, +- u8 irq_index, +- u32 *mask); +- +-int dprc_get_irq_status(struct fsl_mc_io *mc_io, +- u32 cmd_flags, +- u16 token, +- u8 irq_index, +- u32 *status); +- +-int dprc_clear_irq_status(struct fsl_mc_io *mc_io, +- u32 cmd_flags, +- u16 token, +- u8 irq_index, +- u32 status); +- +-/** +- * struct dprc_attributes - Container attributes +- * @container_id: Container's ID +- * @icid: Container's ICID +- * @portal_id: Container's portal ID +- * @options: Container's options as set at container's creation +- */ +-struct dprc_attributes { +- int container_id; +- u16 icid; +- int portal_id; +- u64 options; +-}; +- +-int dprc_get_attributes(struct fsl_mc_io *mc_io, +- u32 cmd_flags, +- u16 token, +- struct dprc_attributes *attributes); +- +-int dprc_get_obj_count(struct fsl_mc_io *mc_io, +- u32 cmd_flags, +- u16 token, +- int *obj_count); +- +-int dprc_get_obj(struct fsl_mc_io *mc_io, +- u32 cmd_flags, +- u16 token, +- int obj_index, +- struct fsl_mc_obj_desc *obj_desc); +- +-int dprc_get_obj_desc(struct fsl_mc_io *mc_io, +- u32 cmd_flags, +- u16 token, +- char *obj_type, +- int obj_id, +- struct fsl_mc_obj_desc *obj_desc); +- +-int dprc_set_obj_irq(struct fsl_mc_io *mc_io, +- u32 cmd_flags, +- u16 token, +- char *obj_type, +- int obj_id, +- u8 irq_index, +- struct dprc_irq_cfg *irq_cfg); +- +-int dprc_get_obj_irq(struct fsl_mc_io *mc_io, +- u32 cmd_flags, +- u16 token, +- char *obj_type, +- int obj_id, +- u8 irq_index, +- int *type, +- struct dprc_irq_cfg *irq_cfg); +- +-int dprc_get_res_count(struct fsl_mc_io *mc_io, +- u32 cmd_flags, +- u16 token, +- char *type, +- int *res_count); +- +-/** +- * enum dprc_iter_status - Iteration status +- * @DPRC_ITER_STATUS_FIRST: Perform first iteration +- * @DPRC_ITER_STATUS_MORE: Indicates more/next iteration is needed +- * @DPRC_ITER_STATUS_LAST: Indicates last iteration +- */ +-enum dprc_iter_status { +- DPRC_ITER_STATUS_FIRST = 0, +- DPRC_ITER_STATUS_MORE = 1, +- DPRC_ITER_STATUS_LAST = 2 +-}; +- +-/* Region flags */ +-/* Cacheable - Indicates that region should be mapped as cacheable */ +-#define DPRC_REGION_CACHEABLE 0x00000001 +- +-/** +- * enum dprc_region_type - Region type +- * @DPRC_REGION_TYPE_MC_PORTAL: MC portal region +- * @DPRC_REGION_TYPE_QBMAN_PORTAL: Qbman portal region +- */ +-enum dprc_region_type { +- DPRC_REGION_TYPE_MC_PORTAL, +- DPRC_REGION_TYPE_QBMAN_PORTAL +-}; +- +-/** +- * struct dprc_region_desc - Mappable region descriptor +- * @base_offset: Region offset from region's base address. +- * For DPMCP and DPRC objects, region base is offset from SoC MC portals +- * base address; For DPIO, region base is offset from SoC QMan portals +- * base address +- * @size: Region size (in bytes) +- * @flags: Region attributes +- * @type: Portal region type +- */ +-struct dprc_region_desc { +- u32 base_offset; +- u32 size; +- u32 flags; +- enum dprc_region_type type; +-}; +- +-int dprc_get_obj_region(struct fsl_mc_io *mc_io, +- u32 cmd_flags, +- u16 token, +- char *obj_type, +- int obj_id, +- u8 region_index, +- struct dprc_region_desc *region_desc); +- +-int dprc_get_api_version(struct fsl_mc_io *mc_io, +- u32 cmd_flags, +- u16 *major_ver, +- u16 *minor_ver); +- +-int dprc_get_container_id(struct fsl_mc_io *mc_io, +- u32 cmd_flags, +- int *container_id); +- +-#endif /* _FSL_DPRC_H */ +- +--- a/drivers/staging/fsl-mc/bus/irq-gic-v3-its-fsl-mc-msi.c ++++ b/drivers/staging/fsl-mc/bus/irq-gic-v3-its-fsl-mc-msi.c +@@ -13,6 +13,7 @@ + #include <linux/msi.h> + #include <linux/of.h> + #include <linux/of_irq.h> ++#include <linux/fsl/mc.h> + #include "fsl-mc-private.h" + + static struct irq_chip its_msi_irq_chip = { +--- /dev/null ++++ b/include/linux/fsl/mc.h +@@ -0,0 +1,1020 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * Freescale Management Complex (MC) bus public interface ++ * ++ * Copyright (C) 2014-2016 Freescale Semiconductor, Inc. ++ * Author: German Rivera <German.Rivera@freescale.com> ++ * ++ */ ++#ifndef _FSL_MC_H_ ++#define _FSL_MC_H_ ++ ++#include <linux/device.h> ++#include <linux/mod_devicetable.h> ++#include <linux/interrupt.h> ++#include <linux/cdev.h> ++#include <uapi/linux/fsl_mc.h> ++ ++#define FSL_MC_VENDOR_FREESCALE 0x1957 ++ ++struct irq_domain; ++struct msi_domain_info; ++ ++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_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) ++ ++#define to_fsl_mc_bus(_mc_dev) \ ++ container_of(_mc_dev, struct fsl_mc_bus, mc_dev) ++ ++/** ++ * enum fsl_mc_pool_type - Types of allocatable MC bus resources ++ * ++ * Entries in these enum are used as indices in the array of resource ++ * pools of an fsl_mc_bus object. ++ */ ++enum fsl_mc_pool_type { ++ FSL_MC_POOL_DPMCP = 0x0, /* corresponds to "dpmcp" in the MC */ ++ FSL_MC_POOL_DPBP, /* corresponds to "dpbp" in the MC */ ++ FSL_MC_POOL_DPCON, /* corresponds to "dpcon" in the MC */ ++ FSL_MC_POOL_IRQ, ++ ++ /* ++ * NOTE: New resource pool types must be added before this entry ++ */ ++ FSL_MC_NUM_POOL_TYPES ++}; ++ ++/** ++ * struct fsl_mc_resource - MC generic resource ++ * @type: type of resource ++ * @id: unique MC resource Id within the resources of the same type ++ * @data: pointer to resource-specific data if the resource is currently ++ * allocated, or NULL if the resource is not currently allocated. ++ * @parent_pool: pointer to the parent resource pool from which this ++ * resource is allocated from. ++ * @node: Node in the free list of the corresponding resource pool ++ * ++ * NOTE: This structure is to be embedded as a field of specific ++ * MC resource structures. ++ */ ++struct fsl_mc_resource { ++ enum fsl_mc_pool_type type; ++ s32 id; ++ void *data; ++ struct fsl_mc_resource_pool *parent_pool; ++ struct list_head node; ++}; ++ ++/** ++ * struct fsl_mc_device_irq - MC object device message-based interrupt ++ * @msi_desc: pointer to MSI descriptor allocated by fsl_mc_msi_alloc_descs() ++ * @mc_dev: MC object device that owns this interrupt ++ * @dev_irq_index: device-relative IRQ index ++ * @resource: MC generic resource associated with the interrupt ++ */ ++struct fsl_mc_device_irq { ++ struct msi_desc *msi_desc; ++ struct fsl_mc_device *mc_dev; ++ u8 dev_irq_index; ++ struct fsl_mc_resource resource; ++}; ++ ++#define to_fsl_mc_irq(_mc_resource) \ ++ container_of(_mc_resource, struct fsl_mc_device_irq, resource) ++ ++/* Opened state - Indicates that an object is open by at least one owner */ ++#define FSL_MC_OBJ_STATE_OPEN 0x00000001 ++/* Plugged state - Indicates that the object is plugged */ ++#define FSL_MC_OBJ_STATE_PLUGGED 0x00000002 ++ ++/** ++ * Shareability flag - Object flag indicating no memory shareability. ++ * the object generates memory accesses that are non coherent with other ++ * masters; ++ * user is responsible for proper memory handling through IOMMU configuration. ++ */ ++#define FSL_MC_OBJ_FLAG_NO_MEM_SHAREABILITY 0x0001 ++ ++/** ++ * struct fsl_mc_obj_desc - Object descriptor ++ * @type: Type of object: NULL terminated string ++ * @id: ID of logical object resource ++ * @vendor: Object vendor identifier ++ * @ver_major: Major version number ++ * @ver_minor: Minor version number ++ * @irq_count: Number of interrupts supported by the object ++ * @region_count: Number of mappable regions supported by the object ++ * @state: Object state: combination of FSL_MC_OBJ_STATE_ states ++ * @label: Object label: NULL terminated string ++ * @flags: Object's flags ++ */ ++struct fsl_mc_obj_desc { ++ char type[16]; ++ int id; ++ u16 vendor; ++ u16 ver_major; ++ u16 ver_minor; ++ u8 irq_count; ++ u8 region_count; ++ u32 state; ++ char label[16]; ++ u16 flags; ++}; ++ ++/** ++ * Bit masks for a MC object device (struct fsl_mc_device) flags ++ */ ++#define FSL_MC_IS_DPRC 0x0001 ++ ++/** ++ * 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 ++ * @irqs: pointer to array of pointers to interrupts allocated to this device ++ * @resource: generic resource associated with this MC object device, if any. ++ * @driver_override: Driver name to force a match ++ * ++ * 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. ++ * - DP_OBJ_DPRC objects are the only MC objects that have built-in MC ++ * portals. For all other MC objects, their device drivers are responsible for ++ * allocating MC portals for them by calling fsl_mc_portal_allocate(). ++ * - Some types of MC objects (e.g., DP_OBJ_DPBP, DP_OBJ_DPCON) are ++ * treated as resources that can be allocated/deallocated from the ++ * corresponding resource pool in the object's parent DPRC, using the ++ * fsl_mc_object_allocate()/fsl_mc_object_free() functions. These MC objects ++ * are known as "allocatable" objects. For them, the corresponding ++ * fsl_mc_device's 'resource' points to the associated resource object. ++ * For MC objects that are not allocatable (e.g., DP_OBJ_DPRC, DP_OBJ_DPNI), ++ * 'resource' is NULL. ++ */ ++struct fsl_mc_device { ++ struct device dev; ++ u64 dma_mask; ++ u16 flags; ++ u32 icid; ++ u16 mc_handle; ++ struct fsl_mc_io *mc_io; ++ struct fsl_mc_obj_desc obj_desc; ++ struct resource *regions; ++ struct fsl_mc_device_irq **irqs; ++ struct fsl_mc_resource *resource; ++ const char *driver_override; ++}; ++ ++#define to_fsl_mc_device(_dev) \ ++ container_of(_dev, struct fsl_mc_device, dev) ++ ++struct mc_cmd_header { ++ u8 src_id; ++ u8 flags_hw; ++ u8 status; ++ u8 flags_sw; ++ __le16 token; ++ __le16 cmd_id; ++}; ++ ++enum mc_cmd_status { ++ MC_CMD_STATUS_OK = 0x0, /* Completed successfully */ ++ MC_CMD_STATUS_READY = 0x1, /* Ready to be processed */ ++ MC_CMD_STATUS_AUTH_ERR = 0x3, /* Authentication error */ ++ MC_CMD_STATUS_NO_PRIVILEGE = 0x4, /* No privilege */ ++ MC_CMD_STATUS_DMA_ERR = 0x5, /* DMA or I/O error */ ++ MC_CMD_STATUS_CONFIG_ERR = 0x6, /* Configuration error */ ++ MC_CMD_STATUS_TIMEOUT = 0x7, /* Operation timed out */ ++ MC_CMD_STATUS_NO_RESOURCE = 0x8, /* No resources */ ++ MC_CMD_STATUS_NO_MEMORY = 0x9, /* No memory available */ ++ MC_CMD_STATUS_BUSY = 0xA, /* Device is busy */ ++ MC_CMD_STATUS_UNSUPPORTED_OP = 0xB, /* Unsupported operation */ ++ MC_CMD_STATUS_INVALID_STATE = 0xC /* Invalid state */ ++}; ++ ++/* ++ * MC command flags ++ */ ++ ++/* High priority flag */ ++#define MC_CMD_FLAG_PRI 0x80 ++/* Command completion flag */ ++#define MC_CMD_FLAG_INTR_DIS 0x01 ++ ++static inline u64 mc_encode_cmd_header(u16 cmd_id, ++ u32 cmd_flags, ++ u16 token) ++{ ++ u64 header = 0; ++ struct mc_cmd_header *hdr = (struct mc_cmd_header *)&header; ++ ++ hdr->cmd_id = cpu_to_le16(cmd_id); ++ hdr->token = cpu_to_le16(token); ++ hdr->status = MC_CMD_STATUS_READY; ++ if (cmd_flags & MC_CMD_FLAG_PRI) ++ hdr->flags_hw = MC_CMD_FLAG_PRI; ++ if (cmd_flags & MC_CMD_FLAG_INTR_DIS) ++ hdr->flags_sw = MC_CMD_FLAG_INTR_DIS; ++ ++ return header; ++} ++ ++static inline u16 mc_cmd_hdr_read_token(struct fsl_mc_command *cmd) ++{ ++ struct mc_cmd_header *hdr = (struct mc_cmd_header *)&cmd->header; ++ u16 token = le16_to_cpu(hdr->token); ++ ++ return token; ++} ++ ++struct mc_rsp_create { ++ __le32 object_id; ++}; ++ ++struct mc_rsp_api_ver { ++ __le16 major_ver; ++ __le16 minor_ver; ++}; ++ ++static inline u32 mc_cmd_read_object_id(struct fsl_mc_command *cmd) ++{ ++ struct mc_rsp_create *rsp_params; ++ ++ rsp_params = (struct mc_rsp_create *)cmd->params; ++ return le32_to_cpu(rsp_params->object_id); ++} ++ ++static inline void mc_cmd_read_api_version(struct fsl_mc_command *cmd, ++ u16 *major_ver, ++ u16 *minor_ver) ++{ ++ struct mc_rsp_api_ver *rsp_params; ++ ++ rsp_params = (struct mc_rsp_api_ver *)cmd->params; ++ *major_ver = le16_to_cpu(rsp_params->major_ver); ++ *minor_ver = le16_to_cpu(rsp_params->minor_ver); ++} ++ ++/** ++ * Bit masks for a MC I/O object (struct fsl_mc_io) flags ++ */ ++#define FSL_MC_IO_ATOMIC_CONTEXT_PORTAL 0x0001 ++ ++/** ++ * struct fsl_mc_io - MC I/O object to be passed-in to mc_send_command() ++ * @dev: device associated with this Mc I/O object ++ * @flags: flags for mc_send_command() ++ * @portal_size: MC command portal size in bytes ++ * @portal_phys_addr: MC command portal physical address ++ * @portal_virt_addr: MC command portal virtual address ++ * @dpmcp_dev: pointer to the DPMCP device associated with the MC portal. ++ * ++ * Fields are only meaningful if the FSL_MC_IO_ATOMIC_CONTEXT_PORTAL flag is not ++ * set: ++ * @mutex: Mutex to serialize mc_send_command() calls that use the same MC ++ * portal, if the fsl_mc_io object was created with the ++ * FSL_MC_IO_ATOMIC_CONTEXT_PORTAL flag off. mc_send_command() calls for this ++ * fsl_mc_io object must be made only from non-atomic context. ++ * ++ * Fields are only meaningful if the FSL_MC_IO_ATOMIC_CONTEXT_PORTAL flag is ++ * set: ++ * @spinlock: Spinlock to serialize mc_send_command() calls that use the same MC ++ * portal, if the fsl_mc_io object was created with the ++ * FSL_MC_IO_ATOMIC_CONTEXT_PORTAL flag on. mc_send_command() calls for this ++ * fsl_mc_io object can be made from atomic or non-atomic context. ++ */ ++struct fsl_mc_io { ++ struct device *dev; ++ u16 flags; ++ u32 portal_size; ++ phys_addr_t portal_phys_addr; ++ void __iomem *portal_virt_addr; ++ struct fsl_mc_device *dpmcp_dev; ++ union { ++ /* ++ * This field is only meaningful if the ++ * FSL_MC_IO_ATOMIC_CONTEXT_PORTAL flag is not set ++ */ ++ struct mutex mutex; /* serializes mc_send_command() */ ++ ++ /* ++ * This field is only meaningful if the ++ * FSL_MC_IO_ATOMIC_CONTEXT_PORTAL flag is set ++ */ ++ spinlock_t spinlock; /* serializes mc_send_command() */ ++ }; ++}; ++ ++int mc_send_command(struct fsl_mc_io *mc_io, struct fsl_mc_command *cmd); ++ ++#ifdef CONFIG_FSL_MC_BUS ++#define dev_is_fsl_mc(_dev) ((_dev)->bus == &fsl_mc_bus_type) ++#else ++/* If fsl-mc bus is not present device cannot belong to fsl-mc bus */ ++#define dev_is_fsl_mc(_dev) (0) ++#endif ++ ++/* Macro to check if a device is a container device */ ++#define fsl_mc_is_cont_dev(_dev) (to_fsl_mc_device(_dev)->flags & \ ++ FSL_MC_IS_DPRC) ++ ++/* Macro to get the container device of a MC device */ ++#define fsl_mc_cont_dev(_dev) (fsl_mc_is_cont_dev(_dev) ? \ ++ (_dev) : (_dev)->parent) ++ ++#define fsl_mc_is_dev_coherent(_dev) \ ++ (!((to_fsl_mc_device(_dev))->obj_desc.flags & \ ++ FSL_MC_OBJ_FLAG_NO_MEM_SHAREABILITY)) ++ ++/* ++ * 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) ++ ++void fsl_mc_device_remove(struct fsl_mc_device *mc_dev); ++ ++/* ++ * 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); ++ ++int __must_check fsl_mc_portal_allocate(struct fsl_mc_device *mc_dev, ++ u16 mc_io_flags, ++ struct fsl_mc_io **new_mc_io); ++ ++void fsl_mc_portal_free(struct fsl_mc_io *mc_io); ++ ++int fsl_mc_portal_reset(struct fsl_mc_io *mc_io); ++ ++int __must_check fsl_mc_object_allocate(struct fsl_mc_device *mc_dev, ++ enum fsl_mc_pool_type pool_type, ++ struct fsl_mc_device **new_mc_adev); ++ ++void fsl_mc_object_free(struct fsl_mc_device *mc_adev); ++ ++struct irq_domain *fsl_mc_msi_create_irq_domain(struct fwnode_handle *fwnode, ++ struct msi_domain_info *info, ++ struct irq_domain *parent); ++ ++int __must_check fsl_mc_allocate_irqs(struct fsl_mc_device *mc_dev); ++ ++void fsl_mc_free_irqs(struct fsl_mc_device *mc_dev); ++ ++extern struct bus_type fsl_mc_bus_type; ++ ++extern struct device_type fsl_mc_bus_dprc_type; ++extern struct device_type fsl_mc_bus_dpni_type; ++extern struct device_type fsl_mc_bus_dpio_type; ++extern struct device_type fsl_mc_bus_dpsw_type; ++extern struct device_type fsl_mc_bus_dpdmux_type; ++extern struct device_type fsl_mc_bus_dpbp_type; ++extern struct device_type fsl_mc_bus_dpcon_type; ++extern struct device_type fsl_mc_bus_dpmcp_type; ++extern struct device_type fsl_mc_bus_dpmac_type; ++extern struct device_type fsl_mc_bus_dprtc_type; ++extern struct device_type fsl_mc_bus_dpseci_type; ++extern struct device_type fsl_mc_bus_dpdcei_type; ++extern struct device_type fsl_mc_bus_dpaiop_type; ++extern struct device_type fsl_mc_bus_dpci_type; ++extern struct device_type fsl_mc_bus_dpdmai_type; ++ ++static inline bool is_fsl_mc_bus_dprc(const struct fsl_mc_device *mc_dev) ++{ ++ return mc_dev->dev.type == &fsl_mc_bus_dprc_type; ++} ++ ++static inline bool is_fsl_mc_bus_dpni(const struct fsl_mc_device *mc_dev) ++{ ++ return mc_dev->dev.type == &fsl_mc_bus_dpni_type; ++} ++ ++static inline bool is_fsl_mc_bus_dpio(const struct fsl_mc_device *mc_dev) ++{ ++ return mc_dev->dev.type == &fsl_mc_bus_dpio_type; ++} ++ ++static inline bool is_fsl_mc_bus_dpsw(const struct fsl_mc_device *mc_dev) ++{ ++ return mc_dev->dev.type == &fsl_mc_bus_dpsw_type; ++} ++ ++static inline bool is_fsl_mc_bus_dpdmux(const struct fsl_mc_device *mc_dev) ++{ ++ return mc_dev->dev.type == &fsl_mc_bus_dpdmux_type; ++} ++ ++static inline bool is_fsl_mc_bus_dpbp(const struct fsl_mc_device *mc_dev) ++{ ++ return mc_dev->dev.type == &fsl_mc_bus_dpbp_type; ++} ++ ++static inline bool is_fsl_mc_bus_dpcon(const struct fsl_mc_device *mc_dev) ++{ ++ return mc_dev->dev.type == &fsl_mc_bus_dpcon_type; ++} ++ ++static inline bool is_fsl_mc_bus_dpmcp(const struct fsl_mc_device *mc_dev) ++{ ++ return mc_dev->dev.type == &fsl_mc_bus_dpmcp_type; ++} ++ ++static inline bool is_fsl_mc_bus_dpmac(const struct fsl_mc_device *mc_dev) ++{ ++ return mc_dev->dev.type == &fsl_mc_bus_dpmac_type; ++} ++ ++static inline bool is_fsl_mc_bus_dprtc(const struct fsl_mc_device *mc_dev) ++{ ++ return mc_dev->dev.type == &fsl_mc_bus_dprtc_type; ++} ++ ++static inline bool is_fsl_mc_bus_dpseci(const struct fsl_mc_device *mc_dev) ++{ ++ return mc_dev->dev.type == &fsl_mc_bus_dpseci_type; ++} ++ ++static inline bool is_fsl_mc_bus_dpdcei(const struct fsl_mc_device *mc_dev) ++{ ++ return mc_dev->dev.type == &fsl_mc_bus_dpdcei_type; ++} ++ ++static inline bool is_fsl_mc_bus_dpaiop(const struct fsl_mc_device *mc_dev) ++{ ++ return mc_dev->dev.type == &fsl_mc_bus_dpaiop_type; ++} ++ ++static inline bool is_fsl_mc_bus_dpci(const struct fsl_mc_device *mc_dev) ++{ ++ return mc_dev->dev.type == &fsl_mc_bus_dpci_type; ++} ++ ++static inline bool is_fsl_mc_bus_dpdmai(const struct fsl_mc_device *mc_dev) ++{ ++ return mc_dev->dev.type == &fsl_mc_bus_dpdmai_type; ++} ++ ++/* ++ * Data Path Resource Container (DPRC) API ++ */ ++ ++/* Minimal supported DPRC Version */ ++#define DPRC_MIN_VER_MAJOR 6 ++#define DPRC_MIN_VER_MINOR 0 ++ ++/* DPRC command versioning */ ++#define DPRC_CMD_BASE_VERSION 1 ++#define DPRC_CMD_ID_OFFSET 4 ++ ++#define DPRC_CMD(id) (((id) << DPRC_CMD_ID_OFFSET) | DPRC_CMD_BASE_VERSION) ++ ++/* DPRC command IDs */ ++#define DPRC_CMDID_CLOSE DPRC_CMD(0x800) ++#define DPRC_CMDID_OPEN DPRC_CMD(0x805) ++#define DPRC_CMDID_GET_API_VERSION DPRC_CMD(0xa05) ++ ++#define DPRC_CMDID_GET_ATTR DPRC_CMD(0x004) ++#define DPRC_CMDID_RESET_CONT DPRC_CMD(0x005) ++ ++#define DPRC_CMDID_SET_IRQ DPRC_CMD(0x010) ++#define DPRC_CMDID_SET_IRQ_ENABLE DPRC_CMD(0x012) ++#define DPRC_CMDID_SET_IRQ_MASK DPRC_CMD(0x014) ++#define DPRC_CMDID_GET_IRQ_STATUS DPRC_CMD(0x016) ++#define DPRC_CMDID_CLEAR_IRQ_STATUS DPRC_CMD(0x017) ++ ++#define DPRC_CMDID_GET_CONT_ID DPRC_CMD(0x830) ++#define DPRC_CMDID_GET_OBJ_COUNT DPRC_CMD(0x159) ++#define DPRC_CMDID_GET_OBJ DPRC_CMD(0x15A) ++#define DPRC_CMDID_GET_OBJ_REG DPRC_CMD(0x15E) ++#define DPRC_CMDID_SET_OBJ_IRQ DPRC_CMD(0x15F) ++ ++struct dprc_cmd_open { ++ __le32 container_id; ++}; ++ ++struct dprc_cmd_reset_container { ++ __le32 child_container_id; ++}; ++ ++struct dprc_cmd_set_irq { ++ /* cmd word 0 */ ++ __le32 irq_val; ++ u8 irq_index; ++ u8 pad[3]; ++ /* cmd word 1 */ ++ __le64 irq_addr; ++ /* cmd word 2 */ ++ __le32 irq_num; ++}; ++ ++#define DPRC_ENABLE 0x1 ++ ++struct dprc_cmd_set_irq_enable { ++ u8 enable; ++ u8 pad[3]; ++ u8 irq_index; ++}; ++ ++struct dprc_cmd_set_irq_mask { ++ __le32 mask; ++ u8 irq_index; ++}; ++ ++struct dprc_cmd_get_irq_status { ++ __le32 status; ++ u8 irq_index; ++}; ++ ++struct dprc_rsp_get_irq_status { ++ __le32 status; ++}; ++ ++struct dprc_cmd_clear_irq_status { ++ __le32 status; ++ u8 irq_index; ++}; ++ ++struct dprc_rsp_get_attributes { ++ /* response word 0 */ ++ __le32 container_id; ++ __le32 icid; ++ /* response word 1 */ ++ __le32 options; ++ __le32 portal_id; ++}; ++ ++struct dprc_rsp_get_obj_count { ++ __le32 pad; ++ __le32 obj_count; ++}; ++ ++struct dprc_cmd_get_obj { ++ __le32 obj_index; ++}; ++ ++struct dprc_rsp_get_obj { ++ /* response word 0 */ ++ __le32 pad0; ++ __le32 id; ++ /* response word 1 */ ++ __le16 vendor; ++ u8 irq_count; ++ u8 region_count; ++ __le32 state; ++ /* response word 2 */ ++ __le16 version_major; ++ __le16 version_minor; ++ __le16 flags; ++ __le16 pad1; ++ /* response word 3-4 */ ++ u8 type[16]; ++ /* response word 5-6 */ ++ u8 label[16]; ++}; ++ ++struct dprc_cmd_get_obj_region { ++ /* cmd word 0 */ ++ __le32 obj_id; ++ __le16 pad0; ++ u8 region_index; ++ u8 pad1; ++ /* cmd word 1-2 */ ++ __le64 pad2[2]; ++ /* cmd word 3-4 */ ++ u8 obj_type[16]; ++}; ++ ++struct dprc_rsp_get_obj_region { ++ /* response word 0 */ ++ __le64 pad0; ++ /* response word 1 */ ++ __le32 base_addr; ++ __le32 pad1; ++ /* response word 2 */ ++ __le32 size; ++ u8 type; ++ u8 pad2[3]; ++ /* response word 3 */ ++ __le32 flags; ++}; ++ ++struct dprc_cmd_set_obj_irq { ++ /* cmd word 0 */ ++ __le32 irq_val; ++ u8 irq_index; ++ u8 pad[3]; ++ /* cmd word 1 */ ++ __le64 irq_addr; ++ /* cmd word 2 */ ++ __le32 irq_num; ++ __le32 obj_id; ++ /* cmd word 3-4 */ ++ u8 obj_type[16]; ++}; ++ ++/* ++ * DPRC API for managing and querying DPAA resources ++ */ ++int dprc_open(struct fsl_mc_io *mc_io, ++ u32 cmd_flags, ++ int container_id, ++ u16 *token); ++ ++int dprc_close(struct fsl_mc_io *mc_io, ++ u32 cmd_flags, ++ u16 token); ++ ++/* DPRC IRQ events */ ++ ++/* IRQ event - Indicates that a new object added to the container */ ++#define DPRC_IRQ_EVENT_OBJ_ADDED 0x00000001 ++/* IRQ event - Indicates that an object was removed from the container */ ++#define DPRC_IRQ_EVENT_OBJ_REMOVED 0x00000002 ++/* ++ * IRQ event - Indicates that one of the descendant containers that opened by ++ * this container is destroyed ++ */ ++#define DPRC_IRQ_EVENT_CONTAINER_DESTROYED 0x00000010 ++ ++/* ++ * IRQ event - Indicates that on one of the container's opened object is ++ * destroyed ++ */ ++#define DPRC_IRQ_EVENT_OBJ_DESTROYED 0x00000020 ++ ++/* Irq event - Indicates that object is created at the container */ ++#define DPRC_IRQ_EVENT_OBJ_CREATED 0x00000040 ++ ++/** ++ * struct dprc_irq_cfg - IRQ configuration ++ * @paddr: Address that must be written to signal a message-based interrupt ++ * @val: Value to write into irq_addr address ++ * @irq_num: A user defined number associated with this IRQ ++ */ ++struct dprc_irq_cfg { ++ phys_addr_t paddr; ++ u32 val; ++ int irq_num; ++}; ++ ++int dprc_set_irq(struct fsl_mc_io *mc_io, ++ u32 cmd_flags, ++ u16 token, ++ u8 irq_index, ++ struct dprc_irq_cfg *irq_cfg); ++ ++int dprc_set_irq_enable(struct fsl_mc_io *mc_io, ++ u32 cmd_flags, ++ u16 token, ++ u8 irq_index, ++ u8 en); ++ ++int dprc_set_irq_mask(struct fsl_mc_io *mc_io, ++ u32 cmd_flags, ++ u16 token, ++ u8 irq_index, ++ u32 mask); ++ ++int dprc_get_irq_status(struct fsl_mc_io *mc_io, ++ u32 cmd_flags, ++ u16 token, ++ u8 irq_index, ++ u32 *status); ++ ++int dprc_clear_irq_status(struct fsl_mc_io *mc_io, ++ u32 cmd_flags, ++ u16 token, ++ u8 irq_index, ++ u32 status); ++ ++/** ++ * struct dprc_attributes - Container attributes ++ * @container_id: Container's ID ++ * @icid: Container's ICID ++ * @portal_id: Container's portal ID ++ * @options: Container's options as set at container's creation ++ */ ++struct dprc_attributes { ++ int container_id; ++ u32 icid; ++ int portal_id; ++ u64 options; ++}; ++ ++int dprc_get_attributes(struct fsl_mc_io *mc_io, ++ u32 cmd_flags, ++ u16 token, ++ struct dprc_attributes *attributes); ++ ++int dprc_get_obj_count(struct fsl_mc_io *mc_io, ++ u32 cmd_flags, ++ u16 token, ++ int *obj_count); ++ ++int dprc_get_obj(struct fsl_mc_io *mc_io, ++ u32 cmd_flags, ++ u16 token, ++ int obj_index, ++ struct fsl_mc_obj_desc *obj_desc); ++ ++int dprc_set_obj_irq(struct fsl_mc_io *mc_io, ++ u32 cmd_flags, ++ u16 token, ++ char *obj_type, ++ int obj_id, ++ u8 irq_index, ++ struct dprc_irq_cfg *irq_cfg); ++ ++/* Region flags */ ++/* Cacheable - Indicates that region should be mapped as cacheable */ ++#define DPRC_REGION_CACHEABLE 0x00000001 ++ ++/** ++ * enum dprc_region_type - Region type ++ * @DPRC_REGION_TYPE_MC_PORTAL: MC portal region ++ * @DPRC_REGION_TYPE_QBMAN_PORTAL: Qbman portal region ++ */ ++enum dprc_region_type { ++ DPRC_REGION_TYPE_MC_PORTAL, ++ DPRC_REGION_TYPE_QBMAN_PORTAL ++}; ++ ++/** ++ * struct dprc_region_desc - Mappable region descriptor ++ * @base_offset: Region offset from region's base address. ++ * For DPMCP and DPRC objects, region base is offset from SoC MC portals ++ * base address; For DPIO, region base is offset from SoC QMan portals ++ * base address ++ * @size: Region size (in bytes) ++ * @flags: Region attributes ++ * @type: Portal region type ++ */ ++struct dprc_region_desc { ++ u32 base_offset; ++ u32 size; ++ u32 flags; ++ enum dprc_region_type type; ++}; ++ ++int dprc_get_obj_region(struct fsl_mc_io *mc_io, ++ u32 cmd_flags, ++ u16 token, ++ char *obj_type, ++ int obj_id, ++ u8 region_index, ++ struct dprc_region_desc *region_desc); ++ ++int dprc_get_api_version(struct fsl_mc_io *mc_io, ++ u32 cmd_flags, ++ u16 *major_ver, ++ u16 *minor_ver); ++ ++int dprc_get_container_id(struct fsl_mc_io *mc_io, ++ u32 cmd_flags, ++ int *container_id); ++ ++int dprc_reset_container(struct fsl_mc_io *mc_io, ++ u32 cmd_flags, ++ u16 token, ++ int child_container_id); ++ ++/* ++ * Data Path Buffer Pool (DPBP) API ++ * Contains initialization APIs and runtime control APIs for DPBP ++ */ ++ ++int dpbp_open(struct fsl_mc_io *mc_io, ++ u32 cmd_flags, ++ int dpbp_id, ++ u16 *token); ++ ++int dpbp_close(struct fsl_mc_io *mc_io, ++ u32 cmd_flags, ++ u16 token); ++ ++int dpbp_enable(struct fsl_mc_io *mc_io, ++ u32 cmd_flags, ++ u16 token); ++ ++int dpbp_disable(struct fsl_mc_io *mc_io, ++ u32 cmd_flags, ++ u16 token); ++ ++int dpbp_reset(struct fsl_mc_io *mc_io, ++ u32 cmd_flags, ++ u16 token); ++ ++/** ++ * struct dpbp_attr - Structure representing DPBP attributes ++ * @id: DPBP object ID ++ * @bpid: Hardware buffer pool ID; should be used as an argument in ++ * acquire/release operations on buffers ++ */ ++struct dpbp_attr { ++ int id; ++ u16 bpid; ++}; ++ ++int dpbp_get_attributes(struct fsl_mc_io *mc_io, ++ u32 cmd_flags, ++ u16 token, ++ struct dpbp_attr *attr); ++ ++/* Data Path Concentrator (DPCON) API ++ * Contains initialization APIs and runtime control APIs for DPCON ++ */ ++ ++/** ++ * Use it to disable notifications; see dpcon_set_notification() ++ */ ++#define DPCON_INVALID_DPIO_ID (int)(-1) ++ ++int dpcon_open(struct fsl_mc_io *mc_io, ++ u32 cmd_flags, ++ int dpcon_id, ++ u16 *token); ++ ++int dpcon_close(struct fsl_mc_io *mc_io, ++ u32 cmd_flags, ++ u16 token); ++ ++int dpcon_enable(struct fsl_mc_io *mc_io, ++ u32 cmd_flags, ++ u16 token); ++ ++int dpcon_disable(struct fsl_mc_io *mc_io, ++ u32 cmd_flags, ++ u16 token); ++ ++int dpcon_reset(struct fsl_mc_io *mc_io, ++ u32 cmd_flags, ++ u16 token); ++ ++/** ++ * struct dpcon_attr - Structure representing DPCON attributes ++ * @id: DPCON object ID ++ * @qbman_ch_id: Channel ID to be used by dequeue operation ++ * @num_priorities: Number of priorities for the DPCON channel (1-8) ++ */ ++struct dpcon_attr { ++ int id; ++ u16 qbman_ch_id; ++ u8 num_priorities; ++}; ++ ++int dpcon_get_attributes(struct fsl_mc_io *mc_io, ++ u32 cmd_flags, ++ u16 token, ++ struct dpcon_attr *attr); ++ ++/** ++ * struct dpcon_notification_cfg - Structure representing notification params ++ * @dpio_id: DPIO object ID; must be configured with a notification channel; ++ * to disable notifications set it to 'DPCON_INVALID_DPIO_ID'; ++ * @priority: Priority selection within the DPIO channel; valid values ++ * are 0-7, depending on the number of priorities in that channel ++ * @user_ctx: User context value provided with each CDAN message ++ */ ++struct dpcon_notification_cfg { ++ int dpio_id; ++ u8 priority; ++ u64 user_ctx; ++}; ++ ++int dpcon_set_notification(struct fsl_mc_io *mc_io, ++ u32 cmd_flags, ++ u16 token, ++ struct dpcon_notification_cfg *cfg); ++ ++struct irq_domain; ++struct msi_domain_info; ++ ++/** ++ * Maximum number of total IRQs that can be pre-allocated for an MC bus' ++ * IRQ pool ++ */ ++#define FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS 256 ++ ++/** ++ * struct fsl_mc_resource_pool - Pool of MC resources of a given ++ * type ++ * @type: type of resources in the pool ++ * @max_count: maximum number of resources in the pool ++ * @free_count: number of free resources in the pool ++ * @mutex: mutex to serialize access to the pool's free list ++ * @free_list: anchor node of list of free resources in the pool ++ * @mc_bus: pointer to the MC bus that owns this resource pool ++ */ ++struct fsl_mc_resource_pool { ++ enum fsl_mc_pool_type type; ++ int max_count; ++ int free_count; ++ struct mutex mutex; /* serializes access to free_list */ ++ struct list_head free_list; ++ struct fsl_mc_bus *mc_bus; ++}; ++ ++/** ++ * struct fsl_mc_restool - information associated with a restool device file ++ * @cdev: struct char device linked to the root dprc ++ * @dev: dev_t for the char device to be added ++ * @device: newly created device in /dev ++ * @mutex: mutex lock to serialize the open/release operations ++ * @local_instance_in_use: local MC I/O instance in use or not ++ * @dynamic_instance_count: number of dynamically created MC I/O instances ++ */ ++struct fsl_mc_restool { ++ struct cdev cdev; ++ dev_t dev; ++ struct device *device; ++ struct mutex mutex; /* serialize open/release operations */ ++ bool local_instance_in_use; ++ u32 dynamic_instance_count; ++}; ++ ++/** ++ * struct fsl_mc_bus - logical bus that corresponds to a physical DPRC ++ * @mc_dev: fsl-mc device for the bus device itself. ++ * @resource_pools: array of resource pools (one pool per resource type) ++ * for this MC bus. These resources represent allocatable entities ++ * from the physical DPRC. ++ * @irq_resources: Pointer to array of IRQ objects for the IRQ pool ++ * @scan_mutex: Serializes bus scanning ++ * @dprc_attr: DPRC attributes ++ * @restool_misc: struct that abstracts the interaction with userspace restool ++ */ ++struct fsl_mc_bus { ++ struct fsl_mc_device mc_dev; ++ struct fsl_mc_resource_pool resource_pools[FSL_MC_NUM_POOL_TYPES]; ++ struct fsl_mc_device_irq *irq_resources; ++ struct mutex scan_mutex; /* serializes bus scanning */ ++ struct dprc_attributes dprc_attr; ++ struct fsl_mc_restool restool_misc; ++}; ++ ++int dprc_scan_objects(struct fsl_mc_device *mc_bus_dev, ++ const char *driver_override, ++ unsigned int *total_irq_count); ++ ++int fsl_mc_find_msi_domain(struct device *mc_platform_dev, ++ struct irq_domain **mc_msi_domain); ++ ++int fsl_mc_populate_irq_pool(struct fsl_mc_bus *mc_bus, ++ unsigned int irq_count); ++ ++void fsl_mc_cleanup_irq_pool(struct fsl_mc_bus *mc_bus); ++ ++void fsl_mc_init_all_resource_pools(struct fsl_mc_device *mc_bus_dev); ++ ++void fsl_mc_cleanup_all_resource_pools(struct fsl_mc_device *mc_bus_dev); ++ ++void fsl_mc_get_root_dprc(struct device *dev, struct device **root_dprc_dev); ++ ++#endif /* _FSL_MC_H_ */ +--- /dev/null ++++ b/include/uapi/linux/fsl_mc.h +@@ -0,0 +1,31 @@ ++/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ ++/* ++ * Management Complex (MC) userspace public interface ++ * ++ * Copyright 2018 NXP ++ * ++ */ ++#ifndef _UAPI_FSL_MC_H_ ++#define _UAPI_FSL_MC_H_ ++ ++#define MC_CMD_NUM_OF_PARAMS 7 ++ ++/** ++ * struct fsl_mc_command - Management Complex (MC) command structure ++ * @header: MC command header ++ * @params: MC command parameters ++ * ++ * Used by RESTOOL_SEND_MC_COMMAND ++ */ ++struct fsl_mc_command { ++ __u64 header; ++ __u64 params[MC_CMD_NUM_OF_PARAMS]; ++}; ++ ++#define RESTOOL_IOCTL_TYPE 'R' ++#define RESTOOL_IOCTL_SEQ 0xE0 ++ ++#define RESTOOL_SEND_MC_COMMAND \ ++ _IOWR(RESTOOL_IOCTL_TYPE, RESTOOL_IOCTL_SEQ, struct fsl_mc_command) ++ ++#endif /* _UAPI_FSL_MC_H_ */ |