Commit 5a727a38 authored by Jeremy Bettis's avatar Jeremy Bettis Committed by Carles Cufi
Browse files

drivers: Add level intrs in gpio_ite_it8xxx2_v2



Implement level based gpio interrupts, by using a worker queue to
repeatedly call the gpio callbacks until the gpio is no longer active.

Update unit test for new interrupts.

Bug #66401

Signed-off-by: default avatarJeremy Bettis <jbettis@google.com>
parent 6c567d48
Loading
Loading
Loading
Loading
+51 −5
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@
#include <zephyr/dt-bindings/interrupt-controller/ite-intc.h>
#include <zephyr/init.h>
#include <zephyr/irq.h>
#include <zephyr/kernel.h>
#include <zephyr/spinlock.h>
#include <zephyr/sys/util.h>
#include <zephyr/types.h>
@@ -61,6 +62,10 @@ struct gpio_ite_data {
	sys_slist_t callbacks;
	uint8_t volt_default_set;
	struct k_spinlock lock;
	uint8_t level_isr_high;
	uint8_t level_isr_low;
	const struct device *instance;
	struct k_work interrupt_worker;
};

/**
@@ -359,6 +364,30 @@ static void gpio_ite_isr(const void *arg)
			break;
		}
	}
	/* Reschedule worker */
	k_work_submit(&data->interrupt_worker);
}

static void gpio_ite_interrupt_worker(struct k_work *work)
{
	struct gpio_ite_data * const data = CONTAINER_OF(
		work, struct gpio_ite_data, interrupt_worker);
	gpio_port_value_t value;
	gpio_port_value_t triggered_int;

	gpio_ite_port_get_raw(data->instance, &value);

	k_spinlock_key_t key = k_spin_lock(&data->lock);

	triggered_int = (value & data->level_isr_high) | (~value & data->level_isr_low);
	k_spin_unlock(&data->lock, key);

	if (triggered_int != 0) {
		gpio_fire_callbacks(&data->callbacks, data->instance,
				    triggered_int);
		/* Reschedule worker */
		k_work_submit(&data->interrupt_worker);
	}
}

