kernel_samsung_a53x/drivers/soc/samsung/cpif/s51xx_pcie.c
Sultan Alsawaf 7a02051d8c soc/google/cpif: Don't pin workers onto CPU2
There's no reason to pin workers onto CPU2. Instead, allow the workers to
run where the workqueue sees fit, most likely on the CPU which enqueues the
worker, which is better than always dumping the workers onto CPU2.

Signed-off-by: Sultan Alsawaf <sultan@kerneltoast.com>
2025-01-23 19:49:06 +01:00

560 lines
15 KiB
C
Executable file

// SPDX-License-Identifier: GPL-2.0
/*
* PCIe modem control driver for S51xx series
*
* Copyright (C) 2019 Samsung Electronics.
*
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/if_arp.h>
#include <linux/version.h>
#include <linux/uaccess.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/irq.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/pci.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/pm_runtime.h>
#include <sound/samsung/abox.h>
#include <linux/exynos-pci-ctrl.h>
#include <linux/exynos-pci-noti.h>
#include "modem_prj.h"
#include "modem_utils.h"
#include "modem_ctrl.h"
#include "s51xx_pcie.h"
extern int exynos_pcie_rc_chk_link_status(int ch_num);
extern int exynos_pcie_l1ss_ctrl(int enable, int id, int ch_num);
static int s51xx_pcie_read_procmem(struct seq_file *m, void *v)
{
pr_err("Procmem READ!\n");
return 0;
}
static int s51xx_pcie_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, s51xx_pcie_read_procmem, NULL);
}
static const struct proc_ops s51xx_pcie_proc_fops = {
.proc_open = s51xx_pcie_proc_open,
.proc_read = seq_read,
.proc_lseek = seq_lseek,
.proc_release = single_release,
};
void s51xx_pcie_chk_ep_conf(struct pci_dev *pdev)
{
int i;
u32 val1, val2, val3, val4;
/* full log
for (i = 0x0; i < 0x50; i += 0x10) {
pci_read_config_dword(pdev, i, &val1);
pci_read_config_dword(pdev, i + 0x4, &val2);
pci_read_config_dword(pdev, i + 0x8, &val3);
pci_read_config_dword(pdev, i + 0xC, &val4);
dev_info(&pdev->dev, "0x%02x: %08x %08x %08x %08x\n",
i, val1, val2, val3, val4);
}
*/
i = 0x0;
pci_read_config_dword(pdev, i, &val1);
pci_read_config_dword(pdev, i + 0x4, &val2);
pci_read_config_dword(pdev, i + 0x8, &val3);
pci_read_config_dword(pdev, i + 0xC, &val4);
dev_info(&pdev->dev, "0x%02x: %08x %08x %08x %08x\n",
i, val1, val2, val3, val4);
i = 0x10;
pci_read_config_dword(pdev, i, &val1);
pci_read_config_dword(pdev, i + 0x4, &val2);
pci_read_config_dword(pdev, i + 0x8, &val3);
pci_read_config_dword(pdev, i + 0xC, &val4);
dev_info(&pdev->dev, "0x%02x: %08x %08x %08x %08x\n",
i, val1, val2, val3, val4);
/*
i = 0x40;
pci_read_config_dword(pdev, i, &val1);
dev_info(&pdev->dev, "0x%02x: %08x\n",
i, val1);
*/
}
inline int s51xx_pcie_send_doorbell_int(struct pci_dev *pdev, int int_num)
{
struct s51xx_pcie *s51xx_pcie = pci_get_drvdata(pdev);
struct pci_driver *driver = pdev->driver;
struct modem_ctl *mc = container_of(driver, struct modem_ctl, pci_driver);
u32 reg, count = 0;
int cnt = 0;
u16 cmd;
if (s51xx_pcie->link_status == 0) {
mif_err_limited("Can't send Interrupt(not enabled)!!!\n");
return -EAGAIN;
}
if (exynos_pcie_rc_get_cpl_timeout_state(s51xx_pcie->pcie_channel_num)) {
mif_err_limited("Can't send Interrupt(cto_retry_cnt: %d)!!!\n",
mc->pcie_cto_retry_cnt);
return 0;
}
if (s51xx_check_pcie_link_status(s51xx_pcie->pcie_channel_num) == 0) {
mif_err_limited("Can't send Interrupt(not linked)!!!\n");
goto check_cpl_timeout;
}
pci_read_config_word(pdev, PCI_COMMAND, &cmd);
if ((((cmd & PCI_COMMAND_MEMORY) == 0) ||
(cmd & PCI_COMMAND_MASTER) == 0) || (cmd == 0xffff)) {
mif_err_limited("Can't send Interrupt(not setted bme_en, 0x%04x)!!!\n", cmd);
do {
cnt++;
/* set bme bit */
pci_set_master(pdev);
pci_read_config_word(pdev, PCI_COMMAND, &cmd);
mif_info("cmd reg = 0x%04x\n", cmd);
/* set mse bit */
cmd |= PCI_COMMAND_MEMORY;
pci_write_config_word(pdev, PCI_COMMAND, cmd);
pci_read_config_word(pdev, PCI_COMMAND, &cmd);
mif_info("cmd reg = 0x%04x\n", cmd);
if ((cmd & PCI_COMMAND_MEMORY) &&
(cmd & PCI_COMMAND_MASTER) && (cmd != 0xffff))
break;
} while (cnt < 10);
if (cnt >= 10) {
mif_err_limited("BME is not set(cnt=%d)\n", cnt);
goto check_cpl_timeout;
}
}
send_doorbell_again:
iowrite32(int_num, s51xx_pcie->doorbell_addr);
reg = ioread32(s51xx_pcie->doorbell_addr);
/* debugging:
* mif_info("s51xx_pcie.doorbell_addr = 0x%p - written(int_num=0x%x) read(reg=0x%x)\n", \
* s51xx_pcie->doorbell_addr, int_num, reg);
*/
if (reg == 0xffffffff) {
count++;
if (count < 10) {
if (!in_interrupt())
udelay(1000); /* 1ms */
else {
mif_err_limited("Can't send doorbell in interrupt mode (0x%08X)\n",
reg);
return 0;
}
goto send_doorbell_again;
}
mif_err("[Need to CHECK] Can't send doorbell int (0x%x)\n", reg);
goto check_cpl_timeout;
}
return 0;
check_cpl_timeout:
if (exynos_pcie_rc_get_cpl_timeout_state(s51xx_pcie->pcie_channel_num)) {
mif_err_limited("Can't send Interrupt(cto_retry_cnt: %d)!!!\n",
mc->pcie_cto_retry_cnt);
return 0;
}
exynos_pcie_rc_register_dump(s51xx_pcie->pcie_channel_num);
return -EAGAIN;
}
void first_save_s51xx_status(struct pci_dev *pdev)
{
struct s51xx_pcie *s51xx_pcie = pci_get_drvdata(pdev);
if (s51xx_check_pcie_link_status(s51xx_pcie->pcie_channel_num) == 0) {
mif_err("It's not Linked - Ignore saving the s5100\n");
return;
}
pci_save_state(pdev);
s51xx_pcie->pci_saved_configs = pci_store_saved_state(pdev);
if (s51xx_pcie->pci_saved_configs == NULL)
mif_err("MSI-DBG: s51xx pcie.pci_saved_configs is NULL(s51xx config NOT saved)\n");
else
mif_info("first s51xx config status save: done\n");
}
void s51xx_pcie_save_state(struct pci_dev *pdev)
{
struct s51xx_pcie *s51xx_pcie = pci_get_drvdata(pdev);
dev_info(&pdev->dev, "[%s]\n", __func__);
if (s51xx_check_pcie_link_status(s51xx_pcie->pcie_channel_num) == 0) {
mif_err("It's not Linked - Ignore restore state!!!\n");
return;
}
/* pci_pme_active(s51xx_pcie.s51xx_pdev, 0); */
/* Disable L1.2 before PCIe power off */
s51xx_pcie_l1ss_ctrl(0);
pci_clear_master(pdev);
pci_save_state(pdev);
s51xx_pcie->pci_saved_configs = pci_store_saved_state(pdev);
s51xx_pcie_chk_ep_conf(pdev);
disable_msi_int(pdev);
/* pci_enable_wake(s51xx_pcie.s51xx_pdev, PCI_D0, 0); */
pci_disable_device(pdev);
pci_wake_from_d3(pdev, false);
if (pci_set_power_state(pdev, PCI_D3hot))
mif_err("Can't set D3 state!!!!\n");
}
void s51xx_pcie_restore_state(struct pci_dev *pdev)
{
struct s51xx_pcie *s51xx_pcie = pci_get_drvdata(pdev);
int ret;
dev_info(&pdev->dev, "[%s]\n", __func__);
if (s51xx_check_pcie_link_status(s51xx_pcie->pcie_channel_num) == 0) {
mif_err("It's not Linked - Ignore restore state!!!\n");
return;
}
if (pci_set_power_state(pdev, PCI_D0))
mif_err("Can't set D0 state!!!!\n");
if (!s51xx_pcie->pci_saved_configs)
dev_err(&pdev->dev, "[%s] s51xx pcie saved configs is NULL\n", __func__);
pci_load_saved_state(pdev, s51xx_pcie->pci_saved_configs);
pci_restore_state(pdev);
/* move chk_ep_conf function after setting BME(Bus Master Enable)
* s51xx_pcie_chk_ep_conf(pdev);
*/
pci_enable_wake(pdev, PCI_D0, 0);
/* pci_enable_wake(s51xx_pcie.s51xx_pdev, PCI_D3hot, 0); */
ret = pci_enable_device(pdev);
if (ret)
pr_err("Can't enable PCIe Device after linkup!\n");
pci_set_master(pdev);
/* DBG: print out EP config values after restore_state */
s51xx_pcie_chk_ep_conf(pdev);
#if IS_ENABLED(CONFIG_DISABLE_PCIE_CP_L1_2)
/* Disable L1.2 after PCIe power on */
s51xx_pcie_l1ss_ctrl(0);
#else
/* Enable L1.2 after PCIe power on */
s51xx_pcie_l1ss_ctrl(1);
#endif
s51xx_pcie->link_status = 1;
/* pci_pme_active(s51xx_pcie.s51xx_pdev, 1); */
}
int s51xx_check_pcie_link_status(int ch_num)
{
return exynos_pcie_rc_chk_link_status(ch_num);
}
void s51xx_pcie_l1ss_ctrl(int enable)
{
exynos_pcie_l1ss_ctrl(enable, PCIE_L1SS_CTRL_MODEM_IF, 1);
}
void disable_msi_int(struct pci_dev *pdev)
{
struct s51xx_pcie *s51xx_pcie = pci_get_drvdata(pdev);
dev_info(&pdev->dev, "[%s]\n", __func__);
s51xx_pcie->link_status = 0;
/* It's not needed now...
* pci_disable_msi(s51xx_pcie.s51xx_pdev);
* pci_config_pm_runtime_put(&s51xx_pcie.s51xx_pdev->dev);
*/
}
int s51xx_pcie_request_msi_int(struct pci_dev *pdev, int int_num)
{
int err = -EFAULT;
pr_err("Enable MSI interrupts : %d\n", int_num);
if (int_num > MAX_MSI_NUM) {
pr_err("Too many MSI interrupts are requested(<=16)!!!\n");
return -EFAULT;
}
err = pci_alloc_irq_vectors_affinity(pdev, int_num, int_num, PCI_IRQ_MSI, NULL);
if (err <= 0) {
pr_err("Can't get msi IRQ!!!!!\n");
return -EFAULT;
}
return pdev->irq;
}
static void s51xx_pcie_linkdown_cb(struct exynos_pcie_notify *noti)
{
struct pci_dev *pdev = (struct pci_dev *)noti->user;
struct pci_driver *driver = pdev->driver;
struct modem_ctl *mc = container_of(driver, struct modem_ctl, pci_driver);
pr_err("s51xx Link-Down notification callback function!!!\n");
if (mc->pcie_powered_on == false) {
pr_info("%s: skip cp crash during dislink sequence\n", __func__);
exynos_pcie_set_perst_gpio(mc->pcie_ch_num, 0);
} else {
s5100_force_crash_exit_ext();
}
}
static void s51xx_pcie_cpl_timeout_cb(struct exynos_pcie_notify *noti)
{
struct pci_dev *pdev = (struct pci_dev *)noti->user;
struct pci_driver *driver = pdev->driver;
struct modem_ctl *mc = container_of(driver, struct modem_ctl, pci_driver);
struct s51xx_pcie *s51xx_pcie = pci_get_drvdata(pdev);
pr_err("s51xx CPL_TIMEOUT notification callback function!!!\n");
pr_err("CPL: a=%d c=%d\n", mc->pcie_cto_retry_cnt_all++, mc->pcie_cto_retry_cnt);
pr_err("s51xx CPLTO RC regdump \n");
exynos_pcie_rc_register_dump(s51xx_pcie->pcie_channel_num);
if (mc->pcie_cto_retry_cnt++ < 10) {
pr_err("[%s][%d] retry pcie poweron !!!\n", __func__,
mc->pcie_cto_retry_cnt);
queue_work(mc->wakeup_wq, &mc->wakeup_work);
} else {
pr_err("[%s][%d] force crash !!!\n", __func__,
mc->pcie_cto_retry_cnt);
s5100_force_crash_exit_ext();
}
}
static int s51xx_pcie_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
int ret;
int __maybe_unused i;
struct s51xx_pcie *s51xx_pcie;
struct device *dev = &pdev->dev;
struct pci_driver *driver = pdev->driver;
struct modem_ctl *mc = container_of(driver, struct modem_ctl, pci_driver);
struct device *mc_dev = mc->dev;
struct pci_bus *bus = pdev->bus;
struct pci_dev *bus_self = bus->self;
struct resource *tmp_rsc;
int resno = PCI_BRIDGE_MEM_WINDOW;
u32 val, db_addr;
dev_info(dev, "%s EP driver Probe(%s), chNum: %d\n",
driver->name, __func__, mc->pcie_ch_num);
s51xx_pcie = devm_kzalloc(dev, sizeof(*s51xx_pcie), GFP_KERNEL);
s51xx_pcie->s51xx_pdev = pdev;
s51xx_pcie->irq_num_base = pdev->irq;
s51xx_pcie->link_status = 1;
s51xx_pcie->pcie_channel_num = mc->pcie_ch_num;
mc->s51xx_pdev = pdev;
if (of_property_read_u32(mc_dev->of_node, "pci_db_addr", &db_addr)) {
dev_err(dev, "Failed to parse the EP DB base address\n");
return -EINVAL;
}
pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0, db_addr);
pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0, &val);
val &= PCI_BASE_ADDRESS_MEM_MASK;
s51xx_pcie->dbaddr_offset = db_addr - val;
dev_info(dev, "db_addr : 0x%x , val : 0x%x, offset : 0x%x\n",
db_addr, val, (unsigned int)s51xx_pcie->dbaddr_offset);
#if 0
pr_err("Disable BAR resources.\n");
for (i = 0; i < 6; i++) {
pdev->resource[i].start = 0x0;
pdev->resource[i].end = 0x0;
pdev->resource[i].flags = 0x82000000;
pci_assign_resource(pdev, i);
}
#endif
/* EP BAR setup: BAR0 (4kB) */
pdev->resource[0].start = val;
pdev->resource[0].end = val + SZ_4K;
//pdev->resource[0].flags = 0x82000000;
pci_assign_resource(pdev, 0);
/* get Doorbell base address from root bus range */
tmp_rsc = bus_self->resource + resno;
dev_info(&bus_self->dev, "[%s] BAR %d: tmp rsc : %pR\n", __func__, resno, tmp_rsc);
s51xx_pcie->dbaddr_base = tmp_rsc->start;
pr_err("Set Doorbell register address.\n");
s51xx_pcie->doorbell_addr = devm_ioremap(&pdev->dev,
s51xx_pcie->dbaddr_base + s51xx_pcie->dbaddr_offset, SZ_4);
ret = abox_pci_doorbell_paddr_set(s51xx_pcie->dbaddr_base + s51xx_pcie->dbaddr_offset);
if (!ret)
dev_err(dev, "PCIe doorbell setting for ABOX is failed \n");
pr_info("s51xx_pcie.doorbell_addr = %p (start 0x%lx offset : %lx)\n",
s51xx_pcie->doorbell_addr, (unsigned long)s51xx_pcie->dbaddr_base,
(unsigned long)s51xx_pcie->dbaddr_offset);
if (s51xx_pcie->doorbell_addr == NULL)
pr_err("Can't ioremap doorbell address!!!\n");
pr_info("Register PCIE notification LINKDOWN event...\n");
s51xx_pcie->pcie_event.events = EXYNOS_PCIE_EVENT_LINKDOWN;
s51xx_pcie->pcie_event.user = pdev;
s51xx_pcie->pcie_event.mode = EXYNOS_PCIE_TRIGGER_CALLBACK;
s51xx_pcie->pcie_event.callback = s51xx_pcie_linkdown_cb;
exynos_pcie_host_v1_register_event(&s51xx_pcie->pcie_event);
pr_info("Register PCIE notification CPL_TIMEOUT event...\n");
s51xx_pcie->pcie_cpl_timeout_event.events = EXYNOS_PCIE_EVENT_CPL_TIMEOUT;
s51xx_pcie->pcie_cpl_timeout_event.user = pdev;
s51xx_pcie->pcie_cpl_timeout_event.mode = EXYNOS_PCIE_TRIGGER_CALLBACK;
s51xx_pcie->pcie_cpl_timeout_event.callback = s51xx_pcie_cpl_timeout_cb;
exynos_pcie_host_v1_register_event(&s51xx_pcie->pcie_cpl_timeout_event);
pr_err("Enable PCI device...\n");
ret = pci_enable_device(pdev);
pci_set_master(pdev);
pci_set_drvdata(pdev, s51xx_pcie);
pr_err("Check EP Conf...\n");
s51xx_pcie_chk_ep_conf(pdev);
return 0;
}
void print_msi_register(struct pci_dev *pdev)
{
struct s51xx_pcie *s51xx_pcie = pci_get_drvdata(pdev);
u32 msi_val;
pci_read_config_dword(pdev, 0x50, &msi_val);
mif_info("MSI Control Reg(0x50) : 0x%x\n", msi_val);
pci_read_config_dword(pdev, 0x54, &msi_val);
mif_info("MSI Message Reg(0x54) : 0x%x\n", msi_val);
pci_read_config_dword(pdev, 0x58, &msi_val);
mif_info("MSI MsgData Reg(0x58) : 0x%x\n", msi_val);
if (msi_val == 0x0) {
mif_info("MSI Message Reg == 0x0 - set MSI again!!!\n");
if (s51xx_pcie->pci_saved_configs != NULL) {
mif_info("msi restore\n");
pci_restore_msi_state(pdev);
} else {
mif_info("[skip] msi restore: saved configs is NULL\n");
}
mif_info("exynos_pcie_msi_init_ext is not implemented\n");
/* exynos_pcie_msi_init_ext(s51xx_pcie.pcie_channel_num); */
pci_read_config_dword(pdev, 0x50, &msi_val);
mif_info("Recheck - MSI Control Reg : 0x%x (0x50)\n", msi_val);
pci_read_config_dword(pdev, 0x54, &msi_val);
mif_info("Recheck - MSI Message Reg : 0x%x (0x54)\n", msi_val);
pci_read_config_dword(pdev, 0x58, &msi_val);
mif_info("Recheck - MSI MsgData Reg : 0x%x (0x58)\n", msi_val);
}
}
static void s51xx_pcie_remove(struct pci_dev *pdev)
{
pr_err("s51xx PCIe Remove!!!\n");
pci_release_regions(pdev);
}
/* For Test */
static struct pci_device_id s51xx_pci_id_tbl[] = {
{ PCI_VENDOR_ID_SAMSUNG, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, }, // SC Basic
{ }
};
MODULE_DEVICE_TABLE(pci, s51xx_pci_id_tbl);
static struct pci_driver s51xx_driver = {
.name = "s51xx",
.id_table = s51xx_pci_id_tbl,
.probe = s51xx_pcie_probe,
.remove = s51xx_pcie_remove,
};
/*
* Initialize PCIe s51xx EP driver.
*/
int s51xx_pcie_init(struct modem_ctl *mc)
{
int ch_num = mc->pcie_ch_num;
int ret;
pr_err("Register PCIE drvier for s51xx.(chNum: %d, mc: 0x%p)\n", ch_num, mc);
mc->pci_driver = s51xx_driver;
ret = pci_register_driver(&mc->pci_driver);
/* Create PROC fs */
proc_create("driver/s51xx_pcie_proc", 0, NULL, &s51xx_pcie_proc_fops);
return 0;
}