560 lines
15 KiB
C
Executable file
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_on(2, 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;
|
|
}
|