static int gpio_ite_pin_interrupt_configure(const struct device *dev,
@@ -386,11 +415,6 @@ static int gpio_ite_pin_interrupt_configure(const struct device *dev,
#endif /* CONFIG_GPIO_ENABLE_DISABLE_INTERRUPT */
	}

	if (mode == GPIO_INT_MODE_LEVEL) {
		LOG_ERR("Level trigger mode not supported");
		return -ENOTSUP;
	}

	/* Disable irq before configuring it */
	irq_disable(gpio_irq);

@@ -415,6 +439,19 @@ static int gpio_ite_pin_interrupt_configure(const struct device *dev,
		} else {
			ECREG(reg_wuemr) &= ~wuc_mask;
		}

		if (mode == GPIO_INT_MODE_LEVEL) {
			if (trig & GPIO_INT_TRIG_LOW) {
				data->level_isr_low |= BIT(pin);
				data->level_isr_high &= ~BIT(pin);
			} else {
				data->level_isr_low &= ~BIT(pin);
				data->level_isr_high |= BIT(pin);
			}
		} else {
			data->level_isr_low &= ~BIT(pin);
			data->level_isr_high &= ~BIT(pin);
		}
		/*
		 * Always write 1 to clear the WUC status register after
		 * modifying edge mode selection register (WUBEMR and WUEMR).
@@ -426,6 +463,7 @@ static int gpio_ite_pin_interrupt_configure(const struct device *dev,
	/* Enable GPIO interrupt */
	irq_connect_dynamic(gpio_irq, 0, gpio_ite_isr, dev, 0);
	irq_enable(gpio_irq);
	k_work_submit(&data->interrupt_worker);

	return 0;
}
@@ -446,6 +484,14 @@ static const struct gpio_driver_api gpio_ite_driver_api = {

static int gpio_ite_init(const struct device *dev)
{
	struct gpio_ite_data *data = dev->data;
	k_spinlock_key_t key = k_spin_lock(&data->lock);

	data->instance = dev;
	k_work_init(&data->interrupt_worker,
		gpio_ite_interrupt_worker);
	k_spin_unlock(&data->lock, key);

	return 0;
}

+38 −7
Original line number Diff line number Diff line
@@ -71,6 +71,11 @@ static void callback(const struct device *port, struct gpio_callback *cb, gpio_p
{
	callback_called++;
	zexpect_equal(pins, BIT(TEST_PIN));

	/* If the callback has been called 5 or more times, toggle the pin in the input register. */
	if (callback_called >= 5) {
		registers.gpdmr ^= pins;
	}
}

static void before_test(void *fixture)
@@ -236,34 +241,60 @@ ZTEST(gpio_ite_it8xxx2_v2, test_interrupt_edge_both)
	zassert_equal(callback_called, 2, "callback_called=%d", callback_called);
}

/* Tests both the active level case and the interrupt not firing at configure case. */
ZTEST(gpio_ite_it8xxx2_v2, test_interrupt_level_active)
{
	zassert_true(device_is_ready(gpio_dev));
	zassert_ok(gpio_pin_configure(gpio_dev, TEST_PIN, GPIO_INPUT | GPIO_ACTIVE_HIGH));
	zassert_equal(gpio_pin_interrupt_configure(gpio_dev, TEST_PIN, GPIO_INT_LEVEL_ACTIVE),
		      -ENOTSUP);

	gpio_init_callback(&callback_struct, &callback, BIT(TEST_PIN));
	zassert_ok(gpio_add_callback(gpio_dev, &callback_struct));
	zassert_ok(gpio_pin_interrupt_configure(gpio_dev, TEST_PIN, GPIO_INT_LEVEL_ACTIVE));
	zexpect_equal(registers.gpotr, 0, "gpotr=%x", registers.gpotr);
	zexpect_equal(registers.p18scr, 0);
	zexpect_equal(registers.gpcr[TEST_PIN], GPCR_PORT_PIN_MODE_INPUT, "gpcr[%d]=%x", TEST_PIN,
		      registers.gpcr[TEST_PIN]);
	zexpect_equal(registers.wubemr, 0, "wubemr=%x", registers.wubemr);
	zexpect_equal(registers.wuemr, 0, "wuemr=%x", registers.wuemr);
	zexpect_equal(registers.wuesr, 0, "wuesr=%x", registers.wuesr);
	zexpect_equal(registers.wuesr, TEST_MASK, "wuesr=%x", registers.wuesr);
	registers.wuesr = 0;
	k_sleep(K_MSEC(100));
	zexpect_equal(callback_called, 0, "callback_called=%d", callback_called);

	registers.gpdmr = BIT(TEST_PIN);
	/* Mock the hardware interrupt. */
	posix_sw_set_pending_IRQ(TEST_IRQ);
	k_sleep(K_MSEC(100));
	zexpect_equal(callback_called, 5, "callback_called=%d", callback_called);
}

/* Tests both the inactive level case and the interrupt already firing at configure case. */
ZTEST(gpio_ite_it8xxx2_v2, test_interrupt_level_inactive)
{
	zassert_true(device_is_ready(gpio_dev));
	zassert_ok(gpio_pin_configure(gpio_dev, TEST_PIN, GPIO_INPUT | GPIO_ACTIVE_HIGH));
	zassert_equal(gpio_pin_interrupt_configure(gpio_dev, TEST_PIN, GPIO_INT_LEVEL_INACTIVE),
		      -ENOTSUP);

	gpio_init_callback(&callback_struct, &callback, BIT(TEST_PIN));
	zassert_ok(gpio_add_callback(gpio_dev, &callback_struct));
	zassert_ok(gpio_pin_interrupt_configure(gpio_dev, TEST_PIN, GPIO_INT_LEVEL_INACTIVE));
	zexpect_equal(registers.gpotr, 0, "gpotr=%x", registers.gpotr);
	zexpect_equal(registers.p18scr, 0);
	zexpect_equal(registers.gpcr[TEST_PIN], GPCR_PORT_PIN_MODE_INPUT, "gpcr[%d]=%x", TEST_PIN,
		      registers.gpcr[TEST_PIN]);
	zexpect_equal(registers.wubemr, 0, "wubemr=%x", registers.wubemr);
	zexpect_equal(registers.wuemr, 0, "wuemr=%x", registers.wuemr);
	zexpect_equal(registers.wuesr, 0, "wuesr=%x", registers.wuesr);
	zexpect_equal(registers.wuemr, TEST_MASK, "wuemr=%x", registers.wuemr);
	zexpect_equal(registers.wuesr, TEST_MASK, "wuesr=%x", registers.wuesr);
	registers.wuesr = 0;
	k_sleep(K_MSEC(100));
	/* The interrupt was already active when we started. */
	zexpect_equal(callback_called, 5, "callback_called=%d", callback_called);

	registers.gpdmr = 0;
	callback_called = 0;
	/* Mock the hardware interrupt. */
	posix_sw_set_pending_IRQ(TEST_IRQ);
	k_sleep(K_MSEC(100));
	zexpect_equal(callback_called, 5, "callback_called=%d", callback_called);
}

ZTEST(gpio_ite_it8xxx2_v2, test_set_active_high)