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

Commit c4b03962 authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

Merge "mhi: core: block low power modes only in mission mode"

parents 9b3bcd0a c234a268
Loading
Loading
Loading
Loading
+96 −73
Original line number Diff line number Diff line
@@ -98,6 +98,57 @@ const char *to_mhi_pm_state_str(enum MHI_PM_STATE state)
	return mhi_pm_state_str[index];
}

static ssize_t time_show(struct device *dev,
			 struct device_attribute *attr,
			 char *buf)
{
	struct mhi_device *mhi_dev = to_mhi_device(dev);
	struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
	u64 t_host, t_device;
	int ret;

	ret = mhi_get_remote_time_sync(mhi_dev, &t_host, &t_device);
	if (ret) {
		MHI_ERR("Failed to obtain time, ret:%d\n", ret);
		return ret;
	}

	return scnprintf(buf, PAGE_SIZE, "local: %llu remote: %llu (ticks)\n",
			 t_host, t_device);
}
static DEVICE_ATTR_RO(time);

static ssize_t time_us_show(struct device *dev,
			    struct device_attribute *attr,
			    char *buf)
{
	struct mhi_device *mhi_dev = to_mhi_device(dev);
	struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
	u64 t_host, t_device;
	int ret;

	ret = mhi_get_remote_time_sync(mhi_dev, &t_host, &t_device);
	if (ret) {
		MHI_ERR("Failed to obtain time, ret:%d\n", ret);
		return ret;
	}

	return scnprintf(buf, PAGE_SIZE, "local: %llu remote: %llu (us)\n",
			 LOCAL_TICKS_TO_US(t_host),
			 REMOTE_TICKS_TO_US(t_device));
}
static DEVICE_ATTR_RO(time_us);

static struct attribute *mhi_tsync_attrs[] = {
	&dev_attr_time.attr,
	&dev_attr_time_us.attr,
	NULL,
};

static const struct attribute_group mhi_tsync_group = {
	.attrs = mhi_tsync_attrs,
};

static ssize_t log_level_show(struct device *dev,
			      struct device_attribute *attr,
			      char *buf)
@@ -199,15 +250,36 @@ static const struct attribute_group mhi_sysfs_group = {
	.attrs = mhi_sysfs_attrs,
};

int mhi_create_sysfs(struct mhi_controller *mhi_cntrl)
void mhi_create_sysfs(struct mhi_controller *mhi_cntrl)
{
	return sysfs_create_group(&mhi_cntrl->mhi_dev->dev.kobj,
				  &mhi_sysfs_group);
	sysfs_create_group(&mhi_cntrl->mhi_dev->dev.kobj, &mhi_sysfs_group);
	if (mhi_cntrl->mhi_tsync)
		sysfs_create_group(&mhi_cntrl->mhi_dev->dev.kobj,
				   &mhi_tsync_group);
}

