/*
	PISO-CAN-FD series device driver.

        Author: Golden Wang

        This program is free software; you can redistribute it and/or modify
        it under the terms of the GNU General Public License as published by
        the Free Software Foundation; either version 2, or (at your option)
        any later version.

        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.

        You should have received a copy of the GNU General Public License
        along with this program; if not, write to the Free Software Foundation,
        Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/

/* File level history (record changes for this file here.)
 *
 *      v 0.0.0 15 Nov 2022 by Golden Wang
 *	Linux SocketCAN Driver for PISO-CAN200U/400U-FD Series.
 *     
 *     
*/

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/netdevice.h>
#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/can.h>
#include <linux/can/dev.h>
#include <linux/io.h>
#include <linux/string.h>
#include "mcp251xfd.h"

#define DRV_NAME  "icpdas-ixcanfd"

MODULE_AUTHOR("Golden <service@icpdas.com>");
MODULE_DESCRIPTION("Socket-CAN driver for ICPDAS PISO-CAN-FD Series");
MODULE_SUPPORTED_DEVICE("ICPDAS PISO-CAN-FD cards");
MODULE_LICENSE("GPL v2");

#define CAN_TIMEOUT_CNT 50000
#define IXCAN_PCI_MAX_CHAN 12	//Max channels at one card
#define CNL 32			//The length of card name

struct ixcanfd_pci_card {
	int version;
	int board;
	int channels;
	char name[CNL];	//20220812 : Golden add

	struct pci_dev *pci_dev;
	struct net_device *net_dev[IXCAN_PCI_MAX_CHAN];

	void __iomem *conf_addr;
	void __iomem *base_addr;
};

#define IXCAN_PCI_CAN_CLOCK 40000000
//#define IXCAN_PCI_CAN_CLOCK (16000000 / 2)

/*
 * Register definitions for the PLX 9030
 */
#define PLX_ICSR            0x84   /* Interrupt Control/Status register */
//#define PLX_ICSR_LINTI1_ENA 0x0001 /* LINTi1 Enable */
#define PLX_ICSR_PCIINT_ENA 0x0001 /* PCI Interrupt Enable */
//#define PLX_ICSR_LINTI1_CLR 0x0400 /* Local Edge Triggerable Interrupt Clear */
#define PLX_ICSR_ENA_CLR  PLX_ICSR_PCIINT_ENA

/*
 * The board configuration is probably following:
 * RX1 is connected to ground.
 * TX1 is not connected.
 * CLKO is not connected.
 * Setting the OCR register to 0xDA is a good idea.
 * This means normal output mode, push-pull and the correct polarity.
 */
#define IXCAN_PCI_OCR         (OCR_TX0_PUSHPULL | OCR_TX1_PUSHPULL)

/*
 * In the CDR register, you should set CBP to 1.
 * You will probably also want to set the clock divider value to 7
 * (meaning direct oscillator output) because the second SJA1000 chip
 * is driven by the first one CLKOUT output.
 */
#define IXCAN_PCI_CDR             (CDR_CBP | CDR_CLKOUT_MASK)

#define IXCAN_VENDOR_ID1        0x10b5

#define IXCAN_DEVICE_ID1        0xc000

#define IXCAN_SUBVENDOR_ID1     0x2129

#define IXCAN_SUBDEVICE_PISO_CANLAT_XO2  0xc000

static unsigned int board_num = 1;
static unsigned int port_num = 1;
static struct pci_device_id ixcanfd_pci_tbl[] = {
        /* PISO" means "Pci" interface and "ISOlation"*/
	/* PISO-CAN200U/400U-FD(Lattice XO2 4000) */
        {IXCAN_VENDOR_ID1, IXCAN_DEVICE_ID1, IXCAN_SUBVENDOR_ID1, IXCAN_SUBDEVICE_PISO_CANLAT_XO2,},
        { 0,}
};

MODULE_DEVICE_TABLE(pci, ixcanfd_pci_tbl);

/*
 * Helper to read internal registers from card logic (not CAN)
 */
#if 0
void fpga_wait_spi_ready(struct ixcanfd_pci_card *card, unsigned int reg_set)
{
        int wait = 1;
        unsigned int deadline = 100; // Wait (100 us max)
        unsigned long reg_val;

        //wait SPI ready
        while(wait)
        {
                reg_val = ioread32(card->base_addr + reg_set);

                //Bit 1(SPI_Rdy) = 0 -> SPI transmit OK.
                //Bit 1(SPI_Rdy) = 1 -> SPI is transmitting.

                if((reg_val & 0x2) == 0)
		{
			printk("XXX %s ... CAN Port SPI OK\n",__FUNCTION__);
                        wait = 0;
		}

                if (wait && deadline <=0 )
                        wait = 0 ;

                if (wait)
                {
                        deadline--;
                        udelay(1);
                }
        }
}
#endif

