Commit fea1c49b authored by Tomasz Bursztyka's avatar Tomasz Bursztyka Committed by Anas Nashif
Browse files

gpio: Improve the public API to handle multi callbacks



Many sub-systems might require to set a callback on different pins.
Thus enabling it via changing the API.

It is also possible to retrieve private-data in the callback handler
using CONTAINER_OF() macro (include/misc/util.h).

Former API is still available, and is emulated through the new one.
Using both should not be a problem as it's using new API calls.
However, it's now better to start using the new API.

Change-Id: Id16594202905976cc524775d1cd3592b54a84514
Signed-off-by: default avatarTomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
Signed-off-by: default avatarJohan Hedberg <johan.hedberg@intel.com>
parent 18fc64dc
Loading
Loading
Loading
Loading
+1 −3
Original line number Diff line number Diff line
@@ -26,10 +26,8 @@ menuconfig GPIO
if GPIO

config GPIO_DEBUG
	bool
	prompt "Debug output for GPIO drivers"
	bool "Debug output for GPIO drivers"
	default n
	depends on GPIO
	help
	  Enable the debug output for GPIO drivers

+2 −0
Original line number Diff line number Diff line
ccflags-$(CONFIG_GPIO_QMSI) +=-I$(CONFIG_QMSI_INSTALL_PATH)/include
ccflags-y +=-I$(srctree)/drivers

obj-y += gpio_api_compat.o

obj-$(CONFIG_GPIO_DW) += gpio_dw.o
obj-$(CONFIG_GPIO_PCAL9535A) += gpio_pcal9535a.o
obj-$(CONFIG_GPIO_MMIO) += gpio_mmio.o
+100 −0
Original line number Diff line number Diff line
/*
 * Copyright (c) 2016 Intel Corporation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @file Implementation of the API 1.0 GPIO compatibility layer
 */

#include <gpio.h>
#include <misc/util.h>

#include "gpio_api_compat.h"

/** These are maintained in a dedicated .gpio_compat section
 * See relevant arch's linker definitions in include/arch/
 */
extern struct gpio_compat_cb __gpio_compat_start[];
extern struct gpio_compat_cb __gpio_compat_end[];

static struct gpio_compat_cb *_gpio_compat_dev_lookup(struct device *dev)
{
	struct gpio_compat_cb *cb;

	for (cb = __gpio_compat_start; cb != __gpio_compat_end; cb++) {
		if (cb->dev == dev) {
			return cb;
		}
	}

	return NULL;
}

static void _gpio_compat_handler(struct device *dev,
				 struct gpio_callback *cb, uint32_t pins)
{
	struct _gpio_compat_data *data;
	int bit;

	data = CONTAINER_OF(cb, struct _gpio_compat_data, cb);

	for (bit = 0; bit < 32; bit++) {
		if (pins & BIT(bit)) {
			data->handler(dev, bit);
		}
	}
}

int gpio_set_callback(struct device *dev, gpio_callback_t callback)
{
	struct gpio_compat_cb *compat = _gpio_compat_dev_lookup(dev);
	int ret;

	if (!compat) {
		return DEV_FAIL;
	}

	ret = gpio_remove_callback(dev, &compat->d->cb);
	if (ret != DEV_OK) {
		return ret;
	}

	if (!callback) {
		return DEV_OK;
	}

	compat->d->handler = callback;
	compat->d->cb.handler = _gpio_compat_handler;

	return gpio_add_callback(dev, &compat->d->cb);
}

void _gpio_enable_callback(struct device *dev, uint32_t pins)
{
	struct gpio_compat_cb *compat = _gpio_compat_dev_lookup(dev);

	if (compat) {
		compat->d->cb.pin_mask |= pins;
	}
}

void _gpio_disable_callback(struct device *dev, uint32_t pins)
{
	struct gpio_compat_cb *compat = _gpio_compat_dev_lookup(dev);

	if (compat) {
		compat->d->cb.pin_mask &= ~(pins);
	}
}
+62 −0
Original line number Diff line number Diff line
/*
 * Copyright (c) 2016 Intel Corporation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @file Header for the API 1.0 GPIO compatibility code
 */

#ifndef __GPIO_API_COMPAT_H__
#define __GPIO_API_COMPAT_H__

struct _gpio_compat_data {
	struct gpio_callback cb;
	gpio_callback_t handler;
};

struct gpio_compat_cb {
	struct device *dev;
	struct _gpio_compat_data *d;
};

/** This macro is mandatory to be used in order to enable the API 1.0
 * support on GPIO drivers.
 */