void mhi_destroy_sysfs(struct mhi_controller *mhi_cntrl)
{
	struct mhi_device *mhi_dev = mhi_cntrl->mhi_dev;
	struct mhi_timesync *mhi_tsync = mhi_cntrl->mhi_tsync;
	struct tsync_node *tsync, *tmp;

	if (mhi_tsync) {
		mutex_lock(&mhi_cntrl->tsync_mutex);
		sysfs_remove_group(&mhi_cntrl->mhi_dev->dev.kobj,
				   &mhi_tsync_group);

		spin_lock(&mhi_tsync->lock);
		list_for_each_entry_safe(tsync, tmp, &mhi_tsync->head, node) {
			list_del(&tsync->node);
			kfree(tsync);
		}
		spin_unlock(&mhi_tsync->lock);

		kfree(mhi_cntrl->mhi_tsync);
		mhi_cntrl->mhi_tsync = NULL;
		mutex_unlock(&mhi_cntrl->tsync_mutex);
	}

	sysfs_remove_group(&mhi_dev->dev.kobj, &mhi_sysfs_group);

@@ -612,42 +684,32 @@ static int mhi_get_er_index(struct mhi_controller *mhi_cntrl,
	return -ENOENT;
}

int mhi_init_timesync(struct mhi_controller *mhi_cntrl)
static int mhi_init_timesync(struct mhi_controller *mhi_cntrl)
{
	struct mhi_timesync *mhi_tsync;
	u32 time_offset, db_offset;
	int ret;

	read_lock_bh(&mhi_cntrl->pm_lock);
	u32 time_offset, time_cfg_offset;
	int ret, er_index;

	if (!MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) {
		ret = -EIO;
		goto exit_timesync;
	}
	if (!mhi_cntrl->time_get || !mhi_cntrl->lpm_disable ||
	     !mhi_cntrl->lpm_enable)
		return -EINVAL;

	ret = mhi_get_capability_offset(mhi_cntrl, TIMESYNC_CAP_ID,
					&time_offset);
	if (ret) {
		MHI_LOG("No timesync capability found\n");
		goto exit_timesync;
		return ret;
	}

	read_unlock_bh(&mhi_cntrl->pm_lock);

	if (!mhi_cntrl->time_get || !mhi_cntrl->lpm_disable ||
	     !mhi_cntrl->lpm_enable)
		return -EINVAL;

	mhi_cntrl->local_timer_freq = arch_timer_get_cntfrq();

	/* register method supported */
	mhi_tsync = kzalloc(sizeof(*mhi_tsync), GFP_KERNEL);
	/* register method is supported */
	mhi_tsync = kzalloc(sizeof(*mhi_tsync), GFP_ATOMIC);
	if (!mhi_tsync)
		return -ENOMEM;

	spin_lock_init(&mhi_tsync->lock);
	INIT_LIST_HEAD(&mhi_tsync->head);
	init_completion(&mhi_tsync->completion);

	/* save time_offset for obtaining time */
	MHI_LOG("TIME OFFS:0x%x\n", time_offset);
@@ -656,61 +718,20 @@ int mhi_init_timesync(struct mhi_controller *mhi_cntrl)

	mhi_cntrl->mhi_tsync = mhi_tsync;

	ret = mhi_create_timesync_sysfs(mhi_cntrl);
	if (unlikely(ret)) {
		/* kernel method still work */
		MHI_ERR("Failed to create timesync sysfs nodes\n");
	}

	read_lock_bh(&mhi_cntrl->pm_lock);

	if (!MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) {
		ret = -EIO;
		goto exit_timesync;
	}

	/* get DB offset if supported, else return */
	ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->regs,
			   time_offset + TIMESYNC_DB_OFFSET, &db_offset);
	if (ret || !db_offset) {
		ret = 0;
		goto exit_timesync;
	}

	MHI_LOG("TIMESYNC_DB OFFS:0x%x\n", db_offset);
	mhi_tsync->db = mhi_cntrl->regs + db_offset;

	read_unlock_bh(&mhi_cntrl->pm_lock);

	/* get time-sync event ring configuration */
	ret = mhi_get_er_index(mhi_cntrl, MHI_ER_TSYNC_ELEMENT_TYPE);
	if (ret < 0) {
	/* get timesync event ring configuration */
	er_index = mhi_get_er_index(mhi_cntrl, MHI_ER_TSYNC_ELEMENT_TYPE);
	if (er_index < 0) {
		MHI_LOG("Could not find timesync event ring\n");
		return ret;
	}

	mhi_tsync->er_index = ret;

	ret = mhi_send_cmd(mhi_cntrl, NULL, MHI_CMD_TIMSYNC_CFG);
	if (ret) {
		MHI_ERR("Failed to send time sync cfg cmd\n");
		return ret;
		return er_index;
	}

	ret = wait_for_completion_timeout(&mhi_tsync->completion,
			msecs_to_jiffies(mhi_cntrl->timeout_ms));
	time_cfg_offset = time_offset + TIMESYNC_CFG_OFFSET;

	if (!ret || mhi_tsync->ccs != MHI_EV_CC_SUCCESS) {
		MHI_ERR("Failed to get time cfg cmd completion\n");
		return -EIO;
	}
	/* advertise host support */
	mhi_cntrl->write_reg(mhi_cntrl, mhi_cntrl->regs, time_cfg_offset,
			     MHI_TIMESYNC_DB_SETUP(er_index));

	return 0;

exit_timesync:
	read_unlock_bh(&mhi_cntrl->pm_lock);

	return ret;
}

int mhi_init_sfr(struct mhi_controller *mhi_cntrl)
@@ -875,7 +896,8 @@ int mhi_init_mmio(struct mhi_controller *mhi_cntrl)
	mhi_cntrl->write_reg(mhi_cntrl, mhi_cntrl->wake_db, 0, 0);
	mhi_cntrl->wake_set = false;

	/* setup bw scale db */
	/* setup special purpose doorbells (timesync, bw scale) */
	mhi_cntrl->tsync_db = base + val + (8 * MHI_TIMESYNC_CHAN_DB);
	mhi_cntrl->bw_scale_db = base + val + (8 * MHI_BW_SCALE_CHAN_DB);

	/* setup channel db addresses */
@@ -908,8 +930,9 @@ int mhi_init_mmio(struct mhi_controller *mhi_cntrl)
				    reg_info[i].mask, reg_info[i].shift,
				    reg_info[i].val);

	/* setup bandwidth scaling features */
	/* setup special purpose features such as timesync or bw scaling */
	mhi_init_bw_scale(mhi_cntrl);
	mhi_init_timesync(mhi_cntrl);

	return 0;
}
+18 −23
Original line number Diff line number Diff line
@@ -143,14 +143,14 @@ extern struct bus_type mhi_bus_type;
#define CAP_NEXT_CAP_SHIFT (12)

