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

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

Merge "mhi: core: use internal sequence numbers for timesync doorbells"

parents 441ffe3a 05722ed6
Loading
Loading
Loading
Loading
+13 −10
Original line number Diff line number Diff line
@@ -334,7 +334,6 @@ static void mhi_boot_monitor(void *data, async_cookie_t cookie)
	struct mhi_controller *mhi_cntrl = data;
	struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
	struct arch_info *arch_info = mhi_dev->arch_info;
	struct mhi_device *boot_dev;
	/* 15 sec timeout for booting device */
	const u32 timeout = msecs_to_jiffies(15000);

@@ -346,15 +345,6 @@ static void mhi_boot_monitor(void *data, async_cookie_t cookie)

	ipc_log_string(arch_info->boot_ipc_log, HLOG "Device current ee = %s\n",
		       TO_MHI_EXEC_STR(mhi_cntrl->ee));

	/* if we successfully booted to amss disable boot log channel */
	if (mhi_cntrl->ee == MHI_EE_AMSS) {
		boot_dev = arch_info->boot_dev;
		if (boot_dev)
			mhi_unprepare_from_transfer(boot_dev);

		pm_runtime_allow(&mhi_dev->pci_dev->dev);
	}
}

int mhi_arch_power_up(struct mhi_controller *mhi_cntrl)
@@ -369,6 +359,19 @@ int mhi_arch_power_up(struct mhi_controller *mhi_cntrl)
	return 0;
}

void mhi_arch_mission_mode_enter(struct mhi_controller *mhi_cntrl)
{
	struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
	struct arch_info *arch_info = mhi_dev->arch_info;
	struct mhi_device *boot_dev = arch_info->boot_dev;

	/* disable boot logger channel */
	if (boot_dev)
		mhi_unprepare_from_transfer(boot_dev);

	pm_runtime_allow(&mhi_dev->pci_dev->dev);
}

static  int mhi_arch_pcie_scale_bw(struct mhi_controller *mhi_cntrl,
				   struct pci_dev *pci_dev,
				   struct mhi_link_info *link_info)
+2 −1
Original line number Diff line number Diff line
/* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
/* Copyright (c) 2018-2020, 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
@@ -606,6 +606,7 @@ static void mhi_status_cb(struct mhi_controller *mhi_cntrl,
		if (!ret)
			mhi_runtime_resume(dev);
		pm_runtime_put(dev);
		mhi_arch_mission_mode_enter(mhi_cntrl);
		break;
	default:
		MHI_ERR("Unhandled cb:0x%x\n", reason);
+6 −1
Original line number Diff line number Diff line
/* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
/* Copyright (c) 2018-2020, 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
@@ -73,6 +73,7 @@ void mhi_reg_write_work(struct work_struct *w);

#ifdef CONFIG_ARCH_QCOM

void mhi_arch_mission_mode_enter(struct mhi_controller *mhi_cntrl);
int mhi_arch_power_up(struct mhi_controller *mhi_cntrl);
int mhi_arch_pcie_init(struct mhi_controller *mhi_cntrl);
void mhi_arch_pcie_deinit(struct mhi_controller *mhi_cntrl);
@@ -120,6 +121,10 @@ static inline int mhi_arch_power_up(struct mhi_controller *mhi_cntrl)
	return 0;
}

static inline void mhi_arch_mission_mode_enter(struct mhi_controller *mhi_cntrl)
{
}

#endif

#endif /* _MHI_QCOM_ */
+119 −0
Original line number Diff line number Diff line
@@ -26,6 +26,125 @@
#include <linux/mhi.h>
#include "mhi_internal.h"

static void mhi_process_sfr(struct mhi_controller *mhi_cntrl,
	struct file_info *info)
{
	struct mhi_buf *mhi_buf = mhi_cntrl->rddm_image->mhi_buf;
	u8 *sfr_buf, *file_offset = info->file_offset;
	u32 file_size = info->file_size;
	u32 rem_seg_len = info->rem_seg_len;
	u32 seg_idx = info->seg_idx;

	sfr_buf = kzalloc(file_size + 1, GFP_KERNEL);
	if (!sfr_buf)
		return;

	while (file_size) {
		/* file offset starting from seg base */
		if (!rem_seg_len) {
			file_offset = mhi_buf[seg_idx].buf;
			if (file_size > mhi_buf[seg_idx].len)
				rem_seg_len = mhi_buf[seg_idx].len;
			else
				rem_seg_len = file_size;
		}

		if (file_size <= rem_seg_len) {
			memcpy(sfr_buf, file_offset, file_size);
			break;
		}

		memcpy(sfr_buf, file_offset, rem_seg_len);
		sfr_buf += rem_seg_len;
		file_size -= rem_seg_len;
		rem_seg_len = 0;
		seg_idx++;
		if (seg_idx == mhi_cntrl->rddm_image->entries) {
			MHI_ERR("invalid size for SFR file\n");
			goto err;
		}
	}
	sfr_buf[info->file_size] = '\0';