#define GPIO_SETUP_COMPAT_DEV(dev_name)					\
	static struct _gpio_compat_data (__gcd_##dev_name) = {};	\
									\
	static struct gpio_compat_cb (__gpio_compat_##dev_name) __used	\
	__attribute__((__section__(".gpio_compat.init"))) = {		\
		.dev = &(__device_##dev_name),				\
		.d = &(__gcd_##dev_name)				\
	}

/**
 * @brief Enable the API v1.0 callback on given pins
 *
 * @param port device driver instance pointer to affect
 * @param pins mask of pins to enable
 */
void _gpio_enable_callback(struct device *port, uint32_t pins);

/**
 * @brief Disable the API v1.0 callback on given pins
 *
 * @param port device driver instance pointer to affect
 * @param pins mask of pins to disable
 */
void _gpio_disable_callback(struct device *port, uint32_t pins);

#endif /* __GPIO_API_COMPAT_H__ */
+15 −26
Original line number Diff line number Diff line
@@ -28,6 +28,8 @@
#include <soc.h>

#include <gpio.h>
#include "gpio_utils.h"
#include "gpio_api_compat.h"

typedef void (*config_func_t)(struct device *port);

@@ -36,8 +38,7 @@ struct gpio_sam3_config {
	volatile struct __pio	*port;

	/* callbacks */
	gpio_callback_t		cb;
	uint32_t		enabled_cb;
	sys_slist_t		cb;

	config_func_t		config_func;
};
@@ -205,35 +206,19 @@ static void gpio_sam3_isr(void *arg)
	struct device *dev = (struct device *)arg;
	struct gpio_sam3_config *cfg = dev->config->config_info;
	uint32_t int_stat;
	uint8_t bit;

	int_stat = cfg->port->isr;

	if (!cfg->cb) {
		return;
	}

	if (cfg->enabled_cb) {
		if (cfg->enabled_cb == 0xFFFFFFFF) {
			cfg->cb(dev, int_stat);
			return;
		}

		int_stat &= cfg->enabled_cb;
		for (bit = 0; bit < 32; bit++) {
			if (int_stat & BIT(bit)) {
				cfg->cb(dev, bit);
			}
		}
	}
	_gpio_fire_callbacks(&cfg->cb, dev, int_stat);
}

static int gpio_sam3_set_callback(struct device *dev,
				  gpio_callback_t callback)
static int gpio_sam3_manage_callback(struct device *dev,
				     struct gpio_callback *callback,
				     bool set)
{
	struct gpio_sam3_config *cfg = dev->config->config_info;

	cfg->cb = callback;
	_gpio_manage_callback(&cfg->cb, callback, set);

	return 0;
}
@@ -255,7 +240,7 @@ static int gpio_sam3_enable_callback(struct device *dev,
		return -ENOTSUP;
	}

	cfg->enabled_cb |= mask;
	_gpio_enable_callback(dev, mask);
	cfg->port->ier |= mask;

	return 0;
@@ -278,7 +263,7 @@ static int gpio_sam3_disable_callback(struct device *dev,
		return -ENOTSUP;
	}

	cfg->enabled_cb &= ~mask;
	_gpio_disable_callback(dev, mask);
	cfg->port->idr |= mask;

	return 0;
@@ -288,7 +273,7 @@ static struct gpio_driver_api gpio_sam3_drv_api_funcs = {
	.config = gpio_sam3_config,
	.write = gpio_sam3_write,
	.read = gpio_sam3_read,
	.set_callback = gpio_sam3_set_callback,
	.manage_callback = gpio_sam3_manage_callback,
	.enable_callback = gpio_sam3_enable_callback,
	.disable_callback = gpio_sam3_disable_callback,
};
@@ -322,6 +307,7 @@ DEVICE_AND_API_INIT(gpio_sam3_a, CONFIG_GPIO_ATMEL_SAM3_PORTA_DEV_NAME,
		    gpio_sam3_init, NULL, &gpio_sam3_a_cfg,
		    SECONDARY, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
		    &gpio_sam3_drv_api_funcs);
GPIO_SETUP_COMPAT_DEV(gpio_sam3_a);

void gpio_sam3_config_a(struct device *dev)
{
@@ -348,6 +334,7 @@ DEVICE_AND_API_INIT(gpio_sam3_b, CONFIG_GPIO_ATMEL_SAM3_PORTB_DEV_NAME,
		    gpio_sam3_init, NULL, &gpio_sam3_b_cfg,
		    SECONDARY, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
		    &gpio_sam3_drv_api_funcs);
GPIO_SETUP_COMPAT_DEV(gpio_sam3_b);

void gpio_sam3_config_b(struct device *dev)
{
@@ -374,6 +361,7 @@ DEVICE_AND_API_INIT(gpio_sam3_c, CONFIG_GPIO_ATMEL_SAM3_PORTC_DEV_NAME,
		    gpio_sam3_init, NULL, &gpio_sam3_c_cfg,
		    SECONDARY, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
		    &gpio_sam3_drv_api_funcs);
GPIO_SETUP_COMPAT_DEV(gpio_sam3_c);

void gpio_sam3_config_c(struct device *dev)
{
@@ -400,6 +388,7 @@ DEVICE_AND_API_INIT(gpio_sam3_d, CONFIG_GPIO_ATMEL_SAM3_PORTD_DEV_NAME,
		    gpio_sam3_init, NULL, &gpio_sam3_d_cfg,
		    SECONDARY, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
		    &gpio_sam3_drv_api_funcs);
GPIO_SETUP_COMPAT_DEV(gpio_sam3_d);

void gpio_sam3_config_d(struct device *dev)
{
Loading