#if 1
void MCP2518FD_reset(struct ixcanfd_pci_card *card, unsigned int canportid)
{
        unsigned int reg_set = 8 + (8 * canportid);
        unsigned int reg_data = 4 + (8 * canportid);
        int wait = 1;
        unsigned int deadline = 100; // Wait (100 us max)
        unsigned long reg_val;

	//printk("XXX %s ... CAN Port %d(TR) = card->base_addr(0x%x) + reg_data(0x%x)\n",__FUNCTION__, canportid, card->base_addr, reg_data);
	//printk("XXX %s ... CAN Port %d(CS) = card->base_addr(0x%x) + reg_set(0x%x)\n",__FUNCTION__, canportid, card->base_addr, reg_set);
        //Data Width = 16-bit + CS_High
        iowrite32(0x2001, card->base_addr + reg_set);

        //Data Width = 16-bit + CS_Low
        iowrite32(0x2000, card->base_addr + reg_set);

        //Reset instruction: 0x0000
        iowrite32(0x0000, card->base_addr + reg_data);

        //wait SPI ready
        //FPGA_WaitSPIRdy(dx, RegSet);
        //fpga_wait_spi_ready(card, reg_set);
        //wait SPI ready
        while(wait)
        {
                reg_val = ioread32(card->base_addr + reg_set);

                //Bit 1(SPI_Rdy) = 0 -> SPI transmit OK.
                //Bit 1(SPI_Rdy) = 1 -> SPI is transmitting.

                if((reg_val & 0x2) == 0)
		{
			//printk("XXX %s ... CAN Port SPI OK\n",__FUNCTION__);
                        wait = 0;
		}

                if (wait && deadline <=0 )
                        wait = 0 ;

                if (wait)
                {
                        deadline--;
                        udelay(1);
                }
        }

        //Data Width = 16-bit + CS_High
        iowrite32(0x2001, card->base_addr + reg_set);
}
#endif

#if 1
static void ixcanfd_pci_card_reset(struct ixcanfd_pci_card *card)
{
	int i = 0;

	if(card->version == 0)
	{
		//Hardware Reset CAN(all channels)
		for(i = 0; i < card->channels; i++)
		{
			MCP2518FD_reset(card, i);
		}
	}
}
#endif

unsigned short endian_swap_ushort(unsigned short x)
{
	return (x>>8)|(x<<8);
}

//Used for reading data from MCP2518FD 0x400 Memory ram register
//datalen must be multiple of 4 byte
void ixcanfd_pci_readraw_reg (const struct mcp251xfd_priv *priv, unsigned short reg_addr, unsigned char *buf, unsigned short datalen)
{
	//struct ixcanfd_pci_card *card = priv->priv;
        unsigned int reg_set = 8 + (8 * priv->can_port);
        unsigned int reg_data = 4 + (8 * priv->can_port);
	unsigned short tmpdatalen;
        unsigned short readCommand = endian_swap_ushort((reg_addr & 0x0FFF) | (0x3 << 12)) ;
        //u8 val = 0;
        //unsigned int u32Data;

	//printk("XXX %s ... can_port(%d) ... read reg_base(0x%x) + reg_addr(0x%x)\n",__FUNCTION__, priv->can_port, priv->reg_base, reg_addr);

	
        //read command
	//WRITE_REGISTER_ULONG((ULONG *)(dx->VirtualAddr[0]+RegSet), 0x2001);  //Data Width = 16-bit + CS_High
	iowrite32(0x2001, priv->reg_base + reg_set);

  	//WRITE_REGISTER_ULONG((ULONG *)(dx->VirtualAddr[0]+RegSet), 0x2000);  //Data Width = 16-bit + CS_Low
	iowrite32(0x2000, priv->reg_base + reg_set);

	//WRITE_REGISTER_ULONG((ULONG *)(dx->VirtualAddr[0]+RegData), readCommand);
        iowrite32(readCommand, priv->reg_base + reg_data);

        //wait SPI ready
	fpga_wait_spi_ready(priv, reg_set);

	//write 32-bit data width
	//WRITE_REGISTER_ULONG((ULONG *)(dx->VirtualAddr[0]+RegSet), 0x4000);  //Data Width = 32-bit + CS_Low
	iowrite32(0x4000, priv->reg_base + reg_set);

	for(tmpdatalen = 0; tmpdatalen < datalen; tmpdatalen+=4)
	{
		//trigger trans data
		//WRITE_REGISTER_ULONG((ULONG *)(dx->VirtualAddr[0]+RegData), 0xFFFFFFFF);
		iowrite32(0xFFFFFFFF, priv->reg_base + reg_data);

        	//wait SPI ready
		fpga_wait_spi_ready(priv, reg_set);

		//*(ULONG*)&buf[tmpdatalen] = READ_REGISTER_ULONG((ULONG *)(dx->VirtualAddr[0]+RegData));
		*(unsigned long *)&buf[tmpdatalen] = ioread32(priv->reg_base + reg_data);
	}

	//WRITE_REGISTER_ULONG((ULONG *)(dx->VirtualAddr[0]+RegSet), 0x2001);  //Data Width = 16-bit + CS_High
	iowrite32(0x2001, priv->reg_base + reg_set);
}

