Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions 08_irq_handling/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
KERNELDIR ?=/home/valenti/Development/Embedded/exercise8/build_orange_zero_image/build/linux-5.10.10/

ifneq ($(KERNELRELEASE),)
obj-m := led_mod_irq.o
else

all:
$(MAKE) CFLAGS_MODULE="-DDEBUG -DORANGE_PI_ZERO" -C $(KERNELDIR) M=$(PWD) modules
clean:
$(MAKE) -C $(KERNELDIR) M=$(PWD) clean
endif
7 changes: 7 additions & 0 deletions 08_irq_handling/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,10 @@
Run polling example on your board.
Modify you driver to enable irq handling instead of polling mechanism

Reference links:
https://gist.github.com/rday/c796e3307c4087ae3539
https://elixir.bootlin.com/linux/latest/source/drivers/iio/humidity/dht11.c#L310
https://www.kernel.org/doc/Documentation/gpio/board.txt
https://elixir.bootlin.com/linux/latest/source/drivers/input/touchscreen/ts4800-ts.c#L211
https://linux-kernel-labs.github.io/refs/heads/master/labs/deferred_work.html
https://elixir.bootlin.com/linux/latest/source/drivers/input/keyboard/matrix_keypad.c#L189
269 changes: 269 additions & 0 deletions 08_irq_handling/led_mod_irq.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,269 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__

#include <linux/module.h>
#include <linux/gpio.h>
#include <linux/ktime.h>
#include <linux/interrupt.h>
#include <linux/of.h>
#include <linux/jiffies.h>
#include <linux/slab.h>
#include <linux/ktime.h>

#define GPIO_NUMBER(port, bit) (32 * (port) + (bit))

/* https://linux-sunxi.org/Xunlong_Orange_Pi_PC#LEDs
* Board config for OPI-PC:
* LED GREEN (PL10): GPIO_11_10
* LED RED (PA15): GPIO_0_15
* BUTTON (PG7) : GPIO_6_7
*
* https://linux-sunxi.org/Xunlong_Orange_Pi_Zero#LEDs
* Board config for OPI-Zero:
* LED GREEN (PL10): GPIO_11_10
* LED RED (PA17): GPIO_0_17
* BUTTON (PA6) : GPIO_0_6
*
*/

#ifdef ORANGE_PI_PC
#error "Implement the necessary port mappings"
#endif
#ifdef ORANGE_PI_ZERO
#define LED_GREEN GPIO_NUMBER(11, 10)
#define LED_RED GPIO_NUMBER(0, 17)
#define BUTTON GPIO_NUMBER(0, 6)
#endif

#define GREEN_LED_TOGGLE_TIMEOUT 400
#define RED_LED_TOGGLE_TIMEOUT 50

struct irq_module_data {
int ledg_gpio;
int ledr_gpio;
int button_gpio;
int button_state;
int button_cnt;
int ledr_gpio_state;
struct delayed_work ledg_check_work;
struct delayed_work ledr_toggle_work;
spinlock_t lock;
};

struct irq_module_data *module_data = NULL;

static void check_green_delayed_handler(struct work_struct *work)
{
pr_info("Delayed work handler\n");

struct irq_module_data *user_data = container_of(
work, struct irq_module_data, ledg_check_work.work);

spin_lock(&user_data->lock);
int button_gpio_state = gpio_get_value(user_data->button_gpio);
int prev_gpio_value = user_data->button_state;

user_data->button_state = button_gpio_state;
spin_unlock(&user_data->lock);

if (button_gpio_state == prev_gpio_value) {
pr_info("Delayed work handler:Disabled green led\n");
gpio_set_value(user_data->ledg_gpio, 0);
} else {
pr_info("Delayed work handler:Enabled green led\n");
gpio_set_value(user_data->ledg_gpio, 1);
}
}

