Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 65d8df76 authored by Sujeev Dias's avatar Sujeev Dias
Browse files

mhi: core: add support for priority based event processing



Not all events require same priority to process. Process
high priority events using a tasklet while processing low
priority events using a kworker. Priority for events can be
configured by device tree.

CRs-Fixed: 2004759
Change-Id: I6bf04624c2fd92afd7c698d6a57f66619ea13b5b
Signed-off-by: default avatarSujeev Dias <sdias@codeaurora.org>
parent 41fb6e92
Loading
Loading
Loading
Loading
+4 −3
Original line number Diff line number Diff line
@@ -88,12 +88,13 @@ Main node properties:
  Usage: required
  Value type: Array of <u32>
  Definition: mhi event ring configuration parameters for platform
	defined as below <A B C D E>:
	defined as below <A B C D E F>:
		A = maximum event descriptors
		B = MSI associated with event
		C = interrupt moderation (see MHI specification)
		D = Associated channel
		E = flags defined by mhi_macros.h GET_EV_PROPS
		E = priority of the event ring. 0 being the highest.
		F = flags defined by mhi_macros.h GET_EV_PROPS

- qcom,mhi-address-window
  Usage: required
@@ -147,5 +148,5 @@ mhi: qcom,mhi {
			<100 512 1200000000 1200000000>;
	mhi-event-rings = <1>;
	mhi-chan-cfg-102 = <0x66 0x80 0x5 0x62>;
	mhi-event-cfg-0 = <0x80 0x0 0x0 0x0 0x11>;
	mhi-event-cfg-0 = <0x80 0x0 0x0 0x0 0 1 0x11>;
};
+18 −13
Original line number Diff line number Diff line
@@ -349,6 +349,10 @@ struct mhi_ring {
	u32 msi_enable_cntr;
	spinlock_t ring_lock;
	struct dma_pool *dma_pool;
	struct tasklet_struct ev_task;
	struct work_struct ev_worker;
	struct mhi_device_ctxt *mhi_dev_ctxt;
	int index;
};

