(Note that I'm changing the codes shown here for simplicity/clarity, so it's different from the real code)
e.g. SPI on imx8mp
From imx8mp dt
compatible = "fsl,imx8mp-ecspi", "fsl,imx6ul-ecspi";
};
ecspi2: spi@30830000 { .. };
ecspi3: spi@30840000 { .. };
Suppose we are using spidev from our dt to expose them to userspace
:
spidev1: spi@0 {
reg = <0>;
compatible = "rohm,dh2228fv";
spi-max-frequency = <10000000>; //10mhz
};
};
Similarly for ecspi2 and ecspi3
And suppose initially we don't have any aliases for ecspi in our dt (nor in the imx8mp.dtsi)
So, it's basically like we have the following
spi0 = &ecspi1;
spi1 = &ecspi2;
spi2 = &ecspi3;
};
Then we'll see this
total 0
lrwxrwxrwx 1 root root 0 Apr 4 06:28 spidev0.0 -> ../../devices/platform/soc@0/30800000.bus/30820000.spi/spi_master/spi0/spi0.0/spidev/spidev0.0
lrwxrwxrwx 1 root root 0 Apr 4 06:28 spidev1.0 -> ../../devices/platform/soc@0/30800000.bus/30830000.spi/spi_master/spi1/spi1.0/spidev/spidev1.0
lrwxrwxrwx 1 root root 0 Apr 4 06:28 spidev2.0 -> ../../devices/platform/soc@0/30800000.bus/30840000.spi/spi_master/spi2/spi2.0/spidev/spidev2.0
crw------- 1 root root 153, 0 Apr 4 06:28 /dev/spidev0.0
crw------- 1 root root 153, 1 Apr 4 06:28 /dev/spidev1.0
crw------- 1 root root 153, 2 Apr 4 06:28 /dev/spidev2.0
Now let's change aliases in our dt
spi0 = &ecspi2;
spi1 = &ecspi1;
spi2 = &ecspi3;
};
Then we'll be seeing this
total 0
lrwxrwxrwx 1 root root 0 Apr 4 06:37 spidev0.0 -> ../../devices/platform/soc@0/30800000.bus/30830000.spi/spi_master/spi0/spi0.0/spidev/spidev0.0
lrwxrwxrwx 1 root root 0 Apr 4 06:37 spidev1.0 -> ../../devices/platform/soc@0/30800000.bus/30820000.spi/spi_master/spi1/spi1.0/spidev/spidev1.0
lrwxrwxrwx 1 root root 0 Apr 4 06:37 spidev2.0 -> ../../devices/platform/soc@0/30800000.bus/30840000.spi/spi_master/spi2/spi2.0/spidev/spidev2.0
crw------- 1 root root 153, 1 Apr 4 06:37 /dev/spidev0.0
crw------- 1 root root 153, 0 Apr 4 06:37 /dev/spidev1.0
crw------- 1 root root 153, 2 Apr 4 06:37 /dev/spidev2.0
Let's check what linux does to try to understand how this happens
controller driver
* SPI controller driver for imx8mp ecspi is drivers/spi/spi-imc.c
* SPI controller driver probe will eventually call spi_register_controller from drivers/spi/spi.c
* drivers/spi/spi.c, spi_register_controller will update bus_num of the spi_controller using of_alias_get_id/of_alias_get_highest_id
* int of_alias_get_id(struct device_node *np, const char *stem) : gets the alias id (numeric id) for the given device_node and alias stem
* int of_alias_get_highest_id(const char *stem) : gets the highest alias id (numeric id) for the given alias stem
* So, with these APIs, now you see how stem and numeric id we saw in the previous post work
protocol driver
* We are using "rohm,dh2228fv" protocol compatible SPI slave/subnode devices
* SPI protocol driver drivers/spi/spidev.c then uses the bus-number when creating sysfs and device nodes
* What we see in sysfs and dev is spidevB.C, where
B is the bus-number
C is the chip-select that comes from the DT "reg"
e.g.
spidev1: spi@0 {
reg = <0>;
* class_create creates /sys/class/spidev/
* device_create during probe creates the spidevB.C under /sys/class/spidev/
* And device_create also creates the spidevB.C under /dev/ as it's specifying the dev_t
Code
controller driver
{
struct spi_controller *master = spi_alloc_master(&pdev->dev, sizeof(...));
master->bus_num = pdev->dev.of_node ? -1 : pdev->id;
spi_register_controller(master);
}
spi_controller bus_num update
{
if (ctlr->bus_num >= 0) {
ctlr->bus_num = allocate an id
} else if (ctlr->dev.of_node) {
ctlr->bus_num = allocate an id based on of_alias_get_id(ctlr->dev.of_node, "spi")
}
if (ctlr->bus_num < 0) {
ctlr->bus_num = allocate an id based on of_alias_get_highest_id("spi")
}
}
protocol driver
* /dev/spidevB.C character device nodes exposing our userspace API.
* It also simplifies memory management.
*/
static struct class *spidev_class;
static const struct of_device_id spidev_dt_ids[] = {
{ .compatible = "rohm,dh2228fv" },
:
{},
};
MODULE_DEVICE_TABLE(of, spidev_dt_ids);
struct spidev_data {
dev_t devt;
:
struct spi_device *spi;
:
};
Creating /sys/class/spidev/ with class_create
{
register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops);
class_create(THIS_MODULE, "spidev");
}
module_init(spidev_init);
Creating /sys/class/spidev/spidevB.C and /dev/spidevB.C with device_create
{
struct spidev_data *spidev;
struct device *dev;
spidev = kzalloc(sizeof(*spidev), GFP_KERNEL);
spidev->spi = spi;
spidev->devt = MKDEV(SPIDEV_MAJOR, minor);
dev = device_create(spidev_class, &spi->dev, spidev->devt,
spidev, "spidev%d.%d",
spi->master->bus_num, spi->chip_select);
}
No comments:
New comments are not allowed.