Quantcast
Channel: Raspberry Pi Forums
Viewing all articles
Browse latest Browse all 7503

Advanced users • using readl() to read GPIO MMIO always returns 0 values

$
0
0
I have a rather strange issue in a kernel module driver.

uname -r :
6.12.34+rpt-rpi-v6

Everything seems to be done by the books.
request_mem_region and ioremap calls to reserve and map the physical memory gpio address space to virtual memory.
besides all the required devm calls in the probe function.

i used dev_dbg() to print the base pointer and check that it appears in /proc/vmallocinfo

0x382e596a-0x1469090d 8192 ad7606_probe+0x130/0x80c [ad7606_par] phys=0x3f200000 ioremap

which shows that ioremap worked.

i also used a python helper script, gpio.py, to check that i can access GPIO through mmio mapping, and it works well. i can detect level changes by plugging a 3.3V line to a GPIO and the corresponding bit lights up.

However, readl() always returns 0, while scanning the whole gpio mapped peripheral space from base to the last register offset in the datasheet (an beyond) in the kernel module.

request_mem_region() also seems to work effectively, as the gpio.py script refuses to read mmio with a busy error while the module is loaded.

Besides the readl() issue, I have pointer display issues and maybe pointer arithmetic issues.
when using %p format specifier in dev_dbg, the addresses get mangled after doing pointer arithmetic but conforms to the
start address displayed in /proc/vmallocinfo.
when using %px in dev_dbg, the address does not conform to the /proc/vmallocinfo data, but dev_dbg shows correct pointer increments
of 4 bytes after a u32 __iomem pointer increment, that is explicitly cast.
I assume that the issue is probably not related to the readl() problem, but worth mentioning.
Maybe the kernel code responsible for vmallocinfo uses %p for printing pointers ??

It seems that i am not the only one to report issues, on the same GPIO physical memory space, that is 0x3F200000
as discussed in :
viewtopic.php?t=362835
We never got the solution though in that thread.

I am using a Raspberry Pi zero W 1.1 board, and all GPIO devices such as I2C,SPI,UART,EEPROM,1wire have been disabled to reclaim all the required GPIOs.
Note that the same pin was set at logical high while testing the module as the in the gpio.py script

I would like to test readl() on a non GPIO related virtual memory address to further debug, but preferably without creating a oops or a kernel panic, any ideas ?

Anyway, here comes the probe and init functions code with the GPIO dump to debug at the end of the probe function,
as well as the gpio.py script.

Also tested with ioread32() with the same issue.
No other issues in module loading, and the iio device appears in /sys/bus/iio/devices.
Reading data using the exposed iio device channel files (it is an ADC ad7606 board) reports 0 for all channels, which conforms to the 0 value of readl(), after the required bit shifting and masking due to the use of the range of GPIO8 to 23 for parallel data transfer.

below the module code is the python helper code (working well)

Code:

