blob: a0e7904a5b78909c8aee7a5d5fcf981f1a63a752 [file] [log] [blame]
/*
* Copyright (c) 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
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/module.h>
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/of_device.h>
#include <linux/slab.h>
#include "commonmhitest.h"
static bool rddm_r;
module_param(rddm_r, bool, 0);
MODULE_PARM_DESC(rddm_r, "Do need to go for recovery after rddm?");
/*
* 0 - MHITEST_LOG_LVL_VERBOSE
* 1 - MHITEST_LOG_LVL_INFO
* 2 - MHITEST_LOG_LVL_ERR
*/
int debug_lvl = MHITEST_LOG_LVL_ERR; /* Let' go with default as ERR */
module_param(debug_lvl, int, 0);
MODULE_PARM_DESC(debug_lvl, "Debug level for mhitest driver 0-VERB,1-INFO,2-ERR");
int timeout_ms = MHI_TIMEOUT_DEFAULT;
module_param(timeout_ms, int, 0);
MODULE_PARM_DESC(timeout_ms, "timeout mhi test");
static int d_instance;
static struct mhitest_platform *mplat_g[MHI_MAX_DEVICE];
static struct platform_device *m_plat_dev;
struct platform_device *get_plat_device(void)
{
return m_plat_dev;
}
int mhitest_store_mplat(struct mhitest_platform *temp)
{
if (d_instance < MHI_MAX_DEVICE) {
mplat_g[d_instance] = temp;
mplat_g[d_instance]->d_instance = d_instance;
MHITEST_VERB("mplat_g[%d]:%p temp:%p same ? d_instance:%d\n",
d_instance, mplat_g[d_instance], temp, d_instance);
d_instance++;
return 0;
}
MHITEST_ERR("Error Max device support count exceeds\n");
return 1;
}
void mhitest_free_mplat(struct mhitest_platform *temp)
{
devm_kfree(&temp->plat_dev->dev, temp);
}
struct mhitest_platform *get_mhitest_mplat(int id)
{
return mplat_g[id];
}
struct mhitest_platform *get_mhitest_mplat_by_pcidev(struct pci_dev *pci_dev)
{
int i;
for (i = 0; i < MHI_MAX_DEVICE; i++) {
if (mplat_g[i]->pci_dev == pci_dev)
return mplat_g[i];
}
return NULL;
}
char *mhitest_recov_reason_to_str(enum mhitest_recovery_reason reason)
{
switch (reason) {
case MHI_DEFAULT:
return "MHI_DEFAULT";
case MHI_LINK_DOWN:
return "MHI_LINK_DOWN";
case MHI_RDDM:
return "MHI_RDDM";
case MHI_TIMEOUT:
return "MHI_TIMEOUT";
default:
return "UNKNOWN";
}
}
void mhitest_recovery_post_rddm(struct mhitest_platform *mplat)
{
int ret;
MHITEST_EMERG("Enter\n");
msleep(10000); /*Let's wait for some time !*/
mhitest_pci_set_mhi_state(mplat, MHI_POWER_OFF);
mhitest_pci_set_mhi_state(mplat, MHI_DEINIT);
mhitest_pci_remove_all(mplat);
ret = mhitest_ss_powerup(mplat->subsys_handle);
if (ret) {
MHITEST_ERR("ERRORRRR..ret:%d\n", ret);
return;
}
MHITEST_EMERG("Exit\n");
}
int mhitest_recovery_event_handler(struct mhitest_platform *mplat, void *data)
{
struct mhitest_driver_event *event = data;
struct mhitest_recovery_data *rdata = event->data;
MHITEST_EMERG("Recovery triggred with reason:(%s)-(%d)\n",
mhitest_recov_reason_to_str(rdata->reason), rdata->reason);
switch (rdata->reason) {
case MHI_DEFAULT:
case MHI_LINK_DOWN:
case MHI_TIMEOUT:
break;
case MHI_RDDM:
mhitest_dump_info(mplat, false);
if (rddm_r) { /*using mod param for now*/
mhitest_recovery_post_rddm(mplat);
return 0; /*for now*/
} else
return 0;
break;
default:
MHITEST_ERR("Incorect reason\n");
break;
}
kfree(data);
return 0;
}
static void mhitest_event_work(struct work_struct *work)
{
struct mhitest_platform *mplat =
container_of(work, struct mhitest_platform, event_work);
struct mhitest_driver_event *event;
unsigned long flags;
int ret = 0;
if (!mplat) {
MHITEST_ERR("NULL mplat\n");
return;
}
spin_lock_irqsave(&mplat->event_lock, flags);
while (!list_empty(&mplat->event_list)) {
event = list_first_entry(&mplat->event_list,
struct mhitest_driver_event, list);
list_del(&event->list);
spin_unlock_irqrestore(&mplat->event_lock, flags);
switch (event->type) {
/*only support recovery event so far*/
case MHITEST_RECOVERY_EVENT:
MHITEST_LOG("MHITEST_RECOVERY_EVENT event\n");
ret = mhitest_recovery_event_handler(mplat, event);
break;
default:
MHITEST_ERR("Invalid event recived ..\n");
kfree(event);
continue;
}
spin_lock_irqsave(&mplat->event_lock, flags);
event->ret = ret;
if (event->sync) {
MHITEST_ERR("Sending event completion event\n");
complete(&event->complete);
continue;
}
spin_unlock_irqrestore(&mplat->event_lock, flags);
kfree(event);
spin_lock_irqsave(&mplat->event_lock, flags);
}
spin_unlock_irqrestore(&mplat->event_lock, flags);
}
int mhitest_register_driver(void)
{
int ret = 1;
MHITEST_LOG("Going for register pci and subsystem\n");
ret = mhitest_pci_register();
if (ret) {
MHITEST_ERR("Error pci register ret:%d\n", ret);
goto error_pci_reg;
}
return 0;
error_pci_reg:
mhitest_pci_unregister();
return ret;
}
void mhitest_unregister_driver(void)
{
MHITEST_LOG("Unregistering\n");
/* add driver related unregister stuffs here */
mhitest_pci_unregister();
}
int mhitest_event_work_init(struct mhitest_platform *mplat)
{
spin_lock_init(&mplat->event_lock);
mplat->event_wq = alloc_workqueue("mhitest_mod_event",
WQ_UNBOUND, 1);
if (!mplat->event_wq) {
MHITEST_ERR("Failed to create event workqueue!\n");
return -EFAULT;
}
INIT_WORK(&mplat->event_work, mhitest_event_work);
INIT_LIST_HEAD(&mplat->event_list);
return 0;
}
void mhitest_event_work_deinit(struct mhitest_platform *mplat)
{
if (mplat->event_wq)
destroy_workqueue(mplat->event_wq);
}
static int mhitest_probe(struct platform_device *plat_dev)
{
int ret;
MHITEST_VERB("Enter\n");
m_plat_dev = plat_dev;
ret = mhitest_register_driver();
if (ret) {
MHITEST_ERR("Error ret:%d\n", ret);
goto fail_probe;
}
MHITEST_VERB("Exit\n");
return 0;
fail_probe:
return ret;
}
static int mhitest_remove(struct platform_device *plat_dev)
{
MHITEST_VERB("Enter\n");
mhitest_unregister_driver();
m_plat_dev = NULL;
MHITEST_VERB("Exit\n");
return 0;
}
void mhitest_pci_disable_msi(struct mhitest_platform *mplat)
{
pci_free_irq_vectors(mplat->pci_dev);
}
void mhitest_pci_unregister_mhi(struct mhitest_platform *mplat)
{
struct mhi_controller *mhi_ctrl = mplat->mhi_ctrl;
mhi_unregister_controller(mhi_ctrl);
kfree(mhi_ctrl->irq);
mhi_free_controller(mhi_ctrl);
}
int mhitest_pci_remove_all(struct mhitest_platform *mplat)
{
MHITEST_VERB("Enter\n");
mhitest_pci_disable_msi(mplat);
mhitest_pci_disable_bus(mplat);
mhitest_pci_unregister_mhi(mplat);
mhitest_unregister_ramdump(mplat);
MHITEST_VERB("Exit\n");
return 0;
}
static const struct platform_device_id test_platform_id_table[] = {
{ .name = "qcn90xx", .driver_data = QCN90xx_DEVICE_ID, },
};
static const struct of_device_id test_of_match_table[] = {
{
.compatible = "qcom,testmhi",
.data = (void *)&test_platform_id_table[0]},
{ },
};
MODULE_DEVICE_TABLE(of, test_of_match_table);
struct platform_driver mhitest_platform_driver = {
.probe = mhitest_probe,
.remove = mhitest_remove,
.driver = {
.name = "mhitest",
.owner = THIS_MODULE,
.of_match_table = test_of_match_table,
},
};
int __init mhitest_init(void)
{
int ret;
MHITEST_EMERG("--->\n");
ret = platform_driver_register(&mhitest_platform_driver);
if (ret)
MHITEST_ERR("Error ret:%d\n", ret);
MHITEST_EMERG("<---done\n");
return ret;
}
void __exit mhitest_exit(void)
{
MHITEST_EMERG("Enter\n");
platform_driver_unregister(&mhitest_platform_driver);
MHITEST_EMERG("Exit\n");
}
module_init(mhitest_init);
module_exit(mhitest_exit);
MODULE_DESCRIPTION("MHITEST");
MODULE_LICENSE("GPL v2");