1526dbbe0SHavard Skinnemoen /* 2526dbbe0SHavard Skinnemoen * Nuvoton NPCM7xx General Purpose Input / Output (GPIO) 3526dbbe0SHavard Skinnemoen * 4526dbbe0SHavard Skinnemoen * Copyright 2020 Google LLC 5526dbbe0SHavard Skinnemoen * 6526dbbe0SHavard Skinnemoen * This program is free software; you can redistribute it and/or 7526dbbe0SHavard Skinnemoen * modify it under the terms of the GNU General Public License 8526dbbe0SHavard Skinnemoen * version 2 as published by the Free Software Foundation. 9526dbbe0SHavard Skinnemoen * 10526dbbe0SHavard Skinnemoen * This program is distributed in the hope that it will be useful, 11526dbbe0SHavard Skinnemoen * but WITHOUT ANY WARRANTY; without even the implied warranty of 12526dbbe0SHavard Skinnemoen * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13526dbbe0SHavard Skinnemoen * GNU General Public License for more details. 14526dbbe0SHavard Skinnemoen */ 15526dbbe0SHavard Skinnemoen 16526dbbe0SHavard Skinnemoen #include "qemu/osdep.h" 17526dbbe0SHavard Skinnemoen 18526dbbe0SHavard Skinnemoen #include "hw/gpio/npcm7xx_gpio.h" 19526dbbe0SHavard Skinnemoen #include "hw/irq.h" 20526dbbe0SHavard Skinnemoen #include "hw/qdev-properties.h" 21526dbbe0SHavard Skinnemoen #include "migration/vmstate.h" 22526dbbe0SHavard Skinnemoen #include "qapi/error.h" 23526dbbe0SHavard Skinnemoen #include "qemu/log.h" 24526dbbe0SHavard Skinnemoen #include "qemu/module.h" 25526dbbe0SHavard Skinnemoen #include "qemu/units.h" 26526dbbe0SHavard Skinnemoen #include "trace.h" 27526dbbe0SHavard Skinnemoen 28526dbbe0SHavard Skinnemoen /* 32-bit register indices. */ 29526dbbe0SHavard Skinnemoen enum NPCM7xxGPIORegister { 30526dbbe0SHavard Skinnemoen NPCM7XX_GPIO_TLOCK1, 31526dbbe0SHavard Skinnemoen NPCM7XX_GPIO_DIN, 32526dbbe0SHavard Skinnemoen NPCM7XX_GPIO_POL, 33526dbbe0SHavard Skinnemoen NPCM7XX_GPIO_DOUT, 34526dbbe0SHavard Skinnemoen NPCM7XX_GPIO_OE, 35526dbbe0SHavard Skinnemoen NPCM7XX_GPIO_OTYP, 36526dbbe0SHavard Skinnemoen NPCM7XX_GPIO_MP, 37526dbbe0SHavard Skinnemoen NPCM7XX_GPIO_PU, 38526dbbe0SHavard Skinnemoen NPCM7XX_GPIO_PD, 39526dbbe0SHavard Skinnemoen NPCM7XX_GPIO_DBNC, 40526dbbe0SHavard Skinnemoen NPCM7XX_GPIO_EVTYP, 41526dbbe0SHavard Skinnemoen NPCM7XX_GPIO_EVBE, 42526dbbe0SHavard Skinnemoen NPCM7XX_GPIO_OBL0, 43526dbbe0SHavard Skinnemoen NPCM7XX_GPIO_OBL1, 44526dbbe0SHavard Skinnemoen NPCM7XX_GPIO_OBL2, 45526dbbe0SHavard Skinnemoen NPCM7XX_GPIO_OBL3, 46526dbbe0SHavard Skinnemoen NPCM7XX_GPIO_EVEN, 47526dbbe0SHavard Skinnemoen NPCM7XX_GPIO_EVENS, 48526dbbe0SHavard Skinnemoen NPCM7XX_GPIO_EVENC, 49526dbbe0SHavard Skinnemoen NPCM7XX_GPIO_EVST, 50526dbbe0SHavard Skinnemoen NPCM7XX_GPIO_SPLCK, 51526dbbe0SHavard Skinnemoen NPCM7XX_GPIO_MPLCK, 52526dbbe0SHavard Skinnemoen NPCM7XX_GPIO_IEM, 53526dbbe0SHavard Skinnemoen NPCM7XX_GPIO_OSRC, 54526dbbe0SHavard Skinnemoen NPCM7XX_GPIO_ODSC, 55526dbbe0SHavard Skinnemoen NPCM7XX_GPIO_DOS = 0x68 / sizeof(uint32_t), 56526dbbe0SHavard Skinnemoen NPCM7XX_GPIO_DOC, 57526dbbe0SHavard Skinnemoen NPCM7XX_GPIO_OES, 58526dbbe0SHavard Skinnemoen NPCM7XX_GPIO_OEC, 59526dbbe0SHavard Skinnemoen NPCM7XX_GPIO_TLOCK2 = 0x7c / sizeof(uint32_t), 60526dbbe0SHavard Skinnemoen NPCM7XX_GPIO_REGS_END, 61526dbbe0SHavard Skinnemoen }; 62526dbbe0SHavard Skinnemoen 63526dbbe0SHavard Skinnemoen #define NPCM7XX_GPIO_REGS_SIZE (4 * KiB) 64526dbbe0SHavard Skinnemoen 65526dbbe0SHavard Skinnemoen #define NPCM7XX_GPIO_LOCK_MAGIC1 (0xc0defa73) 66526dbbe0SHavard Skinnemoen #define NPCM7XX_GPIO_LOCK_MAGIC2 (0xc0de1248) 67526dbbe0SHavard Skinnemoen 68526dbbe0SHavard Skinnemoen static void npcm7xx_gpio_update_events(NPCM7xxGPIOState *s, uint32_t din_diff) 69526dbbe0SHavard Skinnemoen { 70526dbbe0SHavard Skinnemoen uint32_t din_new = s->regs[NPCM7XX_GPIO_DIN]; 71526dbbe0SHavard Skinnemoen 72526dbbe0SHavard Skinnemoen /* Trigger on high level */ 73526dbbe0SHavard Skinnemoen s->regs[NPCM7XX_GPIO_EVST] |= din_new & ~s->regs[NPCM7XX_GPIO_EVTYP]; 74526dbbe0SHavard Skinnemoen /* Trigger on both edges */ 75526dbbe0SHavard Skinnemoen s->regs[NPCM7XX_GPIO_EVST] |= (din_diff & s->regs[NPCM7XX_GPIO_EVTYP] 76526dbbe0SHavard Skinnemoen & s->regs[NPCM7XX_GPIO_EVBE]); 77526dbbe0SHavard Skinnemoen /* Trigger on rising edge */ 78526dbbe0SHavard Skinnemoen s->regs[NPCM7XX_GPIO_EVST] |= (din_diff & din_new 79526dbbe0SHavard Skinnemoen & s->regs[NPCM7XX_GPIO_EVTYP]); 80526dbbe0SHavard Skinnemoen 81526dbbe0SHavard Skinnemoen trace_npcm7xx_gpio_update_events(DEVICE(s)->canonical_path, 82526dbbe0SHavard Skinnemoen s->regs[NPCM7XX_GPIO_EVST], 83526dbbe0SHavard Skinnemoen s->regs[NPCM7XX_GPIO_EVEN]); 84526dbbe0SHavard Skinnemoen qemu_set_irq(s->irq, !!(s->regs[NPCM7XX_GPIO_EVST] 85526dbbe0SHavard Skinnemoen & s->regs[NPCM7XX_GPIO_EVEN])); 86526dbbe0SHavard Skinnemoen } 87526dbbe0SHavard Skinnemoen 88526dbbe0SHavard Skinnemoen static void npcm7xx_gpio_update_pins(NPCM7xxGPIOState *s, uint32_t diff) 89526dbbe0SHavard Skinnemoen { 90526dbbe0SHavard Skinnemoen uint32_t drive_en; 91526dbbe0SHavard Skinnemoen uint32_t drive_lvl; 92526dbbe0SHavard Skinnemoen uint32_t not_driven; 93526dbbe0SHavard Skinnemoen uint32_t undefined; 94526dbbe0SHavard Skinnemoen uint32_t pin_diff; 95526dbbe0SHavard Skinnemoen uint32_t din_old; 96526dbbe0SHavard Skinnemoen 97526dbbe0SHavard Skinnemoen /* Calculate level of each pin driven by GPIO controller. */ 98526dbbe0SHavard Skinnemoen drive_lvl = s->regs[NPCM7XX_GPIO_DOUT] ^ s->regs[NPCM7XX_GPIO_POL]; 99526dbbe0SHavard Skinnemoen /* If OTYP=1, only drive low (open drain) */ 100526dbbe0SHavard Skinnemoen drive_en = s->regs[NPCM7XX_GPIO_OE] & ~(s->regs[NPCM7XX_GPIO_OTYP] 101526dbbe0SHavard Skinnemoen & drive_lvl); 102526dbbe0SHavard Skinnemoen /* 103526dbbe0SHavard Skinnemoen * If a pin is driven to opposite levels by the GPIO controller and the 104526dbbe0SHavard Skinnemoen * external driver, the result is undefined. 105526dbbe0SHavard Skinnemoen */ 106526dbbe0SHavard Skinnemoen undefined = drive_en & s->ext_driven & (drive_lvl ^ s->ext_level); 107526dbbe0SHavard Skinnemoen if (undefined) { 108526dbbe0SHavard Skinnemoen qemu_log_mask(LOG_GUEST_ERROR, 109526dbbe0SHavard Skinnemoen "%s: pins have multiple drivers: 0x%" PRIx32 "\n", 110526dbbe0SHavard Skinnemoen DEVICE(s)->canonical_path, undefined); 111526dbbe0SHavard Skinnemoen } 112526dbbe0SHavard Skinnemoen 113526dbbe0SHavard Skinnemoen not_driven = ~(drive_en | s->ext_driven); 114526dbbe0SHavard Skinnemoen pin_diff = s->pin_level; 115526dbbe0SHavard Skinnemoen 116526dbbe0SHavard Skinnemoen /* Set pins to externally driven level. */ 117526dbbe0SHavard Skinnemoen s->pin_level = s->ext_level & s->ext_driven; 118526dbbe0SHavard Skinnemoen /* Set internally driven pins, ignoring any conflicts. */ 119526dbbe0SHavard Skinnemoen s->pin_level |= drive_lvl & drive_en; 120526dbbe0SHavard Skinnemoen /* Pull up undriven pins with internal pull-up enabled. */ 121526dbbe0SHavard Skinnemoen s->pin_level |= not_driven & s->regs[NPCM7XX_GPIO_PU]; 122526dbbe0SHavard Skinnemoen /* Pins not driven, pulled up or pulled down are undefined */ 123526dbbe0SHavard Skinnemoen undefined |= not_driven & ~(s->regs[NPCM7XX_GPIO_PU] 124526dbbe0SHavard Skinnemoen | s->regs[NPCM7XX_GPIO_PD]); 125526dbbe0SHavard Skinnemoen 126526dbbe0SHavard Skinnemoen /* If any pins changed state, update the outgoing GPIOs. */ 127526dbbe0SHavard Skinnemoen pin_diff ^= s->pin_level; 128526dbbe0SHavard Skinnemoen pin_diff |= undefined & diff; 129526dbbe0SHavard Skinnemoen if (pin_diff) { 130526dbbe0SHavard Skinnemoen int i; 131526dbbe0SHavard Skinnemoen 132526dbbe0SHavard Skinnemoen for (i = 0; i < NPCM7XX_GPIO_NR_PINS; i++) { 133526dbbe0SHavard Skinnemoen uint32_t mask = BIT(i); 134526dbbe0SHavard Skinnemoen if (pin_diff & mask) { 135526dbbe0SHavard Skinnemoen int level = (undefined & mask) ? -1 : !!(s->pin_level & mask); 136526dbbe0SHavard Skinnemoen trace_npcm7xx_gpio_set_output(DEVICE(s)->canonical_path, 137526dbbe0SHavard Skinnemoen i, level); 138526dbbe0SHavard Skinnemoen qemu_set_irq(s->output[i], level); 139526dbbe0SHavard Skinnemoen } 140526dbbe0SHavard Skinnemoen } 141526dbbe0SHavard Skinnemoen } 142526dbbe0SHavard Skinnemoen 143526dbbe0SHavard Skinnemoen /* Calculate new value of DIN after masking and polarity setting. */ 144526dbbe0SHavard Skinnemoen din_old = s->regs[NPCM7XX_GPIO_DIN]; 145526dbbe0SHavard Skinnemoen s->regs[NPCM7XX_GPIO_DIN] = ((s->pin_level & s->regs[NPCM7XX_GPIO_IEM]) 146526dbbe0SHavard Skinnemoen ^ s->regs[NPCM7XX_GPIO_POL]); 147526dbbe0SHavard Skinnemoen 148526dbbe0SHavard Skinnemoen /* See if any new events triggered because of all this. */ 149526dbbe0SHavard Skinnemoen npcm7xx_gpio_update_events(s, din_old ^ s->regs[NPCM7XX_GPIO_DIN]); 150526dbbe0SHavard Skinnemoen } 151526dbbe0SHavard Skinnemoen 152526dbbe0SHavard Skinnemoen static bool npcm7xx_gpio_is_locked(NPCM7xxGPIOState *s) 153526dbbe0SHavard Skinnemoen { 154526dbbe0SHavard Skinnemoen return s->regs[NPCM7XX_GPIO_TLOCK1] == 1; 155526dbbe0SHavard Skinnemoen } 156526dbbe0SHavard Skinnemoen 157526dbbe0SHavard Skinnemoen static uint64_t npcm7xx_gpio_regs_read(void *opaque, hwaddr addr, 158526dbbe0SHavard Skinnemoen unsigned int size) 159526dbbe0SHavard Skinnemoen { 160526dbbe0SHavard Skinnemoen hwaddr reg = addr / sizeof(uint32_t); 161526dbbe0SHavard Skinnemoen NPCM7xxGPIOState *s = opaque; 162526dbbe0SHavard Skinnemoen uint64_t value = 0; 163526dbbe0SHavard Skinnemoen 164526dbbe0SHavard Skinnemoen switch (reg) { 165526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_TLOCK1 ... NPCM7XX_GPIO_EVEN: 166526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_EVST ... NPCM7XX_GPIO_ODSC: 167526dbbe0SHavard Skinnemoen value = s->regs[reg]; 168526dbbe0SHavard Skinnemoen break; 169526dbbe0SHavard Skinnemoen 170526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_EVENS ... NPCM7XX_GPIO_EVENC: 171526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_DOS ... NPCM7XX_GPIO_TLOCK2: 172526dbbe0SHavard Skinnemoen qemu_log_mask(LOG_GUEST_ERROR, 173526dbbe0SHavard Skinnemoen "%s: read from write-only register 0x%" HWADDR_PRIx "\n", 174526dbbe0SHavard Skinnemoen DEVICE(s)->canonical_path, addr); 175526dbbe0SHavard Skinnemoen break; 176526dbbe0SHavard Skinnemoen 177526dbbe0SHavard Skinnemoen default: 178526dbbe0SHavard Skinnemoen qemu_log_mask(LOG_GUEST_ERROR, 179526dbbe0SHavard Skinnemoen "%s: read from invalid offset 0x%" HWADDR_PRIx "\n", 180526dbbe0SHavard Skinnemoen DEVICE(s)->canonical_path, addr); 181526dbbe0SHavard Skinnemoen break; 182526dbbe0SHavard Skinnemoen } 183526dbbe0SHavard Skinnemoen 184526dbbe0SHavard Skinnemoen trace_npcm7xx_gpio_read(DEVICE(s)->canonical_path, addr, value); 185526dbbe0SHavard Skinnemoen 186526dbbe0SHavard Skinnemoen return value; 187526dbbe0SHavard Skinnemoen } 188526dbbe0SHavard Skinnemoen 189526dbbe0SHavard Skinnemoen static void npcm7xx_gpio_regs_write(void *opaque, hwaddr addr, uint64_t v, 190526dbbe0SHavard Skinnemoen unsigned int size) 191526dbbe0SHavard Skinnemoen { 192526dbbe0SHavard Skinnemoen hwaddr reg = addr / sizeof(uint32_t); 193526dbbe0SHavard Skinnemoen NPCM7xxGPIOState *s = opaque; 194526dbbe0SHavard Skinnemoen uint32_t value = v; 195526dbbe0SHavard Skinnemoen uint32_t diff; 196526dbbe0SHavard Skinnemoen 197526dbbe0SHavard Skinnemoen trace_npcm7xx_gpio_write(DEVICE(s)->canonical_path, addr, v); 198526dbbe0SHavard Skinnemoen 199526dbbe0SHavard Skinnemoen if (npcm7xx_gpio_is_locked(s)) { 200526dbbe0SHavard Skinnemoen switch (reg) { 201526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_TLOCK1: 202526dbbe0SHavard Skinnemoen if (s->regs[NPCM7XX_GPIO_TLOCK2] == NPCM7XX_GPIO_LOCK_MAGIC2 && 203526dbbe0SHavard Skinnemoen value == NPCM7XX_GPIO_LOCK_MAGIC1) { 204526dbbe0SHavard Skinnemoen s->regs[NPCM7XX_GPIO_TLOCK1] = 0; 205526dbbe0SHavard Skinnemoen s->regs[NPCM7XX_GPIO_TLOCK2] = 0; 206526dbbe0SHavard Skinnemoen } 207526dbbe0SHavard Skinnemoen break; 208526dbbe0SHavard Skinnemoen 209526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_TLOCK2: 210526dbbe0SHavard Skinnemoen s->regs[reg] = value; 211526dbbe0SHavard Skinnemoen break; 212526dbbe0SHavard Skinnemoen 213526dbbe0SHavard Skinnemoen default: 214526dbbe0SHavard Skinnemoen qemu_log_mask(LOG_GUEST_ERROR, 215526dbbe0SHavard Skinnemoen "%s: write to locked register @ 0x%" HWADDR_PRIx "\n", 216526dbbe0SHavard Skinnemoen DEVICE(s)->canonical_path, addr); 217526dbbe0SHavard Skinnemoen break; 218526dbbe0SHavard Skinnemoen } 219526dbbe0SHavard Skinnemoen 220526dbbe0SHavard Skinnemoen return; 221526dbbe0SHavard Skinnemoen } 222526dbbe0SHavard Skinnemoen 223526dbbe0SHavard Skinnemoen diff = s->regs[reg] ^ value; 224526dbbe0SHavard Skinnemoen 225526dbbe0SHavard Skinnemoen switch (reg) { 226526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_TLOCK1: 227526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_TLOCK2: 228526dbbe0SHavard Skinnemoen s->regs[NPCM7XX_GPIO_TLOCK1] = 1; 229526dbbe0SHavard Skinnemoen s->regs[NPCM7XX_GPIO_TLOCK2] = 0; 230526dbbe0SHavard Skinnemoen break; 231526dbbe0SHavard Skinnemoen 232526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_DIN: 233526dbbe0SHavard Skinnemoen qemu_log_mask(LOG_GUEST_ERROR, 234526dbbe0SHavard Skinnemoen "%s: write to read-only register @ 0x%" HWADDR_PRIx "\n", 235526dbbe0SHavard Skinnemoen DEVICE(s)->canonical_path, addr); 236526dbbe0SHavard Skinnemoen break; 237526dbbe0SHavard Skinnemoen 238526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_POL: 239526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_DOUT: 240526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_OE: 241526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_OTYP: 242526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_PU: 243526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_PD: 244526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_IEM: 245526dbbe0SHavard Skinnemoen s->regs[reg] = value; 246526dbbe0SHavard Skinnemoen npcm7xx_gpio_update_pins(s, diff); 247526dbbe0SHavard Skinnemoen break; 248526dbbe0SHavard Skinnemoen 249526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_DOS: 250526dbbe0SHavard Skinnemoen s->regs[NPCM7XX_GPIO_DOUT] |= value; 251526dbbe0SHavard Skinnemoen npcm7xx_gpio_update_pins(s, value); 252526dbbe0SHavard Skinnemoen break; 253526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_DOC: 254526dbbe0SHavard Skinnemoen s->regs[NPCM7XX_GPIO_DOUT] &= ~value; 255526dbbe0SHavard Skinnemoen npcm7xx_gpio_update_pins(s, value); 256526dbbe0SHavard Skinnemoen break; 257526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_OES: 258526dbbe0SHavard Skinnemoen s->regs[NPCM7XX_GPIO_OE] |= value; 259526dbbe0SHavard Skinnemoen npcm7xx_gpio_update_pins(s, value); 260526dbbe0SHavard Skinnemoen break; 261526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_OEC: 262526dbbe0SHavard Skinnemoen s->regs[NPCM7XX_GPIO_OE] &= ~value; 263526dbbe0SHavard Skinnemoen npcm7xx_gpio_update_pins(s, value); 264526dbbe0SHavard Skinnemoen break; 265526dbbe0SHavard Skinnemoen 266526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_EVTYP: 267526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_EVBE: 268526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_EVEN: 269526dbbe0SHavard Skinnemoen s->regs[reg] = value; 270526dbbe0SHavard Skinnemoen npcm7xx_gpio_update_events(s, 0); 271526dbbe0SHavard Skinnemoen break; 272526dbbe0SHavard Skinnemoen 273526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_EVENS: 274526dbbe0SHavard Skinnemoen s->regs[NPCM7XX_GPIO_EVEN] |= value; 275526dbbe0SHavard Skinnemoen npcm7xx_gpio_update_events(s, 0); 276526dbbe0SHavard Skinnemoen break; 277526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_EVENC: 278526dbbe0SHavard Skinnemoen s->regs[NPCM7XX_GPIO_EVEN] &= ~value; 279526dbbe0SHavard Skinnemoen npcm7xx_gpio_update_events(s, 0); 280526dbbe0SHavard Skinnemoen break; 281526dbbe0SHavard Skinnemoen 282526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_EVST: 283526dbbe0SHavard Skinnemoen s->regs[reg] &= ~value; 284526dbbe0SHavard Skinnemoen npcm7xx_gpio_update_events(s, 0); 285526dbbe0SHavard Skinnemoen break; 286526dbbe0SHavard Skinnemoen 287526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_MP: 288526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_DBNC: 289526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_OSRC: 290526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_ODSC: 291526dbbe0SHavard Skinnemoen /* Nothing to do; just store the value. */ 292526dbbe0SHavard Skinnemoen s->regs[reg] = value; 293526dbbe0SHavard Skinnemoen break; 294526dbbe0SHavard Skinnemoen 295526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_OBL0: 296526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_OBL1: 297526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_OBL2: 298526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_OBL3: 299526dbbe0SHavard Skinnemoen s->regs[reg] = value; 300526dbbe0SHavard Skinnemoen qemu_log_mask(LOG_UNIMP, "%s: Blinking is not implemented\n", 301526dbbe0SHavard Skinnemoen __func__); 302526dbbe0SHavard Skinnemoen break; 303526dbbe0SHavard Skinnemoen 304526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_SPLCK: 305526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_MPLCK: 306526dbbe0SHavard Skinnemoen qemu_log_mask(LOG_UNIMP, "%s: Per-pin lock is not implemented\n", 307526dbbe0SHavard Skinnemoen __func__); 308526dbbe0SHavard Skinnemoen break; 309526dbbe0SHavard Skinnemoen 310526dbbe0SHavard Skinnemoen default: 311526dbbe0SHavard Skinnemoen qemu_log_mask(LOG_GUEST_ERROR, 312526dbbe0SHavard Skinnemoen "%s: write to invalid offset 0x%" HWADDR_PRIx "\n", 313526dbbe0SHavard Skinnemoen DEVICE(s)->canonical_path, addr); 314526dbbe0SHavard Skinnemoen break; 315526dbbe0SHavard Skinnemoen } 316526dbbe0SHavard Skinnemoen } 317526dbbe0SHavard Skinnemoen 318526dbbe0SHavard Skinnemoen static const MemoryRegionOps npcm7xx_gpio_regs_ops = { 319526dbbe0SHavard Skinnemoen .read = npcm7xx_gpio_regs_read, 320526dbbe0SHavard Skinnemoen .write = npcm7xx_gpio_regs_write, 321526dbbe0SHavard Skinnemoen .endianness = DEVICE_NATIVE_ENDIAN, 322526dbbe0SHavard Skinnemoen .valid = { 323526dbbe0SHavard Skinnemoen .min_access_size = 4, 324526dbbe0SHavard Skinnemoen .max_access_size = 4, 325526dbbe0SHavard Skinnemoen .unaligned = false, 326526dbbe0SHavard Skinnemoen }, 327526dbbe0SHavard Skinnemoen }; 328526dbbe0SHavard Skinnemoen 329526dbbe0SHavard Skinnemoen static void npcm7xx_gpio_set_input(void *opaque, int line, int level) 330526dbbe0SHavard Skinnemoen { 331526dbbe0SHavard Skinnemoen NPCM7xxGPIOState *s = opaque; 332526dbbe0SHavard Skinnemoen 333526dbbe0SHavard Skinnemoen trace_npcm7xx_gpio_set_input(DEVICE(s)->canonical_path, line, level); 334526dbbe0SHavard Skinnemoen 335526dbbe0SHavard Skinnemoen g_assert(line >= 0 && line < NPCM7XX_GPIO_NR_PINS); 336526dbbe0SHavard Skinnemoen 337526dbbe0SHavard Skinnemoen s->ext_driven = deposit32(s->ext_driven, line, 1, level >= 0); 338526dbbe0SHavard Skinnemoen s->ext_level = deposit32(s->ext_level, line, 1, level > 0); 339526dbbe0SHavard Skinnemoen 340526dbbe0SHavard Skinnemoen npcm7xx_gpio_update_pins(s, BIT(line)); 341526dbbe0SHavard Skinnemoen } 342526dbbe0SHavard Skinnemoen 343526dbbe0SHavard Skinnemoen static void npcm7xx_gpio_enter_reset(Object *obj, ResetType type) 344526dbbe0SHavard Skinnemoen { 345526dbbe0SHavard Skinnemoen NPCM7xxGPIOState *s = NPCM7XX_GPIO(obj); 346526dbbe0SHavard Skinnemoen 347526dbbe0SHavard Skinnemoen memset(s->regs, 0, sizeof(s->regs)); 348526dbbe0SHavard Skinnemoen 349526dbbe0SHavard Skinnemoen s->regs[NPCM7XX_GPIO_PU] = s->reset_pu; 350526dbbe0SHavard Skinnemoen s->regs[NPCM7XX_GPIO_PD] = s->reset_pd; 351526dbbe0SHavard Skinnemoen s->regs[NPCM7XX_GPIO_OSRC] = s->reset_osrc; 352526dbbe0SHavard Skinnemoen s->regs[NPCM7XX_GPIO_ODSC] = s->reset_odsc; 353526dbbe0SHavard Skinnemoen } 354526dbbe0SHavard Skinnemoen 355ad80e367SPeter Maydell static void npcm7xx_gpio_hold_reset(Object *obj, ResetType type) 356526dbbe0SHavard Skinnemoen { 357526dbbe0SHavard Skinnemoen NPCM7xxGPIOState *s = NPCM7XX_GPIO(obj); 358526dbbe0SHavard Skinnemoen 359526dbbe0SHavard Skinnemoen npcm7xx_gpio_update_pins(s, -1); 360526dbbe0SHavard Skinnemoen } 361526dbbe0SHavard Skinnemoen 362526dbbe0SHavard Skinnemoen static void npcm7xx_gpio_init(Object *obj) 363526dbbe0SHavard Skinnemoen { 364526dbbe0SHavard Skinnemoen NPCM7xxGPIOState *s = NPCM7XX_GPIO(obj); 365526dbbe0SHavard Skinnemoen DeviceState *dev = DEVICE(obj); 366526dbbe0SHavard Skinnemoen 367526dbbe0SHavard Skinnemoen memory_region_init_io(&s->mmio, obj, &npcm7xx_gpio_regs_ops, s, 368526dbbe0SHavard Skinnemoen "regs", NPCM7XX_GPIO_REGS_SIZE); 369526dbbe0SHavard Skinnemoen sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); 370526dbbe0SHavard Skinnemoen sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq); 371526dbbe0SHavard Skinnemoen 372526dbbe0SHavard Skinnemoen qdev_init_gpio_in(dev, npcm7xx_gpio_set_input, NPCM7XX_GPIO_NR_PINS); 373526dbbe0SHavard Skinnemoen qdev_init_gpio_out(dev, s->output, NPCM7XX_GPIO_NR_PINS); 374526dbbe0SHavard Skinnemoen } 375526dbbe0SHavard Skinnemoen 376526dbbe0SHavard Skinnemoen static const VMStateDescription vmstate_npcm7xx_gpio = { 377526dbbe0SHavard Skinnemoen .name = "npcm7xx-gpio", 378526dbbe0SHavard Skinnemoen .version_id = 0, 379526dbbe0SHavard Skinnemoen .minimum_version_id = 0, 3803b9e779bSRichard Henderson .fields = (const VMStateField[]) { 381526dbbe0SHavard Skinnemoen VMSTATE_UINT32(pin_level, NPCM7xxGPIOState), 382526dbbe0SHavard Skinnemoen VMSTATE_UINT32(ext_level, NPCM7xxGPIOState), 383526dbbe0SHavard Skinnemoen VMSTATE_UINT32(ext_driven, NPCM7xxGPIOState), 384526dbbe0SHavard Skinnemoen VMSTATE_UINT32_ARRAY(regs, NPCM7xxGPIOState, NPCM7XX_GPIO_NR_REGS), 385526dbbe0SHavard Skinnemoen VMSTATE_END_OF_LIST(), 386526dbbe0SHavard Skinnemoen }, 387526dbbe0SHavard Skinnemoen }; 388526dbbe0SHavard Skinnemoen 389*de531a6bSRichard Henderson static const Property npcm7xx_gpio_properties[] = { 390526dbbe0SHavard Skinnemoen /* Bit n set => pin n has pullup enabled by default. */ 391526dbbe0SHavard Skinnemoen DEFINE_PROP_UINT32("reset-pullup", NPCM7xxGPIOState, reset_pu, 0), 392526dbbe0SHavard Skinnemoen /* Bit n set => pin n has pulldown enabled by default. */ 393526dbbe0SHavard Skinnemoen DEFINE_PROP_UINT32("reset-pulldown", NPCM7xxGPIOState, reset_pd, 0), 394526dbbe0SHavard Skinnemoen /* Bit n set => pin n has high slew rate by default. */ 395526dbbe0SHavard Skinnemoen DEFINE_PROP_UINT32("reset-osrc", NPCM7xxGPIOState, reset_osrc, 0), 396526dbbe0SHavard Skinnemoen /* Bit n set => pin n has high drive strength by default. */ 397526dbbe0SHavard Skinnemoen DEFINE_PROP_UINT32("reset-odsc", NPCM7xxGPIOState, reset_odsc, 0), 398526dbbe0SHavard Skinnemoen DEFINE_PROP_END_OF_LIST(), 399526dbbe0SHavard Skinnemoen }; 400526dbbe0SHavard Skinnemoen 401526dbbe0SHavard Skinnemoen static void npcm7xx_gpio_class_init(ObjectClass *klass, void *data) 402526dbbe0SHavard Skinnemoen { 403526dbbe0SHavard Skinnemoen ResettableClass *reset = RESETTABLE_CLASS(klass); 404526dbbe0SHavard Skinnemoen DeviceClass *dc = DEVICE_CLASS(klass); 405526dbbe0SHavard Skinnemoen 406526dbbe0SHavard Skinnemoen QEMU_BUILD_BUG_ON(NPCM7XX_GPIO_REGS_END > NPCM7XX_GPIO_NR_REGS); 407526dbbe0SHavard Skinnemoen 408526dbbe0SHavard Skinnemoen dc->desc = "NPCM7xx GPIO Controller"; 409526dbbe0SHavard Skinnemoen dc->vmsd = &vmstate_npcm7xx_gpio; 410526dbbe0SHavard Skinnemoen reset->phases.enter = npcm7xx_gpio_enter_reset; 411526dbbe0SHavard Skinnemoen reset->phases.hold = npcm7xx_gpio_hold_reset; 412526dbbe0SHavard Skinnemoen device_class_set_props(dc, npcm7xx_gpio_properties); 413526dbbe0SHavard Skinnemoen } 414526dbbe0SHavard Skinnemoen 415526dbbe0SHavard Skinnemoen static const TypeInfo npcm7xx_gpio_types[] = { 416526dbbe0SHavard Skinnemoen { 417526dbbe0SHavard Skinnemoen .name = TYPE_NPCM7XX_GPIO, 418526dbbe0SHavard Skinnemoen .parent = TYPE_SYS_BUS_DEVICE, 419526dbbe0SHavard Skinnemoen .instance_size = sizeof(NPCM7xxGPIOState), 420526dbbe0SHavard Skinnemoen .class_init = npcm7xx_gpio_class_init, 421526dbbe0SHavard Skinnemoen .instance_init = npcm7xx_gpio_init, 422526dbbe0SHavard Skinnemoen }, 423526dbbe0SHavard Skinnemoen }; 424526dbbe0SHavard Skinnemoen DEFINE_TYPES(npcm7xx_gpio_types); 425