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.
Related
I am using the Toradex system-on-module colibri imx6ULL with a custom board.
I am trying to disable the LCD and use it's GPIOs for my sensors. I have followed the answer in this link. I got stuck with one of the suggestion in the answer.
In addition to assigning these pings to an iomuxc pinctrl group, you'll also need to assign the pinctrl group to an enabled node for them to get initialized. You can create a new node specifically for this purpose if you'd like.
What does that line mean? Do I need to create a dummy node with status property set to okay?
Can I change of the name appearing in /dev/ for uart port?
How can I enable JTAG in my custom board?
My device tree source file is below:
/dts-v1/;
#include "imx6ull-colibri-nonwifi.dtsi"
#include "imx6ull-colibri-eval-v3.dtsi"
/ {
model = "Toradex Colibri iMX6ULL 256MB on Colibri Evaluation Board V3";
compatible = "toradex,colibri_imx6ull-eval", "fsl,imx6ull";
&uart1 {
status = "okay";
};
&uart2 {
status = "okay";
};
&uart3 {
status = "okay";
};
&uart4 {
status = "disabled";
};
&uart5 {
status = "disabled";
};
&ecspi1 {
status = "okay";
};
&i2c1 {
status = "okay";
/* M41T0M6 real time clock on carrier board */
rtc: m41t0m6#68 {
status = "disabled";
};
};
&pwm1 {
status = "okay";
};
&pwm2 {
status = "okay";
};
&lcdif {
status = "disabled";
};
&iomux {
imx6ull-colibri {
pinctrl_additionalgpio: additionalgpios {
fsl,pins = <
MX6UL_PAD_UART2_RTS_B__GPIO1_IO23 0x14 // Pin 34 => GPS SafeBoot
MX6UL_PAD_CSI_DATA00__GPIO4_IO21 0x14 // Pin 101 => 1V8 Power Supply Enable
MX6UL_PAD_LCD_ENABLE__GPIO3_IO01 0x14 // Pin 44 => GPS EXINT
MX6UL_PAD_LCD_DATA07__GPIO3_IO12 0x14 // Pin 46 => GPS RST
MX6UL_PAD_LCD_DATA09__GPIO3_IO14 0x14 // Pin 48 => UNUSED
MX6UL_PAD_LCD_DATA06__GPIO3_IO11 0x14 // Pin 80 => SX1301 RST
>;
};
};
};
};
The device tree imx6ull-colibri-nonwifi.dtsi can be found over here.
The device tree imx6ull-colibri-eval-v3.dtsi can be found over here.
EDIT:
I have tried few times on my own after reading some documents online on dts. Find the latest version of my dts below. I have also found out SPI is not working in my board so that I have edited my dts to include spi.
I have added a dummy node to enable gpio.
/dts-v1/;
#include "imx6ull-colibri-nonwifi.dtsi"
#include "imx6ull-colibri-eval-v3.dtsi"
/ {
model = "Toradex Colibri iMX6ULL 256MB on Colibri Evaluation Board V3";
compatible = "toradex,colibri_imx6ull-eval", "fsl,imx6ull";
&uart1 {
status = "okay";
};
&uart2 {
status = "okay";
};
&uart3 {
status = "okay";
};
&uart4 {
status = "disabled";
};
&uart5 {
status = "disabled";
};
&ecspi1 {
status = "okay";
};
&i2c1 {
status = "okay";
/* M41T0M6 real time clock on carrier board */
rtc: m41t0m6#68 {
status = "disabled";
};
};
&pwm1 {
status = "okay";
};
&pwm2 {
status = "okay";
};
&lcdif {
status = "disabled";
};
gpio_additional {
pinctrl-name = "default";
pinctrl-0 = <&pinctrl_additionalgpio>;
status = "okay";
};
&ecspi1 {
status = "okay";
};
&iomux {
imx6ull-colibri {
pinctrl_additionalgpio: additionalgpios {
fsl,pins = <
MX6UL_PAD_UART2_RTS_B__GPIO1_IO23 0x14 // Pin 34 => GPS SafeBoot
MX6UL_PAD_CSI_DATA00__GPIO4_IO21 0x14 // Pin 101 => 1V8 Power Supply Enable
MX6UL_PAD_LCD_ENABLE__GPIO3_IO01 0x14 // Pin 44 => GPS EXINT
MX6UL_PAD_LCD_DATA07__GPIO3_IO12 0x14 // Pin 46 => GPS RST
MX6UL_PAD_LCD_DATA09__GPIO3_IO14 0x14 // Pin 48 => UNUSED
MX6UL_PAD_LCD_DATA06__GPIO3_IO11 0x14 // Pin 80 => SX1301 RST
>;
};
};
};
};
1) It means you need to add the pinctrl-group e.g. the one you have created pinctrl_additionalgpio to a node.
I'll take for instance the lcdif node you have disabled in you device-tree to illustrate a pinctrl-group added to a node. In this example, pinctrl_lcdif_dat and pinctrl_lcdif_ctrl are added to the lcdif node:
&lcdif {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_lcdif_dat
&pinctrl_lcdif_ctrl>;
};
You have to either create a node of your own and add the pinctrl-group or find a suitable node, e.g.:
myadditionalgpio {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_additionalgpio>;
};
2) I have not ever got as far as changing the name of the device. If all you need is to have an alternative name, you can use a udev rule to create a symlink:
/etc/udev/rules.d/rename-uart.rules
KERNEL=="ttymxc0", SYMLINK+="test_serial"
Then you will have a /dev/test_serial device that points to /dev/ttymxc0.
3) You can have a look at the generic JTAG Toradex documentation https://developer.toradex.com/knowledge-base/jtag.
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
I am experimenting with a pwm-driver for the Beaglebone black, based of this one.
As I am using Yocto with the meta-bbb layer, I had to rewrite the .dtsi:
&am33xx_pinmux {
bbb_pwm_P8_13_pins: bbb_pwm_P8_13_pins {
pinctrl-single,pins = <0x024 0x4>; /* P8_13 (ZCZ ball T10) | MODE 4 */
};
};
/ {
bbb-pwm#123 {
compatible = "tfe,bbb_pwm-1.00.a";
pwms = <&ehrpwm2 1 0 1>;
pwm-names = "PWM_P8_13";
pinctrl-names = "default";
pinctrl-0 = <&bbb_pwm_P8_13_pins>;
enabled = <0>;
duty = <0>;
status = "okay";
};
};
However, during the driver-probe function, the call
pwm_test->pwm = devm_pwm_get(&pdev->dev, NULL);
returns ENODEV:
[ 7.538249] pinctrl-single 44e10800.pinmux: found group selector 15 for bbb_pwm_P8_13_pins
[ 7.538278] pinctrl-single 44e10800.pinmux: request pin 9 (44e10824.0) for bbb-pwm#123
[ 7.538291] pinctrl-single 44e10800.pinmux: enabling bbb_pwm_P8_13_pins function15
[ 7.538366] Loading bbb_pwm
[ 7.541304] bbb-pwm bbb-pwm#123: obtain a copy of previously claimed pinctrl
[ 7.541321] bbb-pwm bbb-pwm#123: Unable to request PWM (err = -19)
I found that the error-code is returned by a sub-call of devm_pwm_get:
static int pwm_device_request(struct pwm_device *pwm, const char *label)
{
/* .... */
if (!try_module_get(pwm->chip->ops->owner))
return -ENODEV;
/* ... */
}
However, since I am fairly new to Linux-drivers, I do not understand why this happens. Any clues?
It turned out that the lower-level PWM-driver (EHRPWM) was disabled in the kernel. Enabling it using menuconfig and ensuring that EHRPWM and EPWMSS was enabled in the device tree solved my problem:
Using the meta-bbb layer, I simply accessed menuconfig through bitbake:
bitbake virtual/kernel -c menuconfig
and loaded the defconfig located /meta-bbb/recipes-kernel/linux/linux-stable-4.4/beaglebone/defconfig
I also added the following line to my local.conf
PREFERRED_VERSION_linux-stable = "4.4"
Here is my dtsi:
&am33xx_pinmux {
bbb_pwm_P8_13_pins: bbb_pwm_P8_13_pins {
pinctrl-single,pins = <0x024 0x4>; /* P8_13 (ZCZ ball T10) | MODE 4 */
};
};
&ehrpwm2 {
status = "okay";
};
&epwmss2 {
status = "okay";
};
/ {
bbb-pwm#123 {
compatible = "tfe,bbb_pwm-1.00.a";
pwms = <&ehrpwm2 1 0 1>;
pwm-names = "PWM_P8_13";
pinctrl-names = "default";
pinctrl-0 = <&bbb_pwm_P8_13_pins>;
enabled = <0>;
duty = <0>;
status = "okay";
};
};
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.
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.