diff --git a/08_irq_handling/Makefile b/08_irq_handling/Makefile new file mode 100644 index 0000000..4487d36 --- /dev/null +++ b/08_irq_handling/Makefile @@ -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 diff --git a/08_irq_handling/README.md b/08_irq_handling/README.md index 20360a8..28b07dc 100644 --- a/08_irq_handling/README.md +++ b/08_irq_handling/README.md @@ -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 diff --git a/08_irq_handling/led_mod_irq.c b/08_irq_handling/led_mod_irq.c new file mode 100644 index 0000000..2eff09f --- /dev/null +++ b/08_irq_handling/led_mod_irq.c @@ -0,0 +1,269 @@ +#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include + +#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"); \ No newline at end of file diff --git a/08_irq_handling/led_mod.c b/08_irq_handling/led_mod_polling.c similarity index 87% rename from 08_irq_handling/led_mod.c rename to 08_irq_handling/led_mod_polling.c index 5444623..7914d0f 100644 --- a/08_irq_handling/led_mod.c +++ b/08_irq_handling/led_mod_polling.c @@ -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; @@ -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 @@ -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; @@ -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; @@ -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 diff --git a/08_irq_handling/module_test.txt b/08_irq_handling/module_test.txt new file mode 100644 index 0000000..85b63b3 --- /dev/null +++ b/08_irq_handling/module_test.txt @@ -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 +$ diff --git a/08_irq_handling/setup_orange_env.sh b/08_irq_handling/setup_orange_env.sh new file mode 100644 index 0000000..1cd7ede --- /dev/null +++ b/08_irq_handling/setup_orange_env.sh @@ -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) \ No newline at end of file