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

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.

Related

Device tree issue for Hardware GPIO Watchdog in Linux

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).

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?

Adding own sysfs device attribute group to existing standard device class attributes

One of Linux kernel training courses task is to add sysfs support to one of previously written device drivers. I choose my ds1307 rtc driver and want to add device attributes, not to replace rtc device ones.
But I am not sure that my solution is correct.
At the time when the probe function is called i2c client device is already created, so that any manipulation on device attributes will cause race condition.
Thus I decide to add my attributes to rtc device before device registration. I just count default rtc attribute groups, allocate new array with one extra position for my attribute group and replace rtc device groups pointer with allocated array.
static int ds1307x_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct device *dev;
const struct attribute_group **grp, **ngrp;
struct rtc_device *rtc;
int ret, cnt;
/* device presence check code skipped */
dev = &client->dev;
rtc = devm_rtc_allocate_device(dev);
if (!rtc)
return -ENOMEM;
dev = &rtc->dev;
grp = dev->groups;
if (!grp) {
/* no default groups, just use own groups array */
dev->groups = ds1307x_groups;
} else {
/* copy rtc groups array, add own group at end */
cnt = 0;
while (grp[cnt])
++cnt;
ngrp = devm_kmalloc(&rtc->dev, (cnt+2) * sizeof(*ngrp),
GFP_KERNEL);
if (!ngrp)
return -ENOMEM;
memcpy(ngrp, grp, cnt * sizeof(*ngrp));
ngrp[cnt] = &ds1307x_group;
ngrp[cnt+1] = NULL;
dev->groups = ngrp;
}
rtc->uie_unsupported = 1;
rtc->ops = &ds1307x_ops;
ret = rtc_register_device(rtc);
/* remaining of code skipped */
}
Attribute group ds1307x_control with attributes clock_halt and out_ctrl appears in rtc0 device directory among standard rtc class attributes:
~ # ls /sys/class/rtc/rtc0/
date ds1307x_control name subsystem
dev hctosys power time
device max_user_freq since_epoch uevent
~ # ls /sys/class/rtc/rtc0/ds1307x_control/
clock_halt out_ctrl
This solution works for me and I hope it is good enough for training courses.
But what about real life? Are there any hidden problems?

How to check whether memory that was reserved in the device tree has actually been reserved

The relevant device tree entry that I am using is:
memory {
device_type = "memory";
reg = <0x0 0x40000000>;
};
reserved-memory {
#address-cells = <1>;
#size-cells = <1>;
ranges;
spw_dma#3E000000 {
reg = <0x3E000000 0x2000000>;
};
};
Can I check somewhere in the /sys/class interface to verify that the memory has actually been reserved? Since I am accessing the memory via the /dev/mem interface, I effectively bypass all security measures, and therefore, it would be useful to, at least be able to verify, that no one else will write to that memory region.
Note: I am aware that The Right Way™ would be to write a kernel driver or use a dynamic memory UIO driver

How to work with reserved CMA memory?

I would like to allocate piece of physically contiguous reserved memory (in predefined physical addresses) for my device with DMA support.
As I see CMA has three options:
1. To reserve memory via kernel config file. 2. To reserve memory via kernel cmdline. 3. To reserve memory via device-tree memory node.
In the first case: size and number of areas could be reserved.
CONFIG_DMA_CMA=y
CONFIG_CMA_AREAS=7
CONFIG_CMA_SIZE_MBYTES=8
So I could use:
start_cma_virt = dma_alloc_coherent(dev->cmadev, (size_t)size_cma, &start_cma_dma, GFP_KERNEL);
in my driver to allocate contiguous memory. I could use it max 7 times and it will be possible allocate up to 8M. But unfortunately
dma_contiguous_reserve(min(arm_dma_limit, arm_lowmem_limit));
from arch/arm/mm/init.c:
void __init arm_memblock_init(struct meminfo *mi,const struct machine_desc *mdesc)
it is impossible to set predefined physical addresses for contiguous allocation.
Of Course I could use kernel cmdline:
mem=cma=cmadevlabel=8M#32M cma_map=mydevname=cmadevlabel
//struct device *dev = cmadev->dev; /*dev->name is mydevname*/
After that dma_alloc_coherent() should alloc memory in physical memory area from 32M + 8M (0x2000000 + 0x800000) up to 0x27FFFFF.
But unfortunately I have problem with this solution. Maybe my cmdline has error?
Next one try was device tree implementation.
cmadev_region: mycma {
/*no-map;*/ /*DMA coherent memory*/
/*reusable;*/
reg = <0x02000000 0x00100000>;
};
And phandle in some node:
memory-region = <&cmadev_region>;
As I saw in kernel usual it should be used like:
of_find_node_by_name(); //find needed node
of_parse_phandle(); //resolve a phandle property to a device_node pointer
of_get_address(); //get DT __be32 physical addresses
of_translate_address(); //DT represent local (bus, device) addresses so translate it to CPU physical addresses
request_mem_region(); //reserve IOMAP memory (cat /proc/iomem)
ioremap(); //alloc entry in page table for reserved memory and return kernel logical addresses.
But I want use DMA via (as I know only one external API function dma_alloc_coherent) dma_alloc_coherent() instead IO-MAP ioremap(). But how call
start_cma_virt = dma_alloc_coherent(dev->cmadev, (size_t)size_cma, &start_cma_dma, GFP_KERNEL);
associate memory from device-tree (reg = <0x02000000 0x00100000>;) to dev->cmadev ? In case with cmdline it is clear it has device name and addresses region.
Does reserved memory after call of_parse_phandle() automatically should be booked for your special driver (which parse DT). And next call dma_alloc_coherent will allocate dma area inside memory from cmadev_region: mycma?
To use dma_alloc_coherent() on reserved memory node, you need to declare that area as dma_coherent. You can do some thing like:
In dt:
cmadev_region: mycma {
compatible = "compatible-name"
no-map;
reg = <0x02000000 0x00100000>;
};
In your driver:
struct device *cma_dev;
static int rmem_dma_device_init(struct reserved_mem *rmem, struct device *dev)
{
int ret;
if (!mem) {
ret = dma_declare_coherent_memory(cma_dev, rmem->base, rmem->base,
rmem->size, DMA_MEMORY_EXCLUSIVE);
if (ret) {
pr_err("Error");
return ret;
}
}
return 0;
}
static void rmem_dma_device_release(struct reserved_mem *rmem,
struct device *dev)
{
if (dev)
dev->dma_mem = NULL;
}
static const struct reserved_mem_ops rmem_dma_ops = {
.device_init = rmem_dma_device_init,
.device_release = rmem_dma_device_release,
};
int __init cma_setup(struct reserved_mem *rmem)
{
rmem->ops = &rmem_dma_ops;
return 0;
}
RESERVEDMEM_OF_DECLARE(some-name, "compatible-name", cma_setup);
Now on this cma_dev you can perform dma_alloc_coherent and get memory.

Resources