static u8 ixcanfd_pci_read8_reg(const struct mcp251xfd_priv *priv, unsigned short reg_addr)
{
	//struct ixcanfd_pci_card *card = priv->priv;	
	
	//printk("XXX %s ... can_port(%d) ... read reg_base(0x%x) + reg_addr(0x%x)\n",__FUNCTION__, priv->can_port, priv->reg_base, reg_addr);
//
#if 1
	unsigned int reg_set = 8 + (8 * priv->can_port);
	unsigned int reg_data = 4 + (8 * priv->can_port);
	unsigned int u32Data;
        //ULONG u32Data;
	unsigned short readCommand = endian_swap_ushort((reg_addr & 0x0FFF) | (0x3 << 12)) ;
	u8 val = 0;

	//read command
	//Data Width = 32-bit + CS_High
	iowrite32(0x4001, priv->reg_base + reg_set);

        //WRITE_REGISTER_ULONG((ULONG *)(dx->VirtualAddr[0]+RegSet), 0x4000);  //Data Width = 32-bit + CS_Low
        iowrite32(0x4000, priv->reg_base + reg_set);

        //u32Data = ULONG(0xFFFF0000 | readCommand);
        u32Data = 0xFFFF0000 | readCommand;

        iowrite32(u32Data, priv->reg_base + reg_data);

        //wait SPI ready
        //FPGA_WaitSPIRdy(dx, RegSet);
        fpga_wait_spi_ready(priv, reg_set);

        //*val = (unsigned char)(ioread32(priv->reg_base + reg_data) >> 16);
	val = (unsigned char)(ioread32(priv->reg_base + reg_data) >> 16);

	//Data Width = 16-bit + CS_High
	iowrite32(0x2001, priv->reg_base + reg_set);

	return val;
#else
	if(card->version == 0)
	{
		//printk("XXX %s ... card->name = %s, card->version = %d\n",__FUNCTION__, card->name, card->version);
		return ioread8(priv->reg_base + (port * 4));
	}
	else
	{
		return ioread8(priv->reg_base + port);
	}
#endif
}

static u16 ixcanfd_pci_read16_reg(const struct mcp251xfd_priv *priv, unsigned short reg_addr)
{
	//struct ixcanfd_pci_card *card = priv->priv;	
	//printk("XXX %s ... can_port(%d) ... read reg_base(0x%x) + reg_addr(0x%x)\n",__FUNCTION__, priv->can_port, priv->reg_base, reg_addr);
//
#if 1
	unsigned int reg_set = 8 + (8 * priv->can_port);
	unsigned int reg_data = 4 + (8 * priv->can_port);
	unsigned int u32Data;
        //ULONG u32Data;
	unsigned short readCommand = endian_swap_ushort((reg_addr & 0x0FFF) | (0x3 << 12)) ;
	u16 val = 0;

        //read command
	//Data Width = 32-bit + CS_High
	iowrite32(0x4001, priv->reg_base + reg_set);

	//Data Width = 32-bit + CS_Low
	iowrite32(0x4000, priv->reg_base + reg_set);

	//u32Data = ULONG(0xFFFF0000 | readCommand);
	u32Data = 0xFFFF0000 | readCommand;

        iowrite32(u32Data, priv->reg_base + reg_data);

        //wait SPI ready
	fpga_wait_spi_ready(priv, reg_set);

        //*outValue = (UCHAR)(READ_REGISTER_ULONG((ULONG *)(dx->VirtualAddr[0]+RegData)) >> 16);
        val = (unsigned short)(ioread32(priv->reg_base + reg_data) >> 16);

	//Data Width = 16-bit + CS_High
        iowrite32(0x2001, priv->reg_base + reg_set);

	return val;
#else
	if(card->version == 0)
	{
		//printk("XXX %s ... card->name = %s, card->version = %d\n",__FUNCTION__, card->name, card->version);
		return ioread8(priv->reg_base + (port * 4));
	}
	else
	{
		return ioread8(priv->reg_base + port);
	}
#endif
}


