How to set GPIO pin output value in device tree - linux

I am using an iMX6 based board and I'd like to set the GPIO value of an arbirtrary ouput to 1 or 0 at boot using the Device Tree.
Is it possible and how can I do that?
I wonder if I have to rely on the gpio-leds feature or if I can define a new node in the DT.
I found some topics on the internet saying I can do as below but doesn't work.
test {
compatible = "gpio-leds";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_gpio_leds>;
myout {
label = "myoutlabel";
gpios = <&gpio1 8 GPIO_ACTIVE_HIGH>;
default-state = "off";
};
};
pinctrl_gpio_leds: gpioledsgrp {
fsl,pins = <
MX6QDL_PAD_GPIO_8__GPIO1_IO08 0x80000000 // My output
>;
};
Any hint would be appreciated.
TIA

You can enable/disable the internal Pull-Up/Pull-Down resistor (along with other electrical characteristics) that is attached to that pin, thus forcing a logic 1/0 in the pin. You should seek the "pad control register" for that pad (IOMUXC_SW_PAD_CTL_PAD_GPIO_8) in the processor reference manual, and then look which characteristics you wish to enable/disable by setting the right bits to 1 or 0, as needed, in the aforementioned register. However, I'm not sure if this is exactly what you're looking for.
Maybe this can help to understand the operation: http://cache.freescale.com/files/32bit/doc/app_note/AN5078.pdf

Related

#interrupt-cells is 2 but interrupts is a 3-tuple

I was looking at the device tree for the Beagle Bone Black and started with am57xx-beagle-x15.dts. Drilling down into dra7.dtsi I found gpio1:
gpio1: gpio#4ae10000 {
compatible = "ti,omap4-gpio";
reg = <0x4ae10000 0x200>;
interrupts = <GIC_SPI 24 IRQ_TYPE_LEVEL_HIGH>;
ti,hwmods = "gpio1";
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
};
I had read that #interrupt-cells gave the number of u32s or cells that one would expect in an item in the the interrupts list. But when I look at interrupts I see a 3-tuple: <GIC_SPI 24 IRQ_TYPE_LEVEL_HIGH>. Would love to know, why does this contain 3 cells and not 2?
It's a very late answer but add one so that someone could get help.
I can't find the exact dts file from my current linux 5.15 source. But the interrupt-parent of the node should have required 3 cells for the interrupts property. For gic, normally it requires 3 values - {interrupt type, interrupt number, flag}. It's in the device binding document of gic (Documentation/devicetree/bindings/interrupt-controller/arm,gic.yaml in linux 5.15)
"#interrupt-cells":
const: 3
description: |
The 1st cell is the interrupt type; 0 for SPI interrupts, 1 for PPI
interrupts.
The 2nd cell contains the interrupt number for the interrupt type.
SPI interrupts are in the range [0-987]. PPI interrupts are in the
range [0-15].
The 3rd cell is the flags, encoded as follows:
bits[3:0] trigger type and level flags.
1 = low-to-high edge triggered
2 = high-to-low edge triggered (invalid for SPIs)
4 = active high level-sensitive
8 = active low level-sensitive (invalid for SPIs).
bits[15:8] PPI interrupt cpu mask. Each bit corresponds to each of
the 8 possible cpus attached to the GIC. A bit set to '1' indicated
the interrupt is wired to that CPU. Only valid for PPI interrupts.
Also note that the configurability of PPI interrupts is IMPLEMENTATION
DEFINED and as such not guaranteed to be present (most SoC available
in 2014 seem to ignore the setting of this flag and use the hardware
default value).

How to set value of i2c PCA9570 gpio expander as early as possible during Linux boot?

