mhi_bus: core: Add support for MHI host interface
MHI Host Interface is a communication protocol to be used by the host to control and communcate with modem over a high speed peripheral bus. This module will allow host to communicate with external devices that support MHI protocol. CRs-Fixed: 2204910 Change-Id: I6057f9167ebbb5d4dc2ebb91e46ede6bc7054325 Signed-off-by: Sujeev Dias <sdias@codeaurora.org>
This commit is contained in:
@@ -280,6 +280,8 @@ men-chameleon-bus.txt
|
||||
- info on MEN chameleon bus.
|
||||
metag/
|
||||
- directory with info about Linux on Meta architecture.
|
||||
mhi.txt
|
||||
- Modem Host Interface
|
||||
mic/
|
||||
- Intel Many Integrated Core (MIC) architecture device driver.
|
||||
mips/
|
||||
|
||||
136
Documentation/devicetree/bindings/bus/mhi.txt
Normal file
136
Documentation/devicetree/bindings/bus/mhi.txt
Normal file
@@ -0,0 +1,136 @@
|
||||
MHI Host Interface
|
||||
|
||||
MHI used by the host to control and communicate with modem over
|
||||
high speed peripheral bus.
|
||||
|
||||
==============
|
||||
Node Structure
|
||||
==============
|
||||
|
||||
Main node properties:
|
||||
|
||||
- mhi,max-channels
|
||||
Usage: required
|
||||
Value type: <u32>
|
||||
Definition: Maximum number of channels supported by this controller
|
||||
|
||||
- mhi,chan-cfg
|
||||
Usage: required
|
||||
Value type: Array of <u32>
|
||||
Definition: Array of tuples describe channel configuration.
|
||||
1st element: Physical channel number
|
||||
2nd element: Transfer ring length in elements
|
||||
3rd element: Event ring associated with this channel
|
||||
4th element: Channel direction as defined by enum dma_data_direction
|
||||
1 = UL data transfer
|
||||
2 = DL data transfer
|
||||
5th element: Channel doorbell mode configuration as defined by
|
||||
enum MHI_BRSTMODE
|
||||
2 = burst mode disabled
|
||||
3 = burst mode enabled
|
||||
6th element: mhi doorbell configuration, valid only when burst mode
|
||||
enabled.
|
||||
0 = Use default (device specific) polling configuration
|
||||
For UL channels, value specifies the timer to poll MHI context
|
||||
in milliseconds.
|
||||
For DL channels, the threshold to poll the MHI context
|
||||
in multiple of eight ring element.
|
||||
7th element: Channel execution enviornment as defined by enum MHI_EE
|
||||
1 = Bootloader stage
|
||||
2 = AMSS mode
|
||||
8th element: data transfer type accepted as defined by enum
|
||||
MHI_XFER_TYPE
|
||||
0 = accept cpu address for buffer
|
||||
1 = accept skb
|
||||
2 = accept scatterlist
|
||||
3 = offload channel, does not accept any transfer type
|
||||
9th element: Bitwise configuration settings for the channel
|
||||
Bit mask:
|
||||
BIT(0) : LPM notify, this channel master requre lpm enter/exit
|
||||
notifications.
|
||||
BIT(1) : Offload channel, MHI host only involved in setting up
|
||||
the data pipe. Not involved in active data transfer.
|
||||
|
||||
- mhi,chan-names
|
||||
Usage: required
|
||||
Value type: Array of <string>
|
||||
Definition: Channel names configured in mhi,chan-cfg.
|
||||
|
||||
- mhi,ev-cfg
|
||||
Usage: required
|
||||
Value type: Array of <u32>
|
||||
Definition: Array of tuples describe event configuration.
|
||||
1st element: Event ring length in elements
|
||||
2nd element: Interrupt moderation time in ms
|
||||
3rd element: MSI associated with this event ring
|
||||
4th element: Dedicated channel number, if it's a dedicated event ring
|
||||
5th element: Event ring priority, set to 1 for now
|
||||
6th element: Event doorbell mode configuration as defined by
|
||||
enum MHI_BRSTMODE
|
||||
2 = burst mode disabled
|
||||
3 = burst mode enabled
|
||||
7th element: Bitwise configuration settings for the channel
|
||||
Bit mask:
|
||||
BIT(0) : Event ring associated with hardware channels
|
||||
BIT(1) : Client manages the event ring (use by napi_poll)
|
||||
BIT(2) : Event ring associated with offload channel
|
||||
BIT(3) : Event ring dedicated to control events only
|
||||
|
||||
- mhi,timeout
|
||||
Usage: optional
|
||||
Value type: <u32>
|
||||
Definition: Maximum timeout in ms wait for state and cmd completion
|
||||
|
||||
- mhi,fw-name
|
||||
Usage: optional
|
||||
Value type: <string>
|
||||
Definition: Firmware image name to upload
|
||||
|
||||
- mhi,edl-name
|
||||
Usage: optional
|
||||
Value type: <string>
|
||||
Definition: Firmware image name for emergency download
|
||||
|
||||
- mhi,fbc-download
|
||||
Usage: optional
|
||||
Value type: <bool>
|
||||
Definition: If set true, image specified by fw-name is for full image
|
||||
|
||||
- mhi,sbl-size
|
||||
Usage: optional
|
||||
Value type: <u32>
|
||||
Definition: Size of SBL image in bytes
|
||||
|
||||
- mhi,seg-len
|
||||
Usage: optional
|
||||
Value type: <u32>
|
||||
Definition: Size of each segment to allocate for BHIe vector table
|
||||
|
||||
Children node properties:
|
||||
|
||||
MHI drivers that require DT can add driver specific information as a child node.
|
||||
|
||||
- mhi,chan
|
||||
Usage: Required
|
||||
Value type: <string>
|
||||
Definition: Channel name
|
||||
|
||||
========
|
||||
Example:
|
||||
========
|
||||
mhi_controller {
|
||||
mhi,max-channels = <105>;
|
||||
mhi,chan-cfg = <0 64 2 1 2 1 2 0 0>, <1 64 2 2 2 1 2 0 0>,
|
||||
<2 64 1 1 2 1 1 0 0>, <3 64 1 2 2 1 1 0 0>;
|
||||
mhi,chan-names = "LOOPBACK", "LOOPBACK",
|
||||
"SAHARA", "SAHARA";
|
||||
mhi,ev-cfg = <64 1 1 0 1 2 8>
|
||||
<64 1 2 0 1 2 0>;
|
||||
mhi,fw-name = "sbl1.mbn";
|
||||
mhi,timeout = <500>;
|
||||
|
||||
children_node {
|
||||
mhi,chan = "LOOPBACK"
|
||||
<driver specific properties>
|
||||
};
|
||||
};
|
||||
235
Documentation/mhi.txt
Normal file
235
Documentation/mhi.txt
Normal file
@@ -0,0 +1,235 @@
|
||||
Overview of Linux kernel MHI support
|
||||
====================================
|
||||
|
||||
Modem-Host Interface (MHI)
|
||||
=========================
|
||||
MHI used by the host to control and communicate with modem over high speed
|
||||
peripheral bus. Even though MHI can be easily adapt to any peripheral buses,
|
||||
primarily used with PCIe based devices. The host has one or more PCIe root
|
||||
ports connected to modem device. The host has limited access to device memory
|
||||
space, including register configuration and control the device operation.
|
||||
Data transfers are invoked from the device.
|
||||
|
||||
All data structures used by MHI are in the host system memory. Using PCIe
|
||||
interface, the device accesses those data structures. MHI data structures and
|
||||
data buffers in the host system memory regions are mapped for device.
|
||||
|
||||
Memory spaces
|
||||
-------------
|
||||
PCIe Configurations : Used for enumeration and resource management, such as
|
||||
interrupt and base addresses. This is done by mhi control driver.
|
||||
|
||||
MMIO
|
||||
----
|
||||
MHI MMIO : Memory mapped IO consists of set of registers in the device hardware,
|
||||
which are mapped to the host memory space through PCIe base address register
|
||||
(BAR)
|
||||
|
||||
MHI control registers : Access to MHI configurations registers
|
||||
(struct mhi_controller.regs).
|
||||
|
||||
MHI BHI register: Boot host interface registers (struct mhi_controller.bhi) used
|
||||
for firmware download before MHI initialization.
|
||||
|
||||
Channel db array : Doorbell registers (struct mhi_chan.tre_ring.db_addr) used by
|
||||
host to notify device there is new work to do.
|
||||
|
||||
Event db array : Associated with event context array
|
||||
(struct mhi_event.ring.db_addr), host uses to notify device free events are
|
||||
available.
|
||||
|
||||
Data structures
|
||||
---------------
|
||||
Host memory : Directly accessed by the host to manage the MHI data structures
|
||||
and buffers. The device accesses the host memory over the PCIe interface.
|
||||
|
||||
Channel context array : All channel configurations are organized in channel
|
||||
context data array.
|
||||
|
||||
struct __packed mhi_chan_ctxt;
|
||||
struct mhi_ctxt.chan_ctxt;
|
||||
|
||||
Transfer rings : Used by host to schedule work items for a channel and organized
|
||||
as a circular queue of transfer descriptors (TD).
|
||||
|
||||
struct __packed mhi_tre;
|
||||
struct mhi_chan.tre_ring;
|
||||
|
||||
Event context array : All event configurations are organized in event context
|
||||
data array.
|
||||
|
||||
struct mhi_ctxt.er_ctxt;
|
||||
struct __packed mhi_event_ctxt;
|
||||
|
||||
Event rings: Used by device to send completion and state transition messages to
|
||||
host
|
||||
|
||||
struct mhi_event.ring;
|
||||
struct __packed mhi_tre;
|
||||
|
||||
Command context array: All command configurations are organized in command
|
||||
context data array.
|
||||
|
||||
struct __packed mhi_cmd_ctxt;
|
||||
struct mhi_ctxt.cmd_ctxt;
|
||||
|
||||
Command rings: Used by host to send MHI commands to device
|
||||
|
||||
struct __packed mhi_tre;
|
||||
struct mhi_cmd.ring;
|
||||
|
||||
Transfer rings
|
||||
--------------
|
||||
MHI channels are logical, unidirectional data pipes between host and device.
|
||||
Each channel associated with a single transfer ring. The data direction can be
|
||||
either inbound (device to host) or outbound (host to device). Transfer
|
||||
descriptors are managed by using transfer rings, which are defined for each
|
||||
channel between device and host and resides in the host memory.
|
||||
|
||||
Transfer ring Pointer: Transfer Ring Array
|
||||
[Read Pointer (RP)] ----------->[Ring Element] } TD
|
||||
[Write Pointer (WP)]- [Ring Element]
|
||||
- [Ring Element]
|
||||
--------->[Ring Element]
|
||||
[Ring Element]
|
||||
|
||||
1. Host allocate memory for transfer ring
|
||||
2. Host sets base, read pointer, write pointer in corresponding channel context
|
||||
3. Ring is considered empty when RP == WP
|
||||
4. Ring is considered full when WP + 1 == RP
|
||||
4. RP indicates the next element to be serviced by device
|
||||
4. When host new buffer to send, host update the Ring element with buffer
|
||||
information
|
||||
5. Host increment the WP to next element
|
||||
6. Ring the associated channel DB.
|
||||
|
||||
Event rings
|
||||
-----------
|
||||
Events from the device to host are organized in event rings and defined in event
|
||||
descriptors. Event rings are array of EDs that resides in the host memory.
|
||||
|
||||
Transfer ring Pointer: Event Ring Array
|
||||
[Read Pointer (RP)] ----------->[Ring Element] } ED
|
||||
[Write Pointer (WP)]- [Ring Element]
|
||||
- [Ring Element]
|
||||
--------->[Ring Element]
|
||||
[Ring Element]
|
||||
|
||||
1. Host allocate memory for event ring
|
||||
2. Host sets base, read pointer, write pointer in corresponding channel context
|
||||
3. Both host and device has local copy of RP, WP
|
||||
3. Ring is considered empty (no events to service) when WP + 1 == RP
|
||||
4. Ring is full of events when RP == WP
|
||||
4. RP - 1 = last event device programmed
|
||||
4. When there is a new event device need to send, device update ED pointed by RP
|
||||
5. Device increment RP to next element
|
||||
6. Device trigger and interrupt
|
||||
|
||||
Example Operation for data transfer:
|
||||
|
||||
1. Host prepare TD with buffer information
|
||||
2. Host increment Chan[id].ctxt.WP
|
||||
3. Host ring channel DB register
|
||||
4. Device wakes up process the TD
|
||||
5. Device generate a completion event for that TD by updating ED
|
||||
6. Device increment Event[id].ctxt.RP
|
||||
7. Device trigger MSI to wake host
|
||||
8. Host wakes up and check event ring for completion event
|
||||
9. Host update the Event[i].ctxt.WP to indicate processed of completion event.
|
||||
|
||||
MHI States
|
||||
----------
|
||||
|
||||
enum MHI_STATE {
|
||||
MHI_STATE_RESET : MHI is in reset state, POR state. Host is not allowed to
|
||||
access device MMIO register space.
|
||||
MHI_STATE_READY : Device is ready for initialization. Host can start MHI
|
||||
initialization by programming MMIO
|
||||
MHI_STATE_M0 : MHI is in fully active state, data transfer is active
|
||||
MHI_STATE_M1 : Device in a suspended state
|
||||
MHI_STATE_M2 : MHI in low power mode, device may enter lower power mode.
|
||||
MHI_STATE_M3 : Both host and device in suspended state. PCIe link is not
|
||||
accessible to device.
|
||||
|
||||
MHI Initialization
|
||||
------------------
|
||||
|
||||
1. After system boots, the device is enumerated over PCIe interface
|
||||
2. Host allocate MHI context for event, channel and command arrays
|
||||
3. Initialize context array, and prepare interrupts
|
||||
3. Host waits until device enter READY state
|
||||
4. Program MHI MMIO registers and set device into MHI_M0 state
|
||||
5. Wait for device to enter M0 state
|
||||
|
||||
Linux Software Architecture
|
||||
===========================
|
||||
|
||||
MHI Controller
|
||||
--------------
|
||||
MHI controller is also the MHI bus master. In charge of managing the physical
|
||||
link between host and device. Not involved in actual data transfer. At least
|
||||
for PCIe based buses, for other type of bus, we can expand to add support.
|
||||
|
||||
Roles:
|
||||
1. Turn on PCIe bus and configure the link
|
||||
2. Configure MSI, SMMU, and IOMEM
|
||||
3. Allocate struct mhi_controller and register with MHI bus framework
|
||||
2. Initiate power on and shutdown sequence
|
||||
3. Initiate suspend and resume
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
1. Allocate control data structure by calling mhi_alloc_controller()
|
||||
2. Initialize mhi_controller with all the known information such as:
|
||||
- Device Topology
|
||||
- IOMMU window
|
||||
- IOMEM mapping
|
||||
- Device to use for memory allocation, and of_node with DT configuration
|
||||
- Configure asynchronous callback functions
|
||||
3. Register MHI controller with MHI bus framework by calling
|
||||
of_register_mhi_controller()
|
||||
|
||||
After successfully registering controller can initiate any of these power modes:
|
||||
|
||||
1. Power up sequence
|
||||
- mhi_prepare_for_power_up()
|
||||
- mhi_async_power_up()
|
||||
- mhi_sync_power_up()
|
||||
2. Power down sequence
|
||||
- mhi_power_down()
|
||||
- mhi_unprepare_after_power_down()
|
||||
3. Initiate suspend
|
||||
- mhi_pm_suspend()
|
||||
4. Initiate resume
|
||||
- mhi_pm_resume()
|
||||
|
||||
MHI Devices
|
||||
-----------
|
||||
Logical device that bind to maximum of two physical MHI channels. Once MHI is in
|
||||
powered on state, each supported channel by controller will be allocated as a
|
||||
mhi_device.
|
||||
|
||||
Each supported device would be enumerated under
|
||||
/sys/bus/mhi/devices/
|
||||
|
||||
struct mhi_device;
|
||||
|
||||
MHI Driver
|
||||
----------
|
||||
Each MHI driver can bind to one or more MHI devices. MHI host driver will bind
|
||||
mhi_device to mhi_driver.
|
||||
|
||||
All registered drivers are visible under
|
||||
/sys/bus/mhi/drivers/
|
||||
|
||||
struct mhi_driver;
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
1. Register driver using mhi_driver_register
|
||||
2. Before sending data, prepare device for transfer by calling
|
||||
mhi_prepare_for_transfer
|
||||
3. Initiate data transfer by calling mhi_queue_transfer
|
||||
4. After finish, call mhi_unprepare_from_transfer to end data transfer
|
||||
@@ -184,4 +184,21 @@ config DA8XX_MSTPRI
|
||||
configuration. Allows to adjust the priorities of all master
|
||||
peripherals.
|
||||
|
||||
config MHI_BUS
|
||||
tristate "Modem Host Interface"
|
||||
help
|
||||
MHI Host Interface is a communication protocol to be used by the host
|
||||
to control and communcate with modem over a high speed peripheral bus.
|
||||
Enabling this module will allow host to communicate with external
|
||||
devices that support MHI protocol.
|
||||
|
||||
config MHI_DEBUG
|
||||
bool "MHI debug support"
|
||||
depends on MHI_BUS
|
||||
help
|
||||
Say yes here to enable debugging support in the MHI transport
|
||||
and individual MHI client drivers. This option will impact
|
||||
throughput as individual MHI packets and state transitions
|
||||
will be logged.
|
||||
|
||||
endmenu
|
||||
|
||||
@@ -25,3 +25,4 @@ obj-$(CONFIG_UNIPHIER_SYSTEM_BUS) += uniphier-system-bus.o
|
||||
obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-config.o
|
||||
|
||||
obj-$(CONFIG_DA8XX_MSTPRI) += da8xx-mstpri.o
|
||||
obj-$(CONFIG_MHI_BUS) += mhi/
|
||||
|
||||
8
drivers/bus/mhi/Makefile
Normal file
8
drivers/bus/mhi/Makefile
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
# Makefile for the MHI stack
|
||||
#
|
||||
|
||||
# core layer
|
||||
obj-y += core/
|
||||
#obj-y += controllers/
|
||||
#obj-y += devices/
|
||||
1
drivers/bus/mhi/core/Makefile
Normal file
1
drivers/bus/mhi/core/Makefile
Normal file
@@ -0,0 +1 @@
|
||||
obj-$(CONFIG_MHI_BUS) +=mhi_init.o mhi_main.o mhi_pm.o mhi_boot.o mhi_dtr.o
|
||||
498
drivers/bus/mhi/core/mhi_boot.c
Normal file
498
drivers/bus/mhi/core/mhi_boot.c
Normal file
@@ -0,0 +1,498 @@
|
||||
/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/dma-direction.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/mhi.h>
|
||||
#include "mhi_internal.h"
|
||||
|
||||
|
||||
/* setup rddm vector table for rddm transfer */
|
||||
static void mhi_rddm_prepare(struct mhi_controller *mhi_cntrl,
|
||||
struct image_info *img_info)
|
||||
{
|
||||
struct mhi_buf *mhi_buf = img_info->mhi_buf;
|
||||
struct bhi_vec_entry *bhi_vec = img_info->bhi_vec;
|
||||
int i = 0;
|
||||
|
||||
for (i = 0; i < img_info->entries - 1; i++, mhi_buf++, bhi_vec++) {
|
||||
MHI_VERB("Setting vector:%pad size:%zu\n",
|
||||
&mhi_buf->dma_addr, mhi_buf->len);
|
||||
bhi_vec->dma_addr = mhi_buf->dma_addr;
|
||||
bhi_vec->size = mhi_buf->len;
|
||||
}
|
||||
}
|
||||
|
||||
/* download ramdump image from device */
|
||||
int mhi_download_rddm_img(struct mhi_controller *mhi_cntrl, bool in_panic)
|
||||
{
|
||||
void __iomem *base = mhi_cntrl->bhi;
|
||||
rwlock_t *pm_lock = &mhi_cntrl->pm_lock;
|
||||
struct image_info *rddm_image = mhi_cntrl->rddm_image;
|
||||
struct mhi_buf *mhi_buf;
|
||||
int ret;
|
||||
u32 rx_status;
|
||||
u32 sequence_id;
|
||||
|
||||
if (!rddm_image)
|
||||
return -ENOMEM;
|
||||
|
||||
MHI_LOG("Waiting for device to enter RDDM state from EE:%s\n",
|
||||
TO_MHI_EXEC_STR(mhi_cntrl->ee));
|
||||
|
||||
ret = wait_event_timeout(mhi_cntrl->state_event,
|
||||
mhi_cntrl->ee == MHI_EE_RDDM ||
|
||||
MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state),
|
||||
msecs_to_jiffies(mhi_cntrl->timeout_ms));
|
||||
|
||||
if (!ret || MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) {
|
||||
MHI_ERR("MHI is not in valid state, pm_state:%s ee:%s\n",
|
||||
to_mhi_pm_state_str(mhi_cntrl->pm_state),
|
||||
TO_MHI_EXEC_STR(mhi_cntrl->ee));
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
mhi_rddm_prepare(mhi_cntrl, mhi_cntrl->rddm_image);
|
||||
|
||||
/* vector table is the last entry */
|
||||
mhi_buf = &rddm_image->mhi_buf[rddm_image->entries - 1];
|
||||
|
||||
read_lock_bh(pm_lock);
|
||||
if (!MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) {
|
||||
read_unlock_bh(pm_lock);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
MHI_LOG("Starting BHIe Programming for RDDM\n");
|
||||
|
||||
mhi_write_reg(mhi_cntrl, base, BHIE_RXVECADDR_HIGH_OFFS,
|
||||
upper_32_bits(mhi_buf->dma_addr));
|
||||
|
||||
mhi_write_reg(mhi_cntrl, base, BHIE_RXVECADDR_LOW_OFFS,
|
||||
lower_32_bits(mhi_buf->dma_addr));
|
||||
|
||||
mhi_write_reg(mhi_cntrl, base, BHIE_RXVECSIZE_OFFS, mhi_buf->len);
|
||||
|
||||
sequence_id = prandom_u32() & BHIE_RXVECSTATUS_SEQNUM_BMSK;
|
||||
mhi_write_reg_field(mhi_cntrl, base, BHIE_RXVECDB_OFFS,
|
||||
BHIE_RXVECDB_SEQNUM_BMSK, BHIE_RXVECDB_SEQNUM_SHFT,
|
||||
sequence_id);
|
||||
read_unlock_bh(pm_lock);
|
||||
|
||||
MHI_LOG("Upper:0x%x Lower:0x%x len:0x%lx sequence:%u\n",
|
||||
upper_32_bits(mhi_buf->dma_addr),
|
||||
lower_32_bits(mhi_buf->dma_addr),
|
||||
mhi_buf->len, sequence_id);
|
||||
MHI_LOG("Waiting for image download completion\n");
|
||||
|
||||
/* waiting for image download completion */
|
||||
wait_event_timeout(mhi_cntrl->state_event,
|
||||
MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state) ||
|
||||
mhi_read_reg_field(mhi_cntrl, base,
|
||||
BHIE_RXVECSTATUS_OFFS,
|
||||
BHIE_RXVECSTATUS_STATUS_BMSK,
|
||||
BHIE_RXVECSTATUS_STATUS_SHFT,
|
||||
&rx_status) || rx_status,
|
||||
msecs_to_jiffies(mhi_cntrl->timeout_ms));
|
||||
|
||||
if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state))
|
||||
return -EIO;
|
||||
|
||||
return (rx_status == BHIE_RXVECSTATUS_STATUS_XFER_COMPL) ? 0 : -EIO;
|
||||
}
|
||||
EXPORT_SYMBOL(mhi_download_rddm_img);
|
||||
|
||||
static int mhi_fw_load_amss(struct mhi_controller *mhi_cntrl,
|
||||
const struct mhi_buf *mhi_buf)
|
||||
{
|
||||
void __iomem *base = mhi_cntrl->bhi;
|
||||
rwlock_t *pm_lock = &mhi_cntrl->pm_lock;
|
||||
u32 tx_status;
|
||||
|
||||
read_lock_bh(pm_lock);
|
||||
if (!MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) {
|
||||
read_unlock_bh(pm_lock);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
MHI_LOG("Starting BHIe Programming\n");
|
||||
|
||||
mhi_write_reg(mhi_cntrl, base, BHIE_TXVECADDR_HIGH_OFFS,
|
||||
upper_32_bits(mhi_buf->dma_addr));
|
||||
|
||||
mhi_write_reg(mhi_cntrl, base, BHIE_TXVECADDR_LOW_OFFS,
|
||||
lower_32_bits(mhi_buf->dma_addr));
|
||||
|
||||
mhi_write_reg(mhi_cntrl, base, BHIE_TXVECSIZE_OFFS, mhi_buf->len);
|
||||
|
||||
mhi_cntrl->sequence_id = prandom_u32() & BHIE_TXVECSTATUS_SEQNUM_BMSK;
|
||||
mhi_write_reg_field(mhi_cntrl, base, BHIE_TXVECDB_OFFS,
|
||||
BHIE_TXVECDB_SEQNUM_BMSK, BHIE_TXVECDB_SEQNUM_SHFT,
|
||||
mhi_cntrl->sequence_id);
|
||||
read_unlock_bh(pm_lock);
|
||||
|
||||
MHI_LOG("Upper:0x%x Lower:0x%x len:0x%lx sequence:%u\n",
|
||||
upper_32_bits(mhi_buf->dma_addr),
|
||||
lower_32_bits(mhi_buf->dma_addr),
|
||||
mhi_buf->len, mhi_cntrl->sequence_id);
|
||||
MHI_LOG("Waiting for image transfer completion\n");
|
||||
|
||||
/* waiting for image download completion */
|
||||
wait_event_timeout(mhi_cntrl->state_event,
|
||||
MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state) ||
|
||||
mhi_read_reg_field(mhi_cntrl, base,
|
||||
BHIE_TXVECSTATUS_OFFS,
|
||||
BHIE_TXVECSTATUS_STATUS_BMSK,
|
||||
BHIE_TXVECSTATUS_STATUS_SHFT,
|
||||
&tx_status) || tx_status,
|
||||
msecs_to_jiffies(mhi_cntrl->timeout_ms));
|
||||
|
||||
if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state))
|
||||
return -EIO;
|
||||
|
||||
return (tx_status == BHIE_TXVECSTATUS_STATUS_XFER_COMPL) ? 0 : -EIO;
|
||||
}
|
||||
|
||||
static int mhi_fw_load_sbl(struct mhi_controller *mhi_cntrl,
|
||||
void *buf,
|
||||
size_t size)
|
||||
{
|
||||
u32 tx_status, val;
|
||||
int i, ret;
|
||||
void __iomem *base = mhi_cntrl->bhi;
|
||||
rwlock_t *pm_lock = &mhi_cntrl->pm_lock;
|
||||
dma_addr_t phys = dma_map_single(mhi_cntrl->dev, buf, size,
|
||||
DMA_TO_DEVICE);
|
||||
struct {
|
||||
char *name;
|
||||
u32 offset;
|
||||
} error_reg[] = {
|
||||
{ "ERROR_CODE", BHI_ERRCODE },
|
||||
{ "ERROR_DBG1", BHI_ERRDBG1 },
|
||||
{ "ERROR_DBG2", BHI_ERRDBG2 },
|
||||
{ "ERROR_DBG3", BHI_ERRDBG3 },
|
||||
{ NULL },
|
||||
};
|
||||
|
||||
if (dma_mapping_error(mhi_cntrl->dev, phys))
|
||||
return -ENOMEM;
|
||||
|
||||
MHI_LOG("Starting BHI programming\n");
|
||||
|
||||
/* program start sbl download via bhi protocol */
|
||||
read_lock_bh(pm_lock);
|
||||
if (!MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) {
|
||||
read_unlock_bh(pm_lock);
|
||||
goto invalid_pm_state;
|
||||
}
|
||||
|
||||
mhi_write_reg(mhi_cntrl, base, BHI_STATUS, 0);
|
||||
mhi_write_reg(mhi_cntrl, base, BHI_IMGADDR_HIGH, upper_32_bits(phys));
|
||||
mhi_write_reg(mhi_cntrl, base, BHI_IMGADDR_LOW, lower_32_bits(phys));
|
||||
mhi_write_reg(mhi_cntrl, base, BHI_IMGSIZE, size);
|
||||
mhi_cntrl->session_id = prandom_u32() & BHI_TXDB_SEQNUM_BMSK;
|
||||
mhi_write_reg(mhi_cntrl, base, BHI_IMGTXDB, mhi_cntrl->session_id);
|
||||
read_unlock_bh(pm_lock);
|
||||
|
||||
MHI_LOG("Waiting for image transfer completion\n");
|
||||
|
||||
/* waiting for image download completion */
|
||||
wait_event_timeout(mhi_cntrl->state_event,
|
||||
MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state) ||
|
||||
mhi_read_reg_field(mhi_cntrl, base, BHI_STATUS,
|
||||
BHI_STATUS_MASK, BHI_STATUS_SHIFT,
|
||||
&tx_status) || tx_status,
|
||||
msecs_to_jiffies(mhi_cntrl->timeout_ms));
|
||||
if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state))
|
||||
goto invalid_pm_state;
|
||||
|
||||
if (tx_status == BHI_STATUS_ERROR) {
|
||||
MHI_ERR("Image transfer failed\n");
|
||||
read_lock_bh(pm_lock);
|
||||
if (MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) {
|
||||
for (i = 0; error_reg[i].name; i++) {
|
||||
ret = mhi_read_reg(mhi_cntrl, base,
|
||||
error_reg[i].offset, &val);
|
||||
if (ret)
|
||||
break;
|
||||
MHI_ERR("reg:%s value:0x%x\n",
|
||||
error_reg[i].name, val);
|
||||
}
|
||||
}
|
||||
read_unlock_bh(pm_lock);
|
||||
goto invalid_pm_state;
|
||||
}
|
||||
|
||||
dma_unmap_single(mhi_cntrl->dev, phys, size, DMA_TO_DEVICE);
|
||||
|
||||
return (tx_status == BHI_STATUS_SUCCESS) ? 0 : -ETIMEDOUT;
|
||||
|
||||
invalid_pm_state:
|
||||
dma_unmap_single(mhi_cntrl->dev, phys, size, DMA_TO_DEVICE);
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
void mhi_free_bhie_table(struct mhi_controller *mhi_cntrl,
|
||||
struct image_info *image_info)
|
||||
{
|
||||
int i;
|
||||
struct mhi_buf *mhi_buf = image_info->mhi_buf;
|
||||
|
||||
for (i = 0; i < image_info->entries; i++, mhi_buf++)
|
||||
mhi_free_coherent(mhi_cntrl, mhi_buf->len, mhi_buf->buf,
|
||||
mhi_buf->dma_addr);
|
||||
|
||||
kfree(image_info->mhi_buf);
|
||||
kfree(image_info);
|
||||
}
|
||||
|
||||
int mhi_alloc_bhie_table(struct mhi_controller *mhi_cntrl,
|
||||
struct image_info **image_info,
|
||||
size_t alloc_size)
|
||||
{
|
||||
size_t seg_size = mhi_cntrl->seg_len;
|
||||
/* requier additional entry for vec table */
|
||||
int segments = DIV_ROUND_UP(alloc_size, seg_size) + 1;
|
||||
int i;
|
||||
struct image_info *img_info;
|
||||
struct mhi_buf *mhi_buf;
|
||||
|
||||
MHI_LOG("Allocating bytes:%zu seg_size:%zu total_seg:%u\n",
|
||||
alloc_size, seg_size, segments);
|
||||
|
||||
img_info = kzalloc(sizeof(*img_info), GFP_KERNEL);
|
||||
if (!img_info)
|
||||
return -ENOMEM;
|
||||
|
||||
/* allocate memory for entries */
|
||||
img_info->mhi_buf = kcalloc(segments, sizeof(*img_info->mhi_buf),
|
||||
GFP_KERNEL);
|
||||
if (!img_info->mhi_buf)
|
||||
goto error_alloc_mhi_buf;
|
||||
|
||||
/* allocate and populate vector table */
|
||||
mhi_buf = img_info->mhi_buf;
|
||||
for (i = 0; i < segments; i++, mhi_buf++) {
|
||||
size_t vec_size = seg_size;
|
||||
|
||||
/* last entry is for vector table */
|
||||
if (i == segments - 1)
|
||||
vec_size = sizeof(struct __packed bhi_vec_entry) * i;
|
||||
|
||||
mhi_buf->len = vec_size;
|
||||
mhi_buf->buf = mhi_alloc_coherent(mhi_cntrl, vec_size,
|
||||
&mhi_buf->dma_addr, GFP_KERNEL);
|
||||
if (!mhi_buf->buf)
|
||||
goto error_alloc_segment;
|
||||
|
||||
MHI_LOG("Entry:%d Address:0x%llx size:%lu\n", i,
|
||||
mhi_buf->dma_addr, mhi_buf->len);
|
||||
}
|
||||
|
||||
img_info->bhi_vec = img_info->mhi_buf[segments - 1].buf;
|
||||
img_info->entries = segments;
|
||||
*image_info = img_info;
|
||||
|
||||
MHI_LOG("Successfully allocated bhi vec table\n");
|
||||
|
||||
return 0;
|
||||
|
||||
error_alloc_segment:
|
||||
for (--i, --mhi_buf; i >= 0; i--, mhi_buf--)
|
||||
mhi_free_coherent(mhi_cntrl, mhi_buf->len, mhi_buf->buf,
|
||||
mhi_buf->dma_addr);
|
||||
|
||||
error_alloc_mhi_buf:
|
||||
kfree(img_info);
|
||||
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static void mhi_firmware_copy(struct mhi_controller *mhi_cntrl,
|
||||
const struct firmware *firmware,
|
||||
struct image_info *img_info)
|
||||
{
|
||||
size_t remainder = firmware->size;
|
||||
size_t to_cpy;
|
||||
const u8 *buf = firmware->data;
|
||||
int i = 0;
|
||||
struct mhi_buf *mhi_buf = img_info->mhi_buf;
|
||||
struct bhi_vec_entry *bhi_vec = img_info->bhi_vec;
|
||||
|
||||
while (remainder) {
|
||||
MHI_ASSERT(i >= img_info->entries, "malformed vector table");
|
||||
|
||||
to_cpy = min(remainder, mhi_buf->len);
|
||||
memcpy(mhi_buf->buf, buf, to_cpy);
|
||||
bhi_vec->dma_addr = mhi_buf->dma_addr;
|
||||
bhi_vec->size = to_cpy;
|
||||
|
||||
MHI_VERB("Setting Vector:0x%llx size: %llu\n",
|
||||
bhi_vec->dma_addr, bhi_vec->size);
|
||||
buf += to_cpy;
|
||||
remainder -= to_cpy;
|
||||
i++;
|
||||
bhi_vec++;
|
||||
mhi_buf++;
|
||||
}
|
||||
}
|
||||
|
||||
void mhi_fw_load_worker(struct work_struct *work)
|
||||
{
|
||||
int ret;
|
||||
struct mhi_controller *mhi_cntrl;
|
||||
const char *fw_name;
|
||||
const struct firmware *firmware;
|
||||
struct image_info *image_info;
|
||||
void *buf;
|
||||
size_t size;
|
||||
|
||||
mhi_cntrl = container_of(work, struct mhi_controller, fw_worker);
|
||||
|
||||
MHI_LOG("Waiting for device to enter PBL from EE:%s\n",
|
||||
TO_MHI_EXEC_STR(mhi_cntrl->ee));
|
||||
|
||||
ret = wait_event_timeout(mhi_cntrl->state_event,
|
||||
MHI_IN_PBL(mhi_cntrl->ee) ||
|
||||
MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state),
|
||||
msecs_to_jiffies(mhi_cntrl->timeout_ms));
|
||||
|
||||
if (!ret || MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) {
|
||||
MHI_ERR("MHI is not in valid state\n");
|
||||
return;
|
||||
}
|
||||
|
||||
MHI_LOG("Device current EE:%s\n", TO_MHI_EXEC_STR(mhi_cntrl->ee));
|
||||
|
||||
/* if device in pthru, we do not have to load firmware */
|
||||
if (mhi_cntrl->ee == MHI_EE_PTHRU)
|
||||
return;
|
||||
|
||||
fw_name = (mhi_cntrl->ee == MHI_EE_EDL) ?
|
||||
mhi_cntrl->edl_image : mhi_cntrl->fw_image;
|
||||
|
||||
if (!fw_name || (mhi_cntrl->fbc_download && (!mhi_cntrl->sbl_size ||
|
||||
!mhi_cntrl->seg_len))) {
|
||||
MHI_ERR("No firmware image defined or !sbl_size || !seg_len\n");
|
||||
return;
|
||||
}
|
||||
|
||||
ret = request_firmware(&firmware, fw_name, mhi_cntrl->dev);
|
||||
if (ret) {
|
||||
MHI_ERR("Error loading firmware, ret:%d\n", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
size = (mhi_cntrl->fbc_download) ? mhi_cntrl->sbl_size : firmware->size;
|
||||
|
||||
/* the sbl size provided is maximum size, not necessarily image size */
|
||||
if (size > firmware->size)
|
||||
size = firmware->size;
|
||||
|
||||
buf = kmalloc(size, GFP_KERNEL);
|
||||
if (!buf) {
|
||||
MHI_ERR("Could not allocate memory for image\n");
|
||||
release_firmware(firmware);
|
||||
return;
|
||||
}
|
||||
|
||||
/* load sbl image */
|
||||
memcpy(buf, firmware->data, size);
|
||||
ret = mhi_fw_load_sbl(mhi_cntrl, buf, size);
|
||||
kfree(buf);
|
||||
|
||||
if (!mhi_cntrl->fbc_download || ret || mhi_cntrl->ee == MHI_EE_EDL)
|
||||
release_firmware(firmware);
|
||||
|
||||
/* error or in edl, we're done */
|
||||
if (ret || mhi_cntrl->ee == MHI_EE_EDL)
|
||||
return;
|
||||
|
||||
write_lock_irq(&mhi_cntrl->pm_lock);
|
||||
mhi_cntrl->dev_state = MHI_STATE_RESET;
|
||||
write_unlock_irq(&mhi_cntrl->pm_lock);
|
||||
|
||||
/*
|
||||
* if we're doing fbc, populate vector tables while
|
||||
* device transitioning into MHI READY state
|
||||
*/
|
||||
if (mhi_cntrl->fbc_download) {
|
||||
ret = mhi_alloc_bhie_table(mhi_cntrl, &mhi_cntrl->fbc_image,
|
||||
firmware->size);
|
||||
if (ret) {
|
||||
MHI_ERR("Error alloc size of %zu\n", firmware->size);
|
||||
goto error_alloc_fw_table;
|
||||
}
|
||||
|
||||
MHI_LOG("Copying firmware image into vector table\n");
|
||||
|
||||
/* load the firmware into BHIE vec table */
|
||||
mhi_firmware_copy(mhi_cntrl, firmware, mhi_cntrl->fbc_image);
|
||||
}
|
||||
|
||||
/* transitioning into MHI RESET->READY state */
|
||||
ret = mhi_ready_state_transition(mhi_cntrl);
|
||||
|
||||
MHI_LOG("To Reset->Ready PM_STATE:%s MHI_STATE:%s EE:%s, ret:%d\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), ret);
|
||||
|
||||
if (!mhi_cntrl->fbc_download)
|
||||
return;
|
||||
|
||||
if (ret) {
|
||||
MHI_ERR("Did not transition to READY state\n");
|
||||
goto error_read;
|
||||
}
|
||||
|
||||
/* wait for BHIE event */
|
||||
ret = wait_event_timeout(mhi_cntrl->state_event,
|
||||
mhi_cntrl->ee == MHI_EE_BHIE ||
|
||||
MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state),
|
||||
msecs_to_jiffies(mhi_cntrl->timeout_ms));
|
||||
|
||||
if (!ret || MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) {
|
||||
MHI_ERR("MHI did not enter BHIE\n");
|
||||
goto error_read;
|
||||
}
|
||||
|
||||
/* start full firmware image download */
|
||||
image_info = mhi_cntrl->fbc_image;
|
||||
ret = mhi_fw_load_amss(mhi_cntrl,
|
||||
/* last entry is vec table */
|
||||
&image_info->mhi_buf[image_info->entries - 1]);
|
||||
|
||||
MHI_LOG("amss fw_load, ret:%d\n", ret);
|
||||
|
||||
release_firmware(firmware);
|
||||
|
||||
return;
|
||||
|
||||
error_read:
|
||||
mhi_free_bhie_table(mhi_cntrl, mhi_cntrl->fbc_image);
|
||||
mhi_cntrl->fbc_image = NULL;
|
||||
|
||||
error_alloc_fw_table:
|
||||
release_firmware(firmware);
|
||||
}
|
||||
177
drivers/bus/mhi/core/mhi_dtr.c
Normal file
177
drivers/bus/mhi/core/mhi_dtr.c
Normal file
@@ -0,0 +1,177 @@
|
||||
/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/dma-direction.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/termios.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/mhi.h>
|
||||
#include "mhi_internal.h"
|
||||
|
||||
struct __packed dtr_ctrl_msg {
|
||||
u32 preamble;
|
||||
u32 msg_id;
|
||||
u32 dest_id;
|
||||
u32 size;
|
||||
u32 msg;
|
||||
};
|
||||
|
||||
#define CTRL_MAGIC (0x4C525443)
|
||||
#define CTRL_MSG_DTR BIT(0)
|
||||
#define CTRL_MSG_ID (0x10)
|
||||
|
||||
static int mhi_dtr_tiocmset(struct mhi_controller *mhi_cntrl,
|
||||
struct mhi_chan *mhi_chan,
|
||||
u32 tiocm)
|
||||
{
|
||||
struct dtr_ctrl_msg *dtr_msg = NULL;
|
||||
struct mhi_chan *dtr_chan = mhi_cntrl->dtr_dev->ul_chan;
|
||||
int ret = 0;
|
||||
|
||||
tiocm &= TIOCM_DTR;
|
||||
if (mhi_chan->tiocm == tiocm)
|
||||
return 0;
|
||||
|
||||
mutex_lock(&dtr_chan->mutex);
|
||||
|
||||
dtr_msg = kzalloc(sizeof(*dtr_msg), GFP_KERNEL);
|
||||
if (!dtr_msg) {
|
||||
ret = -ENOMEM;
|
||||
goto tiocm_exit;
|
||||
}
|
||||
|
||||
dtr_msg->preamble = CTRL_MAGIC;
|
||||
dtr_msg->msg_id = CTRL_MSG_ID;
|
||||
dtr_msg->dest_id = mhi_chan->chan;
|
||||
dtr_msg->size = sizeof(u32);
|
||||
if (tiocm & TIOCM_DTR)
|
||||
dtr_msg->msg |= CTRL_MSG_DTR;
|
||||
|
||||
reinit_completion(&dtr_chan->completion);
|
||||
ret = mhi_queue_transfer(mhi_cntrl->dtr_dev, DMA_TO_DEVICE, dtr_msg,
|
||||
sizeof(*dtr_msg), MHI_EOT);
|
||||
if (ret)
|
||||
goto tiocm_exit;
|
||||
|
||||
ret = wait_for_completion_timeout(&dtr_chan->completion,
|
||||
msecs_to_jiffies(mhi_cntrl->timeout_ms));
|
||||
|
||||
if (!ret) {
|
||||
MHI_ERR("Failed to receive transfer callback\n");
|
||||
ret = -EIO;
|
||||
goto tiocm_exit;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
mhi_chan->tiocm = tiocm;
|
||||
|
||||
tiocm_exit:
|
||||
kfree(dtr_msg);
|
||||
mutex_unlock(&dtr_chan->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
long mhi_ioctl(struct mhi_device *mhi_dev, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
|
||||
struct mhi_chan *mhi_chan = mhi_dev->ul_chan;
|
||||
int ret;
|
||||
|
||||
/* ioctl not supported by this controller */
|
||||
if (!mhi_cntrl->dtr_dev)
|
||||
return -EIO;
|
||||
|
||||
switch (cmd) {
|
||||
case TIOCMGET:
|
||||
return mhi_chan->tiocm;
|
||||
case TIOCMSET:
|
||||
{
|
||||
u32 tiocm;
|
||||
|
||||
ret = get_user(tiocm, (u32 *)arg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return mhi_dtr_tiocmset(mhi_cntrl, mhi_chan, tiocm);
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
EXPORT_SYMBOL(mhi_ioctl);
|
||||
|
||||
static void mhi_dtr_xfer_cb(struct mhi_device *mhi_dev,
|
||||
struct mhi_result *mhi_result)
|
||||
{
|
||||
struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
|
||||
struct mhi_chan *dtr_chan = mhi_cntrl->dtr_dev->ul_chan;
|
||||
|
||||
MHI_VERB("Received with status:%d\n", mhi_result->transaction_status);
|
||||
if (!mhi_result->transaction_status)
|
||||
complete(&dtr_chan->completion);
|
||||
}
|
||||
|
||||
static void mhi_dtr_remove(struct mhi_device *mhi_dev)
|
||||
{
|
||||
struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
|
||||
|
||||
mhi_cntrl->dtr_dev = NULL;
|
||||
}
|
||||
|
||||
static int mhi_dtr_probe(struct mhi_device *mhi_dev,
|
||||
const struct mhi_device_id *id)
|
||||
{
|
||||
struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
|
||||
int ret;
|
||||
|
||||
MHI_LOG("Enter for DTR control channel\n");
|
||||
|
||||
ret = mhi_prepare_for_transfer(mhi_dev);
|
||||
if (!ret)
|
||||
mhi_cntrl->dtr_dev = mhi_dev;
|
||||
|
||||
MHI_LOG("Exit with ret:%d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct mhi_device_id mhi_dtr_table[] = {
|
||||
{ .chan = "IP_CTRL" },
|
||||
{ NULL },
|
||||
};
|
||||
|
||||
static struct mhi_driver mhi_dtr_driver = {
|
||||
.id_table = mhi_dtr_table,
|
||||
.remove = mhi_dtr_remove,
|
||||
.probe = mhi_dtr_probe,
|
||||
.ul_xfer_cb = mhi_dtr_xfer_cb,
|
||||
.dl_xfer_cb = mhi_dtr_xfer_cb,
|
||||
.driver = {
|
||||
.name = "MHI_DTR",
|
||||
.owner = THIS_MODULE,
|
||||
}
|
||||
};
|
||||
|
||||
int __init mhi_dtr_init(void)
|
||||
{
|
||||
return mhi_driver_register(&mhi_dtr_driver);
|
||||
}
|
||||
1277
drivers/bus/mhi/core/mhi_init.c
Normal file
1277
drivers/bus/mhi/core/mhi_init.c
Normal file
File diff suppressed because it is too large
Load Diff
726
drivers/bus/mhi/core/mhi_internal.h
Normal file
726
drivers/bus/mhi/core/mhi_internal.h
Normal file
@@ -0,0 +1,726 @@
|
||||
/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef _MHI_INT_H
|
||||
#define _MHI_INT_H
|
||||
|
||||
extern struct bus_type mhi_bus_type;
|
||||
|
||||
/* MHI mmio register mapping */
|
||||
#define PCI_INVALID_READ(val) (val == U32_MAX)
|
||||
|
||||
#define MHIREGLEN (0x0)
|
||||
#define MHIREGLEN_MHIREGLEN_MASK (0xFFFFFFFF)
|
||||
#define MHIREGLEN_MHIREGLEN_SHIFT (0)
|
||||
|
||||
#define MHIVER (0x8)
|
||||
#define MHIVER_MHIVER_MASK (0xFFFFFFFF)
|
||||
#define MHIVER_MHIVER_SHIFT (0)
|
||||
|
||||
#define MHICFG (0x10)
|
||||
#define MHICFG_NHWER_MASK (0xFF000000)
|
||||
#define MHICFG_NHWER_SHIFT (24)
|
||||
#define MHICFG_NER_MASK (0xFF0000)
|
||||
#define MHICFG_NER_SHIFT (16)
|
||||
#define MHICFG_NHWCH_MASK (0xFF00)
|
||||
#define MHICFG_NHWCH_SHIFT (8)
|
||||
#define MHICFG_NCH_MASK (0xFF)
|
||||
#define MHICFG_NCH_SHIFT (0)
|
||||
|
||||
#define CHDBOFF (0x18)
|
||||
#define CHDBOFF_CHDBOFF_MASK (0xFFFFFFFF)
|
||||
#define CHDBOFF_CHDBOFF_SHIFT (0)
|
||||
|
||||
#define ERDBOFF (0x20)
|
||||
#define ERDBOFF_ERDBOFF_MASK (0xFFFFFFFF)
|
||||
#define ERDBOFF_ERDBOFF_SHIFT (0)
|
||||
|
||||
#define BHIOFF (0x28)
|
||||
#define BHIOFF_BHIOFF_MASK (0xFFFFFFFF)
|
||||
#define BHIOFF_BHIOFF_SHIFT (0)
|
||||
|
||||
#define DEBUGOFF (0x30)
|
||||
#define DEBUGOFF_DEBUGOFF_MASK (0xFFFFFFFF)
|
||||
#define DEBUGOFF_DEBUGOFF_SHIFT (0)
|
||||
|
||||
#define MHICTRL (0x38)
|
||||
#define MHICTRL_MHISTATE_MASK (0x0000FF00)
|
||||
#define MHICTRL_MHISTATE_SHIFT (8)
|
||||
#define MHICTRL_RESET_MASK (0x2)
|
||||
#define MHICTRL_RESET_SHIFT (1)
|
||||
|
||||
#define MHISTATUS (0x48)
|
||||
#define MHISTATUS_MHISTATE_MASK (0x0000FF00)
|
||||
#define MHISTATUS_MHISTATE_SHIFT (8)
|
||||
#define MHISTATUS_SYSERR_MASK (0x4)
|
||||
#define MHISTATUS_SYSERR_SHIFT (2)
|
||||
#define MHISTATUS_READY_MASK (0x1)
|
||||
#define MHISTATUS_READY_SHIFT (0)
|
||||
|
||||
#define CCABAP_LOWER (0x58)
|
||||
#define CCABAP_LOWER_CCABAP_LOWER_MASK (0xFFFFFFFF)
|
||||
#define CCABAP_LOWER_CCABAP_LOWER_SHIFT (0)
|
||||
|
||||
#define CCABAP_HIGHER (0x5C)
|
||||
#define CCABAP_HIGHER_CCABAP_HIGHER_MASK (0xFFFFFFFF)
|
||||
#define CCABAP_HIGHER_CCABAP_HIGHER_SHIFT (0)
|
||||
|
||||
#define ECABAP_LOWER (0x60)
|
||||
#define ECABAP_LOWER_ECABAP_LOWER_MASK (0xFFFFFFFF)
|
||||
#define ECABAP_LOWER_ECABAP_LOWER_SHIFT (0)
|
||||
|
||||
#define ECABAP_HIGHER (0x64)
|
||||
#define ECABAP_HIGHER_ECABAP_HIGHER_MASK (0xFFFFFFFF)
|
||||
#define ECABAP_HIGHER_ECABAP_HIGHER_SHIFT (0)
|
||||
|
||||
#define CRCBAP_LOWER (0x68)
|
||||
#define CRCBAP_LOWER_CRCBAP_LOWER_MASK (0xFFFFFFFF)
|
||||
#define CRCBAP_LOWER_CRCBAP_LOWER_SHIFT (0)
|
||||
|
||||
#define CRCBAP_HIGHER (0x6C)
|
||||
#define CRCBAP_HIGHER_CRCBAP_HIGHER_MASK (0xFFFFFFFF)
|
||||
#define CRCBAP_HIGHER_CRCBAP_HIGHER_SHIFT (0)
|
||||
|
||||
#define CRDB_LOWER (0x70)
|
||||
#define CRDB_LOWER_CRDB_LOWER_MASK (0xFFFFFFFF)
|
||||
#define CRDB_LOWER_CRDB_LOWER_SHIFT (0)
|
||||
|
||||
#define CRDB_HIGHER (0x74)
|
||||
#define CRDB_HIGHER_CRDB_HIGHER_MASK (0xFFFFFFFF)
|
||||
#define CRDB_HIGHER_CRDB_HIGHER_SHIFT (0)
|
||||
|
||||
#define MHICTRLBASE_LOWER (0x80)
|
||||
#define MHICTRLBASE_LOWER_MHICTRLBASE_LOWER_MASK (0xFFFFFFFF)
|
||||
#define MHICTRLBASE_LOWER_MHICTRLBASE_LOWER_SHIFT (0)
|
||||
|
||||
#define MHICTRLBASE_HIGHER (0x84)
|
||||
#define MHICTRLBASE_HIGHER_MHICTRLBASE_HIGHER_MASK (0xFFFFFFFF)
|
||||
#define MHICTRLBASE_HIGHER_MHICTRLBASE_HIGHER_SHIFT (0)
|
||||
|
||||
#define MHICTRLLIMIT_LOWER (0x88)
|
||||
#define MHICTRLLIMIT_LOWER_MHICTRLLIMIT_LOWER_MASK (0xFFFFFFFF)
|
||||
#define MHICTRLLIMIT_LOWER_MHICTRLLIMIT_LOWER_SHIFT (0)
|
||||
|
||||
#define MHICTRLLIMIT_HIGHER (0x8C)
|
||||
#define MHICTRLLIMIT_HIGHER_MHICTRLLIMIT_HIGHER_MASK (0xFFFFFFFF)
|
||||
#define MHICTRLLIMIT_HIGHER_MHICTRLLIMIT_HIGHER_SHIFT (0)
|
||||
|
||||
#define MHIDATABASE_LOWER (0x98)
|
||||
#define MHIDATABASE_LOWER_MHIDATABASE_LOWER_MASK (0xFFFFFFFF)
|
||||
#define MHIDATABASE_LOWER_MHIDATABASE_LOWER_SHIFT (0)
|
||||
|
||||
#define MHIDATABASE_HIGHER (0x9C)
|
||||
#define MHIDATABASE_HIGHER_MHIDATABASE_HIGHER_MASK (0xFFFFFFFF)
|
||||
#define MHIDATABASE_HIGHER_MHIDATABASE_HIGHER_SHIFT (0)
|
||||
|
||||
#define MHIDATALIMIT_LOWER (0xA0)
|
||||
#define MHIDATALIMIT_LOWER_MHIDATALIMIT_LOWER_MASK (0xFFFFFFFF)
|
||||
#define MHIDATALIMIT_LOWER_MHIDATALIMIT_LOWER_SHIFT (0)
|
||||
|
||||
#define MHIDATALIMIT_HIGHER (0xA4)
|
||||
#define MHIDATALIMIT_HIGHER_MHIDATALIMIT_HIGHER_MASK (0xFFFFFFFF)
|
||||
#define MHIDATALIMIT_HIGHER_MHIDATALIMIT_HIGHER_SHIFT (0)
|
||||
|
||||
/* MHI BHI offfsets */
|
||||
#define BHI_BHIVERSION_MINOR (0x00)
|
||||
#define BHI_BHIVERSION_MAJOR (0x04)
|
||||
#define BHI_IMGADDR_LOW (0x08)
|
||||
#define BHI_IMGADDR_HIGH (0x0C)
|
||||
#define BHI_IMGSIZE (0x10)
|
||||
#define BHI_RSVD1 (0x14)
|
||||
#define BHI_IMGTXDB (0x18)
|
||||
#define BHI_TXDB_SEQNUM_BMSK (0x3FFFFFFF)
|
||||
#define BHI_TXDB_SEQNUM_SHFT (0)
|
||||
#define BHI_RSVD2 (0x1C)
|
||||
#define BHI_INTVEC (0x20)
|
||||
#define BHI_RSVD3 (0x24)
|
||||
#define BHI_EXECENV (0x28)
|
||||
#define BHI_STATUS (0x2C)
|
||||
#define BHI_ERRCODE (0x30)
|
||||
#define BHI_ERRDBG1 (0x34)
|
||||
#define BHI_ERRDBG2 (0x38)
|
||||
#define BHI_ERRDBG3 (0x3C)
|
||||
#define BHI_SERIALNU (0x40)
|
||||
#define BHI_SBLANTIROLLVER (0x44)
|
||||
#define BHI_NUMSEG (0x48)
|
||||
#define BHI_MSMHWID(n) (0x4C + (0x4 * n))
|
||||
#define BHI_OEMPKHASH(n) (0x64 + (0x4 * n))
|
||||
#define BHI_RSVD5 (0xC4)
|
||||
#define BHI_STATUS_MASK (0xC0000000)
|
||||
#define BHI_STATUS_SHIFT (30)
|
||||
#define BHI_STATUS_ERROR (3)
|
||||
#define BHI_STATUS_SUCCESS (2)
|
||||
#define BHI_STATUS_RESET (0)
|
||||
|
||||
/* MHI BHIE offsets */
|
||||
#define BHIE_OFFSET (0x0124) /* BHIE register space offset from BHI base */
|
||||
#define BHIE_MSMSOCID_OFFS (BHIE_OFFSET + 0x0000)
|
||||
#define BHIE_TXVECADDR_LOW_OFFS (BHIE_OFFSET + 0x002C)
|
||||
#define BHIE_TXVECADDR_HIGH_OFFS (BHIE_OFFSET + 0x0030)
|
||||
#define BHIE_TXVECSIZE_OFFS (BHIE_OFFSET + 0x0034)
|
||||
#define BHIE_TXVECDB_OFFS (BHIE_OFFSET + 0x003C)
|
||||
#define BHIE_TXVECDB_SEQNUM_BMSK (0x3FFFFFFF)
|
||||
#define BHIE_TXVECDB_SEQNUM_SHFT (0)
|
||||
#define BHIE_TXVECSTATUS_OFFS (BHIE_OFFSET + 0x0044)
|
||||
#define BHIE_TXVECSTATUS_SEQNUM_BMSK (0x3FFFFFFF)
|
||||
#define BHIE_TXVECSTATUS_SEQNUM_SHFT (0)
|
||||
#define BHIE_TXVECSTATUS_STATUS_BMSK (0xC0000000)
|
||||
#define BHIE_TXVECSTATUS_STATUS_SHFT (30)
|
||||
#define BHIE_TXVECSTATUS_STATUS_RESET (0x00)
|
||||
#define BHIE_TXVECSTATUS_STATUS_XFER_COMPL (0x02)
|
||||
#define BHIE_TXVECSTATUS_STATUS_ERROR (0x03)
|
||||
#define BHIE_RXVECADDR_LOW_OFFS (BHIE_OFFSET + 0x0060)
|
||||
#define BHIE_RXVECADDR_HIGH_OFFS (BHIE_OFFSET + 0x0064)
|
||||
#define BHIE_RXVECSIZE_OFFS (BHIE_OFFSET + 0x0068)
|
||||
#define BHIE_RXVECDB_OFFS (BHIE_OFFSET + 0x0070)
|
||||
#define BHIE_RXVECDB_SEQNUM_BMSK (0x3FFFFFFF)
|
||||
#define BHIE_RXVECDB_SEQNUM_SHFT (0)
|
||||
#define BHIE_RXVECSTATUS_OFFS (BHIE_OFFSET + 0x0078)
|
||||
#define BHIE_RXVECSTATUS_SEQNUM_BMSK (0x3FFFFFFF)
|
||||
#define BHIE_RXVECSTATUS_SEQNUM_SHFT (0)
|
||||
#define BHIE_RXVECSTATUS_STATUS_BMSK (0xC0000000)
|
||||
#define BHIE_RXVECSTATUS_STATUS_SHFT (30)
|
||||
#define BHIE_RXVECSTATUS_STATUS_RESET (0x00)
|
||||
#define BHIE_RXVECSTATUS_STATUS_XFER_COMPL (0x02)
|
||||
#define BHIE_RXVECSTATUS_STATUS_ERROR (0x03)
|
||||
|
||||
struct __packed mhi_event_ctxt {
|
||||
u32 reserved : 8;
|
||||
u32 intmodc : 8;
|
||||
u32 intmodt : 16;
|
||||
u32 ertype;
|
||||
u32 msivec;
|
||||
u64 rbase;
|
||||
u64 rlen;
|
||||
u64 rp;
|
||||
u64 wp;
|
||||
};
|
||||
|
||||
struct __packed mhi_chan_ctxt {
|
||||
u32 chstate : 8;
|
||||
u32 brstmode : 2;
|
||||
u32 pollcfg : 6;
|
||||
u32 reserved : 16;
|
||||
u32 chtype;
|
||||
u32 erindex;
|
||||
u64 rbase;
|
||||
u64 rlen;
|
||||
u64 rp;
|
||||
u64 wp;
|
||||
};
|
||||
|
||||
struct __packed mhi_cmd_ctxt {
|
||||
u32 reserved0;
|
||||
u32 reserved1;
|
||||
u32 reserved2;
|
||||
u64 rbase;
|
||||
u64 rlen;
|
||||
u64 rp;
|
||||
u64 wp;
|
||||
};
|
||||
|
||||
struct __packed mhi_tre {
|
||||
u64 ptr;
|
||||
u32 dword[2];
|
||||
};
|
||||
|
||||
struct __packed bhi_vec_entry {
|
||||
u64 dma_addr;
|
||||
u64 size;
|
||||
};
|
||||
|
||||
/* no operation command */
|
||||
#define MHI_TRE_CMD_NOOP_PTR (0)
|
||||
#define MHI_TRE_CMD_NOOP_DWORD0 (0)
|
||||
#define MHI_TRE_CMD_NOOP_DWORD1 (1 << 16)
|
||||
|
||||
/* channel reset command */
|
||||
#define MHI_TRE_CMD_RESET_PTR (0)
|
||||
#define MHI_TRE_CMD_RESET_DWORD0 (0)
|
||||
#define MHI_TRE_CMD_RESET_DWORD1(chid) ((chid << 24) | (16 << 16))
|
||||
|
||||
/* channel stop command */
|
||||
#define MHI_TRE_CMD_STOP_PTR (0)
|
||||
#define MHI_TRE_CMD_STOP_DWORD0 (0)
|
||||
#define MHI_TRE_CMD_STOP_DWORD1(chid) ((chid << 24) | (17 << 16))
|
||||
|
||||
/* channel start command */
|
||||
#define MHI_TRE_CMD_START_PTR (0)
|
||||
#define MHI_TRE_CMD_START_DWORD0 (0)
|
||||
#define MHI_TRE_CMD_START_DWORD1(chid) ((chid << 24) | (18 << 16))
|
||||
|
||||
#define MHI_TRE_GET_CMD_CHID(tre) (((tre)->dword[1] >> 24) & 0xFF)
|
||||
|
||||
/* event descriptor macros */
|
||||
#define MHI_TRE_EV_PTR(ptr) (ptr)
|
||||
#define MHI_TRE_EV_DWORD0(code, len) ((code << 24) | len)
|
||||
#define MHI_TRE_EV_DWORD1(chid, type) ((chid << 24) | (type << 16))
|
||||
#define MHI_TRE_GET_EV_PTR(tre) ((tre)->ptr)
|
||||
#define MHI_TRE_GET_EV_CODE(tre) (((tre)->dword[0] >> 24) & 0xFF)
|
||||
#define MHI_TRE_GET_EV_LEN(tre) ((tre)->dword[0] & 0xFFFF)
|
||||
#define MHI_TRE_GET_EV_CHID(tre) (((tre)->dword[1] >> 24) & 0xFF)
|
||||
#define MHI_TRE_GET_EV_TYPE(tre) (((tre)->dword[1] >> 16) & 0xFF)
|
||||
#define MHI_TRE_GET_EV_STATE(tre) (((tre)->dword[0] >> 24) & 0xFF)
|
||||
#define MHI_TRE_GET_EV_EXECENV(tre) (((tre)->dword[0] >> 24) & 0xFF)
|
||||
|
||||
|
||||
/* transfer descriptor macros */
|
||||
#define MHI_TRE_DATA_PTR(ptr) (ptr)
|
||||
#define MHI_TRE_DATA_DWORD0(len) (len & MHI_MAX_MTU)
|
||||
#define MHI_TRE_DATA_DWORD1(bei, ieot, ieob, chain) ((2 << 16) | (bei << 10) \
|
||||
| (ieot << 9) | (ieob << 8) | chain)
|
||||
|
||||
enum MHI_CMD {
|
||||
MHI_CMD_NOOP = 0x0,
|
||||
MHI_CMD_RESET_CHAN = 0x1,
|
||||
MHI_CMD_STOP_CHAN = 0x2,
|
||||
MHI_CMD_START_CHAN = 0x3,
|
||||
MHI_CMD_RESUME_CHAN = 0x4,
|
||||
};
|
||||
|
||||
enum MHI_PKT_TYPE {
|
||||
MHI_PKT_TYPE_INVALID = 0x0,
|
||||
MHI_PKT_TYPE_NOOP_CMD = 0x1,
|
||||
MHI_PKT_TYPE_TRANSFER = 0x2,
|
||||
MHI_PKT_TYPE_RESET_CHAN_CMD = 0x10,
|
||||
MHI_PKT_TYPE_STOP_CHAN_CMD = 0x11,
|
||||
MHI_PKT_TYPE_START_CHAN_CMD = 0x12,
|
||||
MHI_PKT_TYPE_STATE_CHANGE_EVENT = 0x20,
|
||||
MHI_PKT_TYPE_CMD_COMPLETION_EVENT = 0x21,
|
||||
MHI_PKT_TYPE_TX_EVENT = 0x22,
|
||||
MHI_PKT_TYPE_EE_EVENT = 0x40,
|
||||
MHI_PKT_TYPE_STALE_EVENT, /* internal event */
|
||||
};
|
||||
|
||||
/* MHI transfer completion events */
|
||||
enum MHI_EV_CCS {
|
||||
MHI_EV_CC_INVALID = 0x0,
|
||||
MHI_EV_CC_SUCCESS = 0x1,
|
||||
MHI_EV_CC_EOT = 0x2,
|
||||
MHI_EV_CC_OVERFLOW = 0x3,
|
||||
MHI_EV_CC_EOB = 0x4,
|
||||
MHI_EV_CC_OOB = 0x5,
|
||||
MHI_EV_CC_DB_MODE = 0x6,
|
||||
MHI_EV_CC_UNDEFINED_ERR = 0x10,
|
||||
MHI_EV_CC_BAD_TRE = 0x11,
|
||||
};
|
||||
|
||||
enum MHI_CH_STATE {
|
||||
MHI_CH_STATE_DISABLED = 0x0,
|
||||
MHI_CH_STATE_ENABLED = 0x1,
|
||||
MHI_CH_STATE_RUNNING = 0x2,
|
||||
MHI_CH_STATE_SUSPENDED = 0x3,
|
||||
MHI_CH_STATE_STOP = 0x4,
|
||||
MHI_CH_STATE_ERROR = 0x5,
|
||||
};
|
||||
|
||||
enum MHI_CH_CFG {
|
||||
MHI_CH_CFG_CHAN_ID = 0,
|
||||
MHI_CH_CFG_ELEMENTS = 1,
|
||||
MHI_CH_CFG_ER_INDEX = 2,
|
||||
MHI_CH_CFG_DIRECTION = 3,
|
||||
MHI_CH_CFG_BRSTMODE = 4,
|
||||
MHI_CH_CFG_POLLCFG = 5,
|
||||
MHI_CH_CFG_EE = 6,
|
||||
MHI_CH_CFG_XFER_TYPE = 7,
|
||||
MHI_CH_CFG_BITCFG = 8,
|
||||
MHI_CH_CFG_MAX
|
||||
};
|
||||
|
||||
#define MHI_CH_CFG_BIT_LPM_NOTIFY BIT(0) /* require LPM notification */
|
||||
#define MHI_CH_CFG_BIT_OFFLOAD_CH BIT(1) /* satellite mhi devices */
|
||||
|
||||
enum MHI_EV_CFG {
|
||||
MHI_EV_CFG_ELEMENTS = 0,
|
||||
MHI_EV_CFG_INTMOD = 1,
|
||||
MHI_EV_CFG_MSI = 2,
|
||||
MHI_EV_CFG_CHAN = 3,
|
||||
MHI_EV_CFG_PRIORITY = 4,
|
||||
MHI_EV_CFG_BRSTMODE = 5,
|
||||
MHI_EV_CFG_BITCFG = 6,
|
||||
MHI_EV_CFG_MAX
|
||||
};
|
||||
|
||||
#define MHI_EV_CFG_BIT_HW_EV BIT(0) /* hw event ring */
|
||||
#define MHI_EV_CFG_BIT_CL_MANAGE BIT(1) /* client manages the event ring */
|
||||
#define MHI_EV_CFG_BIT_OFFLOAD_EV BIT(2) /* satellite driver manges it */
|
||||
#define MHI_EV_CFG_BIT_CTRL_EV BIT(3) /* ctrl event ring */
|
||||
|
||||
enum MHI_BRSTMODE {
|
||||
MHI_BRSTMODE_DISABLE = 0x2,
|
||||
MHI_BRSTMODE_ENABLE = 0x3,
|
||||
};
|
||||
|
||||
#define MHI_INVALID_BRSTMODE(mode) (mode != MHI_BRSTMODE_DISABLE && \
|
||||
mode != MHI_BRSTMODE_ENABLE)
|
||||
|
||||
enum MHI_EE {
|
||||
MHI_EE_PBL = 0x0,
|
||||
MHI_EE_SBL = 0x1,
|
||||
MHI_EE_AMSS = 0x2,
|
||||
MHI_EE_BHIE = 0x3,
|
||||
MHI_EE_RDDM = 0x4,
|
||||
MHI_EE_PTHRU = 0x5,
|
||||
MHI_EE_EDL = 0x6,
|
||||
MHI_EE_MAX_SUPPORTED = MHI_EE_EDL,
|
||||
MHI_EE_DISABLE_TRANSITION, /* local EE, not related to mhi spec */
|
||||
MHI_EE_MAX,
|
||||
};
|
||||
|
||||
extern const char * const mhi_ee_str[MHI_EE_MAX];
|
||||
#define TO_MHI_EXEC_STR(ee) (((ee) >= MHI_EE_MAX) ? \
|
||||
"INVALID_EE" : mhi_ee_str[ee])
|
||||
|
||||
#define MHI_IN_PBL(ee) (ee == MHI_EE_PBL || ee == MHI_EE_PTHRU || \
|
||||
ee == MHI_EE_EDL)
|
||||
|
||||
enum MHI_ST_TRANSITION {
|
||||
MHI_ST_TRANSITION_PBL,
|
||||
MHI_ST_TRANSITION_READY,
|
||||
MHI_ST_TRANSITION_SBL,
|
||||
MHI_ST_TRANSITION_AMSS,
|
||||
MHI_ST_TRANSITION_BHIE,
|
||||
MHI_ST_TRANSITION_MAX,
|
||||
};
|
||||
|
||||
extern const char * const mhi_state_tran_str[MHI_ST_TRANSITION_MAX];
|
||||
#define TO_MHI_STATE_TRANS_STR(state) (((state) >= MHI_ST_TRANSITION_MAX) ? \
|
||||
"INVALID_STATE" : mhi_state_tran_str[state])
|
||||
|
||||
enum MHI_STATE {
|
||||
MHI_STATE_RESET = 0x0,
|
||||
MHI_STATE_READY = 0x1,
|
||||
MHI_STATE_M0 = 0x2,
|
||||
MHI_STATE_M1 = 0x3,
|
||||
MHI_STATE_M2 = 0x4,
|
||||
MHI_STATE_M3 = 0x5,
|
||||
MHI_STATE_BHI = 0x7,
|
||||
MHI_STATE_SYS_ERR = 0xFF,
|
||||
MHI_STATE_MAX,
|
||||
};
|
||||
|
||||
extern const char * const mhi_state_str[MHI_STATE_MAX];
|
||||
#define TO_MHI_STATE_STR(state) ((state >= MHI_STATE_MAX || \
|
||||
!mhi_state_str[state]) ? \
|
||||
"INVALID_STATE" : mhi_state_str[state])
|
||||
|
||||
/* internal power states */
|
||||
enum MHI_PM_STATE {
|
||||
MHI_PM_DISABLE = BIT(0), /* MHI is not enabled */
|
||||
MHI_PM_POR = BIT(1), /* reset state */
|
||||
MHI_PM_M0 = BIT(2),
|
||||
MHI_PM_M1 = BIT(3),
|
||||
MHI_PM_M1_M2_TRANSITION = BIT(4), /* register access not allowed */
|
||||
MHI_PM_M2 = BIT(5),
|
||||
MHI_PM_M3_ENTER = BIT(6),
|
||||
MHI_PM_M3 = BIT(7),
|
||||
MHI_PM_M3_EXIT = BIT(8),
|
||||
MHI_PM_FW_DL_ERR = BIT(9), /* firmware download failure state */
|
||||
MHI_PM_SYS_ERR_DETECT = BIT(10),
|
||||
MHI_PM_SYS_ERR_PROCESS = BIT(11),
|
||||
MHI_PM_SHUTDOWN_PROCESS = BIT(12),
|
||||
MHI_PM_LD_ERR_FATAL_DETECT = BIT(13), /* link not accessible */
|
||||
};
|
||||
|
||||
#define MHI_REG_ACCESS_VALID(pm_state) ((pm_state & (MHI_PM_POR | MHI_PM_M0 | \
|
||||
MHI_PM_M1 | MHI_PM_M2 | MHI_PM_M3_ENTER | MHI_PM_M3_EXIT | \
|
||||
MHI_PM_SYS_ERR_DETECT | MHI_PM_SYS_ERR_PROCESS | \
|
||||
MHI_PM_SHUTDOWN_PROCESS | MHI_PM_FW_DL_ERR)))
|
||||
#define MHI_PM_IN_ERROR_STATE(pm_state) (pm_state >= MHI_PM_FW_DL_ERR)
|
||||
#define MHI_PM_IN_FATAL_STATE(pm_state) (pm_state == MHI_PM_LD_ERR_FATAL_DETECT)
|
||||
#define MHI_DB_ACCESS_VALID(pm_state) (pm_state & (MHI_PM_M0 | MHI_PM_M1))
|
||||
#define MHI_WAKE_DB_ACCESS_VALID(pm_state) (pm_state & (MHI_PM_M0 | \
|
||||
MHI_PM_M1 | MHI_PM_M2))
|
||||
#define MHI_EVENT_ACCESS_INVALID(pm_state) (pm_state == MHI_PM_DISABLE || \
|
||||
MHI_PM_IN_ERROR_STATE(pm_state))
|
||||
#define MHI_PM_IN_SUSPEND_STATE(pm_state) (pm_state & \
|
||||
(MHI_PM_M3_ENTER | MHI_PM_M3))
|
||||
|
||||
/* accepted buffer type for the channel */
|
||||
enum MHI_XFER_TYPE {
|
||||
MHI_XFER_BUFFER,
|
||||
MHI_XFER_SKB,
|
||||
MHI_XFER_SCLIST,
|
||||
MHI_XFER_NOP, /* CPU offload channel, host does not accept transfer */
|
||||
};
|
||||
|
||||
#define NR_OF_CMD_RINGS (1)
|
||||
#define CMD_EL_PER_RING (128)
|
||||
#define PRIMARY_CMD_RING (0)
|
||||
#define MHI_DEV_WAKE_DB (127)
|
||||
#define MHI_M2_DEBOUNCE_TMR_US (10000)
|
||||
#define MHI_MAX_MTU (0xffff)
|
||||
|
||||
enum MHI_ER_TYPE {
|
||||
MHI_ER_TYPE_INVALID = 0x0,
|
||||
MHI_ER_TYPE_VALID = 0x1,
|
||||
};
|
||||
|
||||
struct db_cfg {
|
||||
bool db_mode;
|
||||
u32 pollcfg;
|
||||
enum MHI_BRSTMODE brstmode;
|
||||
dma_addr_t db_val;
|
||||
void (*process_db)(struct mhi_controller *mhi_cntrl,
|
||||
struct db_cfg *db_cfg, void __iomem *io_addr,
|
||||
dma_addr_t db_val);
|
||||
};
|
||||
|
||||
struct mhi_pm_transitions {
|
||||
enum MHI_PM_STATE from_state;
|
||||
u32 to_states;
|
||||
};
|
||||
|
||||
struct state_transition {
|
||||
struct list_head node;
|
||||
enum MHI_ST_TRANSITION state;
|
||||
};
|
||||
|
||||
struct mhi_ctxt {
|
||||
struct mhi_event_ctxt *er_ctxt;
|
||||
struct mhi_chan_ctxt *chan_ctxt;
|
||||
struct mhi_cmd_ctxt *cmd_ctxt;
|
||||
dma_addr_t er_ctxt_addr;
|
||||
dma_addr_t chan_ctxt_addr;
|
||||
dma_addr_t cmd_ctxt_addr;
|
||||
};
|
||||
|
||||
struct mhi_ring {
|
||||
dma_addr_t dma_handle;
|
||||
dma_addr_t iommu_base;
|
||||
u64 *ctxt_wp; /* point to ctxt wp */
|
||||
void *pre_aligned;
|
||||
void *base;
|
||||
void *rp;
|
||||
void *wp;
|
||||
size_t el_size;
|
||||
size_t len;
|
||||
size_t elements;
|
||||
size_t alloc_size;
|
||||
void __iomem *db_addr;
|
||||
};
|
||||
|
||||
struct mhi_cmd {
|
||||
struct mhi_ring ring;
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
struct mhi_buf_info {
|
||||
dma_addr_t p_addr;
|
||||
void *v_addr;
|
||||
void *wp;
|
||||
size_t len;
|
||||
void *cb_buf;
|
||||
enum dma_data_direction dir;
|
||||
};
|
||||
|
||||
struct mhi_event {
|
||||
u32 er_index;
|
||||
u32 intmod;
|
||||
u32 msi;
|
||||
int chan; /* this event ring is dedicated to a channel */
|
||||
u32 priority;
|
||||
struct mhi_ring ring;
|
||||
struct db_cfg db_cfg;
|
||||
bool hw_ring;
|
||||
bool cl_manage;
|
||||
bool offload_ev; /* managed by a device driver */
|
||||
bool ctrl_ev;
|
||||
spinlock_t lock;
|
||||
struct mhi_chan *mhi_chan; /* dedicated to channel */
|
||||
struct tasklet_struct task;
|
||||
struct mhi_controller *mhi_cntrl;
|
||||
};
|
||||
|
||||
struct mhi_chan {
|
||||
u32 chan;
|
||||
const char *name;
|
||||
/*
|
||||
* important, when consuming increment tre_ring first, when releasing
|
||||
* decrement buf_ring first. If tre_ring has space, buf_ring
|
||||
* guranteed to have space so we do not need to check both rings.
|
||||
*/
|
||||
struct mhi_ring buf_ring;
|
||||
struct mhi_ring tre_ring;
|
||||
u32 er_index;
|
||||
u32 intmod;
|
||||
u32 tiocm;
|
||||
enum dma_data_direction dir;
|
||||
struct db_cfg db_cfg;
|
||||
enum MHI_EE ee;
|
||||
enum MHI_XFER_TYPE xfer_type;
|
||||
enum MHI_CH_STATE ch_state;
|
||||
enum MHI_EV_CCS ccs;
|
||||
bool lpm_notify;
|
||||
bool configured;
|
||||
bool offload_ch;
|
||||
/* functions that generate the transfer ring elements */
|
||||
int (*gen_tre)(struct mhi_controller *, struct mhi_chan *, void *,
|
||||
void *, size_t, enum MHI_FLAGS);
|
||||
int (*queue_xfer)(struct mhi_device *, struct mhi_chan *, void *,
|
||||
size_t, enum MHI_FLAGS);
|
||||
/* xfer call back */
|
||||
struct mhi_device *mhi_dev;
|
||||
void (*xfer_cb)(struct mhi_device *, struct mhi_result *);
|
||||
struct mutex mutex;
|
||||
struct completion completion;
|
||||
rwlock_t lock;
|
||||
struct list_head node;
|
||||
};
|
||||
|
||||
struct mhi_bus {
|
||||
struct list_head controller_list;
|
||||
struct mutex lock;
|
||||
struct dentry *dentry;
|
||||
};
|
||||
|
||||
/* default MHI timeout */
|
||||
#define MHI_TIMEOUT_MS (1000)
|
||||
extern struct mhi_bus mhi_bus;
|
||||
|
||||
/* debug fs related functions */
|
||||
int mhi_debugfs_mhi_chan_show(struct seq_file *m, void *d);
|
||||
int mhi_debugfs_mhi_event_show(struct seq_file *m, void *d);
|
||||
int mhi_debugfs_mhi_states_show(struct seq_file *m, void *d);
|
||||
int mhi_debugfs_trigger_reset(void *data, u64 val);
|
||||
|
||||
void mhi_deinit_debugfs(struct mhi_controller *mhi_cntrl);
|
||||
void mhi_init_debugfs(struct mhi_controller *mhi_cntrl);
|
||||
|
||||
/* power management apis */
|
||||
enum MHI_PM_STATE __must_check mhi_tryset_pm_state(
|
||||
struct mhi_controller *mhi_cntrl,
|
||||
enum MHI_PM_STATE state);
|
||||
const char *to_mhi_pm_state_str(enum MHI_PM_STATE state);
|
||||
void mhi_reset_chan(struct mhi_controller *mhi_cntrl,
|
||||
struct mhi_chan *mhi_chan);
|
||||
enum MHI_EE mhi_get_exec_env(struct mhi_controller *mhi_cntrl);
|
||||
enum MHI_STATE mhi_get_m_state(struct mhi_controller *mhi_cntrl);
|
||||
int mhi_queue_state_transition(struct mhi_controller *mhi_cntrl,
|
||||
enum MHI_ST_TRANSITION state);
|
||||
void mhi_pm_st_worker(struct work_struct *work);
|
||||
void mhi_fw_load_worker(struct work_struct *work);
|
||||
void mhi_pm_m1_worker(struct work_struct *work);
|
||||
void mhi_pm_sys_err_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);
|
||||
void mhi_notify(struct mhi_device *mhi_dev, enum MHI_CB cb_reason);
|
||||
|
||||
/* queue transfer buffer */
|
||||
int mhi_gen_tre(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan,
|
||||
void *buf, void *cb, size_t buf_len, enum MHI_FLAGS flags);
|
||||
int mhi_queue_buf(struct mhi_device *mhi_dev, struct mhi_chan *mhi_chan,
|
||||
void *buf, size_t len, enum MHI_FLAGS mflags);
|
||||
int mhi_queue_skb(struct mhi_device *mhi_dev, struct mhi_chan *mhi_chan,
|
||||
void *buf, size_t len, enum MHI_FLAGS mflags);
|
||||
int mhi_queue_sclist(struct mhi_device *mhi_dev, struct mhi_chan *mhi_chan,
|
||||
void *buf, size_t len, enum MHI_FLAGS mflags);
|
||||
int mhi_queue_nop(struct mhi_device *mhi_dev, struct mhi_chan *mhi_chan,
|
||||
void *buf, size_t len, enum MHI_FLAGS mflags);
|
||||
|
||||
|
||||
/* register access methods */
|
||||
void mhi_db_brstmode(struct mhi_controller *mhi_cntrl, struct db_cfg *db_cfg,
|
||||
void __iomem *db_addr, dma_addr_t wp);
|
||||
void mhi_db_brstmode_disable(struct mhi_controller *mhi_cntrl,
|
||||
struct db_cfg *db_mode, void __iomem *db_addr,
|
||||
dma_addr_t wp);
|
||||
int __must_check mhi_read_reg(struct mhi_controller *mhi_cntrl,
|
||||
void __iomem *base, u32 offset, u32 *out);
|
||||
int __must_check mhi_read_reg_field(struct mhi_controller *mhi_cntrl,
|
||||
void __iomem *base, u32 offset, u32 mask,
|
||||
u32 shift, u32 *out);
|
||||
void mhi_write_reg(struct mhi_controller *mhi_cntrl, void __iomem *base,
|
||||
u32 offset, u32 val);
|
||||
void mhi_write_reg_field(struct mhi_controller *mhi_cntrl, void __iomem *base,
|
||||
u32 offset, u32 mask, u32 shift, u32 val);
|
||||
void mhi_ring_er_db(struct mhi_event *mhi_event);
|
||||
void mhi_write_db(struct mhi_controller *mhi_cntrl, void __iomem *db_addr,
|
||||
dma_addr_t wp);
|
||||
void mhi_ring_cmd_db(struct mhi_controller *mhi_cntrl, struct mhi_cmd *mhi_cmd);
|
||||
void mhi_ring_chan_db(struct mhi_controller *mhi_cntrl,
|
||||
struct mhi_chan *mhi_chan);
|
||||
|
||||
/* memory allocation methods */
|
||||
static inline void *mhi_alloc_coherent(struct mhi_controller *mhi_cntrl,
|
||||
size_t size,
|
||||
dma_addr_t *dma_handle,
|
||||
gfp_t gfp)
|
||||
{
|
||||
void *buf = dma_zalloc_coherent(mhi_cntrl->dev, size, dma_handle, gfp);
|
||||
|
||||
if (buf)
|
||||
atomic_add(size, &mhi_cntrl->alloc_size);
|
||||
|
||||
return buf;
|
||||
}
|
||||
static inline void mhi_free_coherent(struct mhi_controller *mhi_cntrl,
|
||||
size_t size,
|
||||
void *vaddr,
|
||||
dma_addr_t dma_handle)
|
||||
{
|
||||
atomic_sub(size, &mhi_cntrl->alloc_size);
|
||||
dma_free_coherent(mhi_cntrl->dev, size, vaddr, dma_handle);
|
||||
}
|
||||
struct mhi_device *mhi_alloc_device(struct mhi_controller *mhi_cntrl);
|
||||
static inline void mhi_dealloc_device(struct mhi_controller *mhi_cntrl,
|
||||
struct mhi_device *mhi_dev)
|
||||
{
|
||||
kfree(mhi_dev);
|
||||
}
|
||||
int mhi_destroy_device(struct device *dev, void *data);
|
||||
void mhi_create_devices(struct mhi_controller *mhi_cntrl);
|
||||
int mhi_alloc_bhie_table(struct mhi_controller *mhi_cntrl,
|
||||
struct image_info **image_info, size_t alloc_size);
|
||||
void mhi_free_bhie_table(struct mhi_controller *mhi_cntrl,
|
||||
struct image_info *image_info);
|
||||
|
||||
/* initialization methods */
|
||||
int mhi_init_chan_ctxt(struct mhi_controller *mhi_cntrl,
|
||||
struct mhi_chan *mhi_chan);
|
||||
void mhi_deinit_chan_ctxt(struct mhi_controller *mhi_cntrl,
|
||||
struct mhi_chan *mhi_chan);
|
||||
int mhi_init_mmio(struct mhi_controller *mhi_cntrl);
|
||||
int mhi_init_dev_ctxt(struct mhi_controller *mhi_cntrl);
|
||||
void mhi_deinit_dev_ctxt(struct mhi_controller *mhi_cntrl);
|
||||
int mhi_init_irq_setup(struct mhi_controller *mhi_cntrl);
|
||||
void mhi_deinit_free_irq(struct mhi_controller *mhi_cntrl);
|
||||
int mhi_dtr_init(void);
|
||||
|
||||
/* isr handlers */
|
||||
irqreturn_t mhi_msi_handlr(int irq_number, void *dev);
|
||||
irqreturn_t mhi_intvec_threaded_handlr(int irq_number, void *dev);
|
||||
irqreturn_t mhi_intvec_handlr(int irq_number, void *dev);
|
||||
void mhi_ev_task(unsigned long data);
|
||||
|
||||
#ifdef CONFIG_MHI_DEBUG
|
||||
|
||||
#define MHI_ASSERT(cond, msg) do { \
|
||||
if (cond) \
|
||||
panic(msg); \
|
||||
} while (0)
|
||||
|
||||
#else
|
||||
|
||||
#define MHI_ASSERT(cond, msg) do { \
|
||||
if (cond) { \
|
||||
MHI_ERR(msg); \
|
||||
WARN_ON(cond); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* _MHI_INT_H */
|
||||
1406
drivers/bus/mhi/core/mhi_main.c
Normal file
1406
drivers/bus/mhi/core/mhi_main.c
Normal file
File diff suppressed because it is too large
Load Diff
1119
drivers/bus/mhi/core/mhi_pm.c
Normal file
1119
drivers/bus/mhi/core/mhi_pm.c
Normal file
File diff suppressed because it is too large
Load Diff
728
include/linux/mhi.h
Normal file
728
include/linux/mhi.h
Normal file
@@ -0,0 +1,728 @@
|
||||
/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
#ifndef _MHI_H_
|
||||
#define _MHI_H_
|
||||
|
||||
struct mhi_chan;
|
||||
struct mhi_event;
|
||||
struct mhi_ctxt;
|
||||
struct mhi_cmd;
|
||||
struct image_info;
|
||||
struct bhi_vec_entry;
|
||||
|
||||
/**
|
||||
* enum MHI_CB - MHI callback
|
||||
* @MHI_CB_IDLE: MHI entered idle state
|
||||
* @MHI_CB_PENDING_DATA: New data available for client to process
|
||||
* @MHI_CB_LPM_ENTER: MHI host entered low power mode
|
||||
* @MHI_CB_LPM_EXIT: MHI host about to exit low power mode
|
||||
* @MHI_CB_EE_RDDM: MHI device entered RDDM execution enviornment
|
||||
*/
|
||||
enum MHI_CB {
|
||||
MHI_CB_IDLE,
|
||||
MHI_CB_PENDING_DATA,
|
||||
MHI_CB_LPM_ENTER,
|
||||
MHI_CB_LPM_EXIT,
|
||||
MHI_CB_EE_RDDM,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum MHI_DEBUG_LEVL - various debugging level
|
||||
*/
|
||||
enum MHI_DEBUG_LEVEL {
|
||||
MHI_MSG_LVL_VERBOSE,
|
||||
MHI_MSG_LVL_INFO,
|
||||
MHI_MSG_LVL_ERROR,
|
||||
MHI_MSG_LVL_CRITICAL,
|
||||
MHI_MSG_LVL_MASK_ALL,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum MHI_FLAGS - Transfer flags
|
||||
* @MHI_EOB: End of buffer for bulk transfer
|
||||
* @MHI_EOT: End of transfer
|
||||
* @MHI_CHAIN: Linked transfer
|
||||
*/
|
||||
enum MHI_FLAGS {
|
||||
MHI_EOB,
|
||||
MHI_EOT,
|
||||
MHI_CHAIN,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct image_info - firmware and rddm table table
|
||||
* @mhi_buf - Contain device firmware and rddm table
|
||||
* @entries - # of entries in table
|
||||
*/
|
||||
struct image_info {
|
||||
struct mhi_buf *mhi_buf;
|
||||
struct bhi_vec_entry *bhi_vec;
|
||||
u32 entries;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mhi_controller - Master controller structure for external modem
|
||||
* @dev: Device associated with this controller
|
||||
* @of_node: DT that has MHI configuration information
|
||||
* @regs: Points to base of MHI MMIO register space
|
||||
* @bhi: Points to base of MHI BHI register space
|
||||
* @wake_db: MHI WAKE doorbell register address
|
||||
* @dev_id: PCIe device id of the external device
|
||||
* @domain: PCIe domain the device connected to
|
||||
* @bus: PCIe bus the device assigned to
|
||||
* @slot: PCIe slot for the modem
|
||||
* @iova_start: IOMMU starting address for data
|
||||
* @iova_stop: IOMMU stop address for data
|
||||
* @fw_image: Firmware image name for normal booting
|
||||
* @edl_image: Firmware image name for emergency download mode
|
||||
* @fbc_download: MHI host needs to do complete image transfer
|
||||
* @rddm_size: RAM dump size that host should allocate for debugging purpose
|
||||
* @sbl_size: SBL image size
|
||||
* @seg_len: BHIe vector size
|
||||
* @fbc_image: Points to firmware image buffer
|
||||
* @rddm_image: Points to RAM dump buffer
|
||||
* @max_chan: Maximum number of channels controller support
|
||||
* @mhi_chan: Points to channel configuration table
|
||||
* @lpm_chans: List of channels that require LPM notifications
|
||||
* @total_ev_rings: Total # of event rings allocated
|
||||
* @hw_ev_rings: Number of hardware event rings
|
||||
* @sw_ev_rings: Number of software event rings
|
||||
* @msi_required: Number of msi required to operate
|
||||
* @msi_allocated: Number of msi allocated by bus master
|
||||
* @irq: base irq # to request
|
||||
* @mhi_event: MHI event ring configurations table
|
||||
* @mhi_cmd: MHI command ring configurations table
|
||||
* @mhi_ctxt: MHI device context, shared memory between host and device
|
||||
* @timeout_ms: Timeout in ms for state transitions
|
||||
* @pm_state: Power management state
|
||||
* @ee: MHI device execution environment
|
||||
* @dev_state: MHI STATE
|
||||
* @status_cb: CB function to notify various power states to but master
|
||||
* @link_status: Query link status in case of abnormal value read from device
|
||||
* @runtime_get: Async runtime resume function
|
||||
* @runtimet_put: Release votes
|
||||
* @priv_data: Points to bus master's private data
|
||||
*/
|
||||
struct mhi_controller {
|
||||
struct list_head node;
|
||||
|
||||
/* device node for iommu ops */
|
||||
struct device *dev;
|
||||
struct device_node *of_node;
|
||||
|
||||
/* mmio base */
|
||||
void __iomem *regs;
|
||||
void __iomem *bhi;
|
||||
void __iomem *wake_db;
|
||||
|
||||
/* device topology */
|
||||
u32 dev_id;
|
||||
u32 domain;
|
||||
u32 bus;
|
||||
u32 slot;
|
||||
|
||||
/* addressing window */
|
||||
dma_addr_t iova_start;
|
||||
dma_addr_t iova_stop;
|
||||
|
||||
/* fw images */
|
||||
const char *fw_image;
|
||||
const char *edl_image;
|
||||
|
||||
/* mhi host manages downloading entire fbc images */
|
||||
bool fbc_download;
|
||||
size_t rddm_size;
|
||||
size_t sbl_size;
|
||||
size_t seg_len;
|
||||
u32 session_id;
|
||||
u32 sequence_id;
|
||||
struct image_info *fbc_image;
|
||||
struct image_info *rddm_image;
|
||||
|
||||
/* physical channel config data */
|
||||
u32 max_chan;
|
||||
struct mhi_chan *mhi_chan;
|
||||
struct list_head lpm_chans; /* these chan require lpm notification */
|
||||
|
||||
/* physical event config data */
|
||||
u32 total_ev_rings;
|
||||
u32 hw_ev_rings;
|
||||
u32 sw_ev_rings;
|
||||
u32 msi_required;
|
||||
u32 msi_allocated;
|
||||
int *irq; /* interrupt table */
|
||||
struct mhi_event *mhi_event;
|
||||
|
||||
/* cmd rings */
|
||||
struct mhi_cmd *mhi_cmd;
|
||||
|
||||
/* mhi context (shared with device) */
|
||||
struct mhi_ctxt *mhi_ctxt;
|
||||
|
||||
u32 timeout_ms;
|
||||
|
||||
/* caller should grab pm_mutex for suspend/resume operations */
|
||||
struct mutex pm_mutex;
|
||||
bool pre_init;
|
||||
rwlock_t pm_lock;
|
||||
u32 pm_state;
|
||||
u32 ee;
|
||||
u32 dev_state;
|
||||
bool wake_set;
|
||||
atomic_t dev_wake;
|
||||
atomic_t alloc_size;
|
||||
struct list_head transition_list;
|
||||
spinlock_t transition_lock;
|
||||
spinlock_t wlock;
|
||||
|
||||
/* debug counters */
|
||||
u32 M0, M1, M2, M3;
|
||||
|
||||
/* worker for different state transitions */
|
||||
struct work_struct st_worker;
|
||||
struct work_struct fw_worker;
|
||||
struct work_struct m1_worker;
|
||||
struct work_struct syserr_worker;
|
||||
wait_queue_head_t state_event;
|
||||
|
||||
/* shadow functions */
|
||||
void (*status_cb)(struct mhi_controller *, void *, enum MHI_CB);
|
||||
int (*link_status)(struct mhi_controller *, void *);
|
||||
void (*wake_get)(struct mhi_controller *, bool);
|
||||
void (*wake_put)(struct mhi_controller *, bool);
|
||||
int (*runtime_get)(struct mhi_controller *, void *);
|
||||
void (*runtime_put)(struct mhi_controller *, void *);
|
||||
|
||||
/* channel to control DTR messaging */
|
||||
struct mhi_device *dtr_dev;
|
||||
|
||||
/* kernel log level */
|
||||
enum MHI_DEBUG_LEVEL klog_lvl;
|
||||
|
||||
/* private log level controller driver to set */
|
||||
enum MHI_DEBUG_LEVEL log_lvl;
|
||||
|
||||
/* controller specific data */
|
||||
void *priv_data;
|
||||
void *log_buf;
|
||||
struct dentry *dentry;
|
||||
struct dentry *parent;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mhi_device - mhi device structure associated bind to channel
|
||||
* @dev: Device associated with the channels
|
||||
* @mtu: Maximum # of bytes controller support
|
||||
* @ul_chan_id: MHI channel id for UL transfer
|
||||
* @dl_chan_id: MHI channel id for DL transfer
|
||||
* @priv: Driver private data
|
||||
*/
|
||||
struct mhi_device {
|
||||
struct device dev;
|
||||
u32 dev_id;
|
||||
u32 domain;
|
||||
u32 bus;
|
||||
u32 slot;
|
||||
size_t mtu;
|
||||
int ul_chan_id;
|
||||
int dl_chan_id;
|
||||
int ul_event_id;
|
||||
int dl_event_id;
|
||||
const struct mhi_device_id *id;
|
||||
const char *chan_name;
|
||||
struct mhi_controller *mhi_cntrl;
|
||||
struct mhi_chan *ul_chan;
|
||||
struct mhi_chan *dl_chan;
|
||||
atomic_t dev_wake;
|
||||
void *priv_data;
|
||||
int (*ul_xfer)(struct mhi_device *, struct mhi_chan *, void *,
|
||||
size_t, enum MHI_FLAGS);
|
||||
int (*dl_xfer)(struct mhi_device *, struct mhi_chan *, void *,
|
||||
size_t, enum MHI_FLAGS);
|
||||
void (*status_cb)(struct mhi_device *, enum MHI_CB);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mhi_result - Completed buffer information
|
||||
* @buf_addr: Address of data buffer
|
||||
* @dir: Channel direction
|
||||
* @bytes_xfer: # of bytes transferred
|
||||
* @transaction_status: Status of last trasnferred
|
||||
*/
|
||||
struct mhi_result {
|
||||
void *buf_addr;
|
||||
enum dma_data_direction dir;
|
||||
size_t bytes_xferd;
|
||||
int transaction_status;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mhi_buf - Describes the buffer
|
||||
* @buf: cpu address for the buffer
|
||||
* @phys_addr: physical address of the buffer
|
||||
* @dma_addr: iommu address for the buffer
|
||||
* @len: # of bytes
|
||||
* @name: Buffer label, for offload channel configurations name must be:
|
||||
* ECA - Event context array data
|
||||
* CCA - Channel context array data
|
||||
*/
|
||||
struct mhi_buf {
|
||||
void *buf;
|
||||
phys_addr_t phys_addr;
|
||||
dma_addr_t dma_addr;
|
||||
size_t len;
|
||||
const char *name; /* ECA, CCA */
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mhi_driver - mhi driver information
|
||||
* @id_table: NULL terminated channel ID names
|
||||
* @ul_xfer_cb: UL data transfer callback
|
||||
* @dl_xfer_cb: DL data transfer callback
|
||||
* @status_cb: Asynchronous status callback
|
||||
*/
|
||||
struct mhi_driver {
|
||||
const struct mhi_device_id *id_table;
|
||||
int (*probe)(struct mhi_device *, const struct mhi_device_id *id);
|
||||
void (*remove)(struct mhi_device *);
|
||||
void (*ul_xfer_cb)(struct mhi_device *, struct mhi_result *);
|
||||
void (*dl_xfer_cb)(struct mhi_device *, struct mhi_result *);
|
||||
void (*status_cb)(struct mhi_device *, enum MHI_CB mhi_cb);
|
||||
struct device_driver driver;
|
||||
};
|
||||
|
||||
#define to_mhi_driver(drv) container_of(drv, struct mhi_driver, driver)
|
||||
#define to_mhi_device(dev) container_of(dev, struct mhi_device, dev)
|
||||
|
||||
static inline void mhi_device_set_devdata(struct mhi_device *mhi_dev,
|
||||
void *priv)
|
||||
{
|
||||
mhi_dev->priv_data = priv;
|
||||
}
|
||||
|
||||
static inline void *mhi_device_get_devdata(struct mhi_device *mhi_dev)
|
||||
{
|
||||
return mhi_dev->priv_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* mhi_queue_transfer - Queue a buffer to hardware
|
||||
* All transfers are asyncronous transfers
|
||||
* @mhi_dev: Device associated with the channels
|
||||
* @dir: Data direction
|
||||
* @buf: Data buffer (skb for hardware channels)
|
||||
* @len: Size in bytes
|
||||
* @mflags: Interrupt flags for the device
|
||||
*/
|
||||
static inline int mhi_queue_transfer(struct mhi_device *mhi_dev,
|
||||
enum dma_data_direction dir,
|
||||
void *buf,
|
||||
size_t len,
|
||||
enum MHI_FLAGS mflags)
|
||||
{
|
||||
if (dir == DMA_TO_DEVICE)
|
||||
return mhi_dev->ul_xfer(mhi_dev, mhi_dev->ul_chan, buf, len,
|
||||
mflags);
|
||||
else
|
||||
return mhi_dev->dl_xfer(mhi_dev, mhi_dev->dl_chan, buf, len,
|
||||
mflags);
|
||||
}
|
||||
|
||||
static inline void *mhi_controller_get_devdata(struct mhi_controller *mhi_cntrl)
|
||||
{
|
||||
return mhi_cntrl->priv_data;
|
||||
}
|
||||
|
||||
static inline void mhi_free_controller(struct mhi_controller *mhi_cntrl)
|
||||
{
|
||||
kfree(mhi_cntrl);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_MHI_BUS)
|
||||
|
||||
/**
|
||||
* mhi_driver_register - Register driver with MHI framework
|
||||
* @mhi_drv: mhi_driver structure
|
||||
*/
|
||||
int mhi_driver_register(struct mhi_driver *mhi_drv);
|
||||
|
||||
/**
|
||||
* mhi_driver_unregister - Unregister a driver for mhi_devices
|
||||
* @mhi_drv: mhi_driver structure
|
||||
*/
|
||||
void mhi_driver_unregister(struct mhi_driver *mhi_drv);
|
||||
|
||||
/**
|
||||
* mhi_device_configure - configure ECA or CCA context
|
||||
* For offload channels that client manage, call this
|
||||
* function to configure channel context or event context
|
||||
* array associated with the channel
|
||||
* @mhi_div: Device associated with the channels
|
||||
* @dir: Direction of the channel
|
||||
* @mhi_buf: Configuration data
|
||||
* @elements: # of configuration elements
|
||||
*/
|
||||
int mhi_device_configure(struct mhi_device *mhi_div,
|
||||
enum dma_data_direction dir,
|
||||
struct mhi_buf *mhi_buf,
|
||||
int elements);
|
||||
|
||||
/**
|
||||
* mhi_device_get - disable all low power modes
|
||||
* Only disables lpm, does not immediately exit low power mode
|
||||
* if controller already in a low power mode
|
||||
* @mhi_dev: Device associated with the channels
|
||||
*/
|
||||
void mhi_device_get(struct mhi_device *mhi_dev);
|
||||
|
||||
/**
|
||||
* mhi_device_get_sync - disable all low power modes
|
||||
* Synchronously disable all low power, exit low power mode if
|
||||
* controller already in a low power state
|
||||
* @mhi_dev: Device associated with the channels
|
||||
*/
|
||||
int mhi_device_get_sync(struct mhi_device *mhi_dev);
|
||||
|
||||
/**
|
||||
* mhi_device_put - re-enable low power modes
|
||||
* @mhi_dev: Device associated with the channels
|
||||
*/
|
||||
void mhi_device_put(struct mhi_device *mhi_dev);
|
||||
|
||||
/**
|
||||
* mhi_prepare_for_transfer - setup channel for data transfer
|
||||
* Moves both UL and DL channel from RESET to START state
|
||||
* @mhi_dev: Device associated with the channels
|
||||
*/
|
||||
int mhi_prepare_for_transfer(struct mhi_device *mhi_dev);
|
||||
|
||||
/**
|
||||
* mhi_unprepare_from_transfer -unprepare the channels
|
||||
* Moves both UL and DL channels to RESET state
|
||||
* @mhi_dev: Device associated with the channels
|
||||
*/
|
||||
void mhi_unprepare_from_transfer(struct mhi_device *mhi_dev);
|
||||
|
||||
/**
|
||||
* mhi_get_no_free_descriptors - Get transfer ring length
|
||||
* Get # of TD available to queue buffers
|
||||
* @mhi_dev: Device associated with the channels
|
||||
* @dir: Direction of the channel
|
||||
*/
|
||||
int mhi_get_no_free_descriptors(struct mhi_device *mhi_dev,
|
||||
enum dma_data_direction dir);
|
||||
|
||||
/**
|
||||
* mhi_poll - poll for any available data to consume
|
||||
* This is only applicable for DL direction
|
||||
* @mhi_dev: Device associated with the channels
|
||||
* @budget: In descriptors to service before returning
|
||||
*/
|
||||
int mhi_poll(struct mhi_device *mhi_dev, u32 budget);
|
||||
|
||||
/**
|
||||
* mhi_ioctl - user space IOCTL support for MHI channels
|
||||
* Native support for setting TIOCM
|
||||
* @mhi_dev: Device associated with the channels
|
||||
* @cmd: IOCTL cmd
|
||||
* @arg: Optional parameter, iotcl cmd specific
|
||||
*/
|
||||
long mhi_ioctl(struct mhi_device *mhi_dev, unsigned int cmd, unsigned long arg);
|
||||
|
||||
/**
|
||||
* mhi_alloc_controller - Allocate mhi_controller structure
|
||||
* Allocate controller structure and additional data for controller
|
||||
* private data. You may get the private data pointer by calling
|
||||
* mhi_controller_get_devdata
|
||||
* @size: # of additional bytes to allocate
|
||||
*/
|
||||
struct mhi_controller *mhi_alloc_controller(size_t size);
|
||||
|
||||
/**
|
||||
* of_register_mhi_controller - Register MHI controller
|
||||
* Registers MHI controller with MHI bus framework. DT must be supported
|
||||
* @mhi_cntrl: MHI controller to register
|
||||
*/
|
||||
int of_register_mhi_controller(struct mhi_controller *mhi_cntrl);
|
||||
|
||||
void mhi_unregister_mhi_controller(struct mhi_controller *mhi_cntrl);
|
||||
|
||||
/**
|
||||
* mhi_bdf_to_controller - Look up a registered controller
|
||||
* Search for controller based on device identification
|
||||
* @domain: RC domain of the device
|
||||
* @bus: Bus device connected to
|
||||
* @slot: Slot device assigned to
|
||||
* @dev_id: Device Identification
|
||||
*/
|
||||
struct mhi_controller *mhi_bdf_to_controller(u32 domain, u32 bus, u32 slot,
|
||||
u32 dev_id);
|
||||
|
||||
/**
|
||||
* mhi_prepare_for_power_up - Do pre-initialization before power up
|
||||
* This is optional, call this before power up if controller do not
|
||||
* want bus framework to automatically free any allocated memory during shutdown
|
||||
* process.
|
||||
* @mhi_cntrl: MHI controller
|
||||
*/
|
||||
int mhi_prepare_for_power_up(struct mhi_controller *mhi_cntrl);
|
||||
|
||||
/**
|
||||
* mhi_async_power_up - Starts MHI power up sequence
|
||||
* @mhi_cntrl: MHI controller
|
||||
*/
|
||||
int mhi_async_power_up(struct mhi_controller *mhi_cntrl);
|
||||
int mhi_sync_power_up(struct mhi_controller *mhi_cntrl);
|
||||
|
||||
/**
|
||||
* mhi_power_down - Start MHI power down sequence
|
||||
* @mhi_cntrl: MHI controller
|
||||
* @graceful: link is still accessible, do a graceful shutdown process otherwise
|
||||
* we will shutdown host w/o putting device into RESET state
|
||||
*/
|
||||
void mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful);
|
||||
|
||||
/**
|
||||
* mhi_unprepare_after_powre_down - free any allocated memory for power up
|
||||
* @mhi_cntrl: MHI controller
|
||||
*/
|
||||
void mhi_unprepare_after_power_down(struct mhi_controller *mhi_cntrl);
|
||||
|
||||
/**
|
||||
* mhi_pm_suspend - Move MHI into a suspended state
|
||||
* Transition to MHI state M3 state from M0||M1||M2 state
|
||||
* @mhi_cntrl: MHI controller
|
||||
*/
|
||||
int mhi_pm_suspend(struct mhi_controller *mhi_cntrl);
|
||||
|
||||
/**
|
||||
* mhi_pm_resume - Resume MHI from suspended state
|
||||
* Transition to MHI state M0 state from M3 state
|
||||
* @mhi_cntrl: MHI controller
|
||||
*/
|
||||
int mhi_pm_resume(struct mhi_controller *mhi_cntrl);
|
||||
|
||||
/**
|
||||
* mhi_download_rddm_img - Download ramdump image from device for
|
||||
* debugging purpose.
|
||||
* @mhi_cntrl: MHI controller
|
||||
* @in_panic: If we trying to capture image while in kernel panic
|
||||
*/
|
||||
int mhi_download_rddm_img(struct mhi_controller *mhi_cntrl, bool in_panic);
|
||||
|
||||
#else
|
||||
|
||||
static inline int mhi_driver_register(struct mhi_driver *mhi_drv)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static inline void mhi_driver_unregister(struct mhi_driver *mhi_drv)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int mhi_device_configure(struct mhi_device *mhi_div,
|
||||
enum dma_data_direction dir,
|
||||
struct mhi_buf *mhi_buf,
|
||||
int elements)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static inline void mhi_device_get(struct mhi_device *mhi_dev)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int mhi_device_get_sync(struct mhi_device *mhi_dev)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static inline void mhi_device_put(struct mhi_device *mhi_dev)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int mhi_prepare_for_transfer(struct mhi_device *mhi_dev)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static inline void mhi_unprepare_from_transfer(struct mhi_device *mhi_dev)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static inline int mhi_get_no_free_descriptors(struct mhi_device *mhi_dev,
|
||||
enum dma_data_direction dir)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static inline int mhi_poll(struct mhi_device *mhi_dev, u32 budget)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static inline long mhi_ioctl(struct mhi_device *mhi_dev,
|
||||
unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static inline struct mhi_controller *mhi_alloc_controller(size_t size)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline int of_register_mhi_controller(struct mhi_controller *mhi_cntrl)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static inline void mhi_unregister_mhi_controller(
|
||||
struct mhi_controller *mhi_cntrl)
|
||||
{
|
||||
}
|
||||
|
||||
static inline struct mhi_controller *mhi_bdf_to_controller(u32 domain,
|
||||
u32 bus,
|
||||
u32 slot,
|
||||
u32 dev_id)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline int mhi_prepare_for_power_up(struct mhi_controller *mhi_cntrl)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static inline int mhi_async_power_up(struct mhi_controller *mhi_cntrl)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static inline int mhi_sync_power_up(struct mhi_controller *mhi_cntrl)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static inline void mhi_power_down(struct mhi_controller *mhi_cntrl,
|
||||
bool graceful)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void mhi_unprepare_after_power_down(
|
||||
struct mhi_controller *mhi_cntrl)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int mhi_pm_suspend(struct mhi_controller *mhi_cntrl)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static inline int mhi_pm_resume(struct mhi_controller *mhi_cntrl)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static inline int mhi_download_rddm_img(struct mhi_controller *mhi_cntrl,
|
||||
bool in_panic)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_ARCH_QCOM
|
||||
|
||||
#ifdef CONFIG_MHI_DEBUG
|
||||
|
||||
#define MHI_VERB(fmt, ...) do { \
|
||||
if (mhi_cntrl->klog_lvl <= MHI_MSG_VERBOSE) \
|
||||
pr_dbg("[D][%s] " fmt, __func__, ##__VA_ARGS__);\
|
||||
} while (0)
|
||||
|
||||
#else
|
||||
|
||||
#define MHI_VERB(fmt, ...)
|
||||
|
||||
#endif
|
||||
|
||||
#define MHI_LOG(fmt, ...) do { \
|
||||
if (mhi_cntrl->klog_lvl <= MHI_MSG_INFO) \
|
||||
pr_info("[I][%s] " fmt, __func__, ##__VA_ARGS__);\
|
||||
} while (0)
|
||||
|
||||
#define MHI_ERR(fmt, ...) do { \
|
||||
if (mhi_cntrl->klog_lvl <= MHI_MSG_LVL_ERROR) \
|
||||
pr_err("[E][%s] " fmt, __func__, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#define MHI_CRITICAL(fmt, ...) do { \
|
||||
if (mhi_cntrl->klog_lvl <= MHI_MSG_LVL_CRITICAL) \
|
||||
pr_alert("[C][%s] " fmt, __func__, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#else /* ARCH QCOM */
|
||||
|
||||
#include <linux/ipc_logging.h>
|
||||
|
||||
#ifdef CONFIG_MHI_DEBUG
|
||||
|
||||
#define MHI_VERB(fmt, ...) do { \
|
||||
if (mhi_cntrl->klog_lvl <= MHI_MSG_LVL_VERBOSE) \
|
||||
pr_err("[D][%s] " fmt, __func__, ##__VA_ARGS__);\
|
||||
if (mhi_cntrl->log_buf && \
|
||||
(mhi_cntrl->log_lvl <= MHI_MSG_LVL_VERBOSE)) \
|
||||
ipc_log_string(mhi_cntrl->log_buf, "[D][%s] " fmt, \
|
||||
__func__, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#else
|
||||
|
||||
#define MHI_VERB(fmt, ...)
|
||||
|
||||
#endif
|
||||
|
||||
#define MHI_LOG(fmt, ...) do { \
|
||||
if (mhi_cntrl->klog_lvl <= MHI_MSG_LVL_INFO) \
|
||||
pr_err("[I][%s] " fmt, __func__, ##__VA_ARGS__);\
|
||||
if (mhi_cntrl->log_buf && \
|
||||
(mhi_cntrl->log_lvl <= MHI_MSG_LVL_INFO)) \
|
||||
ipc_log_string(mhi_cntrl->log_buf, "[I][%s] " fmt, \
|
||||
__func__, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#define MHI_ERR(fmt, ...) do { \
|
||||
if (mhi_cntrl->klog_lvl <= MHI_MSG_LVL_ERROR) \
|
||||
pr_err("[E][%s] " fmt, __func__, ##__VA_ARGS__); \
|
||||
if (mhi_cntrl->log_buf && \
|
||||
(mhi_cntrl->log_lvl <= MHI_MSG_LVL_ERROR)) \
|
||||
ipc_log_string(mhi_cntrl->log_buf, "[E][%s] " fmt, \
|
||||
__func__, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#define MHI_CRITICAL(fmt, ...) do { \
|
||||
if (mhi_cntrl->klog_lvl <= MHI_MSG_LVL_CRITICAL) \
|
||||
pr_err("[C][%s] " fmt, __func__, ##__VA_ARGS__); \
|
||||
if (mhi_cntrl->log_buf && \
|
||||
(mhi_cntrl->log_lvl <= MHI_MSG_LVL_CRITICAL)) \
|
||||
ipc_log_string(mhi_cntrl->log_buf, "[C][%s] " fmt, \
|
||||
__func__, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* _MHI_H_ */
|
||||
@@ -705,5 +705,18 @@ struct fsl_mc_device_id {
|
||||
const char obj_type[16];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mhi_device_id - MHI device identification
|
||||
* @chan: MHI channel name
|
||||
* @driver_data: driver data;
|
||||
*/
|
||||
|
||||
struct mhi_device_id {
|
||||
const char *chan;
|
||||
kernel_ulong_t driver_data;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
#endif /* LINUX_MOD_DEVICETABLE_H */
|
||||
|
||||
Reference in New Issue
Block a user