how to use single platform device driver for multiple devices

1.1k views Asked by At

I have 3 devices which are working in the similar way. I have a driver designed for one of the devices. I have added compatibility with

.compatible = "xyz,hmcSPI-0.00.a"
.compatible = "xyz,hmcSPI-1.00.a" and
.compatible = "xyz,hmcSPI-2.00.a" 

It probes only the last device "xyz,hmcSPI-2.00.a" but first and second are seems to be disconnected. Meaning when I send data to the driver in /proc/hmcSPI0 then it reflects on only last device but not on first and second. When I remove third device "xyz,hmcSPI-2.00.a" from the device tree and driver then it detect "xyz,hmcSPI-1.00.a" but not "xyz,hmcSPI-0.00.a". Also, it shows probing physical address of the last device. I want to use this single driver for all three devices which are having different addresses. Following is my platform driver.

#include <linux/kernel.h>
#include <linux/module.h>
#include <asm/uaccess.h>    /* Needed for copy_from_user */
#include <asm/io.h>         /* Needed for IO Read/Write Functions */
#include <linux/proc_fs.h>  /* Needed for Proc File System Functions */
#include <linux/seq_file.h> /* Needed for Sequence File Operations */
#include <linux/platform_device.h>  /* Needed for Platform Driver    Functions */
#include<linux/slab.h>   
/* Define Driver Name */
#define DRIVER_NAME "hmcSPI0"


unsigned long *base_addr;   /* Vitual Base Address */
struct resource *res;       /* Device Resource Structure */
unsigned long remap_size;   /* Device Memory Size */
u16 slave_reg_add=0;
char hmcSPI0_phrase[16];





