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 */ 1817b7f2dbSPeter Maydell #include "qemu/osdep.h" 1983c9f4caSPaolo Bonzini #include "hw/hw.h" 200d09e41aSPaolo Bonzini #include "hw/arm/sharpsl.h" 2183c9f4caSPaolo Bonzini #include "hw/sysbus.h" 22e33d8cdbSbalrog 23e33d8cdbSbalrog #undef REG_FMT 24e33d8cdbSbalrog #define REG_FMT "0x%02lx" 25e33d8cdbSbalrog 26e33d8cdbSbalrog /* SCOOP devices */ 27e33d8cdbSbalrog 28a009de46SAndreas Färber #define TYPE_SCOOP "scoop" 29a009de46SAndreas Färber #define SCOOP(obj) OBJECT_CHECK(ScoopInfo, (obj), TYPE_SCOOP) 30a009de46SAndreas Färber 31383d01c6SDmitry Eremin-Solenikov typedef struct ScoopInfo ScoopInfo; 32bc24a225SPaul Brook struct ScoopInfo { 33a009de46SAndreas Färber SysBusDevice parent_obj; 34a009de46SAndreas Färber 35e33d8cdbSbalrog qemu_irq handler[16]; 36e71ceafcSAvi Kivity MemoryRegion iomem; 37e33d8cdbSbalrog uint16_t status; 38e33d8cdbSbalrog uint16_t power; 39e33d8cdbSbalrog uint32_t gpio_level; 40e33d8cdbSbalrog uint32_t gpio_dir; 41e33d8cdbSbalrog uint32_t prev_level; 42e33d8cdbSbalrog 43e33d8cdbSbalrog uint16_t mcr; 44e33d8cdbSbalrog uint16_t cdr; 45e33d8cdbSbalrog uint16_t ccr; 46e33d8cdbSbalrog uint16_t irr; 47e33d8cdbSbalrog uint16_t imr; 48e33d8cdbSbalrog uint16_t isr; 49e33d8cdbSbalrog }; 50e33d8cdbSbalrog 51e33d8cdbSbalrog #define SCOOP_MCR 0x00 52e33d8cdbSbalrog #define SCOOP_CDR 0x04 53e33d8cdbSbalrog #define SCOOP_CSR 0x08 54e33d8cdbSbalrog #define SCOOP_CPR 0x0c 55e33d8cdbSbalrog #define SCOOP_CCR 0x10 56e33d8cdbSbalrog #define SCOOP_IRR_IRM 0x14 57e33d8cdbSbalrog #define SCOOP_IMR 0x18 58e33d8cdbSbalrog #define SCOOP_ISR 0x1c 59e33d8cdbSbalrog #define SCOOP_GPCR 0x20 60e33d8cdbSbalrog #define SCOOP_GPWR 0x24 61e33d8cdbSbalrog #define SCOOP_GPRR 0x28 62e33d8cdbSbalrog 63bc24a225SPaul Brook static inline void scoop_gpio_handler_update(ScoopInfo *s) { 64e33d8cdbSbalrog uint32_t level, diff; 65e33d8cdbSbalrog int bit; 66e33d8cdbSbalrog level = s->gpio_level & s->gpio_dir; 67e33d8cdbSbalrog 68e33d8cdbSbalrog for (diff = s->prev_level ^ level; diff; diff ^= 1 << bit) { 69786a4ea8SStefan Hajnoczi bit = ctz32(diff); 70e33d8cdbSbalrog qemu_set_irq(s->handler[bit], (level >> bit) & 1); 71e33d8cdbSbalrog } 72e33d8cdbSbalrog 73e33d8cdbSbalrog s->prev_level = level; 74e33d8cdbSbalrog } 75e33d8cdbSbalrog 76a8170e5eSAvi Kivity static uint64_t scoop_read(void *opaque, hwaddr addr, 77e71ceafcSAvi Kivity unsigned size) 78e33d8cdbSbalrog { 79bc24a225SPaul Brook ScoopInfo *s = (ScoopInfo *) opaque; 80e33d8cdbSbalrog 81aa9438d9SDmitry Eremin-Solenikov switch (addr & 0x3f) { 82e33d8cdbSbalrog case SCOOP_MCR: 83e33d8cdbSbalrog return s->mcr; 84e33d8cdbSbalrog case SCOOP_CDR: 85e33d8cdbSbalrog return s->cdr; 86e33d8cdbSbalrog case SCOOP_CSR: 87e33d8cdbSbalrog return s->status; 88e33d8cdbSbalrog case SCOOP_CPR: 89e33d8cdbSbalrog return s->power; 90e33d8cdbSbalrog case SCOOP_CCR: 91e33d8cdbSbalrog return s->ccr; 92e33d8cdbSbalrog case SCOOP_IRR_IRM: 93e33d8cdbSbalrog return s->irr; 94e33d8cdbSbalrog case SCOOP_IMR: 95e33d8cdbSbalrog return s->imr; 96e33d8cdbSbalrog case SCOOP_ISR: 97e33d8cdbSbalrog return s->isr; 98e33d8cdbSbalrog case SCOOP_GPCR: 99e33d8cdbSbalrog return s->gpio_dir; 100e33d8cdbSbalrog case SCOOP_GPWR: 101e33d8cdbSbalrog case SCOOP_GPRR: 1021f163b14Sbalrog return s->gpio_level; 103e33d8cdbSbalrog default: 104a8b7063bSBlue Swirl zaurus_printf("Bad register offset " REG_FMT "\n", (unsigned long)addr); 105e33d8cdbSbalrog } 106e33d8cdbSbalrog 107e33d8cdbSbalrog return 0; 108e33d8cdbSbalrog } 109e33d8cdbSbalrog 110a8170e5eSAvi Kivity static void scoop_write(void *opaque, hwaddr addr, 111e71ceafcSAvi Kivity uint64_t value, unsigned size) 112e33d8cdbSbalrog { 113bc24a225SPaul Brook ScoopInfo *s = (ScoopInfo *) opaque; 114e33d8cdbSbalrog value &= 0xffff; 115e33d8cdbSbalrog 116aa9438d9SDmitry Eremin-Solenikov switch (addr & 0x3f) { 117e33d8cdbSbalrog case SCOOP_MCR: 118e33d8cdbSbalrog s->mcr = value; 119e33d8cdbSbalrog break; 120e33d8cdbSbalrog case SCOOP_CDR: 121e33d8cdbSbalrog s->cdr = value; 122e33d8cdbSbalrog break; 123e33d8cdbSbalrog case SCOOP_CPR: 124e33d8cdbSbalrog s->power = value; 125e33d8cdbSbalrog if (value & 0x80) 126e33d8cdbSbalrog s->power |= 0x8040; 127e33d8cdbSbalrog break; 128e33d8cdbSbalrog case SCOOP_CCR: 129e33d8cdbSbalrog s->ccr = value; 130e33d8cdbSbalrog break; 131e33d8cdbSbalrog case SCOOP_IRR_IRM: 132e33d8cdbSbalrog s->irr = value; 133e33d8cdbSbalrog break; 134e33d8cdbSbalrog case SCOOP_IMR: 135e33d8cdbSbalrog s->imr = value; 136e33d8cdbSbalrog break; 137e33d8cdbSbalrog case SCOOP_ISR: 138e33d8cdbSbalrog s->isr = value; 139e33d8cdbSbalrog break; 140e33d8cdbSbalrog case SCOOP_GPCR: 141e33d8cdbSbalrog s->gpio_dir = value; 142e33d8cdbSbalrog scoop_gpio_handler_update(s); 143e33d8cdbSbalrog break; 144e33d8cdbSbalrog case SCOOP_GPWR: 1451f163b14Sbalrog case SCOOP_GPRR: /* GPRR is probably R/O in real HW */ 146e33d8cdbSbalrog s->gpio_level = value & s->gpio_dir; 147e33d8cdbSbalrog scoop_gpio_handler_update(s); 148e33d8cdbSbalrog break; 149e33d8cdbSbalrog default: 150a8b7063bSBlue Swirl zaurus_printf("Bad register offset " REG_FMT "\n", (unsigned long)addr); 151e33d8cdbSbalrog } 152e33d8cdbSbalrog } 153e33d8cdbSbalrog 154e71ceafcSAvi Kivity static const MemoryRegionOps scoop_ops = { 155e71ceafcSAvi Kivity .read = scoop_read, 156e71ceafcSAvi Kivity .write = scoop_write, 157e71ceafcSAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 158e33d8cdbSbalrog }; 159e33d8cdbSbalrog 160383d01c6SDmitry Eremin-Solenikov static void scoop_gpio_set(void *opaque, int line, int level) 161e33d8cdbSbalrog { 1628d30b794STorsten Duwe ScoopInfo *s = (ScoopInfo *) opaque; 163e33d8cdbSbalrog 164e33d8cdbSbalrog if (level) 165e33d8cdbSbalrog s->gpio_level |= (1 << line); 166e33d8cdbSbalrog else 167e33d8cdbSbalrog s->gpio_level &= ~(1 << line); 168e33d8cdbSbalrog } 169e33d8cdbSbalrog 170*53677667Sxiaoqiang zhao static void scoop_init(Object *obj) 171e33d8cdbSbalrog { 172*53677667Sxiaoqiang zhao DeviceState *dev = DEVICE(obj); 173*53677667Sxiaoqiang zhao ScoopInfo *s = SCOOP(obj); 174*53677667Sxiaoqiang zhao SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 175e33d8cdbSbalrog 176383d01c6SDmitry Eremin-Solenikov s->status = 0x02; 177a009de46SAndreas Färber qdev_init_gpio_out(dev, s->handler, 16); 178a009de46SAndreas Färber qdev_init_gpio_in(dev, scoop_gpio_set, 16); 179*53677667Sxiaoqiang zhao memory_region_init_io(&s->iomem, obj, &scoop_ops, s, "scoop", 0x1000); 180e33d8cdbSbalrog 181a009de46SAndreas Färber sysbus_init_mmio(sbd, &s->iomem); 182e33d8cdbSbalrog } 183e33d8cdbSbalrog 1847fe63a17SDmitry Eremin-Solenikov static int scoop_post_load(void *opaque, int version_id) 1857fe63a17SDmitry Eremin-Solenikov { 1867fe63a17SDmitry Eremin-Solenikov ScoopInfo *s = (ScoopInfo *) opaque; 1877fe63a17SDmitry Eremin-Solenikov int i; 1887fe63a17SDmitry Eremin-Solenikov uint32_t level; 1897fe63a17SDmitry Eremin-Solenikov 1907fe63a17SDmitry Eremin-Solenikov level = s->gpio_level & s->gpio_dir; 1917fe63a17SDmitry Eremin-Solenikov 1927fe63a17SDmitry Eremin-Solenikov for (i = 0; i < 16; i++) { 1937fe63a17SDmitry Eremin-Solenikov qemu_set_irq(s->handler[i], (level >> i) & 1); 1947fe63a17SDmitry Eremin-Solenikov } 1957fe63a17SDmitry Eremin-Solenikov 1967fe63a17SDmitry Eremin-Solenikov s->prev_level = level; 1977fe63a17SDmitry Eremin-Solenikov 1987fe63a17SDmitry Eremin-Solenikov return 0; 1997fe63a17SDmitry Eremin-Solenikov } 2007fe63a17SDmitry Eremin-Solenikov 201383d01c6SDmitry Eremin-Solenikov static bool is_version_0 (void *opaque, int version_id) 202383d01c6SDmitry Eremin-Solenikov { 203383d01c6SDmitry Eremin-Solenikov return version_id == 0; 204e33d8cdbSbalrog } 205e33d8cdbSbalrog 20652f91c37SMichael S. Tsirkin static bool vmstate_scoop_validate(void *opaque, int version_id) 20752f91c37SMichael S. Tsirkin { 20852f91c37SMichael S. Tsirkin ScoopInfo *s = opaque; 20952f91c37SMichael S. Tsirkin 21052f91c37SMichael S. Tsirkin return !(s->prev_level & 0xffff0000) && 21152f91c37SMichael S. Tsirkin !(s->gpio_level & 0xffff0000) && 21252f91c37SMichael S. Tsirkin !(s->gpio_dir & 0xffff0000); 21352f91c37SMichael S. Tsirkin } 21452f91c37SMichael S. Tsirkin 215383d01c6SDmitry Eremin-Solenikov static const VMStateDescription vmstate_scoop_regs = { 216383d01c6SDmitry Eremin-Solenikov .name = "scoop", 217383d01c6SDmitry Eremin-Solenikov .version_id = 1, 218383d01c6SDmitry Eremin-Solenikov .minimum_version_id = 0, 2197fe63a17SDmitry Eremin-Solenikov .post_load = scoop_post_load, 220383d01c6SDmitry Eremin-Solenikov .fields = (VMStateField[]) { 221383d01c6SDmitry Eremin-Solenikov VMSTATE_UINT16(status, ScoopInfo), 222383d01c6SDmitry Eremin-Solenikov VMSTATE_UINT16(power, ScoopInfo), 223383d01c6SDmitry Eremin-Solenikov VMSTATE_UINT32(gpio_level, ScoopInfo), 224383d01c6SDmitry Eremin-Solenikov VMSTATE_UINT32(gpio_dir, ScoopInfo), 225383d01c6SDmitry Eremin-Solenikov VMSTATE_UINT32(prev_level, ScoopInfo), 22652f91c37SMichael S. Tsirkin VMSTATE_VALIDATE("irq levels are 16 bit", vmstate_scoop_validate), 227383d01c6SDmitry Eremin-Solenikov VMSTATE_UINT16(mcr, ScoopInfo), 228383d01c6SDmitry Eremin-Solenikov VMSTATE_UINT16(cdr, ScoopInfo), 229383d01c6SDmitry Eremin-Solenikov VMSTATE_UINT16(ccr, ScoopInfo), 230383d01c6SDmitry Eremin-Solenikov VMSTATE_UINT16(irr, ScoopInfo), 231383d01c6SDmitry Eremin-Solenikov VMSTATE_UINT16(imr, ScoopInfo), 232383d01c6SDmitry Eremin-Solenikov VMSTATE_UINT16(isr, ScoopInfo), 233383d01c6SDmitry Eremin-Solenikov VMSTATE_UNUSED_TEST(is_version_0, 2), 234383d01c6SDmitry Eremin-Solenikov VMSTATE_END_OF_LIST(), 235383d01c6SDmitry Eremin-Solenikov }, 236383d01c6SDmitry Eremin-Solenikov }; 237383d01c6SDmitry Eremin-Solenikov 238999e12bbSAnthony Liguori static void scoop_sysbus_class_init(ObjectClass *klass, void *data) 239999e12bbSAnthony Liguori { 24039bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 241999e12bbSAnthony Liguori 24239bffca2SAnthony Liguori dc->desc = "Scoop2 Sharp custom ASIC"; 24339bffca2SAnthony Liguori dc->vmsd = &vmstate_scoop_regs; 244383d01c6SDmitry Eremin-Solenikov } 245999e12bbSAnthony Liguori 2468c43a6f0SAndreas Färber static const TypeInfo scoop_sysbus_info = { 247a009de46SAndreas Färber .name = TYPE_SCOOP, 24839bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 24939bffca2SAnthony Liguori .instance_size = sizeof(ScoopInfo), 250*53677667Sxiaoqiang zhao .instance_init = scoop_init, 251999e12bbSAnthony Liguori .class_init = scoop_sysbus_class_init, 252383d01c6SDmitry Eremin-Solenikov }; 253383d01c6SDmitry Eremin-Solenikov 25483f7d43aSAndreas Färber static void scoop_register_types(void) 255383d01c6SDmitry Eremin-Solenikov { 25639bffca2SAnthony Liguori type_register_static(&scoop_sysbus_info); 257383d01c6SDmitry Eremin-Solenikov } 25883f7d43aSAndreas Färber 25983f7d43aSAndreas Färber type_init(scoop_register_types) 260383d01c6SDmitry Eremin-Solenikov 261e33d8cdbSbalrog /* Write the bootloader parameters memory area. */ 262e33d8cdbSbalrog 263e33d8cdbSbalrog #define MAGIC_CHG(a, b, c, d) ((d << 24) | (c << 16) | (b << 8) | a) 264e33d8cdbSbalrog 265541dc0d4SStefan Weil static struct QEMU_PACKED sl_param_info { 266e33d8cdbSbalrog uint32_t comadj_keyword; 267e33d8cdbSbalrog int32_t comadj; 268e33d8cdbSbalrog 269e33d8cdbSbalrog uint32_t uuid_keyword; 270e33d8cdbSbalrog char uuid[16]; 271e33d8cdbSbalrog 272e33d8cdbSbalrog uint32_t touch_keyword; 273e33d8cdbSbalrog int32_t touch_xp; 274e33d8cdbSbalrog int32_t touch_yp; 275e33d8cdbSbalrog int32_t touch_xd; 276e33d8cdbSbalrog int32_t touch_yd; 277e33d8cdbSbalrog 278e33d8cdbSbalrog uint32_t adadj_keyword; 279e33d8cdbSbalrog int32_t adadj; 280e33d8cdbSbalrog 281e33d8cdbSbalrog uint32_t phad_keyword; 282e33d8cdbSbalrog int32_t phadadj; 283e33d8cdbSbalrog } zaurus_bootparam = { 284e33d8cdbSbalrog .comadj_keyword = MAGIC_CHG('C', 'M', 'A', 'D'), 285e33d8cdbSbalrog .comadj = 125, 286e33d8cdbSbalrog .uuid_keyword = MAGIC_CHG('U', 'U', 'I', 'D'), 287e33d8cdbSbalrog .uuid = { -1 }, 288e33d8cdbSbalrog .touch_keyword = MAGIC_CHG('T', 'U', 'C', 'H'), 289e33d8cdbSbalrog .touch_xp = -1, 290e33d8cdbSbalrog .adadj_keyword = MAGIC_CHG('B', 'V', 'A', 'D'), 291e33d8cdbSbalrog .adadj = -1, 292e33d8cdbSbalrog .phad_keyword = MAGIC_CHG('P', 'H', 'A', 'D'), 293e33d8cdbSbalrog .phadadj = 0x01, 294e33d8cdbSbalrog }; 295e33d8cdbSbalrog 296a8170e5eSAvi Kivity void sl_bootparam_write(hwaddr ptr) 297e33d8cdbSbalrog { 298e1fe50dcSStefan Weil cpu_physical_memory_write(ptr, &zaurus_bootparam, 299e33d8cdbSbalrog sizeof(struct sl_param_info)); 300e33d8cdbSbalrog } 301