# HG changeset patch # User Paul Boddie # Date 1695513223 -7200 # Node ID aec11102da9dd20361e182eeabe5313a532dc4f7 # Parent 40a23a1e92f80b96bdfaa74cc16e6b01142cff9a Added X1600 GPIO support, also introducing an operation to report pin configurations. Various minor changes have been made to the JZ4730 support, and further improvements may be desirable. diff -r 40a23a1e92f8 -r aec11102da9d pkg/devices/lib/gpio/include/gpio-jz4730.h --- a/pkg/devices/lib/gpio/include/gpio-jz4730.h Fri Sep 22 21:56:34 2023 +0200 +++ b/pkg/devices/lib/gpio/include/gpio-jz4730.h Sun Sep 24 01:53:43 2023 +0200 @@ -2,7 +2,7 @@ * GPIO driver for Ingenic JZ4730. * (See below for additional copyright and licensing notices.) * - * Copyright (C) 2017, 2018 Paul Boddie + * Copyright (C) 2017, 2018, 2023 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -117,6 +117,11 @@ void _config_pad(unsigned bitmap, unsigned func, unsigned value); + // Paired register field access. + + void _get_pin_value(unsigned pin, uint32_t reg_upper, uint32_t reg_lower, + unsigned *value); + public: Gpio_jz4730_chip(l4_addr_t start, l4_addr_t end, unsigned nr_pins); @@ -136,6 +141,7 @@ void config_pull(unsigned pin, unsigned mode); void config_pad(unsigned pin, unsigned func, unsigned value); void config_get(unsigned pin, unsigned reg, unsigned *value); + void config_pad_get(unsigned pin, unsigned *func, unsigned *value); // Multiple pin configuration methods. @@ -172,6 +178,7 @@ void jz4730_gpio_config_pull(void *gpio, unsigned pin, unsigned mode); void jz4730_gpio_config_pad(void *gpio, unsigned pin, unsigned func, unsigned value); void jz4730_gpio_config_get(void *gpio, unsigned pin, unsigned reg, unsigned *value); +void jz4730_gpio_config_pad_get(void *gpio, unsigned pin, unsigned *func, unsigned *value); void jz4730_gpio_multi_setup(void *gpio, Pin_slice const *mask, unsigned mode, unsigned outvalues); void jz4730_gpio_multi_config_pull(void *gpio, Pin_slice const *mask, unsigned mode); diff -r 40a23a1e92f8 -r aec11102da9d pkg/devices/lib/gpio/include/gpio-jz4740.h --- a/pkg/devices/lib/gpio/include/gpio-jz4740.h Fri Sep 22 21:56:34 2023 +0200 +++ b/pkg/devices/lib/gpio/include/gpio-jz4740.h Sun Sep 24 01:53:43 2023 +0200 @@ -2,7 +2,7 @@ * GPIO driver for Ingenic JZ4740. * (See below for additional copyright and licensing notices.) * - * Copyright (C) 2017, 2018 Paul Boddie + * Copyright (C) 2017, 2018, 2023 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -128,6 +128,7 @@ void config_pull(unsigned pin, unsigned mode); void config_pad(unsigned pin, unsigned func, unsigned value); void config_get(unsigned pin, unsigned reg, unsigned *value); + void config_pad_get(unsigned pin, unsigned *func, unsigned *value); // Multiple pin configuration methods. @@ -164,6 +165,7 @@ void jz4740_gpio_config_pull(void *gpio, unsigned pin, unsigned mode); void jz4740_gpio_config_pad(void *gpio, unsigned pin, unsigned func, unsigned value); void jz4740_gpio_config_get(void *gpio, unsigned pin, unsigned reg, unsigned *value); +void jz4740_gpio_config_pad_get(void *gpio, unsigned pin, unsigned *func, unsigned *value); void jz4740_gpio_multi_setup(void *gpio, Pin_slice const *mask, unsigned mode, unsigned outvalues); void jz4740_gpio_multi_config_pull(void *gpio, Pin_slice const *mask, unsigned mode); diff -r 40a23a1e92f8 -r aec11102da9d pkg/devices/lib/gpio/include/gpio-jz4780.h --- a/pkg/devices/lib/gpio/include/gpio-jz4780.h Fri Sep 22 21:56:34 2023 +0200 +++ b/pkg/devices/lib/gpio/include/gpio-jz4780.h Sun Sep 24 01:53:43 2023 +0200 @@ -2,7 +2,7 @@ * GPIO driver for Ingenic JZ4780. * (See below for additional copyright and licensing notices.) * - * Copyright (C) 2017, 2018 Paul Boddie + * Copyright (C) 2017, 2018, 2023 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -118,6 +118,7 @@ void config_pull(unsigned pin, unsigned mode); void config_pad(unsigned pin, unsigned func, unsigned value); void config_get(unsigned pin, unsigned reg, unsigned *value); + void config_pad_get(unsigned pin, unsigned *func, unsigned *value); // Multiple pin configuration methods. @@ -154,6 +155,7 @@ void jz4780_gpio_config_pull(void *gpio, unsigned pin, unsigned mode); void jz4780_gpio_config_pad(void *gpio, unsigned pin, unsigned func, unsigned value); void jz4780_gpio_config_get(void *gpio, unsigned pin, unsigned reg, unsigned *value); +void jz4780_gpio_config_pad_get(void *gpio, unsigned pin, unsigned *func, unsigned *value); void jz4780_gpio_multi_setup(void *gpio, Pin_slice const *mask, unsigned mode, unsigned outvalues); void jz4780_gpio_multi_config_pad(void *gpio, Pin_slice const *mask, unsigned func, unsigned value); diff -r 40a23a1e92f8 -r aec11102da9d pkg/devices/lib/gpio/include/gpio-x1600.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/devices/lib/gpio/include/gpio-x1600.h Sun Sep 24 01:53:43 2023 +0200 @@ -0,0 +1,171 @@ +/* + * GPIO driver for Ingenic X1600. + * (See below for additional copyright and licensing notices.) + * + * Copyright (C) 2017, 2018, 2023 Paul Boddie + * + * 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 + * + * + * Subject to other copyrights, being derived from the bcm2835.cc and + * omap.cc GPIO driver implementations. + * + * This file is part of TUD:OS and distributed under the terms of the + * GNU General Public License 2. + * Please see the COPYING-GPL-2 file for details. + */ + +#pragma once + +#include +#include +#include +#include "gpio.h" + + + +#ifdef __cplusplus + +#include + +// GPIO device control. + +class Gpio_x1600_irq_pin : public Hw::Gpio_irq_pin +{ + unsigned _pin; + Hw::Register_block<32> _regs; + + // Convenience method for obtaining the bit corresponding to a pin. + + l4_uint32_t _pin_bit(unsigned pin) + { return 1 << (pin & 31); } + + void write_reg_pin(unsigned reg); + +public: + Gpio_x1600_irq_pin(unsigned pin, Hw::Register_block<32> const ®s); + + void do_mask(); + void do_unmask(); + bool do_set_mode(unsigned mode); + int clear(); + bool enabled(); +}; + +class Gpio_x1600_chip : public Hw::Gpio_chip +{ +private: + Hw::Register_block<32> _regs; + + l4_addr_t _start, _end; + unsigned _nr_pins; + l4_uint32_t _pull_ups, _pull_downs; + + // Convenience method for obtaining the bit corresponding to a pin. + + l4_uint32_t _pin_bit(unsigned pin) + { return 1 << (pin & 31); } + + // Convenience method for obtaining the bit position of a pin. + + unsigned _pin_shift(unsigned pin) + { return pin % 32; } + + // Permit only "aligned" accesses to registers. + + unsigned _reg_offset_check(unsigned pin_offset) const + { + switch (pin_offset) + { + case 0: + return 0; + + default: + throw -L4_EINVAL; + } + } + +public: + Gpio_x1600_chip(l4_addr_t start, l4_addr_t end, + unsigned nr_pins, + l4_uint32_t pull_ups, l4_uint32_t pull_downs); + + // Obtain the number of pins. + + unsigned nr_pins() const { return _nr_pins; } + + // Unnecessary operations. + + void request(unsigned) {} + void free(unsigned) {} + + // Configuration methods. + + void setup(unsigned pin, unsigned mode, int value = 0); + void config_pull(unsigned pin, unsigned mode); + void config_pad(unsigned pin, unsigned func, unsigned value); + void config_get(unsigned pin, unsigned reg, unsigned *value); + void config_pad_get(unsigned pin, unsigned *func, unsigned *value); + + // Multiple pin configuration methods. + + void multi_setup(Pin_slice const &mask, unsigned mode, unsigned outvalues = 0); + void multi_config_pad(Pin_slice const &mask, unsigned func, unsigned value = 0); + void multi_set(Pin_slice const &mask, unsigned data); + unsigned multi_get(unsigned offset); + + // IRQ pin configuration. + + Hw::Gpio_irq_pin *get_irq(unsigned pin); + + // Pin/port data methods. + + int get(unsigned pin); + void set(unsigned pin, int value); + +private: + void config(unsigned pin, unsigned mode); +}; + +#endif /* __cplusplus */ + + + +/* C language interface. */ + +EXTERN_C_BEGIN + +void *x1600_gpio_init(l4_addr_t start, l4_addr_t end, unsigned pins, + l4_uint32_t pull_ups, l4_uint32_t pull_downs); + +void x1600_gpio_setup(void *gpio, unsigned pin, unsigned mode, int value); +void x1600_gpio_config_pull(void *gpio, unsigned pin, unsigned mode); +void x1600_gpio_config_pad(void *gpio, unsigned pin, unsigned func, unsigned value); +void x1600_gpio_config_get(void *gpio, unsigned pin, unsigned reg, unsigned *value); +void x1600_gpio_config_pad_get(void *gpio, unsigned pin, unsigned *func, unsigned *value); + +void x1600_gpio_multi_setup(void *gpio, Pin_slice const *mask, unsigned mode, unsigned outvalues); +void x1600_gpio_multi_config_pad(void *gpio, Pin_slice const *mask, unsigned func, unsigned value); +void x1600_gpio_multi_set(void *gpio, Pin_slice const *mask, unsigned data); +unsigned x1600_gpio_multi_get(void *gpio, unsigned offset); + +int x1600_gpio_get(void *gpio, unsigned pin); +void x1600_gpio_set(void *gpio, unsigned pin, int value); + +void *x1600_gpio_get_irq(void *gpio, unsigned pin); +bool x1600_gpio_irq_set_mode(void *gpio_irq, unsigned mode); + +EXTERN_C_END diff -r 40a23a1e92f8 -r aec11102da9d pkg/devices/lib/gpio/include/gpio.h --- a/pkg/devices/lib/gpio/include/gpio.h Fri Sep 22 21:56:34 2023 +0200 +++ b/pkg/devices/lib/gpio/include/gpio.h Sun Sep 24 01:53:43 2023 +0200 @@ -2,7 +2,7 @@ * GPIO driver definitions. * (See below for additional copyright and licensing notices.) * - * Copyright (C) 2017, 2018 Paul Boddie + * Copyright (C) 2017, 2018, 2023 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -80,6 +80,7 @@ { Function_gpio, Function_alt, + Function_irq, }; virtual void request(unsigned pin) = 0; @@ -123,6 +124,14 @@ virtual void config_get(unsigned pin, unsigned func, unsigned *value) = 0; /** + * \brief Get platform specific pad configuration. + * \param pin the pin to configure. + * \param func a platform specific sub-function of a pad to be configured + * \param value a platform specific value for the given sub-function. + */ + virtual void config_pad_get(unsigned pin, unsigned *func, unsigned *value) = 0; + + /** * \brief Get the value of the given pin (generic API). * \param pin the pin to read the value from. */ @@ -210,6 +219,7 @@ { Function_gpio, Function_alt, + Function_irq, }; #endif /* __cplusplus */ diff -r 40a23a1e92f8 -r aec11102da9d pkg/devices/lib/gpio/src/Makefile --- a/pkg/devices/lib/gpio/src/Makefile Fri Sep 22 21:56:34 2023 +0200 +++ b/pkg/devices/lib/gpio/src/Makefile Sun Sep 24 01:53:43 2023 +0200 @@ -4,7 +4,7 @@ TARGET = libgpio.o.a libgpio.o.so PC_FILENAME := libdrivers-gpio -SRC_CC := jz4730.cc jz4740.cc jz4780.cc +SRC_CC := jz4730.cc jz4740.cc jz4780.cc x1600.cc PRIVATE_INCDIR += $(PKGDIR)/lib/gpio/include diff -r 40a23a1e92f8 -r aec11102da9d pkg/devices/lib/gpio/src/jz4730.cc --- a/pkg/devices/lib/gpio/src/jz4730.cc Fri Sep 22 21:56:34 2023 +0200 +++ b/pkg/devices/lib/gpio/src/jz4730.cc Sun Sep 24 01:53:43 2023 +0200 @@ -2,7 +2,7 @@ * GPIO driver for Ingenic JZ4730. * (See below for additional copyright and licensing notices.) * - * Copyright (C) 2017, 2018 Paul Boddie + * Copyright (C) 2017, 2018, 2023 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -46,6 +46,7 @@ Irq_detect_lower = 0x018, // PxGPDLR Irq_detect_upper = 0x01c, // PxGPDUR Irq_enable = 0x020, // PxGPIER + Irq_mask = 0x024, // PxGPIM Irq_flag = 0x028, // PxGPFR }; @@ -70,8 +71,6 @@ } } - - // IRQ control for each GPIO pin. Gpio_jz4730_irq_pin::Gpio_jz4730_irq_pin(unsigned pin, Hw::Register_block<32> const ®s) @@ -129,6 +128,9 @@ { // Set the interrupt bit in the PxGPIER register. + /* NOTE: This should use the Irq_mask/PxGPIM register, with the enable + register actually setting IRQ mode. */ + clear_reg_pin(Irq_enable); } @@ -137,6 +139,9 @@ // Set the interrupt bit in the PxGPIER register, first also clearing the // flag bit in the PxGPFR register to allow interrupts to be delivered. + /* NOTE: This should use the Irq_mask/PxGPIM register, with the enable + register actually setting IRQ mode. */ + clear_reg_pin(Irq_flag); set_reg_pin(Irq_enable); } @@ -306,19 +311,23 @@ Gpio_jz4730_chip::_config(unsigned bitmap, unsigned mode) { uint32_t upper_mask, lower_mask; - unsigned bitmap_values = bitmap; switch (mode) { case Input: case Irq: - // Clear the direction flags below. - bitmap_values = 0; - // Fall through to the next case to complete the operation. + // Clear the direction flags. + _regs[Port_direction] = _regs[Port_direction] & ~bitmap; + + // Clear the port function for the bits. + _get_bitmaps(bitmap, 3, &upper_mask, &lower_mask); + _regs[Port_function_upper] = (_regs[Port_function_upper] & ~upper_mask); + _regs[Port_function_lower] = (_regs[Port_function_lower] & ~lower_mask); + break; case Output: - // Clear the flags if set immediately above; otherwise, set them. - _regs[Port_direction] = (_regs[Port_direction] & ~bitmap) | bitmap_values; + // Set the direction flags. + _regs[Port_direction] = _regs[Port_direction] | bitmap; // Clear the port function for the bits. _get_bitmaps(bitmap, 3, &upper_mask, &lower_mask); @@ -382,12 +391,15 @@ { case Hw::Gpio_chip::Function_alt: _get_bitmaps(bitmap, value, &upper, &lower); - // Fall through to the next case to complete the operation. + _get_bitmaps(bitmap, 3, &upper_mask, &lower_mask); + _regs[Port_function_upper] = (_regs[Port_function_upper] & ~upper_mask) | upper; + _regs[Port_function_lower] = (_regs[Port_function_lower] & ~lower_mask) | lower; + break; case Hw::Gpio_chip::Function_gpio: _get_bitmaps(bitmap, 3, &upper_mask, &lower_mask); - _regs[Port_function_upper] = (_regs[Port_function_upper] & ~upper_mask) | upper; - _regs[Port_function_lower] = (_regs[Port_function_lower] & ~lower_mask) | lower; + _regs[Port_function_upper] = _regs[Port_function_upper] & ~upper_mask; + _regs[Port_function_lower] = _regs[Port_function_lower] & ~lower_mask; break; default: @@ -406,6 +418,62 @@ *value = (_regs[reg] >> _pin_shift(pin)) & 1; } +// Return function and function-specific configuration for a pin. + +void +Gpio_jz4730_chip::config_pad_get(unsigned pin, unsigned *func, unsigned *value) +{ + unsigned detect, direction, pin_function, interrupt; + + // Get the pin function using the awkward register pairing. + + _get_pin_value(pin, Port_function_upper, Port_function_lower, &pin_function); + + if (pin_function) + { + *func = Hw::Gpio_chip::Function_alt; + *value = pin_function; + return; + } + + config_get(pin, Irq_enable, &interrupt); + + if (interrupt) + { + _get_pin_value(pin, Irq_detect_upper, Irq_detect_lower, &detect); + + *func = Hw::Gpio_chip::Function_irq; + + switch (detect) + { + case 0: *value = L4_IRQ_F_LEVEL_LOW; break; + case 1: *value = L4_IRQ_F_LEVEL_HIGH; break; + case 2: *value = L4_IRQ_F_NEG_EDGE; break; + default: case 3: *value = L4_IRQ_F_POS_EDGE; break; + } + return; + } + + config_get(pin, Port_direction, &direction); + + *func = Hw::Gpio_chip::Function_gpio; + *value = direction ? Output : Input; +} + +void +Gpio_jz4730_chip::_get_pin_value(unsigned pin, uint32_t reg_upper, + uint32_t reg_lower, unsigned *value) +{ + uint8_t pin_out; + + if (pin >= _nr_pins) + throw -L4_EINVAL; + + uint32_t reg = _select_bank_for_pin(reg_upper, reg_lower, pin, &pin_out); + + *value = (_regs[reg] & (3 << (pin_out << 1))) >> (pin_out << 1); +} + // Obtain an IRQ abstraction for a pin. Hw::Gpio_irq_pin * @@ -473,6 +541,11 @@ static_cast(gpio)->config_get(pin, reg, value); } +void jz4730_gpio_config_pad_get(void *gpio, unsigned pin, unsigned *func, unsigned *value) +{ + static_cast(gpio)->config_pad_get(pin, func, value); +} + void jz4730_gpio_multi_setup(void *gpio, Pin_slice const *mask, unsigned mode, unsigned outvalues) { static_cast(gpio)->multi_setup(*mask, mode, outvalues); diff -r 40a23a1e92f8 -r aec11102da9d pkg/devices/lib/gpio/src/jz4740.cc --- a/pkg/devices/lib/gpio/src/jz4740.cc Fri Sep 22 21:56:34 2023 +0200 +++ b/pkg/devices/lib/gpio/src/jz4740.cc Sun Sep 24 01:53:43 2023 +0200 @@ -2,7 +2,7 @@ * GPIO driver for Ingenic JZ4740. * (See below for additional copyright and licensing notices.) * - * Copyright (C) 2017, 2018 Paul Boddie + * Copyright (C) 2017, 2018, 2023 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -339,6 +339,38 @@ *value = (_regs[reg] >> _pin_shift(pin)) & 1; } +// Return function and function-specific configuration for a pin. + +void +Gpio_jz4740_chip::config_pad_get(unsigned pin, unsigned *func, unsigned *value) +{ + unsigned direction, function, select, trigger; + + config_get(pin, Port_function, &function); + config_get(pin, Port_select, &select); + config_get(pin, Port_dir, &direction); + + if (function) + { + *func = Hw::Gpio_chip::Function_alt; + *value = select; + return; + } + + if (select) + { + config_get(pin, Port_trigger, &trigger); + + *func = Hw::Gpio_chip::Function_irq; + *value = (trigger ? (direction ? L4_IRQ_F_POS_EDGE : L4_IRQ_F_NEG_EDGE) + : (direction ? L4_IRQ_F_LEVEL_HIGH : L4_IRQ_F_LEVEL_LOW)); + return; + } + + *func = Hw::Gpio_chip::Function_gpio; + *value = direction ? Input : Output; +} + // Obtain an IRQ abstraction for a pin. Hw::Gpio_irq_pin * @@ -406,6 +438,11 @@ static_cast(gpio)->config_get(pin, reg, value); } +void jz4740_gpio_config_pad_get(void *gpio, unsigned pin, unsigned *func, unsigned *value) +{ + static_cast(gpio)->config_pad_get(pin, func, value); +} + void jz4740_gpio_multi_setup(void *gpio, Pin_slice const *mask, unsigned mode, unsigned outvalues) { static_cast(gpio)->multi_setup(*mask, mode, outvalues); diff -r 40a23a1e92f8 -r aec11102da9d pkg/devices/lib/gpio/src/jz4780.cc --- a/pkg/devices/lib/gpio/src/jz4780.cc Fri Sep 22 21:56:34 2023 +0200 +++ b/pkg/devices/lib/gpio/src/jz4780.cc Sun Sep 24 01:53:43 2023 +0200 @@ -2,7 +2,7 @@ * GPIO driver for Ingenic JZ4780. * (See below for additional copyright and licensing notices.) * - * Copyright (C) 2017 Paul Boddie + * Copyright (C) 2017, 2023 Paul Boddie * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -47,20 +47,20 @@ */ enum Regs -{ +{ Pin_level = 0x000, // PxPIN (read-only) - + Port_int = 0x010, // PxINT Port_int_set = 0x014, // PxINTS Port_int_clear = 0x018, // PxINTC - + Irq_mask = 0x020, // PxMSK (for PxINT == 1) Irq_mask_set = 0x024, // PxMSKS Irq_mask_clear = 0x028, // PxMSKC Port_gpio = 0x020, // PxMSK (for PxINT == 0) Port_gpio_set = 0x024, // PxMSKS Port_gpio_clear = 0x028, // PxMSKC - + Port_trigger = 0x030, // PxPAT1 (for PxINT == 1) Port_trigger_set = 0x034, // PxPAT1S Port_trigger_clear = 0x038, // PxPAT1C @@ -70,7 +70,7 @@ Port_group1 = 0x030, // PxPAT1 (for PxINT == 0, PxMSK == 0) Port_group1_set = 0x034, // PxPAT1S Port_group1_clear = 0x038, // PxPAT1C - + Port_level = 0x040, // PxPAT0 (for PxINT == 1) Port_level_set = 0x044, // PxPAT0S Port_level_clear = 0x048, // PxPAT0C @@ -80,10 +80,10 @@ Port_group0 = 0x040, // PxPAT0 (for PxINT == 0, PxMSK == 0) Port_group0_set = 0x044, // PxPAT0S Port_group0_clear = 0x048, // PxPAT0C - + Irq_flag = 0x050, // PxFLG (read-only) Irq_flag_clear = 0x058, // PxFLGC - + Pull_disable = 0x070, // PxPE Pull_disable_set = 0x074, // PxPES Pull_disable_clear = 0x078, // PxPEC @@ -317,7 +317,7 @@ if (pin >= _nr_pins) throw -L4_EINVAL; - if (value > 1) + if (value > 3) throw -L4_EINVAL; switch (func) @@ -354,6 +354,45 @@ *value = (_regs[reg] >> _pin_shift(pin)) & 1; } +// Return function and function-specific configuration for a pin. + +void +Gpio_jz4780_chip::config_pad_get(unsigned pin, unsigned *func, unsigned *value) +{ + unsigned direction, gpio, group0, group1, interrupt, level, trigger; + + config_get(pin, Port_int, &interrupt); + + if (interrupt) + { + config_get(pin, Port_trigger, &trigger); + config_get(pin, Port_level, &level); + + *func = Hw::Gpio_chip::Function_irq; + *value = (trigger ? (level ? L4_IRQ_F_POS_EDGE : L4_IRQ_F_NEG_EDGE) + : (level ? L4_IRQ_F_LEVEL_HIGH : L4_IRQ_F_LEVEL_LOW)); + return; + } + + config_get(pin, Port_gpio, &gpio); + + if (gpio) + { + config_get(pin, Port_dir, &direction); + + *func = Hw::Gpio_chip::Function_gpio; + *value = direction ? Input : Output; + return; + } + + *func = Hw::Gpio_chip::Function_alt; + + config_get(pin, Port_group0, &group0); + config_get(pin, Port_group1, &group1); + + *value = (group1 << 1) | group0; +} + // Obtain an IRQ abstraction for a pin. Hw::Gpio_irq_pin * @@ -417,6 +456,11 @@ static_cast(gpio)->config_get(pin, reg, value); } +void jz4780_gpio_config_pad_get(void *gpio, unsigned pin, unsigned *func, unsigned *value) +{ + static_cast(gpio)->config_pad_get(pin, func, value); +} + void jz4780_gpio_multi_setup(void *gpio, Pin_slice const *mask, unsigned mode, unsigned outvalues) { static_cast(gpio)->multi_setup(*mask, mode, outvalues); diff -r 40a23a1e92f8 -r aec11102da9d pkg/devices/lib/gpio/src/x1600.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/devices/lib/gpio/src/x1600.cc Sun Sep 24 01:53:43 2023 +0200 @@ -0,0 +1,519 @@ +/* + * GPIO driver for Ingenic X1600. + * (See below for additional copyright and licensing notices.) + * + * Copyright (C) 2017, 2023 Paul Boddie + * + * 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 + * + * + * Subject to other copyrights, being derived from the bcm2835.cc and + * omap.cc GPIO driver implementations. + * + * This file is part of TUD:OS and distributed under the terms of the + * GNU General Public License 2. + * Please see the COPYING-GPL-2 file for details. + */ + +#include +#include +#include + +#include "gpio-x1600.h" + +/* +GPIO register offsets (x in A..D). + +Register summary: + +PxINT 0 (function/GPIO) 1 (interrupt) +PxMSK 0 (function) 1 (GPIO) 0 (IRQ enable)/1 (IRQ disable) +PxPAT1 0 (function 0/1) 1 (function 2/3) 0 (output) 1 (input) 0 (level trigger) 1 (edge trigger) +PxPAT0 0 (function 0) 0 (function 2) 0 (output value 0) 0 (low level) 0 (falling edge) + 1 (function 1) 1 (function 3) 1 (output value 1) 1 (high level) 1 (rising edge) +*/ + +enum Regs +{ + Pin_level = 0x000, // PxPINL (read-only) + + Port_int = 0x010, // PxINT + Port_int_set = 0x014, // PxINTS + Port_int_clear = 0x018, // PxINTC + + Irq_mask = 0x020, // PxMSK (for PxINT == 1) + Irq_mask_set = 0x024, // PxMSKS + Irq_mask_clear = 0x028, // PxMSKC + Port_gpio = 0x020, // PxMSK (for PxINT == 0) + Port_gpio_set = 0x024, // PxMSKS + Port_gpio_clear = 0x028, // PxMSKC + + Port_trigger = 0x030, // PxPAT1 (for PxINT == 1) + Port_trigger_set = 0x034, // PxPAT1S + Port_trigger_clear = 0x038, // PxPAT1C + Port_dir = 0x030, // PxPAT1 (for PxINT == 0, PxMSK == 1) + Port_dir_set = 0x034, // PxPAT1S + Port_dir_clear = 0x038, // PxPAT1C + Port_group1 = 0x030, // PxPAT1 (for PxINT == 0, PxMSK == 0) + Port_group1_set = 0x034, // PxPAT1S + Port_group1_clear = 0x038, // PxPAT1C + + Port_level = 0x040, // PxPAT0 (for PxINT == 1) + Port_level_set = 0x044, // PxPAT0S + Port_level_clear = 0x048, // PxPAT0C + Port_data = 0x040, // PxPAT0 (for PxINT == 0, PxMSK == 1, PxPAT1 == 0) + Port_data_set = 0x044, // PxPAT0S + Port_data_clear = 0x048, // PxPAT0C + Port_group0 = 0x040, // PxPAT0 (for PxINT == 0, PxMSK == 0) + Port_group0_set = 0x044, // PxPAT0S + Port_group0_clear = 0x048, // PxPAT0C + + Irq_flag = 0x050, // PxFLG (read-only) + Irq_flag_clear = 0x058, // PxFLGC + + // Only the following registers differ from the JZ4780. The dual-edge + // registers being added to the X1600, with the pull-up/down registers being + // relocated. + + Pull_edge = 0x070, // PxEDG + Pull_edge_set = 0x074, // PxEDGS + Pull_edge_clear = 0x078, // PxEDGC + + Pull_disable = 0x080, // PxPE + Pull_disable_set = 0x084, // PxPES + Pull_disable_clear = 0x088, // PxPEC + + // The shadow port Z is available at offset 0x700 and supports the INTS, INTC, + // MSKS, MSKC, PAT1S, PAT1C, PAT0S, PAT0C registers, along with the following. + + Shadow_transfer = 0x0f0, // PzGID2LD +}; + + + +// IRQ control for each GPIO pin. + +Gpio_x1600_irq_pin::Gpio_x1600_irq_pin(unsigned pin, Hw::Register_block<32> const ®s) +: _pin(pin), _regs(regs) +{} + +void +Gpio_x1600_irq_pin::write_reg_pin(unsigned reg) +{ + // Write the pin bit to the register, setting or clearing the pin + // depending on the register chosen. + + _regs[reg] = _pin_bit(_pin); +} + +void Gpio_x1600_irq_pin::do_mask() +{ + // Set the interrupt bit in the PxIM register. + + write_reg_pin(Irq_mask_set); +} + +void Gpio_x1600_irq_pin::do_unmask() +{ + // Clear the interrupt bit in the PxIM register, first also clearing the + // flag bit in the PxFLG register to allow interrupts to be delivered. + + write_reg_pin(Irq_flag_clear); + write_reg_pin(Irq_mask_clear); +} + +bool Gpio_x1600_irq_pin::do_set_mode(unsigned mode) +{ + // Standard comment found for this method: + // this operation touches multiple mmio registers and is thus + // not atomic, that's why we first mask the IRQ and if it was + // enabled we unmask it after we have changed the mode + + /* NOTE: The X1600 provides a special port Z that allows changes to be made + and then committed atomically using PzGID2LD. This is not currently + used. */ + + if (enabled()) + do_mask(); + + // Do the PxINT, PxPAT1 and PxPAT0 configuration. + + switch(mode) + { + case L4_IRQ_F_LEVEL_HIGH: + write_reg_pin(Port_int_set); + write_reg_pin(Port_trigger_clear); + write_reg_pin(Port_level_set); + break; + case L4_IRQ_F_LEVEL_LOW: + write_reg_pin(Port_int_set); + write_reg_pin(Port_trigger_clear); + write_reg_pin(Port_level_clear); + break; + case L4_IRQ_F_POS_EDGE: + write_reg_pin(Port_int_set); + write_reg_pin(Port_trigger_set); + write_reg_pin(Port_level_set); + break; + case L4_IRQ_F_NEG_EDGE: + write_reg_pin(Port_int_set); + write_reg_pin(Port_trigger_set); + write_reg_pin(Port_level_clear); + break; + + default: + return false; + } + + if (enabled()) + do_unmask(); + + return true; +} + +int Gpio_x1600_irq_pin::clear() +{ + // Obtain the flag status for the pin, clearing it if set. + + l4_uint32_t e = _regs[Irq_flag] & (1UL << _pin); + if (e) + _regs[Irq_flag_clear] = e; + + return (e >> _pin); +} + +bool Gpio_x1600_irq_pin::enabled() +{ + return true; +} + + + +// Initialise the GPIO controller. + +Gpio_x1600_chip::Gpio_x1600_chip(l4_addr_t start, l4_addr_t end, + unsigned nr_pins, + l4_uint32_t pull_ups, l4_uint32_t pull_downs) +: _start(start), _end(end), + _nr_pins(nr_pins), + _pull_ups(pull_ups), _pull_downs(pull_downs) +{ + _regs = new Hw::Mmio_register_block<32>(_start); +} + +// Return the value of a pin. + +int +Gpio_x1600_chip::get(unsigned pin) +{ + if (pin >= _nr_pins) + throw -L4_EINVAL; + + l4_uint32_t val = _regs[Pin_level]; + return (val >> _pin_shift(pin)) & 1; +} + +// Return multiple pin values. + +unsigned +Gpio_x1600_chip::multi_get(unsigned offset) +{ + _reg_offset_check(offset); + return _regs[Pin_level]; +} + +// Set the value of a pin. + +void +Gpio_x1600_chip::set(unsigned pin, int value) +{ + if (pin >= _nr_pins) + throw -L4_EINVAL; + + l4_uint32_t reg_set = value ? Port_data_set : Port_data_clear; + _regs[reg_set] = _pin_bit(pin); +} + +// Set multiple pin values. + +void +Gpio_x1600_chip::multi_set(Pin_slice const &mask, unsigned data) +{ + _reg_offset_check(mask.offset); + if (mask.mask & data) + _regs[Port_data_set] = (mask.mask & data); + if (mask.mask & ~data) + _regs[Port_data_clear] = (mask.mask & ~data); +} + +// Set a pin up with the given mode and value (if appropriate). + +void +Gpio_x1600_chip::setup(unsigned pin, unsigned mode, int value) +{ + if (pin >= _nr_pins) + throw -L4_EINVAL; + + config(pin, mode); + + if (mode == Output) + set(pin, value); +} + +// Configuration of a pin using the generic input/output/IRQ mode. + +void +Gpio_x1600_chip::config(unsigned pin, unsigned mode) +{ + switch (mode) + { + case Input: + _regs[Port_int_clear] = _pin_bit(pin); + _regs[Port_gpio_set] = _pin_bit(pin); + _regs[Port_dir_set] = _pin_bit(pin); + break; + case Output: + _regs[Port_int_clear] = _pin_bit(pin); + _regs[Port_gpio_set] = _pin_bit(pin); + _regs[Port_dir_clear] = _pin_bit(pin); + break; + case Irq: + _regs[Port_int_set] = _pin_bit(pin); + // Other details depend on the actual trigger mode. + break; + default: + break; + } +} + +// Pull-up/down configuration for a pin. + +void +Gpio_x1600_chip::config_pull(unsigned pin, unsigned mode) +{ + if (pin >= _nr_pins) + throw -L4_EINVAL; + + switch (mode) + { + case Pull_none: + _regs[Pull_disable_set] = _pin_bit(pin); + break; + case Pull_down: + if (_pin_bit(pin) & _pull_downs) + _regs[Pull_disable_clear] = _pin_bit(pin); + break; + case Pull_up: + if (_pin_bit(pin) & _pull_ups) + _regs[Pull_disable_clear] = _pin_bit(pin); + break; + default: + // Invalid pull-up/down mode for pin. + throw -L4_EINVAL; + } +} + +// Pin function configuration. + +void +Gpio_x1600_chip::config_pad(unsigned pin, unsigned func, unsigned value) +{ + if (pin >= _nr_pins) + throw -L4_EINVAL; + + if (value > 3) + throw -L4_EINVAL; + + switch (func) + { + // Support two different outputs. + + case Hw::Gpio_chip::Function_gpio: + _regs[Port_int_clear] = _pin_bit(pin); + _regs[Port_gpio_set] = _pin_bit(pin); + _regs[value & 1 ? Port_data_set : Port_data_clear] = _pin_bit(pin); + break; + + // Support four different device functions. + + case Hw::Gpio_chip::Function_alt: + _regs[Port_int_clear] = _pin_bit(pin); + _regs[Port_gpio_clear] = _pin_bit(pin); + _regs[value & 2 ? Port_group1_set : Port_group1_clear] = _pin_bit(pin); + _regs[value & 1 ? Port_group0_set : Port_group0_clear] = _pin_bit(pin); + break; + default: + throw -L4_EINVAL; + } +} + +// Obtain a pin's configuration from a register in the supplied value. + +void +Gpio_x1600_chip::config_get(unsigned pin, unsigned reg, unsigned *value) +{ + if (pin >= _nr_pins) + throw -L4_EINVAL; + + *value = (_regs[reg] >> _pin_shift(pin)) & 1; +} + +// Return function and function-specific configuration for a pin. + +void +Gpio_x1600_chip::config_pad_get(unsigned pin, unsigned *func, unsigned *value) +{ + unsigned direction, gpio, group0, group1, interrupt, level, trigger; + + config_get(pin, Port_int, &interrupt); + + if (interrupt) + { + config_get(pin, Port_trigger, &trigger); + config_get(pin, Port_level, &level); + + *func = Hw::Gpio_chip::Function_irq; + *value = (trigger ? (level ? L4_IRQ_F_POS_EDGE : L4_IRQ_F_NEG_EDGE) + : (level ? L4_IRQ_F_LEVEL_HIGH : L4_IRQ_F_LEVEL_LOW)); + return; + } + + config_get(pin, Port_gpio, &gpio); + + if (gpio) + { + config_get(pin, Port_dir, &direction); + + *func = Hw::Gpio_chip::Function_gpio; + *value = direction ? Input : Output; + return; + } + + *func = Hw::Gpio_chip::Function_alt; + + config_get(pin, Port_group0, &group0); + config_get(pin, Port_group1, &group1); + + *value = (group1 << 1) | group0; +} + +// Obtain an IRQ abstraction for a pin. + +Hw::Gpio_irq_pin * +Gpio_x1600_chip::get_irq(unsigned pin) +{ + if (pin >= _nr_pins) + throw -L4_EINVAL; + + return new Gpio_x1600_irq_pin(pin, _regs); +} + +// Pin function configuration for multiple pins. + +void +Gpio_x1600_chip::multi_config_pad(Pin_slice const &mask, unsigned func, unsigned val) +{ + unsigned m = mask.mask; + for (unsigned pin = mask.offset; pin < _nr_pins; ++pin, m >>= 1) + if (m & 1) + config_pad(pin, func, val); +} + +// Set up multiple pins with the given mode. + +void +Gpio_x1600_chip::multi_setup(Pin_slice const &mask, unsigned mode, unsigned outvalues) +{ + unsigned m = mask.mask; + for (unsigned pin = mask.offset; pin < _nr_pins; ++pin, m >>= 1, outvalues >>= 1) + if (m & 1) + setup(pin, mode, outvalues & 1); +} + + + +// C language interface functions. + +void *x1600_gpio_init(l4_addr_t start, l4_addr_t end, unsigned pins, + l4_uint32_t pull_ups, l4_uint32_t pull_downs) +{ + return (void *) new Gpio_x1600_chip(start, end, pins, pull_ups, pull_downs); +} + +void x1600_gpio_setup(void *gpio, unsigned pin, unsigned mode, int value) +{ + static_cast(gpio)->setup(pin, mode, value); +} + +void x1600_gpio_config_pull(void *gpio, unsigned pin, unsigned mode) +{ + static_cast(gpio)->config_pull(pin, mode); +} + +void x1600_gpio_config_pad(void *gpio, unsigned pin, unsigned func, unsigned value) +{ + static_cast(gpio)->config_pad(pin, func, value); +} + +void x1600_gpio_config_get(void *gpio, unsigned pin, unsigned reg, unsigned *value) +{ + static_cast(gpio)->config_get(pin, reg, value); +} + +void x1600_gpio_config_pad_get(void *gpio, unsigned pin, unsigned *func, unsigned *value) +{ + static_cast(gpio)->config_pad_get(pin, func, value); +} + +void x1600_gpio_multi_setup(void *gpio, Pin_slice const *mask, unsigned mode, unsigned outvalues) +{ + static_cast(gpio)->multi_setup(*mask, mode, outvalues); +} + +void x1600_gpio_multi_config_pad(void *gpio, Pin_slice const *mask, unsigned func, unsigned value) +{ + static_cast(gpio)->multi_config_pad(*mask, func, value); +} + +void x1600_gpio_multi_set(void *gpio, Pin_slice const *mask, unsigned data) +{ + static_cast(gpio)->multi_set(*mask, data); +} + +unsigned x1600_gpio_multi_get(void *gpio, unsigned offset) +{ + return static_cast(gpio)->multi_get(offset); +} + +int x1600_gpio_get(void *gpio, unsigned pin) +{ + return static_cast(gpio)->get(pin); +} + +void x1600_gpio_set(void *gpio, unsigned pin, int value) +{ + static_cast(gpio)->set(pin, value); +} + +void *x1600_gpio_get_irq(void *gpio, unsigned pin) +{ + return (void *) static_cast(gpio)->get_irq(pin); +} + +bool x1600_gpio_irq_set_mode(void *gpio_irq, unsigned mode) +{ + return static_cast(gpio_irq)->do_set_mode(mode); +}