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

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

Merge "mhi: core: add time synchronization feature support"

parents 05c0685f b068f5ed
Loading
Loading
Loading
Loading
+80 −79
Original line number Diff line number Diff line
@@ -403,72 +403,113 @@ int mhi_init_dev_ctxt(struct mhi_controller *mhi_cntrl)
	return ret;
}

static int mhi_get_tsync_er_cfg(struct mhi_controller *mhi_cntrl)
{
	int i;
	struct mhi_event *mhi_event = mhi_cntrl->mhi_event;

	/* find event ring with timesync support */
	for (i = 0; i < mhi_cntrl->total_ev_rings; i++, mhi_event++)
		if (mhi_event->data_type == MHI_ER_TSYNC_ELEMENT_TYPE)
			return mhi_event->er_index;

	return -ENOENT;
}

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

	reinit_completion(&mhi_tsync->completion);
	read_lock_bh(&mhi_cntrl->pm_lock);
	if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) {
		MHI_ERR("MHI host is not in active state\n");
		read_unlock_bh(&mhi_cntrl->pm_lock);
		return -EIO;
	}

	mhi_cntrl->wake_get(mhi_cntrl, false);
	read_unlock_bh(&mhi_cntrl->pm_lock);
	mhi_cntrl->runtime_get(mhi_cntrl, mhi_cntrl->priv_data);
	mhi_cntrl->runtime_put(mhi_cntrl, mhi_cntrl->priv_data);
	if (!MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) {
		ret = -EIO;
		goto exit_timesync;
	}

	ret = mhi_send_cmd(mhi_cntrl, NULL, MHI_CMD_TIMSYNC_CFG);
	ret = mhi_get_capability_offset(mhi_cntrl, TIMESYNC_CAP_ID,
					&time_offset);
	if (ret) {
		MHI_ERR("Failed to send time sync cfg cmd\n");
		goto error_send_cmd;
		MHI_LOG("No timesync capability found\n");
		goto exit_timesync;
	}

	ret = wait_for_completion_timeout(&mhi_tsync->completion,
				msecs_to_jiffies(mhi_cntrl->timeout_ms));
	read_unlock_bh(&mhi_cntrl->pm_lock);

	if (!ret || mhi_tsync->ccs != MHI_EV_CC_SUCCESS) {
		MHI_ERR("Failed to receive cmd completion for time_sync_cfg\n");
		ret = -EIO;
		goto error_send_cmd;
	if (!mhi_cntrl->time_get || !mhi_cntrl->lpm_disable ||
	     !mhi_cntrl->lpm_enable)
		return -EINVAL;

	/* register method supported */
	mhi_tsync = kzalloc(sizeof(*mhi_tsync), GFP_KERNEL);
	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);
	mhi_tsync->time_reg = mhi_cntrl->regs + time_offset
			      + TIMESYNC_TIME_LOW_OFFSET;

	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);
	mhi_cntrl->wake_put(mhi_cntrl, false);

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

	if (!MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state))
		goto error_sync_cap;

	ret = mhi_get_capability_offset(mhi_cntrl, TIMESYNC_CAP_ID,
					&time_offset);
	if (ret) {
		MHI_ERR("could not find timesync capability\n");
		goto error_sync_cap;
		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)
		goto error_sync_cap;
	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;

error_sync_cap:
	read_unlock_bh(&mhi_cntrl->pm_lock);

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

error_send_cmd:
	read_lock_bh(&mhi_cntrl->pm_lock);
	mhi_cntrl->wake_put(mhi_cntrl, false);
	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;
	}

	ret = wait_for_completion_timeout(&mhi_tsync->completion,
			msecs_to_jiffies(mhi_cntrl->timeout_ms));

	if (!ret || mhi_tsync->ccs != MHI_EV_CC_SUCCESS) {
		MHI_ERR("Failed to get time cfg cmd completion\n");
		return -EIO;
	}

	return 0;

exit_timesync:
	read_unlock_bh(&mhi_cntrl->pm_lock);

	return ret;
@@ -978,7 +1019,6 @@ static int of_parse_dt(struct mhi_controller *mhi_cntrl,
		       struct device_node *of_node)
{
	int ret;
	struct mhi_timesync *mhi_tsync;

