/*
  PISO-CAN 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.3.1  12 Aug  2022 by Golden Wang
 *  Give supprot to PISO-CAN100U/200U/400U/800U(Latttice XO2 4000, PCB rev 2.00 or later version).
 *
 *  v 0.3.0  9 Aug  2012 by Golden Wang
 *   Give supprot to PISO-CAN800U.
 *
 *  v 0.2.0  9 Aug  2012 by Golden Wang
 *  Give supprot to PISO-CAN100U-D/T.
 *
 *  v 0.1.0 26 Jul 2012 by Golden Wang
 *  Give support to linux kernel 3.x.x.       
 *
 *      v 0.0.0 19 Mar 2010 by Golden Wang
 *  Linux SocketCAN Driver for PISO-CAN200/400 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 "sja1000.h"

#define DRV_NAME  "icpdas-ixcan"

MODULE_AUTHOR("Golden <service@icpdas.com>");
MODULE_DESCRIPTION("Socket-CAN driver for ICPDAS PISO-CAN Series");
//MODULE_SUPPORTED_DEVICE("ICPDAS PISO-CAN cards"); //winson 20240429
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 ixcan_pci_card {
  int version;
  int board;
  int channels;
  char name[CNL]; //20220812 : Golden add
  int card_id;  //20220922 : Winson 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 (16000000 / 2)

/*
 * Register definitions for the PLX 9030
 */
#define PLX_ICSR            0x4c   /* Interrupt Control/Status register */
#define PLX_ICSR_LINTI1_ENA 0x0001 /* LINTi1 Enable */
#define PLX_ICSR_PCIINT_ENA 0x0040 /* PCI Interrupt Enable */
#define PLX_ICSR_LINTI1_CLR 0x0400 /* Local Edge Triggerable Interrupt Clear */
#define PLX_ICSR_ENA_CLR    (PLX_ICSR_LINTI1_ENA | 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        0x9030
#define IXCAN_DEVICE_ID2        0x9050
#define IXCAN_DEVICE_ID3        0xc002

#define IXCAN_SUBVENDOR_ID1     0x2129

#define IXCAN_SUBDEVICE_PISO_CAN200      0xc200
#define IXCAN_SUBDEVICE_PISO_CAN400      0xc400
#define IXCAN_SUBDEVICE_PISO_CAN100U     0xc110
#define IXCAN_SUBDEVICE_PISO_CAN200U     0xc210
#define IXCAN_SUBDEVICE_PISO_CAN400U     0xc410
#define IXCAN_SUBDEVICE_PISO_CAN800U     0xc810
#define IXCAN_SUBDEVICE_PISO_CAN200E     0xc280
#define IXCAN_SUBDEVICE_PISO_CAN400E     0xc480
#define IXCAN_SUBDEVICE_PCM_CAN100       0xc150
#define IXCAN_SUBDEVICE_PCM_CAN200       0xc250
#define IXCAN_SUBDEVICE_PCM_CAN400       0xc450
#define IXCAN_SUBDEVICE_PISO_CANLAT_XO2  0xc000

static unsigned int board_num = 1;
static unsigned int port_num = 1;
static struct pci_device_id ixcan_pci_tbl[] = {
  /* PISO" means "Pci" interface and "ISOlation"*/
  /* PISO-CAN200, 2-CAN Port*/
  {IXCAN_VENDOR_ID1, IXCAN_DEVICE_ID2, IXCAN_SUBVENDOR_ID1, IXCAN_SUBDEVICE_PISO_CAN200,},

  /* PISO-CAN400, 4-CAN Port */
  {IXCAN_VENDOR_ID1, IXCAN_DEVICE_ID2, IXCAN_SUBVENDOR_ID1, IXCAN_SUBDEVICE_PISO_CAN400,},

  /* PISO-CAN200U, 2-CAN Port, Universal PCI */
  {IXCAN_VENDOR_ID1, IXCAN_DEVICE_ID1, IXCAN_SUBVENDOR_ID1, IXCAN_SUBDEVICE_PISO_CAN200U,},

  /* PISO-CAN400U, 4-CAN Port, Universal PCI */
  {IXCAN_VENDOR_ID1, IXCAN_DEVICE_ID1, IXCAN_SUBVENDOR_ID1, IXCAN_SUBDEVICE_PISO_CAN400U,},

  /* PISO-CAN200E, 2-CAN Port, PCI Express interface*/
  {IXCAN_VENDOR_ID1, IXCAN_DEVICE_ID2, IXCAN_SUBVENDOR_ID1, IXCAN_SUBDEVICE_PISO_CAN200E,},