static void toggle_red_delayed_handler(struct work_struct *work)
{
struct irq_module_data *user_data = container_of(
work, struct irq_module_data, ledr_toggle_work.work);
spin_lock(&user_data->lock);
gpio_set_value(user_data->ledr_gpio, !user_data->ledr_gpio_state);
user_data->ledr_gpio_state = !user_data->ledr_gpio_state;
spin_unlock(&user_data->lock);
}

static int led_gpio_init(int gpio, int *led_gpio)
{
int res;

res = gpio_direction_output(gpio, 0);
if (res != 0)
return res;

*led_gpio = gpio;
return 0;
}

static int button_gpio_init(int gpio)
{
int res;

res = gpio_request(gpio, "Onboard user button");
if (res != 0)
return res;

res = gpio_direction_input(gpio);
if (res != 0)
goto err_input;

module_data->button_gpio = gpio;
pr_info("Init GPIO%d OK\n", module_data->button_gpio);
module_data->button_state = gpio_get_value(module_data->button_gpio);
pr_info("Got button value: %d\n", module_data->button_state);
module_data->button_cnt = 0;

return 0;

err_input:
gpio_free(gpio);
return res;
}

static irqreturn_t button_module_irq_handler(int irq, void *user_data)
{
struct irq_module_data *irq_user_data =
(struct irq_module_data *)user_data;
unsigned long flags;
uint8_t button_value;

spin_lock_irqsave(&irq_user_data->lock, flags);

button_value = gpio_get_value(irq_user_data->button_gpio);
pr_info("Inside interrupt handler, button value:%d, prev_value:%d\n",
button_value, irq_user_data->button_state);
if (module_data->button_state == button_value)
goto out;

if (button_value == 0) {
pr_info("Canceled delayed work because of the button_value==0\n");
cancel_delayed_work(&irq_user_data->ledg_check_work);
cancel_delayed_work(&irq_user_data->ledr_toggle_work);

gpio_set_value(irq_user_data->ledr_gpio, 1);
irq_user_data->ledr_gpio_state = 1;
gpio_set_value(irq_user_data->ledg_gpio, 1);

schedule_delayed_work(&irq_user_data->ledr_toggle_work,
msecs_to_jiffies(RED_LED_TOGGLE_TIMEOUT));

module_data->button_state = button_value;
} else {
pr_info("Scheduled delayed work\n");

module_data->button_state = button_value;

gpio_set_value(irq_user_data->ledr_gpio, 1);
irq_user_data->ledr_gpio_state = 1;
schedule_delayed_work(
&irq_user_data->ledg_check_work,
msecs_to_jiffies(GREEN_LED_TOGGLE_TIMEOUT));
schedule_delayed_work(&irq_user_data->ledr_toggle_work,
msecs_to_jiffies(RED_LED_TOGGLE_TIMEOUT));
}
out:
spin_unlock_irqrestore(&irq_user_data->lock, flags);
return IRQ_HANDLED;
}

static void button_gpio_deinit(struct irq_module_data *module_data_arg)
{
if (module_data_arg->button_gpio >= 0) {
gpio_free(module_data_arg->button_gpio);
pr_info("Deinit GPIO%d\n", module_data_arg->button_gpio);
}
}

static void gpio_setup_irq(void)
{
int irq;
int rc;

irq = gpio_to_irq(BUTTON);
rc = request_irq(irq, button_module_irq_handler,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
"button_module_irq_handler", module_data);
}

static void gpio_deinit_irq(void)
{
int irq = gpio_to_irq(BUTTON);

free_irq(irq, module_data);
}

