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 */ 1883c9f4caSPaolo Bonzini #include "hw/hw.h" 190d09e41aSPaolo Bonzini #include "hw/arm/sharpsl.h" 2083c9f4caSPaolo Bonzini #include "hw/sysbus.h" 21e33d8cdbSbalrog 22e33d8cdbSbalrog #undef REG_FMT 23e33d8cdbSbalrog #define REG_FMT "0x%02lx" 24e33d8cdbSbalrog 25e33d8cdbSbalrog /* SCOOP devices */ 26e33d8cdbSbalrog 27383d01c6SDmitry Eremin-Solenikov typedef struct ScoopInfo ScoopInfo; 28bc24a225SPaul Brook struct ScoopInfo { 29383d01c6SDmitry Eremin-Solenikov SysBusDevice busdev; 30e33d8cdbSbalrog qemu_irq handler[16]; 31e71ceafcSAvi Kivity MemoryRegion iomem; 32e33d8cdbSbalrog uint16_t status; 33e33d8cdbSbalrog uint16_t power; 34e33d8cdbSbalrog uint32_t gpio_level; 35e33d8cdbSbalrog uint32_t gpio_dir; 36e33d8cdbSbalrog uint32_t prev_level; 37e33d8cdbSbalrog 38e33d8cdbSbalrog uint16_t mcr; 39e33d8cdbSbalrog uint16_t cdr; 40e33d8cdbSbalrog uint16_t ccr; 41e33d8cdbSbalrog uint16_t irr; 42e33d8cdbSbalrog uint16_t imr; 43e33d8cdbSbalrog uint16_t isr; 44e33d8cdbSbalrog }; 45e33d8cdbSbalrog 46e33d8cdbSbalrog #define SCOOP_MCR 0x00 47e33d8cdbSbalrog #define SCOOP_CDR 0x04 48e33d8cdbSbalrog #define SCOOP_CSR 0x08 49e33d8cdbSbalrog #define SCOOP_CPR 0x0c 50e33d8cdbSbalrog #define SCOOP_CCR 0x10 51e33d8cdbSbalrog #define SCOOP_IRR_IRM 0x14 52e33d8cdbSbalrog #define SCOOP_IMR 0x18 53e33d8cdbSbalrog #define SCOOP_ISR 0x1c 54e33d8cdbSbalrog #define SCOOP_GPCR 0x20 55e33d8cdbSbalrog #define SCOOP_GPWR 0x24 56e33d8cdbSbalrog #define SCOOP_GPRR 0x28 57e33d8cdbSbalrog 58bc24a225SPaul Brook static inline void scoop_gpio_handler_update(ScoopInfo *s) { 59e33d8cdbSbalrog uint32_t level, diff; 60e33d8cdbSbalrog int bit; 61e33d8cdbSbalrog level = s->gpio_level & s->gpio_dir; 62e33d8cdbSbalrog 63e33d8cdbSbalrog for (diff = s->prev_level ^ level; diff; diff ^= 1 << bit) { 64e33d8cdbSbalrog bit = ffs(diff) - 1; 65e33d8cdbSbalrog qemu_set_irq(s->handler[bit], (level >> bit) & 1); 66e33d8cdbSbalrog } 67e33d8cdbSbalrog 68e33d8cdbSbalrog s->prev_level = level; 69e33d8cdbSbalrog } 70e33d8cdbSbalrog 71a8170e5eSAvi Kivity static uint64_t scoop_read(void *opaque, hwaddr addr, 72e71ceafcSAvi Kivity unsigned size) 73e33d8cdbSbalrog { 74bc24a225SPaul Brook ScoopInfo *s = (ScoopInfo *) opaque; 75e33d8cdbSbalrog 76aa9438d9SDmitry Eremin-Solenikov switch (addr & 0x3f) { 77e33d8cdbSbalrog case SCOOP_MCR: 78e33d8cdbSbalrog return s->mcr; 79e33d8cdbSbalrog case SCOOP_CDR: 80e33d8cdbSbalrog return s->cdr; 81e33d8cdbSbalrog case SCOOP_CSR: 82e33d8cdbSbalrog return s->status; 83e33d8cdbSbalrog case SCOOP_CPR: 84e33d8cdbSbalrog return s->power; 85e33d8cdbSbalrog case SCOOP_CCR: 86e33d8cdbSbalrog return s->ccr; 87e33d8cdbSbalrog case SCOOP_IRR_IRM: 88e33d8cdbSbalrog return s->irr; 89e33d8cdbSbalrog case SCOOP_IMR: 90e33d8cdbSbalrog return s->imr; 91e33d8cdbSbalrog case SCOOP_ISR: 92e33d8cdbSbalrog return s->isr; 93e33d8cdbSbalrog case SCOOP_GPCR: 94e33d8cdbSbalrog return s->gpio_dir; 95e33d8cdbSbalrog case SCOOP_GPWR: 96e33d8cdbSbalrog case SCOOP_GPRR: 971f163b14Sbalrog return s->gpio_level; 98e33d8cdbSbalrog default: 99a8b7063bSBlue Swirl zaurus_printf("Bad register offset " REG_FMT "\n", (unsigned long)addr); 100e33d8cdbSbalrog } 101e33d8cdbSbalrog 102e33d8cdbSbalrog return 0; 103e33d8cdbSbalrog } 104e33d8cdbSbalrog 105a8170e5eSAvi Kivity static void scoop_write(void *opaque, hwaddr addr, 106e71ceafcSAvi Kivity uint64_t value, unsigned size) 107e33d8cdbSbalrog { 108bc24a225SPaul Brook ScoopInfo *s = (ScoopInfo *) opaque; 109e33d8cdbSbalrog value &= 0xffff; 110e33d8cdbSbalrog 111aa9438d9SDmitry Eremin-Solenikov switch (addr & 0x3f) { 112e33d8cdbSbalrog case SCOOP_MCR: 113e33d8cdbSbalrog s->mcr = value; 114e33d8cdbSbalrog break; 115e33d8cdbSbalrog case SCOOP_CDR: 116e33d8cdbSbalrog s->cdr = value; 117e33d8cdbSbalrog break; 118e33d8cdbSbalrog case SCOOP_CPR: 119e33d8cdbSbalrog s->power = value; 120e33d8cdbSbalrog if (value & 0x80) 121e33d8cdbSbalrog s->power |= 0x8040; 122e33d8cdbSbalrog break; 123e33d8cdbSbalrog case SCOOP_CCR: 124e33d8cdbSbalrog s->ccr = value; 125e33d8cdbSbalrog break; 126e33d8cdbSbalrog case SCOOP_IRR_IRM: 127e33d8cdbSbalrog s->irr = value; 128e33d8cdbSbalrog break; 129e33d8cdbSbalrog case SCOOP_IMR: 130e33d8cdbSbalrog s->imr = value; 131e33d8cdbSbalrog break; 132e33d8cdbSbalrog case SCOOP_ISR: 133e33d8cdbSbalrog s->isr = value; 134e33d8cdbSbalrog break; 135e33d8cdbSbalrog case SCOOP_GPCR: 136e33d8cdbSbalrog s->gpio_dir = value; 137e33d8cdbSbalrog scoop_gpio_handler_update(s); 138e33d8cdbSbalrog break; 139e33d8cdbSbalrog case SCOOP_GPWR: 1401f163b14Sbalrog case SCOOP_GPRR: /* GPRR is probably R/O in real HW */ 141e33d8cdbSbalrog s->gpio_level = value & s->gpio_dir; 142e33d8cdbSbalrog scoop_gpio_handler_update(s); 143e33d8cdbSbalrog break; 144e33d8cdbSbalrog default: 145a8b7063bSBlue Swirl zaurus_printf("Bad register offset " REG_FMT "\n", (unsigned long)addr); 146e33d8cdbSbalrog } 147e33d8cdbSbalrog } 148e33d8cdbSbalrog 149e71ceafcSAvi Kivity static const MemoryRegionOps scoop_ops = { 150e71ceafcSAvi Kivity .read = scoop_read, 151e71ceafcSAvi Kivity .write = scoop_write, 152e71ceafcSAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 153e33d8cdbSbalrog }; 154e33d8cdbSbalrog 155383d01c6SDmitry Eremin-Solenikov static void scoop_gpio_set(void *opaque, int line, int level) 156e33d8cdbSbalrog { 1578d30b794STorsten Duwe ScoopInfo *s = (ScoopInfo *) opaque; 158e33d8cdbSbalrog 159e33d8cdbSbalrog if (level) 160e33d8cdbSbalrog s->gpio_level |= (1 << line); 161e33d8cdbSbalrog else 162e33d8cdbSbalrog s->gpio_level &= ~(1 << line); 163e33d8cdbSbalrog } 164e33d8cdbSbalrog 165383d01c6SDmitry Eremin-Solenikov static int scoop_init(SysBusDevice *dev) 166e33d8cdbSbalrog { 167383d01c6SDmitry Eremin-Solenikov ScoopInfo *s = FROM_SYSBUS(ScoopInfo, dev); 168e33d8cdbSbalrog 169383d01c6SDmitry Eremin-Solenikov s->status = 0x02; 170383d01c6SDmitry Eremin-Solenikov qdev_init_gpio_out(&s->busdev.qdev, s->handler, 16); 171383d01c6SDmitry Eremin-Solenikov qdev_init_gpio_in(&s->busdev.qdev, scoop_gpio_set, 16); 172e71ceafcSAvi Kivity memory_region_init_io(&s->iomem, &scoop_ops, s, "scoop", 0x1000); 173e33d8cdbSbalrog 174750ecd44SAvi Kivity sysbus_init_mmio(dev, &s->iomem); 175e33d8cdbSbalrog 176e33d8cdbSbalrog return 0; 177e33d8cdbSbalrog } 178e33d8cdbSbalrog 1797fe63a17SDmitry Eremin-Solenikov static int scoop_post_load(void *opaque, int version_id) 1807fe63a17SDmitry Eremin-Solenikov { 1817fe63a17SDmitry Eremin-Solenikov ScoopInfo *s = (ScoopInfo *) opaque; 1827fe63a17SDmitry Eremin-Solenikov int i; 1837fe63a17SDmitry Eremin-Solenikov uint32_t level; 1847fe63a17SDmitry Eremin-Solenikov 1857fe63a17SDmitry Eremin-Solenikov level = s->gpio_level & s->gpio_dir; 1867fe63a17SDmitry Eremin-Solenikov 1877fe63a17SDmitry Eremin-Solenikov for (i = 0; i < 16; i++) { 1887fe63a17SDmitry Eremin-Solenikov qemu_set_irq(s->handler[i], (level >> i) & 1); 1897fe63a17SDmitry Eremin-Solenikov } 1907fe63a17SDmitry Eremin-Solenikov 1917fe63a17SDmitry Eremin-Solenikov s->prev_level = level; 1927fe63a17SDmitry Eremin-Solenikov 1937fe63a17SDmitry Eremin-Solenikov return 0; 1947fe63a17SDmitry Eremin-Solenikov } 1957fe63a17SDmitry Eremin-Solenikov 196383d01c6SDmitry Eremin-Solenikov static bool is_version_0 (void *opaque, int version_id) 197383d01c6SDmitry Eremin-Solenikov { 198383d01c6SDmitry Eremin-Solenikov return version_id == 0; 199e33d8cdbSbalrog } 200e33d8cdbSbalrog 201383d01c6SDmitry Eremin-Solenikov static const VMStateDescription vmstate_scoop_regs = { 202383d01c6SDmitry Eremin-Solenikov .name = "scoop", 203383d01c6SDmitry Eremin-Solenikov .version_id = 1, 204383d01c6SDmitry Eremin-Solenikov .minimum_version_id = 0, 205383d01c6SDmitry Eremin-Solenikov .minimum_version_id_old = 0, 2067fe63a17SDmitry Eremin-Solenikov .post_load = scoop_post_load, 207383d01c6SDmitry Eremin-Solenikov .fields = (VMStateField []) { 208383d01c6SDmitry Eremin-Solenikov VMSTATE_UINT16(status, ScoopInfo), 209383d01c6SDmitry Eremin-Solenikov VMSTATE_UINT16(power, ScoopInfo), 210383d01c6SDmitry Eremin-Solenikov VMSTATE_UINT32(gpio_level, ScoopInfo), 211383d01c6SDmitry Eremin-Solenikov VMSTATE_UINT32(gpio_dir, ScoopInfo), 212383d01c6SDmitry Eremin-Solenikov VMSTATE_UINT32(prev_level, ScoopInfo), 213383d01c6SDmitry Eremin-Solenikov VMSTATE_UINT16(mcr, ScoopInfo), 214383d01c6SDmitry Eremin-Solenikov VMSTATE_UINT16(cdr, ScoopInfo), 215383d01c6SDmitry Eremin-Solenikov VMSTATE_UINT16(ccr, ScoopInfo), 216383d01c6SDmitry Eremin-Solenikov VMSTATE_UINT16(irr, ScoopInfo), 217383d01c6SDmitry Eremin-Solenikov VMSTATE_UINT16(imr, ScoopInfo), 218383d01c6SDmitry Eremin-Solenikov VMSTATE_UINT16(isr, ScoopInfo), 219383d01c6SDmitry Eremin-Solenikov VMSTATE_UNUSED_TEST(is_version_0, 2), 220383d01c6SDmitry Eremin-Solenikov VMSTATE_END_OF_LIST(), 221383d01c6SDmitry Eremin-Solenikov }, 222383d01c6SDmitry Eremin-Solenikov }; 223383d01c6SDmitry Eremin-Solenikov 224999e12bbSAnthony Liguori static Property scoop_sysbus_properties[] = { 225383d01c6SDmitry Eremin-Solenikov DEFINE_PROP_END_OF_LIST(), 226999e12bbSAnthony Liguori }; 227999e12bbSAnthony Liguori 228999e12bbSAnthony Liguori static void scoop_sysbus_class_init(ObjectClass *klass, void *data) 229999e12bbSAnthony Liguori { 23039bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 231999e12bbSAnthony Liguori SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); 232999e12bbSAnthony Liguori 233999e12bbSAnthony Liguori k->init = scoop_init; 23439bffca2SAnthony Liguori dc->desc = "Scoop2 Sharp custom ASIC"; 23539bffca2SAnthony Liguori dc->vmsd = &vmstate_scoop_regs; 23639bffca2SAnthony Liguori dc->props = scoop_sysbus_properties; 237383d01c6SDmitry Eremin-Solenikov } 238999e12bbSAnthony Liguori 2398c43a6f0SAndreas Färber static const TypeInfo scoop_sysbus_info = { 240999e12bbSAnthony Liguori .name = "scoop", 24139bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 24239bffca2SAnthony Liguori .instance_size = sizeof(ScoopInfo), 243999e12bbSAnthony Liguori .class_init = scoop_sysbus_class_init, 244383d01c6SDmitry Eremin-Solenikov }; 245383d01c6SDmitry Eremin-Solenikov 24683f7d43aSAndreas Färber static void scoop_register_types(void) 247383d01c6SDmitry Eremin-Solenikov { 24839bffca2SAnthony Liguori type_register_static(&scoop_sysbus_info); 249383d01c6SDmitry Eremin-Solenikov } 25083f7d43aSAndreas Färber 25183f7d43aSAndreas Färber type_init(scoop_register_types) 252383d01c6SDmitry Eremin-Solenikov 253e33d8cdbSbalrog /* Write the bootloader parameters memory area. */ 254e33d8cdbSbalrog 255e33d8cdbSbalrog #define MAGIC_CHG(a, b, c, d) ((d << 24) | (c << 16) | (b << 8) | a) 256e33d8cdbSbalrog 257541dc0d4SStefan Weil static struct QEMU_PACKED sl_param_info { 258e33d8cdbSbalrog uint32_t comadj_keyword; 259e33d8cdbSbalrog int32_t comadj; 260e33d8cdbSbalrog 261e33d8cdbSbalrog uint32_t uuid_keyword; 262e33d8cdbSbalrog char uuid[16]; 263e33d8cdbSbalrog 264e33d8cdbSbalrog uint32_t touch_keyword; 265e33d8cdbSbalrog int32_t touch_xp; 266e33d8cdbSbalrog int32_t touch_yp; 267e33d8cdbSbalrog int32_t touch_xd; 268e33d8cdbSbalrog int32_t touch_yd; 269e33d8cdbSbalrog 270e33d8cdbSbalrog uint32_t adadj_keyword; 271e33d8cdbSbalrog int32_t adadj; 272e33d8cdbSbalrog 273e33d8cdbSbalrog uint32_t phad_keyword; 274e33d8cdbSbalrog int32_t phadadj; 275e33d8cdbSbalrog } zaurus_bootparam = { 276e33d8cdbSbalrog .comadj_keyword = MAGIC_CHG('C', 'M', 'A', 'D'), 277e33d8cdbSbalrog .comadj = 125, 278e33d8cdbSbalrog .uuid_keyword = MAGIC_CHG('U', 'U', 'I', 'D'), 279e33d8cdbSbalrog .uuid = { -1 }, 280e33d8cdbSbalrog .touch_keyword = MAGIC_CHG('T', 'U', 'C', 'H'), 281e33d8cdbSbalrog .touch_xp = -1, 282e33d8cdbSbalrog .adadj_keyword = MAGIC_CHG('B', 'V', 'A', 'D'), 283e33d8cdbSbalrog .adadj = -1, 284e33d8cdbSbalrog .phad_keyword = MAGIC_CHG('P', 'H', 'A', 'D'), 285e33d8cdbSbalrog .phadadj = 0x01, 286e33d8cdbSbalrog }; 287e33d8cdbSbalrog 288a8170e5eSAvi Kivity void sl_bootparam_write(hwaddr ptr) 289e33d8cdbSbalrog { 290*e1fe50dcSStefan Weil cpu_physical_memory_write(ptr, &zaurus_bootparam, 291e33d8cdbSbalrog sizeof(struct sl_param_info)); 292e33d8cdbSbalrog } 293