From: Michael Brown Date: Tue, 5 Aug 2025 12:54:27 +0000 (+0100) Subject: [gpio] Add a framework for GPIO controllers X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=90fe3a2924962af6cc35023f6eb68bc43318ab21;p=thirdparty%2Fipxe.git [gpio] Add a framework for GPIO controllers Signed-off-by: Michael Brown --- diff --git a/src/core/gpio.c b/src/core/gpio.c new file mode 100644 index 000000000..f57139a11 --- /dev/null +++ b/src/core/gpio.c @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2025 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include + +/** @file + * + * General purpose I/O + * + */ + +/** List of GPIO controllers */ +static LIST_HEAD ( all_gpios ); + +/** + * Allocate GPIO controller + * + * @v count Number of GPIO pins + * @v priv_len Size of driver-private data + * @ret gpios GPIO controller, or NULL + */ +struct gpios * alloc_gpios ( unsigned int count, size_t priv_len ) { + struct gpios *gpios; + struct gpio *gpio; + size_t len; + unsigned int i; + + /* Allocate and initialise structure */ + len = ( sizeof ( *gpios ) + ( count * sizeof ( *gpio ) ) + priv_len ); + gpios = zalloc ( len ); + if ( ! gpios ) + return NULL; + gpios->count = count; + gpios->gpio = ( ( ( void * ) gpios ) + sizeof ( *gpios ) ); + gpios->priv = ( ( ( void * ) gpios ) + sizeof ( *gpios ) + + ( count * sizeof ( *gpio ) ) ); + + /* Initialise GPIO pins */ + for ( i = 0 ; i < count ; i++ ) { + gpio = &gpios->gpio[i]; + gpio->gpios = gpios; + gpio->index = i; + } + + return gpios; +} + +/** + * Register GPIO controller + * + * @v gpios GPIO controller + * @ret rc Return status code + */ +int gpios_register ( struct gpios *gpios ) { + + /* Add to list of GPIO controllers */ + gpios_get ( gpios ); + list_add_tail ( &gpios->list, &all_gpios ); + DBGC ( gpios, "GPIO %s registered with %d GPIOs\n", + gpios->dev->name, gpios->count ); + + return 0; +} + +/** + * Unregister GPIO controller + * + * @v gpios GPIO controller + */ +void gpios_unregister ( struct gpios *gpios ) { + + /* Remove from list of GPIO controllers */ + DBGC ( gpios, "GPIO %s unregistered\n", gpios->dev->name ); + list_del ( &gpios->list ); + gpios_put ( gpios ); +} + +/** + * Find GPIO controller + * + * @v bus_type Bus type + * @v location Bus location + * @ret gpios GPIO controller, or NULL + */ +struct gpios * gpios_find ( unsigned int bus_type, unsigned int location ) { + struct gpios *gpios; + + /* Scan through list of registered GPIO controllers */ + list_for_each_entry ( gpios, &all_gpios, list ) { + if ( ( gpios->dev->desc.bus_type == bus_type ) && + ( gpios->dev->desc.location == location ) ) + return gpios; + } + + return NULL; +} + +/** + * Get null GPIO input value + * + * @v gpios GPIO controller + * @v gpio GPIO pin + * @ret active Pin is in the active state + */ +static int null_gpio_in ( struct gpios *gpios __unused, + struct gpio *gpio __unused ) { + return 0; +} + +/** + * Set null GPIO output value + * + * @v gpios GPIO controller + * @v gpio GPIO pin + * @v active Set pin to active state + */ +static void null_gpio_out ( struct gpios *gpios __unused, + struct gpio *gpio __unused, int active __unused ) { + /* Nothing to do */ +} + +/** + * Configure null GPIO pin + * + * @v gpios GPIO controller + * @v gpio GPIO pin + * @v config Configuration + * @ret rc Return status code + */ +static int null_gpio_config ( struct gpios *gpios __unused, + struct gpio *gpio __unused, + unsigned int config __unused ) { + return -ENODEV; +} + +/** Null GPIO operations */ +struct gpio_operations null_gpio_operations = { + .in = null_gpio_in, + .out = null_gpio_out, + .config = null_gpio_config, +}; diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index 7db8d5679..b99d2101e 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -87,6 +87,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_efi_open ( ERRFILE_CORE | 0x002f0000 ) #define ERRFILE_efi_table ( ERRFILE_CORE | 0x00300000 ) #define ERRFILE_efi_connect ( ERRFILE_CORE | 0x00310000 ) +#define ERRFILE_gpio ( ERRFILE_CORE | 0x00320000 ) #define ERRFILE_eisa ( ERRFILE_DRIVER | 0x00000000 ) #define ERRFILE_isa ( ERRFILE_DRIVER | 0x00010000 ) diff --git a/src/include/ipxe/gpio.h b/src/include/ipxe/gpio.h new file mode 100644 index 000000000..fa9e19089 --- /dev/null +++ b/src/include/ipxe/gpio.h @@ -0,0 +1,199 @@ +#ifndef _IPXE_GPIO_H +#define _IPXE_GPIO_H + +/** @file + * + * General purpose I/O + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include + +/** A GPIO pin */ +struct gpio { + /** GPIO controller */ + struct gpios *gpios; + /** Pin index */ + unsigned int index; + /** Configuration */ + unsigned int config; +}; + +/** GPIO is active low + * + * This bit is chosen to match the devicetree standard usage. + */ +#define GPIO_CFG_ACTIVE_LOW 0x01 + +/** GPIO is an output */ +#define GPIO_CFG_OUTPUT 0x0100 + +/** A GPIO controller */ +struct gpios { + /** Reference count */ + struct refcnt refcnt; + /** List of GPIO controllers */ + struct list_head list; + /** Generic device */ + struct device *dev; + /** Number of GPIOs */ + unsigned int count; + + /** Individual GPIOs */ + struct gpio *gpio; + /** GPIO operations */ + struct gpio_operations *op; + + /** Driver-private data */ + void *priv; +}; + +/** GPIO operations */ +struct gpio_operations { + /** + * Get current GPIO input value + * + * @v gpios GPIO controller + * @v gpio GPIO pin + * @ret active Pin is in the active state + */ + int ( * in ) ( struct gpios *gpios, struct gpio *gpio ); + /** + * Set current GPIO output value + * + * @v gpios GPIO controller + * @v gpio GPIO pin + * @v active Set pin to active state + */ + void ( * out ) ( struct gpios *gpios, struct gpio *gpio, int active ); + /** + * Configure GPIO pin + * + * @v gpios GPIO controller + * @v gpio GPIO pin + * @v config Configuration + * @ret rc Return status code + */ + int ( * config ) ( struct gpios *gpios, struct gpio *gpio, + unsigned int config ); +}; + +extern struct gpio_operations null_gpio_operations; + +/** + * Get reference to GPIO controller + * + * @v gpios GPIO controller + * @ret gpios GPIO controller + */ +static inline __attribute__ (( always_inline )) struct gpios * +gpios_get ( struct gpios *gpios ) { + ref_get ( &gpios->refcnt ); + return gpios; +} + +/** + * Drop reference to GPIO controller + * + * @v gpios GPIO controller + */ +static inline __attribute__ (( always_inline )) void +gpios_put ( struct gpios *gpios ) { + ref_put ( &gpios->refcnt ); +} + +/** + * Get reference to GPIO pin + * + * @v gpio GPIO pin + * @ret gpio GPIO pin + */ +static inline __attribute__ (( always_inline )) struct gpio * +gpio_get ( struct gpio *gpio ) { + gpios_get ( gpio->gpios ); + return gpio; +} + +/** + * Drop reference to GPIO ping + * + * @v gpio GPIO pin + */ +static inline __attribute__ (( always_inline )) void +gpio_put ( struct gpio *gpio ) { + gpios_put ( gpio->gpios ); +} + +/** + * Initialise a GPIO controller + * + * @v gpios GPIO controller + * @v op GPIO operations + */ +static inline __attribute__ (( always_inline )) void +gpios_init ( struct gpios *gpios, struct gpio_operations *op ) { + gpios->op = op; +} + +/** + * Stop using a GPIO controller + * + * @v gpios GPIO controller + * + * Drivers should call this method immediately before the final call + * to gpios_put(). + */ +static inline __attribute__ (( always_inline )) void +gpios_nullify ( struct gpios *gpios ) { + gpios->op = &null_gpio_operations; +} + +/** + * Get current GPIO input value + * + * @v gpio GPIO pin + * @ret active Pin is in the active state + */ +static inline int gpio_in ( struct gpio *gpio ) { + struct gpios *gpios = gpio->gpios; + + return gpios->op->in ( gpios, gpio ); +} + +/** + * Set current GPIO output value + * + * @v gpio GPIO pin + * @v active Set pin to active state + */ +static inline void gpio_out ( struct gpio *gpio, int active ) { + struct gpios *gpios = gpio->gpios; + + gpios->op->out ( gpios, gpio, active ); +} + +/** + * Configure GPIO pin + * + * @v gpio GPIO pin + * @v config Configuration + * @ret rc Return status code + */ +static inline int gpio_config ( struct gpio *gpio, unsigned int config ) { + struct gpios *gpios = gpio->gpios; + + return gpios->op->config ( gpios, gpio, config ); +} + +extern struct gpios * alloc_gpios ( unsigned int count, size_t priv_len ); +extern int gpios_register ( struct gpios *gpios ); +extern void gpios_unregister ( struct gpios *gpios ); +extern struct gpios * gpios_find ( unsigned int bus_type, + unsigned int location ); + +#endif /* _IPXE_GPIO_H */