Device tree issue for Hardware GPIO Watchdog in Linux - linux-kernel

I have an OrangePi PC Plus board which runs Linux (ubuntu 18.04) with kernel 4.19.57 on Allwinner H3 processor.
We have designed a hardware watchdog using STWD100 ASIC. This IC has a gpio which should be toggled at least once a second, otherwise it resets the board. On the other hand, I have googled on this subject and I realized that Linux kernel has a driver called GPIO watchdog (in drivers/watchdog/gpio_wdt.c file).
Because of the project requirement, watchdog GPIO is connected to pin PA19 of processor should begin toggling as soon as the kernel is decompressed and executed, or board is forced reboot by STWD100. For making the issue more complicated, I should add that we can not make any circuit modification. In order to prevent STWD100 from resetting our board before the kernel is loaded, we have a timer which disables STWD100 for about 5~8 seconds and we can not change this time interval (because it is fixed in the circuit). Therefore, we should run our GPIO watchdog driver in Linux kernel as soon as control is passed to the kernel.
What I have done so far:
Added printk("============================\n"); to gpio_wdt_probe() function of gpio-watchdog driver.
Cross-compiled kernel with CONFIG_GPIO_WATCHDOG=y, CONFIG_GPIO_WATCHDOG_ARCH_INITCALL=y.
Decompiled board device tree using dtc to get device tree source code from dtb file.
Modified my device tree source code as follows (according to this link):
/dts-v1/;
/ {
...
soc {
...
pinctrl#1c20800 {
...
phandle = <0x0a>;
/* Node added by me */
gpio_wdt: gpio_wdt {
pins = "PA19";
function = "gpio_out";
phandle = <0x74>;
};
/* Node added by me */
gpio1: gpio1 {
gpio-controller;
#gpio-cells = <2>;
};
};
...
/* This node is part of original dts file which triggers internal processor watchdog*/
watchdog#1c20ca0 {
compatible = "allwinner,sun6i-a31-wdt";
reg = <0x1c20ca0 0x20>;
interrupts = <0x00 0x19 0x04>;
phandle = <0x57>;
};
...
};
...
/* Node added by me */
watchdog-gpio {
compatible = "linux,wdt-gpio";
gpios = <&gpio1 19 1>; /* PA19 should be toggled */
hw_algo = "toggle";
hw_margin_ms = <200>;
always-running;
phandle = <0x75>;
};
...
__symbols__ {
...
/* Symbol added by me */
gpiowdt = "/watchdog-gpio";
};
};
In the source ... depicts some other nodes which I did not modify.
compiled modified device tree using dtc command.
When kernel runs, I can see ============================ in multiple occasions in kernel logs on UART port. This demonstrates that my builtin GPIO watchdog driver is being probed, but my PA19 pin is not toggling.
In the case above, I do not get any warning from dtc compiler, but if I replace gpio_wdt instead of gpio1 in the watchdog-gpio node, when compiling device tree I get the following warning from dtc compiler:
Warning (gpios_property): /watchdog-gpio: Missing property '#gpio-cells' in node /soc/pinctrl#1c20800/gpio_wdt or bad phandle (referred from gpios[0])
Could anyone help me find the issue?

Eventually, I found the solution.
Definition of neither gpio1 nor gpio_wdt would not be useful. I deleted these definitions and modified gpios property in watchdog-gpio node as follows:
watchdog-gpio {
compatible = "linux,wdt-gpio";
gpios = <0x0a 0 19 1>; /* PA19 should be toggled */
hw_algo = "toggle";
hw_margin_ms = <200>;
always-running;
phandle = <0x75>;
};
where: 0x0a is phandle of pinctrl#1c20800 node, 0 corresponds to PortA (for PortC insert 2, for PortD insert 3 and so on), 19 is pin number and 1 corresponds to GPIO flag GPIO_ACTIVE_LOW (flags, that determine active high or active low, pull-up/pull-down resistor connection and so on, are described here).

Related

dsPIC33 Flash Erase broken