I have written a working driver for the PCA9570. Its four outputs are set (and read back) via Linux's GPIO subsystem. e.g.
root#armbox:/sys/class/gpio# echo 508 > export
root#armbox:/sys/class/gpio# echo 509 > export
root#armbox:/sys/class/gpio# echo 510 > export
root#armbox:/sys/class/gpio# echo 511 > export
The problem is that the chip starts with its outputs high. https://www.nxp.com/docs/en/data-sheet/PCA9570.pdf section 8.1.
root#armbox:/sys/class/gpio# cat gpio508/value
1
root#armbox:/sys/class/gpio# cat gpio509/value
1
root#armbox:/sys/class/gpio# cat gpio510/value
1
root#armbox:/sys/class/gpio# cat gpio511/value
1
I can manually set them low from userspace, after boot. e.g.
root#armbox:/sys/class/gpio# echo 0 > gpio510/value
root#armbox:/sys/class/gpio# cat gpio510/value
0
How can I set the chip's outputs low as early as possible during the boot sequence?
I can hack my own driver to do this, during pca9570_probe(), but that feels very hacky. pca9570_probe() currently reads the values from the chip.
static int pca9570_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
...
ret = pca9570_readb(chip, &chip->reg);
if (ret)
goto out_failed;
return 0;
out_failed:
if (chip->client)
i2c_unregister_device(chip->client);
return -1;
}
Is there a correct way in Linux to specify GPIO values during boot, rather than hacking a driver?
P.S. The dts clause is:
pca9570: pca9570#48 {
compatible = "pca9570";
reg = <0x24>;
gpio-controller;
#gpio-cells = <4>;
};
You can use 'GPIO hogging' mechanism as described in DeviceTree gpio binding documentation. Quoting basic information:
GPIO hogging is a mechanism
providing automatic GPIO request and configuration as part of the
gpio-controller's driver probe function.
For example, we have this definition in our device tree for our gpio expander:
gpio_expander: tca6424a#22 {
compatible = "ti,tca6424";
reg = <0x22>;
[...] /* some other gpio expander configuration */
lcd-rst {
gpio-hog;
gpios = <7 GPIO_ACTIVE_LOW>;
output-low;
};
};
You don't need to extend your driver, it is part of the gpio subsystem.

SPI linux driver

I am trying to learn how to write a basic SPI driver and below is the probe function that I wrote.
What I am trying to do here is setup the spi device for fram(datasheet) and use the spi_sync_transfer()api description to get the manufacturer's id from the chip.
When I execute this code, I can see the data on the SPI bus using logic analyzer but I am unable to read it using the rx buffer. Am I missing something here? Could someone please help me with this?
static int fram_probe(struct spi_device *spi)
{
int err;
unsigned char ch16[] = {0x9F,0x00,0x00,0x00};// 0x9F => 10011111
unsigned char rx16[] = {0x00,0x00,0x00,0x00};
printk("[FRAM DRIVER] fram_probe called \n");
spi->max_speed_hz = 1000000;
spi->bits_per_word = 8;
spi->mode = (3);
err = spi_setup(spi);
if (err < 0) {
printk("[FRAM DRIVER::fram_probe spi_setup failed!\n");
return err;
}
printk("[FRAM DRIVER] spi_setup ok, cs: %d\n", spi->chip_select);
spi_element[0].tx_buf = ch16;
spi_element[1].rx_buf = rx16;
err = spi_sync_transfer(spi, spi_element, ARRAY_SIZE(spi_element)/2);
printk("rx16=%x %x %x %x\n",rx16[0],rx16[1],rx16[2],rx16[3]);
if (err < 0) {
printk("[FRAM DRIVER]::fram_probe spi_sync_transfer failed!\n");
return err;
}
return 0;
}
spi_element is not declared in this example. You should show that and also how all elements of that are array are filled. But just from the code that's there I see a couple mistakes.
You need to set the len parameter of spi_transfer. You've assigned the TX or RX buffer to ch16 or rx16 but not set the length of the buffer in either case.
You should zero out all the fields not used in the spi_transfer.
If you set the length to four, you would not be sending the proper command according to the datasheet. RDID expects a one byte command after which will follow four bytes of output data. You are writing a four byte command in your first transfer and then reading four bytes of data. The tx_buf in the first transfer should just be one byte.
And finally the number of transfers specified as the last argument to spi_sync_transfer() is incorrect. It should be 2 in this case because you have defined two, spi_element[0] and spi_element[1]. You could use ARRAY_SIZE() if spi_element was declared for the purpose of this message and you want to sent all transfers in the array.
Consider this as a way to better fill in the spi_transfers. It will take care of zeroing out fields that are not used, defines the transfers in a easy to see way, and changing the buffer sizes or the number of transfers is automatically accounted for in remaining code.
const char ch16[] = { 0x8f };
char rx16[4];
struct spi_transfer rdid[] = {
{ .tx_buf = ch16, .len = sizeof(ch16) },
{ .rx_buf = rx16, .len = sizeof(rx16) },
};
spi_transfer(spi, rdid, ARRAY_SIZE(rdid));
Since you have a scope, be sure to check that this operation happens under a single chip select pulse. I have found more than one Linux SPI driver to have a bug that pulses chip select when it should not. In some cases switching from TX to RX (like done above) will trigger a CS pulse. In other cases a CS pulse is generated for every word (8 bits here) of data.
Another thing you should change is use dev_info(&spi->dev, "device version %d", id)' and also dev_err() to print messages. This inserts the device name in a standard way instead of your hard-coded non-standard and inconsistent "[FRAME DRIVER]::" text. And sets the level of the message as appropriate.
Also, consider supporting device tree in your driver to read device properties. Then you can do things like change the SPI bus frequency for this device without rebuilding the kernel driver.

