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 27*a009de46SAndreas Färber #define TYPE_SCOOP "scoop" 28*a009de46SAndreas Färber #define SCOOP(obj) OBJECT_CHECK(ScoopInfo, (obj), TYPE_SCOOP) 29*a009de46SAndreas Färber 30383d01c6SDmitry Eremin-Solenikov typedef struct ScoopInfo ScoopInfo; 31bc24a225SPaul Brook struct ScoopInfo { 32*a009de46SAndreas Färber SysBusDevice parent_obj; 33*a009de46SAndreas Färber 34e33d8cdbSbalrog qemu_irq handler[16]; 35e71ceafcSAvi Kivity MemoryRegion iomem; 36e33d8cdbSbalrog uint16_t status; 37e33d8cdbSbalrog uint16_t power; 38e33d8cdbSbalrog uint32_t gpio_level; 39e33d8cdbSbalrog uint32_t gpio_dir; 40e33d8cdbSbalrog uint32_t prev_level; 41e33d8cdbSbalrog 42e33d8cdbSbalrog uint16_t mcr; 43e33d8cdbSbalrog uint16_t cdr; 44e33d8cdbSbalrog uint16_t ccr; 45e33d8cdbSbalrog uint16_t irr; 46e33d8cdbSbalrog uint16_t imr; 47e33d8cdbSbalrog uint16_t isr; 48e33d8cdbSbalrog }; 49e33d8cdbSbalrog 50e33d8cdbSbalrog #define SCOOP_MCR 0x00 51e33d8cdbSbalrog #define SCOOP_CDR 0x04 52e33d8cdbSbalrog #define SCOOP_CSR 0x08 53e33d8cdbSbalrog #define SCOOP_CPR 0x0c 54e33d8cdbSbalrog #define SCOOP_CCR 0x10 55e33d8cdbSbalrog #define SCOOP_IRR_IRM 0x14 56e33d8cdbSbalrog #define SCOOP_IMR 0x18 57e33d8cdbSbalrog #define SCOOP_ISR 0x1c 58e33d8cdbSbalrog #define SCOOP_GPCR 0x20 59e33d8cdbSbalrog #define SCOOP_GPWR 0x24 60e33d8cdbSbalrog #define SCOOP_GPRR 0x28 61e33d8cdbSbalrog 62bc24a225SPaul Brook static inline void scoop_gpio_handler_update(ScoopInfo *s) { 63e33d8cdbSbalrog uint32_t level, diff; 64e33d8cdbSbalrog int bit; 65e33d8cdbSbalrog level = s->gpio_level & s->gpio_dir; 66e33d8cdbSbalrog 67e33d8cdbSbalrog for (diff = s->prev_level ^ level; diff; diff ^= 1 << bit) { 68e33d8cdbSbalrog bit = ffs(diff) - 1; 69e33d8cdbSbalrog qemu_set_irq(s->handler[bit], (level >> bit) & 1); 70e33d8cdbSbalrog } 71e33d8cdbSbalrog 72e33d8cdbSbalrog s->prev_level = level; 73e33d8cdbSbalrog } 74e33d8cdbSbalrog 75a8170e5eSAvi Kivity static uint64_t scoop_read(void *opaque, hwaddr addr, 76e71ceafcSAvi Kivity unsigned size) 77e33d8cdbSbalrog { 78bc24a225SPaul Brook ScoopInfo *s = (ScoopInfo *) opaque; 79e33d8cdbSbalrog 80aa9438d9SDmitry Eremin-Solenikov switch (addr & 0x3f) { 81e33d8cdbSbalrog case SCOOP_MCR: 82e33d8cdbSbalrog return s->mcr; 83e33d8cdbSbalrog case SCOOP_CDR: 84e33d8cdbSbalrog return s->cdr; 85e33d8cdbSbalrog case SCOOP_CSR: 86e33d8cdbSbalrog return s->status; 87e33d8cdbSbalrog case SCOOP_CPR: 88e33d8cdbSbalrog return s->power; 89e33d8cdbSbalrog case SCOOP_CCR: 90e33d8cdbSbalrog return s->ccr; 91e33d8cdbSbalrog case SCOOP_IRR_IRM: 92e33d8cdbSbalrog return s->irr; 93e33d8cdbSbalrog case SCOOP_IMR: 94e33d8cdbSbalrog return s->imr; 95e33d8cdbSbalrog case SCOOP_ISR: 96e33d8cdbSbalrog return s->isr; 97e33d8cdbSbalrog case SCOOP_GPCR: 98e33d8cdbSbalrog return s->gpio_dir; 99e33d8cdbSbalrog case SCOOP_GPWR: 100e33d8cdbSbalrog case SCOOP_GPRR: 1011f163b14Sbalrog return s->gpio_level; 102e33d8cdbSbalrog default: 103a8b7063bSBlue Swirl zaurus_printf("Bad register offset " REG_FMT "\n", (unsigned long)addr); 104e33d8cdbSbalrog } 105e33d8cdbSbalrog 106e33d8cdbSbalrog return 0; 107e33d8cdbSbalrog } 108e33d8cdbSbalrog 109a8170e5eSAvi Kivity static void scoop_write(void *opaque, hwaddr addr, 110e71ceafcSAvi Kivity uint64_t value, unsigned size) 111e33d8cdbSbalrog { 112bc24a225SPaul Brook ScoopInfo *s = (ScoopInfo *) opaque; 113e33d8cdbSbalrog value &= 0xffff; 114e33d8cdbSbalrog 115aa9438d9SDmitry Eremin-Solenikov switch (addr & 0x3f) { 116e33d8cdbSbalrog case SCOOP_MCR: 117e33d8cdbSbalrog s->mcr = value; 118e33d8cdbSbalrog break; 119e33d8cdbSbalrog case SCOOP_CDR: 120e33d8cdbSbalrog s->cdr = value; 121e33d8cdbSbalrog break; 122e33d8cdbSbalrog case SCOOP_CPR: 123e33d8cdbSbalrog s->power = value; 124e33d8cdbSbalrog if (value & 0x80) 125e33d8cdbSbalrog s->power |= 0x8040; 126e33d8cdbSbalrog break; 127e33d8cdbSbalrog case SCOOP_CCR: 128e33d8cdbSbalrog s->ccr = value; 129e33d8cdbSbalrog break; 130e33d8cdbSbalrog case SCOOP_IRR_IRM: 131e33d8cdbSbalrog s->irr = value; 132e33d8cdbSbalrog break; 133e33d8cdbSbalrog case SCOOP_IMR: 134e33d8cdbSbalrog s->imr = value; 135e33d8cdbSbalrog break; 136e33d8cdbSbalrog case SCOOP_ISR: 137e33d8cdbSbalrog s->isr = value; 138e33d8cdbSbalrog break; 139e33d8cdbSbalrog case SCOOP_GPCR: 140e33d8cdbSbalrog s->gpio_dir = value; 141e33d8cdbSbalrog scoop_gpio_handler_update(s); 142e33d8cdbSbalrog break; 143e33d8cdbSbalrog case SCOOP_GPWR: 1441f163b14Sbalrog case SCOOP_GPRR: /* GPRR is probably R/O in real HW */ 145e33d8cdbSbalrog s->gpio_level = value & s->gpio_dir; 146e33d8cdbSbalrog scoop_gpio_handler_update(s); 147e33d8cdbSbalrog break; 148e33d8cdbSbalrog default: 149a8b7063bSBlue Swirl zaurus_printf("Bad register offset " REG_FMT "\n", (unsigned long)addr); 150e33d8cdbSbalrog } 151e33d8cdbSbalrog } 152e33d8cdbSbalrog 153e71ceafcSAvi Kivity static const MemoryRegionOps scoop_ops = { 154e71ceafcSAvi Kivity .read = scoop_read, 155e71ceafcSAvi Kivity .write = scoop_write, 156e71ceafcSAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 157e33d8cdbSbalrog }; 158e33d8cdbSbalrog 159383d01c6SDmitry Eremin-Solenikov static void scoop_gpio_set(void *opaque, int line, int level) 160e33d8cdbSbalrog { 1618d30b794STorsten Duwe ScoopInfo *s = (ScoopInfo *) opaque; 162e33d8cdbSbalrog 163e33d8cdbSbalrog if (level) 164e33d8cdbSbalrog s->gpio_level |= (1 << line); 165e33d8cdbSbalrog else 166e33d8cdbSbalrog s->gpio_level &= ~(1 << line); 167e33d8cdbSbalrog } 168e33d8cdbSbalrog 169*a009de46SAndreas Färber static int scoop_init(SysBusDevice *sbd) 170e33d8cdbSbalrog { 171*a009de46SAndreas Färber DeviceState *dev = DEVICE(sbd); 172*a009de46SAndreas Färber ScoopInfo *s = SCOOP(dev); 173e33d8cdbSbalrog 174383d01c6SDmitry Eremin-Solenikov s->status = 0x02; 175*a009de46SAndreas Färber qdev_init_gpio_out(dev, s->handler, 16); 176*a009de46SAndreas Färber qdev_init_gpio_in(dev, scoop_gpio_set, 16); 177b7163687SPaolo Bonzini memory_region_init_io(&s->iomem, OBJECT(s), &scoop_ops, s, "scoop", 0x1000); 178e33d8cdbSbalrog 179*a009de46SAndreas Färber sysbus_init_mmio(sbd, &s->iomem); 180e33d8cdbSbalrog 181e33d8cdbSbalrog return 0; 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 206383d01c6SDmitry Eremin-Solenikov static const VMStateDescription vmstate_scoop_regs = { 207383d01c6SDmitry Eremin-Solenikov .name = "scoop", 208383d01c6SDmitry Eremin-Solenikov .version_id = 1, 209383d01c6SDmitry Eremin-Solenikov .minimum_version_id = 0, 210383d01c6SDmitry Eremin-Solenikov .minimum_version_id_old = 0, 2117fe63a17SDmitry Eremin-Solenikov .post_load = scoop_post_load, 212383d01c6SDmitry Eremin-Solenikov .fields = (VMStateField []) { 213383d01c6SDmitry Eremin-Solenikov VMSTATE_UINT16(status, ScoopInfo), 214383d01c6SDmitry Eremin-Solenikov VMSTATE_UINT16(power, ScoopInfo), 215383d01c6SDmitry Eremin-Solenikov VMSTATE_UINT32(gpio_level, ScoopInfo), 216383d01c6SDmitry Eremin-Solenikov VMSTATE_UINT32(gpio_dir, ScoopInfo), 217383d01c6SDmitry Eremin-Solenikov VMSTATE_UINT32(prev_level, ScoopInfo), 218383d01c6SDmitry Eremin-Solenikov VMSTATE_UINT16(mcr, ScoopInfo), 219383d01c6SDmitry Eremin-Solenikov VMSTATE_UINT16(cdr, ScoopInfo), 220383d01c6SDmitry Eremin-Solenikov VMSTATE_UINT16(ccr, ScoopInfo), 221383d01c6SDmitry Eremin-Solenikov VMSTATE_UINT16(irr, ScoopInfo), 222383d01c6SDmitry Eremin-Solenikov VMSTATE_UINT16(imr, ScoopInfo), 223383d01c6SDmitry Eremin-Solenikov VMSTATE_UINT16(isr, ScoopInfo), 224383d01c6SDmitry Eremin-Solenikov VMSTATE_UNUSED_TEST(is_version_0, 2), 225383d01c6SDmitry Eremin-Solenikov VMSTATE_END_OF_LIST(), 226383d01c6SDmitry Eremin-Solenikov }, 227383d01c6SDmitry Eremin-Solenikov }; 228383d01c6SDmitry Eremin-Solenikov 229999e12bbSAnthony Liguori static Property scoop_sysbus_properties[] = { 230383d01c6SDmitry Eremin-Solenikov DEFINE_PROP_END_OF_LIST(), 231999e12bbSAnthony Liguori }; 232999e12bbSAnthony Liguori 233999e12bbSAnthony Liguori static void scoop_sysbus_class_init(ObjectClass *klass, void *data) 234999e12bbSAnthony Liguori { 23539bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 236999e12bbSAnthony Liguori SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); 237999e12bbSAnthony Liguori 238999e12bbSAnthony Liguori k->init = scoop_init; 23939bffca2SAnthony Liguori dc->desc = "Scoop2 Sharp custom ASIC"; 24039bffca2SAnthony Liguori dc->vmsd = &vmstate_scoop_regs; 24139bffca2SAnthony Liguori dc->props = scoop_sysbus_properties; 242383d01c6SDmitry Eremin-Solenikov } 243999e12bbSAnthony Liguori 2448c43a6f0SAndreas Färber static const TypeInfo scoop_sysbus_info = { 245*a009de46SAndreas Färber .name = TYPE_SCOOP, 24639bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 24739bffca2SAnthony Liguori .instance_size = sizeof(ScoopInfo), 248999e12bbSAnthony Liguori .class_init = scoop_sysbus_class_init, 249383d01c6SDmitry Eremin-Solenikov }; 250383d01c6SDmitry Eremin-Solenikov 25183f7d43aSAndreas Färber static void scoop_register_types(void) 252383d01c6SDmitry Eremin-Solenikov { 25339bffca2SAnthony Liguori type_register_static(&scoop_sysbus_info); 254383d01c6SDmitry Eremin-Solenikov } 25583f7d43aSAndreas Färber 25683f7d43aSAndreas Färber type_init(scoop_register_types) 257383d01c6SDmitry Eremin-Solenikov 258e33d8cdbSbalrog /* Write the bootloader parameters memory area. */ 259e33d8cdbSbalrog 260e33d8cdbSbalrog #define MAGIC_CHG(a, b, c, d) ((d << 24) | (c << 16) | (b << 8) | a) 261e33d8cdbSbalrog 262541dc0d4SStefan Weil static struct QEMU_PACKED sl_param_info { 263e33d8cdbSbalrog uint32_t comadj_keyword; 264e33d8cdbSbalrog int32_t comadj; 265e33d8cdbSbalrog 266e33d8cdbSbalrog uint32_t uuid_keyword; 267e33d8cdbSbalrog char uuid[16]; 268e33d8cdbSbalrog 269e33d8cdbSbalrog uint32_t touch_keyword; 270e33d8cdbSbalrog int32_t touch_xp; 271e33d8cdbSbalrog int32_t touch_yp; 272e33d8cdbSbalrog int32_t touch_xd; 273e33d8cdbSbalrog int32_t touch_yd; 274e33d8cdbSbalrog 275e33d8cdbSbalrog uint32_t adadj_keyword; 276e33d8cdbSbalrog int32_t adadj; 277e33d8cdbSbalrog 278e33d8cdbSbalrog uint32_t phad_keyword; 279e33d8cdbSbalrog int32_t phadadj; 280e33d8cdbSbalrog } zaurus_bootparam = { 281e33d8cdbSbalrog .comadj_keyword = MAGIC_CHG('C', 'M', 'A', 'D'), 282e33d8cdbSbalrog .comadj = 125, 283e33d8cdbSbalrog .uuid_keyword = MAGIC_CHG('U', 'U', 'I', 'D'), 284e33d8cdbSbalrog .uuid = { -1 }, 285e33d8cdbSbalrog .touch_keyword = MAGIC_CHG('T', 'U', 'C', 'H'), 286e33d8cdbSbalrog .touch_xp = -1, 287e33d8cdbSbalrog .adadj_keyword = MAGIC_CHG('B', 'V', 'A', 'D'), 288e33d8cdbSbalrog .adadj = -1, 289e33d8cdbSbalrog .phad_keyword = MAGIC_CHG('P', 'H', 'A', 'D'), 290e33d8cdbSbalrog .phadadj = 0x01, 291e33d8cdbSbalrog }; 292e33d8cdbSbalrog 293a8170e5eSAvi Kivity void sl_bootparam_write(hwaddr ptr) 294e33d8cdbSbalrog { 295e1fe50dcSStefan Weil cpu_physical_memory_write(ptr, &zaurus_bootparam, 296e33d8cdbSbalrog sizeof(struct sl_param_info)); 297e33d8cdbSbalrog } 298