android13/kernel-5.10/drivers/sprd_pcie/pcie/pcie_sipa_res.c

196 lines
5.1 KiB
C
Raw Normal View History

2024-06-22 08:45:49 -04:00
/*
* Copyright (C) 2018-2019 Unisoc Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* 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/errno.h>
#include <linux/slab.h>
#include <linux/soc/sprd/sprd_mpm.h>
#include <linux/workqueue.h>
#include "pcie_sipa_res.h"
#include "../include/sprd_pcie_resource.h"
struct pcie_sipa_res_prod {
u8 dst;
enum sipa_rm_res_id prod_id; /* producer res id */
enum sipa_rm_res_id cons_id; /* consumer res id */
struct sprd_pms *pms;
char pms_name[20];
struct work_struct wait_work;
struct delayed_work rm_work;
};
static void pcie_sipa_res_wait_res_work_fn(struct work_struct *work)
{
int ret;
struct pcie_sipa_res_prod *res = container_of(work,
struct pcie_sipa_res_prod,
wait_work);
ret = sprd_pcie_wait_resource(res->dst, -1);
/* pcie not ready, just return. */
if (ret) {
pr_err("pcie_sipa_res: wait res error = %d!\n", ret);
return;
}
/* notify ipa module that pcie is ready. */
sipa_rm_notify_completion(SIPA_RM_EVT_GRANTED,
res->prod_id);
}
static int pcie_sipa_res_request_resource(void *data)
{
int ret;
struct pcie_sipa_res_prod *res = data;
pr_info("pcie_sipa_res: request resource.\n");
sprd_pms_power_up(res->pms);
/*
* when the resource is not ready, the IPA module doesn't want be
* blocked in here until the pcie ready, the IPA owner designed
* a notification api sipa_rm_notify_completion to notify the
* IPA module that the resource requested by IPA is ready.
* The designated error value is -EINPROGRESS, so we must override the
* return value -ETIME to -EINPROGRESS.
*/
ret = sprd_pcie_wait_resource(res->dst, 0);
if (ret == -ETIME) {
/* add a work to wait pcie ready */
schedule_work(&res->wait_work);
ret = -EINPROGRESS;
}
return ret;
}
static int pcie_sipa_res_release_resource(void *data)
{
struct pcie_sipa_res_prod *res = data;
pr_info("pcie_sipa_res: release resource.\n");
sprd_pms_release_resource(res->pms);
return 0;
}
static void pcie_sipa_res_create_rm_work_fn(struct work_struct *work)
{
int ret;
struct sipa_rm_create_params rm_params;
struct pcie_sipa_res_prod *res = container_of(to_delayed_work(work),
struct pcie_sipa_res_prod,
rm_work);
rm_params.name = res->prod_id;
rm_params.floor_voltage = 0;
rm_params.reg_params.notify_cb = NULL;
rm_params.reg_params.user_data = res;
rm_params.request_resource = pcie_sipa_res_request_resource;
rm_params.release_resource = pcie_sipa_res_release_resource;
ret = sipa_rm_create_resource(&rm_params);
/* defer to create rm */
if (ret == -EPROBE_DEFER) {
schedule_delayed_work(&res->rm_work, msecs_to_jiffies(1000));
return;
}
/* add dependencys */
ret = sipa_rm_add_dependency(res->cons_id, res->prod_id);
if (ret < 0 && ret != -EINPROGRESS) {
pr_err("pcie_sipa_res: add_dependency error = %d!\n", ret);
sipa_rm_delete_resource(res->prod_id);
sprd_pms_destroy(res->pms);
kfree(res);
}
}
void *pcie_sipa_res_create(u8 dst, enum sipa_rm_res_id prod_id,
enum sipa_rm_res_id cons_id)
{
int ret;
struct sipa_rm_create_params rm_params;
struct pcie_sipa_res_prod *res;
res = kzalloc(sizeof(*res), GFP_KERNEL);
if (!res)
return NULL;
/* init wait pcie res work */
INIT_WORK(&res->wait_work, pcie_sipa_res_wait_res_work_fn);
INIT_DELAYED_WORK(&res->rm_work, pcie_sipa_res_create_rm_work_fn);
/* create pms */
strncpy(res->pms_name, "sipa", sizeof(res->pms_name));
res->pms = sprd_pms_create(dst, res->pms_name, false);
if (!res->pms) {
pr_err("pcie_sipa_res: create pms failed!\n");
kfree(res);
return NULL;
}
res->dst = dst;
res->prod_id = prod_id;
res->cons_id = cons_id;
/* create prod */
rm_params.name = prod_id;
rm_params.floor_voltage = 0;
rm_params.reg_params.notify_cb = NULL;
rm_params.reg_params.user_data = res;
rm_params.request_resource = pcie_sipa_res_request_resource;
rm_params.release_resource = pcie_sipa_res_release_resource;
ret = sipa_rm_create_resource(&rm_params);
/* defer to create rm */
if (ret == -EPROBE_DEFER) {
schedule_delayed_work(&res->rm_work, msecs_to_jiffies(1000));
return res;
} else if (ret) {
pr_err("pcie_sipa_res: create rm error = %d!\n", ret);
sprd_pms_destroy(res->pms);
kfree(res);
return NULL;
}
/* add dependencys */
ret = sipa_rm_add_dependency(cons_id, prod_id);
if (ret < 0 && ret != -EINPROGRESS) {
pr_err("pcie_sipa_res: add_dependency error = %d!\n", ret);
sipa_rm_delete_resource(prod_id);
sprd_pms_destroy(res->pms);
kfree(res);
return NULL;
}
return res;
}
void pcie_sipa_res_destroy(void *data)
{
struct pcie_sipa_res_prod *res = data;
cancel_work_sync(&res->wait_work);
cancel_delayed_work_sync(&res->rm_work);
sprd_pms_destroy(res->pms);
sipa_rm_delete_dependency(res->cons_id, res->prod_id);
sipa_rm_delete_resource(res->prod_id);
kfree(res);
}