static u32 ixcanfd_pci_read32_reg(const struct mcp251xfd_priv *priv, unsigned short reg_addr)
{
	//struct ixcanfd_pci_card *card = priv->priv;	

	//printk("XXX %s ... can_port(%d) ... read reg_base(0x%x) + reg_addr(0x%x)\n",__FUNCTION__, priv->can_port, priv->reg_base, reg_addr);
//
#if 1
	unsigned int reg_set = 8 + (8 * priv->can_port);
	unsigned int reg_data = 4 + (8 * priv->can_port);
	//unsigned int u32Data;
        //ULONG u32Data;
	unsigned short readCommand = endian_swap_ushort((reg_addr & 0x0FFF) | (0x3 << 12)) ;
	u32 val = 0;

        //read command
	//Data Width = 16-bit + CS_High
	iowrite32(0x2001, priv->reg_base + reg_set);

	//Data Width = 16-bit + CS_Low
	iowrite32(0x2000, priv->reg_base + reg_set);

        iowrite32(readCommand, priv->reg_base + reg_data);

        //wait SPI ready
	fpga_wait_spi_ready(priv, reg_set);

        //write 32-bit data width
	//Data Width = 32-bit + CS_Low
	iowrite32(0x4000, priv->reg_base + reg_set);

        //trigger trans data
        iowrite32(0xFFFFFFFF, priv->reg_base + reg_data);

        //wait SPI ready
        fpga_wait_spi_ready(priv, reg_set);

        val = ioread32(priv->reg_base + reg_data);

	//Data Width = 16-bit + CS_High
        iowrite32(0x2001, priv->reg_base + reg_set);

	return val;
#else
	if(card->version == 0)
	{
		//printk("XXX %s ... card->name = %s, card->version = %d\n",__FUNCTION__, card->name, card->version);
		return ioread8(priv->reg_base + (port * 4));
	}
	else
	{
		return ioread8(priv->reg_base + port);
	}
#endif
}

static void ixcanfd_pci_write8_reg(const struct mcp251xfd_priv *priv, unsigned short reg_addr, u8 val)
{
	//struct ixcanfd_pci_card *card = priv->priv;	

#if 1
	unsigned int reg_set = 8 + (8 * priv->can_port);
	unsigned int reg_data = 4 + (8 * priv->can_port);
	//unsigned int u32Data;
	unsigned short writeCommand = endian_swap_ushort((reg_addr & 0x0FFF) | (0x2 << 12)) ;

	//printk("XXX %s ... can_port(%d) ... write value(0x%x) -> reg_base(0x%x) + reg_addr(0x%x)\n",__FUNCTION__, priv->can_port, val, priv->reg_base, reg_addr);
	//printk("XXX %s ... card->name = %s, card->version = %d, can_port = %d\n",__FUNCTION__, card->name, card->version, priv->can_port);
	//printk("XXX %s ... Board%d(%s) can%d(Trans/Receive) = bar0(0x%x) + 0x%x\n",__FUNCTION__, card->board, card->name, priv->can_port, priv->reg_base, reg_data);
	//printk("XXX %s ... Board%d(%s) can%d(CS) = bar0(0x%x) + 0x%x\n",__FUNCTION__, card->board, card->name, priv->can_port, priv->reg_base, reg_set);
        //write command
	//Data Width = 16-bit + CS_High
	iowrite32(0x2001, priv->reg_base + reg_set);

	//Data Width = 16-bit + CS_Low
	iowrite32(0x2000, priv->reg_base + reg_set);

	//set CAN Port to the configuration mode
	iowrite32(writeCommand,  priv->reg_base + reg_data);

	//wait SPI ready
	fpga_wait_spi_ready(priv, reg_set);

	//write 8-bit data width
	//Data Width = 8-bit + CS_Low
	iowrite32(0x1000, priv->reg_base + reg_set);

	//write 8-bit data
	iowrite32(val,  priv->reg_base + reg_data);

	//wait SPI ready
	fpga_wait_spi_ready(priv, reg_set);

	//Data Width = 16-bit + CS_High
	iowrite32(0x2001, priv->reg_base + reg_set);
#else
	if(card->version == 0)
	{
		//printk("XXX %s ... card->name = %s, card->version = %d\n",__FUNCTION__, card->name, card->version);
		iowrite8(val, priv->reg_base + (port*4));
	}
	else
	{
		iowrite8(val, priv->reg_base + port);
	}
	
	if(priv->udelay)
	{
		//printk("Enable UDELAY Flag\n");
		udelay(1);
	}
#endif

}