	/* force sfr string to log in kernel msg */
	MHI_ERR("%s\n", sfr_buf);
err:
	kfree(sfr_buf);
}

static int mhi_find_next_file_offset(struct mhi_controller *mhi_cntrl,
	struct file_info *info, struct rddm_table_info *table_info)
{
	struct mhi_buf *mhi_buf = mhi_cntrl->rddm_image->mhi_buf;

	if (info->rem_seg_len >= table_info->size) {
		info->file_offset += (size_t)table_info->size;
		info->rem_seg_len -= table_info->size;
		return 0;
	}

	info->file_size = table_info->size - info->rem_seg_len;
	info->rem_seg_len = 0;
	/* iterate over segments until eof is reached */
	while (info->file_size) {
		info->seg_idx++;
		if (info->seg_idx == mhi_cntrl->rddm_image->entries) {
			MHI_ERR("invalid size for file %s\n",
					table_info->file_name);
			return -EINVAL;
		}
		if (info->file_size > mhi_buf[info->seg_idx].len) {
			info->file_size -= mhi_buf[info->seg_idx].len;
		} else {
			info->file_offset = mhi_buf[info->seg_idx].buf +
				info->file_size;
			info->rem_seg_len = mhi_buf[info->seg_idx].len -
				info->file_size;
			info->file_size = 0;
		}
	}

	return 0;
}

void mhi_dump_sfr(struct mhi_controller *mhi_cntrl)
{
	struct mhi_buf *mhi_buf = mhi_cntrl->rddm_image->mhi_buf;
	struct rddm_header *rddm_header =
		(struct rddm_header *)mhi_buf->buf;
	struct rddm_table_info *table_info;
	struct file_info info = {NULL};
	u32 table_size, n;

	if (rddm_header->header_size > sizeof(*rddm_header) ||
			rddm_header->header_size < 8) {
		MHI_ERR("invalid reported header size %u\n",
				rddm_header->header_size);
		return;
	}

	table_size = (rddm_header->header_size - 8) / sizeof(*table_info);
	if (!table_size) {
		MHI_ERR("invalid rddm table size %u\n", table_size);
		return;
	}

	info.file_offset = (u8 *)rddm_header + rddm_header->header_size;
	info.rem_seg_len = mhi_buf[0].len - rddm_header->header_size;
	for (n = 0; n < table_size; n++) {
		table_info = &rddm_header->table_info[n];

		if (!strcmp(table_info->file_name, "Q6-SFR.bin")) {
			info.file_size = table_info->size;
			mhi_process_sfr(mhi_cntrl, &info);
			return;
		}

		if (mhi_find_next_file_offset(mhi_cntrl, &info, table_info))
			return;
	}
}
EXPORT_SYMBOL(mhi_dump_sfr);

/* setup rddm vector table for rddm transfer and program rxvec */
void mhi_rddm_prepare(struct mhi_controller *mhi_cntrl,
+97 −77
Original line number Diff line number Diff line
@@ -105,6 +105,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)
@@ -206,15 +257,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);

@@ -590,40 +662,30 @@ 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;

	/* 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);
@@ -632,61 +694,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;
		return er_index;
	}

	mhi_tsync->er_index = ret;
	time_cfg_offset = time_offset + TIMESYNC_CFG_OFFSET;

	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;
	}
	/* 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)
@@ -851,7 +872,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 */
@@ -884,8 +906,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;
}
@@ -1498,6 +1521,7 @@ int of_register_mhi_controller(struct mhi_controller *mhi_cntrl)
	}

	mhi_dev->dev_type = MHI_CONTROLLER_TYPE;
	mhi_dev->chan_name = mhi_cntrl->name;
	mhi_dev->mhi_cntrl = mhi_cntrl;
	dev_set_name(&mhi_dev->dev, "%04x_%02u.%02u.%02u", mhi_dev->dev_id,
		     mhi_dev->domain, mhi_dev->bus, mhi_dev->slot);
@@ -1859,10 +1883,6 @@ static int mhi_driver_remove(struct device *dev)
		mutex_unlock(&mhi_chan->mutex);
	}


	if (mhi_cntrl->tsync_dev == mhi_dev)
		mhi_cntrl->tsync_dev = NULL;

	/* relinquish any pending votes for device */
	while (atomic_read(&mhi_dev->dev_vote))
		mhi_device_put(mhi_dev, MHI_VOTE_DEVICE);
Loading