15193899aSPaolo Bonzini /*
25193899aSPaolo Bonzini * Copyright (c) 2006-2008 Openedhand Ltd.
35193899aSPaolo Bonzini * Written by Andrzej Zaborowski <balrog@zabor.org>
45193899aSPaolo Bonzini *
55193899aSPaolo Bonzini * This program is free software; you can redistribute it and/or
65193899aSPaolo Bonzini * modify it under the terms of the GNU General Public License as
75193899aSPaolo Bonzini * published by the Free Software Foundation; either version 2 or
85193899aSPaolo Bonzini * (at your option) version 3 of the License.
95193899aSPaolo Bonzini *
105193899aSPaolo Bonzini * This program is distributed in the hope that it will be useful,
115193899aSPaolo Bonzini * but WITHOUT ANY WARRANTY; without even the implied warranty of
125193899aSPaolo Bonzini * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
135193899aSPaolo Bonzini * GNU General Public License for more details.
145193899aSPaolo Bonzini *
155193899aSPaolo Bonzini * You should have received a copy of the GNU General Public License along
165193899aSPaolo Bonzini * with this program; if not, see <http://www.gnu.org/licenses/>.
175193899aSPaolo Bonzini */
180b8fa32fSMarkus Armbruster
1917b7f2dbSPeter Maydell #include "qemu/osdep.h"
2064552b6bSMarkus Armbruster #include "hw/irq.h"
215193899aSPaolo Bonzini #include "hw/arm/sharpsl.h"
225193899aSPaolo 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"
275193899aSPaolo Bonzini
285193899aSPaolo Bonzini /* SCOOP devices */
295193899aSPaolo Bonzini
30a009de46SAndreas Färber #define TYPE_SCOOP "scoop"
318063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(ScoopInfo, SCOOP)
32a009de46SAndreas Färber
335193899aSPaolo Bonzini struct ScoopInfo {
34a009de46SAndreas Färber SysBusDevice parent_obj;
35a009de46SAndreas Färber
365193899aSPaolo Bonzini qemu_irq handler[16];
375193899aSPaolo Bonzini MemoryRegion iomem;
385193899aSPaolo Bonzini uint16_t status;
395193899aSPaolo Bonzini uint16_t power;
405193899aSPaolo Bonzini uint32_t gpio_level;
415193899aSPaolo Bonzini uint32_t gpio_dir;
425193899aSPaolo Bonzini uint32_t prev_level;
435193899aSPaolo Bonzini
445193899aSPaolo Bonzini uint16_t mcr;
455193899aSPaolo Bonzini uint16_t cdr;
465193899aSPaolo Bonzini uint16_t ccr;
475193899aSPaolo Bonzini uint16_t irr;
485193899aSPaolo Bonzini uint16_t imr;
495193899aSPaolo Bonzini uint16_t isr;
505193899aSPaolo Bonzini };
515193899aSPaolo Bonzini
525193899aSPaolo Bonzini #define SCOOP_MCR 0x00
535193899aSPaolo Bonzini #define SCOOP_CDR 0x04
545193899aSPaolo Bonzini #define SCOOP_CSR 0x08
555193899aSPaolo Bonzini #define SCOOP_CPR 0x0c
565193899aSPaolo Bonzini #define SCOOP_CCR 0x10
575193899aSPaolo Bonzini #define SCOOP_IRR_IRM 0x14
585193899aSPaolo Bonzini #define SCOOP_IMR 0x18
595193899aSPaolo Bonzini #define SCOOP_ISR 0x1c
605193899aSPaolo Bonzini #define SCOOP_GPCR 0x20
615193899aSPaolo Bonzini #define SCOOP_GPWR 0x24
625193899aSPaolo Bonzini #define SCOOP_GPRR 0x28
635193899aSPaolo Bonzini
scoop_gpio_handler_update(ScoopInfo * s)64*d0a040a8STanmay Patil static inline void scoop_gpio_handler_update(ScoopInfo *s)
65*d0a040a8STanmay Patil {
665193899aSPaolo Bonzini uint32_t level, diff;
675193899aSPaolo Bonzini int bit;
685193899aSPaolo Bonzini level = s->gpio_level & s->gpio_dir;
695193899aSPaolo Bonzini
705193899aSPaolo Bonzini for (diff = s->prev_level ^ level; diff; diff ^= 1 << bit) {
71786a4ea8SStefan Hajnoczi bit = ctz32(diff);
725193899aSPaolo Bonzini qemu_set_irq(s->handler[bit], (level >> bit) & 1);
735193899aSPaolo Bonzini }
745193899aSPaolo Bonzini
755193899aSPaolo Bonzini s->prev_level = level;
765193899aSPaolo Bonzini }
775193899aSPaolo Bonzini
scoop_read(void * opaque,hwaddr addr,unsigned size)785193899aSPaolo Bonzini static uint64_t scoop_read(void *opaque, hwaddr addr,
795193899aSPaolo Bonzini unsigned size)
805193899aSPaolo Bonzini {
815193899aSPaolo Bonzini ScoopInfo *s = (ScoopInfo *) opaque;
825193899aSPaolo Bonzini
835193899aSPaolo Bonzini switch (addr & 0x3f) {
845193899aSPaolo Bonzini case SCOOP_MCR:
855193899aSPaolo Bonzini return s->mcr;
865193899aSPaolo Bonzini case SCOOP_CDR:
875193899aSPaolo Bonzini return s->cdr;
885193899aSPaolo Bonzini case SCOOP_CSR:
895193899aSPaolo Bonzini return s->status;
905193899aSPaolo Bonzini case SCOOP_CPR:
915193899aSPaolo Bonzini return s->power;
925193899aSPaolo Bonzini case SCOOP_CCR:
935193899aSPaolo Bonzini return s->ccr;
945193899aSPaolo Bonzini case SCOOP_IRR_IRM:
955193899aSPaolo Bonzini return s->irr;
965193899aSPaolo Bonzini case SCOOP_IMR:
975193899aSPaolo Bonzini return s->imr;
985193899aSPaolo Bonzini case SCOOP_ISR:
995193899aSPaolo Bonzini return s->isr;
1005193899aSPaolo Bonzini case SCOOP_GPCR:
1015193899aSPaolo Bonzini return s->gpio_dir;
1025193899aSPaolo Bonzini case SCOOP_GPWR:
1035193899aSPaolo Bonzini case SCOOP_GPRR:
1045193899aSPaolo Bonzini return s->gpio_level;
1055193899aSPaolo Bonzini default:
106a0a8cf78SPeter Maydell qemu_log_mask(LOG_GUEST_ERROR,
107a0a8cf78SPeter Maydell "scoop_read: bad register offset 0x%02" HWADDR_PRIx "\n",
108a0a8cf78SPeter Maydell addr);
1095193899aSPaolo Bonzini }
1105193899aSPaolo Bonzini
1115193899aSPaolo Bonzini return 0;
1125193899aSPaolo Bonzini }
1135193899aSPaolo Bonzini
scoop_write(void * opaque,hwaddr addr,uint64_t value,unsigned size)1145193899aSPaolo Bonzini static void scoop_write(void *opaque, hwaddr addr,
1155193899aSPaolo Bonzini uint64_t value, unsigned size)
1165193899aSPaolo Bonzini {
1175193899aSPaolo Bonzini ScoopInfo *s = (ScoopInfo *) opaque;
1185193899aSPaolo Bonzini value &= 0xffff;
1195193899aSPaolo Bonzini
1205193899aSPaolo Bonzini switch (addr & 0x3f) {
1215193899aSPaolo Bonzini case SCOOP_MCR:
1225193899aSPaolo Bonzini s->mcr = value;
1235193899aSPaolo Bonzini break;
1245193899aSPaolo Bonzini case SCOOP_CDR:
1255193899aSPaolo Bonzini s->cdr = value;
1265193899aSPaolo Bonzini break;
1275193899aSPaolo Bonzini case SCOOP_CPR:
1285193899aSPaolo Bonzini s->power = value;
129*d0a040a8STanmay Patil if (value & 0x80) {
1305193899aSPaolo Bonzini s->power |= 0x8040;
131*d0a040a8STanmay Patil }
1325193899aSPaolo Bonzini break;
1335193899aSPaolo Bonzini case SCOOP_CCR:
1345193899aSPaolo Bonzini s->ccr = value;
1355193899aSPaolo Bonzini break;
1365193899aSPaolo Bonzini case SCOOP_IRR_IRM:
1375193899aSPaolo Bonzini s->irr = value;
1385193899aSPaolo Bonzini break;
1395193899aSPaolo Bonzini case SCOOP_IMR:
1405193899aSPaolo Bonzini s->imr = value;
1415193899aSPaolo Bonzini break;
1425193899aSPaolo Bonzini case SCOOP_ISR:
1435193899aSPaolo Bonzini s->isr = value;
1445193899aSPaolo Bonzini break;
1455193899aSPaolo Bonzini case SCOOP_GPCR:
1465193899aSPaolo Bonzini s->gpio_dir = value;
1475193899aSPaolo Bonzini scoop_gpio_handler_update(s);
1485193899aSPaolo Bonzini break;
1495193899aSPaolo Bonzini case SCOOP_GPWR:
1505193899aSPaolo Bonzini case SCOOP_GPRR: /* GPRR is probably R/O in real HW */
1515193899aSPaolo Bonzini s->gpio_level = value & s->gpio_dir;
1525193899aSPaolo Bonzini scoop_gpio_handler_update(s);
1535193899aSPaolo Bonzini break;
1545193899aSPaolo Bonzini default:
155a0a8cf78SPeter Maydell qemu_log_mask(LOG_GUEST_ERROR,
156a0a8cf78SPeter Maydell "scoop_write: bad register offset 0x%02" HWADDR_PRIx "\n",
157a0a8cf78SPeter Maydell addr);
1585193899aSPaolo Bonzini }
1595193899aSPaolo Bonzini }
1605193899aSPaolo Bonzini
1615193899aSPaolo Bonzini static const MemoryRegionOps scoop_ops = {
1625193899aSPaolo Bonzini .read = scoop_read,
1635193899aSPaolo Bonzini .write = scoop_write,
1645193899aSPaolo Bonzini .endianness = DEVICE_NATIVE_ENDIAN,
1655193899aSPaolo Bonzini };
1665193899aSPaolo Bonzini
scoop_gpio_set(void * opaque,int line,int level)1675193899aSPaolo Bonzini static void scoop_gpio_set(void *opaque, int line, int level)
1685193899aSPaolo Bonzini {
1695193899aSPaolo Bonzini ScoopInfo *s = (ScoopInfo *) opaque;
1705193899aSPaolo Bonzini
171*d0a040a8STanmay Patil if (level) {
1725193899aSPaolo Bonzini s->gpio_level |= (1 << line);
173*d0a040a8STanmay Patil } else {
1745193899aSPaolo Bonzini s->gpio_level &= ~(1 << line);
1755193899aSPaolo Bonzini }
176*d0a040a8STanmay Patil }
1775193899aSPaolo Bonzini
scoop_init(Object * obj)17853677667Sxiaoqiang zhao static void scoop_init(Object *obj)
1795193899aSPaolo Bonzini {
18053677667Sxiaoqiang zhao DeviceState *dev = DEVICE(obj);
18153677667Sxiaoqiang zhao ScoopInfo *s = SCOOP(obj);
18253677667Sxiaoqiang zhao SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
1835193899aSPaolo Bonzini
1845193899aSPaolo Bonzini 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);
1885193899aSPaolo Bonzini
189a009de46SAndreas Färber sysbus_init_mmio(sbd, &s->iomem);
1905193899aSPaolo Bonzini }
1915193899aSPaolo Bonzini
scoop_post_load(void * opaque,int version_id)1925193899aSPaolo Bonzini static int scoop_post_load(void *opaque, int version_id)
1935193899aSPaolo Bonzini {
1945193899aSPaolo Bonzini ScoopInfo *s = (ScoopInfo *) opaque;
1955193899aSPaolo Bonzini int i;
1965193899aSPaolo Bonzini uint32_t level;
1975193899aSPaolo Bonzini
1985193899aSPaolo Bonzini level = s->gpio_level & s->gpio_dir;
1995193899aSPaolo Bonzini
2005193899aSPaolo Bonzini for (i = 0; i < 16; i++) {
2015193899aSPaolo Bonzini qemu_set_irq(s->handler[i], (level >> i) & 1);
2025193899aSPaolo Bonzini }
2035193899aSPaolo Bonzini
2045193899aSPaolo Bonzini s->prev_level = level;
2055193899aSPaolo Bonzini
2065193899aSPaolo Bonzini return 0;
2075193899aSPaolo Bonzini }
2085193899aSPaolo Bonzini
is_version_0(void * opaque,int version_id)2095193899aSPaolo Bonzini static bool is_version_0(void *opaque, int version_id)
2105193899aSPaolo Bonzini {
2115193899aSPaolo Bonzini return version_id == 0;
2125193899aSPaolo Bonzini }
2135193899aSPaolo Bonzini
vmstate_scoop_validate(void * opaque,int version_id)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
2235193899aSPaolo Bonzini static const VMStateDescription vmstate_scoop_regs = {
2245193899aSPaolo Bonzini .name = "scoop",
2255193899aSPaolo Bonzini .version_id = 1,
2265193899aSPaolo Bonzini .minimum_version_id = 0,
2275193899aSPaolo Bonzini .post_load = scoop_post_load,
2283b9e779bSRichard Henderson .fields = (const VMStateField[]) {
2295193899aSPaolo Bonzini VMSTATE_UINT16(status, ScoopInfo),
2305193899aSPaolo Bonzini VMSTATE_UINT16(power, ScoopInfo),
2315193899aSPaolo Bonzini VMSTATE_UINT32(gpio_level, ScoopInfo),
2325193899aSPaolo Bonzini VMSTATE_UINT32(gpio_dir, ScoopInfo),
2335193899aSPaolo Bonzini VMSTATE_UINT32(prev_level, ScoopInfo),
23452f91c37SMichael S. Tsirkin VMSTATE_VALIDATE("irq levels are 16 bit", vmstate_scoop_validate),
2355193899aSPaolo Bonzini VMSTATE_UINT16(mcr, ScoopInfo),
2365193899aSPaolo Bonzini VMSTATE_UINT16(cdr, ScoopInfo),
2375193899aSPaolo Bonzini VMSTATE_UINT16(ccr, ScoopInfo),
2385193899aSPaolo Bonzini VMSTATE_UINT16(irr, ScoopInfo),
2395193899aSPaolo Bonzini VMSTATE_UINT16(imr, ScoopInfo),
2405193899aSPaolo Bonzini VMSTATE_UINT16(isr, ScoopInfo),
2415193899aSPaolo Bonzini VMSTATE_UNUSED_TEST(is_version_0, 2),
2425193899aSPaolo Bonzini VMSTATE_END_OF_LIST(),
2435193899aSPaolo Bonzini },
2445193899aSPaolo Bonzini };
2455193899aSPaolo Bonzini
scoop_sysbus_class_init(ObjectClass * klass,void * data)2465193899aSPaolo Bonzini static void scoop_sysbus_class_init(ObjectClass *klass, void *data)
2475193899aSPaolo Bonzini {
2485193899aSPaolo Bonzini DeviceClass *dc = DEVICE_CLASS(klass);
2495193899aSPaolo Bonzini
2505193899aSPaolo Bonzini dc->desc = "Scoop2 Sharp custom ASIC";
2515193899aSPaolo Bonzini dc->vmsd = &vmstate_scoop_regs;
2525193899aSPaolo Bonzini }
2535193899aSPaolo Bonzini
2545193899aSPaolo Bonzini static const TypeInfo scoop_sysbus_info = {
255a009de46SAndreas Färber .name = TYPE_SCOOP,
2565193899aSPaolo Bonzini .parent = TYPE_SYS_BUS_DEVICE,
2575193899aSPaolo Bonzini .instance_size = sizeof(ScoopInfo),
25853677667Sxiaoqiang zhao .instance_init = scoop_init,
2595193899aSPaolo Bonzini .class_init = scoop_sysbus_class_init,
2605193899aSPaolo Bonzini };
2615193899aSPaolo Bonzini
scoop_register_types(void)2625193899aSPaolo Bonzini static void scoop_register_types(void)
2635193899aSPaolo Bonzini {
2645193899aSPaolo Bonzini type_register_static(&scoop_sysbus_info);
2655193899aSPaolo Bonzini }
2665193899aSPaolo Bonzini
2675193899aSPaolo Bonzini type_init(scoop_register_types)
2685193899aSPaolo Bonzini
2695193899aSPaolo Bonzini /* Write the bootloader parameters memory area. */
2705193899aSPaolo Bonzini
2715193899aSPaolo Bonzini #define MAGIC_CHG(a, b, c, d) ((d << 24) | (c << 16) | (b << 8) | a)
2725193899aSPaolo Bonzini
2735193899aSPaolo Bonzini static struct QEMU_PACKED sl_param_info {
2745193899aSPaolo Bonzini uint32_t comadj_keyword;
2755193899aSPaolo Bonzini int32_t comadj;
2765193899aSPaolo Bonzini
2775193899aSPaolo Bonzini uint32_t uuid_keyword;
2785193899aSPaolo Bonzini char uuid[16];
2795193899aSPaolo Bonzini
2805193899aSPaolo Bonzini uint32_t touch_keyword;
2815193899aSPaolo Bonzini int32_t touch_xp;
2825193899aSPaolo Bonzini int32_t touch_yp;
2835193899aSPaolo Bonzini int32_t touch_xd;
2845193899aSPaolo Bonzini int32_t touch_yd;
2855193899aSPaolo Bonzini
2865193899aSPaolo Bonzini uint32_t adadj_keyword;
2875193899aSPaolo Bonzini int32_t adadj;
2885193899aSPaolo Bonzini
2895193899aSPaolo Bonzini uint32_t phad_keyword;
2905193899aSPaolo Bonzini int32_t phadadj;
2915193899aSPaolo Bonzini } zaurus_bootparam = {
2925193899aSPaolo Bonzini .comadj_keyword = MAGIC_CHG('C', 'M', 'A', 'D'),
2935193899aSPaolo Bonzini .comadj = 125,
2945193899aSPaolo Bonzini .uuid_keyword = MAGIC_CHG('U', 'U', 'I', 'D'),
2955193899aSPaolo Bonzini .uuid = { -1 },
2965193899aSPaolo Bonzini .touch_keyword = MAGIC_CHG('T', 'U', 'C', 'H'),
2975193899aSPaolo Bonzini .touch_xp = -1,
2985193899aSPaolo Bonzini .adadj_keyword = MAGIC_CHG('B', 'V', 'A', 'D'),
2995193899aSPaolo Bonzini .adadj = -1,
3005193899aSPaolo Bonzini .phad_keyword = MAGIC_CHG('P', 'H', 'A', 'D'),
3015193899aSPaolo Bonzini .phadadj = 0x01,
3025193899aSPaolo Bonzini };
3035193899aSPaolo Bonzini
sl_bootparam_write(hwaddr ptr)3045193899aSPaolo Bonzini void sl_bootparam_write(hwaddr ptr)
3055193899aSPaolo Bonzini {
306e1fe50dcSStefan Weil cpu_physical_memory_write(ptr, &zaurus_bootparam,
3075193899aSPaolo Bonzini sizeof(struct sl_param_info));
3085193899aSPaolo Bonzini }
309