1 /* 2 * GPIO driver for Ingenic JZ4740. 3 * (See below for additional copyright and licensing notices.) 4 * 5 * (c) 2017, 2018 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 // IRQ control for each GPIO pin. 70 71 Gpio_jz4740_irq_pin::Gpio_jz4740_irq_pin(unsigned pin, Hw::Register_block<32> const ®s) 72 : _pin(pin), _regs(regs) 73 {} 74 75 void 76 Gpio_jz4740_irq_pin::write_reg_pin(unsigned reg) 77 { 78 // Write the pin bit to the register, setting or clearing the pin 79 // depending on the register chosen. 80 81 _regs[reg] = _pin_bit(_pin); 82 } 83 84 void Gpio_jz4740_irq_pin::do_mask() 85 { 86 // Set the interrupt bit in the PxIM register. 87 88 write_reg_pin(Irq_mask_set); 89 } 90 91 void Gpio_jz4740_irq_pin::do_unmask() 92 { 93 // Clear the interrupt bit in the PxIM register, first also clearing the 94 // flag bit in the PxFLG register to allow interrupts to be delivered. 95 96 write_reg_pin(Irq_flag_clear); 97 write_reg_pin(Irq_mask_clear); 98 } 99 100 bool Gpio_jz4740_irq_pin::do_set_mode(unsigned mode) 101 { 102 // Standard comment found for this method: 103 // this operation touches multiple mmio registers and is thus 104 // not atomic, that's why we first mask the IRQ and if it was 105 // enabled we unmask it after we have changed the mode 106 107 if (enabled()) 108 do_mask(); 109 110 // Do the PxTRG, PxFUN, PxSEL and PxDIR configuration. 111 112 switch(mode) 113 { 114 case L4_IRQ_F_LEVEL_HIGH: 115 write_reg_pin(Port_trigger_clear); 116 write_reg_pin(Port_function_clear); 117 write_reg_pin(Port_select_set); 118 write_reg_pin(Port_dir_set); 119 break; 120 case L4_IRQ_F_LEVEL_LOW: 121 write_reg_pin(Port_trigger_clear); 122 write_reg_pin(Port_function_clear); 123 write_reg_pin(Port_select_set); 124 write_reg_pin(Port_dir_clear); 125 break; 126 case L4_IRQ_F_POS_EDGE: 127 write_reg_pin(Port_trigger_set); 128 write_reg_pin(Port_function_clear); 129 write_reg_pin(Port_select_set); 130 write_reg_pin(Port_dir_set); 131 break; 132 case L4_IRQ_F_NEG_EDGE: 133 write_reg_pin(Port_trigger_set); 134 write_reg_pin(Port_function_clear); 135 write_reg_pin(Port_select_set); 136 write_reg_pin(Port_dir_clear); 137 break; 138 139 default: 140 return false; 141 } 142 143 if (enabled()) 144 do_unmask(); 145 146 return true; 147 } 148 149 int Gpio_jz4740_irq_pin::clear() 150 { 151 // Obtain the flag status for the pin, clearing it if set. 152 153 l4_uint32_t e = _regs[Irq_flag] & (1UL << _pin); 154 if (e) 155 _regs[Irq_flag_clear] = e; 156 157 return (e >> _pin); 158 } 159 160 bool Gpio_jz4740_irq_pin::enabled() 161 { 162 return true; 163 } 164 165 166 167 // Initialise the GPIO controller. 168 169 Gpio_jz4740_chip::Gpio_jz4740_chip(l4_addr_t start, l4_addr_t end, 170 unsigned nr_pins) 171 : _start(start), _end(end), 172 _nr_pins(nr_pins) 173 { 174 _regs = new Hw::Mmio_register_block<32>(_start); 175 } 176 177 // Return the value of a pin. 178 179 int 180 Gpio_jz4740_chip::get(unsigned pin) 181 { 182 if (pin >= _nr_pins) 183 throw -L4_EINVAL; 184 185 l4_uint32_t val = _regs[Pin_level]; 186 return (val >> _pin_shift(pin)) & 1; 187 } 188 189 // Return multiple pin values. 190 191 unsigned 192 Gpio_jz4740_chip::multi_get(unsigned offset) 193 { 194 _reg_offset_check(offset); 195 return _regs[Pin_level]; 196 } 197 198 // Set the value of a pin. 199 200 void 201 Gpio_jz4740_chip::set(unsigned pin, int value) 202 { 203 if (pin >= _nr_pins) 204 throw -L4_EINVAL; 205 206 l4_uint32_t reg_set = value ? Port_data_set : Port_data_clear; 207 _regs[reg_set] = _pin_bit(pin); 208 } 209 210 // Set multiple pin values. 211 212 void 213 Gpio_jz4740_chip::multi_set(Pin_slice const &mask, unsigned data) 214 { 215 _reg_offset_check(mask.offset); 216 if (mask.mask & data) 217 _regs[Port_data_set] = (mask.mask & data); 218 if (mask.mask & ~data) 219 _regs[Port_data_clear] = (mask.mask & ~data); 220 } 221 222 // Set a pin up with the given mode and value (if appropriate). 223 224 void 225 Gpio_jz4740_chip::setup(unsigned pin, unsigned mode, int value) 226 { 227 if (pin >= _nr_pins) 228 throw -L4_EINVAL; 229 230 config(pin, mode); 231 232 if (mode == Output) 233 set(pin, value); 234 } 235 236 // Configuration of a pin using the generic input/output/IRQ mode. 237 238 void 239 Gpio_jz4740_chip::config(unsigned pin, unsigned mode) 240 { 241 _config(_pin_bit(pin), mode); 242 } 243 244 void 245 Gpio_jz4740_chip::_config(unsigned bitmap, unsigned mode) 246 { 247 switch (mode) 248 { 249 case Input: 250 _regs[Port_function_clear] = bitmap; 251 _regs[Port_select_clear] = bitmap; 252 _regs[Port_dir_clear] = bitmap; 253 break; 254 case Output: 255 _regs[Port_function_clear] = bitmap; 256 _regs[Port_select_clear] = bitmap; 257 _regs[Port_dir_set] = bitmap; 258 break; 259 case Irq: 260 _regs[Port_function_clear] = bitmap; 261 _regs[Port_select_set] = bitmap; 262 // The direction depends on the actual trigger mode. 263 break; 264 default: 265 break; 266 } 267 } 268 269 // Pull-up configuration for a pin. 270 271 void 272 Gpio_jz4740_chip::config_pull(unsigned pin, unsigned mode) 273 { 274 if (pin >= _nr_pins) 275 throw -L4_EINVAL; 276 277 _config_pull(_pin_bit(pin), mode); 278 } 279 280 void 281 Gpio_jz4740_chip::_config_pull(unsigned bitmap, unsigned mode) 282 { 283 switch (mode) 284 { 285 case Pull_none: 286 _regs[Pull_disable_set] = bitmap; 287 break; 288 case Pull_up: 289 _regs[Pull_disable_clear] = bitmap; 290 break; 291 default: 292 // Invalid pull-up/down mode for pin. 293 throw -L4_EINVAL; 294 } 295 } 296 297 // Pin function configuration. 298 299 void 300 Gpio_jz4740_chip::config_pad(unsigned pin, unsigned func, unsigned value) 301 { 302 if (pin >= _nr_pins) 303 throw -L4_EINVAL; 304 305 _config_pad(_pin_bit(pin), func, value); 306 } 307 308 void 309 Gpio_jz4740_chip::_config_pad(unsigned bitmap, unsigned func, unsigned value) 310 { 311 if (value > 1) 312 throw -L4_EINVAL; 313 314 switch (func) 315 { 316 case Hw::Gpio_chip::Function_gpio: 317 _regs[Port_function_clear] = bitmap; 318 break; 319 320 // Support two different device functions. 321 322 case Hw::Gpio_chip::Function_alt: 323 _regs[Port_function_set] = bitmap; 324 _regs[value ? Port_select_set : Port_select_clear] = bitmap; 325 break; 326 default: 327 throw -L4_EINVAL; 328 } 329 } 330 331 // Obtain a pin's configuration from a register in the supplied value. 332 333 void 334 Gpio_jz4740_chip::config_get(unsigned pin, unsigned reg, unsigned *value) 335 { 336 if (pin >= _nr_pins) 337 throw -L4_EINVAL; 338 339 *value = (_regs[reg] >> _pin_shift(pin)) & 1; 340 } 341 342 // Obtain an IRQ abstraction for a pin. 343 344 Hw::Gpio_irq_pin * 345 Gpio_jz4740_chip::get_irq(unsigned pin) 346 { 347 if (pin >= _nr_pins) 348 throw -L4_EINVAL; 349 350 return new Gpio_jz4740_irq_pin(pin, _regs); 351 } 352 353 // Pull-up function configuration for multiple pins. 354 355 void 356 Gpio_jz4740_chip::multi_config_pull(Pin_slice const &mask, unsigned mode) 357 { 358 _config_pull(mask.mask << mask.offset, mode); 359 } 360 361 // Pin function configuration for multiple pins. 362 363 void 364 Gpio_jz4740_chip::multi_config_pad(Pin_slice const &mask, unsigned func, unsigned val) 365 { 366 _config_pad(mask.mask << mask.offset, func, val); 367 } 368 369 // Set up multiple pins with the given mode. 370 371 void 372 Gpio_jz4740_chip::multi_setup(Pin_slice const &mask, unsigned mode, unsigned outvalues) 373 { 374 _config(mask.mask << mask.offset, mode); 375 376 if (mode == Output) 377 multi_set(mask, outvalues); 378 } 379 380 381 382 // C language interface functions. 383 384 void *jz4740_gpio_init(l4_addr_t start, l4_addr_t end, unsigned pins) 385 { 386 return (void *) new Gpio_jz4740_chip(start, end, pins); 387 } 388 389 void jz4740_gpio_setup(void *gpio, unsigned pin, unsigned mode, int value) 390 { 391 static_cast<Gpio_jz4740_chip *>(gpio)->setup(pin, mode, value); 392 } 393 394 void jz4740_gpio_config_pull(void *gpio, unsigned pin, unsigned mode) 395 { 396 static_cast<Gpio_jz4740_chip *>(gpio)->config_pull(pin, mode); 397 } 398 399 void jz4740_gpio_config_pad(void *gpio, unsigned pin, unsigned func, unsigned value) 400 { 401 static_cast<Gpio_jz4740_chip *>(gpio)->config_pad(pin, func, value); 402 } 403 404 void jz4740_gpio_config_get(void *gpio, unsigned pin, unsigned reg, unsigned *value) 405 { 406 static_cast<Gpio_jz4740_chip *>(gpio)->config_get(pin, reg, value); 407 } 408 409 void jz4740_gpio_multi_setup(void *gpio, Pin_slice const *mask, unsigned mode, unsigned outvalues) 410 { 411 static_cast<Gpio_jz4740_chip *>(gpio)->multi_setup(*mask, mode, outvalues); 412 } 413 414 void jz4740_gpio_multi_config_pull(void *gpio, Pin_slice const *mask, unsigned mode) 415 { 416 static_cast<Gpio_jz4740_chip *>(gpio)->multi_config_pull(*mask, mode); 417 } 418 419 void jz4740_gpio_multi_config_pad(void *gpio, Pin_slice const *mask, unsigned func, unsigned value) 420 { 421 static_cast<Gpio_jz4740_chip *>(gpio)->multi_config_pad(*mask, func, value); 422 } 423 424 void jz4740_gpio_multi_set(void *gpio, Pin_slice const *mask, unsigned data) 425 { 426 static_cast<Gpio_jz4740_chip *>(gpio)->multi_set(*mask, data); 427 } 428 429 unsigned jz4740_gpio_multi_get(void *gpio, unsigned offset) 430 { 431 return static_cast<Gpio_jz4740_chip *>(gpio)->multi_get(offset); 432 } 433 434 int jz4740_gpio_get(void *gpio, unsigned pin) 435 { 436 return static_cast<Gpio_jz4740_chip *>(gpio)->get(pin); 437 } 438 439 void jz4740_gpio_set(void *gpio, unsigned pin, int value) 440 { 441 static_cast<Gpio_jz4740_chip *>(gpio)->set(pin, value); 442 } 443 444 void *jz4740_gpio_get_irq(void *gpio, unsigned pin) 445 { 446 return (void *) static_cast<Gpio_jz4740_chip *>(gpio)->get_irq(pin); 447 } 448 449 bool jz4740_gpio_irq_set_mode(void *gpio_irq, unsigned mode) 450 { 451 return static_cast<Hw::Gpio_irq_pin *>(gpio_irq)->do_set_mode(mode); 452 }