Linux kernel driver same device on different buses? - linux

Is there an example of same device using different buses, for example spi and i2c (simultaneously, depending on the choice)?
I am interested in a device that have some common routines, but uses different read/write functions.
For example an adc that has common function for calibration or triggering (never seen this in kernel adc drivers, but why not?), but different functions to read samples from different sources.
How such driver can be realized?
Should i do something like this in my module? :
static struct i2c_driver my_i2c_driver = {
.driver = {
.name = "my-i2c-driver",
},
.probe = my_i2c_driver_probe,
.remove = my_i2c_driver_remove,
};
static struct spi_driver my_spi_driver = {
.driver = {
.name = "my-spi-driver",
},
.probe = my_spi_driver_probe,
.remove = my_spi_driver_remove,
};
etc... for read/write, ops
I'm interested in theory, even if the "no need", "no one does that."

Actually in 3.10.25 kernel there is such an example,
A ST magnetometer LIS3MDL wich has indeed two interface i2c/spi, controlled via gpio input.
With drivers included in kernel official tree, located in drivers/iio/magnetometer.
With 2 separate driver modules located in st_magn_i2c.c and st_magn_spi.c
st_magn_i2c.c:
static struct spi_driver st_magn_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "st-magn-spi",
},
.probe = st_magn_spi_probe,
.remove = st_magn_spi_remove,
.id_table = st_magn_id_table,
};
st_magn_spi.c:
static struct i2c_driver st_magn_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "st-magn-i2c",
},
.probe = st_magn_i2c_probe,
.remove = st_magn_i2c_remove,
.id_table = st_magn_id_table,
};
Such devices some times happens - for example PCA2129T RTC.
So in assume this is an example i was almost looking for, though i wanted something more trickier (like a USB and something else) or some basic lead.

Related

Register snd-soc-dummy in a device tree

