0% found this document useful (0 votes)
67 views

Device Tree Examples

The document describes several examples of device tree changes to configure GPIO devices and peripherals: 1. A device node is added for an LCD display with compatible, status and GPIO properties to define the LCD pins. 2. A device node is added for GPIO devices with compatible, label and GPIO properties to define and expose GPIO pins in sysfs. 3. A device node is added for LEDs with compatible, label, GPIO properties to define the GPIO controlling each LED. 4. A device node is added for a humidity sensor with compatible and GPIO properties to define the sensor's data pin. 5. An example device node shows using a led-gpios property to define the GPIO

Uploaded by

srinath3shetty
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
67 views

Device Tree Examples

The document describes several examples of device tree changes to configure GPIO devices and peripherals: 1. A device node is added for an LCD display with compatible, status and GPIO properties to define the LCD pins. 2. A device node is added for GPIO devices with compatible, label and GPIO properties to define and expose GPIO pins in sysfs. 3. A device node is added for LEDs with compatible, label, GPIO properties to define the GPIO controlling each LED. 4. A device node is added for a humidity sensor with compatible and GPIO properties to define the sensor's data pin. 5. An example device node shows using a led-gpios property to define the GPIO

Uploaded by

srinath3shetty
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 14

Example 1:

Device Tree Changes:


/{
lcd16x2 {
compatible = "org,lcd16x2";
pictrl-names = "default";
pinctrl-0 = <&p8_gpios>;
status = "okay";
rs-gpios = <&gpio2 2 GPIO_ACTIVE_HIGH>;
rw-gpios = <&gpio2 7 GPIO_ACTIVE_HIGH>;
en-gpios = <&gpio2 8 GPIO_ACTIVE_HIGH>;
d4-gpios = <&gpio2 9 GPIO_ACTIVE_HIGH>;
d5-gpios = <&gpio2 10 GPIO_ACTIVE_HIGH>;
d6-gpios = <&gpio2 11 GPIO_ACTIVE_HIGH>;
d7-gpios = <&gpio2 12 GPIO_ACTIVE_HIGH>;
};

}; //root node

&am33xx_pinmux {
p8_gpios: bone_p8_gpios {
pinctrl-single,pins = <
AM33XX_PADCONF(AM335X_PIN_GPMC_ADVN_ALE,PIN_OUTPUT,MUX_MODE7)
AM33XX_PADCONF(AM335X_PIN_LCD_DATA1,PIN_OUTPUT,MUX_MODE7)
AM33XX_PADCONF(AM335X_PIN_LCD_DATA2,PIN_OUTPUT,MUX_MODE7)
AM33XX_PADCONF(AM335X_PIN_LCD_DATA3,PIN_OUTPUT,MUX_MODE7)
AM33XX_PADCONF(AM335X_PIN_LCD_DATA4,PIN_OUTPUT,MUX_MODE7)
AM33XX_PADCONF(AM335X_PIN_LCD_DATA5,PIN_OUTPUT,MUX_MODE7)
AM33XX_PADCONF(AM335X_PIN_LCD_DATA6,PIN_OUTPUT,MUX_MODE7)
>;
};
};

Client Driver changes:


static ssize_t lcdcmd_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size) {
status = kstrtol(buf, 0, &value);
lcd_send_command((u8)value,dev);
return status ? : size;
}
static DEVICE_ATTR_WO(lcdcmd);

static ssize_t lcdtext_store(struct device *dev,struct device_attribute *attr,\


const char *buf, size_t size) {
lcd_print_string((char*)buf,dev);
return size;
}
static DEVICE_ATTR_WO(lcdtext);

static ssize_t lcdscroll_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct lcd_private_data *dev_data = dev_get_drvdata(dev);

if(dev_data->lcd_scroll)
ret = sprintf(buf, "%s\n", "on");
else
ret = sprintf(buf, "%s\n", "off");

return ret;
}

static ssize_t lcdscroll_store(struct device *dev,struct device_attribute *attr,\


const char *buf, size_t size)
{
int status = 0;
struct lcd_private_data *dev_data = dev_get_drvdata(dev);

if (sysfs_streq(buf, "on")){
dev_data->lcd_scroll = 1;
/*Display shift left */
lcd_send_command(0x18,dev);
}
else if (sysfs_streq(buf, "off")){
dev_data->lcd_scroll = 0;
/*return home */
lcd_send_command(0x2,dev);
/*Turn off display shift */
lcd_send_command(0x10,dev);
}
else
status = -EINVAL;
return status ? : size;
}

static DEVICE_ATTR_RW(lcdscroll);
static struct attribute *lcd_attrs[] = {
&dev_attr_lcdcmd.attr,
&dev_attr_lcdtext.attr,
&dev_attr_lcdscroll.attr,
&dev_attr_lcdxy.attr,
NULL
};

