Landfall

pkg/devices/lib/gpio/src/jz4740.cc

287:8b607f1d98a0
6 months ago Paul Boddie Introduced more generic handling of GPIO abstractions. cpm-library-improvements
     1 /*     2  * GPIO driver for Ingenic JZ4740.     3  * (See below for additional copyright and licensing notices.)     4  *     5  * Copyright (C) 2017, 2018, 2023, 2024 Paul Boddie <paul@boddie.org.uk>     6  *     7  * This program is free software; you can redistribute it and/or     8  * modify it under the terms of the GNU General Public License as     9  * published by the Free Software Foundation; either version 2 of    10  * the License, or (at your option) any later version.    11  *    12  * This program is distributed in the hope that it will be useful,    13  * but WITHOUT ANY WARRANTY; without even the implied warranty of    14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    15  * GNU General Public License for more details.    16  *    17  * You should have received a copy of the GNU General Public License    18  * along with this program; if not, write to the Free Software    19  * Foundation, Inc., 51 Franklin Street, Fifth Floor,    20  * Boston, MA  02110-1301, USA    21  *    22  *    23  * Subject to other copyrights, being derived from the bcm2835.cc and    24  * omap.cc GPIO driver implementations.    25  *    26  * This file is part of TUD:OS and distributed under the terms of the    27  * GNU General Public License 2.    28  * Please see the COPYING-GPL-2 file for details.    29  */    30     31 #include <l4/sys/icu.h>    32 #include <l4/util/util.h>    33 #include <l4/devices/hw_mmio_register_block.h>    34     35 #include "gpio-jz4740.h"    36     37 // GPIO register offsets (x in A..D).    38     39 enum Regs    40 {    41   Pin_level           = 0x000,  // PxPIN (read-only)    42   Port_data           = 0x010,  // PxDAT (read-only)    43   Port_data_set       = 0x014,  // PxDATS    44   Port_data_clear     = 0x018,  // PxDATC    45   Irq_mask            = 0x020,  // PxIM (read-only)    46   Irq_mask_set        = 0x024,  // PxIMS    47   Irq_mask_clear      = 0x028,  // PxIMC    48   Pull_disable        = 0x030,  // PxPE (read-only)    49   Pull_disable_set    = 0x034,  // PxPES    50   Pull_disable_clear  = 0x038,  // PxPEC    51   Port_function       = 0x040,  // PxFUN (read-only)    52   Port_function_set   = 0x044,  // PxFUNS    53   Port_function_clear = 0x048,  // PxFUNC    54   Port_select         = 0x050,  // PxSEL (read-only)    55   Port_select_set     = 0x054,  // PxSELS    56   Port_select_clear   = 0x058,  // PxSELC    57   Port_dir            = 0x060,  // PxDIR (read-only)    58   Port_dir_set        = 0x064,  // PxDIRS    59   Port_dir_clear      = 0x068,  // PxDIRC    60   Port_trigger        = 0x070,  // PxTRG (read-only)    61   Port_trigger_set    = 0x074,  // PxTRGS    62   Port_trigger_clear  = 0x078,  // PxTRGC    63   Irq_flag            = 0x080,  // PxFLG (read-only)    64   Irq_flag_clear      = 0x084,  // PxFLGC    65 };    66     67     68     69 // JZ4740 pull-up/down configuration.    70     71 static struct gpio_port gpio_ports[] = {    72   {0xffffffff, 0x00000000},    73   {0xffffffff, 0x00000000},    74   {0x7fffffff, 0x00000000},    75   {0xdeffffff, 0x00000000},    76 };    77     78     79     80 // IRQ control for each GPIO pin.    81     82 Gpio_jz4740_irq_pin::Gpio_jz4740_irq_pin(unsigned pin, Hw::Register_block<32> const &regs)    83 : _pin(pin), _regs(regs)    84 {}    85     86 void    87 Gpio_jz4740_irq_pin::write_reg_pin(unsigned reg)    88 {    89   // Write the pin bit to the register, setting or clearing the pin    90   // depending on the register chosen.    91     92   _regs[reg] = _pin_bit(_pin);    93 }    94     95 void Gpio_jz4740_irq_pin::do_mask()    96 {    97   // Set the interrupt bit in the PxIM register.    98     99   write_reg_pin(Irq_mask_set);   100 }   101    102 void Gpio_jz4740_irq_pin::do_unmask()   103 {   104   // Clear the interrupt bit in the PxIM register, first also clearing the   105   // flag bit in the PxFLG register to allow interrupts to be delivered.   106    107   write_reg_pin(Irq_flag_clear);   108   write_reg_pin(Irq_mask_clear);   109 }   110    111 bool Gpio_jz4740_irq_pin::do_set_mode(unsigned mode)   112 {   113   // Standard comment found for this method:   114   // this operation touches multiple mmio registers and is thus   115   // not atomic, that's why we first mask the IRQ and if it was   116   // enabled we unmask it after we have changed the mode   117    118   if (enabled())   119     do_mask();   120    121   // Do the PxTRG, PxFUN, PxSEL and PxDIR configuration.   122    123   switch(mode)   124   {   125     case L4_IRQ_F_LEVEL_HIGH:   126       write_reg_pin(Port_trigger_clear);   127       write_reg_pin(Port_function_clear);   128       write_reg_pin(Port_select_set);   129       write_reg_pin(Port_dir_set);   130       break;   131     case L4_IRQ_F_LEVEL_LOW:   132       write_reg_pin(Port_trigger_clear);   133       write_reg_pin(Port_function_clear);   134       write_reg_pin(Port_select_set);   135       write_reg_pin(Port_dir_clear);   136       break;   137     case L4_IRQ_F_POS_EDGE:   138       write_reg_pin(Port_trigger_set);   139       write_reg_pin(Port_function_clear);   140       write_reg_pin(Port_select_set);   141       write_reg_pin(Port_dir_set);   142       break;   143     case L4_IRQ_F_NEG_EDGE:   144       write_reg_pin(Port_trigger_set);   145       write_reg_pin(Port_function_clear);   146       write_reg_pin(Port_select_set);   147       write_reg_pin(Port_dir_clear);   148       break;   149    150     default:   151       return false;   152   }   153    154   if (enabled())   155     do_unmask();   156    157   return true;   158 }   159    160 int Gpio_jz4740_irq_pin::clear()   161 {   162   // Obtain the flag status for the pin, clearing it if set.   163    164   l4_uint32_t e = _regs[Irq_flag] & (1UL << _pin);   165   if (e)   166     _regs[Irq_flag_clear] = e;   167    168   return (e >> _pin);   169 }   170    171 bool Gpio_jz4740_irq_pin::enabled()   172 {   173   return true;   174 }   175    176    177    178 // Initialise the GPIO controller.   179    180 Gpio_jz4740_chip::Gpio_jz4740_chip(l4_addr_t start, uint8_t port_number)   181 : Hw::Gpio_chip(32)   182 {   183   _start = start + port_number * 0x100;   184   _regs = new Hw::Mmio_register_block<32>(_start);   185   _pull_config = &gpio_ports[port_number];   186 }   187    188 // Return the value of a pin.   189    190 int   191 Gpio_jz4740_chip::get(unsigned pin)   192 {   193   if (pin >= _nr_pins)   194     throw -L4_EINVAL;   195    196   l4_uint32_t val = _regs[Pin_level];   197   return (val >> _pin_shift(pin)) & 1;   198 }   199    200 // Return multiple pin values.   201    202 unsigned   203 Gpio_jz4740_chip::multi_get(unsigned offset)   204 {   205   _reg_offset_check(offset);   206   return _regs[Pin_level];   207 }   208    209 // Set the value of a pin.   210    211 void   212 Gpio_jz4740_chip::set(unsigned pin, int value)   213 {   214   if (pin >= _nr_pins)   215     throw -L4_EINVAL;   216    217   l4_uint32_t reg_set = value ? Port_data_set : Port_data_clear;   218   _regs[reg_set] = _pin_bit(pin);   219 }   220    221 // Set multiple pin values.   222    223 void   224 Gpio_jz4740_chip::multi_set(Pin_slice const &mask, unsigned data)   225 {   226   _reg_offset_check(mask.offset);   227   if (mask.mask & data)   228     _regs[Port_data_set] = (mask.mask & data);   229   if (mask.mask & ~data)   230     _regs[Port_data_clear] = (mask.mask & ~data);   231 }   232    233 // Set a pin up with the given mode and value (if appropriate).   234    235 void   236 Gpio_jz4740_chip::setup(unsigned pin, unsigned mode, int value)   237 {   238   if (pin >= _nr_pins)   239     throw -L4_EINVAL;   240    241   config(pin, mode);   242    243   if (mode == Output)   244     set(pin, value);   245 }   246    247 // Configuration of a pin using the generic input/output/IRQ mode.   248    249 void   250 Gpio_jz4740_chip::config(unsigned pin, unsigned mode)   251 {   252   _config(_pin_bit(pin), mode);   253 }   254    255 void   256 Gpio_jz4740_chip::_config(unsigned bitmap, unsigned mode)   257 {   258   switch (mode)   259   {   260     case Input:   261       _regs[Port_function_clear] = bitmap;   262       _regs[Port_select_clear] = bitmap;   263       _regs[Port_dir_clear] = bitmap;   264       break;   265     case Output:   266       _regs[Port_function_clear] = bitmap;   267       _regs[Port_select_clear] = bitmap;   268       _regs[Port_dir_set] = bitmap;   269       break;   270     case Irq:   271       _regs[Port_function_clear] = bitmap;   272       _regs[Port_select_set] = bitmap;   273       // The direction depends on the actual trigger mode.   274       break;   275     default:   276       break;   277   }   278 }   279    280 // Pull-up configuration for a pin.   281    282 void   283 Gpio_jz4740_chip::config_pull(unsigned pin, unsigned mode)   284 {   285   if (pin >= _nr_pins)   286     throw -L4_EINVAL;   287    288   _config_pull(_pin_bit(pin), mode);   289 }   290    291 void   292 Gpio_jz4740_chip::_config_pull(unsigned bitmap, unsigned mode)   293 {   294   switch (mode)   295   {   296     case Pull_none:   297       _regs[Pull_disable_set] = bitmap;   298       break;   299     case Pull_down:   300       _regs[Pull_disable_clear] = (bitmap & _pull_config->pull_downs);   301       break;   302     case Pull_up:   303       _regs[Pull_disable_clear] = (bitmap & _pull_config->pull_ups);   304       break;   305     default:   306       // Invalid pull-up/down mode for pin.   307       throw -L4_EINVAL;   308   }   309 }   310    311 // Pin function configuration.   312    313 void   314 Gpio_jz4740_chip::config_pad(unsigned pin, unsigned func, unsigned value)   315 {   316   if (pin >= _nr_pins)   317     throw -L4_EINVAL;   318    319   _config_pad(_pin_bit(pin), func, value);   320 }   321    322 void   323 Gpio_jz4740_chip::_config_pad(unsigned bitmap, unsigned func, unsigned value)   324 {   325   if (value > 1)   326     throw -L4_EINVAL;   327    328   switch (func)   329   {   330     case Hw::Gpio_chip::Function_gpio:   331       _regs[Port_function_clear] = bitmap;   332       break;   333    334     // Support two different device functions.   335    336     case Hw::Gpio_chip::Function_alt:   337       _regs[Port_function_set] = bitmap;   338       _regs[value ? Port_select_set : Port_select_clear] = bitmap;   339       break;   340     default:   341       throw -L4_EINVAL;   342   }   343 }   344    345 // Obtain a pin's configuration from a register in the supplied value.   346    347 void   348 Gpio_jz4740_chip::config_get(unsigned pin, unsigned reg, unsigned *value)   349 {   350   if (pin >= _nr_pins)   351     throw -L4_EINVAL;   352    353   *value = (_regs[reg] >> _pin_shift(pin)) & 1;   354 }   355    356 // Return function and function-specific configuration for a pin.   357    358 void   359 Gpio_jz4740_chip::config_pad_get(unsigned pin, unsigned *func, unsigned *value)   360 {   361   unsigned direction, function, select, trigger;   362    363   config_get(pin, Port_function, &function);   364   config_get(pin, Port_select, &select);   365   config_get(pin, Port_dir, &direction);   366    367   if (function)   368   {   369     *func = Hw::Gpio_chip::Function_alt;   370     *value = select;   371     return;   372   }   373    374   if (select)   375   {   376     config_get(pin, Port_trigger, &trigger);   377    378     *func = Hw::Gpio_chip::Function_irq;   379     *value = (trigger ? (direction ? L4_IRQ_F_POS_EDGE : L4_IRQ_F_NEG_EDGE)   380                       : (direction ? L4_IRQ_F_LEVEL_HIGH : L4_IRQ_F_LEVEL_LOW));   381     return;   382   }   383    384   *func = Hw::Gpio_chip::Function_gpio;   385   *value = direction ? Input : Output;   386 }   387    388 // Obtain an IRQ abstraction for a pin.   389    390 Hw::Gpio_irq_pin *   391 Gpio_jz4740_chip::get_irq(unsigned pin)   392 {   393   if (pin >= _nr_pins)   394     throw -L4_EINVAL;   395    396   return new Gpio_jz4740_irq_pin(pin, _regs);   397 }   398    399 // Pull-up function configuration for multiple pins.   400    401 void   402 Gpio_jz4740_chip::multi_config_pull(Pin_slice const &mask, unsigned mode)   403 {   404   _config_pull(mask.mask << mask.offset, mode);   405 }   406    407 // Pin function configuration for multiple pins.   408    409 void   410 Gpio_jz4740_chip::multi_config_pad(Pin_slice const &mask, unsigned func, unsigned val)   411 {   412   _config_pad(mask.mask << mask.offset, func, val);   413 }   414    415 // Set up multiple pins with the given mode.   416    417 void   418 Gpio_jz4740_chip::multi_setup(Pin_slice const &mask, unsigned mode, unsigned outvalues)   419 {   420   _config(mask.mask << mask.offset, mode);   421    422   if (mode == Output)   423     multi_set(mask, outvalues);   424 }   425    426 Hw::Gpio_chip *jz4740_gpio_chip(l4_addr_t start, uint8_t port_number, bool shadow)   427 {   428   (void) shadow;   429   return new Gpio_jz4740_chip(start, port_number);   430 }   431    432    433    434 // C language interface functions.   435    436 void *jz4740_gpio_init(l4_addr_t start, uint8_t port_number)   437 {   438   return (void *) jz4740_gpio_chip(start, port_number, false);   439 }   440    441 void jz4740_gpio_setup(void *gpio, unsigned pin, unsigned mode, int value)   442 {   443   static_cast<Gpio_jz4740_chip *>(gpio)->setup(pin, mode, value);   444 }   445    446 void jz4740_gpio_config_pull(void *gpio, unsigned pin, unsigned mode)   447 {   448   static_cast<Gpio_jz4740_chip *>(gpio)->config_pull(pin, mode);   449 }   450    451 void jz4740_gpio_config_pad(void *gpio, unsigned pin, unsigned func, unsigned value)   452 {   453   static_cast<Gpio_jz4740_chip *>(gpio)->config_pad(pin, func, value);   454 }   455    456 void jz4740_gpio_config_get(void *gpio, unsigned pin, unsigned reg, unsigned *value)   457 {   458   static_cast<Gpio_jz4740_chip *>(gpio)->config_get(pin, reg, value);   459 }   460    461 void jz4740_gpio_config_pad_get(void *gpio, unsigned pin, unsigned *func, unsigned *value)   462 {   463   static_cast<Gpio_jz4740_chip *>(gpio)->config_pad_get(pin, func, value);   464 }   465    466 void jz4740_gpio_multi_setup(void *gpio, Pin_slice const *mask, unsigned mode, unsigned outvalues)   467 {   468   static_cast<Gpio_jz4740_chip *>(gpio)->multi_setup(*mask, mode, outvalues);   469 }   470    471 void jz4740_gpio_multi_config_pull(void *gpio, Pin_slice const *mask, unsigned mode)   472 {   473   static_cast<Gpio_jz4740_chip *>(gpio)->multi_config_pull(*mask, mode);   474 }   475    476 void jz4740_gpio_multi_config_pad(void *gpio, Pin_slice const *mask, unsigned func, unsigned value)   477 {   478   static_cast<Gpio_jz4740_chip *>(gpio)->multi_config_pad(*mask, func, value);   479 }   480    481 void jz4740_gpio_multi_set(void *gpio, Pin_slice const *mask, unsigned data)   482 {   483   static_cast<Gpio_jz4740_chip *>(gpio)->multi_set(*mask, data);   484 }   485    486 unsigned jz4740_gpio_multi_get(void *gpio, unsigned offset)   487 {   488   return static_cast<Gpio_jz4740_chip *>(gpio)->multi_get(offset);   489 }   490    491 int jz4740_gpio_get(void *gpio, unsigned pin)   492 {   493   return static_cast<Gpio_jz4740_chip *>(gpio)->get(pin);   494 }   495    496 void jz4740_gpio_set(void *gpio, unsigned pin, int value)   497 {   498   static_cast<Gpio_jz4740_chip *>(gpio)->set(pin, value);   499 }   500    501 void *jz4740_gpio_get_irq(void *gpio, unsigned pin)   502 {   503   return (void *) static_cast<Gpio_jz4740_chip *>(gpio)->get_irq(pin);   504 }   505    506 bool jz4740_gpio_irq_set_mode(void *gpio_irq, unsigned mode)   507 {   508   return static_cast<Hw::Gpio_irq_pin *>(gpio_irq)->do_set_mode(mode);   509 }