1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/pkg/devices/lib/gpio/src/x1600.cc Sun Sep 24 01:53:43 2023 +0200
1.3 @@ -0,0 +1,519 @@
1.4 +/*
1.5 + * GPIO driver for Ingenic X1600.
1.6 + * (See below for additional copyright and licensing notices.)
1.7 + *
1.8 + * Copyright (C) 2017, 2023 Paul Boddie <paul@boddie.org.uk>
1.9 + *
1.10 + * This program is free software; you can redistribute it and/or
1.11 + * modify it under the terms of the GNU General Public License as
1.12 + * published by the Free Software Foundation; either version 2 of
1.13 + * the License, or (at your option) any later version.
1.14 + *
1.15 + * This program is distributed in the hope that it will be useful,
1.16 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1.17 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1.18 + * GNU General Public License for more details.
1.19 + *
1.20 + * You should have received a copy of the GNU General Public License
1.21 + * along with this program; if not, write to the Free Software
1.22 + * Foundation, Inc., 51 Franklin Street, Fifth Floor,
1.23 + * Boston, MA 02110-1301, USA
1.24 + *
1.25 + *
1.26 + * Subject to other copyrights, being derived from the bcm2835.cc and
1.27 + * omap.cc GPIO driver implementations.
1.28 + *
1.29 + * This file is part of TUD:OS and distributed under the terms of the
1.30 + * GNU General Public License 2.
1.31 + * Please see the COPYING-GPL-2 file for details.
1.32 + */
1.33 +
1.34 +#include <l4/sys/icu.h>
1.35 +#include <l4/util/util.h>
1.36 +#include <l4/devices/hw_mmio_register_block.h>
1.37 +
1.38 +#include "gpio-x1600.h"
1.39 +
1.40 +/*
1.41 +GPIO register offsets (x in A..D).
1.42 +
1.43 +Register summary:
1.44 +
1.45 +PxINT 0 (function/GPIO) 1 (interrupt)
1.46 +PxMSK 0 (function) 1 (GPIO) 0 (IRQ enable)/1 (IRQ disable)
1.47 +PxPAT1 0 (function 0/1) 1 (function 2/3) 0 (output) 1 (input) 0 (level trigger) 1 (edge trigger)
1.48 +PxPAT0 0 (function 0) 0 (function 2) 0 (output value 0) 0 (low level) 0 (falling edge)
1.49 + 1 (function 1) 1 (function 3) 1 (output value 1) 1 (high level) 1 (rising edge)
1.50 +*/
1.51 +
1.52 +enum Regs
1.53 +{
1.54 + Pin_level = 0x000, // PxPINL (read-only)
1.55 +
1.56 + Port_int = 0x010, // PxINT
1.57 + Port_int_set = 0x014, // PxINTS
1.58 + Port_int_clear = 0x018, // PxINTC
1.59 +
1.60 + Irq_mask = 0x020, // PxMSK (for PxINT == 1)
1.61 + Irq_mask_set = 0x024, // PxMSKS
1.62 + Irq_mask_clear = 0x028, // PxMSKC
1.63 + Port_gpio = 0x020, // PxMSK (for PxINT == 0)
1.64 + Port_gpio_set = 0x024, // PxMSKS
1.65 + Port_gpio_clear = 0x028, // PxMSKC
1.66 +
1.67 + Port_trigger = 0x030, // PxPAT1 (for PxINT == 1)
1.68 + Port_trigger_set = 0x034, // PxPAT1S
1.69 + Port_trigger_clear = 0x038, // PxPAT1C
1.70 + Port_dir = 0x030, // PxPAT1 (for PxINT == 0, PxMSK == 1)
1.71 + Port_dir_set = 0x034, // PxPAT1S
1.72 + Port_dir_clear = 0x038, // PxPAT1C
1.73 + Port_group1 = 0x030, // PxPAT1 (for PxINT == 0, PxMSK == 0)
1.74 + Port_group1_set = 0x034, // PxPAT1S
1.75 + Port_group1_clear = 0x038, // PxPAT1C
1.76 +
1.77 + Port_level = 0x040, // PxPAT0 (for PxINT == 1)
1.78 + Port_level_set = 0x044, // PxPAT0S
1.79 + Port_level_clear = 0x048, // PxPAT0C
1.80 + Port_data = 0x040, // PxPAT0 (for PxINT == 0, PxMSK == 1, PxPAT1 == 0)
1.81 + Port_data_set = 0x044, // PxPAT0S
1.82 + Port_data_clear = 0x048, // PxPAT0C
1.83 + Port_group0 = 0x040, // PxPAT0 (for PxINT == 0, PxMSK == 0)
1.84 + Port_group0_set = 0x044, // PxPAT0S
1.85 + Port_group0_clear = 0x048, // PxPAT0C
1.86 +
1.87 + Irq_flag = 0x050, // PxFLG (read-only)
1.88 + Irq_flag_clear = 0x058, // PxFLGC
1.89 +
1.90 + // Only the following registers differ from the JZ4780. The dual-edge
1.91 + // registers being added to the X1600, with the pull-up/down registers being
1.92 + // relocated.
1.93 +
1.94 + Pull_edge = 0x070, // PxEDG
1.95 + Pull_edge_set = 0x074, // PxEDGS
1.96 + Pull_edge_clear = 0x078, // PxEDGC
1.97 +
1.98 + Pull_disable = 0x080, // PxPE
1.99 + Pull_disable_set = 0x084, // PxPES
1.100 + Pull_disable_clear = 0x088, // PxPEC
1.101 +
1.102 + // The shadow port Z is available at offset 0x700 and supports the INTS, INTC,
1.103 + // MSKS, MSKC, PAT1S, PAT1C, PAT0S, PAT0C registers, along with the following.
1.104 +
1.105 + Shadow_transfer = 0x0f0, // PzGID2LD
1.106 +};
1.107 +
1.108 +
1.109 +
1.110 +// IRQ control for each GPIO pin.
1.111 +
1.112 +Gpio_x1600_irq_pin::Gpio_x1600_irq_pin(unsigned pin, Hw::Register_block<32> const ®s)
1.113 +: _pin(pin), _regs(regs)
1.114 +{}
1.115 +
1.116 +void
1.117 +Gpio_x1600_irq_pin::write_reg_pin(unsigned reg)
1.118 +{
1.119 + // Write the pin bit to the register, setting or clearing the pin
1.120 + // depending on the register chosen.
1.121 +
1.122 + _regs[reg] = _pin_bit(_pin);
1.123 +}
1.124 +
1.125 +void Gpio_x1600_irq_pin::do_mask()
1.126 +{
1.127 + // Set the interrupt bit in the PxIM register.
1.128 +
1.129 + write_reg_pin(Irq_mask_set);
1.130 +}
1.131 +
1.132 +void Gpio_x1600_irq_pin::do_unmask()
1.133 +{
1.134 + // Clear the interrupt bit in the PxIM register, first also clearing the
1.135 + // flag bit in the PxFLG register to allow interrupts to be delivered.
1.136 +
1.137 + write_reg_pin(Irq_flag_clear);
1.138 + write_reg_pin(Irq_mask_clear);
1.139 +}
1.140 +
1.141 +bool Gpio_x1600_irq_pin::do_set_mode(unsigned mode)
1.142 +{
1.143 + // Standard comment found for this method:
1.144 + // this operation touches multiple mmio registers and is thus
1.145 + // not atomic, that's why we first mask the IRQ and if it was
1.146 + // enabled we unmask it after we have changed the mode
1.147 +
1.148 + /* NOTE: The X1600 provides a special port Z that allows changes to be made
1.149 + and then committed atomically using PzGID2LD. This is not currently
1.150 + used. */
1.151 +
1.152 + if (enabled())
1.153 + do_mask();
1.154 +
1.155 + // Do the PxINT, PxPAT1 and PxPAT0 configuration.
1.156 +
1.157 + switch(mode)
1.158 + {
1.159 + case L4_IRQ_F_LEVEL_HIGH:
1.160 + write_reg_pin(Port_int_set);
1.161 + write_reg_pin(Port_trigger_clear);
1.162 + write_reg_pin(Port_level_set);
1.163 + break;
1.164 + case L4_IRQ_F_LEVEL_LOW:
1.165 + write_reg_pin(Port_int_set);
1.166 + write_reg_pin(Port_trigger_clear);
1.167 + write_reg_pin(Port_level_clear);
1.168 + break;
1.169 + case L4_IRQ_F_POS_EDGE:
1.170 + write_reg_pin(Port_int_set);
1.171 + write_reg_pin(Port_trigger_set);
1.172 + write_reg_pin(Port_level_set);
1.173 + break;
1.174 + case L4_IRQ_F_NEG_EDGE:
1.175 + write_reg_pin(Port_int_set);
1.176 + write_reg_pin(Port_trigger_set);
1.177 + write_reg_pin(Port_level_clear);
1.178 + break;
1.179 +
1.180 + default:
1.181 + return false;
1.182 + }
1.183 +
1.184 + if (enabled())
1.185 + do_unmask();
1.186 +
1.187 + return true;
1.188 +}
1.189 +
1.190 +int Gpio_x1600_irq_pin::clear()
1.191 +{
1.192 + // Obtain the flag status for the pin, clearing it if set.
1.193 +
1.194 + l4_uint32_t e = _regs[Irq_flag] & (1UL << _pin);
1.195 + if (e)
1.196 + _regs[Irq_flag_clear] = e;
1.197 +
1.198 + return (e >> _pin);
1.199 +}
1.200 +
1.201 +bool Gpio_x1600_irq_pin::enabled()
1.202 +{
1.203 + return true;
1.204 +}
1.205 +
1.206 +
1.207 +
1.208 +// Initialise the GPIO controller.
1.209 +
1.210 +Gpio_x1600_chip::Gpio_x1600_chip(l4_addr_t start, l4_addr_t end,
1.211 + unsigned nr_pins,
1.212 + l4_uint32_t pull_ups, l4_uint32_t pull_downs)
1.213 +: _start(start), _end(end),
1.214 + _nr_pins(nr_pins),
1.215 + _pull_ups(pull_ups), _pull_downs(pull_downs)
1.216 +{
1.217 + _regs = new Hw::Mmio_register_block<32>(_start);
1.218 +}
1.219 +
1.220 +// Return the value of a pin.
1.221 +
1.222 +int
1.223 +Gpio_x1600_chip::get(unsigned pin)
1.224 +{
1.225 + if (pin >= _nr_pins)
1.226 + throw -L4_EINVAL;
1.227 +
1.228 + l4_uint32_t val = _regs[Pin_level];
1.229 + return (val >> _pin_shift(pin)) & 1;
1.230 +}
1.231 +
1.232 +// Return multiple pin values.
1.233 +
1.234 +unsigned
1.235 +Gpio_x1600_chip::multi_get(unsigned offset)
1.236 +{
1.237 + _reg_offset_check(offset);
1.238 + return _regs[Pin_level];
1.239 +}
1.240 +
1.241 +// Set the value of a pin.
1.242 +
1.243 +void
1.244 +Gpio_x1600_chip::set(unsigned pin, int value)
1.245 +{
1.246 + if (pin >= _nr_pins)
1.247 + throw -L4_EINVAL;
1.248 +
1.249 + l4_uint32_t reg_set = value ? Port_data_set : Port_data_clear;
1.250 + _regs[reg_set] = _pin_bit(pin);
1.251 +}
1.252 +
1.253 +// Set multiple pin values.
1.254 +
1.255 +void
1.256 +Gpio_x1600_chip::multi_set(Pin_slice const &mask, unsigned data)
1.257 +{
1.258 + _reg_offset_check(mask.offset);
1.259 + if (mask.mask & data)
1.260 + _regs[Port_data_set] = (mask.mask & data);
1.261 + if (mask.mask & ~data)
1.262 + _regs[Port_data_clear] = (mask.mask & ~data);
1.263 +}
1.264 +
1.265 +// Set a pin up with the given mode and value (if appropriate).
1.266 +
1.267 +void
1.268 +Gpio_x1600_chip::setup(unsigned pin, unsigned mode, int value)
1.269 +{
1.270 + if (pin >= _nr_pins)
1.271 + throw -L4_EINVAL;
1.272 +
1.273 + config(pin, mode);
1.274 +
1.275 + if (mode == Output)
1.276 + set(pin, value);
1.277 +}
1.278 +
1.279 +// Configuration of a pin using the generic input/output/IRQ mode.
1.280 +
1.281 +void
1.282 +Gpio_x1600_chip::config(unsigned pin, unsigned mode)
1.283 +{
1.284 + switch (mode)
1.285 + {
1.286 + case Input:
1.287 + _regs[Port_int_clear] = _pin_bit(pin);
1.288 + _regs[Port_gpio_set] = _pin_bit(pin);
1.289 + _regs[Port_dir_set] = _pin_bit(pin);
1.290 + break;
1.291 + case Output:
1.292 + _regs[Port_int_clear] = _pin_bit(pin);
1.293 + _regs[Port_gpio_set] = _pin_bit(pin);
1.294 + _regs[Port_dir_clear] = _pin_bit(pin);
1.295 + break;
1.296 + case Irq:
1.297 + _regs[Port_int_set] = _pin_bit(pin);
1.298 + // Other details depend on the actual trigger mode.
1.299 + break;
1.300 + default:
1.301 + break;
1.302 + }
1.303 +}
1.304 +
1.305 +// Pull-up/down configuration for a pin.
1.306 +
1.307 +void
1.308 +Gpio_x1600_chip::config_pull(unsigned pin, unsigned mode)
1.309 +{
1.310 + if (pin >= _nr_pins)
1.311 + throw -L4_EINVAL;
1.312 +
1.313 + switch (mode)
1.314 + {
1.315 + case Pull_none:
1.316 + _regs[Pull_disable_set] = _pin_bit(pin);
1.317 + break;
1.318 + case Pull_down:
1.319 + if (_pin_bit(pin) & _pull_downs)
1.320 + _regs[Pull_disable_clear] = _pin_bit(pin);
1.321 + break;
1.322 + case Pull_up:
1.323 + if (_pin_bit(pin) & _pull_ups)
1.324 + _regs[Pull_disable_clear] = _pin_bit(pin);
1.325 + break;
1.326 + default:
1.327 + // Invalid pull-up/down mode for pin.
1.328 + throw -L4_EINVAL;
1.329 + }
1.330 +}
1.331 +
1.332 +// Pin function configuration.
1.333 +
1.334 +void
1.335 +Gpio_x1600_chip::config_pad(unsigned pin, unsigned func, unsigned value)
1.336 +{
1.337 + if (pin >= _nr_pins)
1.338 + throw -L4_EINVAL;
1.339 +
1.340 + if (value > 3)
1.341 + throw -L4_EINVAL;
1.342 +
1.343 + switch (func)
1.344 + {
1.345 + // Support two different outputs.
1.346 +
1.347 + case Hw::Gpio_chip::Function_gpio:
1.348 + _regs[Port_int_clear] = _pin_bit(pin);
1.349 + _regs[Port_gpio_set] = _pin_bit(pin);
1.350 + _regs[value & 1 ? Port_data_set : Port_data_clear] = _pin_bit(pin);
1.351 + break;
1.352 +
1.353 + // Support four different device functions.
1.354 +
1.355 + case Hw::Gpio_chip::Function_alt:
1.356 + _regs[Port_int_clear] = _pin_bit(pin);
1.357 + _regs[Port_gpio_clear] = _pin_bit(pin);
1.358 + _regs[value & 2 ? Port_group1_set : Port_group1_clear] = _pin_bit(pin);
1.359 + _regs[value & 1 ? Port_group0_set : Port_group0_clear] = _pin_bit(pin);
1.360 + break;
1.361 + default:
1.362 + throw -L4_EINVAL;
1.363 + }
1.364 +}
1.365 +
1.366 +// Obtain a pin's configuration from a register in the supplied value.
1.367 +
1.368 +void
1.369 +Gpio_x1600_chip::config_get(unsigned pin, unsigned reg, unsigned *value)
1.370 +{
1.371 + if (pin >= _nr_pins)
1.372 + throw -L4_EINVAL;
1.373 +
1.374 + *value = (_regs[reg] >> _pin_shift(pin)) & 1;
1.375 +}
1.376 +
1.377 +// Return function and function-specific configuration for a pin.
1.378 +
1.379 +void
1.380 +Gpio_x1600_chip::config_pad_get(unsigned pin, unsigned *func, unsigned *value)
1.381 +{
1.382 + unsigned direction, gpio, group0, group1, interrupt, level, trigger;
1.383 +
1.384 + config_get(pin, Port_int, &interrupt);
1.385 +
1.386 + if (interrupt)
1.387 + {
1.388 + config_get(pin, Port_trigger, &trigger);
1.389 + config_get(pin, Port_level, &level);
1.390 +
1.391 + *func = Hw::Gpio_chip::Function_irq;
1.392 + *value = (trigger ? (level ? L4_IRQ_F_POS_EDGE : L4_IRQ_F_NEG_EDGE)
1.393 + : (level ? L4_IRQ_F_LEVEL_HIGH : L4_IRQ_F_LEVEL_LOW));
1.394 + return;
1.395 + }
1.396 +
1.397 + config_get(pin, Port_gpio, &gpio);
1.398 +
1.399 + if (gpio)
1.400 + {
1.401 + config_get(pin, Port_dir, &direction);
1.402 +
1.403 + *func = Hw::Gpio_chip::Function_gpio;
1.404 + *value = direction ? Input : Output;
1.405 + return;
1.406 + }
1.407 +
1.408 + *func = Hw::Gpio_chip::Function_alt;
1.409 +
1.410 + config_get(pin, Port_group0, &group0);
1.411 + config_get(pin, Port_group1, &group1);
1.412 +
1.413 + *value = (group1 << 1) | group0;
1.414 +}
1.415 +
1.416 +// Obtain an IRQ abstraction for a pin.
1.417 +
1.418 +Hw::Gpio_irq_pin *
1.419 +Gpio_x1600_chip::get_irq(unsigned pin)
1.420 +{
1.421 + if (pin >= _nr_pins)
1.422 + throw -L4_EINVAL;
1.423 +
1.424 + return new Gpio_x1600_irq_pin(pin, _regs);
1.425 +}
1.426 +
1.427 +// Pin function configuration for multiple pins.
1.428 +
1.429 +void
1.430 +Gpio_x1600_chip::multi_config_pad(Pin_slice const &mask, unsigned func, unsigned val)
1.431 +{
1.432 + unsigned m = mask.mask;
1.433 + for (unsigned pin = mask.offset; pin < _nr_pins; ++pin, m >>= 1)
1.434 + if (m & 1)
1.435 + config_pad(pin, func, val);
1.436 +}
1.437 +
1.438 +// Set up multiple pins with the given mode.
1.439 +
1.440 +void
1.441 +Gpio_x1600_chip::multi_setup(Pin_slice const &mask, unsigned mode, unsigned outvalues)
1.442 +{
1.443 + unsigned m = mask.mask;
1.444 + for (unsigned pin = mask.offset; pin < _nr_pins; ++pin, m >>= 1, outvalues >>= 1)
1.445 + if (m & 1)
1.446 + setup(pin, mode, outvalues & 1);
1.447 +}
1.448 +
1.449 +
1.450 +
1.451 +// C language interface functions.
1.452 +
1.453 +void *x1600_gpio_init(l4_addr_t start, l4_addr_t end, unsigned pins,
1.454 + l4_uint32_t pull_ups, l4_uint32_t pull_downs)
1.455 +{
1.456 + return (void *) new Gpio_x1600_chip(start, end, pins, pull_ups, pull_downs);
1.457 +}
1.458 +
1.459 +void x1600_gpio_setup(void *gpio, unsigned pin, unsigned mode, int value)
1.460 +{
1.461 + static_cast<Gpio_x1600_chip *>(gpio)->setup(pin, mode, value);
1.462 +}
1.463 +
1.464 +void x1600_gpio_config_pull(void *gpio, unsigned pin, unsigned mode)
1.465 +{
1.466 + static_cast<Gpio_x1600_chip *>(gpio)->config_pull(pin, mode);
1.467 +}
1.468 +
1.469 +void x1600_gpio_config_pad(void *gpio, unsigned pin, unsigned func, unsigned value)
1.470 +{
1.471 + static_cast<Gpio_x1600_chip *>(gpio)->config_pad(pin, func, value);
1.472 +}
1.473 +
1.474 +void x1600_gpio_config_get(void *gpio, unsigned pin, unsigned reg, unsigned *value)
1.475 +{
1.476 + static_cast<Gpio_x1600_chip *>(gpio)->config_get(pin, reg, value);
1.477 +}
1.478 +
1.479 +void x1600_gpio_config_pad_get(void *gpio, unsigned pin, unsigned *func, unsigned *value)
1.480 +{
1.481 + static_cast<Gpio_x1600_chip *>(gpio)->config_pad_get(pin, func, value);
1.482 +}
1.483 +
1.484 +void x1600_gpio_multi_setup(void *gpio, Pin_slice const *mask, unsigned mode, unsigned outvalues)
1.485 +{
1.486 + static_cast<Gpio_x1600_chip *>(gpio)->multi_setup(*mask, mode, outvalues);
1.487 +}
1.488 +
1.489 +void x1600_gpio_multi_config_pad(void *gpio, Pin_slice const *mask, unsigned func, unsigned value)
1.490 +{
1.491 + static_cast<Gpio_x1600_chip *>(gpio)->multi_config_pad(*mask, func, value);
1.492 +}
1.493 +
1.494 +void x1600_gpio_multi_set(void *gpio, Pin_slice const *mask, unsigned data)
1.495 +{
1.496 + static_cast<Gpio_x1600_chip *>(gpio)->multi_set(*mask, data);
1.497 +}
1.498 +
1.499 +unsigned x1600_gpio_multi_get(void *gpio, unsigned offset)
1.500 +{
1.501 + return static_cast<Gpio_x1600_chip *>(gpio)->multi_get(offset);
1.502 +}
1.503 +
1.504 +int x1600_gpio_get(void *gpio, unsigned pin)
1.505 +{
1.506 + return static_cast<Gpio_x1600_chip *>(gpio)->get(pin);
1.507 +}
1.508 +
1.509 +void x1600_gpio_set(void *gpio, unsigned pin, int value)
1.510 +{
1.511 + static_cast<Gpio_x1600_chip *>(gpio)->set(pin, value);
1.512 +}
1.513 +
1.514 +void *x1600_gpio_get_irq(void *gpio, unsigned pin)
1.515 +{
1.516 + return (void *) static_cast<Gpio_x1600_chip *>(gpio)->get_irq(pin);
1.517 +}
1.518 +
1.519 +bool x1600_gpio_irq_set_mode(void *gpio_irq, unsigned mode)
1.520 +{
1.521 + return static_cast<Hw::Gpio_irq_pin *>(gpio_irq)->do_set_mode(mode);
1.522 +}