1-Wire Module for A13 working!

Started by hhornbacher, February 17, 2013, 04:47:58 PM

Previous topic - Next topic

hhornbacher

Hey guys,
I want to programm a 1-Wire-Host-Driver for my A13-micro to use my DS18S20 temperature sensors (it's controlling everything from light, humidty, temperature for my rain forest terrarium)

I've looked at the standard w1-gpio driver, but that can't be build with the "ugly sunxi gpio" drivers. Then I'had a look at that driver and the vibrator motor driver, to get out how to controll pins on the board. But at this point I got stuck...
Does anybody have some documentation on the sunxi api, or some advise how to control ios in kernel space correctly with that board?

[Driver will be published here, when ready ;-)]


EDIT:
Now you can get all my module sources from:
https://github.com/hhornbacher/a13-kernel-modules


UPDATE:
now the driver is fixed and working (only tested on micro, modified fex/script.bin file needed!):

root@harry-a13-2:~# ls /sys/bus/w1/devices/
10-00080290847e/ w1 bus master/   
root@harry-a13-2:~# ls /sys/bus/w1/devices/10-00080290847e/
driver/    id         name       subsystem/ uevent     w1_slave   
root@harry-a13-2:~# cat /sys/bus/w1/devices/10-00080290847e/w1_slave
30 00 4b 46 ff ff 0e 10 7c : crc=7c YES
30 00 4b 46 ff ff 0e 10 7c t=23875


hhornbacher

made some success, I' working now with script.bin for config and script_parser_fetch(..), gpio_request(...), etc. from mach/sys_config.h


hhornbacher

#3
OK now I have some knowledge about GPIO API and I've modified the w1-gpio module to use this API, it compiles without errors. But when I try to load the module it says device not found:

Error: could not insert module sunxi-w1.ko: No such device

Here is my code:

sunxi-w1.c:
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>

#include <mach/sys_config.h>

#include "../w1.h"
#include "../w1_int.h"
#include "sunxi-w1.h"


static unsigned gpio_handler;
static script_gpio_set_t info;

static void w1_gpio_write_bit_dir(void *data, u8 bit) {
    int ret;

    ret = gpio_set_one_pin_io_status(gpio_handler, 1, info.gpio_name);
    if (!ret)
        gpio_set_one_pin_io_status(gpio_handler, bit, info.gpio_name);
}

static void w1_gpio_write_bit_val(void *data, u8 bit) {
    gpio_write_one_pin_value(gpio_handler, bit, info.gpio_name);
}

static u8 w1_gpio_read_bit(void *data) {
    return gpio_read_one_pin_value(gpio_handler, info.gpio_name);
}

static int __init w1_gpio_probe(struct platform_device *pdev) {
    int err = 0;
    int i;
    int w1_used = 0;
    struct w1_bus_master *master;
    struct w1_gpio_platform_data *pdata = pdev->dev.platform_data;

    if (!pdata)
        return -ENXIO;

    master = kzalloc(sizeof (struct w1_bus_master), GFP_KERNEL);
    if (!master)
        return -ENOMEM;

    err = script_parser_fetch("w1_para", "w1_used", &w1_used, sizeof (w1_used) / sizeof (int));
    if (!w1_used || err) {
        printk(KERN_INFO "%s w1-bus is not used in config\n", __FUNCTION__);
        err = -1;
        goto exit;
    }

    err = script_parser_fetch("w1_para", "w1_pin", (int *) &info, sizeof (script_gpio_set_t));
    gpio_handler = gpio_request_ex("w1_para", "w1_pin");
    if (!gpio_handler || err) {
        printk(KERN_INFO "%s can not get \"w1_para\" \"w1_pin\" gpio handler, already used by others?", __FUNCTION__);
        goto exit;
    }

    printk(KERN_INFO "w1-bus on w1_pins: port:%d, portnum:%d\n", info.port, info.port_num);

    master->data = pdata;
    master->read_bit = w1_gpio_read_bit;

    if (pdata->is_open_drain) {
        gpio_set_one_pin_io_status(gpio_handler, 1, info.gpio_name);
        master->write_bit = w1_gpio_write_bit_val;
    } else {
        gpio_set_one_pin_io_status(gpio_handler, 0, info.gpio_name);
        master->write_bit = w1_gpio_write_bit_dir;
    }
    gpio_write_one_pin_value(gpio_handler, 1, info.gpio_name);

    err = w1_add_master_device(master);
    if (err)
        goto free_gpio;

    if (pdata->enable_external_pullup)
        pdata->enable_external_pullup(1);

    platform_set_drvdata(pdev, master);

    return 0;

free_gpio:
    gpio_release(gpio_handler, 0);
free_master:
    kfree(master);

    return err;
    return 0;


exit:
    return err;
}