/* MHI Timesync offsets */
#define TIMESYNC_CFG_OFFSET (0x00)
#define TIMESYNC_CFG_CAPID_MASK (CAP_CAPID_MASK)
#define TIMESYNC_CFG_CAPID_SHIFT (CAP_CAPID_SHIFT)
#define TIMESYNC_CFG_NEXT_OFF_MASK (CAP_NEXT_CAP_MASK)
#define TIMESYNC_CFG_NEXT_OFF_SHIFT (CAP_NEXT_CAP_SHIFT)
#define TIMESYNC_CFG_NUMCMD_MASK (0xFF)
#define TIMESYNC_CFG_NUMCMD_SHIFT (0)
#define TIMESYNC_DB_OFFSET (0x4)
#define TIMESYNC_CFG_OFFSET (0x04)
#define TIMESYNC_CFG_ENABLED_MASK (0x80000000)
#define TIMESYNC_CFG_ENABLED_SHIFT (31)
#define TIMESYNC_CFG_CHAN_DB_ID_MASK (0x0000FF00)
#define TIMESYNC_CFG_CHAN_DB_ID_SHIFT (8)
#define TIMESYNC_CFG_ER_ID_MASK (0x000000FF)
#define TIMESYNC_CFG_ER_ID_SHIFT (0)

#define TIMESYNC_TIME_LOW_OFFSET (0x8)
#define TIMESYNC_TIME_HIGH_OFFSET (0xC)

@@ -324,12 +324,6 @@ enum mhi_cmd_type {
#define MHI_TRE_CMD_START_DWORD1(chid) ((chid << 24) | \
					(MHI_CMD_TYPE_START << 16))

/* time sync cfg command */
#define MHI_TRE_CMD_TSYNC_CFG_PTR (0)
#define MHI_TRE_CMD_TSYNC_CFG_DWORD0 (0)
#define MHI_TRE_CMD_TSYNC_CFG_DWORD1(er) ((MHI_CMD_TYPE_TSYNC << 16) | \
					  (er << 24))

/* subsystem failure reason cfg command */
#define MHI_TRE_CMD_SFR_CFG_PTR(ptr) (ptr)
#define MHI_TRE_CMD_SFR_CFG_DWORD0(len) (len)
@@ -350,6 +344,7 @@ enum mhi_cmd_type {
#define MHI_TRE_GET_EV_STATE(tre) (((tre)->dword[0] >> 24) & 0xFF)
#define MHI_TRE_GET_EV_EXECENV(tre) (((tre)->dword[0] >> 24) & 0xFF)
#define MHI_TRE_GET_EV_TSYNC_SEQ(tre) ((tre)->dword[0])
#define MHI_TRE_GET_EV_TSYNC_UNIT(tre) (((tre)->dword[1] >> 24) & 0xFF)
#define MHI_TRE_GET_EV_TIME(tre) ((tre)->ptr)
#define MHI_TRE_GET_EV_COOKIE(tre) lower_32_bits((tre)->ptr)
#define MHI_TRE_GET_EV_VEID(tre) (((tre)->dword[0] >> 16) & 0xFF)
@@ -374,7 +369,6 @@ enum MHI_CMD {
	MHI_CMD_RESET_CHAN,
	MHI_CMD_START_CHAN,
	MHI_CMD_STOP_CHAN,
	MHI_CMD_TIMSYNC_CFG,
	MHI_CMD_SFR_CFG,
};

@@ -529,10 +523,16 @@ enum MHI_XFER_TYPE {
#define NR_OF_CMD_RINGS (1)
#define CMD_EL_PER_RING (128)
#define PRIMARY_CMD_RING (0)
#define MHI_TIMESYNC_CHAN_DB (125)
#define MHI_BW_SCALE_CHAN_DB (126)
#define MHI_DEV_WAKE_DB (127)
#define MHI_MAX_MTU (0xffff)

#define MHI_TIMESYNC_DB_SETUP(er_index) ((MHI_TIMESYNC_CHAN_DB << \
	TIMESYNC_CFG_CHAN_DB_ID_SHIFT) & TIMESYNC_CFG_CHAN_DB_ID_MASK | \
	(1 << TIMESYNC_CFG_ENABLED_SHIFT) & TIMESYNC_CFG_ENABLED_MASK | \
	((er_index) << TIMESYNC_CFG_ER_ID_SHIFT) & TIMESYNC_CFG_ER_ID_MASK)