/* Module entry/exit points */
static int __init gpio_irq_init(void)
{
module_data = kzalloc(sizeof(struct irq_module_data), GFP_KERNEL);

if (!module_data)
return -ENOMEM;

module_data->button_cnt = -1;
module_data->button_state = -1;

int res;

pr_info("GPIO Init\n");

res = button_gpio_init(BUTTON);
if (res != 0) {
pr_err("Can't set GPIO%d for button\n", BUTTON);
kfree(module_data);
return res;
}

res = led_gpio_init(LED_GREEN, &module_data->ledg_gpio);
if (res != 0) {
pr_err("Can't set GPIO%d for output\n", LED_GREEN);
goto err_led;
}

gpio_set_value(module_data->ledg_gpio, 0);

res = led_gpio_init(LED_RED, &module_data->ledr_gpio);
if (res != 0) {
pr_err("Can't set GPIO%d for output\n", LED_RED);
goto err_led;
}
gpio_set_value(module_data->ledr_gpio, 1);
module_data->ledr_gpio_state = 1;

INIT_DELAYED_WORK(&module_data->ledg_check_work,
check_green_delayed_handler);
INIT_DELAYED_WORK(&module_data->ledr_toggle_work,
toggle_red_delayed_handler);
spin_lock_init(&module_data->lock);

gpio_setup_irq();
return 0;

err_led:
button_gpio_deinit(module_data);
kfree(module_data);
return res;
}

static void __exit gpio_irq_exit(void)
{
gpio_deinit_irq();
cancel_delayed_work_sync(&module_data->ledg_check_work);
cancel_delayed_work_sync(&module_data->ledr_toggle_work);

gpio_set_value(module_data->ledg_gpio, 1);
gpio_set_value(module_data->ledr_gpio, 0);

button_gpio_deinit(module_data);

kfree(module_data);
}

module_init(gpio_irq_init);
module_exit(gpio_irq_exit);

MODULE_AUTHOR("Valentyn Korniienko valentyn.korniienko1@nure.ua>");
MODULE_DESCRIPTION("LED Test");
MODULE_LICENSE("GPL");
MODULE_VERSION("0.1");
23 changes: 14 additions & 9 deletions 08_irq_handling/led_mod.c → 08_irq_handling/led_mod_polling.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,18 @@
* Board config for OPI-Zero:
* LED GREEN (PL10): GPIO_11_10
* LED RED (PA17): GPIO_0_17
* BUTTON (PG7) : GPIO_6_7
* BUTTON (PA6) : GPIO_0_6
*
*/

#ifdef ORANGE_PI_PC
#error "Implement the necessary port mappings"
#endif
#ifdef ORANGE_PI_ZERO
#define LED_GREEN GPIO_NUMBER(11, 10)
#define LED_RED GPIO_NUMBER(0, 15)
#define BUTTON GPIO_NUMBER(6, 7)

#define LED_RED GPIO_NUMBER(0, 17)
#define BUTTON GPIO_NUMBER(0, 6)
#endif
//#define TIMER_ENABLE 1

static int ledg_gpio = -1;
Expand All @@ -44,11 +48,11 @@ static enum hrtimer_restart timer_callback(struct hrtimer *timer)
cur_button_state = gpio_get_value(button_gpio);
button_cnt = (cur_button_state == button_state) ? (button_cnt + 1) : 0;
button_state = cur_button_state;
gpio_set_value(ledr_gpio, ((button_cnt == 20) ? 1 : 0));
if (button_cnt >= 20)
gpio_set_value(ledr_gpio, ((button_cnt == 400) ? 1 : 0));
if (button_cnt >= 400)
gpio_set_value(ledg_gpio, !button_state);
hrtimer_forward(timer, timer->base->get_time(), timer_period);
return HRTIMER_RESTART; //restart timer
return HRTIMER_RESTART; //restart timer
}
#endif

Expand Down Expand Up @@ -79,6 +83,7 @@ static int button_gpio_init(int gpio)
button_gpio = gpio;
pr_info("Init GPIO%d OK\n", button_gpio);
button_state = gpio_get_value(button_gpio);
pr_info("Got button value: %d\n", button_state);
button_cnt = 0;

