diff options
Diffstat (limited to 'drivers/staging/fsl-mc/bus/mc-sys.c')
-rw-r--r-- | drivers/staging/fsl-mc/bus/mc-sys.c | 208 |
1 files changed, 184 insertions, 24 deletions
diff --git a/drivers/staging/fsl-mc/bus/mc-sys.c b/drivers/staging/fsl-mc/bus/mc-sys.c index 5737f599f..6e1489246 100644 --- a/drivers/staging/fsl-mc/bus/mc-sys.c +++ b/drivers/staging/fsl-mc/bus/mc-sys.c @@ -34,15 +34,17 @@ #include "../include/mc-sys.h" #include "../include/mc-cmd.h" +#include "../include/mc.h" #include <linux/delay.h> #include <linux/slab.h> #include <linux/ioport.h> #include <linux/device.h> +#include "dpmcp.h" /** - * Timeout in jiffies to wait for the completion of an MC command + * Timeout in milliseconds to wait for the completion of an MC command */ -#define MC_CMD_COMPLETION_TIMEOUT_JIFFIES (HZ / 2) /* 500 ms */ +#define MC_CMD_COMPLETION_TIMEOUT_MS 500 /* * usleep_range() min and max values used to throttle down polling @@ -52,7 +54,7 @@ #define MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS 500 #define MC_CMD_HDR_READ_CMDID(_hdr) \ - ((uint16_t)mc_dec((_hdr), MC_CMD_HDR_CMDID_O, MC_CMD_HDR_CMDID_S)) + ((u16)mc_dec((_hdr), MC_CMD_HDR_CMDID_O, MC_CMD_HDR_CMDID_S)) /** * Creates an MC I/O object @@ -60,8 +62,8 @@ * @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 - * @resource: Pointer to MC bus object allocator resource associated - * with this MC I/O object or NULL if none. + * @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 * @@ -69,10 +71,11 @@ */ int __must_check fsl_create_mc_io(struct device *dev, phys_addr_t mc_portal_phys_addr, - uint32_t mc_portal_size, - struct fsl_mc_resource *resource, - uint32_t flags, struct fsl_mc_io **new_mc_io) + 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; @@ -85,7 +88,11 @@ int __must_check fsl_create_mc_io(struct device *dev, mc_io->flags = flags; mc_io->portal_phys_addr = mc_portal_phys_addr; mc_io->portal_size = mc_portal_size; - mc_io->resource = resource; + 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, @@ -108,8 +115,18 @@ int __must_check fsl_create_mc_io(struct device *dev, } 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; } EXPORT_SYMBOL_GPL(fsl_create_mc_io); @@ -120,6 +137,11 @@ EXPORT_SYMBOL_GPL(fsl_create_mc_io); */ 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, @@ -130,6 +152,57 @@ void fsl_destroy_mc_io(struct fsl_mc_io *mc_io) } EXPORT_SYMBOL_GPL(fsl_destroy_mc_io); +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; +} +EXPORT_SYMBOL_GPL(fsl_mc_io_set_dpmcp); + +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; +} +EXPORT_SYMBOL_GPL(fsl_mc_io_unset_dpmcp); + static int mc_status_to_error(enum mc_cmd_status status) { static const int mc_status_to_error_map[] = { @@ -224,25 +297,20 @@ static inline enum mc_cmd_status mc_read_response(struct mc_command __iomem * } /** - * Sends an command to the MC device using the given MC I/O object + * 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 to be sent - * - * Returns '0' on Success; Error code otherwise. - * - * NOTE: This function cannot be invoked from from atomic contexts. + * @cmd: command buffer to receive MC response + * @mc_status: MC command completion status */ -int mc_send_command(struct fsl_mc_io *mc_io, struct mc_command *cmd) +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 + MC_CMD_COMPLETION_TIMEOUT_JIFFIES; - - /* - * Send command to the MC hardware: - */ - mc_write_command(mc_io->portal_virt_addr, cmd); + jiffies + msecs_to_jiffies(MC_CMD_COMPLETION_TIMEOUT_MS); /* * Wait for response from the MC hardware: @@ -271,6 +339,90 @@ int mc_send_command(struct fsl_mc_io *mc_io, struct mc_command *cmd) } } + *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) { + pr_debug("MC command timed out (portal: %#llx, obj handle: %#x, command: %#x)\n", + mc_io->portal_phys_addr, + (unsigned int) + MC_CMD_HDR_READ_TOKEN(cmd->header), + (unsigned int) + MC_CMD_HDR_READ_CMDID(cmd->header)); + + 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) { pr_debug("MC command failed: portal: %#llx, obj handle: %#x, command: %#x, status: %s (%#x)\n", mc_io->portal_phys_addr, @@ -279,9 +431,17 @@ int mc_send_command(struct fsl_mc_io *mc_io, struct mc_command *cmd) mc_status_to_string(status), (unsigned int)status); - return mc_status_to_error(status); + error = mc_status_to_error(status); + goto common_exit; } - return 0; + 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); |