static void ixcanfd_pci_write16_reg(const struct mcp251xfd_priv *priv, unsigned short reg_addr, u16 val)
{
	//struct ixcanfd_pci_card *card = priv->priv;	
#if 1
	unsigned int reg_set = 8 + (8 * priv->can_port);
	unsigned int reg_data = 4 + (8 * priv->can_port);
	//unsigned int u32Data;
	unsigned short writeCommand = endian_swap_ushort((reg_addr & 0x0FFF) | (0x2 << 12)) ;

	//printk("XXX %s ... can_port(%d) ... write value(0x%x) -> reg_base(0x%x) + reg_addr(0x%x)\n",__FUNCTION__, priv->can_port, val, priv->reg_base, reg_addr);
	//printk("XXX %s ... Board%d(%s) can%d(Trans/Receive) = bar0(0x%x) + 0x%x\n",__FUNCTION__, card->board, card->name, priv->can_port, priv->reg_base, reg_data);
	//printk("XXX %s ... Board%d(%s) can%d(CS) = bar0(0x%x) + 0x%x\n",__FUNCTION__, card->board, card->name, priv->can_port, priv->reg_base, reg_set);
        //write command
	//Data Width = 16-bit + CS_High
	//write command
	//Data Width = 16-bit + CS_High
	iowrite32(0x2001, priv->reg_base + reg_set);

	//Data Width = 16-bit + CS_Low
        iowrite32(0x2000, priv->reg_base + reg_set);

	//set CAN Port to the configuration mode
        iowrite32(writeCommand, priv->reg_base + reg_data);

        //wait SPI ready
        //FPGA_WaitSPIRdy(dx, RegSet);
        fpga_wait_spi_ready(priv, reg_set);

	//write 16-bit data width
	//Data Width = 16-bit + CS_Low
	//iowrite32(0x2000, priv->reg_base + reg_set);

	//write 16-bit data
        iowrite32(val, priv->reg_base + reg_data);

	//wait SPI ready
	fpga_wait_spi_ready(priv, reg_set);

	//Data Width = 16-bit + CS_High
	iowrite32(0x2001, priv->reg_base + reg_set);

#else
	if(card->version == 0)
	{
		//printk("XXX %s ... card->name = %s, card->version = %d\n",__FUNCTION__, card->name, card->version);
		iowrite8(val, priv->reg_base + (port*4));
	}
	else
	{
		iowrite8(val, priv->reg_base + port);
	}
	
	if(priv->udelay)
	{
		//printk("Enable UDELAY Flag\n");
		udelay(1);
	}
#endif

}


static void ixcanfd_pci_write32_reg(const struct mcp251xfd_priv *priv, unsigned short reg_addr, u32 val)
{
	//struct ixcanfd_pci_card *card = priv->priv;	
#if 1
	unsigned int reg_set = 8 + (8 * priv->can_port);
	unsigned int reg_data = 4 + (8 * priv->can_port);
	//unsigned int u32Data;
	unsigned short writeCommand = endian_swap_ushort((reg_addr & 0x0FFF) | (0x2 << 12)) ;

	//printk("XXX %s ... can_port(%d) ... write value(0x%x) -> reg_base(0x%x) + reg_addr(0x%x)\n",__FUNCTION__, priv->can_port, val, priv->reg_base, reg_addr);
	//printk("XXX %s ... Board%d(%s) can%d(Trans/Receive) = bar0(0x%x) + 0x%x\n",__FUNCTION__, card->board, card->name, priv->can_port, priv->reg_base, reg_data);
	//printk("XXX %s ... Board%d(%s) can%d(CS) = bar0(0x%x) + 0x%x\n",__FUNCTION__, card->board, card->name, priv->can_port, priv->reg_base, reg_set);
	//write command
	//Data Width = 16-bit + CS_High
	iowrite32(0x2001, priv->reg_base + reg_set);

	//Data Width = 16-bit + CS_Low
	iowrite32(0x2000, priv->reg_base + reg_set);

	//set CAN Port to the configuration mode
	iowrite32(writeCommand, priv->reg_base + reg_data);

	//wait SPI ready
	fpga_wait_spi_ready(priv, reg_set);

	//write 32-bit data width
	//Data Width = 32-bit + CS_Low
	iowrite32(0x4000, priv->reg_base + reg_set);

        //write 32-bit data
        iowrite32(val, priv->reg_base + reg_data);

	//wait SPI ready
	fpga_wait_spi_ready(priv, reg_set);

	//Data Width = 16-bit + CS_High
	iowrite32(0x2001, priv->reg_base + reg_set);

#else
	if(card->version == 0)
	{
		//printk("XXX %s ... card->name = %s, card->version = %d\n",__FUNCTION__, card->name, card->version);
		iowrite8(val, priv->reg_base + (port*4));
	}
	else
	{
		iowrite8(val, priv->reg_base + port);
	}
#endif

	if(priv->udelay)
	{
		//printk("Enable UDELAY Flag\n");
		udelay(1);
	}
}