  /* PCM-CAN100, 1-CAN Port, PC104/PC104+ compliant */
  {IXCAN_VENDOR_ID1, IXCAN_DEVICE_ID1, IXCAN_SUBVENDOR_ID1, IXCAN_SUBDEVICE_PCM_CAN100,},

  /* PCM-CAN200, 2-CAN Port, PC104/PC104+ compliant */
  {IXCAN_VENDOR_ID1, IXCAN_DEVICE_ID1, IXCAN_SUBVENDOR_ID1, IXCAN_SUBDEVICE_PCM_CAN200,},

  /* PCM-CAN400, 4-CAN Port, PC104/PC104+ compliant */
  {IXCAN_VENDOR_ID1, IXCAN_DEVICE_ID1, IXCAN_SUBVENDOR_ID1, IXCAN_SUBDEVICE_PCM_CAN400,},
  /* PISO-CAN100U, 1-CAN Port compliant */
  {IXCAN_VENDOR_ID1, IXCAN_DEVICE_ID1, IXCAN_SUBVENDOR_ID1, IXCAN_SUBDEVICE_PISO_CAN100U,},
  /* PISO-CAN800U, 8-CAN Port, PC1 Interface */
  {IXCAN_VENDOR_ID1, IXCAN_DEVICE_ID1, IXCAN_SUBVENDOR_ID1, IXCAN_SUBDEVICE_PISO_CAN800U,},
  /* PISO-CAN100U/200U/400U/800U(Lattice XO2 4000, PCB rev 2.00 or later version) */
  {IXCAN_VENDOR_ID1, IXCAN_DEVICE_ID3, IXCAN_SUBVENDOR_ID1, IXCAN_SUBDEVICE_PISO_CANLAT_XO2,},
  { 0,}
};

MODULE_DEVICE_TABLE(pci, ixcan_pci_tbl);

/*
 * Helper to read internal registers from card logic (not CAN)
 */
static u8 ixcan_pci_read_reg(const struct sja1000_priv *priv, int port)
{
  struct ixcan_pci_card *card = priv->priv; 
//  printk("%s ... reg_base(0x%x) can_port(%d)\n",__FUNCTION__, priv->reg_base, priv->can_port);
  if(card->version == 1){
    //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);
  }
}

static void ixcan_pci_write_reg(const struct sja1000_priv *priv,
                                 int port, u8 val)
{
  struct ixcan_pci_card *card = priv->priv; 
//  printk("%s ... reg_base(0x%x) can_port(%d)\n",__FUNCTION__, priv->reg_base, priv->can_port);

  if(card->version == 1){
    //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);
  }
}

/*
 * Check if a CAN controller is present at the specified location
 * by trying to set 'em into the PeliCAN mode
 */
static inline int ixcan_pci_check_chan(const struct sja1000_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;
}

static void ixcan_pci_del_card(struct pci_dev *pdev)
{
  struct ixcan_pci_card *card = pci_get_drvdata(pdev);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,31)
  struct sja1000_priv *priv;
#endif
  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);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,31)
    priv = netdev_priv(dev);
  
    if(card->version == 0){
      if(priv->reg_base != NULL)
        pci_iounmap(card->pci_dev, priv->reg_base);
    }

#endif

    unregister_sja1000dev(dev);
    free_sja1000dev(dev);
  }

  if(card->version == 1){
    //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);
  }
  else{
    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);
}

static void ixcan_pci_card_reset(struct ixcan_pci_card *card)
{
  unsigned int timeout = 0;

  if(card->version == 1){
    //Hardware Reset CAN(all channels)
    iowrite8(0, card->base_addr + 0);
    //WRITE_REGISTER_UCHAR((UCHAR *)(dx->VirtualAddr[0] + 0x00), ~ResetReg[Port]);
    iowrite8(0xff, card->base_addr + 0);
    //WRITE_REGISTER_UCHAR((UCHAR *)(dx->VirtualAddr[0] + 0x00), 0xFF);
    while(++timeout < CAN_TIMEOUT_CNT){}
  }
  else{
    /* Request board reset */
    writeb(0, card->base_addr);
  }
}

/*
 * Probe PCI device for EMS CAN signature and register each available
 * CAN channel to SJA1000 Socket-CAN subsystem.
 */
