1e33d8cdbSbalrog /* 2e33d8cdbSbalrog * Copyright (c) 2006-2008 Openedhand Ltd. 3e33d8cdbSbalrog * Written by Andrzej Zaborowski <balrog@zabor.org> 4e33d8cdbSbalrog * 5e33d8cdbSbalrog * This program is free software; you can redistribute it and/or 6e33d8cdbSbalrog * modify it under the terms of the GNU General Public License as 7e33d8cdbSbalrog * published by the Free Software Foundation; either version 2 or 8e33d8cdbSbalrog * (at your option) version 3 of the License. 9e33d8cdbSbalrog * 10e33d8cdbSbalrog * This program is distributed in the hope that it will be useful, 11e33d8cdbSbalrog * but WITHOUT ANY WARRANTY; without even the implied warranty of 12e33d8cdbSbalrog * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13e33d8cdbSbalrog * GNU General Public License for more details. 14e33d8cdbSbalrog * 15fad6cb1aSaurel32 * You should have received a copy of the GNU General Public License along 168167ee88SBlue Swirl * with this program; if not, see <http://www.gnu.org/licenses/>. 17e33d8cdbSbalrog */ 18*0b8fa32fSMarkus Armbruster 1917b7f2dbSPeter Maydell #include "qemu/osdep.h" 2083c9f4caSPaolo Bonzini #include "hw/hw.h" 210d09e41aSPaolo Bonzini #include "hw/arm/sharpsl.h" 2283c9f4caSPaolo Bonzini #include "hw/sysbus.h" 23*0b8fa32fSMarkus Armbruster #include "qemu/module.h" 24e33d8cdbSbalrog 25e33d8cdbSbalrog #undef REG_FMT 26e33d8cdbSbalrog #define REG_FMT "0x%02lx" 27e33d8cdbSbalrog 28e33d8cdbSbalrog /* SCOOP devices */ 29e33d8cdbSbalrog 30a009de46SAndreas Färber #define TYPE_SCOOP "scoop" 31a009de46SAndreas Färber #define SCOOP(obj) OBJECT_CHECK(ScoopInfo, (obj), TYPE_SCOOP) 32a009de46SAndreas Färber 33383d01c6SDmitry Eremin-Solenikov typedef struct ScoopInfo ScoopInfo; 34bc24a225SPaul Brook struct ScoopInfo { 35a009de46SAndreas Färber SysBusDevice parent_obj; 36a009de46SAndreas Färber 37e33d8cdbSbalrog qemu_irq handler[16]; 38e71ceafcSAvi Kivity MemoryRegion iomem; 39e33d8cdbSbalrog uint16_t status; 40e33d8cdbSbalrog uint16_t power; 41e33d8cdbSbalrog uint32_t gpio_level; 42e33d8cdbSbalrog uint32_t gpio_dir; 43e33d8cdbSbalrog uint32_t prev_level; 44e33d8cdbSbalrog 45e33d8cdbSbalrog uint16_t mcr; 46e33d8cdbSbalrog uint16_t cdr; 47e33d8cdbSbalrog uint16_t ccr; 48e33d8cdbSbalrog uint16_t irr; 49e33d8cdbSbalrog uint16_t imr; 50e33d8cdbSbalrog uint16_t isr; 51e33d8cdbSbalrog }; 52e33d8cdbSbalrog 53e33d8cdbSbalrog #define SCOOP_MCR 0x00 54e33d8cdbSbalrog #define SCOOP_CDR 0x04 55e33d8cdbSbalrog #define SCOOP_CSR 0x08 56e33d8cdbSbalrog #define SCOOP_CPR 0x0c 57e33d8cdbSbalrog #define SCOOP_CCR 0x10 58e33d8cdbSbalrog #define SCOOP_IRR_IRM 0x14 59e33d8cdbSbalrog #define SCOOP_IMR 0x18 60e33d8cdbSbalrog #define SCOOP_ISR 0x1c 61e33d8cdbSbalrog #define SCOOP_GPCR 0x20 62e33d8cdbSbalrog #define SCOOP_GPWR 0x24 63e33d8cdbSbalrog #define SCOOP_GPRR 0x28 64e33d8cdbSbalrog 65bc24a225SPaul Brook static inline void scoop_gpio_handler_update(ScoopInfo *s) { 66e33d8cdbSbalrog uint32_t level, diff; 67e33d8cdbSbalrog int bit; 68e33d8cdbSbalrog level = s->gpio_level & s->gpio_dir; 69e33d8cdbSbalrog 70e33d8cdbSbalrog for (diff = s->prev_level ^ level; diff; diff ^= 1 << bit) { 71786a4ea8SStefan Hajnoczi bit = ctz32(diff); 72e33d8cdbSbalrog qemu_set_irq(s->handler[bit], (level >> bit) & 1); 73e33d8cdbSbalrog } 74e33d8cdbSbalrog 75e33d8cdbSbalrog s->prev_level = level; 76e33d8cdbSbalrog } 77e33d8cdbSbalrog 78a8170e5eSAvi Kivity static uint64_t scoop_read(void *opaque, hwaddr addr, 79e71ceafcSAvi Kivity unsigned size) 80e33d8cdbSbalrog { 81bc24a225SPaul Brook ScoopInfo *s = (ScoopInfo *) opaque; 82e33d8cdbSbalrog 83aa9438d9SDmitry Eremin-Solenikov switch (addr & 0x3f) { 84e33d8cdbSbalrog case SCOOP_MCR: 85e33d8cdbSbalrog return s->mcr; 86e33d8cdbSbalrog case SCOOP_CDR: 87e33d8cdbSbalrog return s->cdr; 88e33d8cdbSbalrog case SCOOP_CSR: 89e33d8cdbSbalrog return s->status; 90e33d8cdbSbalrog case SCOOP_CPR: 91e33d8cdbSbalrog return s->power; 92e33d8cdbSbalrog case SCOOP_CCR: 93e33d8cdbSbalrog return s->ccr; 94e33d8cdbSbalrog case SCOOP_IRR_IRM: 95e33d8cdbSbalrog return s->irr; 96e33d8cdbSbalrog case SCOOP_IMR: 97e33d8cdbSbalrog return s->imr; 98e33d8cdbSbalrog case SCOOP_ISR: 99e33d8cdbSbalrog return s->isr; 100e33d8cdbSbalrog case SCOOP_GPCR: 101e33d8cdbSbalrog return s->gpio_dir; 102e33d8cdbSbalrog case SCOOP_GPWR: 103e33d8cdbSbalrog case SCOOP_GPRR: 1041f163b14Sbalrog return s->gpio_level; 105e33d8cdbSbalrog default: 106a8b7063bSBlue Swirl zaurus_printf("Bad register offset " REG_FMT "\n", (unsigned long)addr); 107e33d8cdbSbalrog } 108e33d8cdbSbalrog 109e33d8cdbSbalrog return 0; 110e33d8cdbSbalrog } 111e33d8cdbSbalrog 112a8170e5eSAvi Kivity static void scoop_write(void *opaque, hwaddr addr, 113e71ceafcSAvi Kivity uint64_t value, unsigned size) 114e33d8cdbSbalrog { 115bc24a225SPaul Brook ScoopInfo *s = (ScoopInfo *) opaque; 116e33d8cdbSbalrog value &= 0xffff; 117e33d8cdbSbalrog 118aa9438d9SDmitry Eremin-Solenikov switch (addr & 0x3f) { 119e33d8cdbSbalrog case SCOOP_MCR: 120e33d8cdbSbalrog s->mcr = value; 121e33d8cdbSbalrog break; 122e33d8cdbSbalrog case SCOOP_CDR: 123e33d8cdbSbalrog s->cdr = value; 124e33d8cdbSbalrog break; 125e33d8cdbSbalrog case SCOOP_CPR: 126e33d8cdbSbalrog s->power = value; 127e33d8cdbSbalrog if (value & 0x80) 128e33d8cdbSbalrog s->power |= 0x8040; 129e33d8cdbSbalrog break; 130e33d8cdbSbalrog case SCOOP_CCR: 131e33d8cdbSbalrog s->ccr = value; 132e33d8cdbSbalrog break; 133e33d8cdbSbalrog case SCOOP_IRR_IRM: 134e33d8cdbSbalrog s->irr = value; 135e33d8cdbSbalrog break; 136e33d8cdbSbalrog case SCOOP_IMR: 137e33d8cdbSbalrog s->imr = value; 138e33d8cdbSbalrog break; 139e33d8cdbSbalrog case SCOOP_ISR: 140e33d8cdbSbalrog s->isr = value; 141e33d8cdbSbalrog break; 142e33d8cdbSbalrog case SCOOP_GPCR: 143e33d8cdbSbalrog s->gpio_dir = value; 144e33d8cdbSbalrog scoop_gpio_handler_update(s); 145e33d8cdbSbalrog break; 146e33d8cdbSbalrog case SCOOP_GPWR: 1471f163b14Sbalrog case SCOOP_GPRR: /* GPRR is probably R/O in real HW */ 148e33d8cdbSbalrog s->gpio_level = value & s->gpio_dir; 149e33d8cdbSbalrog scoop_gpio_handler_update(s); 150e33d8cdbSbalrog break; 151e33d8cdbSbalrog default: 152a8b7063bSBlue Swirl zaurus_printf("Bad register offset " REG_FMT "\n", (unsigned long)addr); 153e33d8cdbSbalrog } 154e33d8cdbSbalrog } 155e33d8cdbSbalrog 156e71ceafcSAvi Kivity static const MemoryRegionOps scoop_ops = { 157e71ceafcSAvi Kivity .read = scoop_read, 158e71ceafcSAvi Kivity .write = scoop_write, 159e71ceafcSAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 160e33d8cdbSbalrog }; 161e33d8cdbSbalrog 162383d01c6SDmitry Eremin-Solenikov static void scoop_gpio_set(void *opaque, int line, int level) 163e33d8cdbSbalrog { 1648d30b794STorsten Duwe ScoopInfo *s = (ScoopInfo *) opaque; 165e33d8cdbSbalrog 166e33d8cdbSbalrog if (level) 167e33d8cdbSbalrog s->gpio_level |= (1 << line); 168e33d8cdbSbalrog else 169e33d8cdbSbalrog s->gpio_level &= ~(1 << line); 170e33d8cdbSbalrog } 171e33d8cdbSbalrog 17253677667Sxiaoqiang zhao static void scoop_init(Object *obj) 173e33d8cdbSbalrog { 17453677667Sxiaoqiang zhao DeviceState *dev = DEVICE(obj); 17553677667Sxiaoqiang zhao ScoopInfo *s = SCOOP(obj); 17653677667Sxiaoqiang zhao SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 177e33d8cdbSbalrog 178383d01c6SDmitry Eremin-Solenikov s->status = 0x02; 179a009de46SAndreas Färber qdev_init_gpio_out(dev, s->handler, 16); 180a009de46SAndreas Färber qdev_init_gpio_in(dev, scoop_gpio_set, 16); 18153677667Sxiaoqiang zhao memory_region_init_io(&s->iomem, obj, &scoop_ops, s, "scoop", 0x1000); 182e33d8cdbSbalrog 183a009de46SAndreas Färber sysbus_init_mmio(sbd, &s->iomem); 184e33d8cdbSbalrog } 185e33d8cdbSbalrog 1867fe63a17SDmitry Eremin-Solenikov static int scoop_post_load(void *opaque, int version_id) 1877fe63a17SDmitry Eremin-Solenikov { 1887fe63a17SDmitry Eremin-Solenikov ScoopInfo *s = (ScoopInfo *) opaque; 1897fe63a17SDmitry Eremin-Solenikov int i; 1907fe63a17SDmitry Eremin-Solenikov uint32_t level; 1917fe63a17SDmitry Eremin-Solenikov 1927fe63a17SDmitry Eremin-Solenikov level = s->gpio_level & s->gpio_dir; 1937fe63a17SDmitry Eremin-Solenikov 1947fe63a17SDmitry Eremin-Solenikov for (i = 0; i < 16; i++) { 1957fe63a17SDmitry Eremin-Solenikov qemu_set_irq(s->handler[i], (level >> i) & 1); 1967fe63a17SDmitry Eremin-Solenikov } 1977fe63a17SDmitry Eremin-Solenikov 1987fe63a17SDmitry Eremin-Solenikov s->prev_level = level; 1997fe63a17SDmitry Eremin-Solenikov 2007fe63a17SDmitry Eremin-Solenikov return 0; 2017fe63a17SDmitry Eremin-Solenikov } 2027fe63a17SDmitry Eremin-Solenikov 203383d01c6SDmitry Eremin-Solenikov static bool is_version_0 (void *opaque, int version_id) 204383d01c6SDmitry Eremin-Solenikov { 205383d01c6SDmitry Eremin-Solenikov return version_id == 0; 206e33d8cdbSbalrog } 207e33d8cdbSbalrog 20852f91c37SMichael S. Tsirkin static bool vmstate_scoop_validate(void *opaque, int version_id) 20952f91c37SMichael S. Tsirkin { 21052f91c37SMichael S. Tsirkin ScoopInfo *s = opaque; 21152f91c37SMichael S. Tsirkin 21252f91c37SMichael S. Tsirkin return !(s->prev_level & 0xffff0000) && 21352f91c37SMichael S. Tsirkin !(s->gpio_level & 0xffff0000) && 21452f91c37SMichael S. Tsirkin !(s->gpio_dir & 0xffff0000); 21552f91c37SMichael S. Tsirkin } 21652f91c37SMichael S. Tsirkin 217383d01c6SDmitry Eremin-Solenikov static const VMStateDescription vmstate_scoop_regs = { 218383d01c6SDmitry Eremin-Solenikov .name = "scoop", 219383d01c6SDmitry Eremin-Solenikov .version_id = 1, 220383d01c6SDmitry Eremin-Solenikov .minimum_version_id = 0, 2217fe63a17SDmitry Eremin-Solenikov .post_load = scoop_post_load, 222383d01c6SDmitry Eremin-Solenikov .fields = (VMStateField[]) { 223383d01c6SDmitry Eremin-Solenikov VMSTATE_UINT16(status, ScoopInfo), 224383d01c6SDmitry Eremin-Solenikov VMSTATE_UINT16(power, ScoopInfo), 225383d01c6SDmitry Eremin-Solenikov VMSTATE_UINT32(gpio_level, ScoopInfo), 226383d01c6SDmitry Eremin-Solenikov VMSTATE_UINT32(gpio_dir, ScoopInfo), 227383d01c6SDmitry Eremin-Solenikov VMSTATE_UINT32(prev_level, ScoopInfo), 22852f91c37SMichael S. Tsirkin VMSTATE_VALIDATE("irq levels are 16 bit", vmstate_scoop_validate), 229383d01c6SDmitry Eremin-Solenikov VMSTATE_UINT16(mcr, ScoopInfo), 230383d01c6SDmitry Eremin-Solenikov VMSTATE_UINT16(cdr, ScoopInfo), 231383d01c6SDmitry Eremin-Solenikov VMSTATE_UINT16(ccr, ScoopInfo), 232383d01c6SDmitry Eremin-Solenikov VMSTATE_UINT16(irr, ScoopInfo), 233383d01c6SDmitry Eremin-Solenikov VMSTATE_UINT16(imr, ScoopInfo), 234383d01c6SDmitry Eremin-Solenikov VMSTATE_UINT16(isr, ScoopInfo), 235383d01c6SDmitry Eremin-Solenikov VMSTATE_UNUSED_TEST(is_version_0, 2), 236383d01c6SDmitry Eremin-Solenikov VMSTATE_END_OF_LIST(), 237383d01c6SDmitry Eremin-Solenikov }, 238383d01c6SDmitry Eremin-Solenikov }; 239383d01c6SDmitry Eremin-Solenikov 240999e12bbSAnthony Liguori static void scoop_sysbus_class_init(ObjectClass *klass, void *data) 241999e12bbSAnthony Liguori { 24239bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 243999e12bbSAnthony Liguori 24439bffca2SAnthony Liguori dc->desc = "Scoop2 Sharp custom ASIC"; 24539bffca2SAnthony Liguori dc->vmsd = &vmstate_scoop_regs; 246383d01c6SDmitry Eremin-Solenikov } 247999e12bbSAnthony Liguori 2488c43a6f0SAndreas Färber static const TypeInfo scoop_sysbus_info = { 249a009de46SAndreas Färber .name = TYPE_SCOOP, 25039bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 25139bffca2SAnthony Liguori .instance_size = sizeof(ScoopInfo), 25253677667Sxiaoqiang zhao .instance_init = scoop_init, 253999e12bbSAnthony Liguori .class_init = scoop_sysbus_class_init, 254383d01c6SDmitry Eremin-Solenikov }; 255383d01c6SDmitry Eremin-Solenikov 25683f7d43aSAndreas Färber static void scoop_register_types(void) 257383d01c6SDmitry Eremin-Solenikov { 25839bffca2SAnthony Liguori type_register_static(&scoop_sysbus_info); 259383d01c6SDmitry Eremin-Solenikov } 26083f7d43aSAndreas Färber 26183f7d43aSAndreas Färber type_init(scoop_register_types) 262383d01c6SDmitry Eremin-Solenikov 263e33d8cdbSbalrog /* Write the bootloader parameters memory area. */ 264e33d8cdbSbalrog 265e33d8cdbSbalrog #define MAGIC_CHG(a, b, c, d) ((d << 24) | (c << 16) | (b << 8) | a) 266e33d8cdbSbalrog 267541dc0d4SStefan Weil static struct QEMU_PACKED sl_param_info { 268e33d8cdbSbalrog uint32_t comadj_keyword; 269e33d8cdbSbalrog int32_t comadj; 270e33d8cdbSbalrog 271e33d8cdbSbalrog uint32_t uuid_keyword; 272e33d8cdbSbalrog char uuid[16]; 273e33d8cdbSbalrog 274e33d8cdbSbalrog uint32_t touch_keyword; 275e33d8cdbSbalrog int32_t touch_xp; 276e33d8cdbSbalrog int32_t touch_yp; 277e33d8cdbSbalrog int32_t touch_xd; 278e33d8cdbSbalrog int32_t touch_yd; 279e33d8cdbSbalrog 280e33d8cdbSbalrog uint32_t adadj_keyword; 281e33d8cdbSbalrog int32_t adadj; 282e33d8cdbSbalrog 283e33d8cdbSbalrog uint32_t phad_keyword; 284e33d8cdbSbalrog int32_t phadadj; 285e33d8cdbSbalrog } zaurus_bootparam = { 286e33d8cdbSbalrog .comadj_keyword = MAGIC_CHG('C', 'M', 'A', 'D'), 287e33d8cdbSbalrog .comadj = 125, 288e33d8cdbSbalrog .uuid_keyword = MAGIC_CHG('U', 'U', 'I', 'D'), 289e33d8cdbSbalrog .uuid = { -1 }, 290e33d8cdbSbalrog .touch_keyword = MAGIC_CHG('T', 'U', 'C', 'H'), 291e33d8cdbSbalrog .touch_xp = -1, 292e33d8cdbSbalrog .adadj_keyword = MAGIC_CHG('B', 'V', 'A', 'D'), 293e33d8cdbSbalrog .adadj = -1, 294e33d8cdbSbalrog .phad_keyword = MAGIC_CHG('P', 'H', 'A', 'D'), 295e33d8cdbSbalrog .phadadj = 0x01, 296e33d8cdbSbalrog }; 297e33d8cdbSbalrog 298a8170e5eSAvi Kivity void sl_bootparam_write(hwaddr ptr) 299e33d8cdbSbalrog { 300e1fe50dcSStefan Weil cpu_physical_memory_write(ptr, &zaurus_bootparam, 301e33d8cdbSbalrog sizeof(struct sl_param_info)); 302e33d8cdbSbalrog } 303