I am having a lot of trouble when it comes to flash erasing on the dsPIC33EP64GP503 and I am hoping someone on here will be able help.
I am wanting to store a data struct in the flash program memory of the device. I am having trouble when it comes to erasing the flash though. I need to erase it and re-write it when the data changes.
I am padding the rest of the page with 0s so it can be safely erased.
I can write to the same memory location of the struct. When doing a flash write onto the start of the struct, the byStructValid turns into 0x11 (I know this is all very bad, because it is writing double word. But I am just trying to get the flash operations working first), however when I do an erase nothing happens. Is someone able to figure out what I am doing wrong?
I initialised the struct with 0xFF's and tried to perform a flash write. This was successful as the CAN message I received showed the data changed from 0xFF to 0x11.
I then tried to do a flash erase, but nothing happened. The device just carried on as normal. I don't have access to debug so it is hard to fully understand what is going on during this time.
I have tried moving the struct location around, so that it is on an 'even' page boundary (as specified in the datasheet) but this hasn't worked either.
I have also tried using an assembly version of the erase function, provided by the datasheet, this also doesn't work. The device just carries on as though there was no command for flash erase.
Below are some snippets of code that I have been using.
Any help would be greatly appreciated, thank you.
Note: I am unable to use the debugger. I use CAN messages to periodically send ‘debug’ messages, which contain data that is read from the flash location. This is so I can see if the write/erases are working.
#define MEMORY_USER_CALIBRATION_LOC 0x006000
typedef struct
{
byte byStructValid;
byte abyStructData[3];
}stFlashStruct_t;
volatile const __prog__ stFlashStruct_t stFlashStruct __attribute__((space(prog), address(MEMORY_USER_CALIBRATION_LOC))) =
{
.byStructValid = 0xFF,
.abyStructData = {50, 10, 20},
};
const byte padding[_FLASH_PAGE*2 - sizeof(stFlashStruct_t)] __attribute__((space(prog), address(MEMORY_USER_CALIBRATION_LOC + sizeof(stFlashStruct_t)))) = {0};
//FLASH Write
void FLASH_WriteDoubleWord(dword address, dword data[2])
{
word INTCON2Save;
word i;
//set WREN and ERASE settings for operation
NVMCON = 0x4001;
TBLPAG = 0xFA;
//set address to erase
NVMADR = address & 0xFFFF;
NVMADRU = (address >> 16) & 0x3F;
for (i = 0; i < 2; i++)
{
__builtin_tblwtl(i*2, data[i] & 0xFFFF);
__builtin_tblwth(i*2, (data[i] >> 16) & 0xFF);
}
//save the interrupt register
INTCON2Save = INTCON2;
// Disable interrupts for NVM unlock
__builtin_disable_interrupts();
__builtin_write_NVM();
// Start write cycle
while(NVMCONbits.WR == 1);
//restore interrupts
INTCON2 = INTCON2Save;
}
//FLASH Erase
void FLASH_ErasePageC(dword dwAddress)
{
word INTCON2Save;
//set WREN and ERASE settings for operation
NVMCON = 0x4003;
//set address to erase
NVMADRU = (dwAddress >> 16) & 0x3F;
NVMADR = dwAddress & 0xFFFF;
//save the interrupt register
INTCON2Save = INTCON2;
__builtin_disable_interrupts();
// Disable interrupts for NVM unlock
__builtin_write_NVM();
// Start write cycle
while(NVMCONbits.WR == 1);
//restore interrupts
INTCON2 = INTCON2Save;
}
byte temp_flash_write(void)
{
dword new_data[2] = {0x1111, 0x1111};
FLASH_WriteDoubleWord(&stCustomerCalibration, new_data);
return 0;
}
Your "dsPIC33 Flash Erase broken" issue is one of not understanding just how badly the Run Time Flash Programming (RTFP) method is described in the Microchip dsPIC33EP64GP503 data sheet and family reference manuals.
This post will not explain how any of this works. It does work but is really hard to comprehend.
What will be hard for you is that a program flash word can only be written one time after an erase. Writing to the same program flash word a second time will corrupt it and the next time it is read an ECC trap error will assert.
Attached is example code that allocates a 1024 instruction word page at address 0x6000. Declares a structure at the start of that page that is 2 instruction words in size. The code then erases that page then writes different data to the first 2 instruction words in that page.
/*
* File: main.c
* Author: Dan1138
*
* Description:
* Example for Run Time Self Programming (RTSP).
* This is very limited, useful as a test bench but not much more.
*
* Created on December 10, 2022, 2:05 PM
*/
/* Define the system oscillator frequency this code must configure */
#define FSYS (7372800ul)
#define FCY (FSYS/2ul)
// DSPIC33EP64GP503 Configuration Bit Settings
// 'C' source line config statements
// FICD
#pragma config ICS = PGD1 // ICD Communication Channel Select bits (Communicate on PGEC1 and PGED1)
#pragma config JTAGEN = OFF // JTAG Enable bit (JTAG is disabled)
// FPOR
#pragma config ALTI2C1 = OFF // Alternate I2C1 pins (I2C1 mapped to SDA1/SCL1 pins)
#pragma config ALTI2C2 = OFF // Alternate I2C2 pins (I2C2 mapped to SDA2/SCL2 pins)
#pragma config WDTWIN = WIN25 // Watchdog Window Select bits (WDT Window is 25% of WDT period)
// FWDT
#pragma config WDTPOST = PS32768 // Watchdog Timer Postscaler bits (1:32,768)
#pragma config WDTPRE = PR128 // Watchdog Timer Prescaler bit (1:128)
#pragma config PLLKEN = ON // PLL Lock Enable bit (Clock switch to PLL source will wait until the PLL lock signal is valid.)
#pragma config WINDIS = OFF // Watchdog Timer Window Enable bit (Watchdog Timer in Non-Window mode)
#pragma config FWDTEN = OFF // Watchdog Timer Enable bit (Watchdog timer enabled/disabled by user software)
// FOSC
#pragma config POSCMD = NONE // Primary Oscillator Mode Select bits (Primary Oscillator disabled)
#pragma config OSCIOFNC = ON // OSC2 Pin Function bit (OSC2 is general purpose digital I/O pin)
#pragma config IOL1WAY = OFF // Peripheral pin select configuration (Allow multiple reconfigurations)
#pragma config FCKSM = CSECMD // Clock Switching Mode bits (Clock switching is enabled,Fail-safe Clock Monitor is disabled)
// FOSCSEL
#pragma config FNOSC = FRC // Oscillator Source Selection (Internal Fast RC (FRC))
#pragma config IESO = ON // Two-speed Oscillator Start-up Enable bit (Start up device with FRC, then switch to user-selected oscillator source)
// FGS
#pragma config GWRP = OFF // General Segment Write-Protect bit (General Segment may be written)
#pragma config GCP = OFF // General Segment Code-Protect bit (General Segment Code protect is Disabled)
// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.
#include <xc.h>
#include <libpic30.h>
#define MEMORY_USER_CALIBRATION_LOC (_FLASH_PAGE * 24)
typedef struct
{
uint8_t byStructValid;
uint8_t abyStructData[3];
} stFlashStruct_t;
volatile const __prog__ __attribute__((space(prog), address(MEMORY_USER_CALIBRATION_LOC))) union
{
uint16_t words[_FLASH_PAGE]; /* reserve the entire erase page. Note only the low 16-bits of the instruction word can be accessed with this method. */
struct {
stFlashStruct_t stFlashStruct; /* calibration structure */
};
} CalSpace =
{
.stFlashStruct.byStructValid = 0xFF,
.stFlashStruct.abyStructData = {50, 10, 20},
};
int main(void)
{
volatile stFlashStruct_t ReadBack;
/*
* application initialization
*/
ReadBack.byStructValid = CalSpace.stFlashStruct.byStructValid;
ReadBack.abyStructData[0] = CalSpace.stFlashStruct.abyStructData[0];
ReadBack.abyStructData[1] = CalSpace.stFlashStruct.abyStructData[1];
ReadBack.abyStructData[2] = CalSpace.stFlashStruct.abyStructData[2];
__builtin_software_breakpoint(); /* breakpoint here to inspect the ReadBack structure with the debugger */
Nop();
Nop();
/* Erase 1024 instruction words starting at address MEMORY_USER_CALIBRATION_LOC */
NVMCON = 0x4003;
NVMADR = __builtin_tbloffset(&CalSpace);
NVMADRU = __builtin_tblpage(&CalSpace);
__builtin_disi(5); // Disable interrupts for NVM unlock
__builtin_write_NVM(); // Start write cycle
while(NVMCONbits.WR == 1);
ReadBack.byStructValid = CalSpace.stFlashStruct.byStructValid;
ReadBack.abyStructData[0] = CalSpace.stFlashStruct.abyStructData[0];
ReadBack.abyStructData[1] = CalSpace.stFlashStruct.abyStructData[1];
ReadBack.abyStructData[2] = CalSpace.stFlashStruct.abyStructData[2];
__builtin_software_breakpoint(); /* breakpoint here to inspect the ReadBack structure with the debugger */
Nop();
Nop();
/* Update data in structure to be written */
ReadBack.byStructValid = 1;
ReadBack.abyStructData[0] = 2;
ReadBack.abyStructData[1] = 3;
ReadBack.abyStructData[2] = 4;
/* Write 2 instruction words starting at address MEMORY_USER_CALIBRATION_LOC */
NVMCON = 0x4001; // Set WREN and word program mode
TBLPAG = 0xFA; // write latch upper address
NVMADR = __builtin_tbloffset(&CalSpace.stFlashStruct);
NVMADRU = __builtin_tblpage(&CalSpace);
__builtin_tblwtl(0,*((uint16_t *)(&ReadBack)+0)); // load low 16-bits of first instruction word
__builtin_tblwth(0,0x00); // make high 8-bits of first instruction word zero
__builtin_tblwtl(2,*((uint16_t *)(&ReadBack)+1)); // load low 16-bits of second instruction word
__builtin_tblwth(2,0x00); // make high 8-bits of second instruction word zero
__builtin_disi(5); // Disable interrupts for NVM unlock sequence
__builtin_write_NVM(); // initiate write
while(NVMCONbits.WR == 1);
ReadBack.byStructValid = CalSpace.stFlashStruct.byStructValid;
ReadBack.abyStructData[0] = CalSpace.stFlashStruct.abyStructData[0];
ReadBack.abyStructData[1] = CalSpace.stFlashStruct.abyStructData[1];
ReadBack.abyStructData[2] = CalSpace.stFlashStruct.abyStructData[2];
__builtin_software_breakpoint(); /* breakpoint here to inspect the ReadBack structure with the debugger */
Nop();
Nop();
/*
* Application process loop
*/
for(;;)
{
Nop();
Nop();
Nop();
__delay_ms(100);
}
}