//Used for writing data into MCP2518FD 0x400 Memory ram register
//datalen must be multiple of 4 byte
void ixcanfd_pci_writeraw_reg(const struct mcp251xfd_priv *priv, unsigned short reg_addr, unsigned char *buf, unsigned short datalen)
{
        //struct ixcanfd_pci_card *card = priv->priv;
//      printk("%s ... reg_base(0x%x) can_port(%d)\n",__FUNCTION__, priv->reg_base, priv->can_port);

	unsigned int reg_set = 8 + (8 * priv->can_port);
	unsigned int reg_data = 4 + (8 * priv->can_port);
	//unsigned int u32Data;
	unsigned short writeCommand = endian_swap_ushort((reg_addr & 0x0FFF) | (0x2 << 12)) ;
	unsigned short tmpdatalen;

	//printk("XXX %s ... can_port(%d) ... write reg_base(0x%x) + reg_addr(0x%x)\n",__FUNCTION__, priv->can_port, priv->reg_base, reg_addr);
	//printk("XXX %s ... Board%d(%s) can%d(Trans/Receive) = bar0(0x%x) + 0x%x\n",__FUNCTION__, card->board, card->name, priv->can_port, priv->reg_base, reg_data);
	//printk("XXX %s ... Board%d(%s) can%d(CS) = bar0(0x%x) + 0x%x\n",__FUNCTION__, card->board, card->name, priv->can_port, priv->reg_base, reg_set);

	//write command
	//WRITE_REGISTER_ULONG((ULONG *)(dx->VirtualAddr[0]+RegSet), 0x2001);  //Data Width = 16-bit + CS_High
	iowrite32(0x2001, priv->reg_base + reg_set);

	//WRITE_REGISTER_ULONG((ULONG *)(dx->VirtualAddr[0]+RegSet), 0x2000);  //Data Width = 16-bit + CS_Low
	iowrite32(0x2000, priv->reg_base + reg_set);

	//WRITE_REGISTER_ULONG((ULONG *)(dx->VirtualAddr[0]+RegData), writeCommand);
	//printk("XXX %s ... writeCommand = 0x%x\n",__FUNCTION__, writeCommand);
	iowrite32(writeCommand, priv->reg_base + reg_data);

	//wait SPI ready
	fpga_wait_spi_ready(priv, reg_set);

	//write 32-bit data width
	//WRITE_REGISTER_ULONG((ULONG *)(dx->VirtualAddr[0]+RegSet), 0x4000);  //Data Width = 32-bit + CS_Low
	iowrite32(0x4000, priv->reg_base + reg_set);

	for(tmpdatalen = 0; tmpdatalen < datalen; tmpdatalen+=4)
	{
		//write 32-bit data
		//WRITE_REGISTER_ULONG((ULONG *)(dx->VirtualAddr[0]+RegData), *(ULONG*)&buf[tmpdatalen]);
		//printk("XXX %s ... *(unsigned int *)&buf[tmpdatalen] = 0x%x\n", __FUNCTION__, *(unsigned int *)&buf[tmpdatalen]);
		iowrite32(*(unsigned int *)&buf[tmpdatalen], priv->reg_base + reg_data);

		//wait SPI ready
		fpga_wait_spi_ready(priv, reg_set);
	}

	//WRITE_REGISTER_ULONG((ULONG *)(dx->VirtualAddr[0]+RegSet), 0x2001);  //Data Width = 16-bit + CS_High
	iowrite32(0x2001, priv->reg_base + reg_set);
}

#if 0
/*
 * Check if a CAN controller is present at the specified location
 * by trying to set 'em into the PeliCAN mode
 */
static inline int ixcanfd_pci_check_chan(const struct mcp251xfd_priv *priv)
{
	unsigned char res;

	/* Make sure SJA1000 is in reset mode */
	priv->write_reg(priv, REG_MOD, 1);

	priv->write_reg(priv, REG_CDR, CDR_PELICAN);

	/* read reset-values */
	res = priv->read_reg(priv, REG_CDR);

	if (res == CDR_PELICAN)
		return 1;

	return 0;
}
#endif

