Kernel driver, is pinctrl property always needed when using GPIO overlay? - linux-device-driver

I've asked this question on Unix Stackexchange, but it seems it was a wrong place for this kind of problem. Ad rem:
I'm creating a kernel driver for SPI controlled display, which is meant to be working with Raspberry PI. Besides the three SPI lines, the display has 3 additional control lines: BUSY, RST and DC. In order to has a possibility of controlling these lines, my DTS has to include additional fragment for GPIO.
fragment#0 {
target = <&spi0>;
__overlay__ {
#address-cells = <1>;
#size-cells = <0>;
status = "okay";
spidev#0 {
status = "disabled";
};
epd0: epd#0 {
compatible = "waveshare,epd";
reg = <0>;
pinctrl-names = "default";
pinctrl-0 = <&epd_pins>;
spi-max-frequency = <1000000>;
width = <128>;
height = <296>;
dc-gpios = <&gpio 16 0>;
reset-gpios = <&gpio 20 0>;
busy-gpios = <&gpio 21 0>;
status = "okay";
};
};
};
fragment#1 {
target = <&gpio>;
__overlay__ {
epd_pins: epd_pins {
brcm,pins = <16 20 21>; /* DC RST BUSY */
brcm,function = <1 1 0>; /* out out in */
};
};
};
That DTS works perfectly fine and I didn't expect any troubles. But there is one thing I'm not sure about:
pinctrl-names = "default";
pinctrl-0 = <&epd_pins>;
I've seen properties like that in other's DTs with gpio fragments, but not always; sometimes they are, sometimes they're not. If I comment out these two lines, it seems like nothing changes, and my driver still works as it should.
I have two questions:
What is the purpose of those pinctrl lines? I'm aware of pin controller subsystem, but I'm asking strictly in context of my DT.
Why do I need to declare the gpio overlay? I set IN or OUT function directly from my driver code anyway and my gpio numbers are defined in spi overlay (dc-gpios, reset-gpios, busy-gpios).

To answer you question (assuming you understand the function of the pinctrl line in the device tree in general).
When your device is probed by the kernel, if you have those pinctrl lines in your dts then the kernel requests the pinctrl subsystem to configure the pins listed under brcm,pins as their respective functions defined under brcm,function. The pinctrl state named default is requested to be set by the kernel automatically. You may define other states as
pinctrl-names = "default", "sleep";
pinctrl-0 = <&spi1_default>;
pinctrl-1 = <&spi1_sleep>;
and for other states like sleep or idle you will have to explicitly call them when the driver changes state for power management by calling functions pinctrl_pm_select_sleep_state or pinctrl_pm_select_idle_state respectively. You can only call these functions if the respective pin states are defined in device tree else you might have to do the configuring manually calling the pinctrl apis.
Not necessary in your case, as you are said that you are explicitly setting the pin modes and configuration in device driver, then in that case for your particular case you might not need these lines in your device tree.

Related

How to enable I2C2 on beaglebone black for yocto project?

I am trying to build yocto project on beaglebone black. I would like to enable I2C2 port on beagle bone. I am newbie in yocto project.
Any pointer or reference document would be helpful.
here is the original file am335x-boneblack-common.dtsi and I would like to modify that file with following content
i2c2_pins: pinmux_i2c2_pins {
pinctrl-single,pins = <0x178 0x73 0x17c 0x73>;
};
user_leds_pins: pinmux_user_leds {
pinctrl-single,pins = < AM33XX_IOPAD(0x848, PIN_OUTPUT | MUX_MODE7) >; /* P9.14, gpio1[18] */
};
and
leds {
compatible = "gpio-leds";
pinctrl-names = "default";
pinctrl-0 = <&user_leds_pins>;
i2c2-live {
gpios = <&gpio1 18 GPIO_ACTIVE_HIGH>;
default-state = "on";
};
};
&i2c2: i2c#4819c000 {
pinctrl-names = "default";
pinctrl-0 = <&i2c2_pins>;
status = "okay";
clock-frequency = <100000>;
tmp75#4d {
compatible = "national,lm75";
reg = <0x4d>;
};
};
Kindly suggest how to create patch for above code.
Thanks in Advance
Regards,
Nikhil
At first you need to understand how to deal with your dts file.
You need to decide whether you want to work with the official dts file that is provided by your linux kernel and patch it. Or, create your own dts file and use it instead.
To understand how to patch the dts or how to add custom one, I have 2 stackoverflow answers for that, here and here.
After you understand that, now lets talk about adding your I2C node.
First, you need to search if your sensor has an official kernel driver.
If it has a driver, then, it has a compatible name that is defined in it.
You take that name with the sensor address in that I2C bus and:
For example, adding the sensor of address 0x01 to I2C 1 bus:
&i2c1 {
// ...
sensor_name: sensor_name#0x01 {
compatible = "compatible_name";
reg = "0x01";
}
}
Now, you need to activate the flag of the driver in the kernel defconfig
bitbake linux-XXX -c menuconfig
Activate the flag, and:
bitbake linux-XXX -c diffconfig
it will generate .cfg file containing only the difference.
Take that and add it to your linux recipe:
SRC_URI_append = " file://sensor.cfg"