How to reserve Linux's memory for a single driver

I am running a secure OS in the ARM Trustzone, along side Linux which is running in the "normal" world. Since some part of the RAM is protected by hardware from access by the normal world (using a TZASC), I want to prevent Linux to try to access it otherwise it will crash. I also need a shared buffer between the two world, allocated by Linux. To this purpose, I want to reserve two memory regions, using this document.
I have two kind of areas:
the first one is a static one and I don't want to ever touch it
the second one is a dynamic one and I only want a single driver to be able to modify its content.
Here is an extract of my device tree:
reserved-memory {
#address-cells = <2>;
#size-cells = <2>;
ranges;
/* global autoconfigured region for contiguous allocations */
linux,cma {
compatible = "shared-dma-pool";
reusable;
reg = <0 0xa0000000 0 0x14000000>;
linux,cma-default;
};
/* First range, static, that I don't want anyone to touch */
reserved_static: mymem#0xc0000000 {
compatible = "mymem,reserved-memory";
reg = <0 0xc0000000 0 0x08000000>;
no-map;
};
/* Second range, limited to a single driver */
reserved_dynamic: shared {
compatible = "mymem,memory-shared";
size = <0 0x08000000>;
alignment = <0 0x200000>;
alloc-ranges = <0 0xc8000000 0 0x38000000>;
};
};
mydev {
compatible = "mydev,mydev";
memory-region = <&reserved_dynamic>;
};
I can access the device tree information from my driver code, and I would like to prevent any access to the static memory range (I think I got that one covered with the no-map attribute), and also that only my driver is able to access the dynamic one.
Is this possible, and how can I achieve it ?

generating EXTI0 interrupt when extenal temperature sensor is connected to STM32F207VC

I am using STM32F207VC controller . I have my external probe temperature sensor connected to one of the internal ADC channel of stm32.
I want to generate an external interrupt when this is connected to controller and i should start measuring from external temperature sensor.
Please could any one provide me code or any help in this
Thanks
I have an STM32F4 processor interfaced with a Solomon Systems SSD1963 GPU. The GPU has a Tearing Signal (TE) that notifies the processor when it is about to do a vertical refresh. I hope you can use this code as an example and adapt it to your solution.
The TE signal is connected to the CPU's GPIO G7 pin. So first I have to configure the GPIO pin.
//GPIO Pin G7
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOG, &GPIO_InitStructure);
Next I have to configure the interrupt and NVIC.
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOG, EXTI_PinSource7);
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_StructInit(&EXTI_InitStructure);
EXTI_InitStructure.EXTI_Line = EXTI_Line7;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
Finally I have to create the interrupt handler. The EXTI9_5_IRQHandler handles external interrupts on lines 5 ~ 7. This method is actually defined in my CPUs startup assembly file as a weak reference. I just need to redefine the method and the linker will do the rest.
extern "C" void EXTI9_5_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line7) != RESET)
{
//Handle the interrupt
EXTI_ClearITPendingBit(EXTI_Line7);
}
}
I'm using Mentor Graphics' Sourcery Codebench Lite as my toolchain.

Resources