Pointer properties can be set only by code, not by device_add. A device with a pointer property can work with device_add only when the property may remain null. This is the case for property "interrupt_vector" of device "etraxfs,pic". Add a comment there. Set cannot_instantiate_with_device_add_yet for the other devices with pointer properties, with a comment explaining why. Juha Riihimäki and Peter Maydell deserve my thanks for making "pointer property must not remain null" blatantly obvious in the OMAP devices. Only device "smbus-eeprom" is actually changed. The others are all sysbus devices, which get cannot_instantiate_with_device_add_yet set in their abstract base's class init function. Setting it again in their class init function is technically redundant, but serves as insurance for when sysbus devices become available with device_add, and as documentation. Signed-off-by: Markus Armbruster <armbru@redhat.com> Reviewed-by: Edgar E. Iglesias <edgar.iglesias@gmail.com> (for ETRAX) Signed-off-by: Andreas Färber <afaerber@suse.de>
811 lines
21 KiB
C
811 lines
21 KiB
C
/*
|
|
* TI OMAP processors GPIO emulation.
|
|
*
|
|
* Copyright (C) 2006-2008 Andrzej Zaborowski <balrog@zabor.org>
|
|
* Copyright (C) 2007-2009 Nokia Corporation
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation; either version 2 or
|
|
* (at your option) version 3 of the License.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "hw/hw.h"
|
|
#include "hw/arm/omap.h"
|
|
#include "hw/sysbus.h"
|
|
|
|
struct omap_gpio_s {
|
|
qemu_irq irq;
|
|
qemu_irq handler[16];
|
|
|
|
uint16_t inputs;
|
|
uint16_t outputs;
|
|
uint16_t dir;
|
|
uint16_t edge;
|
|
uint16_t mask;
|
|
uint16_t ints;
|
|
uint16_t pins;
|
|
};
|
|
|
|
#define TYPE_OMAP1_GPIO "omap-gpio"
|
|
#define OMAP1_GPIO(obj) \
|
|
OBJECT_CHECK(struct omap_gpif_s, (obj), TYPE_OMAP1_GPIO)
|
|
|
|
struct omap_gpif_s {
|
|
SysBusDevice parent_obj;
|
|
|
|
MemoryRegion iomem;
|
|
int mpu_model;
|
|
void *clk;
|
|
struct omap_gpio_s omap1;
|
|
};
|
|
|
|
/* General-Purpose I/O of OMAP1 */
|
|
static void omap_gpio_set(void *opaque, int line, int level)
|
|
{
|
|
struct omap_gpio_s *s = &((struct omap_gpif_s *) opaque)->omap1;
|
|
uint16_t prev = s->inputs;
|
|
|
|
if (level)
|
|
s->inputs |= 1 << line;
|
|
else
|
|
s->inputs &= ~(1 << line);
|
|
|
|
if (((s->edge & s->inputs & ~prev) | (~s->edge & ~s->inputs & prev)) &
|
|
(1 << line) & s->dir & ~s->mask) {
|
|
s->ints |= 1 << line;
|
|
qemu_irq_raise(s->irq);
|
|
}
|
|
}
|
|
|
|
static uint64_t omap_gpio_read(void *opaque, hwaddr addr,
|
|
unsigned size)
|
|
{
|
|
struct omap_gpio_s *s = (struct omap_gpio_s *) opaque;
|
|
int offset = addr & OMAP_MPUI_REG_MASK;
|
|
|
|
if (size != 2) {
|
|
return omap_badwidth_read16(opaque, addr);
|
|
}
|
|
|
|
switch (offset) {
|
|
case 0x00: /* DATA_INPUT */
|
|
return s->inputs & s->pins;
|
|
|
|
case 0x04: /* DATA_OUTPUT */
|
|
return s->outputs;
|
|
|
|
case 0x08: /* DIRECTION_CONTROL */
|
|
return s->dir;
|
|
|
|
case 0x0c: /* INTERRUPT_CONTROL */
|
|
return s->edge;
|
|
|
|
case 0x10: /* INTERRUPT_MASK */
|
|
return s->mask;
|
|
|
|
case 0x14: /* INTERRUPT_STATUS */
|
|
return s->ints;
|
|
|
|
case 0x18: /* PIN_CONTROL (not in OMAP310) */
|
|
OMAP_BAD_REG(addr);
|
|
return s->pins;
|
|
}
|
|
|
|
OMAP_BAD_REG(addr);
|
|
return 0;
|
|
}
|
|
|
|
static void omap_gpio_write(void *opaque, hwaddr addr,
|
|
uint64_t value, unsigned size)
|
|
{
|
|
struct omap_gpio_s *s = (struct omap_gpio_s *) opaque;
|
|
int offset = addr & OMAP_MPUI_REG_MASK;
|
|
uint16_t diff;
|
|
int ln;
|
|
|
|
if (size != 2) {
|
|
return omap_badwidth_write16(opaque, addr, value);
|
|
}
|
|
|
|
switch (offset) {
|
|
case 0x00: /* DATA_INPUT */
|
|
OMAP_RO_REG(addr);
|
|
return;
|
|
|
|
case 0x04: /* DATA_OUTPUT */
|
|
diff = (s->outputs ^ value) & ~s->dir;
|
|
s->outputs = value;
|
|
while ((ln = ffs(diff))) {
|
|
ln --;
|
|
if (s->handler[ln])
|
|
qemu_set_irq(s->handler[ln], (value >> ln) & 1);
|
|
diff &= ~(1 << ln);
|
|
}
|
|
break;
|
|
|
|
case 0x08: /* DIRECTION_CONTROL */
|
|
diff = s->outputs & (s->dir ^ value);
|
|
s->dir = value;
|
|
|
|
value = s->outputs & ~s->dir;
|
|
while ((ln = ffs(diff))) {
|
|
ln --;
|
|
if (s->handler[ln])
|
|
qemu_set_irq(s->handler[ln], (value >> ln) & 1);
|
|
diff &= ~(1 << ln);
|
|
}
|
|
break;
|
|
|
|
case 0x0c: /* INTERRUPT_CONTROL */
|
|
s->edge = value;
|
|
break;
|
|
|
|
case 0x10: /* INTERRUPT_MASK */
|
|
s->mask = value;
|
|
break;
|
|
|
|
case 0x14: /* INTERRUPT_STATUS */
|
|
s->ints &= ~value;
|
|
if (!s->ints)
|
|
qemu_irq_lower(s->irq);
|
|
break;
|
|
|
|
case 0x18: /* PIN_CONTROL (not in OMAP310 TRM) */
|
|
OMAP_BAD_REG(addr);
|
|
s->pins = value;
|
|
break;
|
|
|
|
default:
|
|
OMAP_BAD_REG(addr);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* *Some* sources say the memory region is 32-bit. */
|
|
static const MemoryRegionOps omap_gpio_ops = {
|
|
.read = omap_gpio_read,
|
|
.write = omap_gpio_write,
|
|
.endianness = DEVICE_NATIVE_ENDIAN,
|
|
};
|
|
|
|
static void omap_gpio_reset(struct omap_gpio_s *s)
|
|
{
|
|
s->inputs = 0;
|
|
s->outputs = ~0;
|
|
s->dir = ~0;
|
|
s->edge = ~0;
|
|
s->mask = ~0;
|
|
s->ints = 0;
|
|
s->pins = ~0;
|
|
}
|
|
|
|
struct omap2_gpio_s {
|
|
qemu_irq irq[2];
|
|
qemu_irq wkup;
|
|
qemu_irq *handler;
|
|
MemoryRegion iomem;
|
|
|
|
uint8_t revision;
|
|
uint8_t config[2];
|
|
uint32_t inputs;
|
|
uint32_t outputs;
|
|
uint32_t dir;
|
|
uint32_t level[2];
|
|
uint32_t edge[2];
|
|
uint32_t mask[2];
|
|
uint32_t wumask;
|
|
uint32_t ints[2];
|
|
uint32_t debounce;
|
|
uint8_t delay;
|
|
};
|
|
|
|
#define TYPE_OMAP2_GPIO "omap2-gpio"
|
|
#define OMAP2_GPIO(obj) \
|
|
OBJECT_CHECK(struct omap2_gpif_s, (obj), TYPE_OMAP2_GPIO)
|
|
|
|
struct omap2_gpif_s {
|
|
SysBusDevice parent_obj;
|
|
|
|
MemoryRegion iomem;
|
|
int mpu_model;
|
|
void *iclk;
|
|
void *fclk[6];
|
|
int modulecount;
|
|
struct omap2_gpio_s *modules;
|
|
qemu_irq *handler;
|
|
int autoidle;
|
|
int gpo;
|
|
};
|
|
|
|
/* General-Purpose Interface of OMAP2/3 */
|
|
static inline void omap2_gpio_module_int_update(struct omap2_gpio_s *s,
|
|
int line)
|
|
{
|
|
qemu_set_irq(s->irq[line], s->ints[line] & s->mask[line]);
|
|
}
|
|
|
|
static void omap2_gpio_module_wake(struct omap2_gpio_s *s, int line)
|
|
{
|
|
if (!(s->config[0] & (1 << 2))) /* ENAWAKEUP */
|
|
return;
|
|
if (!(s->config[0] & (3 << 3))) /* Force Idle */
|
|
return;
|
|
if (!(s->wumask & (1 << line)))
|
|
return;
|
|
|
|
qemu_irq_raise(s->wkup);
|
|
}
|
|
|
|
static inline void omap2_gpio_module_out_update(struct omap2_gpio_s *s,
|
|
uint32_t diff)
|
|
{
|
|
int ln;
|
|
|
|
s->outputs ^= diff;
|
|
diff &= ~s->dir;
|
|
while ((ln = ffs(diff))) {
|
|
ln --;
|
|
qemu_set_irq(s->handler[ln], (s->outputs >> ln) & 1);
|
|
diff &= ~(1 << ln);
|
|
}
|
|
}
|
|
|
|
static void omap2_gpio_module_level_update(struct omap2_gpio_s *s, int line)
|
|
{
|
|
s->ints[line] |= s->dir &
|
|
((s->inputs & s->level[1]) | (~s->inputs & s->level[0]));
|
|
omap2_gpio_module_int_update(s, line);
|
|
}
|
|
|
|
static inline void omap2_gpio_module_int(struct omap2_gpio_s *s, int line)
|
|
{
|
|
s->ints[0] |= 1 << line;
|
|
omap2_gpio_module_int_update(s, 0);
|
|
s->ints[1] |= 1 << line;
|
|
omap2_gpio_module_int_update(s, 1);
|
|
omap2_gpio_module_wake(s, line);
|
|
}
|
|
|
|
static void omap2_gpio_set(void *opaque, int line, int level)
|
|
{
|
|
struct omap2_gpif_s *p = opaque;
|
|
struct omap2_gpio_s *s = &p->modules[line >> 5];
|
|
|
|
line &= 31;
|
|
if (level) {
|
|
if (s->dir & (1 << line) & ((~s->inputs & s->edge[0]) | s->level[1]))
|
|
omap2_gpio_module_int(s, line);
|
|
s->inputs |= 1 << line;
|
|
} else {
|
|
if (s->dir & (1 << line) & ((s->inputs & s->edge[1]) | s->level[0]))
|
|
omap2_gpio_module_int(s, line);
|
|
s->inputs &= ~(1 << line);
|
|
}
|
|
}
|
|
|
|
static void omap2_gpio_module_reset(struct omap2_gpio_s *s)
|
|
{
|
|
s->config[0] = 0;
|
|
s->config[1] = 2;
|
|
s->ints[0] = 0;
|
|
s->ints[1] = 0;
|
|
s->mask[0] = 0;
|
|
s->mask[1] = 0;
|
|
s->wumask = 0;
|
|
s->dir = ~0;
|
|
s->level[0] = 0;
|
|
s->level[1] = 0;
|
|
s->edge[0] = 0;
|
|
s->edge[1] = 0;
|
|
s->debounce = 0;
|
|
s->delay = 0;
|
|
}
|
|
|
|
static uint32_t omap2_gpio_module_read(void *opaque, hwaddr addr)
|
|
{
|
|
struct omap2_gpio_s *s = (struct omap2_gpio_s *) opaque;
|
|
|
|
switch (addr) {
|
|
case 0x00: /* GPIO_REVISION */
|
|
return s->revision;
|
|
|
|
case 0x10: /* GPIO_SYSCONFIG */
|
|
return s->config[0];
|
|
|
|
case 0x14: /* GPIO_SYSSTATUS */
|
|
return 0x01;
|
|
|
|
case 0x18: /* GPIO_IRQSTATUS1 */
|
|
return s->ints[0];
|
|
|
|
case 0x1c: /* GPIO_IRQENABLE1 */
|
|
case 0x60: /* GPIO_CLEARIRQENABLE1 */
|
|
case 0x64: /* GPIO_SETIRQENABLE1 */
|
|
return s->mask[0];
|
|
|
|
case 0x20: /* GPIO_WAKEUPENABLE */
|
|
case 0x80: /* GPIO_CLEARWKUENA */
|
|
case 0x84: /* GPIO_SETWKUENA */
|
|
return s->wumask;
|
|
|
|
case 0x28: /* GPIO_IRQSTATUS2 */
|
|
return s->ints[1];
|
|
|
|
case 0x2c: /* GPIO_IRQENABLE2 */
|
|
case 0x70: /* GPIO_CLEARIRQENABLE2 */
|
|
case 0x74: /* GPIO_SETIREQNEABLE2 */
|
|
return s->mask[1];
|
|
|
|
case 0x30: /* GPIO_CTRL */
|
|
return s->config[1];
|
|
|
|
case 0x34: /* GPIO_OE */
|
|
return s->dir;
|
|
|
|
case 0x38: /* GPIO_DATAIN */
|
|
return s->inputs;
|
|
|
|
case 0x3c: /* GPIO_DATAOUT */
|
|
case 0x90: /* GPIO_CLEARDATAOUT */
|
|
case 0x94: /* GPIO_SETDATAOUT */
|
|
return s->outputs;
|
|
|
|
case 0x40: /* GPIO_LEVELDETECT0 */
|
|
return s->level[0];
|
|
|
|
case 0x44: /* GPIO_LEVELDETECT1 */
|
|
return s->level[1];
|
|
|
|
case 0x48: /* GPIO_RISINGDETECT */
|
|
return s->edge[0];
|
|
|
|
case 0x4c: /* GPIO_FALLINGDETECT */
|
|
return s->edge[1];
|
|
|
|
case 0x50: /* GPIO_DEBOUNCENABLE */
|
|
return s->debounce;
|
|
|
|
case 0x54: /* GPIO_DEBOUNCINGTIME */
|
|
return s->delay;
|
|
}
|
|
|
|
OMAP_BAD_REG(addr);
|
|
return 0;
|
|
}
|
|
|
|
static void omap2_gpio_module_write(void *opaque, hwaddr addr,
|
|
uint32_t value)
|
|
{
|
|
struct omap2_gpio_s *s = (struct omap2_gpio_s *) opaque;
|
|
uint32_t diff;
|
|
int ln;
|
|
|
|
switch (addr) {
|
|
case 0x00: /* GPIO_REVISION */
|
|
case 0x14: /* GPIO_SYSSTATUS */
|
|
case 0x38: /* GPIO_DATAIN */
|
|
OMAP_RO_REG(addr);
|
|
break;
|
|
|
|
case 0x10: /* GPIO_SYSCONFIG */
|
|
if (((value >> 3) & 3) == 3)
|
|
fprintf(stderr, "%s: bad IDLEMODE value\n", __FUNCTION__);
|
|
if (value & 2)
|
|
omap2_gpio_module_reset(s);
|
|
s->config[0] = value & 0x1d;
|
|
break;
|
|
|
|
case 0x18: /* GPIO_IRQSTATUS1 */
|
|
if (s->ints[0] & value) {
|
|
s->ints[0] &= ~value;
|
|
omap2_gpio_module_level_update(s, 0);
|
|
}
|
|
break;
|
|
|
|
case 0x1c: /* GPIO_IRQENABLE1 */
|
|
s->mask[0] = value;
|
|
omap2_gpio_module_int_update(s, 0);
|
|
break;
|
|
|
|
case 0x20: /* GPIO_WAKEUPENABLE */
|
|
s->wumask = value;
|
|
break;
|
|
|
|
case 0x28: /* GPIO_IRQSTATUS2 */
|
|
if (s->ints[1] & value) {
|
|
s->ints[1] &= ~value;
|
|
omap2_gpio_module_level_update(s, 1);
|
|
}
|
|
break;
|
|
|
|
case 0x2c: /* GPIO_IRQENABLE2 */
|
|
s->mask[1] = value;
|
|
omap2_gpio_module_int_update(s, 1);
|
|
break;
|
|
|
|
case 0x30: /* GPIO_CTRL */
|
|
s->config[1] = value & 7;
|
|
break;
|
|
|
|
case 0x34: /* GPIO_OE */
|
|
diff = s->outputs & (s->dir ^ value);
|
|
s->dir = value;
|
|
|
|
value = s->outputs & ~s->dir;
|
|
while ((ln = ffs(diff))) {
|
|
diff &= ~(1 <<-- ln);
|
|
qemu_set_irq(s->handler[ln], (value >> ln) & 1);
|
|
}
|
|
|
|
omap2_gpio_module_level_update(s, 0);
|
|
omap2_gpio_module_level_update(s, 1);
|
|
break;
|
|
|
|
case 0x3c: /* GPIO_DATAOUT */
|
|
omap2_gpio_module_out_update(s, s->outputs ^ value);
|
|
break;
|
|
|
|
case 0x40: /* GPIO_LEVELDETECT0 */
|
|
s->level[0] = value;
|
|
omap2_gpio_module_level_update(s, 0);
|
|
omap2_gpio_module_level_update(s, 1);
|
|
break;
|
|
|
|
case 0x44: /* GPIO_LEVELDETECT1 */
|
|
s->level[1] = value;
|
|
omap2_gpio_module_level_update(s, 0);
|
|
omap2_gpio_module_level_update(s, 1);
|
|
break;
|
|
|
|
case 0x48: /* GPIO_RISINGDETECT */
|
|
s->edge[0] = value;
|
|
break;
|
|
|
|
case 0x4c: /* GPIO_FALLINGDETECT */
|
|
s->edge[1] = value;
|
|
break;
|
|
|
|
case 0x50: /* GPIO_DEBOUNCENABLE */
|
|
s->debounce = value;
|
|
break;
|
|
|
|
case 0x54: /* GPIO_DEBOUNCINGTIME */
|
|
s->delay = value;
|
|
break;
|
|
|
|
case 0x60: /* GPIO_CLEARIRQENABLE1 */
|
|
s->mask[0] &= ~value;
|
|
omap2_gpio_module_int_update(s, 0);
|
|
break;
|
|
|
|
case 0x64: /* GPIO_SETIRQENABLE1 */
|
|
s->mask[0] |= value;
|
|
omap2_gpio_module_int_update(s, 0);
|
|
break;
|
|
|
|
case 0x70: /* GPIO_CLEARIRQENABLE2 */
|
|
s->mask[1] &= ~value;
|
|
omap2_gpio_module_int_update(s, 1);
|
|
break;
|
|
|
|
case 0x74: /* GPIO_SETIREQNEABLE2 */
|
|
s->mask[1] |= value;
|
|
omap2_gpio_module_int_update(s, 1);
|
|
break;
|
|
|
|
case 0x80: /* GPIO_CLEARWKUENA */
|
|
s->wumask &= ~value;
|
|
break;
|
|
|
|
case 0x84: /* GPIO_SETWKUENA */
|
|
s->wumask |= value;
|
|
break;
|
|
|
|
case 0x90: /* GPIO_CLEARDATAOUT */
|
|
omap2_gpio_module_out_update(s, s->outputs & value);
|
|
break;
|
|
|
|
case 0x94: /* GPIO_SETDATAOUT */
|
|
omap2_gpio_module_out_update(s, ~s->outputs & value);
|
|
break;
|
|
|
|
default:
|
|
OMAP_BAD_REG(addr);
|
|
return;
|
|
}
|
|
}
|
|
|
|
static uint32_t omap2_gpio_module_readp(void *opaque, hwaddr addr)
|
|
{
|
|
return omap2_gpio_module_read(opaque, addr & ~3) >> ((addr & 3) << 3);
|
|
}
|
|
|
|
static void omap2_gpio_module_writep(void *opaque, hwaddr addr,
|
|
uint32_t value)
|
|
{
|
|
uint32_t cur = 0;
|
|
uint32_t mask = 0xffff;
|
|
|
|
switch (addr & ~3) {
|
|
case 0x00: /* GPIO_REVISION */
|
|
case 0x14: /* GPIO_SYSSTATUS */
|
|
case 0x38: /* GPIO_DATAIN */
|
|
OMAP_RO_REG(addr);
|
|
break;
|
|
|
|
case 0x10: /* GPIO_SYSCONFIG */
|
|
case 0x1c: /* GPIO_IRQENABLE1 */
|
|
case 0x20: /* GPIO_WAKEUPENABLE */
|
|
case 0x2c: /* GPIO_IRQENABLE2 */
|
|
case 0x30: /* GPIO_CTRL */
|
|
case 0x34: /* GPIO_OE */
|
|
case 0x3c: /* GPIO_DATAOUT */
|
|
case 0x40: /* GPIO_LEVELDETECT0 */
|
|
case 0x44: /* GPIO_LEVELDETECT1 */
|
|
case 0x48: /* GPIO_RISINGDETECT */
|
|
case 0x4c: /* GPIO_FALLINGDETECT */
|
|
case 0x50: /* GPIO_DEBOUNCENABLE */
|
|
case 0x54: /* GPIO_DEBOUNCINGTIME */
|
|
cur = omap2_gpio_module_read(opaque, addr & ~3) &
|
|
~(mask << ((addr & 3) << 3));
|
|
|
|
/* Fall through. */
|
|
case 0x18: /* GPIO_IRQSTATUS1 */
|
|
case 0x28: /* GPIO_IRQSTATUS2 */
|
|
case 0x60: /* GPIO_CLEARIRQENABLE1 */
|
|
case 0x64: /* GPIO_SETIRQENABLE1 */
|
|
case 0x70: /* GPIO_CLEARIRQENABLE2 */
|
|
case 0x74: /* GPIO_SETIREQNEABLE2 */
|
|
case 0x80: /* GPIO_CLEARWKUENA */
|
|
case 0x84: /* GPIO_SETWKUENA */
|
|
case 0x90: /* GPIO_CLEARDATAOUT */
|
|
case 0x94: /* GPIO_SETDATAOUT */
|
|
value <<= (addr & 3) << 3;
|
|
omap2_gpio_module_write(opaque, addr, cur | value);
|
|
break;
|
|
|
|
default:
|
|
OMAP_BAD_REG(addr);
|
|
return;
|
|
}
|
|
}
|
|
|
|
static const MemoryRegionOps omap2_gpio_module_ops = {
|
|
.old_mmio = {
|
|
.read = {
|
|
omap2_gpio_module_readp,
|
|
omap2_gpio_module_readp,
|
|
omap2_gpio_module_read,
|
|
},
|
|
.write = {
|
|
omap2_gpio_module_writep,
|
|
omap2_gpio_module_writep,
|
|
omap2_gpio_module_write,
|
|
},
|
|
},
|
|
.endianness = DEVICE_NATIVE_ENDIAN,
|
|
};
|
|
|
|
static void omap_gpif_reset(DeviceState *dev)
|
|
{
|
|
struct omap_gpif_s *s = OMAP1_GPIO(dev);
|
|
|
|
omap_gpio_reset(&s->omap1);
|
|
}
|
|
|
|
static void omap2_gpif_reset(DeviceState *dev)
|
|
{
|
|
struct omap2_gpif_s *s = OMAP2_GPIO(dev);
|
|
int i;
|
|
|
|
for (i = 0; i < s->modulecount; i++) {
|
|
omap2_gpio_module_reset(&s->modules[i]);
|
|
}
|
|
s->autoidle = 0;
|
|
s->gpo = 0;
|
|
}
|
|
|
|
static uint64_t omap2_gpif_top_read(void *opaque, hwaddr addr,
|
|
unsigned size)
|
|
{
|
|
struct omap2_gpif_s *s = (struct omap2_gpif_s *) opaque;
|
|
|
|
switch (addr) {
|
|
case 0x00: /* IPGENERICOCPSPL_REVISION */
|
|
return 0x18;
|
|
|
|
case 0x10: /* IPGENERICOCPSPL_SYSCONFIG */
|
|
return s->autoidle;
|
|
|
|
case 0x14: /* IPGENERICOCPSPL_SYSSTATUS */
|
|
return 0x01;
|
|
|
|
case 0x18: /* IPGENERICOCPSPL_IRQSTATUS */
|
|
return 0x00;
|
|
|
|
case 0x40: /* IPGENERICOCPSPL_GPO */
|
|
return s->gpo;
|
|
|
|
case 0x50: /* IPGENERICOCPSPL_GPI */
|
|
return 0x00;
|
|
}
|
|
|
|
OMAP_BAD_REG(addr);
|
|
return 0;
|
|
}
|
|
|
|
static void omap2_gpif_top_write(void *opaque, hwaddr addr,
|
|
uint64_t value, unsigned size)
|
|
{
|
|
struct omap2_gpif_s *s = (struct omap2_gpif_s *) opaque;
|
|
|
|
switch (addr) {
|
|
case 0x00: /* IPGENERICOCPSPL_REVISION */
|
|
case 0x14: /* IPGENERICOCPSPL_SYSSTATUS */
|
|
case 0x18: /* IPGENERICOCPSPL_IRQSTATUS */
|
|
case 0x50: /* IPGENERICOCPSPL_GPI */
|
|
OMAP_RO_REG(addr);
|
|
break;
|
|
|
|
case 0x10: /* IPGENERICOCPSPL_SYSCONFIG */
|
|
if (value & (1 << 1)) /* SOFTRESET */
|
|
omap2_gpif_reset(DEVICE(s));
|
|
s->autoidle = value & 1;
|
|
break;
|
|
|
|
case 0x40: /* IPGENERICOCPSPL_GPO */
|
|
s->gpo = value & 1;
|
|
break;
|
|
|
|
default:
|
|
OMAP_BAD_REG(addr);
|
|
return;
|
|
}
|
|
}
|
|
|
|
static const MemoryRegionOps omap2_gpif_top_ops = {
|
|
.read = omap2_gpif_top_read,
|
|
.write = omap2_gpif_top_write,
|
|
.endianness = DEVICE_NATIVE_ENDIAN,
|
|
};
|
|
|
|
static int omap_gpio_init(SysBusDevice *sbd)
|
|
{
|
|
DeviceState *dev = DEVICE(sbd);
|
|
struct omap_gpif_s *s = OMAP1_GPIO(dev);
|
|
|
|
if (!s->clk) {
|
|
hw_error("omap-gpio: clk not connected\n");
|
|
}
|
|
qdev_init_gpio_in(dev, omap_gpio_set, 16);
|
|
qdev_init_gpio_out(dev, s->omap1.handler, 16);
|
|
sysbus_init_irq(sbd, &s->omap1.irq);
|
|
memory_region_init_io(&s->iomem, OBJECT(s), &omap_gpio_ops, &s->omap1,
|
|
"omap.gpio", 0x1000);
|
|
sysbus_init_mmio(sbd, &s->iomem);
|
|
return 0;
|
|
}
|
|
|
|
static int omap2_gpio_init(SysBusDevice *sbd)
|
|
{
|
|
DeviceState *dev = DEVICE(sbd);
|
|
struct omap2_gpif_s *s = OMAP2_GPIO(dev);
|
|
int i;
|
|
|
|
if (!s->iclk) {
|
|
hw_error("omap2-gpio: iclk not connected\n");
|
|
}
|
|
if (s->mpu_model < omap3430) {
|
|
s->modulecount = (s->mpu_model < omap2430) ? 4 : 5;
|
|
memory_region_init_io(&s->iomem, OBJECT(s), &omap2_gpif_top_ops, s,
|
|
"omap2.gpio", 0x1000);
|
|
sysbus_init_mmio(sbd, &s->iomem);
|
|
} else {
|
|
s->modulecount = 6;
|
|
}
|
|
s->modules = g_malloc0(s->modulecount * sizeof(struct omap2_gpio_s));
|
|
s->handler = g_malloc0(s->modulecount * 32 * sizeof(qemu_irq));
|
|
qdev_init_gpio_in(dev, omap2_gpio_set, s->modulecount * 32);
|
|
qdev_init_gpio_out(dev, s->handler, s->modulecount * 32);
|
|
for (i = 0; i < s->modulecount; i++) {
|
|
struct omap2_gpio_s *m = &s->modules[i];
|
|
if (!s->fclk[i]) {
|
|
hw_error("omap2-gpio: fclk%d not connected\n", i);
|
|
}
|
|
m->revision = (s->mpu_model < omap3430) ? 0x18 : 0x25;
|
|
m->handler = &s->handler[i * 32];
|
|
sysbus_init_irq(sbd, &m->irq[0]); /* mpu irq */
|
|
sysbus_init_irq(sbd, &m->irq[1]); /* dsp irq */
|
|
sysbus_init_irq(sbd, &m->wkup);
|
|
memory_region_init_io(&m->iomem, OBJECT(s), &omap2_gpio_module_ops, m,
|
|
"omap.gpio-module", 0x1000);
|
|
sysbus_init_mmio(sbd, &m->iomem);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Using qdev pointer properties for the clocks is not ideal.
|
|
* qdev should support a generic means of defining a 'port' with
|
|
* an arbitrary interface for connecting two devices. Then we
|
|
* could reframe the omap clock API in terms of clock ports,
|
|
* and get some type safety. For now the best qdev provides is
|
|
* passing an arbitrary pointer.
|
|
* (It's not possible to pass in the string which is the clock
|
|
* name, because this device does not have the necessary information
|
|
* (ie the struct omap_mpu_state_s*) to do the clockname to pointer
|
|
* translation.)
|
|
*/
|
|
|
|
static Property omap_gpio_properties[] = {
|
|
DEFINE_PROP_INT32("mpu_model", struct omap_gpif_s, mpu_model, 0),
|
|
DEFINE_PROP_PTR("clk", struct omap_gpif_s, clk),
|
|
DEFINE_PROP_END_OF_LIST(),
|
|
};
|
|
|
|
static void omap_gpio_class_init(ObjectClass *klass, void *data)
|
|
{
|
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
|
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
|
|
|
|
k->init = omap_gpio_init;
|
|
dc->reset = omap_gpif_reset;
|
|
dc->props = omap_gpio_properties;
|
|
/* Reason: pointer property "clk" */
|
|
dc->cannot_instantiate_with_device_add_yet = true;
|
|
}
|
|
|
|
static const TypeInfo omap_gpio_info = {
|
|
.name = TYPE_OMAP1_GPIO,
|
|
.parent = TYPE_SYS_BUS_DEVICE,
|
|
.instance_size = sizeof(struct omap_gpif_s),
|
|
.class_init = omap_gpio_class_init,
|
|
};
|
|
|
|
static Property omap2_gpio_properties[] = {
|
|
DEFINE_PROP_INT32("mpu_model", struct omap2_gpif_s, mpu_model, 0),
|
|
DEFINE_PROP_PTR("iclk", struct omap2_gpif_s, iclk),
|
|
DEFINE_PROP_PTR("fclk0", struct omap2_gpif_s, fclk[0]),
|
|
DEFINE_PROP_PTR("fclk1", struct omap2_gpif_s, fclk[1]),
|
|
DEFINE_PROP_PTR("fclk2", struct omap2_gpif_s, fclk[2]),
|
|
DEFINE_PROP_PTR("fclk3", struct omap2_gpif_s, fclk[3]),
|
|
DEFINE_PROP_PTR("fclk4", struct omap2_gpif_s, fclk[4]),
|
|
DEFINE_PROP_PTR("fclk5", struct omap2_gpif_s, fclk[5]),
|
|
DEFINE_PROP_END_OF_LIST(),
|
|
};
|
|
|
|
static void omap2_gpio_class_init(ObjectClass *klass, void *data)
|
|
{
|
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
|
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
|
|
|
|
k->init = omap2_gpio_init;
|
|
dc->reset = omap2_gpif_reset;
|
|
dc->props = omap2_gpio_properties;
|
|
/* Reason: pointer properties "iclk", "fclk0", ..., "fclk5" */
|
|
dc->cannot_instantiate_with_device_add_yet = true;
|
|
}
|
|
|
|
static const TypeInfo omap2_gpio_info = {
|
|
.name = TYPE_OMAP2_GPIO,
|
|
.parent = TYPE_SYS_BUS_DEVICE,
|
|
.instance_size = sizeof(struct omap2_gpif_s),
|
|
.class_init = omap2_gpio_class_init,
|
|
};
|
|
|
|
static void omap_gpio_register_types(void)
|
|
{
|
|
type_register_static(&omap_gpio_info);
|
|
type_register_static(&omap2_gpio_info);
|
|
}
|
|
|
|
type_init(omap_gpio_register_types)
|