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 */ 180b8fa32fSMarkus Armbruster 1917b7f2dbSPeter Maydell #include "qemu/osdep.h" 2064552b6bSMarkus Armbruster #include "hw/irq.h" 210d09e41aSPaolo Bonzini #include "hw/arm/sharpsl.h" 2283c9f4caSPaolo Bonzini #include "hw/sysbus.h" 23d6454270SMarkus Armbruster #include "migration/vmstate.h" 240b8fa32fSMarkus Armbruster #include "qemu/module.h" 25a0a8cf78SPeter Maydell #include "qemu/log.h" 26db1015e9SEduardo Habkost #include "qom/object.h" 27e33d8cdbSbalrog 28e33d8cdbSbalrog /* SCOOP devices */ 29e33d8cdbSbalrog 30a009de46SAndreas Färber #define TYPE_SCOOP "scoop" 318063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(ScoopInfo, SCOOP) 32a009de46SAndreas Färber 33bc24a225SPaul Brook struct ScoopInfo { 34a009de46SAndreas Färber SysBusDevice parent_obj; 35a009de46SAndreas Färber 36e33d8cdbSbalrog qemu_irq handler[16]; 37e71ceafcSAvi Kivity MemoryRegion iomem; 38e33d8cdbSbalrog uint16_t status; 39e33d8cdbSbalrog uint16_t power; 40e33d8cdbSbalrog uint32_t gpio_level; 41e33d8cdbSbalrog uint32_t gpio_dir; 42e33d8cdbSbalrog uint32_t prev_level; 43e33d8cdbSbalrog 44e33d8cdbSbalrog uint16_t mcr; 45e33d8cdbSbalrog uint16_t cdr; 46e33d8cdbSbalrog uint16_t ccr; 47e33d8cdbSbalrog uint16_t irr; 48e33d8cdbSbalrog uint16_t imr; 49e33d8cdbSbalrog uint16_t isr; 50e33d8cdbSbalrog }; 51e33d8cdbSbalrog 52e33d8cdbSbalrog #define SCOOP_MCR 0x00 53e33d8cdbSbalrog #define SCOOP_CDR 0x04 54e33d8cdbSbalrog #define SCOOP_CSR 0x08 55e33d8cdbSbalrog #define SCOOP_CPR 0x0c 56e33d8cdbSbalrog #define SCOOP_CCR 0x10 57e33d8cdbSbalrog #define SCOOP_IRR_IRM 0x14 58e33d8cdbSbalrog #define SCOOP_IMR 0x18 59e33d8cdbSbalrog #define SCOOP_ISR 0x1c 60e33d8cdbSbalrog #define SCOOP_GPCR 0x20 61e33d8cdbSbalrog #define SCOOP_GPWR 0x24 62e33d8cdbSbalrog #define SCOOP_GPRR 0x28 63e33d8cdbSbalrog 64*d0a040a8STanmay Patil static inline void scoop_gpio_handler_update(ScoopInfo *s) 65*d0a040a8STanmay Patil { 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: 106a0a8cf78SPeter Maydell qemu_log_mask(LOG_GUEST_ERROR, 107a0a8cf78SPeter Maydell "scoop_read: bad register offset 0x%02" HWADDR_PRIx "\n", 108a0a8cf78SPeter Maydell addr); 109e33d8cdbSbalrog } 110e33d8cdbSbalrog 111e33d8cdbSbalrog return 0; 112e33d8cdbSbalrog } 113e33d8cdbSbalrog 114a8170e5eSAvi Kivity static void scoop_write(void *opaque, hwaddr addr, 115e71ceafcSAvi Kivity uint64_t value, unsigned size) 116e33d8cdbSbalrog { 117bc24a225SPaul Brook ScoopInfo *s = (ScoopInfo *) opaque; 118e33d8cdbSbalrog value &= 0xffff; 119e33d8cdbSbalrog 120aa9438d9SDmitry Eremin-Solenikov switch (addr & 0x3f) { 121e33d8cdbSbalrog case SCOOP_MCR: 122e33d8cdbSbalrog s->mcr = value; 123e33d8cdbSbalrog break; 124e33d8cdbSbalrog case SCOOP_CDR: 125e33d8cdbSbalrog s->cdr = value; 126e33d8cdbSbalrog break; 127e33d8cdbSbalrog case SCOOP_CPR: 128e33d8cdbSbalrog s->power = value; 129*d0a040a8STanmay Patil if (value & 0x80) { 130e33d8cdbSbalrog s->power |= 0x8040; 131*d0a040a8STanmay Patil } 132e33d8cdbSbalrog break; 133e33d8cdbSbalrog case SCOOP_CCR: 134e33d8cdbSbalrog s->ccr = value; 135e33d8cdbSbalrog break; 136e33d8cdbSbalrog case SCOOP_IRR_IRM: 137e33d8cdbSbalrog s->irr = value; 138e33d8cdbSbalrog break; 139e33d8cdbSbalrog case SCOOP_IMR: 140e33d8cdbSbalrog s->imr = value; 141e33d8cdbSbalrog break; 142e33d8cdbSbalrog case SCOOP_ISR: 143e33d8cdbSbalrog s->isr = value; 144e33d8cdbSbalrog break; 145e33d8cdbSbalrog case SCOOP_GPCR: 146e33d8cdbSbalrog s->gpio_dir = value; 147e33d8cdbSbalrog scoop_gpio_handler_update(s); 148e33d8cdbSbalrog break; 149e33d8cdbSbalrog case SCOOP_GPWR: 1501f163b14Sbalrog case SCOOP_GPRR: /* GPRR is probably R/O in real HW */ 151e33d8cdbSbalrog s->gpio_level = value & s->gpio_dir; 152e33d8cdbSbalrog scoop_gpio_handler_update(s); 153e33d8cdbSbalrog break; 154e33d8cdbSbalrog default: 155a0a8cf78SPeter Maydell qemu_log_mask(LOG_GUEST_ERROR, 156a0a8cf78SPeter Maydell "scoop_write: bad register offset 0x%02" HWADDR_PRIx "\n", 157a0a8cf78SPeter Maydell addr); 158e33d8cdbSbalrog } 159e33d8cdbSbalrog } 160e33d8cdbSbalrog 161e71ceafcSAvi Kivity static const MemoryRegionOps scoop_ops = { 162e71ceafcSAvi Kivity .read = scoop_read, 163e71ceafcSAvi Kivity .write = scoop_write, 164e71ceafcSAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 165e33d8cdbSbalrog }; 166e33d8cdbSbalrog 167383d01c6SDmitry Eremin-Solenikov static void scoop_gpio_set(void *opaque, int line, int level) 168e33d8cdbSbalrog { 1698d30b794STorsten Duwe ScoopInfo *s = (ScoopInfo *) opaque; 170e33d8cdbSbalrog 171*d0a040a8STanmay Patil if (level) { 172e33d8cdbSbalrog s->gpio_level |= (1 << line); 173*d0a040a8STanmay Patil } else { 174e33d8cdbSbalrog s->gpio_level &= ~(1 << line); 175e33d8cdbSbalrog } 176*d0a040a8STanmay Patil } 177e33d8cdbSbalrog 17853677667Sxiaoqiang zhao static void scoop_init(Object *obj) 179e33d8cdbSbalrog { 18053677667Sxiaoqiang zhao DeviceState *dev = DEVICE(obj); 18153677667Sxiaoqiang zhao ScoopInfo *s = SCOOP(obj); 18253677667Sxiaoqiang zhao SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 183e33d8cdbSbalrog 184383d01c6SDmitry Eremin-Solenikov s->status = 0x02; 185a009de46SAndreas Färber qdev_init_gpio_out(dev, s->handler, 16); 186a009de46SAndreas Färber qdev_init_gpio_in(dev, scoop_gpio_set, 16); 18753677667Sxiaoqiang zhao memory_region_init_io(&s->iomem, obj, &scoop_ops, s, "scoop", 0x1000); 188e33d8cdbSbalrog 189a009de46SAndreas Färber sysbus_init_mmio(sbd, &s->iomem); 190e33d8cdbSbalrog } 191e33d8cdbSbalrog 1927fe63a17SDmitry Eremin-Solenikov static int scoop_post_load(void *opaque, int version_id) 1937fe63a17SDmitry Eremin-Solenikov { 1947fe63a17SDmitry Eremin-Solenikov ScoopInfo *s = (ScoopInfo *) opaque; 1957fe63a17SDmitry Eremin-Solenikov int i; 1967fe63a17SDmitry Eremin-Solenikov uint32_t level; 1977fe63a17SDmitry Eremin-Solenikov 1987fe63a17SDmitry Eremin-Solenikov level = s->gpio_level & s->gpio_dir; 1997fe63a17SDmitry Eremin-Solenikov 2007fe63a17SDmitry Eremin-Solenikov for (i = 0; i < 16; i++) { 2017fe63a17SDmitry Eremin-Solenikov qemu_set_irq(s->handler[i], (level >> i) & 1); 2027fe63a17SDmitry Eremin-Solenikov } 2037fe63a17SDmitry Eremin-Solenikov 2047fe63a17SDmitry Eremin-Solenikov s->prev_level = level; 2057fe63a17SDmitry Eremin-Solenikov 2067fe63a17SDmitry Eremin-Solenikov return 0; 2077fe63a17SDmitry Eremin-Solenikov } 2087fe63a17SDmitry Eremin-Solenikov 209383d01c6SDmitry Eremin-Solenikov static bool is_version_0(void *opaque, int version_id) 210383d01c6SDmitry Eremin-Solenikov { 211383d01c6SDmitry Eremin-Solenikov return version_id == 0; 212e33d8cdbSbalrog } 213e33d8cdbSbalrog 21452f91c37SMichael S. Tsirkin static bool vmstate_scoop_validate(void *opaque, int version_id) 21552f91c37SMichael S. Tsirkin { 21652f91c37SMichael S. Tsirkin ScoopInfo *s = opaque; 21752f91c37SMichael S. Tsirkin 21852f91c37SMichael S. Tsirkin return !(s->prev_level & 0xffff0000) && 21952f91c37SMichael S. Tsirkin !(s->gpio_level & 0xffff0000) && 22052f91c37SMichael S. Tsirkin !(s->gpio_dir & 0xffff0000); 22152f91c37SMichael S. Tsirkin } 22252f91c37SMichael S. Tsirkin 223383d01c6SDmitry Eremin-Solenikov static const VMStateDescription vmstate_scoop_regs = { 224383d01c6SDmitry Eremin-Solenikov .name = "scoop", 225383d01c6SDmitry Eremin-Solenikov .version_id = 1, 226383d01c6SDmitry Eremin-Solenikov .minimum_version_id = 0, 2277fe63a17SDmitry Eremin-Solenikov .post_load = scoop_post_load, 2283b9e779bSRichard Henderson .fields = (const VMStateField[]) { 229383d01c6SDmitry Eremin-Solenikov VMSTATE_UINT16(status, ScoopInfo), 230383d01c6SDmitry Eremin-Solenikov VMSTATE_UINT16(power, ScoopInfo), 231383d01c6SDmitry Eremin-Solenikov VMSTATE_UINT32(gpio_level, ScoopInfo), 232383d01c6SDmitry Eremin-Solenikov VMSTATE_UINT32(gpio_dir, ScoopInfo), 233383d01c6SDmitry Eremin-Solenikov VMSTATE_UINT32(prev_level, ScoopInfo), 23452f91c37SMichael S. Tsirkin VMSTATE_VALIDATE("irq levels are 16 bit", vmstate_scoop_validate), 235383d01c6SDmitry Eremin-Solenikov VMSTATE_UINT16(mcr, ScoopInfo), 236383d01c6SDmitry Eremin-Solenikov VMSTATE_UINT16(cdr, ScoopInfo), 237383d01c6SDmitry Eremin-Solenikov VMSTATE_UINT16(ccr, ScoopInfo), 238383d01c6SDmitry Eremin-Solenikov VMSTATE_UINT16(irr, ScoopInfo), 239383d01c6SDmitry Eremin-Solenikov VMSTATE_UINT16(imr, ScoopInfo), 240383d01c6SDmitry Eremin-Solenikov VMSTATE_UINT16(isr, ScoopInfo), 241383d01c6SDmitry Eremin-Solenikov VMSTATE_UNUSED_TEST(is_version_0, 2), 242383d01c6SDmitry Eremin-Solenikov VMSTATE_END_OF_LIST(), 243383d01c6SDmitry Eremin-Solenikov }, 244383d01c6SDmitry Eremin-Solenikov }; 245383d01c6SDmitry Eremin-Solenikov 246999e12bbSAnthony Liguori static void scoop_sysbus_class_init(ObjectClass *klass, void *data) 247999e12bbSAnthony Liguori { 24839bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 249999e12bbSAnthony Liguori 25039bffca2SAnthony Liguori dc->desc = "Scoop2 Sharp custom ASIC"; 25139bffca2SAnthony Liguori dc->vmsd = &vmstate_scoop_regs; 252383d01c6SDmitry Eremin-Solenikov } 253999e12bbSAnthony Liguori 2548c43a6f0SAndreas Färber static const TypeInfo scoop_sysbus_info = { 255a009de46SAndreas Färber .name = TYPE_SCOOP, 25639bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 25739bffca2SAnthony Liguori .instance_size = sizeof(ScoopInfo), 25853677667Sxiaoqiang zhao .instance_init = scoop_init, 259999e12bbSAnthony Liguori .class_init = scoop_sysbus_class_init, 260383d01c6SDmitry Eremin-Solenikov }; 261383d01c6SDmitry Eremin-Solenikov 26283f7d43aSAndreas Färber static void scoop_register_types(void) 263383d01c6SDmitry Eremin-Solenikov { 26439bffca2SAnthony Liguori type_register_static(&scoop_sysbus_info); 265383d01c6SDmitry Eremin-Solenikov } 26683f7d43aSAndreas Färber 26783f7d43aSAndreas Färber type_init(scoop_register_types) 268383d01c6SDmitry Eremin-Solenikov 269e33d8cdbSbalrog /* Write the bootloader parameters memory area. */ 270e33d8cdbSbalrog 271e33d8cdbSbalrog #define MAGIC_CHG(a, b, c, d) ((d << 24) | (c << 16) | (b << 8) | a) 272e33d8cdbSbalrog 273541dc0d4SStefan Weil static struct QEMU_PACKED sl_param_info { 274e33d8cdbSbalrog uint32_t comadj_keyword; 275e33d8cdbSbalrog int32_t comadj; 276e33d8cdbSbalrog 277e33d8cdbSbalrog uint32_t uuid_keyword; 278e33d8cdbSbalrog char uuid[16]; 279e33d8cdbSbalrog 280e33d8cdbSbalrog uint32_t touch_keyword; 281e33d8cdbSbalrog int32_t touch_xp; 282e33d8cdbSbalrog int32_t touch_yp; 283e33d8cdbSbalrog int32_t touch_xd; 284e33d8cdbSbalrog int32_t touch_yd; 285e33d8cdbSbalrog 286e33d8cdbSbalrog uint32_t adadj_keyword; 287e33d8cdbSbalrog int32_t adadj; 288e33d8cdbSbalrog 289e33d8cdbSbalrog uint32_t phad_keyword; 290e33d8cdbSbalrog int32_t phadadj; 291e33d8cdbSbalrog } zaurus_bootparam = { 292e33d8cdbSbalrog .comadj_keyword = MAGIC_CHG('C', 'M', 'A', 'D'), 293e33d8cdbSbalrog .comadj = 125, 294e33d8cdbSbalrog .uuid_keyword = MAGIC_CHG('U', 'U', 'I', 'D'), 295e33d8cdbSbalrog .uuid = { -1 }, 296e33d8cdbSbalrog .touch_keyword = MAGIC_CHG('T', 'U', 'C', 'H'), 297e33d8cdbSbalrog .touch_xp = -1, 298e33d8cdbSbalrog .adadj_keyword = MAGIC_CHG('B', 'V', 'A', 'D'), 299e33d8cdbSbalrog .adadj = -1, 300e33d8cdbSbalrog .phad_keyword = MAGIC_CHG('P', 'H', 'A', 'D'), 301e33d8cdbSbalrog .phadadj = 0x01, 302e33d8cdbSbalrog }; 303e33d8cdbSbalrog 304a8170e5eSAvi Kivity void sl_bootparam_write(hwaddr ptr) 305e33d8cdbSbalrog { 306e1fe50dcSStefan Weil cpu_physical_memory_write(ptr, &zaurus_bootparam, 307e33d8cdbSbalrog sizeof(struct sl_param_info)); 308e33d8cdbSbalrog } 309