//static int __devinit ixcan_pci_add_card(struct pci_dev *pdev,const struct pci_device_id *ent)
static int ixcan_pci_add_card(struct pci_dev *pdev,const struct pci_device_id *ent)
{
  struct sja1000_priv *priv;
  struct net_device *dev;
  struct ixcan_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 ixcan_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_ID3){
    card->version = 1;  //support PISO-CAN100U/200U/400U/800U(Lattice XO2, PCB rev 2.0.0)
    
    /* 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
    
    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 = ioread8(card->conf_addr + 4) & 0xff;
    canports = ~canports;

    switch(((canports >> 4) & 0x0f)){
      case 1:
        card->channels = 1;
        strncpy(card->name, "PISO-CAN100U", CNL);
        break;
      case 2:
        card->channels = 2;
        strncpy(card->name, "PISO-CAN200U", CNL);
        break;
      case 3:
        card->channels = 4;
        strncpy(card->name, "PISO-CAN400U", CNL);
        break;
      case 4:
        card->channels = 8;
        strncpy(card->name, "PISO-CAN800U", CNL);
        break;
      default:
        card->channels = 0;
        strncpy(card->name, "UnKnow", CNL);
        break;
    }

    if (card->channels == 0)
      goto failure_cleanup;
  }
  else{
    card->version = 0;  //support PISO-CAN100U/200U/400U/800U(PLX IC, PCB rev 1.00)  

    if (((pdev->subsystem_device >> 8)&0x0f) == 1)
      card->channels = 1;
    else if (((pdev->subsystem_device >> 8)&0x0f) == 2)
      card->channels = 2;
    else if (((pdev->subsystem_device >> 8)&0x0f) == 4)
      card->channels = 4;
    else
      card->channels = 8;

    /* 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;
    }

    if (ent->subdevice == IXCAN_SUBDEVICE_PISO_CAN800U){
      card->base_addr = pci_iomap(pdev, 3, pci_resource_len(pdev, 3));
      //card->can800_base_addr = pci_iomap(pdev, 3, pci_resource_len(pdev, 3));
    }
    else{
      card->base_addr = pci_iomap(pdev, 2, pci_resource_len(pdev, 2));
    }

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

  ixcan_pci_card_reset(card);

  if(card->version == 1){  //old card id doesn't tested, just add in new version 800U PCB 3.00 above, other PCB 2.00 above
    card->card_id = ioread8(card->conf_addr + 4) & 0xf;
    card->card_id = (~card->card_id) & 0xf;
    dev_info(&pdev->dev,"Card name %s, Card ID %d\n",card->name, card->card_id);  //winson
  }
  //Add here winson
  /* Detect available channels */
  for (i = 0; i < card->channels; i++) {
    dev = alloc_sja1000dev(sizeof(struct ixcan_pci_card));
    if (dev == NULL) {
      err = -ENOMEM;
      goto failure_cleanup;
    }

    card->net_dev[i] = dev;
    priv = netdev_priv(dev);
    priv->priv = card;
    priv->irq_flags = IRQF_SHARED;

    dev->irq = pdev->irq;

    if(card->version == 1){
      priv->reg_base = card->base_addr + (0x80 * (i + 1));
      //printk("XXX %s ... channel%d reg_base = 0x%x\n",__FUNCTION__, i, priv->reg_base);
    }
    else{
      if (ent->subdevice == IXCAN_SUBDEVICE_PISO_CAN800U){
        // Port Addr = Bar 3 + (Port << 8)
        priv->reg_base = card->base_addr + (i << 8);
        //printk("%s ...  CAN800U reg_base : 0x%x\n", __FUNCTION__, priv->reg_base);
      }
      else{
        //Bar 0 (PLX), Bar 2 (port 1),  Bar 3 ~ 5   (port 2 ~ 4)
        priv->reg_base = pci_iomap(pdev, (i+2), pci_resource_len(pdev, (i+2)));
      }
    }

    priv->read_reg  = ixcan_pci_read_reg;
    priv->write_reg = ixcan_pci_write_reg;

    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 (ixcan_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);
    }

    port_num++;
  }

  if(card->version == 0){
    iowrite32(PLX_ICSR_ENA_CLR, card->conf_addr + PLX_ICSR);
  }

  board_num++;
  return 0;

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

  ixcan_pci_del_card(pdev);

  return err;
}

static struct pci_driver ixcan_pci_driver = {
  .name = DRV_NAME,
  .id_table = ixcan_pci_tbl,
  .probe = ixcan_pci_add_card,
  .remove = ixcan_pci_del_card,
};

static int __init ixcan_pci_init(void)
{
  return pci_register_driver(&ixcan_pci_driver);
}

static void __exit ixcan_pci_exit(void)
{
  pci_unregister_driver(&ixcan_pci_driver);
}

module_init(ixcan_pci_init);
module_exit(ixcan_pci_exit);