static void ixcanfd_pci_del_card(struct pci_dev *pdev)
{
	struct ixcanfd_pci_card *card = pci_get_drvdata(pdev);
	struct mcp251xfd_priv *priv;
	struct net_device *dev;
	int i = 0;

	for (i = 0; i < card->channels; i++) 
	{
		dev = card->net_dev[i];
		
		if (!dev)
			continue;
		
		dev_info(&pdev->dev, "Removing %s.\n", dev->name);
		
		priv = netdev_priv(dev);
		//mcp251xfd_unregister(priv);
		free_mcp251xfddev(dev);
	}

	if(card->version == 0)
	{
#if 1
		///FPGA disable all interrupt
		iowrite32(0xFFFFFFFF, card->conf_addr + 0);

		//clear all interrupt flag
		for(i=0; i<8; i++)
		{
			iowrite32(0xFFFFFFFF, card->conf_addr + ( 0x48 + (8 * i)));
		}
#endif
		//iowrite32(PLX_ICSR_ENA_CLR, card->conf_addr + PLX_ICSR);
		//if (card->base_addr != NULL)
		//	pci_iounmap(card->pci_dev, card->base_addr);

		if (card->conf_addr != NULL)
			pci_iounmap(card->pci_dev, card->conf_addr);
	}

	kfree(card);

	pci_disable_device(pdev);
	pci_set_drvdata(pdev, NULL);
}


/*
 * Probe PCI device for EMS CAN signature and register each available
 * CAN channel to SJA1000 Socket-CAN subsystem.
 */
