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.