Loading drivers/bus/mhi/controllers/mhi_arch_qcom.c +13 −10 Original line number Diff line number Diff line Loading @@ -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); Loading @@ -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) Loading @@ -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) Loading drivers/bus/mhi/controllers/mhi_qcom.c +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 Loading Loading @@ -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); Loading drivers/bus/mhi/controllers/mhi_qcom.h +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 Loading Loading @@ -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); Loading Loading @@ -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_ */ drivers/bus/mhi/core/mhi_boot.c +119 −0 Original line number Diff line number Diff line Loading @@ -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, Loading drivers/bus/mhi/core/mhi_init.c +97 −77 Original line number Diff line number Diff line Loading @@ -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) Loading Loading @@ -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); Loading Loading @@ -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); Loading @@ -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) Loading Loading @@ -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 */ Loading Loading @@ -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; } Loading Loading @@ -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); Loading Loading @@ -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 Loading
drivers/bus/mhi/controllers/mhi_arch_qcom.c +13 −10 Original line number Diff line number Diff line Loading @@ -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); Loading @@ -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) Loading @@ -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) Loading
drivers/bus/mhi/controllers/mhi_qcom.c +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 Loading Loading @@ -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); Loading
drivers/bus/mhi/controllers/mhi_qcom.h +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 Loading Loading @@ -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); Loading Loading @@ -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_ */
drivers/bus/mhi/core/mhi_boot.c +119 −0 Original line number Diff line number Diff line Loading @@ -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, Loading
drivers/bus/mhi/core/mhi_init.c +97 −77 Original line number Diff line number Diff line Loading @@ -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) Loading Loading @@ -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); Loading Loading @@ -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); Loading @@ -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) Loading Loading @@ -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 */ Loading Loading @@ -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; } Loading Loading @@ -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); Loading Loading @@ -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