Unable to use PC15 as GPIO input on stm32f030rc

I'm working on a project using an stm32f030rc. I need to use PC15 as a GPIO input but it appears I'm unable to.
I understand the couple PC14/PC15 is shared with the LFE oscillator, but of course I'm not using that function. Moreover, I am able to read the correct pin level on the PC14 GPIO. In the datasheed regarding my model the PC15 pin is marked as a I/O with OSC32_OUT as additional function: can it be used as input at all?
For reference, this is the C code I'm using to test the functionality; I'm using libopencm3 for initialization.
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/gpio.h>
static void clock_setup(void)
{
rcc_clock_setup_in_hsi_out_48mhz();
/* Enable GPIOA, GPIOB, GPIOC clock. */
rcc_periph_clock_enable(RCC_GPIOA);
rcc_periph_clock_enable(RCC_GPIOB);
rcc_periph_clock_enable(RCC_GPIOC);
rcc_periph_clock_enable(RCC_DBGMCU);
/* Enable clocks for GPIO port B and C*/
gpio_mode_setup(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO5);
gpio_mode_setup(GPIOC, GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO15);
gpio_mode_setup(GPIOC, GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO14);
}
int main(void)
{
unsigned long long i = 0;
clock_setup();
/* Blink the LED (PA8) on the board with every transmitted byte. */
while (1)
{
gpio_toggle(GPIOA, GPIO5); /* LED on/off */
for (i = 0; i < 400000; i++) /* Wait a bit. */
__asm__("nop");
// This conditional is never entered
if (gpio_get(GPIOC, GPIO15) == 0) {
__asm__("nop");
__asm__("nop");
__asm__("nop");
}
// This one works
if (gpio_get(GPIOC, GPIO14) == 0) {
__asm__("nop");
__asm__("nop");
__asm__("nop");
}
}
return 0;
}
PC14 & PC15 have the same configuration properties. Of course, there are some limitations regarding using these pins as outputs (including PC13), but it should be okay to use them as inputs as long as you don't activate LSE functionality.
PC14 & PC15 are GPIO inputs after power-up and considering that LSE is disabled by default, you should be able to use them directly even without any configuration.
As you don't have any problems with PC14, I suspect 3 possible causes:
1) A bug in the GPIO code the library provides. Although it's very unlikely, it's easy to test. You can remove the configuration code for PC14 & PC15, as they are GPIO inputs after power-up by default. This eliminates the possibility of having a bug in gpio_mode_setup() function. To avoid using gpio_get() function, you can use the following code:
if (GPIOC->IDR & (1 << 15) == 0)
2) A bug in the clock config code the library provides. Again, this one is very unlikely, but you can test it by removing the rcc_clock_setup_in_hsi_out_48mhz() function. MCU uses HSI running at 8 MHz after power-up.
3) This can be a hardware problem. I suggest checking the voltage on PC15. Test it by physically connecting it to GND. Also measure PC14 for comparison. Among these 3 possible causes I can think of, this is the most probable one.

i2c instead of mdio, marvell phy driver

I am trying to run marvell phy linux driver on my custom board.
The driver uses mdio interface, but my board has i2c.
I replaced phy_read()/phy_write() in marvell.c file by i2c read/write functions. It doesn't work. probe function doesn't called, phy subsystem uses mdio for detecting marvell, and cannot detect it.
How can I use i2c in phy linux sysbsystem?
I decided it with mdio-i2c.c module and wrote my own platform driver.
In my driver probe:
new_bus = mdio_i2c_alloc(&pdev->dev, i2c); /* create bridge */
if (!new_bus){
return -ENOMEM;
}
new_bus->name = "marvell mdio i2c bus";
new_bus->parent = &pdev->dev;
err = of_mdiobus_register(new_bus, pdev->dev.of_node);
dts:
mdio_i2c{
compatible = "marvell,i2c-mdio";
i2c-bus = <&i2c_0>;
ethphy1: ethernet-phy#1f {
reg = <0x1f>;
};
};
It's work.
There is one caveat. A marvell88e1111 has 0x5f i2c address. This address is unacceptable for mdio. I set address 0x1f. The mdio-i2c.c module corrects it
static unsigned int i2c_mii_phy_addr(int phy_id)
{
return phy_id + 0x40;
}