static struct platform_driver ad7606_driver = {.probe = ad7606_probe,.id_table = ad7606_driver_ids,.driver = {.name = "ad7606",.pm = AD7606_PM_OPS,.of_match_table = ad7606_of_match,},};static int __init board_init(void){    printk(KERN_INFO "ad7606_par module init.");//platform_add_devices(board_devices, ARRAY_SIZE(board_devices));printk(KERN_INFO "platform_add_devices() called.");platform_driver_register(&ad7606_driver);printk(KERN_INFO "platform_driver_register() called.");    return 0;}int ad7606_probe(struct platform_device *pdev){_pdev = pdev;dev_dbg(&pdev->dev,"ad7606:entered ad7606_par_probe\n");const struct platform_device_id *id = platform_get_device_id(pdev);if (IS_ERR(id)){dev_dbg(&pdev->dev,"platform_get_device_id() failed !\n");return PTR_ERR(id);}struct resource *res;resource_size_t remap_size;int ret;int irq;irq = platform_get_irq(pdev, 0);if (IS_ERR_VALUE(irq)){dev_dbg(&pdev->dev,"platform_get_irq() failed, err = %i\n",irq);return irq;}dev_dbg(&pdev->dev,"platform_get_irq() ok, irq = %i\n",irq);//addr = devm_platform_get_and_ioremap_resource(pdev, 0, &res);struct resource *res2;res = platform_get_resource(pdev, IORESOURCE_MEM, 0);phy_addr = res->start;dev_dbg(&pdev->dev,"platform_get_resource() ok, start_addr = %lu\n",phy_addr);dev_dbg(&pdev->dev,"will request mem region(), for device name = %s\n",dev_name(&pdev->dev));    res2 = request_mem_region(phy_addr, SZ_4K, dev_name(&pdev->dev));    // check for errorsif (IS_ERR(res2))return PTR_ERR(res2);dev_dbg(&pdev->dev,"request mem region ok()\n");        addr = ioremap(phy_addr, SZ_4K);if (IS_ERR(addr))return PTR_ERR(addr);dev_dbg(&pdev->dev,"ioremap() ok, ptr base is = %px\n",addr);u32 offset = 13;u32 __iomem * offset_addr = (u32 __iomem *) addr + offset;//unsigned long remapped_addr = *((unsigned long *) addr);dev_dbg(&pdev->dev,"ioremap() ok, ptr base for GPIO is = %px\n",addr);dev_dbg(&pdev->dev,"ioremap() ok, ptr base for GPIO get level is = %px\n",offset_addr);//dev_dbg(&pdev->dev,"ioremap() ok, addr is = %lu\n",remapped_addr);/*if(!remapped_addr){dev_dbg(&pdev->dev,"invalid remapped_addr!\n");return -1;}*/remap_size = resource_size(res);dev_dbg(&pdev->dev,"resource_size() ok, remap_size = %i\n",(int) remap_size);dev_dbg(&pdev->dev,"ad7606:entered ad7606_probe()\n");struct ad7606_state *st;struct iio_dev *indio_dev;dev_dbg(&pdev->dev,"ad7606:declared structs() ok\n");indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*st));if (!indio_dev)return -ENOMEM;dev_dbg(&pdev->dev,"ad7606:devm_iio_device_allloc() ok\n");st = iio_priv(indio_dev);dev_set_drvdata(&pdev->dev, indio_dev);st->dev = &pdev->dev;mutex_init(&st->lock);st->bops = &ad7606_par16_bops;st->base_address = offset_addr;/* tied to logic low, analog input range is +/- 5V */st->range[0] = 0;st->oversampling = 1;st->scale_avail = ad7606_scale_avail;st->num_scales = ARRAY_SIZE(ad7606_scale_avail);ret = devm_regulator_get_enable(&pdev->dev, "avcc");if (ret)return dev_err_probe(&pdev->dev, ret,"Failed to enable specified AVcc supply\n");dev_dbg(&pdev->dev,"ad7606:devm_regulator_get_enable() ok\n");dev_dbg(&pdev->dev,"chip_id is hardcoded = ID_AD7606_8 \n");//BUG : the id->driver_data pointer has an issue, seems unable to fetch from the dts, or a struct/pointer nesting issue.// WORKAROUND : hardcode id driver_data to 1 = ID_AD7606_8//st->chip_info = &ad7606_chip_info_tbl[id->driver_data];st->chip_info = &ad7606_chip_info_tbl[ID_AD7606_8];dev_dbg(&pdev->dev,"ad7606:state chip info set() ok\n");if (st->chip_info->oversampling_num) {st->oversampling_avail = st->chip_info->oversampling_avail;dev_dbg(&pdev->dev,"ad7606:state oversampling_avail set() ok\n");st->num_os_ratios = st->chip_info->oversampling_num;dev_dbg(&pdev->dev,"ad7606:state num_os_ratios set() ok\n");}ret = ad7606_request_gpios(st);if (ret)return ret;dev_dbg(&pdev->dev,"ad7606:ad7606_request_gpios() ok\n");if (st->gpio_os) {if (st->gpio_range)indio_dev->info = &ad7606_info_os_and_range;elseindio_dev->info = &ad7606_info_os;} else {if (st->gpio_range)indio_dev->info = &ad7606_info_range;elseindio_dev->info = &ad7606_info_no_os_or_range;}indio_dev->modes = INDIO_DIRECT_MODE;indio_dev->name = id->name;indio_dev->channels = st->chip_info->channels;indio_dev->num_channels = st->chip_info->num_channels;init_completion(&st->completion);ret = ad7606_reset(st);if (ret)dev_warn(st->dev, "failed to RESET: no RESET GPIO specified\n");dev_dbg(&pdev->dev,"ad7606:ad760_reset() ok\n");/* AD7616 requires al least 15ms to reconfigure after a reset */if (st->chip_info->init_delay_ms) {if (msleep_interruptible(st->chip_info->init_delay_ms))return -ERESTARTSYS;}st->write_scale = ad7606_write_scale_hw;st->write_os = ad7606_write_os_hw;if (st->bops->sw_mode_config)st->sw_mode_en = device_property_present(st->dev,"adi,sw-mode");if (st->sw_mode_en) {/* Scale of 0.076293 is only available in sw mode */st->scale_avail = ad7616_sw_scale_avail;st->num_scales = ARRAY_SIZE(ad7616_sw_scale_avail);/* After reset, in software mode, ±10 V is set by default */memset32(st->range, 2, ARRAY_SIZE(st->range));indio_dev->info = &ad7606_info_os_range_and_debug;ret = st->bops->sw_mode_config(indio_dev);if (ret < 0)return ret;}st->trig = devm_iio_trigger_alloc(&pdev->dev, "%s-dev%d",indio_dev->name,iio_device_id(indio_dev));if (!st->trig)return -ENOMEM;dev_dbg(&pdev->dev,"ad7606:devm_iio_trigger_allloc() ok\n");st->trig->ops = &ad7606_trigger_ops;iio_trigger_set_drvdata(st->trig, indio_dev);ret = devm_iio_trigger_register(&pdev->dev, st->trig);if (ret)return ret;indio_dev->trig = iio_trigger_get(st->trig);ret = devm_request_threaded_irq(&pdev->dev, irq,NULL,&ad7606_interrupt,IRQF_TRIGGER_FALLING | IRQF_ONESHOT,id->name, indio_dev);if (ret)return ret;dev_dbg(&pdev->dev,"ad7606:devm_request_threaded_irq() ok\n");ret = devm_iio_triggered_buffer_setup(&pdev->dev, indio_dev,&iio_pollfunc_store_time,&ad7606_trigger_handler,&ad7606_buffer_ops);if (ret)return ret;dev_dbg(&pdev->dev,"ad7606:devm_iio_triggered_buffer_setup() ok\n");//u32 __iomem * addr_test = (u32 __iomem *) offset_addr;for(u16 i = 0;i<256;i++){ u32 testval = readl(addr);dev_dbg(&pdev->dev,"readl() on GPIO memory map test : ptr = %px\n",addr);dev_dbg(&pdev->dev,"readl() on GPIO memory map test : val = %u\n",testval);addr = (u32 __iomem *) addr + 1;}

Code:

import osimport RPi.GPIO as GPIOGPIO.setmode(GPIO.BCM)for channel in range(0,28):    GPIO.setup(channel, GPIO.IN, pull_up_down=GPIO.PUD_UP)for channel in range(0,28):    print(str(channel))    GPIO.setup(channel, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)GPIO_BASE = 0x3F200000  # GPIO base address for Raspberry Pi Zero WGPIO_LEN = 0x1000       # Length of the GPIO memory region# Open /dev/gpiomemwith open("/dev/gpiomem", "r") as f:# Memory-map the GPIO region    gpio_mem = mmap.mmap(f.fileno(), GPIO_LEN, access=mmap.ACCESS_READ)    print(gpio_mem.tell())    gpio_mem.seek(0x34)        print(gpio_mem.tell())    gplev0 = gpio_mem.read(4)    print(gpio_mem.tell())    gpio_mem.seek(0x35)    gplev0b = gpio_mem.read(4)  # Read 4 bytes (32-bit register)    print(gpio_mem.tell())       # Print the GPLEV0 value as a binary    print("GPLEV0 register value (Binary):")    print("{:#034b}".format(int.from_bytes(gplev0,'little')))    #print("GPLEV1 register value (Binary):")    print("{:#034b}".format(int.from_bytes(gplev0b,'little')))    # Close the mmap    gpio_mem.close()

Statistics: Posted by rodv92 — Wed Aug 06, 2025 12:55 pm — Replies 2 — Views 45



Viewing all articles
Browse latest Browse all 7503

Trending Articles