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 ®s) 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 }