kernel_samsung_a53x/drivers/soc/samsung/cpif/cp_btl.c
2024-06-15 16:02:09 -03:00

420 lines
9.5 KiB
C
Executable file

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019, Samsung Electronics.
*
*/
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <linux/of_reserved_mem.h>
#include <linux/uaccess.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/memblock.h>
#include <soc/samsung/exynos-smc.h>
#include "modem_utils.h"
#include "modem_ctrl.h"
#include "cp_btl.h"
#if IS_ENABLED(CONFIG_LINK_DEVICE_PCIE)
#include "s51xx_pcie.h"
#endif
#define BTL_READ_SIZE_MAX SZ_1M
#if IS_ENABLED(CONFIG_LINK_DEVICE_PCIE)
#define BTL_MAP_SIZE SZ_1M /* per PCI BAR2 limit */
#endif
#define convert_to_kb(x) ((x) << (PAGE_SHIFT - 10))
/* fops */
static int btl_open(struct inode *inode, struct file *filep)
{
struct cp_btl *btl = container_of(filep->private_data, struct cp_btl, miscdev);
filep->private_data = (void *)btl;
if (!btl) {
mif_err("btl is null\n");
return -ENODEV;
}
if ((btl->link_type == LINKDEV_SHMEM) && !btl->mem.v_base) {
mif_err("%s: v_base is null\n", btl->name);
return -ENOMEM;
}
#if IS_ENABLED(CONFIG_LINK_DEVICE_PCIE)
btl->last_pcie_atu_grp = -1;
#endif
mif_info("%s opened by %s\n", btl->name, current->comm);
return 0;
}
static int btl_release(struct inode *inode, struct file *filep)
{
struct cp_btl *btl = NULL;
btl = (struct cp_btl *)filep->private_data;
if (!btl) {
mif_err("btl is null\n");
return -ENODEV;
}
mif_info("%s closed by %s\n", btl->name, current->comm);
return 0;
}
static ssize_t btl_read(struct file *filep, char __user *buf, size_t count, loff_t *pos)
{
struct cp_btl *btl = NULL;
unsigned long remainder = 0;
int len = 0;
int ret = 0;
#if IS_ENABLED(CONFIG_LINK_DEVICE_PCIE)
struct link_device *ld;
struct modem_ctl *mc;
void *btl_buf;
u32 atu_pos;
#endif
btl = (struct cp_btl *)filep->private_data;
if (!btl) {
mif_err("btl is null\n");
return -ENODEV;
}
if ((filep->f_flags & O_NONBLOCK) && !atomic_read(&btl->active))
return -EAGAIN;
if (*pos < 0 || *pos >= btl->mem.size) {
mif_err("Tried to read over %d:%lld\n", btl->mem.size, *pos);
return 0;
}
remainder = btl->mem.size - *pos;
if (remainder == 0) { /* EOF */
mif_info("%s: %lld bytes read\n", btl->name, *pos);
*pos = 0;
return 0;
}
len = min_t(size_t, count, BTL_READ_SIZE_MAX);
len = min_t(unsigned long, len, remainder);
switch (btl->link_type) {
case LINKDEV_SHMEM:
if (!btl->mem.v_base) {
mif_err("%s: v_base is null\n", btl->name);
ret = -ENOMEM;
break;
}
ret = copy_to_user(buf, btl->mem.v_base + *pos, len);
if (ret)
mif_err("%s: copy_to_user() error:%d", btl->name, ret);
break;
case LINKDEV_PCIE:
#if IS_ENABLED(CONFIG_LINK_DEVICE_PCIE)
ld = &btl->mld->link_dev;
mc = ld->mc;
btl_buf = NULL;
btl->mem.v_base = NULL;
atu_pos = (*pos) % BTL_MAP_SIZE;
len = min_t(unsigned long, len, BTL_MAP_SIZE - atu_pos);
mutex_lock(&mc->pcie_check_lock);
/* assume that pci link is up after CP BTL trigger */
if (!mc->pcie_powered_on ||
(s51xx_check_pcie_link_status(mc->pcie_ch_num) == 0)) {
mif_err("pci link is not ready\n");
ret = -EWOULDBLOCK;
goto link_exit;
}
ret = s5100_set_outbound_atu(ld->mc, btl, pos, BTL_MAP_SIZE);
if (ret != 0) {
mif_err("%s: failed to set ATU error:%d\n", btl->name, ret);
goto link_exit;
}
btl->mem.v_base = devm_ioremap(ld->dev, btl->mem.p_base, BTL_MAP_SIZE);
if (IS_ERR_OR_NULL(btl->mem.v_base)) {
mif_err("%s: failed to map\n", btl->name);
ret = -EFAULT;
goto link_exit;
}
btl_buf = vzalloc(len);
if (!btl_buf) {
mif_err("%s: failed to alloc\n", btl->name);
ret = -ENOMEM;
goto link_exit;
}
memcpy_fromio(btl_buf, btl->mem.v_base + atu_pos, len);
ret = copy_to_user(buf, btl_buf, len);
if (ret)
mif_err("%s: copy_to_user() error:%d", btl->name, ret);
link_exit:
if (btl_buf)
vfree(btl_buf);
if (!IS_ERR_OR_NULL(btl->mem.v_base)) {
devm_iounmap(ld->dev, btl->mem.v_base);
btl->mem.v_base = NULL;
}
mutex_unlock(&mc->pcie_check_lock);
#endif
break;
default:
break;
}
if (ret) {
*pos = 0;
return ret;
}
*pos += len;
return len;
}
#define IOCTL_GET_BTL_SIZE _IO('o', 0x59)
static long btl_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
{
struct cp_btl *btl = NULL;
size_t btl_size;
int ret = 0;
btl = (struct cp_btl *)filep->private_data;
if (!btl) {
mif_err("btl is null\n");
return -ENODEV;
}
btl_size = btl->mem.size;
switch (cmd) {
case IOCTL_GET_BTL_SIZE:
mif_info("IOCTL_BTL_FULL_DUMP:%d 0x%08lx\n", btl->id, btl_size);
ret = copy_to_user((void __user *)arg, &btl_size, sizeof(btl_size));
if (ret) {
mif_err("copy_to_user() error:%d\n", ret);
return ret;
}
break;
default:
mif_err("Invalid ioctl:0x%08x\n", cmd);
ret = -ENOIOCTLCMD;
break;
}
return ret;
}
/* Command line parameter */
static bool _is_enabled[MAX_BTL_ID] = {false, false};
#if defined(MODULE)
static int btl_set_enable(const char *str, const struct kernel_param *kp)
{
if (!strcmp(str, "ON") || !strcmp(str, "on"))
_is_enabled[BTL_ID_0] = true;
if (!strcmp(str, "DUAL_ON") || !strcmp(str, "dual_on")) {
_is_enabled[BTL_ID_0] = true;
_is_enabled[BTL_ID_1] = true;
}
mif_info("%s enable:%d/%d\n", str, _is_enabled[BTL_ID_0], _is_enabled[BTL_ID_1]);
return 0;
}
static const struct kernel_param_ops cp_btl_param_ops = {
.set = &btl_set_enable,
};
module_param_cb(cp_btl, &cp_btl_param_ops, NULL, 0644);
#else /* MODULE */
static int btl_set_enable(char *str)
{
if (!strcmp(str, "ON") || !strcmp(str, "on"))
_is_enabled[BTL_ID_0] = true;
if (!strcmp(str, "DUAL_ON") || !strcmp(str, "dual_on")) {
_is_enabled[BTL_ID_0] = true;
_is_enabled[BTL_ID_1] = true;
}
mif_info("%s enable:%d/%d\n", str, _is_enabled[BTL_ID_0], _is_enabled[BTL_ID_1]);
return 0;
}
static int __init btl_console_setup(char *str)
{
return btl_set_enable(str);
}
__setup("androidboot.cp_reserved_mem=", btl_console_setup);
static int __init btl_console_setup_alt(char *str)
{
return btl_set_enable(str);
}
__setup("androidboot.cp_btl=", btl_console_setup_alt);
#endif /* MODULE */
/* Create */
static const struct file_operations btl_file_ops = {
.open = btl_open,
.release = btl_release,
.read = btl_read,
.unlocked_ioctl = btl_ioctl,
.compat_ioctl = btl_ioctl,
};
int cp_btl_create(struct cp_btl *btl, struct device *dev)
{
struct modem_data *pdata = NULL;
int ret = 0;
struct sysinfo s;
if (!dev) {
mif_err("dev is null\n");
return -ENODEV;
}
pdata = dev->platform_data;
if (!pdata) {
mif_err("pdata is null\n");
return -ENODEV;
}
if (!btl) {
mif_err("btl is null\n");
return -ENOMEM;
}
atomic_set(&btl->active, 0);
mif_dt_read_string(dev->of_node, "cp_btl_node_name", btl->name);
mif_dt_read_u32_noerr(dev->of_node, "cp_btl_support_extension", btl->support_extension);
mif_dt_read_u32_noerr(dev->of_node, "cp_btl_extension_dram_size", btl->extension_dram_size);
if (btl->support_extension)
btl->extension_enabled = true;
btl->id = pdata->cp_num;
if (btl->id >= MAX_BTL_ID) {
mif_err("id is over max:%d\n", btl->id);
ret = -EINVAL;
goto create_exit;
}
if (!_is_enabled[btl->id]) {
mif_err("CP BTL is disabled for %d\n", btl->id);
ret = 0;
goto create_exit;
}
btl->enabled = true;
btl->link_type = pdata->link_type;
mif_info("name:%s id:%d link:%d\n", btl->name, btl->id, btl->link_type);
switch (btl->link_type) {
case LINKDEV_SHMEM:
btl->mem.size = cp_shmem_get_size(btl->id, SHMEM_BTL);
if (btl->support_extension) {
si_meminfo(&s);
mif_info("total mem (%ld kb)\n", convert_to_kb(s.totalram));
/* DRAM size: over 8GB -> BTL size: 64MB */
/* DRAM size: under 8GB -> BTL size: 32MB */
if (convert_to_kb(s.totalram) > btl->extension_dram_size) {
btl->mem.size += cp_shmem_get_size(btl->id, SHMEM_BTL_EXT);
} else {
cp_shmem_release_rmem(btl->id, SHMEM_BTL_EXT, 0);
btl->extension_enabled = false;
}
}
/* TODO: cached */
btl->mem.v_base = cp_shmem_get_nc_region(cp_shmem_get_base(btl->id, SHMEM_BTL),
btl->mem.size);
if (!btl->mem.v_base) {
mif_err("cp_shmem_get_region() error:v_base\n");
ret = -ENOMEM;
goto create_exit;
}
/* BAAW */
exynos_smc(SMC_ID_CLK, SSS_CLK_ENABLE, 0, 0);
ret = (int)exynos_smc(SMC_ID, CP_BOOT_REQ_CP_RAM_LOGGING, 0, 0);
if (ret) {
mif_err("exynos_smc() error:%d\n", ret);
goto create_exit;
}
exynos_smc(SMC_ID_CLK, SSS_CLK_DISABLE, 0, 0);
break;
case LINKDEV_PCIE:
btl->mem.v_base = NULL;
btl->mem.p_base = 0x14200000;
/* actual cp address is 0x47200000, but needs additional 0x40000000 */
btl->mem.cp_p_base = 0x87200000;
btl->mem.size = (SZ_32M - SZ_2M);
break;
default:
mif_err("link_type error:%d\n", btl->link_type);
ret = -EINVAL;
goto create_exit;
}
btl->mld = pdata->mld;
btl->miscdev.minor = MISC_DYNAMIC_MINOR;
btl->miscdev.name = btl->name;
btl->miscdev.fops = &btl_file_ops;
ret = misc_register(&btl->miscdev);
if (ret) {
mif_err("misc_register() error for %s:%d", btl->name, ret);
goto create_exit;
}
if (btl->mem.v_base)
memset(btl->mem.v_base, 0, btl->mem.size);
atomic_set(&btl->active, 1);
return 0;
create_exit:
if (btl->mem.v_base)
vunmap(btl->mem.v_base);
cp_shmem_release_rmem(btl->id, SHMEM_BTL, 0);
if (btl->extension_enabled)
cp_shmem_release_rmem(btl->id, SHMEM_BTL_EXT, 0);
return ret;
}
int cp_btl_destroy(struct cp_btl *btl)
{
if (!btl) {
mif_err("btl is null\n");
return -ENODEV;
}
if (btl->mem.v_base)
vunmap(btl->mem.v_base);
misc_deregister(&btl->miscdev);
return 0;
}