Linux device drivers: mapping MMIO with `devm_of_iomap()` not working

I am writing a kernel platform device driver for booting group of remoteprocs. I am iterating over the groups of remoteprocs (child device nodes) and iomap them by their indexes inside the group with the help of for_each_child_of_node() macro. driver fails on the first devm_of_iomap() function call attempt. Now, I suspected that the platform device driver framework is not recognizing the resources inside the child device nodes (and I was right!), hence I have printed the device resources number from platform.c platform_get_resource() function and the result was 1 (which is the shared-buffer resource).
The Q: Maintaining the current DT format, how can I retrieve (or make them visible to the platform framework) these inner resources (adsp_dtcm, adsp_conf...etc) inside for loop?
// Device Tree
// ----------------------------
// Not the real values of course
// ----------------------------
dsp-cluster {
#address-cells = <2>;
#size-cells = <2>;
compatible = "xxxxxx,dsp_remoteproc";
dsp_count = <2>;
reg = <0x0 0x10000000 0x0 0x100000>; // the only one that recognized by platform framework
reg-names = "share-buffer";
dsp#0 {
reg = <0x0 0xfff00000 0x0 0x40000>,
<0x0 0xfffc0000 0x0 0x20000>,
<0x0 0xfff00000 0x0 0x20000>,
<0x0 0xfffa0000 0x0 0x20000>,
<0x0 0xfffc0000 0x0 0x4000>,
<0x0 0xfffc8000 0x0 0x8000>,
<0x0 0xfffd0000 0x0 0x8000>;
reg-names = "adsp_dtcm", "adsp_conf", "vdsp_dtcm", "vdsp_conf",
"cdsp_dtcm", "cdsp_itcm", "cdsp_conf";
};
dsp#1 {
reg = <0x0 0x10400000 0x0 0x40000>,
<0x0 0xxxx0000 0x0 0x20000>,
<0x0 0xyyy00000 0x0 0x20000>,
<0x0 0xzzza0000 0x0 0x20000>,
<0x0 0xxxxc0000 0x0 0x4000>,
<0x0 0xyyyc8000 0x0 0x8000>,
<0x0 0xeeed0000 0x0 0x8000>;
reg-names = "adsp_dtcm", "adsp_conf", "vdsp_dtcm", "vdsp_conf",
"cdsp_dtcm", "cdsp_itcm", "cdsp_conf";
};
};
My driver code (only the section that trying to iterate and iomap these all resources inside dsp#0 and dsp#1):
static int dsp_remoteproc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
u32 dsp_count = 1;
int ret;
struct device_node *dev_node = dev->of_node;
struct device_node *child = NULL;
unsigned j = 0;
if (of_property_read_u32(dev->of_node, "dsp_count ", &dsp_count))
dev_warn(dev, "dsp_count property not exist, defaulting to 1\n");
for_each_child_of_node(dev_node, child) {
if (!child || (j++ > dsp_count))
break;
void __iomem *iomap_ret;
// iomap adsp conf regs
iomap_ret = devm_of_iomap(dev, dev_node, 1, NULL);
if (IS_ERR(iomap_ret))
return -ENODEV;
}
Translation of addresses in a node's reg property relies on the presence of a ranges property in the parent node. This is checked by the of_translate_one function in "drivers/of/address.c", an extract of which is shown below:
/*
* Normally, an absence of a "ranges" property means we are
* crossing a non-translatable boundary, and thus the addresses
* below the current cannot be converted to CPU physical ones.
* Unfortunately, while this is very clear in the spec, it's not
* what Apple understood, and they do have things like /uni-n or
* /ht nodes with no "ranges" property and a lot of perfectly
* useable mapped devices below them. Thus we treat the absence of
* "ranges" as equivalent to an empty "ranges" property which means
* a 1:1 translation at that level. It's up to the caller not to try
* to translate addresses that aren't supposed to be translated in
* the first place. --BenH.
*
* As far as we know, this damage only exists on Apple machines, so
* This code is only enabled on powerpc. --gcl
*
* This quirk also applies for 'dma-ranges' which frequently exist in
* child nodes without 'dma-ranges' in the parent nodes. --RobH
*/
ranges = of_get_property(parent, rprop, &rlen);
if (ranges == NULL && !of_empty_ranges_quirk(parent) &&
strcmp(rprop, "dma-ranges")) {
pr_debug("no ranges; cannot translate\n");
return 1;
}
Here, parent is the parent node, rprop is the string "ranges" for a normal range (the same function is also used for translating DMA addresses, where rprop would be the string "dma-ranges"), and the non-zero return value indicates failure. (Don't worry about the !of_empty_ranges_quirk(parent). That is just for some weird special cases.) If the parent node doesn't have the ranges property (for a normal range) then the ranges variable will be NULL and the function will return 1 to indicate failure to translate the address.
You may wonder why the code doesn't search up the tree until it finds a ranges property. The reason is that not all reg properties are used for translating physical addresses. This is explained in Device Tree Usage # Ranges (Address Translation) when discussing the reg property for the rtc#58 node (an I2C device) whose parent is the i2c#1,0 node:
You should also notice that there is no ranges property in the i2c#1,0 node. The reason for this is that unlike the external bus, devices on the i2c bus are not memory mapped on the CPU's address domain. Instead, the CPU indirectly accesses the rtc#58 device via the i2c#1,0 device. The lack of a ranges property means that a device cannot be directly accessed by any device other than it's parent.

Interrupt parameter: device tree configuration?

I am currently writing a device tree node to configure SCISIS752 Dual Channel UART with I2C which is connected to the slave address 0x4d. I am also using a clock of 1.8432MHz. The IRQ pin of SCISIS752 is attached to an IO Expander GPIO which is gpiopin 456 in my case.
I am using yocto to create the linux distro. My linux kernel version 4.18.25-yocto-standard
My dts configuration:
/dts-v1/;
#include "am33xx.dtsi"
#include "am335x-bone-common.dtsi"
#include "am335x-boneblack-common.dtsi"
/ {
model = "TI AM335x BeagleBone Black";
compatible = "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx";
};
&am33xx_pinmux {
pinctrl-0 = <&gpio_pins>;
i2c1_pins_default: i2c1_pins_default {
pinctrl-single,pins = <
AM33XX_IOPAD(0x984, PIN_INPUT_PULLUP | MUX_MODE3) /* (D15) uart1_txd.I2C1_SCL */
AM33XX_IOPAD(0x980, PIN_INPUT_PULLUP | MUX_MODE3) /* (D16) uart1_rxd.I2C1_SDA */
>;};
&i2c1 {
pinctrl-names = "default";
pinctrl-0 = <&i2c1_pins_default>;
status = "okay";
clock-frequency = <400000>;
pcf8574a_38: pcf8574a#38 {
compatible = "nxp,pcf8574a";
reg = <0x38>;
gpio-controller;
#gpio-cells = <2>;
};
sc16is752#4d {
compatible = "nxp,sc16is752";
reg = <0x4d>;
clocks = <&sc16is752_clk>;
interrupt-parent = <&gpio3>;
interrupts = <7 2>;
gpio-controller;
#gpio-cells = <2>;
sc16is752_clk: sc16is752_clk {
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <1843200>;
};};
};
I am confused on setting the values of interrupt-parent and interrupts to make this configuration work.
I cannot see your entire device tree, nor do I know what kernel you are running... so I can't point to where your exact problem is. But I can provide some guidance in troubleshooting...
First, it appears you've copied your node from the kernel documentation in Documentation/devicetree/bindings/serial/nxp,sc16is7xx.txt. That is a point of reference, but it's simply meant to illustrate.
There is nothing magical about the device tree. It is parsed by drivers in the kernel to describe the electrical configuration. Which means, anytime you're not sure how something works, all you need to do is look at the driver to see how it parses it.
I happen to have the 4.19.0 source code on me. I found your NXP driver in drivers/tty/serial/sc16is7xx.c. I confirmed through the compatible list that it supports nxp,sc16is752.
Start at the probe sc16is7xx_i2c_probe() where the driver is entered and you will immediately see that an IRQ value is being passed in through the i2c_client structure and then setup by the call to devm_request_irq() in sc16is7xx_probe(). This means that the interrupt DT properties aren't processed in this driver. They are passed to it.
You then need to read: https://www.kernel.org/doc/Documentation/devicetree/bindings/interrupt-controller/interrupts.txt to understand how interrupt controllers work. Does your &gpio3 meet the requirements? Is it configured as an interrupt controller? Does the it even exist?

Why is this DMA I2C transfer locking up execution?

I'm trying to change a library for STM32F407 to include DMA transfers when using I2C. I'm using it do drive an OLED screen. In its original form it is working w/o problems. In the comments, somebody added DMA, but also ported it to STM32F10 and I'm trying to port it back to F407.
My problem is, after enabling DMA transfer, debugger stops working (at exactly that line) - debugger activity LED stops / turns off and debugger stays at next statement.
After some more testing (blinking a led at certain events to see if they happen) I found out that code actually continues to a certain point (specifically, next time when DMA transfer is needed - in second call to update screen). After that, program doesn't continue (LED doesn't turn ON if set ON after that statement).
The weird thing is, I know the transfer is working because the screen gets a few characters written on it. That only happens if I don't debug step by step because CPU writes new data to screen buffer in the mean time and changes content of it before it is entirely sent to the screen by DMA (I will figure out how to fix that later - probably dual buffer, but it shouldn't interfere with DMA transfer anyway). However if I debug step by step, DMA finishes before CPU writes new content to screen buffer and screen is black (as it should be as buffer is first cleared). For testing, I removed the first call to DMA (after the clearing of buffer) and let the program write the text intended into buffer. It displays without any anomalies, so that means DMA must have finished, but something happened after. I simply can't explain why debugger stops working if DMA finishes the transfer.
I tried blinking a led in transfer finished interrupt handler of DMA but it never blinks, that means it is never fired. I would appreciate any help as I'm at a loss (been debugging for a few days now).
Thank you!
Here is relevant part of code (I have omitted rest of the code because there is a lot of it, but if required I can post). The code works without DMA (with ordinary I2C transfers), it only breaks with DMA.
// TM_STM32F4_I2C.h
typedef struct DMA_Data
{
DMA_Stream_TypeDef* DMAy_Streamx;
uint32_t feif;
uint32_t dmeif;
uint32_t teif;
uint32_t htif;
uint32_t tcif;
} DMA_Data;
//...
// TM_STM32F4_I2C.c
void TM_I2C_Init(I2C_TypeDef* I2Cx, uint32_t clockSpeed) {
I2C_InitTypeDef I2C_InitStruct;
/* Enable clock */
RCC->APB1ENR |= RCC_APB1ENR_I2C3EN;
/* Enable pins */
TM_GPIO_InitAlternate(GPIOA, GPIO_PIN_8, TM_GPIO_OType_OD, TM_GPIO_PuPd_UP, TM_GPIO_Speed_Medium, GPIO_AF_I2C3);
TM_GPIO_InitAlternate(GPIOC, GPIO_PIN_9, TM_GPIO_OType_OD, TM_GPIO_PuPd_UP, TM_GPIO_Speed_Medium, GPIO_AF_I2C3);
/* Check clock, set the lowest clock your devices support on the same I2C bus */
if (clockSpeed < TM_I2C_INT_Clocks[2]) {
TM_I2C_INT_Clocks[2] = clockSpeed;
}
/* Set values */
I2C_InitStruct.I2C_ClockSpeed = TM_I2C_INT_Clocks[2];
I2C_InitStruct.I2C_AcknowledgedAddress = TM_I2C3_ACKNOWLEDGED_ADDRESS;
I2C_InitStruct.I2C_Mode = TM_I2C3_MODE;
I2C_InitStruct.I2C_OwnAddress1 = TM_I2C3_OWN_ADDRESS;
I2C_InitStruct.I2C_Ack = TM_I2C3_ACK;
I2C_InitStruct.I2C_DutyCycle = TM_I2C3_DUTY_CYCLE;
/* Disable I2C first */
I2Cx->CR1 &= ~I2C_CR1_PE;
/* Initialize I2C */
I2C_Init(I2Cx, &I2C_InitStruct);
/* Enable I2C */
I2Cx->CR1 |= I2C_CR1_PE;
}
int16_t TM_I2C_WriteMultiDMA(DMA_Data* dmaData, I2C_TypeDef* I2Cx, uint8_t address, uint8_t reg, uint16_t len)
{
int16_t ok = 0;
// If DMA is already enabled, wait for it to complete first.
// Interrupt will disable this after transmission is complete.
TM_I2C_Timeout = 10000000;
// TODO: Is this I2C check ok?
while (I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY) && !I2C_GetFlagStatus(I2Cx, I2C_FLAG_TXE) && DMA_GetCmdStatus(dmaData->DMAy_Streamx) && TM_I2C_Timeout)
{
if (--TM_I2C_Timeout == 0)
{
return -1;
}
}
//Set amount of bytes to transfer
DMA_Cmd(dmaData->DMAy_Streamx, DISABLE); //should already be disabled at this point
DMA_SetCurrDataCounter(dmaData->DMAy_Streamx, len);
DMA_ClearFlag(dmaData->DMAy_Streamx, dmaData->feif | dmaData->dmeif | dmaData->teif | dmaData->htif | dmaData->tcif); // Clear dma flags
DMA_Cmd(dmaData->DMAy_Streamx, ENABLE); // enable DMA
//Send I2C start
ok = TM_I2C_Start(I2Cx, address, I2C_TRANSMITTER_MODE, I2C_ACK_DISABLE);
//Send register to write to
TM_I2C_WriteData(I2Cx, reg);
//Start DMA transmission, interrupt will handle transmit complete.
I2C_DMACmd(I2Cx, ENABLE);
return ok;
}
//...
// TM_STM32F4_SSD1306.h
#define SSD1306_I2C I2C3
#define SSD1306_I2Cx 3
#define SSD1306_DMA_STREAM DMA1_Stream4
#define SSD1306_DMA_FEIF DMA_FLAG_FEIF4
#define SSD1306_DMA_DMEIF DMA_FLAG_DMEIF4
#define SSD1306_DMA_TEIF DMA_FLAG_TEIF4
#define SSD1306_DMA_HTIF DMA_FLAG_HTIF4
#define SSD1306_DMA_TCIF DMA_FLAG_TCIF4
static DMA_Data ssd1306_dma_data = { SSD1306_DMA_STREAM, SSD1306_DMA_FEIF, SSD1306_DMA_DMEIF, SSD1306_DMA_TEIF, SSD1306_DMA_HTIF, SSD1306_DMA_TCIF };
#define SSD1306_I2C_ADDR 0x78
//...
// TM_STM32F4_SSD1306.c
void TM_SSD1306_initDMA(void)
{
DMA_InitTypeDef DMA_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);
DMA_DeInit(DMA1_Stream4);
DMA_Cmd(DMA1_Stream4, DISABLE);
//Configure DMA controller channel 3, I2C TX channel.
DMA_StructInit(&DMA_InitStructure); // Load defaults
DMA_InitStructure.DMA_Channel = DMA_Channel_3;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&(I2C3->DR)); // I2C3 data register address
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)SSD1306_Buffer; // Display buffer address
DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral; // DMA from mem to periph
DMA_InitStructure.DMA_BufferSize = 1024; // Is set later in transmit function
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // Do not increment peripheral address
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; // Do increment memory address
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; // DMA one shot, no circular.
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; // Tweak if interfering with other dma actions
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DMA_Init(DMA1_Stream4, &DMA_InitStructure);
DMA_ITConfig(DMA1_Stream4, DMA_IT_TC, ENABLE); // Enable transmit complete interrupt
DMA_ClearITPendingBit(DMA1_Stream4, DMA_IT_TC);
// Set interrupt controller for DMA
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Stream4_IRQn; // I2C3 TX connect to stream 4 of DMA1
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x05;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x05;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
// Set interrupt controller for I2C
NVIC_InitStructure.NVIC_IRQChannel = I2C3_EV_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
I2C_ITConfig(I2C3, I2C_IT_BTF, ENABLE);
}
extern void DMA1_Channel3_IRQHandler(void)
{
//I2C3 DMA transmit completed
if (DMA_GetITStatus(DMA1_Stream4, DMA_IT_TC) != RESET)
{
// Stop DMA, clear interrupt
DMA_Cmd(DMA1_Stream4, DISABLE);
DMA_ClearITPendingBit(DMA1_Stream4, DMA_IT_TC);
I2C_DMACmd(SSD1306_I2C, DISABLE);
}
}
// Sending stop condition to I2C in separate handler necessary
// because DMA can finish before I2C finishes
// transmitting and last byte is not sent
extern void I2C3_EV_IRQHandler(void)
{
if (I2C_GetITStatus(I2C3, I2C_IT_BTF) != RESET)
{
TM_I2C_Stop(SSD1306_I2C); // send i2c stop
I2C_ClearITPendingBit(I2C3, I2C_IT_BTF);
}
}
// ...
void TM_SSD1306_UpdateScreen(void) {
TM_I2C_WriteMultiDMA(&ssd1306_dma_data, SSD1306_I2C, SSD1306_I2C_ADDR, 0x40, 1024); // Use DMA
}
edit: i noticed the wrong condition checking at initializing a new transfer, but fixing it doesn't fix the main problem
while ((I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY) || !I2C_GetFlagStatus(I2Cx, I2C_FLAG_TXE) || DMA_GetCmdStatus(dmaData->DMAy_Streamx)) && TM_I2C_Timeout)

