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 switch (reg) { 224526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_TLOCK1: 225526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_TLOCK2: 226526dbbe0SHavard Skinnemoen s->regs[NPCM7XX_GPIO_TLOCK1] = 1; 227526dbbe0SHavard Skinnemoen s->regs[NPCM7XX_GPIO_TLOCK2] = 0; 228526dbbe0SHavard Skinnemoen break; 229526dbbe0SHavard Skinnemoen 230526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_DIN: 231526dbbe0SHavard Skinnemoen qemu_log_mask(LOG_GUEST_ERROR, 232526dbbe0SHavard Skinnemoen "%s: write to read-only register @ 0x%" HWADDR_PRIx "\n", 233526dbbe0SHavard Skinnemoen DEVICE(s)->canonical_path, addr); 234526dbbe0SHavard Skinnemoen break; 235526dbbe0SHavard Skinnemoen 236526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_POL: 237526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_DOUT: 238526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_OE: 239526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_OTYP: 240526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_PU: 241526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_PD: 242526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_IEM: 2433b2e22c0SPatrick Venture diff = s->regs[reg] ^ value; 244526dbbe0SHavard Skinnemoen s->regs[reg] = value; 245526dbbe0SHavard Skinnemoen npcm7xx_gpio_update_pins(s, diff); 246526dbbe0SHavard Skinnemoen break; 247526dbbe0SHavard Skinnemoen 248526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_DOS: 249526dbbe0SHavard Skinnemoen s->regs[NPCM7XX_GPIO_DOUT] |= value; 250526dbbe0SHavard Skinnemoen npcm7xx_gpio_update_pins(s, value); 251526dbbe0SHavard Skinnemoen break; 252526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_DOC: 253526dbbe0SHavard Skinnemoen s->regs[NPCM7XX_GPIO_DOUT] &= ~value; 254526dbbe0SHavard Skinnemoen npcm7xx_gpio_update_pins(s, value); 255526dbbe0SHavard Skinnemoen break; 256526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_OES: 257526dbbe0SHavard Skinnemoen s->regs[NPCM7XX_GPIO_OE] |= value; 258526dbbe0SHavard Skinnemoen npcm7xx_gpio_update_pins(s, value); 259526dbbe0SHavard Skinnemoen break; 260526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_OEC: 261526dbbe0SHavard Skinnemoen s->regs[NPCM7XX_GPIO_OE] &= ~value; 262526dbbe0SHavard Skinnemoen npcm7xx_gpio_update_pins(s, value); 263526dbbe0SHavard Skinnemoen break; 264526dbbe0SHavard Skinnemoen 265526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_EVTYP: 266526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_EVBE: 267526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_EVEN: 268526dbbe0SHavard Skinnemoen s->regs[reg] = value; 269526dbbe0SHavard Skinnemoen npcm7xx_gpio_update_events(s, 0); 270526dbbe0SHavard Skinnemoen break; 271526dbbe0SHavard Skinnemoen 272526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_EVENS: 273526dbbe0SHavard Skinnemoen s->regs[NPCM7XX_GPIO_EVEN] |= value; 274526dbbe0SHavard Skinnemoen npcm7xx_gpio_update_events(s, 0); 275526dbbe0SHavard Skinnemoen break; 276526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_EVENC: 277526dbbe0SHavard Skinnemoen s->regs[NPCM7XX_GPIO_EVEN] &= ~value; 278526dbbe0SHavard Skinnemoen npcm7xx_gpio_update_events(s, 0); 279526dbbe0SHavard Skinnemoen break; 280526dbbe0SHavard Skinnemoen 281526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_EVST: 282526dbbe0SHavard Skinnemoen s->regs[reg] &= ~value; 283526dbbe0SHavard Skinnemoen npcm7xx_gpio_update_events(s, 0); 284526dbbe0SHavard Skinnemoen break; 285526dbbe0SHavard Skinnemoen 286526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_MP: 287526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_DBNC: 288526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_OSRC: 289526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_ODSC: 290526dbbe0SHavard Skinnemoen /* Nothing to do; just store the value. */ 291526dbbe0SHavard Skinnemoen s->regs[reg] = value; 292526dbbe0SHavard Skinnemoen break; 293526dbbe0SHavard Skinnemoen 294526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_OBL0: 295526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_OBL1: 296526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_OBL2: 297526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_OBL3: 298526dbbe0SHavard Skinnemoen s->regs[reg] = value; 299526dbbe0SHavard Skinnemoen qemu_log_mask(LOG_UNIMP, "%s: Blinking is not implemented\n", 300526dbbe0SHavard Skinnemoen __func__); 301526dbbe0SHavard Skinnemoen break; 302526dbbe0SHavard Skinnemoen 303526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_SPLCK: 304526dbbe0SHavard Skinnemoen case NPCM7XX_GPIO_MPLCK: 305526dbbe0SHavard Skinnemoen qemu_log_mask(LOG_UNIMP, "%s: Per-pin lock is not implemented\n", 306526dbbe0SHavard Skinnemoen __func__); 307526dbbe0SHavard Skinnemoen break; 308526dbbe0SHavard Skinnemoen 309526dbbe0SHavard Skinnemoen default: 310526dbbe0SHavard Skinnemoen qemu_log_mask(LOG_GUEST_ERROR, 311526dbbe0SHavard Skinnemoen "%s: write to invalid offset 0x%" HWADDR_PRIx "\n", 312526dbbe0SHavard Skinnemoen DEVICE(s)->canonical_path, addr); 313526dbbe0SHavard Skinnemoen break; 314526dbbe0SHavard Skinnemoen } 315526dbbe0SHavard Skinnemoen } 316526dbbe0SHavard Skinnemoen 317526dbbe0SHavard Skinnemoen static const MemoryRegionOps npcm7xx_gpio_regs_ops = { 318526dbbe0SHavard Skinnemoen .read = npcm7xx_gpio_regs_read, 319526dbbe0SHavard Skinnemoen .write = npcm7xx_gpio_regs_write, 320526dbbe0SHavard Skinnemoen .endianness = DEVICE_NATIVE_ENDIAN, 321526dbbe0SHavard Skinnemoen .valid = { 322526dbbe0SHavard Skinnemoen .min_access_size = 4, 323526dbbe0SHavard Skinnemoen .max_access_size = 4, 324526dbbe0SHavard Skinnemoen .unaligned = false, 325526dbbe0SHavard Skinnemoen }, 326526dbbe0SHavard Skinnemoen }; 327526dbbe0SHavard Skinnemoen 328526dbbe0SHavard Skinnemoen static void npcm7xx_gpio_set_input(void *opaque, int line, int level) 329526dbbe0SHavard Skinnemoen { 330526dbbe0SHavard Skinnemoen NPCM7xxGPIOState *s = opaque; 331526dbbe0SHavard Skinnemoen 332526dbbe0SHavard Skinnemoen trace_npcm7xx_gpio_set_input(DEVICE(s)->canonical_path, line, level); 333526dbbe0SHavard Skinnemoen 334526dbbe0SHavard Skinnemoen g_assert(line >= 0 && line < NPCM7XX_GPIO_NR_PINS); 335526dbbe0SHavard Skinnemoen 336526dbbe0SHavard Skinnemoen s->ext_driven = deposit32(s->ext_driven, line, 1, level >= 0); 337526dbbe0SHavard Skinnemoen s->ext_level = deposit32(s->ext_level, line, 1, level > 0); 338526dbbe0SHavard Skinnemoen 339526dbbe0SHavard Skinnemoen npcm7xx_gpio_update_pins(s, BIT(line)); 340526dbbe0SHavard Skinnemoen } 341526dbbe0SHavard Skinnemoen 342526dbbe0SHavard Skinnemoen static void npcm7xx_gpio_enter_reset(Object *obj, ResetType type) 343526dbbe0SHavard Skinnemoen { 344526dbbe0SHavard Skinnemoen NPCM7xxGPIOState *s = NPCM7XX_GPIO(obj); 345526dbbe0SHavard Skinnemoen 346526dbbe0SHavard Skinnemoen memset(s->regs, 0, sizeof(s->regs)); 347526dbbe0SHavard Skinnemoen 348526dbbe0SHavard Skinnemoen s->regs[NPCM7XX_GPIO_PU] = s->reset_pu; 349526dbbe0SHavard Skinnemoen s->regs[NPCM7XX_GPIO_PD] = s->reset_pd; 350526dbbe0SHavard Skinnemoen s->regs[NPCM7XX_GPIO_OSRC] = s->reset_osrc; 351526dbbe0SHavard Skinnemoen s->regs[NPCM7XX_GPIO_ODSC] = s->reset_odsc; 352526dbbe0SHavard Skinnemoen } 353526dbbe0SHavard Skinnemoen 354ad80e367SPeter Maydell static void npcm7xx_gpio_hold_reset(Object *obj, ResetType type) 355526dbbe0SHavard Skinnemoen { 356526dbbe0SHavard Skinnemoen NPCM7xxGPIOState *s = NPCM7XX_GPIO(obj); 357526dbbe0SHavard Skinnemoen 358526dbbe0SHavard Skinnemoen npcm7xx_gpio_update_pins(s, -1); 359526dbbe0SHavard Skinnemoen } 360526dbbe0SHavard Skinnemoen 361526dbbe0SHavard Skinnemoen static void npcm7xx_gpio_init(Object *obj) 362526dbbe0SHavard Skinnemoen { 363526dbbe0SHavard Skinnemoen NPCM7xxGPIOState *s = NPCM7XX_GPIO(obj); 364526dbbe0SHavard Skinnemoen DeviceState *dev = DEVICE(obj); 365526dbbe0SHavard Skinnemoen 366526dbbe0SHavard Skinnemoen memory_region_init_io(&s->mmio, obj, &npcm7xx_gpio_regs_ops, s, 367526dbbe0SHavard Skinnemoen "regs", NPCM7XX_GPIO_REGS_SIZE); 368526dbbe0SHavard Skinnemoen sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); 369526dbbe0SHavard Skinnemoen sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq); 370526dbbe0SHavard Skinnemoen 371526dbbe0SHavard Skinnemoen qdev_init_gpio_in(dev, npcm7xx_gpio_set_input, NPCM7XX_GPIO_NR_PINS); 372526dbbe0SHavard Skinnemoen qdev_init_gpio_out(dev, s->output, NPCM7XX_GPIO_NR_PINS); 373526dbbe0SHavard Skinnemoen } 374526dbbe0SHavard Skinnemoen 375526dbbe0SHavard Skinnemoen static const VMStateDescription vmstate_npcm7xx_gpio = { 376526dbbe0SHavard Skinnemoen .name = "npcm7xx-gpio", 377526dbbe0SHavard Skinnemoen .version_id = 0, 378526dbbe0SHavard Skinnemoen .minimum_version_id = 0, 3793b9e779bSRichard Henderson .fields = (const VMStateField[]) { 380526dbbe0SHavard Skinnemoen VMSTATE_UINT32(pin_level, NPCM7xxGPIOState), 381526dbbe0SHavard Skinnemoen VMSTATE_UINT32(ext_level, NPCM7xxGPIOState), 382526dbbe0SHavard Skinnemoen VMSTATE_UINT32(ext_driven, NPCM7xxGPIOState), 383526dbbe0SHavard Skinnemoen VMSTATE_UINT32_ARRAY(regs, NPCM7xxGPIOState, NPCM7XX_GPIO_NR_REGS), 384526dbbe0SHavard Skinnemoen VMSTATE_END_OF_LIST(), 385526dbbe0SHavard Skinnemoen }, 386526dbbe0SHavard Skinnemoen }; 387526dbbe0SHavard Skinnemoen 388de531a6bSRichard Henderson static const Property npcm7xx_gpio_properties[] = { 389526dbbe0SHavard Skinnemoen /* Bit n set => pin n has pullup enabled by default. */ 390526dbbe0SHavard Skinnemoen DEFINE_PROP_UINT32("reset-pullup", NPCM7xxGPIOState, reset_pu, 0), 391526dbbe0SHavard Skinnemoen /* Bit n set => pin n has pulldown enabled by default. */ 392526dbbe0SHavard Skinnemoen DEFINE_PROP_UINT32("reset-pulldown", NPCM7xxGPIOState, reset_pd, 0), 393526dbbe0SHavard Skinnemoen /* Bit n set => pin n has high slew rate by default. */ 394526dbbe0SHavard Skinnemoen DEFINE_PROP_UINT32("reset-osrc", NPCM7xxGPIOState, reset_osrc, 0), 395526dbbe0SHavard Skinnemoen /* Bit n set => pin n has high drive strength by default. */ 396526dbbe0SHavard Skinnemoen DEFINE_PROP_UINT32("reset-odsc", NPCM7xxGPIOState, reset_odsc, 0), 397526dbbe0SHavard Skinnemoen }; 398526dbbe0SHavard Skinnemoen 399*12d1a768SPhilippe Mathieu-Daudé static void npcm7xx_gpio_class_init(ObjectClass *klass, const void *data) 400526dbbe0SHavard Skinnemoen { 401526dbbe0SHavard Skinnemoen ResettableClass *reset = RESETTABLE_CLASS(klass); 402526dbbe0SHavard Skinnemoen DeviceClass *dc = DEVICE_CLASS(klass); 403526dbbe0SHavard Skinnemoen 404526dbbe0SHavard Skinnemoen QEMU_BUILD_BUG_ON(NPCM7XX_GPIO_REGS_END > NPCM7XX_GPIO_NR_REGS); 405526dbbe0SHavard Skinnemoen 406526dbbe0SHavard Skinnemoen dc->desc = "NPCM7xx GPIO Controller"; 407526dbbe0SHavard Skinnemoen dc->vmsd = &vmstate_npcm7xx_gpio; 408526dbbe0SHavard Skinnemoen reset->phases.enter = npcm7xx_gpio_enter_reset; 409526dbbe0SHavard Skinnemoen reset->phases.hold = npcm7xx_gpio_hold_reset; 410526dbbe0SHavard Skinnemoen device_class_set_props(dc, npcm7xx_gpio_properties); 411526dbbe0SHavard Skinnemoen } 412526dbbe0SHavard Skinnemoen 413526dbbe0SHavard Skinnemoen static const TypeInfo npcm7xx_gpio_types[] = { 414526dbbe0SHavard Skinnemoen { 415526dbbe0SHavard Skinnemoen .name = TYPE_NPCM7XX_GPIO, 416526dbbe0SHavard Skinnemoen .parent = TYPE_SYS_BUS_DEVICE, 417526dbbe0SHavard Skinnemoen .instance_size = sizeof(NPCM7xxGPIOState), 418526dbbe0SHavard Skinnemoen .class_init = npcm7xx_gpio_class_init, 419526dbbe0SHavard Skinnemoen .instance_init = npcm7xx_gpio_init, 420526dbbe0SHavard Skinnemoen }, 421526dbbe0SHavard Skinnemoen }; 422526dbbe0SHavard Skinnemoen DEFINE_TYPES(npcm7xx_gpio_types); 423