#define MHI_BW_SCALE_SETUP(er_index) ((MHI_BW_SCALE_CHAN_DB << \
	BW_SCALE_CFG_CHAN_DB_ID_SHIFT) & BW_SCALE_CFG_CHAN_DB_ID_MASK | \
	(1 << BW_SCALE_CFG_ENABLED_SHIFT) & BW_SCALE_CFG_ENABLED_MASK | \
@@ -716,6 +716,7 @@ struct mhi_chan {
struct tsync_node {
	struct list_head node;
	u32 sequence;
	u32 int_sequence;
	u64 local_time;
	u64 remote_time;
	struct mhi_device *mhi_dev;
@@ -724,11 +725,8 @@ struct tsync_node {
};

struct mhi_timesync {
	u32 er_index;
	void __iomem *db;
	void __iomem *time_reg;
	enum MHI_EV_CCS ccs;
	struct completion completion;
	u32 int_sequence;
	spinlock_t lock; /* list protection */
	struct list_head head;
};
@@ -840,11 +838,8 @@ void mhi_ring_chan_db(struct mhi_controller *mhi_cntrl,
int mhi_get_capability_offset(struct mhi_controller *mhi_cntrl, u32 capability,
			      u32 *offset);
void *mhi_to_virtual(struct mhi_ring *ring, dma_addr_t addr);
int mhi_init_timesync(struct mhi_controller *mhi_cntrl);
int mhi_init_sfr(struct mhi_controller *mhi_cntrl);
int mhi_create_timesync_sysfs(struct mhi_controller *mhi_cntrl);
void mhi_destroy_timesync(struct mhi_controller *mhi_cntrl);
int mhi_create_sysfs(struct mhi_controller *mhi_cntrl);
void mhi_create_sysfs(struct mhi_controller *mhi_cntrl);
void mhi_destroy_sysfs(struct mhi_controller *mhi_cntrl);
int mhi_early_notify_device(struct device *dev, void *data);
void mhi_write_reg_offload(struct mhi_controller *mhi_cntrl,
+63 −44
Original line number Diff line number Diff line
@@ -1184,7 +1184,6 @@ static void mhi_process_cmd_completion(struct mhi_controller *mhi_cntrl,
	struct mhi_ring *mhi_ring = &cmd_ring->ring;
	struct mhi_tre *cmd_pkt;
	struct mhi_chan *mhi_chan;
	struct mhi_timesync *mhi_tsync;
	struct mhi_sfr_info *sfr_info;
	enum mhi_cmd_type type;
	u32 chan;
@@ -1197,11 +1196,6 @@ static void mhi_process_cmd_completion(struct mhi_controller *mhi_cntrl,
	type = MHI_TRE_GET_CMD_TYPE(cmd_pkt);

	switch (type) {
	case MHI_CMD_TYPE_TSYNC:
		mhi_tsync = mhi_cntrl->mhi_tsync;
		mhi_tsync->ccs = MHI_TRE_GET_EV_CODE(tre);
		complete(&mhi_tsync->completion);
		break;
	case MHI_CMD_TYPE_SFR_CFG:
		sfr_info = mhi_cntrl->mhi_sfr;
		sfr_info->ccs = MHI_TRE_GET_EV_CODE(tre);
@@ -1433,7 +1427,7 @@ int mhi_process_tsync_event_ring(struct mhi_controller *mhi_cntrl,
		&mhi_cntrl->mhi_ctxt->er_ctxt[mhi_event->er_index];
	struct mhi_timesync *mhi_tsync = mhi_cntrl->mhi_tsync;
	int count = 0;
	u32 sequence;
	u32 int_sequence, unit;
	u64 remote_time;

	if (unlikely(MHI_EVENT_ACCESS_INVALID(mhi_cntrl->pm_state))) {
@@ -1454,30 +1448,29 @@ int mhi_process_tsync_event_ring(struct mhi_controller *mhi_cntrl,

		MHI_ASSERT(type != MHI_PKT_TYPE_TSYNC_EVENT, "!TSYNC event");

		sequence = MHI_TRE_GET_EV_TSYNC_SEQ(local_rp);
		int_sequence = MHI_TRE_GET_EV_TSYNC_SEQ(local_rp);
		unit = MHI_TRE_GET_EV_TSYNC_UNIT(local_rp);
		remote_time = MHI_TRE_GET_EV_TIME(local_rp);

		do {
			spin_lock_irq(&mhi_tsync->lock);
			spin_lock(&mhi_tsync->lock);
			tsync_node = list_first_entry_or_null(&mhi_tsync->head,
						      struct tsync_node, node);
			MHI_ASSERT(!tsync_node, "Unexpected Event");

			if (unlikely(!tsync_node)) {
				spin_unlock_irq(&mhi_tsync->lock);
			if (!tsync_node) {
				spin_unlock(&mhi_tsync->lock);
				break;
			}

			list_del(&tsync_node->node);
			spin_unlock_irq(&mhi_tsync->lock);
			spin_unlock(&mhi_tsync->lock);

			/*
			 * device may not able to process all time sync commands
			 * host issue and only process last command it receive
			 */
			if (tsync_node->sequence == sequence) {
			if (tsync_node->int_sequence == int_sequence) {
				tsync_node->cb_func(tsync_node->mhi_dev,
						    sequence,
						    tsync_node->sequence,
						    tsync_node->local_time,
						    remote_time);
				kfree(tsync_node);
@@ -1820,12 +1813,6 @@ int mhi_send_cmd(struct mhi_controller *mhi_cntrl,
		cmd_tre->dword[0] = MHI_TRE_CMD_STOP_DWORD0;
		cmd_tre->dword[1] = MHI_TRE_CMD_STOP_DWORD1(chan);
		break;
	case MHI_CMD_TIMSYNC_CFG:
		cmd_tre->ptr = MHI_TRE_CMD_TSYNC_CFG_PTR;
		cmd_tre->dword[0] = MHI_TRE_CMD_TSYNC_CFG_DWORD0;
		cmd_tre->dword[1] = MHI_TRE_CMD_TSYNC_CFG_DWORD1
			(mhi_cntrl->mhi_tsync->er_index);
		break;
	case MHI_CMD_SFR_CFG:
		sfr_info = mhi_cntrl->mhi_sfr;
		cmd_tre->ptr = MHI_TRE_CMD_SFR_CFG_PTR
@@ -1856,6 +1843,7 @@ int mhi_prepare_channel(struct mhi_controller *mhi_cntrl,
			struct mhi_chan *mhi_chan)
{
	int ret = 0;
	bool in_mission_mode = false;

	MHI_LOG("Entered: preparing channel:%d\n", mhi_chan->chan);

@@ -1894,7 +1882,12 @@ int mhi_prepare_channel(struct mhi_controller *mhi_cntrl,
		goto error_pm_state;
	}

	/* block host low power modes */
	if (MHI_IN_MISSION_MODE(mhi_cntrl->ee)) {
		atomic_inc(&mhi_cntrl->pending_pkts);
		in_mission_mode = true;
	}

	mhi_cntrl->wake_toggle(mhi_cntrl);
	if (MHI_PM_IN_SUSPEND_STATE(mhi_cntrl->pm_state))
		mhi_trigger_resume(mhi_cntrl);
@@ -1915,6 +1908,7 @@ int mhi_prepare_channel(struct mhi_controller *mhi_cntrl,
		goto error_dec_pendpkt;
	}

	if (in_mission_mode)
		atomic_dec(&mhi_cntrl->pending_pkts);

	write_lock_irq(&mhi_chan->lock);
@@ -2099,6 +2093,7 @@ static void __mhi_unprepare_channel(struct mhi_controller *mhi_cntrl,
				    struct mhi_chan *mhi_chan)
{
	int ret;
	bool in_mission_mode = false;

	MHI_LOG("Entered: unprepare channel:%d\n", mhi_chan->chan);

@@ -2122,7 +2117,12 @@ static void __mhi_unprepare_channel(struct mhi_controller *mhi_cntrl,
		goto error_invalid_state;
	}

	/* block host low power modes */
	if (MHI_IN_MISSION_MODE(mhi_cntrl->ee)) {
		atomic_inc(&mhi_cntrl->pending_pkts);
		in_mission_mode = true;
	}

	mhi_cntrl->wake_toggle(mhi_cntrl);
	if (MHI_PM_IN_SUSPEND_STATE(mhi_cntrl->pm_state))
		mhi_trigger_resume(mhi_cntrl);
@@ -2141,6 +2141,7 @@ static void __mhi_unprepare_channel(struct mhi_controller *mhi_cntrl,
		MHI_ERR("Failed to receive cmd completion, still resetting\n");

error_dec_pendpkt:
	if (in_mission_mode)
		atomic_dec(&mhi_cntrl->pending_pkts);
error_invalid_state:
	if (!mhi_chan->offload_ch) {
@@ -2350,6 +2351,7 @@ static int mhi_update_channel_state(struct mhi_controller *mhi_cntrl,
				    enum MHI_CMD cmd)
{
	int ret = -EIO;
	bool in_mission_mode = false;

	mutex_lock(&mhi_chan->mutex);

@@ -2372,7 +2374,12 @@ static int mhi_update_channel_state(struct mhi_controller *mhi_cntrl,
		goto error_chan_state;
	}

	/* block host low power modes */
	if (MHI_IN_MISSION_MODE(mhi_cntrl->ee)) {
		atomic_inc(&mhi_cntrl->pending_pkts);
		in_mission_mode = true;
	}

	mhi_cntrl->wake_toggle(mhi_cntrl);
	if (MHI_PM_IN_SUSPEND_STATE(mhi_cntrl->pm_state))
		mhi_trigger_resume(mhi_cntrl);
@@ -2399,6 +2406,7 @@ static int mhi_update_channel_state(struct mhi_controller *mhi_cntrl,
		 mhi_chan->chan, cmd == MHI_CMD_START_CHAN ? "START" : "STOP");

error_dec_pendpkt:
	if (in_mission_mode)
		atomic_dec(&mhi_cntrl->pending_pkts);
error_chan_state:
	mutex_unlock(&mhi_chan->mutex);
@@ -2616,13 +2624,23 @@ int mhi_get_remote_time(struct mhi_device *mhi_dev,
	mutex_lock(&mhi_cntrl->tsync_mutex);
	if (!mhi_tsync) {
		ret = -EIO;
		goto err_unlock;
		goto error_unlock;
	}

	/* tsync db can only be rung in M0 state */
	ret = __mhi_device_get_sync(mhi_cntrl);
	if (ret)
		goto err_unlock;
		goto error_unlock;

	read_lock_bh(&mhi_cntrl->pm_lock);
	if (unlikely(MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state))) {
		MHI_ERR("MHI is not in active state, pm_state:%s\n",
			to_mhi_pm_state_str(mhi_cntrl->pm_state));
		ret = -EIO;
		read_unlock_bh(&mhi_cntrl->pm_lock);
		goto error_no_mem;
	}
	read_unlock_bh(&mhi_cntrl->pm_lock);

	/*
	 * technically we can use GFP_KERNEL, but wants to avoid
@@ -2634,24 +2652,26 @@ int mhi_get_remote_time(struct mhi_device *mhi_dev,
		goto error_no_mem;
	}

	mhi_tsync->int_sequence++;
	if (mhi_tsync->int_sequence == 0xFFFFFFFF)
		mhi_tsync->int_sequence = 0;

	tsync_node->sequence = sequence;
	tsync_node->int_sequence = mhi_tsync->int_sequence;
	tsync_node->cb_func = cb_func;
	tsync_node->mhi_dev = mhi_dev;

	/* disable link level low power modes */
	mhi_cntrl->lpm_disable(mhi_cntrl, mhi_cntrl->priv_data);

	read_lock_bh(&mhi_cntrl->pm_lock);
	if (unlikely(MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state))) {
		MHI_ERR("MHI is not in active state, pm_state:%s\n",
			to_mhi_pm_state_str(mhi_cntrl->pm_state));
		ret = -EIO;
	ret = mhi_cntrl->lpm_disable(mhi_cntrl, mhi_cntrl->priv_data);
	if (ret) {
		MHI_ERR("LPM disable request failed for %s!\n",
			mhi_dev->chan_name);
		goto error_invalid_state;
	}

	spin_lock_irq(&mhi_tsync->lock);
	spin_lock(&mhi_tsync->lock);
	list_add_tail(&tsync_node->node, &mhi_tsync->head);
	spin_unlock_irq(&mhi_tsync->lock);
	spin_unlock(&mhi_tsync->lock);

	/*
	 * time critical code, delay between these two steps should be
@@ -2662,26 +2682,25 @@ int mhi_get_remote_time(struct mhi_device *mhi_dev,

	tsync_node->local_time =
		mhi_cntrl->time_get(mhi_cntrl, mhi_cntrl->priv_data);
	writel_relaxed_no_log(tsync_node->sequence, mhi_tsync->db);
	writel_relaxed_no_log(tsync_node->int_sequence, mhi_cntrl->tsync_db);
	/* write must go thru immediately */
	wmb();

	local_irq_enable();
	preempt_enable();

	mhi_cntrl->lpm_enable(mhi_cntrl, mhi_cntrl->priv_data);

	ret = 0;

error_invalid_state:
	if (ret)
		kfree(tsync_node);
	read_unlock_bh(&mhi_cntrl->pm_lock);
	mhi_cntrl->lpm_enable(mhi_cntrl, mhi_cntrl->priv_data);

error_no_mem:
	read_lock_bh(&mhi_cntrl->pm_lock);
	mhi_cntrl->wake_put(mhi_cntrl, false);
	read_unlock_bh(&mhi_cntrl->pm_lock);
err_unlock:
error_unlock:
	mutex_unlock(&mhi_cntrl->tsync_mutex);
	return ret;
}
+1 −5
Original line number Diff line number Diff line
@@ -528,9 +528,8 @@ static int mhi_pm_mission_mode_transition(struct mhi_controller *mhi_cntrl)

	read_unlock_bh(&mhi_cntrl->pm_lock);

	/* setup support for additional features (SFR, timesync, etc.) */
	/* setup support for additional features */
	mhi_init_sfr(mhi_cntrl);
	mhi_init_timesync(mhi_cntrl);

	if (MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state))
		mhi_timesync_log(mhi_cntrl);
@@ -657,9 +656,6 @@ static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl,
	wake_up_all(&mhi_cntrl->state_event);
	flush_work(&mhi_cntrl->special_work);

	/* remove support for time sync */
	mhi_destroy_timesync(mhi_cntrl);

	if (sfr_info && sfr_info->buf_addr) {
		mhi_free_coherent(mhi_cntrl, sfr_info->len, sfr_info->buf_addr,
				  sfr_info->dma_addr);
+18 −0
Original line number Diff line number Diff line
@@ -270,6 +270,7 @@ struct mhi_controller {
	void __iomem *bhi;
	void __iomem *bhie;
	void __iomem *wake_db;
	void __iomem *tsync_db;
	void __iomem *bw_scale_db;

	/* device topology */
@@ -781,6 +782,23 @@ int mhi_force_rddm_mode(struct mhi_controller *mhi_cntrl);
 */
void mhi_dump_sfr(struct mhi_controller *mhi_cntrl);

/**
 * mhi_get_remote_time - Get external modem time relative to host time
 * Trigger event to capture modem time, also capture host time so client
 * can do a relative drift comparision.
 * Recommended only tsync device calls this method and do not call this
 * from atomic context
 * @mhi_dev: Device associated with the channels
 * @sequence:unique sequence id track event
 * @cb_func: callback function to call back
 */
int mhi_get_remote_time(struct mhi_device *mhi_dev,
			u32 sequence,
			void (*cb_func)(struct mhi_device *mhi_dev,
					u32 sequence,
					u64 local_time,
					u64 remote_time));

/**
 * mhi_get_remote_time_sync - Get external soc time relative to local soc time
 * using MMIO method.