This are the sources of the device driver, allowing you to create the
group of GPIOs, and access them via sysfs.
I have tested it in a system, where two AXI GPIOs were implemented in the
Xilinx MPSoC based design.
In the device tree, the GPIO groups were defined as follows:
&amba_pl {
multigpio {
compatible = "wzab,multi-gpio";
dout1-gpios = <&axi_gpio_0 0 0>,
<&axi_gpio_0 1 0>,
<&axi_gpio_0 2 0>,
<&axi_gpio_0 3 0>,
<&axi_gpio_0 4 0>,
<&axi_gpio_0 5 0>,
<&axi_gpio_0 6 0>,
<&axi_gpio_0 7 0>;
dout2-gpios = <&axi_gpio_0 8 0>,
<&axi_gpio_0 9 0>,
<&axi_gpio_0 10 0>,
<&axi_gpio_0 11 0>,
<&axi_gpio_0 12 0>,
<&axi_gpio_0 13 0>,
<&axi_gpio_0 14 0>,
<&axi_gpio_0 15 0>;
din-gpios = <&axi_gpio_1 0 0>,
<&axi_gpio_1 1 0>,
<&axi_gpio_1 2 0>,
<&axi_gpio_1 3 0>,
<&axi_gpio_1 4 0>,
<&axi_gpio_1 5 0>,
<&axi_gpio_1 6 0>,
<&axi_gpio_1 7 0>;
};
};
Then, after loading the "multi-gpio.ko" module, it was possible to
access GPIOs via shell commands like below:
echo 37 > /sys/class/class_multi_gpio/my_dev0/dout1
cat /sys/class/class_multi_gpio/my_dev0/din
121
echo 17 > /sys/class/class_multi_gpio/my_dev0/dout2
Please note, that this is a quick&dirty, proof of the concept implementation.
However, I hope that somebody may find it useful.
I do not provide any warranty. If you decide to use it, you do it on your own
risk.
Regards,
Wojtek
#!/bin/sh
# This is a shell archive (produced by GNU sharutils 4.15.2).
# To extract the files from this archive, save it to some FILE, remove
# everything before the '#!/bin/sh' line above, then type 'sh FILE'.
#
lock_dir=_sh28743
# Made on 2018-01-25 17:43 CET by <wzab@wzdell>.
# Source directory was '/tmp'.
#
# Existing files will *not* be overwritten, unless '-c' is specified.
#
# This shar contains:
# length mode name
# ------ ---------- ------------------------------------------
# 8127 -rw-rw-r-- multi-gpio.c
#
MD5SUM=${MD5SUM-md5sum}
f=`${MD5SUM} --version | egrep '^md5sum .*(core|text)utils'`
test -n "${f}" && md5check=true || md5check=false
${md5check} || \
echo 'Note: not verifying md5sums. Consider installing GNU coreutils.'
if test "X$1" = "X-c"
then keep_file=''
else keep_file=true
fi
echo=echo
save_IFS="${IFS}"
IFS="${IFS}:"
gettext_dir=
locale_dir=
set_echo=false
for dir in $PATH
do
if test -f $dir/gettext \
&& ($dir/gettext --version >/dev/null 2>&1)
then
case `$dir/gettext --version 2>&1 | sed 1q` in
*GNU*) gettext_dir=$dir
set_echo=true
break ;;
esac
fi
done
if ${set_echo}
then
set_echo=false
for dir in $PATH
do
if test -f $dir/shar \
&& ($dir/shar --print-text-domain-dir >/dev/null 2>&1)
then
locale_dir=`$dir/shar --print-text-domain-dir`
set_echo=true
break
fi
done
if ${set_echo}
then
TEXTDOMAINDIR=$locale_dir
export TEXTDOMAINDIR
TEXTDOMAIN=sharutils
export TEXTDOMAIN
echo="$gettext_dir/gettext -s"
fi
fi
IFS="$save_IFS"
if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null
then if (echo -n test; echo 1,2,3) | grep n >/dev/null
then shar_n= shar_c='
'
else shar_n=-n shar_c= ; fi
else shar_n= shar_c='\c' ; fi
f=shar-touch.$$
st1=200112312359.59
st2=123123592001.59
st2tr=123123592001.5 # old SysV 14-char limit
st3=1231235901
if touch -am -t ${st1} ${f} >/dev/null 2>&1 && \
test ! -f ${st1} && test -f ${f}; then
shar_touch='touch -am -t $1$2$3$4$5$6.$7 "$8"'
elif touch -am ${st2} ${f} >/dev/null 2>&1 && \
test ! -f ${st2} && test ! -f ${st2tr} && test -f ${f}; then
shar_touch='touch -am $3$4$5$6$1$2.$7 "$8"'
elif touch -am ${st3} ${f} >/dev/null 2>&1 && \
test ! -f ${st3} && test -f ${f}; then
shar_touch='touch -am $3$4$5$6$2 "$8"'
else
shar_touch=:
echo
${echo} 'WARNING: not restoring timestamps. Consider getting and
installing GNU '\''touch'\'', distributed in GNU coreutils...'
echo
fi
rm -f ${st1} ${st2} ${st2tr} ${st3} ${f}
#
if test ! -d ${lock_dir} ; then :
else ${echo} "lock directory ${lock_dir} exists"
exit 1
fi
if mkdir ${lock_dir}
then ${echo} "x - created lock directory ${lock_dir}."
else ${echo} "x - failed to create lock directory ${lock_dir}."
exit 1
fi
# ============= multi-gpio.c ==============
if test -n "${keep_file}" && test -f 'multi-gpio.c'
then
${echo} "x - SKIPPING multi-gpio.c (file already exists)"
else
${echo} "x - extracting multi-gpio.c (text)"
sed 's/^X//' << 'SHAR_EOF' > 'multi-gpio.c' &&
/* Driver for grups of pins in AXI GPIO blocks
X *
X * Copyright (C) 2018 by Wojciech M. Zabolotny
X * wzab<at>
ise.pw.edu.pl
X * Significantly based on multiple drivers included in
X * sources of Linux
X * Therefore this source is licensed under GPL v2
X */
X
#include <linux/kernel.h>
#include <linux/module.h>
#include <asm/uaccess.h>
MODULE_LICENSE("GPL v2");
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/mm.h>
#include <linux/dma-mapping.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/gpio/consumer.h>
#include <asm/uaccess.h>
X
#define SUCCESS 0
#define DEVICE_NAME "multi-gpio"
#define CLASS_NAME "class_multi_gpio"
X
static struct t_dev_state {
X int n_of_dev;
} dev_states;
X
//It is a dirty trick, but we can service only one device :-(
static struct platform_device * my_pdev = NULL;
static dev_t my_dev=0;
static struct cdev *my_cdev = NULL;
static struct class *my_class = NULL;
X
struct gpio_descs *g_dout1 = NULL , *g_dout2 = NULL, *g_din = NULL;
X
//Store result buffer to gpio
static ssize_t gpio_store(struct device *dev, struct gpio_descs *gds, const char *buf, size_t size)
{
X long val;
X int res;
X int i;
X long mask = 1;
X res=kstrtol(buf, 0, &val);
X if(res<0) return res;
X for(i=0;i<gds->ndescs;i++) {
X gpiod_set_value(gds->desc[i],(val & mask) ? 1 : 0);
X mask <<= 1;
X }
X return 0;
}
X
static ssize_t gpio_show(struct gpio_descs *gds, char *buf)
{
X long val=0;
X int res;
X int i;
X long mask = 1;
X for(i=0;i<gds->ndescs;i++) {
X res=gpiod_get_value(gds->desc[i]);
X if(res<0) return res;
X if(res) val |= mask;
X mask <<= 1;
X }
X res=snprintf(buf,PAGE_SIZE,"%ld",val);
X return res;
}
X
X
static ssize_t dout1_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
{
X int res;
X struct t_dev_state * ds = dev_get_drvdata(dev);
X printk(KERN_INFO "dout1 in device %d set to %s\n", ds->n_of_dev,buf);
X res = gpio_store(dev,g_dout1,buf,size);
X if(res<0) return res;
X return size;
}
X
static ssize_t dout2_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
{
X int res;
X struct t_dev_state * ds = dev_get_drvdata(dev);
X printk(KERN_INFO "dout2 in device %d set to %s\n", ds->n_of_dev,buf);
X res = gpio_store(dev,g_dout2,buf,size);
X if(res<0) return res;
X return size;
}
X
X
static ssize_t din_show(struct device *dev, struct device_attribute *attr, char *buf)
{
X int res;
X struct t_dev_state * ds = dev_get_drvdata(dev);
X res = gpio_show(g_din,buf);
X if(res<0) return res;
X printk(KERN_INFO "atr1 in device %d is equal to %s\n", ds->n_of_dev,buf);
X return res;
}
X
DEVICE_ATTR_WO(dout1);
DEVICE_ATTR_WO(dout2);
DEVICE_ATTR_RO(din);
X
/* List of attributes of our device */
static struct attribute *mydev_attributes[] ={
X & dev_attr_dout1.attr,
X & dev_attr_dout2.attr,
X & dev_attr_din.attr,
X NULL,
};
X
/* Group of attributes of our device */
static struct attribute_group mydev_attrgrp ={
X .attrs = mydev_attributes,
};
X
/* List of groups of attributes of our device */
static const struct attribute_group *mydev_attrgrps[] ={
X &mydev_attrgrp,
X NULL,
};
X
static void my_dev_cleanup(void)
{
X int i;
X printk(KERN_ALERT "MULTI GPIO dev says good bye\n");
X /* deregister the class */
X if(my_dev && my_class) {
X device_destroy(my_class,MKDEV(MAJOR(my_dev),MINOR(my_dev)));
X }
X if(my_cdev) cdev_del(my_cdev);
X my_cdev=NULL;
X /* release device number */
X unregister_chrdev_region(my_dev, 1);
X /* destroy the class */
X if(my_class) {
X class_destroy(my_class);
X my_class=NULL;
X }
X
}
X
struct file_operations fops = {
X .owner = THIS_MODULE,
};
X
X
static int my_dev_create(void)
{
X int res,i;
X printk(KERN_ALERT "Welcome to MULTI GPIO dev\n");
X /* Create the class for our device */
X my_class = class_create(THIS_MODULE, CLASS_NAME);
X if (IS_ERR(my_class)) {
X printk(KERN_ERR "Error creating my_class class.\n");
X res=PTR_ERR(my_class);
X goto err1;
X } /*Get the device number */
X res=alloc_chrdev_region(&my_dev, 0, 1, DEVICE_NAME);
X if(res) {
X printk ("<1>Alocation of the device number for %s failed\n",
X DEVICE_NAME);
X goto err1;
X };
X my_cdev = cdev_alloc();
X if (my_cdev==NULL) {
X printk (KERN_ERR "Allocation of cdev for %s failed\n", DEVICE_NAME);
X res = -ENODEV;
X goto err1;
X }
X my_cdev->ops = &fops;
X my_cdev->owner = THIS_MODULE;
X /* Add device to the system */
X res=cdev_add(my_cdev, my_dev, 1);
X if(res) {
X printk (KERN_ERR "Registration of the device number for %s failed\n",
X DEVICE_NAME);
X goto err1;
X };
X /* Create our device */
X device_create_with_groups(my_class,NULL,MKDEV(MAJOR(my_dev),MINOR(my_dev)+i),&dev_states, mydev_attrgrps, "my_dev%d",MINOR(my_dev));
X printk (KERN_ALERT "Registeration is a success. The major device number %s is %d.\n",
X DEVICE_NAME,
X MAJOR(my_dev));
X return SUCCESS;
err1:
X my_dev_cleanup();
X return res;
}
X
X
void cleanup_tst1( void );
int init_tst1( void );
X
int tst1_remove(struct platform_device *pdev )
{
X if(my_pdev == pdev) {
X if(g_dout1) {
X devm_gpiod_put_array(&pdev->dev, g_dout1);
X }
X if(g_dout2) {
X devm_gpiod_put_array(&pdev->dev, g_dout2);
X }
X if(g_din) {
X devm_gpiod_put_array(&pdev->dev, g_din);
X }
X my_dev_cleanup();
X return 0;
X } else {
X return -ENODEV;
X }
}
X
static int tst1_probe(struct platform_device *pdev)
{
X int res = 0;
X printk(KERN_INFO "MULTI GPIO probe called: %p\n", my_pdev);
X if (my_pdev) {
X //We can't handle more than one device
X printk(KERN_INFO "The driver handles already one device: %p\n", my_pdev);
X return -EINVAL;
X }
X //Now we connect the GPIOs
X //Here we test the GPIO access
X g_dout1 = devm_gpiod_get_array(&pdev->dev, "dout1", GPIOD_OUT_HIGH);
X if(IS_ERR(g_dout1)) {
X res = (int) g_dout1;
X printk (KERN_ERR "I can't connect to the DOUT1 GPIO: error %d\n",res);
X g_dout1 = NULL;
X goto err1;
X };
X g_dout2 = devm_gpiod_get_array(&pdev->dev, "dout2", GPIOD_OUT_HIGH);
X if(IS_ERR(g_dout2)) {
X res = (int) g_dout2;
X printk (KERN_ERR "I can't connect to the DOUT2 GPIO: error %d\n",res);
X g_dout2 = NULL;
X goto err1;
X };
X g_din = devm_gpiod_get_array(&pdev->dev, "din", GPIOD_IN);
X if(IS_ERR(g_din)) {
X res = (int) g_din;
X printk (KERN_ERR "I can't connect to the DIN GPIO: error %d\n",res);
X g_din = NULL;
X goto err1;
X };
X res = my_dev_create();
X if(res<0) return res;
X res = SUCCESS;
X my_pdev = pdev;
X printk(KERN_INFO "KSGPIO probe successfull g_dout1=%d, g_dout2=%d, g_din=%d\n", g_dout1->ndescs, g_dout2->ndescs, g_din->ndescs);
X return res;
X err1:
X tst1_remove(pdev);
X return res;
}
X
//We connect to the platform device
static struct of_device_id multi_gpio_driver_ids[] = {
X {
X .compatible = "wzab,multi-gpio",
X },
X {},
};
X
static struct platform_driver my_driver = {
X .driver = {
X .name = DEVICE_NAME,
X .of_match_table = multi_gpio_driver_ids,
X },
X .probe = tst1_probe,
X .remove = tst1_remove,
};
X
static int tst1_init_module(void)
{
X /* when a module, this is printed whether or not devices are found in probe */
#ifdef MODULE
X // printk(version);
#endif
X printk(KERN_ALERT "Welcome to MULTI GPIO\n");
X return platform_driver_register(&my_driver);
}
X
X
static void tst1_cleanup_module(void)
{
X printk(KERN_ALERT "MULTI GPIO says good-bye\n");
X platform_driver_unregister(&my_driver);
}
X
//Exported function to check status of the GPIOS
int multi_gpio_check_status(void)
{
X if(my_pdev && g_dout1 && g_dout2 && g_din) return SUCCESS;
X else return -ENODEV;
}
/*
//Exported functions to control GPIOS
int ksgpio_set_start(int val)
{
X if(g_start) {
X gpiod_set_value(g_start,val);
X return SUCCESS;
X } else {
X return -ENODEV;
X }
X
}
X
int ksgpio_set_reset(int val)
{
X if(g_reset) {
X gpiod_set_value(g_reset,val);
X return SUCCESS;
X } else {
X return -ENODEV;
X }
}
*/
EXPORT_SYMBOL_GPL(multi_gpio_check_status);
X
module_init(tst1_init_module);
module_exit(tst1_cleanup_module);
X
SHAR_EOF
(set 20 18 01 25 17 42 00 'multi-gpio.c'
eval "${shar_touch}") && \
chmod 0664 'multi-gpio.c'
if test $? -ne 0
then ${echo} "restore of multi-gpio.c failed"
fi
if ${md5check}
then (
${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'multi-gpio.c': 'MD5 check failed'
) << \SHAR_EOF
0605477e7ea58cec1481bd13ee2833c1 multi-gpio.c
SHAR_EOF
else
test `LC_ALL=C wc -c < 'multi-gpio.c'` -ne 8127 && \
${echo} "restoration warning: size of 'multi-gpio.c' is not 8127"
fi
fi
if rm -fr ${lock_dir}
then ${echo} "x - removed lock directory ${lock_dir}."
else ${echo} "x - failed to remove lock directory ${lock_dir}."
exit 1
fi
exit 0