366 lines
10 KiB
C
Executable File

/*++
Copyright (c) 2021 Motor-comm Corporation.
Confidential and Proprietary. All rights reserved.
This is Motor-comm Corporation NIC driver relevant files. Please don't copy, modify,
distribute without commercial permission.
--*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
/* for file operation */
#include <linux/fs.h>
#include "fuxi-gmac.h"
#include "fuxi-gmac-reg.h"
#define FXGMAC_DBG 0
/* for power state debug using, 20210629. */
#if FXGMAC_DBG
static struct file *dbg_file = NULL;
#define FXGMAC_DBG_BUF_SIZE 128
static char log_buf[FXGMAC_DBG_BUF_SIZE + 2];
#endif
#if FXGMAC_DBG
static char * file_name = "/home/fxgmac/fxgmac_dbg.log";
#endif
#if (LINUX_VERSION_CODE < KERNEL_VERSION(5,13,0))
#if FXGMAC_DBG
#define fxgmac_dbg_log(logstr, arg...) \
do { \
static struct kstat dbg_stat; \
static loff_t pos = 0; \
if (!(IS_ERR(dbg_file))) { \
mm_segment_t previous_fs; /*for address-space switch */ \
sprintf (&log_buf[0], (logstr), ##arg); \
previous_fs = get_fs(); \
set_fs(KERNEL_DS); \
vfs_stat(file_name, &dbg_stat); \
pos = (loff_t)dbg_stat.size; \
kernel_write(dbg_file, (const char*)log_buf, strlen(log_buf) > FXGMAC_DBG_BUF_SIZE ? FXGMAC_DBG_BUF_SIZE : strlen(log_buf), /*&dbg_file->f_pos*/&pos)/**/; \
/*DPRINTK("kernel write done, file size=%d, s=%s", (int)dbg_stat.size, log_buf)*/; \
set_fs(previous_fs); \
/*dbg_file->f_op->flush(dbg_file)*/; \
} \
}while(0)
#else
#define fxgmac_dbg_log(logstr, arg...) \
do { \
/*nothing*/; \
}while(0)
#endif
#else
#if FXGMAC_DBG
/* in kernel 5.14, get_fs/set_fs are removed. */
#define fxgmac_dbg_log(logstr, arg...) \
do { \
static struct kstat dbg_stat; \
static loff_t pos = 0; \
fxgmac_dbg_log_init(); \
if (!(IS_ERR(dbg_file))) { \
sprintf (&log_buf[0], (logstr), ##arg); \
vfs_getattr(&(dbg_file->f_path), &dbg_stat, 0, 0); \
pos = (loff_t)dbg_stat.size; \
kernel_write(dbg_file, (const char*)log_buf, strlen(log_buf) > FXGMAC_DBG_BUF_SIZE ? FXGMAC_DBG_BUF_SIZE : strlen(log_buf), /*&dbg_file->f_pos*/&pos)/**/; \
/*DPRINTK("kernel write done, file size=%d, s=%s", (int)dbg_stat.size, log_buf)*/; \
/*dbg_file->f_op->flush(dbg_file)*/; \
} \
fxgmac_dbg_log_uninit();\
}while(0)
#else
#define fxgmac_dbg_log(logstr, arg...) \
do { \
/*nothing*/; \
}while(0)
#endif
#endif
/* declarations */
static void fxgmac_shutdown(struct pci_dev *pdev);
/*
* functions definitions
*/
int fxgmac_dbg_log_init( void )
{
#if FXGMAC_DBG
dbg_file = filp_open(file_name, O_RDWR | O_CREAT, 0644);
if (IS_ERR(dbg_file))
{
DPRINTK("File fxgmac_dbg.log open or created failed.\n");
return PTR_ERR(dbg_file);
}
#endif
return 0;
}
int fxgmac_dbg_log_uninit( void )
{
#if FXGMAC_DBG
if(IS_ERR(dbg_file))
{
DPRINTK("Invalid fd for file fxgmac_dbg.log, %p.\n", dbg_file);
dbg_file= NULL;
return PTR_ERR(dbg_file);
}
filp_close(dbg_file, NULL);
dbg_file= NULL;
#endif
return 0;
}
static int fxgmac_probe(struct pci_dev *pcidev, const struct pci_device_id *id)
{
struct device *dev = &pcidev->dev;
struct fxgmac_resources res;
int i, ret;
ret = pcim_enable_device(pcidev);
if (ret) {
dev_err(dev, "ERROR: fxgmac_probe failed to enable device\n");
return ret;
}
for (i = 0; i <= PCI_STD_RESOURCE_END; i++) {
if (pci_resource_len(pcidev, i) == 0)
continue;
ret = pcim_iomap_regions(pcidev, BIT(i), FXGMAC_DRV_NAME);
if (ret)
{
DPRINTK(KERN_INFO "fxgmac_probe pcim_iomap_regions failed\n");
return ret;
}
DPRINTK(KERN_INFO "fxgmac_probe iomap_region i=%#x,ret=%#x\n",(int)i,(int)ret);
break;
}
pci_set_master(pcidev);
memset(&res, 0, sizeof(res));
res.irq = pcidev->irq;
res.addr = pcim_iomap_table(pcidev)[i];
//fxgmac_dbg_log_init();
fxgmac_dbg_log("fxpm,_fxgmac_probe\n");
return fxgmac_drv_probe(&pcidev->dev, &res);
}
static void fxgmac_remove(struct pci_dev *pcidev)
{
struct net_device *netdev = dev_get_drvdata(&pcidev->dev);
struct fxgmac_pdata * pdata = netdev_priv(netdev);
#ifdef CONFIG_PCI_MSI
u32 msix = FXGMAC_GET_REG_BITS(pdata->expansion.int_flags,
FXGMAC_FLAG_MSIX_POS,
FXGMAC_FLAG_MSIX_LEN);
#endif
fxgmac_drv_remove(&pcidev->dev);
#ifdef CONFIG_PCI_MSI
if(msix){
pci_disable_msix(pcidev);
kfree(pdata->expansion.msix_entries);
pdata->expansion.msix_entries = NULL;
}
#endif
fxgmac_dbg_log("fxpm,_fxgmac_remove\n");
#ifdef HAVE_FXGMAC_DEBUG_FS
fxgmac_dbg_exit(pdata);
#endif /* HAVE_FXGMAC_DEBUG_FS */
//fxgmac_dbg_log_uninit();
}
/* for Power management, 20210628 */
static int __fxgmac_shutdown(struct pci_dev *pdev, bool *enable_wake)
{
struct net_device *netdev = dev_get_drvdata(&pdev->dev);
struct fxgmac_pdata *pdata = netdev_priv(netdev);
u32 wufc = pdata->expansion.wol;
#ifdef CONFIG_PM
int retval = 0;
#endif
DPRINTK("fxpm,_fxgmac_shutdown, callin\n");
fxgmac_dbg_log("fxpm,_fxgmac_shutdown, callin.\n");
rtnl_lock();
/* for linux shutdown, we just treat it as power off wol can be ignored
* for suspend, we do need recovery by wol
*/
fxgmac_net_powerdown(pdata, (unsigned int)!!wufc);
netif_device_detach(netdev);
rtnl_unlock();
#ifdef CONFIG_PM
retval = pci_save_state(pdev);
if (retval) {
DPRINTK("fxpm,_fxgmac_shutdown, save pci state failed.\n");
return retval;
}
#endif
DPRINTK("fxpm,_fxgmac_shutdown, save pci state done.\n");
fxgmac_dbg_log("fxpm,_fxgmac_shutdown, save pci state done.\n");
pci_wake_from_d3(pdev, !!wufc);
*enable_wake = !!wufc;
pci_disable_device(pdev);
DPRINTK("fxpm,_fxgmac_shutdown callout, enable wake=%d.\n", *enable_wake);
fxgmac_dbg_log("fxpm,_fxgmac_shutdown callout, enable wake=%d.\n", *enable_wake);
return 0;
}
static void fxgmac_shutdown(struct pci_dev *pdev)
{
bool wake;
struct net_device *netdev = dev_get_drvdata(&pdev->dev);
struct fxgmac_pdata *pdata = netdev_priv(netdev);
DPRINTK("fxpm, fxgmac_shutdown callin\n");
fxgmac_dbg_log("fxpm, fxgmac_shutdown callin\n");
pdata->expansion.current_state = CURRENT_STATE_SHUTDOWN;
__fxgmac_shutdown(pdev, &wake);
if (system_state == SYSTEM_POWER_OFF) {
pci_wake_from_d3(pdev, wake);
pci_set_power_state(pdev, PCI_D3hot);
}
DPRINTK("fxpm, fxgmac_shutdown callout, system power off=%d\n", (system_state == SYSTEM_POWER_OFF)? 1 : 0);
fxgmac_dbg_log("fxpm, fxgmac_shutdown callout, system power off=%d\n", (system_state == SYSTEM_POWER_OFF)? 1 : 0);
}
#ifdef CONFIG_PM
/* yzhang, 20210628 for PM */
static int fxgmac_suspend(struct pci_dev *pdev,
pm_message_t __always_unused state)
{
int retval;
bool wake;
struct net_device *netdev = dev_get_drvdata(&pdev->dev);
struct fxgmac_pdata *pdata = netdev_priv(netdev);
DPRINTK("fxpm, fxgmac_suspend callin\n");
fxgmac_dbg_log("fxpm, fxgmac_suspend callin\n");
pdata->expansion.current_state = CURRENT_STATE_SUSPEND;
if (netif_running(netdev)) {
retval = __fxgmac_shutdown(pdev, &wake);
if (retval)
return retval;
} else {
wake = !!(pdata->expansion.wol);
}
if (wake) {
pci_prepare_to_sleep(pdev);
} else {
pci_wake_from_d3(pdev, false);
pci_set_power_state(pdev, PCI_D3hot);
}
DPRINTK("fxpm, fxgmac_suspend callout to %s\n", wake ? "sleep" : "D3hot");
fxgmac_dbg_log("fxpm, fxgmac_suspend callout to %s\n", wake ? "sleep" : "D3hot");
return 0;
}
static int fxgmac_resume(struct pci_dev *pdev)
{
struct fxgmac_pdata *pdata;
struct net_device *netdev;
u32 err;
DPRINTK("fxpm, fxgmac_resume callin\n");
fxgmac_dbg_log("fxpm, fxgmac_resume callin\n");
netdev = dev_get_drvdata(&pdev->dev);
pdata = netdev_priv(netdev);
pdata->expansion.current_state = CURRENT_STATE_RESUME;
pci_set_power_state(pdev, PCI_D0);
pci_restore_state(pdev);
/*
* pci_restore_state clears dev->state_saved so call
* pci_save_state to restore it.
*/
pci_save_state(pdev);
err = pci_enable_device_mem(pdev);
if (err) {
dev_err(pdata->dev, "fxgmac_resume, failed to enable PCI device from suspend\n");
return err;
}
smp_mb__before_atomic();
__clear_bit(FXGMAC_POWER_STATE_DOWN, &pdata->expansion.powerstate);
pci_set_master(pdev);
pci_wake_from_d3(pdev, false);
rtnl_lock();
err = 0;//ixgbe_init_interrupt_scheme(adapter);
if (!err && netif_running(netdev))
fxgmac_net_powerup(pdata);
if (!err)
netif_device_attach(netdev);
rtnl_unlock();
DPRINTK("fxpm, fxgmac_resume callout\n");
fxgmac_dbg_log("fxpm, fxgmac_resume callout\n");
return err;
}
#endif
static const struct pci_device_id fxgmac_pci_tbl[] = {
{ PCI_DEVICE(0x1f0a, 0x6801) },
{ 0 }
};
MODULE_DEVICE_TABLE(pci, fxgmac_pci_tbl);
static struct pci_driver fxgmac_pci_driver = {
.name = FXGMAC_DRV_NAME,
.id_table = fxgmac_pci_tbl,
.probe = fxgmac_probe,
.remove = fxgmac_remove,
#ifdef CONFIG_PM
/* currently, we only use USE_LEGACY_PM_SUPPORT */
.suspend = fxgmac_suspend,
.resume = fxgmac_resume,
#endif
.shutdown = fxgmac_shutdown,
};
module_pci_driver(fxgmac_pci_driver);
MODULE_DESCRIPTION(FXGMAC_DRV_DESC);
MODULE_VERSION(FXGMAC_DRV_VERSION);
MODULE_AUTHOR("Frank <Frank.Sae@motor-comm.com>");
MODULE_LICENSE("Dual BSD/GPL");