STM32F7 + FatFs = FR_NOT_READY

I am now using CubeMx 4.23.0 and FW package for STM32F7 1.8.0
MCU is STM32F746 on Core746i board.
Everything is generated by CubeMx automatically.
main.c:
SCB_EnableICache();
SCB_EnableDCache();
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_SDMMC1_SD_Init();
MX_USB_DEVICE_Init();
MX_FATFS_Init();
HAL_Delay(3000);
DebugString("start OK");
uint8_t res = 0;
FATFS SDFatFs;
FIL MyFile; /* File object */
char SD_Path[4];
res = f_mount(&SDFatFs, (TCHAR const*)SD_Path, 0);
sprintf(DebugStr, "f_mount = 0x%02X", res);
DebugString(DebugStr);
res = f_open(&MyFile, "test.txt", FA_READ);
sprintf(DebugStr, "f_open = 0x%02X", res);
DebugString(DebugStr);
sdmmc.c:
void MX_SDMMC1_SD_Init(void)
{
hsd1.Instance = SDMMC1;
hsd1.Init.ClockEdge = SDMMC_CLOCK_EDGE_RISING;
hsd1.Init.ClockBypass = SDMMC_CLOCK_BYPASS_DISABLE;
hsd1.Init.ClockPowerSave = SDMMC_CLOCK_POWER_SAVE_DISABLE;
hsd1.Init.BusWide = SDMMC_BUS_WIDE_1B;
hsd1.Init.HardwareFlowControl = SDMMC_HARDWARE_FLOW_CONTROL_DISABLE;
hsd1.Init.ClockDiv = 7;
//HAL_SD_Init(&hsd1);
// ^^^^^ I also tried this here
//HAL_SD_ConfigWideBusOperation(&hsd1, SDMMC_BUS_WIDE_4B)
//^^^^ and this
}
In case of f_mount(&SDFatFs, (TCHAR const*)SD_Path, 0) <- with 1 here (forced mount), output is:
f_mount = 0x03
f_open = 0x01
With 0 (do not mount now) output is:
f_mount = 0x00
f_open = 0x03
0x03 value is FR_NOT_READY, but official info is pretty vague about it
Things I've tried:
Adding HAL_SD_Init(&hsd1) to MX_SDMMC1_SD_Init() since i didnt find where is SD card GPIO init happening.
2 GB noname SD card, 1 GB Transcend card.
Different hsd1.Init.ClockDiv 3 to 255.
Resoldering everything completely.
Switching to 4-bit wide bus using HAL_SD_ConfigWideBusOperation(&hsd1, SDMMC_BUS_WIDE_4B);
Turn on and off pullups.
But card still does not mount. It's formatted in FAT, working on a PC, files i've tried to open are exist, but empty.
How to get it to mount?
Thanks in advance!
there was the problem with exact version of cubemx.
updating stm32cubemx helped.
You can try
`f_mount(0, "path", 0);
` after the f_open call . it may work.
If the function with forced mounting failed with FR_NOT_READY, it means that the filesystem object has been registered successfully but the
volume is currently not ready to work
. The volume mount process will be attempted on subsequent file/directroy function.
If implementation of the disk I/O layer lacks asynchronous media change detection, application program needs to perform f_mount function after each media change to force cleared the filesystem object.
Changing all SDIO pins except SDIO_CK to pull-up according to This Topic works for me
Try commenting MX_USB_DEVICE_Init(), see what happens.

How are am335x GPIOs numbered in device tree?

I am trying to use a driver with a gpio interrupt on BeagleboneBlack. My device tree has the following entry for my custom device:
&i2c1{...
mydevice: mydevice#0c {
compatible = "mydevice,mydeice";
reg = <0x0c>;
mag_irq_gpio = <&gpio1 13 0>; /* INT line */
};
...}
Its driver counterpart has this:
static int parse_dt(struct i2c_client *client)
{
struct device_node *node = client->dev.of_node;
struct mydev_data *data = i2c_get_clientdata(client);
return of_property_read_u32(node, "mag_irq_gpio", &data->gpio);
}
The driver loads and works fine, except the gpio number is completely wrong. The property read function returns success, and reads 8 as the gpio number, even if I put a different number to the device tree.
How am I supposed to pass a gpio number as generic data? The interrupt works if I manually override the gpio number inside my driver.
As per comment by #sawdust
<&gpio1 13 0>
denotes an array of three values. I solved the issue by manually calculating the GPIO number and passing it as a single number:
<14>