static int __exit w1_gpio_remove(struct platform_device *pdev) {
    struct w1_bus_master *master = platform_get_drvdata(pdev);
    struct w1_gpio_platform_data *pdata = pdev->dev.platform_data;

    if (pdata->enable_external_pullup)
        pdata->enable_external_pullup(0);

    w1_remove_master_device(master);
    gpio_release(gpio_handler, 0);
    kfree(master);


    printk(KERN_INFO "Removed: w1-bus on w1_pin [%d,%d]\n", info.port, info.port_num);
    return 0;
}

static struct platform_driver w1_gpio_driver = {
    .driver =
    {
        .name = DRIVER_NAME,
        .owner = THIS_MODULE,
    },
    .remove = __exit_p(w1_gpio_remove),
};

static int __init w1_gpio_init(void) {
    return platform_driver_probe(&w1_gpio_driver, w1_gpio_probe);
}

static void __exit w1_gpio_exit(void) {
    platform_driver_unregister(&w1_gpio_driver);
}

module_init(w1_gpio_init);
module_exit(w1_gpio_exit);

MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_VERSION);


sunxi-w1.h:
#define DRIVER_AUTHOR   "Harry Hornbacher <h.hornbacher@gmail.com>"
#define DRIVER_DESC     "W1 driver for sunxi devices"
#define DRIVER_NAME     "sunxi-w1"
#define DRV_VERSION     "0.0.1"


struct w1_gpio_platform_data {
unsigned int pin;
unsigned int is_open_drain:1;
void (*enable_external_pullup)(int enable);
};


and this is the modification in the fex file:

[w1_para]
w1_used = 1
w1_pin = port:PB03<1><default><default><default>

vinifr

Do you used modprobe or insmod? If this is a module outside of kernel' tree, you need to use insmod

hhornbacher

#5
I used insmod, with every other module I wrote it works, I think it has something to do with the
platform_driver_probe(&w1_gpio_driver, w1_gpio_probe);

BTW.: I compiled the Kernel with W1 support.

EDIT:
seems like it doesn't even get to the point of executing platform_driver_probe... -.- don't know whats the matter

hhornbacher

I still had no success but as I said before it has something to do with the platform_driver_probe(...) function.
Is there some kernel option I need to enable to use platform_driver?

vinifr

Sorry, but why you're trying to make a platform driver? Generally platform driver are made to controllers i2c, spi, uart, usb, others...

his intention would be to create client driver?  ;)

hhornbacher

I thought I have to use the platform_driver to register a w1-bus-master driver... (what i did was porting the w1-gpio.c [http://code.metager.de/source/xref/linux/stable/drivers/w1/masters/w1-gpio.c] to the a13 gpio-routines, so the w1-gpio driver used platform_driver too. and this module works on other platforms ver well)

But if i don't have to use platform-driver, would else do i have to use for a w1-master driver?

If my first post with the temperature-sensor confused you, there is allready a client module for the DS18S20, but what i need is a driver for w1-protocol on some custom pin ;-)

vinifr

Alright.  ;D

I ask because usually a platform driver have platform_get_resource(), request_mem_region(), ioremap() and sometimes platform_get_irq() and your friend request_irq().

Good luck.

hhornbacher

Ah, thanks for the tip!  :D

I will do some search on the functions you mentioned, maybe I'll make some success.

EDIT:
Now I've managed to load unload my w1-bus-master driver :D
I had to register a platform_device before adding the platform_driver, which I'm doing now with:

static struct platform_device *w1_sun5i_device = NULL;
...
    w1_sun5i_device = platform_device_register_simple(DRIVER_NAME, 0, NULL, 0);
    w1_sun5i_device->dev.platform_data = kzalloc(sizeof (struct w1_sun5i_platform_data), GFP_KERNEL);


The new code is availible at my github repository...