enum MHI_CMD_STATUS {
@@ -469,7 +473,6 @@ struct mhi_counters {
	u32 bb_used[MHI_MAX_CHANNELS];
	atomic_t device_wake;
	atomic_t outbound_acks;
	atomic_t events_pending;
	u32 *msi_counter;
	u32 mhi_reset_cntr;
	u32 link_down_cntr;
@@ -479,16 +482,10 @@ struct mhi_counters {
struct mhi_flags {
	u32 mhi_initialized;
	u32 link_up;
	int stop_threads;
	u32 kill_threads;
	u32 ev_thread_stopped;
	u32 st_thread_stopped;
	bool bb_required;
};

struct mhi_wait_queues {
	wait_queue_head_t *mhi_event_wq;
	wait_queue_head_t *state_change_event;
	wait_queue_head_t *m0_event;
	wait_queue_head_t *m3_event;
	wait_queue_head_t *bhi_event;
@@ -547,9 +544,7 @@ struct mhi_device_ctxt {

	struct mhi_client_handle *client_handle_list[MHI_MAX_CHANNELS];
	struct mhi_event_ring_cfg *ev_ring_props;
	struct task_struct *event_thread_handle;
	struct task_struct *st_thread_handle;
	struct tasklet_struct ev_task; /* Process control Events */
	struct work_struct st_thread_worker;
	struct work_struct process_m1_worker;
	struct mhi_wait_queues mhi_ev_wq;
	struct dev_mmio_info mmio_info;
@@ -609,10 +604,18 @@ struct mhi_event_ring_cfg {
	u32 intmod;
	enum MHI_CLIENT_CHANNEL chan;
	u32 flags;
	/*
	 * Priority of event handling:
	 * 0 = highest, handle events in isr (reserved for future)
	 * 1 = handles event using tasklet
	 * 2 = handles events using workerthread
	 */
	u32 priority;
	enum MHI_RING_CLASS class;
	enum MHI_EVENT_RING_STATE state;
	irqreturn_t (*mhi_handler_ptr)(int , void *);
};
#define MHI_EV_PRIORITY_TASKLET (1)

struct mhi_data_buf {
	dma_addr_t bounce_buffer;
@@ -670,14 +673,13 @@ enum MHI_EVENT_CCS get_cmd_pkt(struct mhi_device_ctxt *mhi_dev_ctxt,
				union mhi_cmd_pkt **cmd_pkt, u32 event_index);
int parse_cmd_event(struct mhi_device_ctxt *ctxt,
				union mhi_event_pkt *event, u32 event_index);
int parse_event_thread(void *ctxt);
int mhi_test_for_device_ready(
					struct mhi_device_ctxt *mhi_dev_ctxt);
int mhi_test_for_device_reset(
					struct mhi_device_ctxt *mhi_dev_ctxt);
int validate_ring_el_addr(struct mhi_ring *ring, uintptr_t addr);
int validate_ev_el_addr(struct mhi_ring *ring, uintptr_t addr);
int mhi_state_change_thread(void *ctxt);
void mhi_state_change_worker(struct work_struct *work);
int mhi_init_state_transition(struct mhi_device_ctxt *mhi_dev_ctxt,
					enum STATE_TRANSITION new_state);
int mhi_wait_for_mdm(struct mhi_device_ctxt *mhi_dev_ctxt);
@@ -751,6 +753,9 @@ int set_mhi_base_state(struct mhi_device_ctxt *mhi_dev_ctxt);
void mhi_set_m_state(struct mhi_device_ctxt *mhi_dev_ctxt,
		     enum MHI_STATE new_state);
const char *state_transition_str(enum STATE_TRANSITION state);
void mhi_ctrl_ev_task(unsigned long data);
void mhi_ev_task(unsigned long data);
void process_event_ring(struct work_struct *work);
int process_m0_transition(struct mhi_device_ctxt *mhi_dev_ctxt);
int process_m3_transition(struct mhi_device_ctxt *mhi_dev_ctxt);

#endif
+18 −8
Original line number Diff line number Diff line
/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
/* Copyright (c) 2015-2017, 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
@@ -43,13 +43,13 @@ int mhi_populate_event_cfg(struct mhi_device_ctxt *mhi_dev_ctxt)
		return -ENOMEM;

	for (i = 0; i < mhi_dev_ctxt->mmio_info.nr_event_rings; ++i) {
		u32 dt_configs[5];
		int len;
		u32 dt_configs[6];
		int no_elements;

		scnprintf(dt_prop, MAX_BUF_SIZE, "%s%d", "mhi-event-cfg-", i);
		if (!of_find_property(np, dt_prop, &len))
			goto dt_error;
		if (len != sizeof(dt_configs))
		no_elements = of_property_count_elems_of_size(np, dt_prop,
							sizeof(dt_configs));
		if (no_elements != 1)
			goto dt_error;
		r = of_property_read_u32_array(
					np,
@@ -66,14 +66,16 @@ int mhi_populate_event_cfg(struct mhi_device_ctxt *mhi_dev_ctxt)
		mhi_dev_ctxt->ev_ring_props[i].msi_vec = dt_configs[1];
		mhi_dev_ctxt->ev_ring_props[i].intmod = dt_configs[2];
		mhi_dev_ctxt->ev_ring_props[i].chan = dt_configs[3];
		mhi_dev_ctxt->ev_ring_props[i].flags = dt_configs[4];
		mhi_dev_ctxt->ev_ring_props[i].priority = dt_configs[4];
		mhi_dev_ctxt->ev_ring_props[i].flags = dt_configs[5];
		mhi_log(mhi_dev_ctxt, MHI_MSG_INFO,
			"ev ring %d,desc:0x%x,msi:0x%x,intmod%d chan:%u flags0x%x\n",
			"ev ring %d,desc:0x%x,msi:0x%x,intmod%d chan:%u priority:%u flags0x%x\n",
			i,
			mhi_dev_ctxt->ev_ring_props[i].nr_desc,
			mhi_dev_ctxt->ev_ring_props[i].msi_vec,
			mhi_dev_ctxt->ev_ring_props[i].intmod,
			mhi_dev_ctxt->ev_ring_props[i].chan,
			mhi_dev_ctxt->ev_ring_props[i].priority,
			mhi_dev_ctxt->ev_ring_props[i].flags);
		if (GET_EV_PROPS(EV_MANAGED,
			mhi_dev_ctxt->ev_ring_props[i].flags))
@@ -128,6 +130,9 @@ int create_local_ev_ctxt(struct mhi_device_ctxt *mhi_dev_ctxt)
			mhi_local_event_ctxt[i];

		spin_lock_init(&mhi_ring->ring_lock);
		tasklet_init(&mhi_ring->ev_task, mhi_ev_task,
			     (unsigned long)mhi_ring);
		INIT_WORK(&mhi_ring->ev_worker, process_event_ring);
	}

	return r;
@@ -157,6 +162,8 @@ void ring_ev_db(struct mhi_device_ctxt *mhi_dev_ctxt, u32 event_ring_index)

static int mhi_event_ring_init(struct mhi_event_ctxt *ev_list,
			       struct mhi_ring *ring,
			       struct mhi_device_ctxt *mhi_dev_ctxt,
			       int index,
			       u32 el_per_ring,
			       u32 intmodt_val,
			       u32 msi_vec,
@@ -166,6 +173,8 @@ static int mhi_event_ring_init(struct mhi_event_ctxt *ev_list,
	ev_list->mhi_msi_vector     = msi_vec;
	ev_list->mhi_event_ring_len = el_per_ring*sizeof(union mhi_event_pkt);
	MHI_SET_EV_CTXT(EVENT_CTXT_INTMODT, ev_list, intmodt_val);
	ring->mhi_dev_ctxt = mhi_dev_ctxt;
	ring->index = index;
	ring->len = ((size_t)(el_per_ring)*sizeof(union mhi_event_pkt));
	ring->el_size = sizeof(union mhi_event_pkt);
	ring->overwrite_en = 0;
@@ -198,6 +207,7 @@ void init_event_ctxt_array(struct mhi_device_ctxt *mhi_dev_ctxt)
		event_ctxt = &mhi_dev_ctxt->dev_space.ring_ctxt.ec_list[i];
		mhi_local_event_ctxt = &mhi_dev_ctxt->mhi_local_event_ctxt[i];
		mhi_event_ring_init(event_ctxt, mhi_local_event_ctxt,
				    mhi_dev_ctxt, i,
				    mhi_dev_ctxt->ev_ring_props[i].nr_desc,
				    mhi_dev_ctxt->ev_ring_props[i].intmod,
				    mhi_dev_ctxt->ev_ring_props[i].msi_vec,
+1 −5
Original line number Diff line number Diff line
@@ -110,8 +110,6 @@ int mhi_ctxt_init(struct mhi_device_ctxt *mhi_dev_ctxt)

irq_error:
	kfree(mhi_dev_ctxt->state_change_work_item_list.q_lock);
	kfree(mhi_dev_ctxt->mhi_ev_wq.mhi_event_wq);
	kfree(mhi_dev_ctxt->mhi_ev_wq.state_change_event);
	kfree(mhi_dev_ctxt->mhi_ev_wq.m0_event);
	kfree(mhi_dev_ctxt->mhi_ev_wq.m3_event);
	kfree(mhi_dev_ctxt->mhi_ev_wq.bhi_event);
@@ -190,12 +188,10 @@ static int mhi_pci_probe(struct pci_dev *pcie_device,
	pcie_device->dev.of_node = plat_dev->dev.of_node;
	mhi_dev_ctxt->mhi_pm_state = MHI_PM_DISABLE;
	INIT_WORK(&mhi_dev_ctxt->process_m1_worker, process_m1_transition);
	INIT_WORK(&mhi_dev_ctxt->st_thread_worker, mhi_state_change_worker);
	mutex_init(&mhi_dev_ctxt->pm_lock);
	rwlock_init(&mhi_dev_ctxt->pm_xfer_lock);
	spin_lock_init(&mhi_dev_ctxt->dev_wake_lock);
	tasklet_init(&mhi_dev_ctxt->ev_task,
		     mhi_ctrl_ev_task,
		     (unsigned long)mhi_dev_ctxt);
	init_completion(&mhi_dev_ctxt->cmd_complete);
	mhi_dev_ctxt->flags.link_up = 1;

+2 −46
Original line number Diff line number Diff line
/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
/* Copyright (c) 2014-2017, 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
@@ -250,19 +250,6 @@ err_ev_alloc:
static int mhi_init_events(struct mhi_device_ctxt *mhi_dev_ctxt)
{

	mhi_dev_ctxt->mhi_ev_wq.mhi_event_wq = kmalloc(
						sizeof(wait_queue_head_t),
						GFP_KERNEL);
	if (NULL == mhi_dev_ctxt->mhi_ev_wq.mhi_event_wq) {
		mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, "Failed to init event");
		return -ENOMEM;
	}
	mhi_dev_ctxt->mhi_ev_wq.state_change_event =
				kmalloc(sizeof(wait_queue_head_t), GFP_KERNEL);
	if (NULL == mhi_dev_ctxt->mhi_ev_wq.state_change_event) {
		mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, "Failed to init event");
		goto error_event_handle_alloc;
	}
	/* Initialize the event which signals M0 */
	mhi_dev_ctxt->mhi_ev_wq.m0_event = kmalloc(sizeof(wait_queue_head_t),
								GFP_KERNEL);
@@ -284,10 +271,7 @@ static int mhi_init_events(struct mhi_device_ctxt *mhi_dev_ctxt)
		mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, "Failed to init event");
		goto error_bhi_event;
	}
	/* Initialize the event which starts the event parsing thread */
	init_waitqueue_head(mhi_dev_ctxt->mhi_ev_wq.mhi_event_wq);
	/* Initialize the event which starts the state change thread */
	init_waitqueue_head(mhi_dev_ctxt->mhi_ev_wq.state_change_event);

	/* Initialize the event which triggers clients waiting to send */
	init_waitqueue_head(mhi_dev_ctxt->mhi_ev_wq.m0_event);
	/* Initialize the event which triggers D3hot */
@@ -300,9 +284,6 @@ error_bhi_event:
error_m0_event:
	kfree(mhi_dev_ctxt->mhi_ev_wq.m0_event);
error_state_change_event_handle:
	kfree(mhi_dev_ctxt->mhi_ev_wq.state_change_event);
error_event_handle_alloc:
	kfree(mhi_dev_ctxt->mhi_ev_wq.mhi_event_wq);
	return -ENOMEM;
}

@@ -339,21 +320,6 @@ static void mhi_init_wakelock(struct mhi_device_ctxt *mhi_dev_ctxt)
	wakeup_source_init(&mhi_dev_ctxt->w_lock, "mhi_wakeup_source");
}

static int mhi_spawn_threads(struct mhi_device_ctxt *mhi_dev_ctxt)
{
	mhi_dev_ctxt->event_thread_handle = kthread_run(parse_event_thread,
							mhi_dev_ctxt,
							"mhi_ev_thrd");
	if (IS_ERR(mhi_dev_ctxt->event_thread_handle))
		return PTR_ERR(mhi_dev_ctxt->event_thread_handle);
	mhi_dev_ctxt->st_thread_handle = kthread_run(mhi_state_change_thread,
							mhi_dev_ctxt,
							"mhi_st_thrd");
	if (IS_ERR(mhi_dev_ctxt->event_thread_handle))
		return PTR_ERR(mhi_dev_ctxt->event_thread_handle);
	return 0;
}

/**
 * @brief Main initialization function for a mhi struct device context
 *	 All threads, events mutexes, mhi specific data structures
@@ -408,21 +374,11 @@ int mhi_init_device_ctxt(struct mhi_device_ctxt *mhi_dev_ctxt)
	init_event_ctxt_array(mhi_dev_ctxt);
	mhi_dev_ctxt->mhi_state = MHI_STATE_RESET;

	r = mhi_spawn_threads(mhi_dev_ctxt);
	if (r) {
		mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
			"Failed to spawn threads ret %d\n", r);
		goto error_during_thread_spawn;
	}
	mhi_init_wakelock(mhi_dev_ctxt);

	return r;

error_during_thread_spawn:
	kfree(mhi_dev_ctxt->state_change_work_item_list.q_lock);
error_during_thread_init:
	kfree(mhi_dev_ctxt->mhi_ev_wq.mhi_event_wq);
	kfree(mhi_dev_ctxt->mhi_ev_wq.state_change_event);
	kfree(mhi_dev_ctxt->mhi_ev_wq.m0_event);
	kfree(mhi_dev_ctxt->mhi_ev_wq.m3_event);
	kfree(mhi_dev_ctxt->mhi_ev_wq.bhi_event);
Loading