struct attribute_group lcd_attr_group = {


.attrs = lcd_attrs
};

static const struct attribute_group *lcd_attr_groups[] = {


&lcd_attr_group,
NULL
};

static ssize_t lcdxy_show(struct device *dev, struct device_attribute *attr, char *buf)
{
int status;
struct lcd_private_data *dev_data = dev_get_drvdata(dev);

status = sprintf(buf,"%s\n",dev_data->lcdxy);
return status;
}

static ssize_t lcdxy_store(struct device *dev,struct device_attribute *attr, const char *buf, size_t size)
{
long value;
int status;
int x, y;
struct lcd_private_data *dev_data = dev_get_drvdata(dev);

status = kstrtol(buf,10,&value);
if(status)
return status;
x = value /10;
y = value % 10;
status = sprintf(dev_data->lcdxy ,"(%d,%d)",x,y);
lcd_set_cursor(x,y,dev);
return status;

int lcd_probe(struct platform_device *pdev)


{
int ret;
struct device *dev = &pdev->dev;

dev_set_drvdata(dev,&lcd_data);

lcd_data.desc[LCD_RS] = gpiod_get(dev, "rs", GPIOD_OUT_LOW);


lcd_data.desc[LCD_RW] = gpiod_get(dev, "rw", GPIOD_OUT_LOW);
lcd_data.desc[LCD_EN] = gpiod_get(dev, "en", GPIOD_OUT_LOW);
lcd_data.desc[LCD_D4] = gpiod_get(dev, "d4", GPIOD_OUT_LOW);
lcd_data.desc[LCD_D5] = gpiod_get(dev, "d5", GPIOD_OUT_LOW);
lcd_data.desc[LCD_D6] = gpiod_get(dev, "d6", GPIOD_OUT_LOW);
lcd_data.desc[LCD_D7] = gpiod_get(dev, "d7", GPIOD_OUT_LOW);

lcd_data.dev = device_create_with_groups(drv_data.class_lcd,dev,0,\
lcd_data,lcd_attr_groups,"LCD16x2");
}

Example 2:

Device Tree Changes:


/{
bone_gpio_devs {

compatible = "org,bone-gpio-sysfs";
pinctrl-single,names = "default";
pinctrl-0 = <&p8_gpios>;
status = "disabled";

gpio1 {
label = "gpio2.2";
bone-gpios = <&gpio2 2 GPIO_ACTIVE_HIGH>;

};

gpio2 {
label = "gpio2.7";
bone-gpios = <&gpio2 7 GPIO_ACTIVE_HIGH>;
};

led1 {
label = "usrled1:gpio1.22";
bone-gpios = <&gpio1 22 GPIO_ACTIVE_HIGH>;
};

led2 {
label = "usrled2:gpio1.23";
bone-gpios = <&gpio1 23 GPIO_ACTIVE_HIGH>;
};
};//bone_gpio_devs

}; //root node

&am33xx_pinmux {
p8_gpios: bone_p8_gpios {
pinctrl-single,pins = <
AM33XX_PADCONF(AM335X_PIN_GPMC_ADVN_ALE,PIN_OUTPUT,MUX_MODE7)
AM33XX_PADCONF(AM335X_PIN_LCD_DATA1,PIN_OUTPUT,MUX_MODE7)
AM33XX_PADCONF(AM335X_PIN_LCD_DATA2,PIN_OUTPUT,MUX_MODE7)
AM33XX_PADCONF(AM335X_PIN_LCD_DATA3,PIN_OUTPUT,MUX_MODE7)
AM33XX_PADCONF(AM335X_PIN_LCD_DATA4,PIN_OUTPUT,MUX_MODE7)
AM33XX_PADCONF(AM335X_PIN_LCD_DATA5,PIN_OUTPUT,MUX_MODE7)
AM33XX_PADCONF(AM335X_PIN_LCD_DATA6,PIN_OUTPUT,MUX_MODE7)
>;
};
};

Driver changes:

ssize_t direction_show(struct device *dev, struct device_attribute *attr,char *buf) {


struct gpiodev_private_data *dev_data = dev_get_drvdata(dev);

dir = gpiod_get_direction(dev_data->desc);
/* if dir = 0 , then show "out". if dir =1 , then show "in" */
direction = (dir == 0) ? "out":"in";
return sprintf(buf,"%s\n",direction);

ssize_t direction_store(struct device *dev, struct device_attribute *attr,const char *buf, size_t count) {
struct gpiodev_private_data *dev_data = dev_get_drvdata(dev);
if(sysfs_streq(buf,"in") )
ret = gpiod_direction_input(dev_data->desc);
else if (sysfs_streq(buf,"out"))
ret = gpiod_direction_output(dev_data->desc,0);
return ret ? : count;
}

ssize_t value_show(struct device *dev, struct device_attribute *attr,char *buf) {


struct gpiodev_private_data *dev_data = dev_get_drvdata(dev);
value = gpiod_get_value(dev_data->desc);
return sprintf(buf,"%d\n",value);
}

ssize_t value_store(struct device *dev, struct device_attribute *attr,const char *buf, size_t count) {
struct gpiodev_private_data *dev_data = dev_get_drvdata(dev);
ret = kstrtol(buf,0,&value);
gpiod_set_value(dev_data->desc,value);
return count;
}

ssize_t label_show(struct device *dev, struct device_attribute *attr,char *buf)


{
struct gpiodev_private_data *dev_data = dev_get_drvdata(dev);
return sprintf(buf, "%s\n", dev_data->label);
}

static DEVICE_ATTR_RW(direction);
static DEVICE_ATTR_RW(value);
static DEVICE_ATTR_RO(label);

static struct attribute *gpio_attrs[] = {


&dev_attr_direction.attr,
&dev_attr_value.attr,
&dev_attr_label.attr,
NULL
};

static struct attribute_group gpio_attr_group = {


.attrs = gpio_attrs
};

static const struct attribute_group *gpio_attr_groups[] = {


&gpio_attr_group,
NULL
};

struct of_device_id gpio_device_match[] =


{
{.compatible = "org,bone-gpio-sysfs"},
{}
};

struct platform_driver gpiosysfs_platform_driver =


{
.probe = gpio_sysfs_probe,
.remove = gpio_sysfs_remove,
.driver = {
.name = "bone-gpio-syfs",
.of_match_table = of_match_ptr(gpio_device_match)
}
};

int gpio_sysfs_probe(struct platform_device *pdev) {


struct device_node *parent = pdev->dev.of_node;
struct device_node *child = NULL;

struct gpiodev_private_data *dev_data;


gpio_drv_data.total_devices = of_get_child_count(parent);
for_each_available_child_of_node(parent,child)
{
dev_data = devm_kzalloc(dev,sizeof(*dev_data), GFP_KERNEL);

if(of_property_read_string(child,"label",&name) )
{
dev_warn(dev,"Missing label information\n");
snprintf(dev_data->label,sizeof(dev_data->label),"unkngpio%d",i);
}else{
strcpy(dev_data->label,name);
dev_info(dev,"GPIO label = %s\n",dev_data->label);
}

dev_data->desc = devm_fwnode_get_gpiod_from_child(dev,"bone",&child->fwnode,\
GPIOD_ASIS,dev_data->label);

/* set the gpio direction to output */


ret = gpiod_direction_output(dev_data->desc,0);

/*Create devices under /sys/class/bone_gpios */


gpio_drv_data.dev[i] =
device_create_with_groups(gpio_drv_data.class_gpio,dev,0,dev_data,gpio_attr_groups,\
dev_data->label);
}
return 0;
}

int __init gpio_sysfs_init(void) {


gpio_drv_data.class_gpio = class_create(THIS_MODULE,"bone_gpios");
platform_driver_register(&gpiosysfs_platform_driver);
return 0;
}
Example 3
https://www.leaflabs.com/news/2017/3/7/using-linux-device-trees-for-fun-and-profit

leds {
compatible = "gpio-leds";
user {
label = "green";
gpios = <0x13 0x1 0x0>;
linux,default-trigger = “heartbeat”;
};
};

Here we have a node labeled “leds” which has a property saying it is compatible with the “gpio-leds”
driver. If we reference the documentation for the gpio-leds driver here, we see that each subnode (in
our case “user”) is a controllable LED which appears as an entry in the ‘/sys/class/leds/’ directory with a
name specified by the “label” property.

he GPIO bank is specified with a pointer handle (or phandle in device tree language) to a GPIO bank
node. If we search for “phandle = <0x13>” in the ‘dts’ file we find the node for Freescale MXS
compatible GPIO controller:

gpio@2 {
compatible = "fsl,imx23-gpio", "fsl,mxs-gpio";
interrupts = <0x12>;
gpio-controller;
#gpio-cells = <0x2>;
interrupt-controller;
#interrupt-cells = <0x2>;
linux,phandle = <0x13>;
phandle = <0x13>;
};

Example 4
humidity_sensor {
compatible = "dht11";
gpios = <0x11 19 0x0>;
};

Example 5
https://www.kernel.org/doc/html/latest/driver-api/gpio/board.html

Device Tree Changes:


foo_device {
compatible = "acme,foo";
...
led-gpios = <&gpio 15 GPIO_ACTIVE_HIGH>, /* red */
<&gpio 16 GPIO_ACTIVE_HIGH>, /* green */
<&gpio 17 GPIO_ACTIVE_HIGH>; /* blue */

power-gpios = <&gpio 1 GPIO_ACTIVE_LOW>;


};

Driver changes:
struct gpio_desc *red, *green, *blue, *power;

red = gpiod_get_index(dev, "led", 0, GPIOD_OUT_HIGH);


green = gpiod_get_index(dev, "led", 1, GPIOD_OUT_HIGH);
blue = gpiod_get_index(dev, "led", 2, GPIOD_OUT_HIGH);

power = gpiod_get(dev, "power", GPIOD_OUT_HIGH);

Beaglebone: Introduction to GPIOs - Using Device Tree Overlays under Linux 3.8+
GPIO Debouncing:

Debouncing is a configuration set to a pin indicating that it is connected to a mechanical switch or


button, or similar that may bounce. Bouncing means the line is pulled high/low quickly at very short
intervals for mechanical reasons. This can result in the value being unstable or irqs fireing repeatedly
unless the line is debounced.

Debouncing in practice involves setting up a timer when something happens on the line, wait a little
while and then sample the line again, so see if it still has the same value (low or high). This could also be
repeated by a clever state machine, waiting for a line to become stable. In either case, it sets a certain
number of milliseconds for debouncing, or just “on/off” if that time is not configurable.

Ideally the gpio controller will have debounce support, so the gpio controller provides a wrapper
function for it gpio_set_debounce(). If your controller supports it should return 0. Also if debounce is
not supported by controller, software debounce can be added for example gpio_keys

gpiod_direction_input(gpioButton); // Set the button GPIO to be an input


gpiod_set_debounce(gpioButton, DEBOUNCE_TIME); // Debounce the button with a delay of 200ms
/// GPIO numbers and IRQ numbers are not the same! This function performs the mapping for us
irqNumber = gpiod_to_irq(gpioButton);
printk(KERN_INFO "EBB Button: The button is mapped to IRQ: %d\n", irqNumber);
// This next call requests an interrupt line
result = request_irq(irqNumber, // The interrupt number requested
(irq_handler_t) ebbgpio_irq_handler, // The pointer to the handler function below
IRQflags, // Use the custom kernel param to set interrupt type
"ebb_button_handler", // Used in /proc/interrupts to identify the owner
NULL); // The *dev_id for shared interrupt l
// This next call requests an interrupt line
result = request_irq(irqNumber, // The interrupt number requested
(irq_handler_t) ebbgpio_irq_handler, // The pointer to the handler function below
IRQflags, // Use the custom kernel param to set interrupt type
"ebb_button_handler", // Used in /proc/interrupts to identify the owner
NULL); // The *dev_id for shared interrupt lines, NULL is okay

static irq_handler_t ebbgpio_irq_handler(unsigned int irq, void *dev_id, struct pt_regs *regs){
ledOn = !ledOn; // Invert the LED state on each button press
gpiod_set_value(gpioLED, ledOn); // Set the physical LED accordingly
getnstimeofday(&ts_current); // Get the current time as ts_current
ts_diff = timespec_sub(ts_current, ts_last); // Determine the time difference between last 2 presses
ts_last = ts_current; // Store the current time as the last time ts_last
printk(KERN_INFO "EBB Button: The button state is currently: %d\n", gpio_get_value(gpioButton));
numberPresses++; // Global counter, will be outputted when the module is unloaded
return (irq_handler_t) IRQ_HANDLED; // Announce that the IRQ has been handled correctly
}

Software Debouncing:

static irqreturn_t irq_handler(int irq, void *dev_id, struct pt_regs *regs) {

static unsigned long old_jiffie = 0;


unsigned long diff = jiffies - old_jiffie;
if (diff < 20)
{
return IRQ_HANDLED;
}

printk(KERN_NOTICE "Interrupt [%d] for device %s was triggered, jiffies=%lu, diff=%lu, direction: %d
\n",
irq, (char *) dev_id, jiffies, diff, gpio_get_value(GPIO_BUTTON1));

old_jiffie = jiffies;
return IRQ_HANDLED;
}
More Example of DT LED node:
leds {
compatible = "gpio-leds";
led0 {
gpios = <&mcu_pio 0 GPIO_ACTIVE_LOW>;
linux,default-trigger = "disk-activity";
function = LED_FUNCTION_DISK;
};

led1 {
gpios = <&mcu_pio 1 GPIO_ACTIVE_HIGH>;
/* Keep LED on if BIOS detected hardware fault */
default-state = "keep";
function = LED_FUNCTION_FAULT;
};
};

run-control {
compatible = "gpio-leds";
led0 {
gpios = <&mpc8572 6 GPIO_ACTIVE_HIGH>;
color = <LED_COLOR_ID_RED>;
default-state = "off";
};
led1 {
gpios = <&mpc8572 7 GPIO_ACTIVE_HIGH>;
color = <LED_COLOR_ID_GREEN>;
default-state = "on";
};
};

You might also like