freescale imx6 with mpu9250

I am trying to interface freescale imx6 SoC with mpu92/65 sensor device.
I have taken mpu92/65 device driver from android (https://github.com/NoelMacwan/Kernel-10.4.1.B.0.101/tree/master/drivers/staging/iio/imu ) and have done necessary modifications to the driver and device tree.
Device tree modifications:
&i2c3{
...
extaccelerometer: mpu9250#68{
compatible = "mpu9250";
reg = <0x68>;
interrupt-parent = <&gpio2>;
interrupts = <9>;
int_config = /bits/ 8 <0x00>;
level_shifter = /bits/ 8 <0>;
orientation = [ 01 00 00 00 01 00 00 00 01 ];
sec_slave_type = <2>;
sec_slave_id = <0x12>;
secondary_i2c_addr = /bits/ 16 <0x0C>;
secondary_orientation = [ 00 01 00 ff 00 00 00 00 01 ];
};
}
inv_mpu_iio driver modifications:
static void get_platdata(struct device *dev, struct inv_mpu_iio_s *st){
struct device_node *np = dev->of_node;
int i=0;
of_property_read_u8(np, "int_config", &st->plat_data.int_config);
of_property_read_u8(np, "level_shifter", &st->plat_data.level_shifter);
of_property_read_u8_array(np, "orientation", &st->plat_data.orientation,9);
of_property_read_u32(np, "sec_slave_type", &st->plat_data.sec_slave_type);
of_property_read_u32(np, "sec_slave_id", &st->plat_data.sec_slave_id);
of_property_read_u16(np, "secondary_i2c_addr", &st->plat_data.secondary_i2c_addr);
of_property_read_u8_array(np, "secondary_orientation", &st->plat_data.secondary_orientation,9);
}
static int inv_mpu_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
.....
if (client->dev.of_node) {
get_platdata(&client->dev, st);
} else {
st->plat_data = *(struct mpu_platform_data *)dev_get_platdata(&client->dev);
}
.....
}
I have retrieved the platform data from device tree in the above manner. In probe function I am getting client->irq=0. But I have mentioned about the IRQ in the device tree. Please can someone tell me what else I need to do to mention gpio2-9 (linux pad) as an interrupt line for this i2c device.
0x68 is the slave address of the i2c device. Driver probe functionality is trying to write on to the device for verifying the chip type initially. So the data and the address of the slave is sent to the adapter driver where in the adapter driver start function writes onto and reads from control and status registers is successfully executed.
static int i2c_imx_start(struct imx_i2c_struct *i2c_imx)
{
unsigned int temp = 0;
int result;
dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);
i2c_imx_set_clk(i2c_imx);
result = clk_prepare_enable(i2c_imx->clk);
if (result)
return result;
imx_i2c_write_reg(i2c_imx->ifdr, i2c_imx, IMX_I2C_IFDR,__func__);
/* Enable I2C controller */
imx_i2c_write_reg(i2c_imx->hwdata->i2sr_clr_opcode, i2c_imx, IMX_I2C_I2SR,__func__);
imx_i2c_write_reg(i2c_imx->hwdata->i2cr_ien_opcode, i2c_imx, IMX_I2C_I2CR,__func__);
/* Wait controller to be stable */
udelay(50);
/* Start I2C transaction */
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
temp |= I2CR_MSTA;
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR,__func__);
result = i2c_imx_bus_busy(i2c_imx, 1);
if (result)
return result;
i2c_imx->stopped = 0;
temp |= I2CR_IIEN | I2CR_MTX | I2CR_TXAK;
temp &= ~I2CR_DMAEN;
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR,__func__);
return result;
}
Then the adapter driver writes on to the data register
imx_i2c_write_reg(msgs->addr << 1, i2c_imx, IMX_I2C_I2DR,__func__);
After this the adapter interrupt is generated ( bus interrupt got i2c3: 291).
static irqreturn_t i2c_imx_isr(int irq, void *dev_id)
{
struct imx_i2c_struct *i2c_imx = dev_id;
unsigned int temp;
printk("irq:%d\n",irq);
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
if (temp & I2SR_IIF) {
/* save status register */
i2c_imx->i2csr = temp;
temp &= ~I2SR_IIF;
printk("temp=%d\n",temp);
temp |= (i2c_imx->hwdata->i2sr_clr_opcode & I2SR_IIF);
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2SR,__func__);
wake_up(&i2c_imx->queue);
return IRQ_HANDLED;
}
return IRQ_NONE;
}
In ISR after reading status register the value should be 162 (last bit should be 0 to indicate acknowledged) but for my device I am getting this value as 163 (last bit is 1 so it is not acknowledged). Then in acknowledge success function -EIO error is thrown. For all the other device connected to this bus the status register after writing onto the data register is 162.
I don't know why I am getting the above behavior. And one more thing is that even if I don't connect the device the start function is able to write into and read from the status and control registers. I am not sure which status register is being read and writing into. If I assume that this writes and reads the adapter registers, then can I also assume that the adapter h/w automatically reads and writes onto the device connected. If so then how am I getting the same behavior if I don't connect the device?
Please help me out.
In probe function I am getting client->irq=0. But I have mentioned about the IRQ in the device tree. Please can someone tell me what else I need to do to mention gpio2-9 (linux pad) as an interrupt line for this i2c device.
Wrong definition of interrupts property
Your interrupts definition seems incorrect:
interrupts = <9>;
It should be in "two cells" format (see Documentation/devicetree/bindings/interrupt-controller/interrupts.txt for details).
I ran next command:
$ find arch/arm/boot/dts/ -name '*imx6*' -exec grep -Hn interrupt {} \; | grep cell
and I see that most of imx6 SoCs have two-cell format for GPIO interrupts. So your definition of interrupts should look like that:
interrupts = <9 IRQ_TYPE_EDGE_FALLING>;
or if your kernel version still doesn't have named constants for IRQ types:
interrupts = <9 2>;
Refer to the datasheet or driver code for MPU9250 to figure out the type of IRQ (falling/rising).
Missingof_match_table
I'm not 100% sure that what explained next is the cause of your issue, but at least that's worth to be checked.
As I see it, the problem is that OF (device tree) matching is not happening. To fix this, in addition to .id_table you need to define and assign .of_match_table in your driver struct. So for now you have next driver definition in your driver:
static const struct i2c_device_id inv_mpu_id[] = {
...
{"mpu9250", INV_MPU9250},
...
{}
};
static struct i2c_driver inv_mpu_driver = {
...
.id_table = inv_mpu_id,
...
};
And you need to add something like this:
#include <linux/of.h>
#ifdef CONFIG_OF
static const struct of_device_id inv_mpu_of_table[] = {
...
{ .compatible = "invensense,mpu9250" },
...
{ }
};
MODULE_DEVICE_TABLE(of, inv_mpu_of_table);
#endif
static struct i2c_driver inv_mpu_driver = {
.driver = {
.of_match_table = of_match_ptr(inv_mpu_of_table),
...
},
...
};
Be sure that your compatible strings have exactly "vendor,product" format (which is "invensense,mpu9250" in your case).
Now in your device tree you can describe your device using "invensense,mpu9250" as a value for compatible property:
&i2c3 {
...
extaccelerometer: mpu9250#68 {
compatible = "invensense,mpu9250";
...
}
After these steps OF matching should happen correctly and you should see your client->irq assigned appropriately (so it's not 0).
Run next command to list all I2C/IIO drivers that has device tree support, and you'll see that they all have both tables in driver struct:
$ git grep --all-match -e of_match_table -e '\i2c_driver' -e '\.id_table\b' drivers/iio/* | sed 's/:.*//g' | sort -u
Under the hood
Look into drivers/i2c/i2c-core.c, i2c_device_probe() function to see how IRQ number is being read from device tree for I2C device:
static int i2c_device_probe(struct device *dev)
{
...
if (dev->of_node) {
...
irq = of_irq_get(dev->of_node, 0);
}
...
client->irq = irq;
...
status = driver->probe(client, i2c_match_id(driver->id_table, client));
}
This function is being executed when device/driver match happens. Devices information is read from device tree on your I2C adapter probe. So on i2c_add_driver() call for your driver there can be match (by compatible string) with device from device tree, and i2c_device_probe() called, populating client->irq and calling your driver probe function next.
of_irq_get() function obtains IRQ number from device tree interrupts property
Also, there was an attempt to get rid of .id_table and use .of_match_table exclusively for device matching: commit. But then it was reverted further in this commit, due to some side effects. So for now we must define both .id_table AND .of_match_table for I2C driver to work correctly.

Resources