/* Write operation for /proc/hmcSPI0
 * -----------------------------------
 *  When user cat a string to /proc/hmcSPI0 file, the string will be stored in
 *  const char __user *buf. This function will copy the string from user
 *  space into kernel space, and change it to an unsigned long value.
 *  It will then write the value to the register of hmcSPI0 controller.
 */

 static ssize_t proc_hmcSPI0_write(struct file *file, const char __user * buf,
              size_t count, loff_t * ppos)
  {
  
  u32 hmcSPI0_value;
                                                            

if (count < 14) {
        if (copy_from_user(hmcSPI0_phrase, buf, count))
            return -EFAULT;

    hmcSPI0_phrase[count] = '\0';
    slave_reg_add=hmcSPI0_phrase[2]; //Copy first hex number which is offset to variable
    hmcSPI0_phrase[2]=48;       //Replace it by zero
 
if (slave_reg_add>=48){     //48='0' ASCII
    slave_reg_add=(slave_reg_add-48);   // //Convert the ACSII chr to Decimal Number
    
        if(slave_reg_add<15){
         hmcSPI0_value = simple_strtoul(hmcSPI0_phrase, NULL, 0);
         wmb();
         iowrite32(0x0, (base_addr+2)); //Clear Done slave reg2
         iowrite32(hmcSPI0_value, (base_addr+slave_reg_add));
        if(slave_reg_add == 0){
            if((hmcSPI0_phrase[3]-48)==4)
                iowrite32(0x4, (base_addr));
            else if ((hmcSPI0_phrase[3]-48)==1 || (hmcSPI0_phrase[3]-48)==3){
            while (ioread32((base_addr+2)) == 0);//wait untill transfer of all data
                iowrite32(0x0, (base_addr+2));//Clear Done slave reg2
            iowrite32(0x0, (base_addr));
            }
            else{}
        }           
    
    }else {
            slave_reg_add=hmcSPI0_phrase[3];
        slave_reg_add=(slave_reg_add-48);
    }
 }
     }
 
 return count;
 }
 
 /* Callback function when opening file /proc/hmcSPI0
  * ------------------------------------------------------
  *  Read the register value of hmcSPI0 controller, print the value to
  *  the sequence file struct seq_file *p. In file open operation for /proc/hmcSPI0
  *  this callback function will be called first to fill up the seq_file,
  *  and seq_read function will print whatever in seq_file to the terminal.
  */
 static int proc_hmcSPI0_show(struct seq_file *p, void *v)
 {
     u32 hmcSPI0_value;
     slave_reg_add=3;                                                                        
     hmcSPI0_value = ioread32((base_addr+slave_reg_add));
     seq_printf(p, "Slave Reg=0x%x , Data=0x%x \n", slave_reg_add,hmcSPI0_value);
     return 0;
 }
 
 /* Open function for /proc/hmcSPI0
   * ------------------------------------
   *  When user want to read /proc/hmcSPI0 (i.e. cat /proc/hmcSPI0), the open function 
   *  will be called first. In the open function, a seq_file will be prepared and the modprobe chdir no such file or directory zynq
   *  status of hmcSPI0 will be filled into the seq_file by proc_hmcSPI0_show function.
   */
 static int proc_hmcSPI0_open(struct inode *inode, struct file *file)
  {
      unsigned int size = 16;
      char *buf;
      struct seq_file *m;
      int res;
  
      buf = (char *)kmalloc(size * sizeof(char), GFP_KERNEL);
      if (!buf)
          return -ENOMEM;
  
      res = single_open(file, proc_hmcSPI0_show, NULL);
  
      if (!res) {
          m = file->private_data;
          m->buf = buf;
          m->size = size;
      } else {
          kfree(buf);
      }
  
      return res;
  }
  
  /* File Operations for /proc/hmcSPI0 */
  static const struct file_operations proc_hmcSPI0_operations = {
      .open = proc_hmcSPI0_open,
      .read = seq_read,
      .write = proc_hmcSPI0_write,
      .llseek = seq_lseek,
      .release = single_release
  };
  
  /* Shutdown function for hmcSPI0
   * -----------------------------------
   *  Before hmcSPI0 shutdown, clear the data
  */
 static void hmcSPI0_shutdown(struct platform_device *pdev)
 {
    iowrite32(0, base_addr);
 }
 
 /* Remove function for hmcSPI0
  * ----------------------------------
  *  When hmcSPI0 module is removed, clear all data first,
  *  release virtual address and the memory region requested.
  */
 static int hmcSPI0_remove(struct platform_device *pdev)
 {
     hmcSPI0_shutdown(pdev);
 
     /* Remove /proc/hmcSPI0 entry */
     remove_proc_entry(DRIVER_NAME, NULL);
 
     /* Release mapped virtual address */
     iounmap(base_addr);
 
     /* Release the region */
     release_mem_region(res->start, remap_size);
 
     return 0;
 }
 
 /* Device Probe function for hmcSPI0
  * ------------------------------------
  *  Get the resource structure from the information in device tree.
  *  request the memory regioon needed for the controller, and map it into
  *  kernel virtual memory space. Create an entry under /proc file system
  *  and register file operations for that entry.
  */
 static int hmcSPI0_probe(struct platform_device *pdev)
 {
     struct proc_dir_entry *hmcSPI0_proc_entry;
     int ret = 0;
 
     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
     if (!res) {
         dev_err(&pdev->dev, "No memory resource\n");
         return -ENODEV;
     }
 
     remap_size = res->end - res->start + 1;
     if (!request_mem_region(res->start, remap_size, pdev->name)) {
         dev_err(&pdev->dev, "Cannot request IO\n");
         return -ENXIO;
     }
 
     base_addr = ioremap(res->start, remap_size);
     if (base_addr == NULL) {
         dev_err(&pdev->dev, "Couldn't ioremap memory at 0x%08lx\n",
             (unsigned long)res->start);
         ret = -ENOMEM;
         goto err_release_region;
     }

     hmcSPI0_proc_entry = proc_create(DRIVER_NAME, 0, NULL,
                        &proc_hmcSPI0_operations);
     if (hmcSPI0_proc_entry == NULL) {
         dev_err(&pdev->dev, "Couldn't create proc entry\n");
         ret = -ENOMEM;
         goto err_create_proc_entry;
     }
 
     printk(KERN_INFO DRIVER_NAME " probed at VA 0x%08lx\n",
            (unsigned long) base_addr);
     printk(KERN_INFO DRIVER_NAME " probed at PA 0x%08lx\n",
        (unsigned long)res->start);
 
     return 0;
 
  err_create_proc_entry:
     iounmap(base_addr);
  err_release_region:
     release_mem_region(res->start, remap_size);
 
     return ret;
 }
 
 /* device match table to match with device node in device tree */
 static const struct of_device_id hmcSPI0_of_match[] = {
     {.compatible = "xyz,hmcSPI-0.00.a"},
     {.compatible = "xyz,hmcSPI-1.00.a"},
     {.compatible = "xyz,hmcSPI-2.00.a"},
     {},
 };
 
 MODULE_DEVICE_TABLE(of, hmcSPI0_of_match);
 
 /* platform driver structure for hmcSPI0 driver */
 static struct platform_driver hmcSPI0_driver = {
     .driver = {
            .name = DRIVER_NAME,
            .owner = THIS_MODULE,
            .of_match_table = hmcSPI0_of_match},
     .probe = hmcSPI0_probe,
     .remove = hmcSPI0_remove,
     .shutdown = hmcSPI0_shutdown
};

 /* Register hmcSPI0 platform driver */
 module_platform_driver(hmcSPI0_driver);

 /* Module Informations */
 MODULE_AUTHOR("Ganesh Kalbhor, xyz.");
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION(DRIVER_NAME ": hmcSPI0 driver (Simple Version)");
 MODULE_ALIAS(DRIVER_NAME);

device tree entry is as follows

/ {
    model = "Zynq Zed Development Board";
    compatible = "xlnx,zynq-zed", "xlnx,zynq-7000";

    aliases {
        ethernet0 = &gem0;
        serial0 = &uart1;
        spi0 = &qspi;
    };

    memory {
        device_type = "memory";
        reg = <0x0 0x20000000>;
    };

    chosen {
        bootargs =  "console=ttyPS0,115200 root=/dev/ram rw earlyprintk";
        linux,stdout-path = &uart1;
        stdout-path = &uart1;
    };

    usb_phy0: phy0 {
        compatible = "usb-nop-xceiv";
        #phy-cells = <0>;
    };

    hmcSPI0 {
        compatible = "xyz,hmcSPI-0.00.a";
        reg = <0x43C00000 0x1000>;
    };

    hmcSPI0 {
        compatible = "xyz,hmcSPI-1.00.a";
        reg = <0x43C10000 0x1000>;
    };

    hmcSPI0 {
        compatible = "xyz,hmcSPI-2.00.a";
        reg = <0x43C20000 0x1000>;
    };

};

Please help me to resolve this problem.

0

There are 0 answers