return 0;
Expand Down Expand Up @@ -108,7 +113,7 @@ static int __init gpio_poll_init(void)
return res;
}
#ifdef TIMER_ENABLE
timer_period = ktime_set(0, 1000000); /*1 msec*/
timer_period = ktime_set(0, 1000000); /*1 msec*/
hrtimer_init(&button_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
hrtimer_start(&button_timer, timer_period, HRTIMER_MODE_REL);
button_timer.function = timer_callback;
Expand Down Expand Up @@ -137,7 +142,7 @@ static int __init gpio_poll_init(void)

static void __exit gpio_poll_exit(void)
{
gpio_set_value(ledg_gpio, 0);
gpio_set_value(ledg_gpio, 1);
gpio_set_value(ledr_gpio, 0);
button_gpio_deinit();
#ifdef TIMER_ENABLE
Expand Down
28 changes: 28 additions & 0 deletions 08_irq_handling/module_test.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
$ insmod led_mod_irq.ko
[ 909.472442] led_mod_irq: gpio_irq_init: GPIO Init
[ 909.477269] led_mod_irq: button_gpio_init: Init GPIO6 OK
[ 909.482585] led_mod_irq: button_gpio_init: Got button value: 0
$ [ 911.090843] led_mod_irq: button_module_irq_handler: Inside interrupt handler, button value:1, prev_value:0
[ 911.100507] led_mod_irq: button_module_irq_handler: Scheduled delayed work
[ 911.163376] led_mod_irq: button_module_irq_handler: Inside interrupt handler, button value:0, prev_value:1
[ 911.173025] led_mod_irq: button_module_irq_handler: Canceled delayed work because of the button_value==0
[ 912.948822] led_mod_irq: button_module_irq_handler: Inside interrupt handler, button value:1, prev_value:0
[ 912.958474] led_mod_irq: button_module_irq_handler: Scheduled delayed work
[ 913.356733] led_mod_irq: check_green_delayed_handler: Delayed work handler
[ 913.363614] led_mod_irq: check_green_delayed_handler: Delayed work handler:Disabled green led
[ 913.795551] led_mod_irq: button_module_irq_handler: Inside interrupt handler, button value:1, prev_value:1
[ 913.805224] led_mod_irq: button_module_irq_handler: Inside interrupt handler, button value:0, prev_value:1
[ 913.814869] led_mod_irq: button_module_irq_handler: Canceled delayed work because of the button_value==0
[ 914.892841] led_mod_irq: button_module_irq_handler: Inside interrupt handler, button value:1, prev_value:0
[ 914.902491] led_mod_irq: button_module_irq_handler: Scheduled delayed work
[ 915.296738] led_mod_irq: check_green_delayed_handler: Delayed work handler
[ 915.303623] led_mod_irq: check_green_delayed_handler: Delayed work handler:Disabled green led
[ 915.742357] led_mod_irq: button_module_irq_handler: Inside interrupt handler, button value:0, prev_value:1
[ 915.752008] led_mod_irq: button_module_irq_handler: Canceled delayed work because of the button_value==0
[ 916.695663] led_mod_irq: button_module_irq_handler: Inside interrupt handler, button value:1, prev_value:0
[ 916.705315] led_mod_irq: button_module_irq_handler: Scheduled delayed work
[ 916.815188] led_mod_irq: button_module_irq_handler: Inside interrupt handler, button value:0, prev_value:1
[ 916.824835] led_mod_irq: button_module_irq_handler: Canceled delayed work because of the button_value==0
rmmod led_mod_irq
[ 923.244082] led_mod_irq: button_gpio_deinit: Deinit GPIO6
$
6 changes: 6 additions & 0 deletions 08_irq_handling/setup_orange_env.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/bin/sh
export ARCH=arm
export CROSS_COMPILE='arm-buildroot-linux-uclibcgnueabihf-'
export PATH=$PATH:~/Development/Embedded/exercise8/build_orange_image/host/bin
export BUILD_KERNEL=/home/valenti/Development/Embedded/exercise8/build_orange_image/build/linux-5.10.10
export CURDIR=$(pwd)