/* * GRLIB AHB APB PNP * * Copyright (C) 2019 AdaCore * * Developed by : * Frederic Konrad * * 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 of the License, or * (at your option) any later version. * * 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 . * */ #include "qemu/osdep.h" #include "qemu/log.h" #include "hw/sysbus.h" #include "hw/misc/grlib_ahb_apb_pnp.h" #define GRLIB_PNP_VENDOR_SHIFT (24) #define GRLIB_PNP_VENDOR_SIZE (8) #define GRLIB_PNP_DEV_SHIFT (12) #define GRLIB_PNP_DEV_SIZE (12) #define GRLIB_PNP_VER_SHIFT (5) #define GRLIB_PNP_VER_SIZE (5) #define GRLIB_PNP_IRQ_SHIFT (0) #define GRLIB_PNP_IRQ_SIZE (5) #define GRLIB_PNP_ADDR_SHIFT (20) #define GRLIB_PNP_ADDR_SIZE (12) #define GRLIB_PNP_MASK_SHIFT (4) #define GRLIB_PNP_MASK_SIZE (12) #define GRLIB_AHB_DEV_ADDR_SHIFT (20) #define GRLIB_AHB_DEV_ADDR_SIZE (12) #define GRLIB_AHB_ENTRY_SIZE (0x20) #define GRLIB_AHB_MAX_DEV (64) #define GRLIB_AHB_SLAVE_OFFSET (0x800) #define GRLIB_APB_DEV_ADDR_SHIFT (8) #define GRLIB_APB_DEV_ADDR_SIZE (12) #define GRLIB_APB_ENTRY_SIZE (0x08) #define GRLIB_APB_MAX_DEV (512) #define GRLIB_PNP_MAX_REGS (0x1000) typedef struct AHBPnp { SysBusDevice parent_obj; MemoryRegion iomem; uint32_t regs[GRLIB_PNP_MAX_REGS >> 2]; uint8_t master_count; uint8_t slave_count; } AHBPnp; void grlib_ahb_pnp_add_entry(AHBPnp *dev, uint32_t address, uint32_t mask, uint8_t vendor, uint16_t device, int slave, int type) { unsigned int reg_start; /* * AHB entries look like this: * * 31 -------- 23 -------- 11 ----- 9 -------- 4 --- 0 * | VENDOR ID | DEVICE ID | IRQ ? | VERSION | IRQ | * -------------------------------------------------- * | USER | * -------------------------------------------------- * | USER | * -------------------------------------------------- * | USER | * -------------------------------------------------- * | USER | * -------------------------------------------------- * 31 ----------- 20 --- 15 ----------------- 3 ---- 0 * | ADDR[31..12] | 00PC | MASK | TYPE | * -------------------------------------------------- * 31 ----------- 20 --- 15 ----------------- 3 ---- 0 * | ADDR[31..12] | 00PC | MASK | TYPE | * -------------------------------------------------- * 31 ----------- 20 --- 15 ----------------- 3 ---- 0 * | ADDR[31..12] | 00PC | MASK | TYPE | * -------------------------------------------------- * 31 ----------- 20 --- 15 ----------------- 3 ---- 0 * | ADDR[31..12] | 00PC | MASK | TYPE | * -------------------------------------------------- */ if (slave) { assert(dev->slave_count < GRLIB_AHB_MAX_DEV); reg_start = (GRLIB_AHB_SLAVE_OFFSET + (dev->slave_count * GRLIB_AHB_ENTRY_SIZE)) >> 2; dev->slave_count++; } else { assert(dev->master_count < GRLIB_AHB_MAX_DEV); reg_start = (dev->master_count * GRLIB_AHB_ENTRY_SIZE) >> 2; dev->master_count++; } dev->regs[reg_start] = deposit32(dev->regs[reg_start], GRLIB_PNP_VENDOR_SHIFT, GRLIB_PNP_VENDOR_SIZE, vendor); dev->regs[reg_start] = deposit32(dev->regs[reg_start], GRLIB_PNP_DEV_SHIFT, GRLIB_PNP_DEV_SIZE, device); reg_start += 4; /* AHB Memory Space */ dev->regs[reg_start] = type; dev->regs[reg_start] = deposit32(dev->regs[reg_start], GRLIB_PNP_ADDR_SHIFT, GRLIB_PNP_ADDR_SIZE, extract32(address, GRLIB_AHB_DEV_ADDR_SHIFT, GRLIB_AHB_DEV_ADDR_SIZE)); dev->regs[reg_start] = deposit32(dev->regs[reg_start], GRLIB_PNP_MASK_SHIFT, GRLIB_PNP_MASK_SIZE, mask); } static uint64_t grlib_ahb_pnp_read(void *opaque, hwaddr offset, unsigned size) { AHBPnp *ahb_pnp = GRLIB_AHB_PNP(opaque); return ahb_pnp->regs[offset >> 2]; } static const MemoryRegionOps grlib_ahb_pnp_ops = { .read = grlib_ahb_pnp_read, .endianness = DEVICE_BIG_ENDIAN, }; static void grlib_ahb_pnp_realize(DeviceState *dev, Error **errp) { AHBPnp *ahb_pnp = GRLIB_AHB_PNP(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); memory_region_init_io(&ahb_pnp->iomem, OBJECT(dev), &grlib_ahb_pnp_ops, ahb_pnp, TYPE_GRLIB_AHB_PNP, GRLIB_PNP_MAX_REGS); sysbus_init_mmio(sbd, &ahb_pnp->iomem); } static void grlib_ahb_pnp_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = grlib_ahb_pnp_realize; } static const TypeInfo grlib_ahb_pnp_info = { .name = TYPE_GRLIB_AHB_PNP, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(AHBPnp), .class_init = grlib_ahb_pnp_class_init, }; /* APBPnp */ typedef struct APBPnp { SysBusDevice parent_obj; MemoryRegion iomem; uint32_t regs[GRLIB_PNP_MAX_REGS >> 2]; uint32_t entry_count; } APBPnp; void grlib_apb_pnp_add_entry(APBPnp *dev, uint32_t address, uint32_t mask, uint8_t vendor, uint16_t device, uint8_t version, uint8_t irq, int type) { unsigned int reg_start; /* * APB entries look like this: * * 31 -------- 23 -------- 11 ----- 9 ------- 4 --- 0 * | VENDOR ID | DEVICE ID | IRQ ? | VERSION | IRQ | * * 31 ---------- 20 --- 15 ----------------- 3 ---- 0 * | ADDR[20..8] | 0000 | MASK | TYPE | */ assert(dev->entry_count < GRLIB_APB_MAX_DEV); reg_start = (dev->entry_count * GRLIB_APB_ENTRY_SIZE) >> 2; dev->entry_count++; dev->regs[reg_start] = deposit32(dev->regs[reg_start], GRLIB_PNP_VENDOR_SHIFT, GRLIB_PNP_VENDOR_SIZE, vendor); dev->regs[reg_start] = deposit32(dev->regs[reg_start], GRLIB_PNP_DEV_SHIFT, GRLIB_PNP_DEV_SIZE, device); dev->regs[reg_start] = deposit32(dev->regs[reg_start], GRLIB_PNP_VER_SHIFT, GRLIB_PNP_VER_SIZE, version); dev->regs[reg_start] = deposit32(dev->regs[reg_start], GRLIB_PNP_IRQ_SHIFT, GRLIB_PNP_IRQ_SIZE, irq); reg_start += 1; dev->regs[reg_start] = type; dev->regs[reg_start] = deposit32(dev->regs[reg_start], GRLIB_PNP_ADDR_SHIFT, GRLIB_PNP_ADDR_SIZE, extract32(address, GRLIB_APB_DEV_ADDR_SHIFT, GRLIB_APB_DEV_ADDR_SIZE)); dev->regs[reg_start] = deposit32(dev->regs[reg_start], GRLIB_PNP_MASK_SHIFT, GRLIB_PNP_MASK_SIZE, mask); } static uint64_t grlib_apb_pnp_read(void *opaque, hwaddr offset, unsigned size) { APBPnp *apb_pnp = GRLIB_APB_PNP(opaque); return apb_pnp->regs[offset >> 2]; } static void grlib_apb_pnp_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { qemu_log_mask(LOG_UNIMP, "%s not implemented\n", __func__); } static const MemoryRegionOps grlib_apb_pnp_ops = { .read = grlib_apb_pnp_read, .write = grlib_apb_pnp_write, .endianness = DEVICE_BIG_ENDIAN, .impl = { .min_access_size = 4, .max_access_size = 4, }, }; static void grlib_apb_pnp_realize(DeviceState *dev, Error **errp) { APBPnp *apb_pnp = GRLIB_APB_PNP(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); memory_region_init_io(&apb_pnp->iomem, OBJECT(dev), &grlib_apb_pnp_ops, apb_pnp, TYPE_GRLIB_APB_PNP, GRLIB_PNP_MAX_REGS); sysbus_init_mmio(sbd, &apb_pnp->iomem); } static void grlib_apb_pnp_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = grlib_apb_pnp_realize; } static const TypeInfo grlib_apb_pnp_info = { .name = TYPE_GRLIB_APB_PNP, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(APBPnp), .class_init = grlib_apb_pnp_class_init, }; static void grlib_ahb_apb_pnp_register_types(void) { type_register_static(&grlib_ahb_pnp_info); type_register_static(&grlib_apb_pnp_info); } type_init(grlib_ahb_apb_pnp_register_types)