2 * Nuvoton NPCM7xx General Purpose Input / Output (GPIO)
4 * Copyright 2020 Google LLC
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * version 2 as published by the Free Software Foundation.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
16 #include "qemu/osdep.h"
18 #include "hw/gpio/npcm7xx_gpio.h"
20 #include "hw/qdev-properties.h"
21 #include "migration/vmstate.h"
22 #include "qapi/error.h"
24 #include "qemu/module.h"
25 #include "qemu/units.h"
28 /* 32-bit register indices. */
29 enum NPCM7xxGPIORegister
{
55 NPCM7XX_GPIO_DOS
= 0x68 / sizeof(uint32_t),
59 NPCM7XX_GPIO_TLOCK2
= 0x7c / sizeof(uint32_t),
60 NPCM7XX_GPIO_REGS_END
,
63 #define NPCM7XX_GPIO_REGS_SIZE (4 * KiB)
65 #define NPCM7XX_GPIO_LOCK_MAGIC1 (0xc0defa73)
66 #define NPCM7XX_GPIO_LOCK_MAGIC2 (0xc0de1248)
68 static void npcm7xx_gpio_update_events(NPCM7xxGPIOState
*s
, uint32_t din_diff
)
70 uint32_t din_new
= s
->regs
[NPCM7XX_GPIO_DIN
];
72 /* Trigger on high level */
73 s
->regs
[NPCM7XX_GPIO_EVST
] |= din_new
& ~s
->regs
[NPCM7XX_GPIO_EVTYP
];
74 /* Trigger on both edges */
75 s
->regs
[NPCM7XX_GPIO_EVST
] |= (din_diff
& s
->regs
[NPCM7XX_GPIO_EVTYP
]
76 & s
->regs
[NPCM7XX_GPIO_EVBE
]);
77 /* Trigger on rising edge */
78 s
->regs
[NPCM7XX_GPIO_EVST
] |= (din_diff
& din_new
79 & s
->regs
[NPCM7XX_GPIO_EVTYP
]);
81 trace_npcm7xx_gpio_update_events(DEVICE(s
)->canonical_path
,
82 s
->regs
[NPCM7XX_GPIO_EVST
],
83 s
->regs
[NPCM7XX_GPIO_EVEN
]);
84 qemu_set_irq(s
->irq
, !!(s
->regs
[NPCM7XX_GPIO_EVST
]
85 & s
->regs
[NPCM7XX_GPIO_EVEN
]));
88 static void npcm7xx_gpio_update_pins(NPCM7xxGPIOState
*s
, uint32_t diff
)
97 /* Calculate level of each pin driven by GPIO controller. */
98 drive_lvl
= s
->regs
[NPCM7XX_GPIO_DOUT
] ^ s
->regs
[NPCM7XX_GPIO_POL
];
99 /* If OTYP=1, only drive low (open drain) */
100 drive_en
= s
->regs
[NPCM7XX_GPIO_OE
] & ~(s
->regs
[NPCM7XX_GPIO_OTYP
]
103 * If a pin is driven to opposite levels by the GPIO controller and the
104 * external driver, the result is undefined.
106 undefined
= drive_en
& s
->ext_driven
& (drive_lvl
^ s
->ext_level
);
108 qemu_log_mask(LOG_GUEST_ERROR
,
109 "%s: pins have multiple drivers: 0x%" PRIx32
"\n",
110 DEVICE(s
)->canonical_path
, undefined
);
113 not_driven
= ~(drive_en
| s
->ext_driven
);
114 pin_diff
= s
->pin_level
;
116 /* Set pins to externally driven level. */
117 s
->pin_level
= s
->ext_level
& s
->ext_driven
;
118 /* Set internally driven pins, ignoring any conflicts. */
119 s
->pin_level
|= drive_lvl
& drive_en
;
120 /* Pull up undriven pins with internal pull-up enabled. */
121 s
->pin_level
|= not_driven
& s
->regs
[NPCM7XX_GPIO_PU
];
122 /* Pins not driven, pulled up or pulled down are undefined */
123 undefined
|= not_driven
& ~(s
->regs
[NPCM7XX_GPIO_PU
]
124 | s
->regs
[NPCM7XX_GPIO_PD
]);
126 /* If any pins changed state, update the outgoing GPIOs. */
127 pin_diff
^= s
->pin_level
;
128 pin_diff
|= undefined
& diff
;
132 for (i
= 0; i
< NPCM7XX_GPIO_NR_PINS
; i
++) {
133 uint32_t mask
= BIT(i
);
134 if (pin_diff
& mask
) {
135 int level
= (undefined
& mask
) ? -1 : !!(s
->pin_level
& mask
);
136 trace_npcm7xx_gpio_set_output(DEVICE(s
)->canonical_path
,
138 qemu_set_irq(s
->output
[i
], level
);
143 /* Calculate new value of DIN after masking and polarity setting. */
144 din_old
= s
->regs
[NPCM7XX_GPIO_DIN
];
145 s
->regs
[NPCM7XX_GPIO_DIN
] = ((s
->pin_level
& s
->regs
[NPCM7XX_GPIO_IEM
])
146 ^ s
->regs
[NPCM7XX_GPIO_POL
]);
148 /* See if any new events triggered because of all this. */
149 npcm7xx_gpio_update_events(s
, din_old
^ s
->regs
[NPCM7XX_GPIO_DIN
]);
152 static bool npcm7xx_gpio_is_locked(NPCM7xxGPIOState
*s
)
154 return s
->regs
[NPCM7XX_GPIO_TLOCK1
] == 1;
157 static uint64_t npcm7xx_gpio_regs_read(void *opaque
, hwaddr addr
,
160 hwaddr reg
= addr
/ sizeof(uint32_t);
161 NPCM7xxGPIOState
*s
= opaque
;
165 case NPCM7XX_GPIO_TLOCK1
... NPCM7XX_GPIO_EVEN
:
166 case NPCM7XX_GPIO_EVST
... NPCM7XX_GPIO_ODSC
:
167 value
= s
->regs
[reg
];
170 case NPCM7XX_GPIO_EVENS
... NPCM7XX_GPIO_EVENC
:
171 case NPCM7XX_GPIO_DOS
... NPCM7XX_GPIO_TLOCK2
:
172 qemu_log_mask(LOG_GUEST_ERROR
,
173 "%s: read from write-only register 0x%" HWADDR_PRIx
"\n",
174 DEVICE(s
)->canonical_path
, addr
);
178 qemu_log_mask(LOG_GUEST_ERROR
,
179 "%s: read from invalid offset 0x%" HWADDR_PRIx
"\n",
180 DEVICE(s
)->canonical_path
, addr
);
184 trace_npcm7xx_gpio_read(DEVICE(s
)->canonical_path
, addr
, value
);
189 static void npcm7xx_gpio_regs_write(void *opaque
, hwaddr addr
, uint64_t v
,
192 hwaddr reg
= addr
/ sizeof(uint32_t);
193 NPCM7xxGPIOState
*s
= opaque
;
197 trace_npcm7xx_gpio_write(DEVICE(s
)->canonical_path
, addr
, v
);
199 if (npcm7xx_gpio_is_locked(s
)) {
201 case NPCM7XX_GPIO_TLOCK1
:
202 if (s
->regs
[NPCM7XX_GPIO_TLOCK2
] == NPCM7XX_GPIO_LOCK_MAGIC2
&&
203 value
== NPCM7XX_GPIO_LOCK_MAGIC1
) {
204 s
->regs
[NPCM7XX_GPIO_TLOCK1
] = 0;
205 s
->regs
[NPCM7XX_GPIO_TLOCK2
] = 0;
209 case NPCM7XX_GPIO_TLOCK2
:
210 s
->regs
[reg
] = value
;
214 qemu_log_mask(LOG_GUEST_ERROR
,
215 "%s: write to locked register @ 0x%" HWADDR_PRIx
"\n",
216 DEVICE(s
)->canonical_path
, addr
);
223 diff
= s
->regs
[reg
] ^ value
;
226 case NPCM7XX_GPIO_TLOCK1
:
227 case NPCM7XX_GPIO_TLOCK2
:
228 s
->regs
[NPCM7XX_GPIO_TLOCK1
] = 1;
229 s
->regs
[NPCM7XX_GPIO_TLOCK2
] = 0;
232 case NPCM7XX_GPIO_DIN
:
233 qemu_log_mask(LOG_GUEST_ERROR
,
234 "%s: write to read-only register @ 0x%" HWADDR_PRIx
"\n",
235 DEVICE(s
)->canonical_path
, addr
);
238 case NPCM7XX_GPIO_POL
:
239 case NPCM7XX_GPIO_DOUT
:
240 case NPCM7XX_GPIO_OE
:
241 case NPCM7XX_GPIO_OTYP
:
242 case NPCM7XX_GPIO_PU
:
243 case NPCM7XX_GPIO_PD
:
244 case NPCM7XX_GPIO_IEM
:
245 s
->regs
[reg
] = value
;
246 npcm7xx_gpio_update_pins(s
, diff
);
249 case NPCM7XX_GPIO_DOS
:
250 s
->regs
[NPCM7XX_GPIO_DOUT
] |= value
;
251 npcm7xx_gpio_update_pins(s
, value
);
253 case NPCM7XX_GPIO_DOC
:
254 s
->regs
[NPCM7XX_GPIO_DOUT
] &= ~value
;
255 npcm7xx_gpio_update_pins(s
, value
);
257 case NPCM7XX_GPIO_OES
:
258 s
->regs
[NPCM7XX_GPIO_OE
] |= value
;
259 npcm7xx_gpio_update_pins(s
, value
);
261 case NPCM7XX_GPIO_OEC
:
262 s
->regs
[NPCM7XX_GPIO_OE
] &= ~value
;
263 npcm7xx_gpio_update_pins(s
, value
);
266 case NPCM7XX_GPIO_EVTYP
:
267 case NPCM7XX_GPIO_EVBE
:
268 case NPCM7XX_GPIO_EVEN
:
269 s
->regs
[reg
] = value
;
270 npcm7xx_gpio_update_events(s
, 0);
273 case NPCM7XX_GPIO_EVENS
:
274 s
->regs
[NPCM7XX_GPIO_EVEN
] |= value
;
275 npcm7xx_gpio_update_events(s
, 0);
277 case NPCM7XX_GPIO_EVENC
:
278 s
->regs
[NPCM7XX_GPIO_EVEN
] &= ~value
;
279 npcm7xx_gpio_update_events(s
, 0);
282 case NPCM7XX_GPIO_EVST
:
283 s
->regs
[reg
] &= ~value
;
284 npcm7xx_gpio_update_events(s
, 0);
287 case NPCM7XX_GPIO_MP
:
288 case NPCM7XX_GPIO_DBNC
:
289 case NPCM7XX_GPIO_OSRC
:
290 case NPCM7XX_GPIO_ODSC
:
291 /* Nothing to do; just store the value. */
292 s
->regs
[reg
] = value
;
295 case NPCM7XX_GPIO_OBL0
:
296 case NPCM7XX_GPIO_OBL1
:
297 case NPCM7XX_GPIO_OBL2
:
298 case NPCM7XX_GPIO_OBL3
:
299 s
->regs
[reg
] = value
;
300 qemu_log_mask(LOG_UNIMP
, "%s: Blinking is not implemented\n",
304 case NPCM7XX_GPIO_SPLCK
:
305 case NPCM7XX_GPIO_MPLCK
:
306 qemu_log_mask(LOG_UNIMP
, "%s: Per-pin lock is not implemented\n",
311 qemu_log_mask(LOG_GUEST_ERROR
,
312 "%s: write to invalid offset 0x%" HWADDR_PRIx
"\n",
313 DEVICE(s
)->canonical_path
, addr
);
318 static const MemoryRegionOps npcm7xx_gpio_regs_ops
= {
319 .read
= npcm7xx_gpio_regs_read
,
320 .write
= npcm7xx_gpio_regs_write
,
321 .endianness
= DEVICE_NATIVE_ENDIAN
,
323 .min_access_size
= 4,
324 .max_access_size
= 4,
329 static void npcm7xx_gpio_set_input(void *opaque
, int line
, int level
)
331 NPCM7xxGPIOState
*s
= opaque
;
333 trace_npcm7xx_gpio_set_input(DEVICE(s
)->canonical_path
, line
, level
);
335 g_assert(line
>= 0 && line
< NPCM7XX_GPIO_NR_PINS
);
337 s
->ext_driven
= deposit32(s
->ext_driven
, line
, 1, level
>= 0);
338 s
->ext_level
= deposit32(s
->ext_level
, line
, 1, level
> 0);
340 npcm7xx_gpio_update_pins(s
, BIT(line
));
343 static void npcm7xx_gpio_enter_reset(Object
*obj
, ResetType type
)
345 NPCM7xxGPIOState
*s
= NPCM7XX_GPIO(obj
);
347 memset(s
->regs
, 0, sizeof(s
->regs
));
349 s
->regs
[NPCM7XX_GPIO_PU
] = s
->reset_pu
;
350 s
->regs
[NPCM7XX_GPIO_PD
] = s
->reset_pd
;
351 s
->regs
[NPCM7XX_GPIO_OSRC
] = s
->reset_osrc
;
352 s
->regs
[NPCM7XX_GPIO_ODSC
] = s
->reset_odsc
;
355 static void npcm7xx_gpio_hold_reset(Object
*obj
)
357 NPCM7xxGPIOState
*s
= NPCM7XX_GPIO(obj
);
359 npcm7xx_gpio_update_pins(s
, -1);
362 static void npcm7xx_gpio_init(Object
*obj
)
364 NPCM7xxGPIOState
*s
= NPCM7XX_GPIO(obj
);
365 DeviceState
*dev
= DEVICE(obj
);
367 memory_region_init_io(&s
->mmio
, obj
, &npcm7xx_gpio_regs_ops
, s
,
368 "regs", NPCM7XX_GPIO_REGS_SIZE
);
369 sysbus_init_mmio(SYS_BUS_DEVICE(obj
), &s
->mmio
);
370 sysbus_init_irq(SYS_BUS_DEVICE(obj
), &s
->irq
);
372 qdev_init_gpio_in(dev
, npcm7xx_gpio_set_input
, NPCM7XX_GPIO_NR_PINS
);
373 qdev_init_gpio_out(dev
, s
->output
, NPCM7XX_GPIO_NR_PINS
);
376 static const VMStateDescription vmstate_npcm7xx_gpio
= {
377 .name
= "npcm7xx-gpio",
379 .minimum_version_id
= 0,
380 .fields
= (VMStateField
[]) {
381 VMSTATE_UINT32(pin_level
, NPCM7xxGPIOState
),
382 VMSTATE_UINT32(ext_level
, NPCM7xxGPIOState
),
383 VMSTATE_UINT32(ext_driven
, NPCM7xxGPIOState
),
384 VMSTATE_UINT32_ARRAY(regs
, NPCM7xxGPIOState
, NPCM7XX_GPIO_NR_REGS
),
385 VMSTATE_END_OF_LIST(),
389 static Property npcm7xx_gpio_properties
[] = {
390 /* Bit n set => pin n has pullup enabled by default. */
391 DEFINE_PROP_UINT32("reset-pullup", NPCM7xxGPIOState
, reset_pu
, 0),
392 /* Bit n set => pin n has pulldown enabled by default. */
393 DEFINE_PROP_UINT32("reset-pulldown", NPCM7xxGPIOState
, reset_pd
, 0),
394 /* Bit n set => pin n has high slew rate by default. */
395 DEFINE_PROP_UINT32("reset-osrc", NPCM7xxGPIOState
, reset_osrc
, 0),
396 /* Bit n set => pin n has high drive strength by default. */
397 DEFINE_PROP_UINT32("reset-odsc", NPCM7xxGPIOState
, reset_odsc
, 0),
398 DEFINE_PROP_END_OF_LIST(),
401 static void npcm7xx_gpio_class_init(ObjectClass
*klass
, void *data
)
403 ResettableClass
*reset
= RESETTABLE_CLASS(klass
);
404 DeviceClass
*dc
= DEVICE_CLASS(klass
);
406 QEMU_BUILD_BUG_ON(NPCM7XX_GPIO_REGS_END
> NPCM7XX_GPIO_NR_REGS
);
408 dc
->desc
= "NPCM7xx GPIO Controller";
409 dc
->vmsd
= &vmstate_npcm7xx_gpio
;
410 reset
->phases
.enter
= npcm7xx_gpio_enter_reset
;
411 reset
->phases
.hold
= npcm7xx_gpio_hold_reset
;
412 device_class_set_props(dc
, npcm7xx_gpio_properties
);
415 static const TypeInfo npcm7xx_gpio_types
[] = {
417 .name
= TYPE_NPCM7XX_GPIO
,
418 .parent
= TYPE_SYS_BUS_DEVICE
,
419 .instance_size
= sizeof(NPCM7xxGPIOState
),
420 .class_init
= npcm7xx_gpio_class_init
,
421 .instance_init
= npcm7xx_gpio_init
,
424 DEFINE_TYPES(npcm7xx_gpio_types
);