diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2020-10-15 20:01:51 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2020-10-15 20:01:51 +0300 |
commit | 726eb70e0d34dc4bc4dada71f52bba8ed638431e (patch) | |
tree | e49674616f4513c8c6a4746a08e93c9441708d34 /drivers/bus | |
parent | c6dbef7307629cce855aa6b482b60cbf7777ed88 (diff) | |
parent | f3277cbfba763cd2826396521b9296de67cf1bbc (diff) | |
download | linux-726eb70e0d34dc4bc4dada71f52bba8ed638431e.tar.xz |
Merge tag 'char-misc-5.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc
Pull char/misc driver updates from Greg KH:
"Here is the big set of char, misc, and other assorted driver subsystem
patches for 5.10-rc1.
There's a lot of different things in here, all over the drivers/
directory. Some summaries:
- soundwire driver updates
- habanalabs driver updates
- extcon driver updates
- nitro_enclaves new driver
- fsl-mc driver and core updates
- mhi core and bus updates
- nvmem driver updates
- eeprom driver updates
- binder driver updates and fixes
- vbox minor bugfixes
- fsi driver updates
- w1 driver updates
- coresight driver updates
- interconnect driver updates
- misc driver updates
- other minor driver updates
All of these have been in linux-next for a while with no reported
issues"
* tag 'char-misc-5.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (396 commits)
binder: fix UAF when releasing todo list
docs: w1: w1_therm: Fix broken xref, mistakes, clarify text
misc: Kconfig: fix a HISI_HIKEY_USB dependency
LSM: Fix type of id parameter in kernel_post_load_data prototype
misc: Kconfig: add a new dependency for HISI_HIKEY_USB
firmware_loader: fix a kernel-doc markup
w1: w1_therm: make w1_poll_completion static
binder: simplify the return expression of binder_mmap
test_firmware: Test partial read support
firmware: Add request_partial_firmware_into_buf()
firmware: Store opt_flags in fw_priv
fs/kernel_file_read: Add "offset" arg for partial reads
IMA: Add support for file reads without contents
LSM: Add "contents" flag to kernel_read_file hook
module: Call security_kernel_post_load_data()
firmware_loader: Use security_post_load_data()
LSM: Introduce kernel_post_load_data() hook
fs/kernel_read_file: Add file_size output argument
fs/kernel_read_file: Switch buffer size arg to size_t
fs/kernel_read_file: Remove redundant size argument
...
Diffstat (limited to 'drivers/bus')
-rw-r--r-- | drivers/bus/fsl-mc/dprc-driver.c | 190 | ||||
-rw-r--r-- | drivers/bus/fsl-mc/dprc.c | 141 | ||||
-rw-r--r-- | drivers/bus/fsl-mc/fsl-mc-allocator.c | 12 | ||||
-rw-r--r-- | drivers/bus/fsl-mc/fsl-mc-bus.c | 75 | ||||
-rw-r--r-- | drivers/bus/fsl-mc/fsl-mc-private.h | 31 | ||||
-rw-r--r-- | drivers/bus/fsl-mc/mc-io.c | 7 | ||||
-rw-r--r-- | drivers/bus/mhi/Kconfig | 20 | ||||
-rw-r--r-- | drivers/bus/mhi/core/Makefile | 3 | ||||
-rw-r--r-- | drivers/bus/mhi/core/boot.c | 17 | ||||
-rw-r--r-- | drivers/bus/mhi/core/debugfs.c | 411 | ||||
-rw-r--r-- | drivers/bus/mhi/core/init.c | 87 | ||||
-rw-r--r-- | drivers/bus/mhi/core/internal.h | 37 | ||||
-rw-r--r-- | drivers/bus/mhi/core/main.c | 27 | ||||
-rw-r--r-- | drivers/bus/mhi/core/pm.c | 28 |
14 files changed, 904 insertions, 182 deletions
diff --git a/drivers/bus/fsl-mc/dprc-driver.c b/drivers/bus/fsl-mc/dprc-driver.c index 2a473c09bc33..91dc015963a8 100644 --- a/drivers/bus/fsl-mc/dprc-driver.c +++ b/drivers/bus/fsl-mc/dprc-driver.c @@ -3,6 +3,7 @@ * Freescale data path resource container (DPRC) driver * * Copyright (C) 2014-2016 Freescale Semiconductor, Inc. + * Copyright 2019-2020 NXP * Author: German Rivera <German.Rivera@freescale.com> * */ @@ -80,9 +81,9 @@ static int __fsl_mc_device_remove(struct device *dev, void *data) * 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) +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) { /* @@ -104,6 +105,7 @@ static void dprc_remove_devices(struct fsl_mc_device *mc_bus_dev, __fsl_mc_device_remove); } } +EXPORT_SYMBOL_GPL(dprc_remove_devices); static int __fsl_mc_device_match(struct device *dev, void *data) { @@ -220,8 +222,8 @@ static void dprc_add_new_devices(struct fsl_mc_device *mc_bus_dev, * 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: If argument is provided the function populates the - * total number of IRQs created by objects in the DPRC. + * @alloc_interrupts: if true the function allocates the interrupt pool, + * otherwise the interrupt allocation is delayed * * Detects objects added and removed from a DPRC and synchronizes the * state of the Linux bus driver, MC by adding and removing @@ -236,7 +238,7 @@ static void dprc_add_new_devices(struct fsl_mc_device *mc_bus_dev, * 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) + bool alloc_interrupts) { int num_child_objects; int dprc_get_obj_failures; @@ -317,22 +319,21 @@ static int dprc_scan_objects(struct fsl_mc_device *mc_bus_dev, * 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 (dev_get_msi_domain(&mc_bus_dev->dev)) { 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 (alloc_interrupts && !mc_bus->irq_resources) { + error = fsl_mc_populate_irq_pool(mc_bus_dev, + 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); @@ -354,9 +355,10 @@ static int dprc_scan_objects(struct fsl_mc_device *mc_bus_dev, * 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 dprc_scan_container(struct fsl_mc_device *mc_bus_dev, + bool alloc_interrupts) { - int error; + int error = 0; struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev); fsl_mc_init_all_resource_pools(mc_bus_dev); @@ -365,16 +367,12 @@ static int dprc_scan_container(struct fsl_mc_device *mc_bus_dev) * Discover objects in the DPRC: */ mutex_lock(&mc_bus->scan_mutex); - error = dprc_scan_objects(mc_bus_dev, NULL); + error = dprc_scan_objects(mc_bus_dev, alloc_interrupts); mutex_unlock(&mc_bus->scan_mutex); - if (error < 0) { - fsl_mc_cleanup_all_resource_pools(mc_bus_dev); - return error; - } - return 0; + return error; } - +EXPORT_SYMBOL_GPL(dprc_scan_container); /** * dprc_irq0_handler - Regular ISR for DPRC interrupt 0 * @@ -434,9 +432,8 @@ static irqreturn_t dprc_irq0_handler_thread(int irq_num, void *arg) 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); + error = dprc_scan_objects(mc_dev, true); if (error < 0) { /* * If the error is -ENXIO, we ignore it, as it indicates @@ -451,12 +448,6 @@ static irqreturn_t dprc_irq0_handler_thread(int irq_num, void *arg) 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: @@ -597,25 +588,24 @@ error_free_irqs: } /** - * dprc_probe - callback invoked when a DPRC is being bound to this driver + * dprc_setup - opens and creates a mc_io for DPRC * * @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. + * It configures the DPRC portal used to communicate with MC */ -static int dprc_probe(struct fsl_mc_device *mc_dev) + +int dprc_setup(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); + struct irq_domain *mc_msi_domain; bool mc_io_created = false; bool msi_domain_set = false; u16 major_ver, minor_ver; - struct irq_domain *mc_msi_domain; + size_t region_size; + int error; if (!is_fsl_mc_bus_dprc(mc_dev)) return -EINVAL; @@ -690,37 +680,63 @@ static int dprc_probe(struct fsl_mc_device *mc_dev) goto error_cleanup_open; } - mutex_init(&mc_bus->scan_mutex); + 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; +} +EXPORT_SYMBOL_GPL(dprc_setup); + +/** + * 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; + + error = dprc_setup(mc_dev); + if (error < 0) + return error; /* * Discover MC objects in DPRC object: */ - error = dprc_scan_container(mc_dev); + error = dprc_scan_container(mc_dev, true); if (error < 0) - goto error_cleanup_open; + goto dprc_cleanup; /* * Configure interrupt for the DPRC object associated with this MC bus: */ error = dprc_setup_irq(mc_dev); if (error < 0) - goto error_cleanup_open; + goto scan_cleanup; 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; - } - +scan_cleanup: + device_for_each_child(&mc_dev->dev, NULL, __fsl_mc_device_remove); +dprc_cleanup: + dprc_cleanup(mc_dev); return error; } @@ -739,40 +755,39 @@ static void dprc_teardown_irq(struct fsl_mc_device *mc_dev) } /** - * dprc_remove - callback invoked when a DPRC is being unbound from this driver + * dprc_cleanup - function that cleanups a DPRC * * @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 closes the DPRC device in the MC. * It destroys the interrupt pool associated with this MC bus. */ -static int dprc_remove(struct fsl_mc_device *mc_dev) + +int dprc_cleanup(struct fsl_mc_device *mc_dev) { int error; - struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev); + /* this function should be called only for DPRCs, it + * is an error to call it for regular objects + */ 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); + fsl_mc_cleanup_irq_pool(mc_dev); dev_set_msi_domain(&mc_dev->dev, NULL); } fsl_mc_cleanup_all_resource_pools(mc_dev); + /* if this step fails we cannot go further with cleanup as there is no way of + * communicating with the firmware + */ + if (!mc_dev->mc_io) { + dev_err(&mc_dev->dev, "mc_io is NULL, tear down cannot be performed in firmware\n"); + return -EINVAL; + } + 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); @@ -782,6 +797,37 @@ static int dprc_remove(struct fsl_mc_device *mc_dev) mc_dev->mc_io = NULL; } + return 0; +} +EXPORT_SYMBOL_GPL(dprc_cleanup); + +/** + * 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) +{ + struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev); + + if (!is_fsl_mc_bus_dprc(mc_dev)) + 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); + + dprc_cleanup(mc_dev); + dev_info(&mc_dev->dev, "DPRC device unbound from driver"); return 0; } diff --git a/drivers/bus/fsl-mc/dprc.c b/drivers/bus/fsl-mc/dprc.c index 602f030d84eb..57b097caf255 100644 --- a/drivers/bus/fsl-mc/dprc.c +++ b/drivers/bus/fsl-mc/dprc.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) /* * Copyright 2013-2016 Freescale Semiconductor Inc. + * Copyright 2020 NXP * */ #include <linux/kernel.h> @@ -8,6 +9,13 @@ #include "fsl-mc-private.h" +/* + * cache the DPRC version to reduce the number of commands + * towards the mc firmware + */ +static u16 dprc_major_ver; +static u16 dprc_minor_ver; + /** * dprc_open() - Open DPRC object for use * @mc_io: Pointer to MC portal's I/O object @@ -73,6 +81,77 @@ int dprc_close(struct fsl_mc_io *mc_io, 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 + * @options: 32 bit options: + * - 0 (no bits set) - all the objects inside the container are + * reset. The child containers are entered recursively and the + * objects reset. All the objects (including the child containers) + * are closed. + * - bit 0 set - all the objects inside the container are reset. + * However the child containers are not entered recursively. + * This option is supported for API versions >= 6.5 + * 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, + u32 options) +{ + struct fsl_mc_command cmd = { 0 }; + struct dprc_cmd_reset_container *cmd_params; + u32 cmdid = DPRC_CMDID_RESET_CONT; + int err; + + /* + * If the DPRC object version was not yet cached, cache it now. + * Otherwise use the already cached value. + */ + if (!dprc_major_ver && !dprc_minor_ver) { + err = dprc_get_api_version(mc_io, 0, + &dprc_major_ver, + &dprc_minor_ver); + if (err) + return err; + } + + /* + * MC API 6.5 introduced a new field in the command used to pass + * some flags. + * Bit 0 indicates that the child containers are not recursively reset. + */ + if (dprc_major_ver > 6 || (dprc_major_ver == 6 && dprc_minor_ver >= 5)) + cmdid = DPRC_CMDID_RESET_CONT_V2; + + /* prepare command */ + cmd.header = mc_encode_cmd_header(cmdid, cmd_flags, token); + cmd_params = (struct dprc_cmd_reset_container *)cmd.params; + cmd_params->child_container_id = cpu_to_le32(child_container_id); + cmd_params->options = cpu_to_le32(options); + + /* 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_' @@ -281,7 +360,7 @@ int dprc_get_attributes(struct fsl_mc_io *mc_io, /* 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->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); @@ -443,30 +522,44 @@ int dprc_get_obj_region(struct fsl_mc_io *mc_io, struct fsl_mc_command cmd = { 0 }; struct dprc_cmd_get_obj_region *cmd_params; struct dprc_rsp_get_obj_region *rsp_params; - u16 major_ver, minor_ver; int err; - /* prepare command */ - err = dprc_get_api_version(mc_io, 0, - &major_ver, - &minor_ver); - if (err) - return err; - - /** - * MC API version 6.3 introduced a new field to the region - * descriptor: base_address. If the older API is in use then the base - * address is set to zero to indicate it needs to be obtained elsewhere - * (typically the device tree). - */ - if (major_ver > 6 || (major_ver == 6 && minor_ver >= 3)) - cmd.header = - mc_encode_cmd_header(DPRC_CMDID_GET_OBJ_REG_V2, - cmd_flags, token); - else - cmd.header = - mc_encode_cmd_header(DPRC_CMDID_GET_OBJ_REG, - cmd_flags, token); + /* + * If the DPRC object version was not yet cached, cache it now. + * Otherwise use the already cached value. + */ + if (!dprc_major_ver && !dprc_minor_ver) { + err = dprc_get_api_version(mc_io, 0, + &dprc_major_ver, + &dprc_minor_ver); + if (err) + return err; + } + + if (dprc_major_ver > 6 || (dprc_major_ver == 6 && dprc_minor_ver >= 6)) { + /* + * MC API version 6.6 changed the size of the MC portals and software + * portals to 64K (as implemented by hardware). If older API is in use the + * size reported is less (64 bytes for mc portals and 4K for software + * portals). + */ + + cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_OBJ_REG_V3, + cmd_flags, token); + + } else if (dprc_major_ver == 6 && dprc_minor_ver >= 3) { + /* + * MC API version 6.3 introduced a new field to the region + * descriptor: base_address. If the older API is in use then the base + * address is set to zero to indicate it needs to be obtained elsewhere + * (typically the device tree). + */ + cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_OBJ_REG_V2, + cmd_flags, token); + } else { + 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); @@ -483,7 +576,7 @@ int dprc_get_obj_region(struct fsl_mc_io *mc_io, rsp_params = (struct dprc_rsp_get_obj_region *)cmd.params; region_desc->base_offset = le64_to_cpu(rsp_params->base_offset); region_desc->size = le32_to_cpu(rsp_params->size); - if (major_ver > 6 || (major_ver == 6 && minor_ver >= 3)) + if (dprc_major_ver > 6 || (dprc_major_ver == 6 && dprc_minor_ver >= 3)) region_desc->base_address = le64_to_cpu(rsp_params->base_addr); else region_desc->base_address = 0; diff --git a/drivers/bus/fsl-mc/fsl-mc-allocator.c b/drivers/bus/fsl-mc/fsl-mc-allocator.c index cc7bb900f524..e71a6f52ea0c 100644 --- a/drivers/bus/fsl-mc/fsl-mc-allocator.c +++ b/drivers/bus/fsl-mc/fsl-mc-allocator.c @@ -344,7 +344,7 @@ EXPORT_SYMBOL_GPL(fsl_mc_object_free); * 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, +int fsl_mc_populate_irq_pool(struct fsl_mc_device *mc_bus_dev, unsigned int irq_count) { unsigned int i; @@ -352,10 +352,14 @@ int fsl_mc_populate_irq_pool(struct fsl_mc_bus *mc_bus, 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_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev); struct fsl_mc_resource_pool *res_pool = &mc_bus->resource_pools[FSL_MC_POOL_IRQ]; + /* do nothing if the IRQ pool is already populated */ + if (mc_bus->irq_resources) + return 0; + if (irq_count == 0 || irq_count > FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS) return -EINVAL; @@ -407,9 +411,9 @@ 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) +void fsl_mc_cleanup_irq_pool(struct fsl_mc_device *mc_bus_dev) { - struct fsl_mc_device *mc_bus_dev = &mc_bus->mc_dev; + struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev); struct fsl_mc_resource_pool *res_pool = &mc_bus->resource_pools[FSL_MC_POOL_IRQ]; diff --git a/drivers/bus/fsl-mc/fsl-mc-bus.c b/drivers/bus/fsl-mc/fsl-mc-bus.c index b69794e7364d..76a6ee505d33 100644 --- a/drivers/bus/fsl-mc/fsl-mc-bus.c +++ b/drivers/bus/fsl-mc/fsl-mc-bus.c @@ -3,6 +3,7 @@ * Freescale Management Complex (MC) bus driver * * Copyright (C) 2014-2016 Freescale Semiconductor, Inc. + * Copyright 2019-2020 NXP * Author: German Rivera <German.Rivera@freescale.com> * */ @@ -78,6 +79,12 @@ static int fsl_mc_bus_match(struct device *dev, struct device_driver *drv) 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; @@ -147,8 +154,52 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR_RO(modalias); +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); + 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_driver_override.attr, NULL, }; @@ -452,7 +503,7 @@ common_cleanup: } static int get_dprc_icid(struct fsl_mc_io *mc_io, - int container_id, u16 *icid) + int container_id, u32 *icid) { struct dprc_attributes attr; int error; @@ -564,11 +615,8 @@ static int fsl_mc_device_get_mmio_regions(struct fsl_mc_device *mc_dev, 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; - if (region_desc.flags & DPRC_REGION_SHAREABLE) - regions[i].flags |= IORESOURCE_MEM; + regions[i].flags = region_desc.flags & IORESOURCE_BITS; + regions[i].flags |= IORESOURCE_MEM; } mc_dev->regions = regions; @@ -630,6 +678,7 @@ int fsl_mc_device_add(struct fsl_mc_obj_desc *obj_desc, if (!mc_bus) return -ENOMEM; + mutex_init(&mc_bus->scan_mutex); mc_dev = &mc_bus->mc_dev; } else { /* @@ -748,6 +797,9 @@ EXPORT_SYMBOL_GPL(fsl_mc_device_add); */ 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() */ @@ -908,9 +960,6 @@ static int fsl_mc_bus_probe(struct platform_device *pdev) u32 mc_portal_size, mc_stream_id; struct resource *plat_res; - if (!iommu_present(&fsl_mc_bus_type)) - return -EPROBE_DEFER; - mc = devm_kzalloc(&pdev->dev, sizeof(*mc), GFP_KERNEL); if (!mc) return -ENOMEM; @@ -918,11 +967,11 @@ static int fsl_mc_bus_probe(struct platform_device *pdev) platform_set_drvdata(pdev, mc); plat_res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - mc->fsl_mc_regs = devm_ioremap_resource(&pdev->dev, plat_res); - if (IS_ERR(mc->fsl_mc_regs)) - return PTR_ERR(mc->fsl_mc_regs); + if (plat_res) + mc->fsl_mc_regs = devm_ioremap_resource(&pdev->dev, plat_res); - if (IS_ENABLED(CONFIG_ACPI) && !dev_of_node(&pdev->dev)) { + if (mc->fsl_mc_regs && IS_ENABLED(CONFIG_ACPI) && + !dev_of_node(&pdev->dev)) { mc_stream_id = readl(mc->fsl_mc_regs + FSL_MC_FAPR); /* * HW ORs the PL and BMT bit, places the result in bit 15 of diff --git a/drivers/bus/fsl-mc/fsl-mc-private.h b/drivers/bus/fsl-mc/fsl-mc-private.h index 7a46a12eb747..85ca5fdee581 100644 --- a/drivers/bus/fsl-mc/fsl-mc-private.h +++ b/drivers/bus/fsl-mc/fsl-mc-private.h @@ -80,10 +80,12 @@ int dpmcp_reset(struct fsl_mc_io *mc_io, /* DPRC command versioning */ #define DPRC_CMD_BASE_VERSION 1 #define DPRC_CMD_2ND_VERSION 2 +#define DPRC_CMD_3RD_VERSION 3 #define DPRC_CMD_ID_OFFSET 4 #define DPRC_CMD(id) (((id) << DPRC_CMD_ID_OFFSET) | DPRC_CMD_BASE_VERSION) #define DPRC_CMD_V2(id) (((id) << DPRC_CMD_ID_OFFSET) | DPRC_CMD_2ND_VERSION) +#define DPRC_CMD_V3(id) (((id) << DPRC_CMD_ID_OFFSET) | DPRC_CMD_3RD_VERSION) /* DPRC command IDs */ #define DPRC_CMDID_CLOSE DPRC_CMD(0x800) @@ -91,6 +93,8 @@ int dpmcp_reset(struct fsl_mc_io *mc_io, #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_RESET_CONT_V2 DPRC_CMD_V2(0x005) #define DPRC_CMDID_SET_IRQ DPRC_CMD(0x010) #define DPRC_CMDID_SET_IRQ_ENABLE DPRC_CMD(0x012) @@ -103,6 +107,7 @@ int dpmcp_reset(struct fsl_mc_io *mc_io, #define DPRC_CMDID_GET_OBJ DPRC_CMD(0x15A) #define DPRC_CMDID_GET_OBJ_REG DPRC_CMD(0x15E) #define DPRC_CMDID_GET_OBJ_REG_V2 DPRC_CMD_V2(0x15E) +#define DPRC_CMDID_GET_OBJ_REG_V3 DPRC_CMD_V3(0x15E) #define DPRC_CMDID_SET_OBJ_IRQ DPRC_CMD(0x15F) #define DPRC_CMDID_GET_CONNECTION DPRC_CMD(0x16C) @@ -111,6 +116,11 @@ struct dprc_cmd_open { __le32 container_id; }; +struct dprc_cmd_reset_container { + __le32 child_container_id; + __le32 options; +}; + struct dprc_cmd_set_irq { /* cmd word 0 */ __le32 irq_val; @@ -152,8 +162,7 @@ struct dprc_cmd_clear_irq_status { struct dprc_rsp_get_attributes { /* response word 0 */ __le32 container_id; - __le16 icid; - __le16 pad; + __le32 icid; /* response word 1 */ __le32 options; __le32 portal_id; @@ -330,7 +339,7 @@ int dprc_clear_irq_status(struct fsl_mc_io *mc_io, */ struct dprc_attributes { int container_id; - u16 icid; + u32 icid; int portal_id; u64 options; }; @@ -358,12 +367,6 @@ int dprc_set_obj_irq(struct fsl_mc_io *mc_io, 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 -#define DPRC_REGION_SHAREABLE 0x00000002 - /** * enum dprc_region_type - Region type * @DPRC_REGION_TYPE_MC_PORTAL: MC portal region @@ -518,11 +521,6 @@ struct dpcon_cmd_set_notification { __le64 user_ctx; }; -/** - * 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 @@ -597,11 +595,6 @@ void fsl_mc_msi_domain_free_irqs(struct device *dev); struct irq_domain *fsl_mc_find_msi_domain(struct device *dev); -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); - int __must_check fsl_create_mc_io(struct device *dev, phys_addr_t mc_portal_phys_addr, u32 mc_portal_size, diff --git a/drivers/bus/fsl-mc/mc-io.c b/drivers/bus/fsl-mc/mc-io.c index a30b53f1d87d..305015486b91 100644 --- a/drivers/bus/fsl-mc/mc-io.c +++ b/drivers/bus/fsl-mc/mc-io.c @@ -129,7 +129,12 @@ error_destroy_mc_io: */ void fsl_destroy_mc_io(struct fsl_mc_io *mc_io) { - struct fsl_mc_device *dpmcp_dev = mc_io->dpmcp_dev; + struct fsl_mc_device *dpmcp_dev; + + if (!mc_io) + return; + + dpmcp_dev = mc_io->dpmcp_dev; if (dpmcp_dev) fsl_mc_io_unset_dpmcp(mc_io); diff --git a/drivers/bus/mhi/Kconfig b/drivers/bus/mhi/Kconfig index a8bd9bd7db7c..e841c1097fb4 100644 --- a/drivers/bus/mhi/Kconfig +++ b/drivers/bus/mhi/Kconfig @@ -6,9 +6,17 @@ # config MHI_BUS - tristate "Modem Host Interface (MHI) bus" - help - Bus driver for MHI protocol. Modem Host Interface (MHI) is a - communication protocol used by the host processors to control - and communicate with modem devices over a high speed peripheral - bus or shared memory. + tristate "Modem Host Interface (MHI) bus" + help + Bus driver for MHI protocol. Modem Host Interface (MHI) is a + communication protocol used by the host processors to control + and communicate with modem devices over a high speed peripheral + bus or shared memory. + +config MHI_BUS_DEBUG + bool "Debugfs support for the MHI bus" + depends on MHI_BUS && DEBUG_FS + help + Enable debugfs support for use with the MHI transport. Allows + reading and/or modifying some values within the MHI controller + for debug and test purposes. diff --git a/drivers/bus/mhi/core/Makefile b/drivers/bus/mhi/core/Makefile index 66e2700c9032..c3feb4130aa3 100644 --- a/drivers/bus/mhi/core/Makefile +++ b/drivers/bus/mhi/core/Makefile @@ -1,3 +1,4 @@ -obj-$(CONFIG_MHI_BUS) := mhi.o +obj-$(CONFIG_MHI_BUS) += mhi.o mhi-y := init.o main.o pm.o boot.o +mhi-$(CONFIG_MHI_BUS_DEBUG) += debugfs.o diff --git a/drivers/bus/mhi/core/boot.c b/drivers/bus/mhi/core/boot.c index 0b38014d040e..24422f5c3d80 100644 --- a/drivers/bus/mhi/core/boot.c +++ b/drivers/bus/mhi/core/boot.c @@ -392,13 +392,28 @@ void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl) void *buf; dma_addr_t dma_addr; size_t size; - int ret; + int i, ret; if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) { dev_err(dev, "Device MHI is not in valid state\n"); return; } + /* save hardware info from BHI */ + ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->bhi, BHI_SERIALNU, + &mhi_cntrl->serial_number); + if (ret) + dev_err(dev, "Could not capture serial number via BHI\n"); + + for (i = 0; i < ARRAY_SIZE(mhi_cntrl->oem_pk_hash); i++) { + ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->bhi, BHI_OEMPKHASH(i), + &mhi_cntrl->oem_pk_hash[i]); + if (ret) { + dev_err(dev, "Could not capture OEM PK HASH via BHI\n"); + break; + } + } + /* If device is in pass through, do reset to ready state transition */ if (mhi_cntrl->ee == MHI_EE_PTHRU) goto fw_load_ee_pthru; diff --git a/drivers/bus/mhi/core/debugfs.c b/drivers/bus/mhi/core/debugfs.c new file mode 100644 index 000000000000..3a48801e01f4 --- /dev/null +++ b/drivers/bus/mhi/core/debugfs.c @@ -0,0 +1,411 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * + */ + +#include <linux/debugfs.h> +#include <linux/device.h> +#include <linux/interrupt.h> +#include <linux/list.h> +#include <linux/mhi.h> +#include <linux/module.h> +#include "internal.h" + +static int mhi_debugfs_states_show(struct seq_file *m, void *d) +{ + struct mhi_controller *mhi_cntrl = m->private; + + /* states */ + seq_printf(m, "PM state: %s Device: %s MHI state: %s EE: %s wake: %s\n", + to_mhi_pm_state_str(mhi_cntrl->pm_state), + mhi_is_active(mhi_cntrl) ? "Active" : "Inactive", + TO_MHI_STATE_STR(mhi_cntrl->dev_state), + TO_MHI_EXEC_STR(mhi_cntrl->ee), + mhi_cntrl->wake_set ? "true" : "false"); + + /* counters */ + seq_printf(m, "M0: %u M2: %u M3: %u", mhi_cntrl->M0, mhi_cntrl->M2, + mhi_cntrl->M3); + + seq_printf(m, " device wake: %u pending packets: %u\n", + atomic_read(&mhi_cntrl->dev_wake), + atomic_read(&mhi_cntrl->pending_pkts)); + + return 0; +} + +static int mhi_debugfs_events_show(struct seq_file *m, void *d) +{ + struct mhi_controller *mhi_cntrl = m->private; + struct mhi_event *mhi_event; + struct mhi_event_ctxt *er_ctxt; + int i; + + if (!mhi_is_active(mhi_cntrl)) { + seq_puts(m, "Device not ready\n"); + return -ENODEV; + } + + er_ctxt = mhi_cntrl->mhi_ctxt->er_ctxt; + mhi_event = mhi_cntrl->mhi_event; + for (i = 0; i < mhi_cntrl->total_ev_rings; + i++, er_ctxt++, mhi_event++) { + struct mhi_ring *ring = &mhi_event->ring; + + if (mhi_event->offload_ev) { + seq_printf(m, "Index: %d is an offload event ring\n", + i); + continue; + } + + seq_printf(m, "Index: %d intmod count: %lu time: %lu", + i, (er_ctxt->intmod & EV_CTX_INTMODC_MASK) >> + EV_CTX_INTMODC_SHIFT, + (er_ctxt->intmod & EV_CTX_INTMODT_MASK) >> + EV_CTX_INTMODT_SHIFT); + + seq_printf(m, " base: 0x%0llx len: 0x%llx", er_ctxt->rbase, + er_ctxt->rlen); + + seq_printf(m, " rp: 0x%llx wp: 0x%llx", er_ctxt->rp, + er_ctxt->wp); + + seq_printf(m, " local rp: 0x%pK db: 0x%pad\n", ring->rp, + &mhi_event->db_cfg.db_val); + } + + return 0; +} + +static int mhi_debugfs_channels_show(struct seq_file *m, void *d) +{ + struct mhi_controller *mhi_cntrl = m->private; + struct mhi_chan *mhi_chan; + struct mhi_chan_ctxt *chan_ctxt; + int i; + + if (!mhi_is_active(mhi_cntrl)) { + seq_puts(m, "Device not ready\n"); + return -ENODEV; + } + + mhi_chan = mhi_cntrl->mhi_chan; + chan_ctxt = mhi_cntrl->mhi_ctxt->chan_ctxt; + for (i = 0; i < mhi_cntrl->max_chan; i++, chan_ctxt++, mhi_chan++) { + struct mhi_ring *ring = &mhi_chan->tre_ring; + + if (mhi_chan->offload_ch) { + seq_printf(m, "%s(%u) is an offload channel\n", + mhi_chan->name, mhi_chan->chan); + continue; + } + + if (!mhi_chan->mhi_dev) + continue; + + seq_printf(m, + "%s(%u) state: 0x%lx brstmode: 0x%lx pollcfg: 0x%lx", + mhi_chan->name, mhi_chan->chan, (chan_ctxt->chcfg & + CHAN_CTX_CHSTATE_MASK) >> CHAN_CTX_CHSTATE_SHIFT, + (chan_ctxt->chcfg & CHAN_CTX_BRSTMODE_MASK) >> + CHAN_CTX_BRSTMODE_SHIFT, (chan_ctxt->chcfg & + CHAN_CTX_POLLCFG_MASK) >> CHAN_CTX_POLLCFG_SHIFT); + + seq_printf(m, " type: 0x%x event ring: %u", chan_ctxt->chtype, + chan_ctxt->erindex); + + seq_printf(m, " base: 0x%llx len: 0x%llx rp: 0x%llx wp: 0x%llx", + chan_ctxt->rbase, chan_ctxt->rlen, chan_ctxt->rp, + chan_ctxt->wp); + + seq_printf(m, " local rp: 0x%pK local wp: 0x%pK db: 0x%pad\n", + ring->rp, ring->wp, + &mhi_chan->db_cfg.db_val); + } + + return 0; +} + +static int mhi_device_info_show(struct device *dev, void *data) +{ + struct mhi_device *mhi_dev; + + if (dev->bus != &mhi_bus_type) + return 0; + + mhi_dev = to_mhi_device(dev); + + seq_printf((struct seq_file *)data, "%s: type: %s dev_wake: %u", + mhi_dev->name, mhi_dev->dev_type ? "Controller" : "Transfer", + mhi_dev->dev_wake); + + /* for transfer device types only */ + if (mhi_dev->dev_type == MHI_DEVICE_XFER) + seq_printf((struct seq_file *)data, " channels: %u(UL)/%u(DL)", + mhi_dev->ul_chan_id, mhi_dev->dl_chan_id); + + seq_puts((struct seq_file *)data, "\n"); + + return 0; +} + +static int mhi_debugfs_devices_show(struct seq_file *m, void *d) +{ + struct mhi_controller *mhi_cntrl = m->private; + + if (!mhi_is_active(mhi_cntrl)) { + seq_puts(m, "Device not ready\n"); + return -ENODEV; + } + + device_for_each_child(mhi_cntrl->cntrl_dev, m, mhi_device_info_show); + + return 0; +} + +static int mhi_debugfs_regdump_show(struct seq_file *m, void *d) +{ + struct mhi_controller *mhi_cntrl = m->private; + enum mhi_state state; + enum mhi_ee_type ee; + int i, ret = -EIO; + u32 val; + void __iomem *mhi_base = mhi_cntrl->regs; + void __iomem *bhi_base = mhi_cntrl->bhi; + void __iomem *bhie_base = mhi_cntrl->bhie; + void __iomem *wake_db = mhi_cntrl->wake_db; + struct { + const char *name; + int offset; + void __iomem *base; + } regs[] = { + { "MHI_REGLEN", MHIREGLEN, mhi_base}, + { "MHI_VER", MHIVER, mhi_base}, + { "MHI_CFG", MHICFG, mhi_base}, + { "MHI_CTRL", MHICTRL, mhi_base}, + { "MHI_STATUS", MHISTATUS, mhi_base}, + { "MHI_WAKE_DB", 0, wake_db}, + { "BHI_EXECENV", BHI_EXECENV, bhi_base}, + { "BHI_STATUS", BHI_STATUS, bhi_base}, + { "BHI_ERRCODE", BHI_ERRCODE, bhi_base}, + { "BHI_ERRDBG1", BHI_ERRDBG1, bhi_base}, + { "BHI_ERRDBG2", BHI_ERRDBG2, bhi_base}, + { "BHI_ERRDBG3", BHI_ERRDBG3, bhi_base}, + { "BHIE_TXVEC_DB", BHIE_TXVECDB_OFFS, bhie_base}, + { "BHIE_TXVEC_STATUS", BHIE_TXVECSTATUS_OFFS, bhie_base}, + { "BHIE_RXVEC_DB", BHIE_RXVECDB_OFFS, bhie_base}, + { "BHIE_RXVEC_STATUS", BHIE_RXVECSTATUS_OFFS, bhie_base}, + { NULL }, + }; + + if (!MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) + return ret; + + seq_printf(m, "Host PM state: %s Device state: %s EE: %s\n", + to_mhi_pm_state_str(mhi_cntrl->pm_state), + TO_MHI_STATE_STR(mhi_cntrl->dev_state), + TO_MHI_EXEC_STR(mhi_cntrl->ee)); + + state = mhi_get_mhi_state(mhi_cntrl); + ee = mhi_get_exec_env(mhi_cntrl); + seq_printf(m, "Device EE: %s state: %s\n", TO_MHI_EXEC_STR(ee), + TO_MHI_STATE_STR(state)); + + for (i = 0; regs[i].name; i++) { + if (!regs[i].base) + continue; + ret = mhi_read_reg(mhi_cntrl, regs[i].base, regs[i].offset, + &val); + if (ret) + continue; + + seq_printf(m, "%s: 0x%x\n", regs[i].name, val); + } + + return 0; +} + +static int mhi_debugfs_device_wake_show(struct seq_file *m, void *d) +{ + struct mhi_controller *mhi_cntrl = m->private; + struct mhi_device *mhi_dev = mhi_cntrl->mhi_dev; + + if (!mhi_is_active(mhi_cntrl)) { + seq_puts(m, "Device not ready\n"); + return -ENODEV; + } + + seq_printf(m, + "Wake count: %d\n%s\n", mhi_dev->dev_wake, + "Usage: echo get/put > device_wake to vote/unvote for M0"); + + return 0; +} + +static ssize_t mhi_debugfs_device_wake_write(struct file *file, + const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct seq_file *m = file->private_data; + struct mhi_controller *mhi_cntrl = m->private; + struct mhi_device *mhi_dev = mhi_cntrl->mhi_dev; + char buf[16]; + int ret = -EINVAL; + + if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) + return -EFAULT; + + if (!strncmp(buf, "get", 3)) { + ret = mhi_device_get_sync(mhi_dev); + } else if (!strncmp(buf, "put", 3)) { + mhi_device_put(mhi_dev); + ret = 0; + } + + return ret ? ret : count; +} + +static int mhi_debugfs_timeout_ms_show(struct seq_file *m, void *d) +{ + struct mhi_controller *mhi_cntrl = m->private; + + seq_printf(m, "%u ms\n", mhi_cntrl->timeout_ms); + + return 0; +} + +static ssize_t mhi_debugfs_timeout_ms_write(struct file *file, + const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct seq_file *m = file->private_data; + struct mhi_controller *mhi_cntrl = m->private; + u32 timeout_ms; + + if (kstrtou32_from_user(ubuf, count, 0, &timeout_ms)) + return -EINVAL; + + mhi_cntrl->timeout_ms = timeout_ms; + + return count; +} + +static int mhi_debugfs_states_open(struct inode *inode, struct file *fp) +{ + return single_open(fp, mhi_debugfs_states_show, inode->i_private); +} + +static int mhi_debugfs_events_open(struct inode *inode, struct file *fp) +{ + return single_open(fp, mhi_debugfs_events_show, inode->i_private); +} + +static int mhi_debugfs_channels_open(struct inode *inode, struct file *fp) +{ + return single_open(fp, mhi_debugfs_channels_show, inode->i_private); +} + +static int mhi_debugfs_devices_open(struct inode *inode, struct file *fp) +{ + return single_open(fp, mhi_debugfs_devices_show, inode->i_private); +} + +static int mhi_debugfs_regdump_open(struct inode *inode, struct file *fp) +{ + return single_open(fp, mhi_debugfs_regdump_show, inode->i_private); +} + +static int mhi_debugfs_device_wake_open(struct inode *inode, struct file *fp) +{ + return single_open(fp, mhi_debugfs_device_wake_show, inode->i_private); +} + +static int mhi_debugfs_timeout_ms_open(struct inode *inode, struct file *fp) +{ + return single_open(fp, mhi_debugfs_timeout_ms_show, inode->i_private); +} + +static const struct file_operations debugfs_states_fops = { + .open = mhi_debugfs_states_open, + .release = single_release, + .read = seq_read, +}; + +static const struct file_operations debugfs_events_fops = { + .open = mhi_debugfs_events_open, + .release = single_release, + .read = seq_read, +}; + +static const struct file_operations debugfs_channels_fops = { + .open = mhi_debugfs_channels_open, + .release = single_release, + .read = seq_read, +}; + +static const struct file_operations debugfs_devices_fops = { + .open = mhi_debugfs_devices_open, + .release = single_release, + .read = seq_read, +}; + +static const struct file_operations debugfs_regdump_fops = { + .open = mhi_debugfs_regdump_open, + .release = single_release, + .read = seq_read, +}; + +static const struct file_operations debugfs_device_wake_fops = { + .open = mhi_debugfs_device_wake_open, + .write = mhi_debugfs_device_wake_write, + .release = single_release, + .read = seq_read, +}; + +static const struct file_operations debugfs_timeout_ms_fops = { + .open = mhi_debugfs_timeout_ms_open, + .write = mhi_debugfs_timeout_ms_write, + .release = single_release, + .read = seq_read, +}; + +static struct dentry *mhi_debugfs_root; + +void mhi_create_debugfs(struct mhi_controller *mhi_cntrl) +{ + mhi_cntrl->debugfs_dentry = + debugfs_create_dir(dev_name(mhi_cntrl->cntrl_dev), + mhi_debugfs_root); + + debugfs_create_file("states", 0444, mhi_cntrl->debugfs_dentry, + mhi_cntrl, &debugfs_states_fops); + debugfs_create_file("events", 0444, mhi_cntrl->debugfs_dentry, + mhi_cntrl, &debugfs_events_fops); + debugfs_create_file("channels", 0444, mhi_cntrl->debugfs_dentry, + mhi_cntrl, &debugfs_channels_fops); + debugfs_create_file("devices", 0444, mhi_cntrl->debugfs_dentry, + mhi_cntrl, &debugfs_devices_fops); + debugfs_create_file("regdump", 0444, mhi_cntrl->debugfs_dentry, + mhi_cntrl, &debugfs_regdump_fops); + debugfs_create_file("device_wake", 0644, mhi_cntrl->debugfs_dentry, + mhi_cntrl, &debugfs_device_wake_fops); + debugfs_create_file("timeout_ms", 0644, mhi_cntrl->debugfs_dentry, + mhi_cntrl, &debugfs_timeout_ms_fops); +} + +void mhi_destroy_debugfs(struct mhi_controller *mhi_cntrl) +{ + debugfs_remove_recursive(mhi_cntrl->debugfs_dentry); + mhi_cntrl->debugfs_dentry = NULL; +} + +void mhi_debugfs_init(void) +{ + mhi_debugfs_root = debugfs_create_dir(mhi_bus_type.name, NULL); +} + +void mhi_debugfs_exit(void) +{ + debugfs_remove_recursive(mhi_debugfs_root); +} diff --git a/drivers/bus/mhi/core/init.c b/drivers/bus/mhi/core/init.c index e43a190a7a36..0ffdebde8265 100644 --- a/drivers/bus/mhi/core/init.c +++ b/drivers/bus/mhi/core/init.c @@ -4,6 +4,7 @@ * */ +#include <linux/debugfs.h> #include <linux/device.h> #include <linux/dma-direction.h> #include <linux/dma-mapping.h> @@ -75,6 +76,42 @@ const char *to_mhi_pm_state_str(enum mhi_pm_state state) return mhi_pm_state_str[index]; } +static ssize_t serial_number_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct mhi_device *mhi_dev = to_mhi_device(dev); + struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl; + + return snprintf(buf, PAGE_SIZE, "Serial Number: %u\n", + mhi_cntrl->serial_number); +} +static DEVICE_ATTR_RO(serial_number); + +static ssize_t oem_pk_hash_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct mhi_device *mhi_dev = to_mhi_device(dev); + struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl; + int i, cnt = 0; + + for (i = 0; i < ARRAY_SIZE(mhi_cntrl->oem_pk_hash); i++) + cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, + "OEMPKHASH[%d]: 0x%x\n", i, + mhi_cntrl->oem_pk_hash[i]); + + return cnt; +} +static DEVICE_ATTR_RO(oem_pk_hash); + +static struct attribute *mhi_dev_attrs[] = { + &dev_attr_serial_number.attr, + &dev_attr_oem_pk_hash.attr, + NULL, +}; +ATTRIBUTE_GROUPS(mhi_dev); + /* MHI protocol requires the transfer ring to be aligned with ring length */ static int mhi_alloc_aligned_ring(struct mhi_controller *mhi_cntrl, struct mhi_ring *ring, @@ -125,6 +162,13 @@ int mhi_init_irq_setup(struct mhi_controller *mhi_cntrl) if (mhi_event->offload_ev) continue; + if (mhi_event->irq >= mhi_cntrl->nr_irqs) { + dev_err(dev, "irq %d not available for event ring\n", + mhi_event->irq); + ret = -EINVAL; + goto error_request; + } + ret = request_irq(mhi_cntrl->irq[mhi_event->irq], mhi_irq_handler, IRQF_SHARED | IRQF_NO_SUSPEND, @@ -562,10 +606,10 @@ int mhi_init_chan_ctxt(struct mhi_controller *mhi_cntrl, } static int parse_ev_cfg(struct mhi_controller *mhi_cntrl, - struct mhi_controller_config *config) + const struct mhi_controller_config *config) { struct mhi_event *mhi_event; - struct mhi_event_config *event_cfg; + const struct mhi_event_config *event_cfg; struct device *dev = &mhi_cntrl->mhi_dev->dev; int i, num; @@ -636,9 +680,6 @@ static int parse_ev_cfg(struct mhi_controller *mhi_cntrl, mhi_event++; } - /* We need IRQ for each event ring + additional one for BHI */ - mhi_cntrl->nr_irqs_req = mhi_cntrl->total_ev_rings + 1; - return 0; error_ev_cfg: @@ -648,9 +689,9 @@ error_ev_cfg: } static int parse_ch_cfg(struct mhi_controller *mhi_cntrl, - struct mhi_controller_config *config) + const struct mhi_controller_config *config) { - struct mhi_channel_config *ch_cfg; + const struct mhi_channel_config *ch_cfg; struct device *dev = &mhi_cntrl->mhi_dev->dev; int i; u32 chan; @@ -766,7 +807,7 @@ error_chan_cfg: } static int parse_config(struct mhi_controller *mhi_cntrl, - struct mhi_controller_config *config) + const struct mhi_controller_config *config) { int ret; @@ -803,7 +844,7 @@ error_ev_cfg: } int mhi_register_controller(struct mhi_controller *mhi_cntrl, - struct mhi_controller_config *config) + const struct mhi_controller_config *config) { struct mhi_event *mhi_event; struct mhi_chan *mhi_chan; @@ -904,6 +945,7 @@ int mhi_register_controller(struct mhi_controller *mhi_cntrl, mhi_dev->dev_type = MHI_DEVICE_CONTROLLER; mhi_dev->mhi_cntrl = mhi_cntrl; dev_set_name(&mhi_dev->dev, "%s", dev_name(mhi_cntrl->cntrl_dev)); + mhi_dev->name = dev_name(mhi_cntrl->cntrl_dev); /* Init wakeup source */ device_init_wakeup(&mhi_dev->dev, true); @@ -914,6 +956,8 @@ int mhi_register_controller(struct mhi_controller *mhi_cntrl, mhi_cntrl->mhi_dev = mhi_dev; + mhi_create_debugfs(mhi_cntrl); + return 0; error_add_dev: @@ -936,6 +980,8 @@ void mhi_unregister_controller(struct mhi_controller *mhi_cntrl) struct mhi_chan *mhi_chan = mhi_cntrl->mhi_chan; unsigned int i; + mhi_destroy_debugfs(mhi_cntrl); + kfree(mhi_cntrl->mhi_cmd); kfree(mhi_cntrl->mhi_event); @@ -953,6 +999,22 @@ void mhi_unregister_controller(struct mhi_controller *mhi_cntrl) } EXPORT_SYMBOL_GPL(mhi_unregister_controller); +struct mhi_controller *mhi_alloc_controller(void) +{ + struct mhi_controller *mhi_cntrl; + + mhi_cntrl = kzalloc(sizeof(*mhi_cntrl), GFP_KERNEL); + + return mhi_cntrl; +} +EXPORT_SYMBOL_GPL(mhi_alloc_controller); + +void mhi_free_controller(struct mhi_controller *mhi_cntrl) +{ + kfree(mhi_cntrl); +} +EXPORT_SYMBOL_GPL(mhi_free_controller); + int mhi_prepare_for_power_up(struct mhi_controller *mhi_cntrl) { struct device *dev = &mhi_cntrl->mhi_dev->dev; @@ -1249,7 +1311,7 @@ static int mhi_uevent(struct device *dev, struct kobj_uevent_env *env) struct mhi_device *mhi_dev = to_mhi_device(dev); return add_uevent_var(env, "MODALIAS=" MHI_DEVICE_MODALIAS_FMT, - mhi_dev->chan_name); + mhi_dev->name); } static int mhi_match(struct device *dev, struct device_driver *drv) @@ -1266,7 +1328,7 @@ static int mhi_match(struct device *dev, struct device_driver *drv) return 0; for (id = mhi_drv->id_table; id->chan[0]; id++) - if (!strcmp(mhi_dev->chan_name, id->chan)) { + if (!strcmp(mhi_dev->name, id->chan)) { mhi_dev->id = id; return 1; } @@ -1279,15 +1341,18 @@ struct bus_type mhi_bus_type = { .dev_name = "mhi", .match = mhi_match, .uevent = mhi_uevent, + .dev_groups = mhi_dev_groups, }; static int __init mhi_init(void) { + mhi_debugfs_init(); return bus_register(&mhi_bus_type); } static void __exit mhi_exit(void) { + mhi_debugfs_exit(); bus_unregister(&mhi_bus_type); } diff --git a/drivers/bus/mhi/core/internal.h b/drivers/bus/mhi/core/internal.h index b1f640b75a94..7989269ddd96 100644 --- a/drivers/bus/mhi/core/internal.h +++ b/drivers/bus/mhi/core/internal.h @@ -570,6 +570,30 @@ struct mhi_chan { /* Default MHI timeout */ #define MHI_TIMEOUT_MS (1000) +/* debugfs related functions */ +#ifdef CONFIG_MHI_BUS_DEBUG +void mhi_create_debugfs(struct mhi_controller *mhi_cntrl); +void mhi_destroy_debugfs(struct mhi_controller *mhi_cntrl); +void mhi_debugfs_init(void); +void mhi_debugfs_exit(void); +#else +static inline void mhi_create_debugfs(struct mhi_controller *mhi_cntrl) +{ +} + +static inline void mhi_destroy_debugfs(struct mhi_controller *mhi_cntrl) +{ +} + +static inline void mhi_debugfs_init(void) +{ +} + +static inline void mhi_debugfs_exit(void) +{ +} +#endif + struct mhi_device *mhi_alloc_device(struct mhi_controller *mhi_cntrl); int mhi_destroy_device(struct device *dev, void *data); @@ -592,13 +616,24 @@ void mhi_pm_st_worker(struct work_struct *work); void mhi_pm_sys_err_handler(struct mhi_controller *mhi_cntrl); void mhi_fw_load_worker(struct work_struct *work); int mhi_ready_state_transition(struct mhi_controller *mhi_cntrl); -void mhi_ctrl_ev_task(unsigned long data); int mhi_pm_m0_transition(struct mhi_controller *mhi_cntrl); void mhi_pm_m1_transition(struct mhi_controller *mhi_cntrl); int mhi_pm_m3_transition(struct mhi_controller *mhi_cntrl); int __mhi_device_get_sync(struct mhi_controller *mhi_cntrl); int mhi_send_cmd(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan, enum mhi_cmd_type cmd); +static inline bool mhi_is_active(struct mhi_controller *mhi_cntrl) +{ + return (mhi_cntrl->dev_state >= MHI_STATE_M0 && + mhi_cntrl->dev_state <= MHI_STATE_M3_FAST); +} + +static inline void mhi_trigger_resume(struct mhi_controller *mhi_cntrl) +{ + pm_wakeup_event(&mhi_cntrl->mhi_dev->dev, 0); + mhi_cntrl->runtime_get(mhi_cntrl); + mhi_cntrl->runtime_put(mhi_cntrl); +} /* Register access methods */ void mhi_db_brstmode(struct mhi_controller *mhi_cntrl, struct db_cfg *db_cfg, diff --git a/drivers/bus/mhi/core/main.c b/drivers/bus/mhi/core/main.c index 1f622ce6be8b..2cff5ddff225 100644 --- a/drivers/bus/mhi/core/main.c +++ b/drivers/bus/mhi/core/main.c @@ -249,7 +249,7 @@ int mhi_destroy_device(struct device *dev, void *data) put_device(&mhi_dev->dl_chan->mhi_dev->dev); dev_dbg(&mhi_cntrl->mhi_dev->dev, "destroy device for chan:%s\n", - mhi_dev->chan_name); + mhi_dev->name); /* Notify the client and remove the device from MHI bus */ device_del(dev); @@ -327,10 +327,10 @@ void mhi_create_devices(struct mhi_controller *mhi_cntrl) } /* Channel name is same for both UL and DL */ - mhi_dev->chan_name = mhi_chan->name; + mhi_dev->name = mhi_chan->name; dev_set_name(&mhi_dev->dev, "%s_%s", dev_name(mhi_cntrl->cntrl_dev), - mhi_dev->chan_name); + mhi_dev->name); /* Init wakeup source if available */ if (mhi_dev->dl_chan && mhi_dev->dl_chan->wake_capable) @@ -909,8 +909,7 @@ void mhi_ctrl_ev_task(unsigned long data) * process it since we are probably in a suspended state, * so trigger a resume. */ - mhi_cntrl->runtime_get(mhi_cntrl); - mhi_cntrl->runtime_put(mhi_cntrl); + mhi_trigger_resume(mhi_cntrl); return; } @@ -971,10 +970,8 @@ int mhi_queue_skb(struct mhi_device *mhi_dev, enum dma_data_direction dir, } /* we're in M3 or transitioning to M3 */ - if (MHI_PM_IN_SUSPEND_STATE(mhi_cntrl->pm_state)) { - mhi_cntrl->runtime_get(mhi_cntrl); - mhi_cntrl->runtime_put(mhi_cntrl); - } + if (MHI_PM_IN_SUSPEND_STATE(mhi_cntrl->pm_state)) + mhi_trigger_resume(mhi_cntrl); /* Toggle wake to exit out of M2 */ mhi_cntrl->wake_toggle(mhi_cntrl); @@ -1032,10 +1029,8 @@ int mhi_queue_dma(struct mhi_device *mhi_dev, enum dma_data_direction dir, } /* we're in M3 or transitioning to M3 */ - if (MHI_PM_IN_SUSPEND_STATE(mhi_cntrl->pm_state)) { - mhi_cntrl->runtime_get(mhi_cntrl); - mhi_cntrl->runtime_put(mhi_cntrl); - } + if (MHI_PM_IN_SUSPEND_STATE(mhi_cntrl->pm_state)) + mhi_trigger_resume(mhi_cntrl); /* Toggle wake to exit out of M2 */ mhi_cntrl->wake_toggle(mhi_cntrl); @@ -1147,10 +1142,8 @@ int mhi_queue_buf(struct mhi_device *mhi_dev, enum dma_data_direction dir, read_lock_irqsave(&mhi_cntrl->pm_lock, flags); /* we're in M3 or transitioning to M3 */ - if (MHI_PM_IN_SUSPEND_STATE(mhi_cntrl->pm_state)) { - mhi_cntrl->runtime_get(mhi_cntrl); - mhi_cntrl->runtime_put(mhi_cntrl); - } + if (MHI_PM_IN_SUSPEND_STATE(mhi_cntrl->pm_state)) + mhi_trigger_resume(mhi_cntrl); /* Toggle wake to exit out of M2 */ mhi_cntrl->wake_toggle(mhi_cntrl); diff --git a/drivers/bus/mhi/core/pm.c b/drivers/bus/mhi/core/pm.c index 796098078083..3de7b1639ec6 100644 --- a/drivers/bus/mhi/core/pm.c +++ b/drivers/bus/mhi/core/pm.c @@ -256,6 +256,7 @@ int mhi_pm_m0_transition(struct mhi_controller *mhi_cntrl) dev_err(dev, "Unable to transition to M0 state\n"); return -EIO; } + mhi_cntrl->M0++; /* Wake up the device */ read_lock_bh(&mhi_cntrl->pm_lock); @@ -326,6 +327,8 @@ void mhi_pm_m1_transition(struct mhi_controller *mhi_cntrl) mhi_cntrl->dev_state = MHI_STATE_M2; write_unlock_irq(&mhi_cntrl->pm_lock); + + mhi_cntrl->M2++; wake_up_all(&mhi_cntrl->state_event); /* If there are any pending resources, exit M2 immediately */ @@ -362,6 +365,7 @@ int mhi_pm_m3_transition(struct mhi_controller *mhi_cntrl) return -EIO; } + mhi_cntrl->M3++; wake_up_all(&mhi_cntrl->state_event); return 0; @@ -686,7 +690,8 @@ int mhi_pm_suspend(struct mhi_controller *mhi_cntrl) return -EIO; /* Return busy if there are any pending resources */ - if (atomic_read(&mhi_cntrl->dev_wake)) + if (atomic_read(&mhi_cntrl->dev_wake) || + atomic_read(&mhi_cntrl->pending_pkts)) return -EBUSY; /* Take MHI out of M2 state */ @@ -712,7 +717,8 @@ int mhi_pm_suspend(struct mhi_controller *mhi_cntrl) write_lock_irq(&mhi_cntrl->pm_lock); - if (atomic_read(&mhi_cntrl->dev_wake)) { + if (atomic_read(&mhi_cntrl->dev_wake) || + atomic_read(&mhi_cntrl->pending_pkts)) { write_unlock_irq(&mhi_cntrl->pm_lock); return -EBUSY; } @@ -822,11 +828,8 @@ int __mhi_device_get_sync(struct mhi_controller *mhi_cntrl) /* Wake up the device */ read_lock_bh(&mhi_cntrl->pm_lock); mhi_cntrl->wake_get(mhi_cntrl, true); - if (MHI_PM_IN_SUSPEND_STATE(mhi_cntrl->pm_state)) { - pm_wakeup_event(&mhi_cntrl->mhi_dev->dev, 0); - mhi_cntrl->runtime_get(mhi_cntrl); - mhi_cntrl->runtime_put(mhi_cntrl); - } + if (MHI_PM_IN_SUSPEND_STATE(mhi_cntrl->pm_state)) + mhi_trigger_resume(mhi_cntrl); read_unlock_bh(&mhi_cntrl->pm_lock); ret = wait_event_timeout(mhi_cntrl->state_event, @@ -915,7 +918,7 @@ int mhi_async_power_up(struct mhi_controller *mhi_cntrl) dev_info(dev, "Requested to power ON\n"); - if (mhi_cntrl->nr_irqs < mhi_cntrl->total_ev_rings) + if (mhi_cntrl->nr_irqs < 1) return -EINVAL; /* Supply default wake routines if not provided by controller driver */ @@ -1113,6 +1116,9 @@ void mhi_device_get(struct mhi_device *mhi_dev) mhi_dev->dev_wake++; read_lock_bh(&mhi_cntrl->pm_lock); + if (MHI_PM_IN_SUSPEND_STATE(mhi_cntrl->pm_state)) + mhi_trigger_resume(mhi_cntrl); + mhi_cntrl->wake_get(mhi_cntrl, true); read_unlock_bh(&mhi_cntrl->pm_lock); } @@ -1137,10 +1143,8 @@ void mhi_device_put(struct mhi_device *mhi_dev) mhi_dev->dev_wake--; read_lock_bh(&mhi_cntrl->pm_lock); - if (MHI_PM_IN_SUSPEND_STATE(mhi_cntrl->pm_state)) { - mhi_cntrl->runtime_get(mhi_cntrl); - mhi_cntrl->runtime_put(mhi_cntrl); - } + if (MHI_PM_IN_SUSPEND_STATE(mhi_cntrl->pm_state)) + mhi_trigger_resume(mhi_cntrl); mhi_cntrl->wake_put(mhi_cntrl, false); read_unlock_bh(&mhi_cntrl->pm_lock); |