	/* parse MHI channel configuration */
	ret = of_parse_ch_cfg(mhi_cntrl, of_node);
@@ -995,28 +1035,6 @@ static int of_parse_dt(struct mhi_controller *mhi_cntrl,
	if (ret)
		mhi_cntrl->timeout_ms = MHI_TIMEOUT_MS;

	mhi_cntrl->time_sync = of_property_read_bool(of_node, "mhi,time-sync");

	if (mhi_cntrl->time_sync) {
		mhi_tsync = kzalloc(sizeof(*mhi_tsync), GFP_KERNEL);
		if (!mhi_tsync) {
			ret = -ENOMEM;
			goto error_time_sync;
		}

		ret = of_property_read_u32(of_node, "mhi,tsync-er",
					   &mhi_tsync->er_index);
		if (ret)
			goto error_time_sync;

		if (mhi_tsync->er_index >= mhi_cntrl->total_ev_rings) {
			ret = -EINVAL;
			goto error_time_sync;
		}

		mhi_cntrl->mhi_tsync = mhi_tsync;
	}

	mhi_cntrl->bounce_buf = of_property_read_bool(of_node, "mhi,use-bb");
	ret = of_property_read_u32(of_node, "mhi,buffer-len",
				   (u32 *)&mhi_cntrl->buffer_len);
@@ -1025,10 +1043,6 @@ static int of_parse_dt(struct mhi_controller *mhi_cntrl,

	return 0;

error_time_sync:
	kfree(mhi_tsync);
	kfree(mhi_cntrl->mhi_event);

error_ev_cfg:
	kfree(mhi_cntrl->mhi_chan);

@@ -1057,11 +1071,6 @@ int of_register_mhi_controller(struct mhi_controller *mhi_cntrl)
	if (ret)
		return -EINVAL;

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

	mhi_cntrl->mhi_cmd = kcalloc(NR_OF_CMD_RINGS,
				     sizeof(*mhi_cntrl->mhi_cmd), GFP_KERNEL);
	if (!mhi_cntrl->mhi_cmd) {
@@ -1105,14 +1114,6 @@ int of_register_mhi_controller(struct mhi_controller *mhi_cntrl)
		rwlock_init(&mhi_chan->lock);
	}

	if (mhi_cntrl->mhi_tsync) {
		struct mhi_timesync *mhi_tsync = mhi_cntrl->mhi_tsync;

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

	if (mhi_cntrl->bounce_buf) {
		mhi_cntrl->map_single = mhi_map_single_use_bb;
		mhi_cntrl->unmap_single = mhi_unmap_single_use_bb;
+11 −4
Original line number Diff line number Diff line
@@ -143,9 +143,9 @@ extern struct bus_type mhi_bus_type;
#define TIMESYNC_CFG_NEXT_OFF_SHIFT (CAP_NEXT_CAP_SHIFT)
#define TIMESYNC_CFG_NUMCMD_MASK (0xFF)
#define TIMESYNC_CFG_NUMCMD_SHIFT (0)
#define TIMESYNC_TIME_LOW_OFFSET (0x4)
#define TIMESYNC_TIME_HIGH_OFFSET (0x8)
#define TIMESYNC_DB_OFFSET (0xC)
#define TIMESYNC_DB_OFFSET (0x4)
#define TIMESYNC_TIME_LOW_OFFSET (0x8)
#define TIMESYNC_TIME_HIGH_OFFSET (0xC)

#define TIMESYNC_CAP_ID (2)

@@ -211,6 +211,9 @@ extern struct bus_type mhi_bus_type;
#define BHIE_RXVECSTATUS_STATUS_XFER_COMPL (0x02)
#define BHIE_RXVECSTATUS_STATUS_ERROR (0x03)

/* convert ticks to micro seconds by dividing by 19.2 */
#define TIME_TICKS_TO_US(x) (((x) * 10) / 192)

struct mhi_event_ctxt {
	u32 reserved : 8;
	u32 intmodc : 8;
@@ -614,9 +617,11 @@ struct tsync_node {
struct mhi_timesync {
	u32 er_index;
	void __iomem *db;
	void __iomem *time_reg;
	enum MHI_EV_CCS ccs;
	struct completion completion;
	spinlock_t lock;
	spinlock_t lock; /* list protection */
	struct mutex lpm_mutex; /* lpm protection */
	struct list_head head;
};

@@ -707,6 +712,8 @@ void mhi_set_mhi_state(struct mhi_controller *mhi_cntrl,
int mhi_get_capability_offset(struct mhi_controller *mhi_cntrl, u32 capability,
			      u32 *offset);
int mhi_init_timesync(struct mhi_controller *mhi_cntrl);
int mhi_create_timesync_sysfs(struct mhi_controller *mhi_cntrl);
void mhi_destroy_timesync(struct mhi_controller *mhi_cntrl);

/* memory allocation methods */
static inline void *mhi_alloc_coherent(struct mhi_controller *mhi_cntrl,
+122 −4
Original line number Diff line number Diff line
@@ -595,15 +595,77 @@ static void mhi_assign_of_node(struct mhi_controller *mhi_cntrl,
	}
}

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",
			 TIME_TICKS_TO_US(t_host), TIME_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,
};

void mhi_destroy_timesync(struct mhi_controller *mhi_cntrl)
{
	if (mhi_cntrl->mhi_tsync) {
		sysfs_remove_group(&mhi_cntrl->mhi_dev->dev.kobj,
				   &mhi_tsync_group);
		kfree(mhi_cntrl->mhi_tsync);
		mhi_cntrl->mhi_tsync = NULL;
	}
}

int mhi_create_timesync_sysfs(struct mhi_controller *mhi_cntrl)
{
	return sysfs_create_group(&mhi_cntrl->mhi_dev->dev.kobj,
				  &mhi_tsync_group);
}

static void mhi_create_time_sync_dev(struct mhi_controller *mhi_cntrl)
{
	struct mhi_device *mhi_dev;
	struct mhi_timesync *mhi_tsync = mhi_cntrl->mhi_tsync;
	int ret;

	if (!mhi_tsync || !mhi_tsync->db)
		return;

	if (!MHI_IN_MISSION_MODE(mhi_cntrl->ee))
		return;

@@ -1779,6 +1841,62 @@ int mhi_poll(struct mhi_device *mhi_dev,
}
EXPORT_SYMBOL(mhi_poll);

int mhi_get_remote_time_sync(struct mhi_device *mhi_dev,
			     u64 *t_host,
			     u64 *t_dev)
{
	struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
	struct mhi_timesync *mhi_tsync = mhi_cntrl->mhi_tsync;
	int ret;

	/* not all devices support time feature */
	if (!mhi_tsync)
		return -EIO;

	/* bring to M0 state */
	ret = __mhi_device_get_sync(mhi_cntrl);
	if (ret)
		return ret;

	mutex_lock(&mhi_tsync->lpm_mutex);

	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;
		goto error_invalid_state;
	}

	/* disable link level low power modes */
	ret = mhi_cntrl->lpm_disable(mhi_cntrl, mhi_cntrl->priv_data);
	if (ret)
		goto error_invalid_state;

	/*
	 * time critical code to fetch device times,
	 * delay between these two steps should be
	 * deterministic as possible.
	 */
	preempt_disable();
	local_irq_disable();

	*t_host = mhi_cntrl->time_get(mhi_cntrl, mhi_cntrl->priv_data);
	*t_dev = readq_relaxed_no_log(mhi_tsync->time_reg);

	local_irq_enable();
	preempt_enable();

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

error_invalid_state:
	mhi_cntrl->wake_put(mhi_cntrl, false);
	read_unlock_bh(&mhi_cntrl->pm_lock);
	mutex_unlock(&mhi_tsync->lpm_mutex);

	return ret;
}
EXPORT_SYMBOL(mhi_get_remote_time_sync);

/**
 * mhi_get_remote_time - Get external modem time relative to host time
+4 −5
Original line number Diff line number Diff line
@@ -462,7 +462,6 @@ static int mhi_pm_mission_mode_transition(struct mhi_controller *mhi_cntrl)
	read_unlock_bh(&mhi_cntrl->pm_lock);

	/* setup support for time sync */
	if (mhi_cntrl->time_sync)
	mhi_init_timesync(mhi_cntrl);

	MHI_LOG("Adding new devices\n");
@@ -601,6 +600,9 @@ static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl,
		er_ctxt->wp = er_ctxt->rbase;
	}

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

	if (cur_state == MHI_PM_SYS_ERR_PROCESS) {
		mhi_ready_state_transition(mhi_cntrl);
	} else {
@@ -867,9 +869,6 @@ void mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful)
		mhi_deinit_free_irq(mhi_cntrl);
		mhi_deinit_dev_ctxt(mhi_cntrl);
	}

	if (mhi_cntrl->mhi_tsync)
		mhi_cntrl->mhi_tsync->db = NULL;
}
EXPORT_SYMBOL(mhi_power_down);

+11 −1
Original line number Diff line number Diff line
@@ -267,7 +267,6 @@ struct mhi_controller {
	size_t buffer_len;

	/* supports time sync feature */
	bool time_sync;
	struct mhi_timesync *mhi_tsync;
	struct mhi_device *tsync_dev;

@@ -595,6 +594,17 @@ int mhi_download_rddm_img(struct mhi_controller *mhi_cntrl, bool in_panic);
 */
int mhi_force_rddm_mode(struct mhi_controller *mhi_cntrl);

/**
 * mhi_get_remote_time_sync - Get external soc time relative to local soc time
 * using MMIO method.
 * @mhi_dev: Device associated with the channels
 * @t_host: Pointer to output local soc time
 * @t_dev: Pointer to output remote soc time
 */
int mhi_get_remote_time_sync(struct mhi_device *mhi_dev,
			     u64 *t_host,
			     u64 *t_dev);

#ifndef CONFIG_ARCH_QCOM

#ifdef CONFIG_MHI_DEBUG