I'm trying to register the ALSA dummy codec provided in soc-utils in my device tree source file, to use it with an i2s device driver (sun8i-i2s).
I've tried to set the sound-dai field in my i2s configuration as explained here : https://patchwork.kernel.org/patch/7679391/, but the device driver fails to find the dai name when reading the device tree.
I've found two workarounds, which consists in either writing my own dummy codec and giving it to the device tree :
/ {
stupid-codec {
#sound-dai-cells = <0x00000000>;
compatible = "linux,snd-soc-stupid";
status = "okay";
linux,phandle = <0x0000dead>;
phandle = <0x0000dead>;
};
...
};
...
&i2s0 {
#sound-dai-cells = <0x00000000>;
compatible = "allwinner,sun8i-h3-i2s";
sound-dai = <0x0000dead>;
status = "okay";
};
Or force-link the codec in the device driver and have no sound-dai field in the device :
device tree :
&i2s0 {
// No sound-dai
status = "okay";
};
i2s driver :
static int sun8i_card_create(struct device *dev, struct priv *priv)
{
struct snd_soc_card *card;
...
// Skip the part where it's reading the device tree
#if 0
codec->of_node = sun8i_get_codec(dev);
if (!codec->of_node) {
dev_err(dev, "no port node\n");
return -ENXIO;
}
DBGOUT("%s: codec_name=\"%s\"\n", __func__, codec->of_node->name);
if(snd_soc_of_get_dai_name(dev->of_node, &codec->dai_name) < 0)
{
dev_err(dev, "%s: failed to find dai name, use codec's name as dai name.\n", __func__);
codec->dai_name = codec->of_node->name;
}
DBGOUT("%s: dai_name=\"%s\"\n", __func__, codec->dai_name);
#endif
// Force codec and dai name
codec->name = "snd-soc-dummy";
codec->dai_name = "snd-soc-dummy-dai";
Both kind of work, but it is still a dirty hack, so it would be great if someone had the correct syntax for the dummy in the device tree.
EDIT 2019/10/10
We ended up using a different codec (pcm5102a), which gets implemented separately in the dts :
(in sun8i-h3-nanopi-neo-air.dts)
&i2s0 {
/* sound-dai = <&pcm5102a>; */
status = "okay";
};
(in sun8i-h3-nanopi.dtsi)
pcm5102a: pcm5102a-codec {
#sound-dai-cells = <0>;
compatible = "ti,pcm5102a";
status = "disabled";
};
As for the use of snd-soc-dummy, I couldn't make it work properly, but I noticed that the generic linux driver seems to use a hardcoded string to register it :
https://github.com/torvalds/linux/blob/master/sound/soc/generic/simple-card.c

Linux I2C Kernel Driver

I have a requirement to create a Linux Kernel Driver to interface with a bunch of MCU's over I2C, through an Interface Board. Because they would be many in number, i.e around 1-18, we are utilising a NXP / TI I2C Multiplexer.
I have imported the NXP / TI I2C Multiplexer kernel driver and have incorporated the same in the DTS file. I have been successfully able to list all the I2C Multiplexers as different i2c-x nodes.
The MCU's all are using the same Address 0x08. And I create a kernel driver which creates a hwmon + sysfs interface for it.
However, the driver upon insmod only triggers __init function. And does not bother with probe().
Upon referring to many documentations I was able to trigger probe function only when am inserting
static struct i2c_board_info xxxx_i2c_devices[] = {
{
I2C_BOARD_INFO("xxxx", 0x08),
},
};
And its corresponding
i2c_register_board_info(0, xxxx_i2c_devices, ARRAY_SIZE(xxxx_i2c_devices));
However, this creates only one instance in HWMON and not several as I imagined it to be. I have referred https://www.kernel.org/doc/Documentation/i2c/instantiating-devices
Without the changes in board init, I tried to utilize the Method 3, however kernel doesn't even call the probe() and detect function(), only the __init function of the driver.
Am following the driver -
https://github.com/1119553797/sprd-kernel-common/blob/sprdb2g_gonk4.0/drivers/hwmon/w83l785ts.c
Would post the entire kernel driver if needed, for reference am using the Kernel 3.0.8, a custom board using board level changes to the same mentioned kernel.
Ok, after extensive googling and patching different methods heres a workaround.
First and foremost in the DTS, we can assign the I2C address in the Mux addressing directly. For .e.g.
i2c#0 {
#address-cells = <1>;
#size-cells = <0>;
reg = <0>;
adc0: nau7802#2a {
compatible = "nuvoton,nau7802";
reg = <0x2a>;
nuvoton,vldo = <3000>;
};
};
i2c#1 {
#address-cells = <1>;
#size-cells = <0>;
reg = <1>;
adc1: nau7802#2a {
compatible = "nuvoton,nau7802";
reg = <0x2a>;
nuvoton,vldo = <3000>;
};
};
i2c#2 {
#address-cells = <1>;
#size-cells = <0>;
reg = <2>;
adc2: nau7802#2a {
compatible = "nuvoton,nau7802";
reg = <0x2a>;
nuvoton,vldo = <3000>;
};
};
Then in the driver, in order to identify with the VendorID,ProductID structure as in the above example "nuvoton,nau7802", we have to make the following changes :-
static const struct i2c_device_id nau7802_i2c_id[] = {
{ "nau7802", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, nau7802_i2c_id);
static const struct of_device_id nau7802_dt_ids[] = {
{ .compatible = "nuvoton,nau7802" },
{},
};
MODULE_DEVICE_TABLE(of, nau7802_dt_ids);
static struct i2c_driver nau7802_driver = {
.probe = nau7802_probe,
.remove = nau7802_remove,
.id_table = nau7802_i2c_id,
.driver = {
.name = "nau7802",
.of_match_table = nau7802_dt_ids,
},
};
The part "adc1" etc can be simply skipped. "MODULE_DEVICE_TABLE(of," is meant to be device_tree information, from what I could gather.
Please do let me know if you find a better method which does not require a DTS & board _init changes.

Difference between spi_driver.id_table and spi_driver.driver.of_match_table

I'm currently trying to understand how linux drivers work. As far as I know, A driver's probe/init function is called when the kernel parses the corresponding .compatible string in the device tree. However, in the arizona-spi driver it looks like there are multiple compatible strings referenced in different members:
static const struct spi_device_id arizona_spi_ids[] = {
{ "wm5102", WM5102 },
{ "wm5110", WM5110 },
{ },
};
MODULE_DEVICE_TABLE(spi, arizona_spi_ids);
static struct spi_driver arizona_spi_driver = {
.driver = {
.name = "arizona",
.owner = THIS_MODULE,
.pm = &arizona_pm_ops,
// Contains e.g. "wlf,wm5102"
.of_match_table = of_match_ptr(arizona_of_match),
},
.probe = arizona_spi_probe,
.remove = arizona_spi_remove,
.id_table = arizona_spi_ids, // Contains "wm5102" and "wm5110"
};
This is an excerpt from here.
So what is the difference between arizona_spi_driver.id_table and arizona_spi_driver.driver.of_match_table?
There are several mechanism for driver matching. The id_table is intended to be used for finding a match from stripped device-tree entries (without vendor part), while of_match_table is used to find a match from full device-tree entries (the ones with vendor part).
If you check, arizona_of_match is defined as this:
const struct of_device_id arizona_of_match[] = {
{ .compatible = "wlf,wm5102", .data = (void *)WM5102 },
{ .compatible = "wlf,wm5110", .data = (void *)WM5110 },
{ .compatible = "wlf,wm8280", .data = (void *)WM8280 },
{ .compatible = "wlf,wm8997", .data = (void *)WM8997 },
{},
};
wlf is the vendor part for this case, while arizona_spi_ids doesn't contain the vendor part.
Hence, if you have something like this in your device tree:
compatible = "myvendor,wm5102"
Your device will match against id_table but not against of_match_table since the vendor is different.
The kernel will do matching against of_match_table first before check id_table (see spi_get_device_id in here). The device matching priority is: of_match_table > acpi_driver > id_table.

What's the structure member 'pm' in the driver structure?

What's 'pm' and 'of_match_table' in the following spi driver code
static struct platform_driver omap2_mcspi_driver = {
.driver = {
.name = "omap2_mcspi",
.owner = THIS_MODULE,
.pm = &omap2_mcspi_pm_ops,
.of_match_table = omap_mcspi_of_match,
},
.probe = omap2_mcspi_probe,
.remove = omap2_mcspi_remove,
};
First, take a look on From where platform device gets it name. You can find more details on ACPI based device enumeration.

How to change LED trigger?

I have register in BSP's the LED:
static struct gpio_led ic_leds[] = {
{
.name = "led1:green",
.gpio = USER_LED,
.default_trigger = "heartbeat",
.active_low = 1,
},
};
static struct gpio_led_platform_data ic_led_info = {
.num_leds = ARRAY_SIZE(ic_leds),
.leds = ic_leds,
};
static struct platform_device ic_leds_device = {
.name = "leds-gpio",
.id = -1,
.dev = {
.platform_data = &ic_led_info,
},
};
static void __init ic_add_device_leds(void)
{
platform_device_register(&ic_leds_device);
}
How can I change the trigger in run time? I know that it's possible with sysfs, but maybe exist another way?
echo "thetriggeryouwant" > /sys/class/leds/someled/trigger
where thetriggeryouwant is for example phy0rx and someled is the name of the led you want to change the trigger for.
To get all available triggers for that led, you can cat the file:
cat /sys/class/leds/someled/trigger
gives for example:
[none] timer oneshot mtd nand-disk heartbeat backlight gpio cpu cpu0 cpu1 activity default-on 2188000.ethernet-2:00:link 2188000.ethernet-2:00:100Mbps 2188000.ethernet-2:00:10Mbps
where the item in brackets ([none]) denotes the current trigger.

Resources