//static int __devinit ixcanfd_pci_add_card(struct pci_dev *pdev,const struct pci_device_id *ent)
static int ixcanfd_pci_add_card(struct pci_dev *pdev,const struct pci_device_id *ent)
{
	struct mcp251xfd_priv *priv;
	struct net_device *dev;
	struct ixcanfd_pci_card *card;
	//int max_chan, conf_size, base_bar;
	int err = 0, i, canports = 0;

	//printk("XXX %s ... vendor = 0x%x, device = 0x%x, subven = 0x%x, subdev = 0x%x\n",__FUNCTION__, ent->vendor, ent->device,  ent->subvendor, ent->subdevice);

	/* Enabling PCI device */
	if (pci_enable_device(pdev) < 0) 
	{
		dev_err(&pdev->dev, "Enabling PCI device failed\n");
		return -ENODEV;
	}

	/* Allocating card structures to hold addresses, ... */
	card = kzalloc(sizeof(struct ixcanfd_pci_card), GFP_KERNEL);
	if (card == NULL) 
	{
		dev_err(&pdev->dev, "Unable to allocate memory\n");
		pci_disable_device(pdev);
		return -ENOMEM;
	}

	pci_set_drvdata(pdev, card);

	card->pci_dev = pdev;
	card->board = board_num;

	if(ent->device == IXCAN_DEVICE_ID1)
	{
		card->version = 0;	//support PISO-CAN200U/400U-FD(Lattice XO2)
		
		/* Remap configuration space and controller memory area */
		card->conf_addr = pci_iomap(pdev, 0, pci_resource_len(pdev, 0));

		if (card->conf_addr == NULL) 
		{
			err = -ENOMEM;
			goto failure_cleanup;
		}

		card->base_addr = card->conf_addr;	//Using Bar0 to set Lattice XO2
		//printk("XXX %s .... card->base_addr = 0x%x ... card->conf_addr = 0x%x\n",__FUNCTION__, card->base_addr, card->conf_addr);
		
		if (card->base_addr == NULL) 
		{
			err = -ENOMEM;
			goto failure_cleanup;
		}

		//printk("XXX %s ... Lattice XO2 conf_addr = 0x%x, base_addr = 0x%x\n", __FUNCTION__, card->conf_addr, card->base_addr);
		canports = ioread32(card->conf_addr + 0) & 0xf;

		//printk("XXX %s ... Lattice XO2 conf_addr = 0x%x, base_addr = 0x%x, board id(canports) = 0x%x\n", __FUNCTION__, card->conf_addr, card->base_addr, canports);

		switch(canports)
		{
			case 0:
				card->channels = 2;
				strncpy(card->name, "CAN200-FD", CNL);
				break;
			case 1:
				card->channels = 2;
				strncpy(card->name, "PISO-CAN200U-FD", CNL);
				break;
			case 2:
				card->channels = 4;
				strncpy(card->name, "PISO-CAN400U-FD", CNL);
				break;
			case 3:
				card->channels = 2;
				strncpy(card->name, "CAN200-FD", CNL);
				break;
			default:
				card->channels = 0;
				break;
		}
		
		if(card->channels == 0)
			goto failure_cleanup;
	}
	else
	{
		card->channels = 0;
		goto failure_cleanup;
	}

#if 1	
	if(card->version == 0)
	{
		///FPGA disable all interrupt
		iowrite32(0xFFFFFFFF, card->conf_addr + 0);

		//clear all interrupt flag
		for(i=0; i<8; i++)
		{
			iowrite32(0xFFFFFFFF, card->conf_addr + ( 0x48 + (8 * i)));
		}
		//iowrite32(PLX_ICSR_ENA_CLR, card->conf_addr + PLX_ICSR);
	}
#endif
	ixcanfd_pci_card_reset(card);

	/* Detect available channels */
	for (i = 0; i < card->channels; i++) 
	{
		if(card->version == 0)
		{
			dev = alloc_mcp251xfddev(sizeof(struct ixcanfd_pci_card));
		}

		if (dev == NULL) 
		{
			err = -ENOMEM;
			goto failure_cleanup;
		}

		card->net_dev[i] = dev;
		priv = netdev_priv(dev);
#if 1
		priv->card = card;
		priv->priv = card;
		priv->ndev = dev;
		priv->irq = pdev->irq;
#else
		priv->priv = card;
		priv->irq_flags = IRQF_SHARED;
#endif

		dev->irq = pdev->irq;

		if(card->version == 0)
		{
			priv->reg_base = card->base_addr;
			//printk("XXX %s card->base = 0x%x ... channel%d reg_base = 0x%x\n",__FUNCTION__, card->base_addr, i, priv->reg_base);
		}
		else
		{
			card->channels = 0;
			goto failure_cleanup;
		}

#if 1
		priv->read8_reg  = ixcanfd_pci_read8_reg;
		priv->write8_reg = ixcanfd_pci_write8_reg;
		priv->read16_reg  = ixcanfd_pci_read16_reg;
		priv->write16_reg = ixcanfd_pci_write16_reg;
		priv->read32_reg  = ixcanfd_pci_read32_reg;
		priv->write32_reg = ixcanfd_pci_write32_reg;
		priv->readraw_reg = ixcanfd_pci_readraw_reg;
		priv->writeraw_reg = ixcanfd_pci_writeraw_reg;
#endif
		priv->can_port = i;
		priv->vendor = ent->vendor;
		priv->device = ent->device;
		priv->subvendor = ent->subvendor;
		priv->subdevice = ent->subdevice;

		priv->udelay = 0;

		/* Check if channel is present */
#if 1
		//priv->can.clock.freq = IXCAN_PCI_CAN_CLOCK;	//"alloc_mcp251xfddev" will set the "priv->can.clock.freq" = 40M
		SET_NETDEV_DEV(dev, &pdev->dev);

		/* Register SJA1000 device */
#if 1
		err =  mcp251xfd_register(priv);
		//err = register_mcp251xfddev(dev);
#endif

		if (err)
		{
			dev_err(&pdev->dev, "Registering device failed (err=%d)\n", err);
			free_mcp251xfddev(dev);
			goto failure_cleanup;
		}

		dev_info(&pdev->dev, "Board #%d : Channel #%d Port #%d (can%d) at 0x%p, irq %d\n", card->board, i + 1, port_num, (port_num-1), priv->reg_base, dev->irq);

#else
		if (ixcanfd_pci_check_chan(priv)) 
		{
			priv->can.clock.freq = IXCAN_PCI_CAN_CLOCK;
			priv->ocr = IXCAN_PCI_OCR;
			priv->cdr = IXCAN_PCI_CDR;

			SET_NETDEV_DEV(dev, &pdev->dev);

			/* Register SJA1000 device */
			err = register_sja1000dev(dev);
			if (err) 
			{
				dev_err(&pdev->dev, "Registering device failed "
							"(err=%d)\n", err);
				free_sja1000dev(dev);
				goto failure_cleanup;
			}

			dev_info(&pdev->dev, "Board #%d : Channel #%d Port #%d (can%d) at 0x%p, irq %d\n", card->board, i + 1, port_num, (port_num-1), priv->reg_base, dev->irq);
		} 
		else 
		{
			free_sja1000dev(dev);
		}
#endif
		port_num++;
	}

#if 1	
	if(card->version == 0)
	{
		///FPGA enable PCI interrupt
		iowrite32(PLX_ICSR_ENA_CLR, card->conf_addr + PLX_ICSR);

		//printk("XXX %s ... PLX_ICSR = 0x%x\n",__FUNCTION__, ioread32(card->conf_addr + PLX_ICSR));
	}
#endif

	board_num++;

	return 0;

failure_cleanup:
	dev_err(&pdev->dev, "Error: %d. Cleaning Up.\n", err);

	ixcanfd_pci_del_card(pdev);

	return err;
}

static struct pci_driver ixcanfd_pci_driver = {
	.name = DRV_NAME,
	.id_table = ixcanfd_pci_tbl,
	.probe = ixcanfd_pci_add_card,
	.remove = ixcanfd_pci_del_card,
};

static int __init ixcanfd_pci_init(void)
{
	return pci_register_driver(&ixcanfd_pci_driver);
}

static void __exit ixcanfd_pci_exit(void)
{
	pci_unregister_driver(&ixcanfd_pci_driver);
}

module_init(ixcanfd_pci_init);
module_exit(ixcanfd_pci_exit);

