pxa2xx.c 7.3 KB
Newer Older
1 2 3 4 5 6 7
/*
 * Intel XScale PXA255/270 PC Card and CompactFlash Interface.
 *
 * Copyright (c) 2006 Openedhand Ltd.
 * Written by Andrzej Zaborowski <balrog@zabor.org>
 *
 * This code is licensed under the GPLv2.
8 9 10
 *
 * Contributions after 2012-01-13 are licensed under the terms of the
 * GNU GPL, version 2 or (at your option) any later version.
11 12
 */

13
#include "hw/hw.h"
14
#include "hw/sysbus.h"
15
#include "hw/pcmcia.h"
P
Paolo Bonzini 已提交
16
#include "hw/arm/pxa.h"
17

18 19 20
#define TYPE_PXA2XX_PCMCIA "pxa2xx-pcmcia"
#define PXA2XX_PCMCIA(obj) \
    OBJECT_CHECK(PXA2xxPCMCIAState, obj, TYPE_PXA2XX_PCMCIA)
21

P
Paul Brook 已提交
22
struct PXA2xxPCMCIAState {
23 24
    SysBusDevice parent_obj;

P
Paul Brook 已提交
25
    PCMCIASocket slot;
26
    MemoryRegion container_mem;
27
    MemoryRegion common_iomem;
28
    MemoryRegion attr_iomem;
29
    MemoryRegion iomem;
30 31 32

    qemu_irq irq;
    qemu_irq cd_irq;
33 34

    PCMCIACardState *card;
35 36
};

37
static uint64_t pxa2xx_pcmcia_common_read(void *opaque,
A
Avi Kivity 已提交
38
                hwaddr offset, unsigned size)
39
{
P
Paul Brook 已提交
40
    PXA2xxPCMCIAState *s = (PXA2xxPCMCIAState *) opaque;
41
    PCMCIACardClass *pcc;
42 43

    if (s->slot.attached) {
44 45
        pcc = PCMCIA_CARD_GET_CLASS(s->card);
        return pcc->common_read(s->card, offset);
46 47 48 49 50
    }

    return 0;
}

A
Avi Kivity 已提交
51
static void pxa2xx_pcmcia_common_write(void *opaque, hwaddr offset,
52
                                       uint64_t value, unsigned size)
53
{
P
Paul Brook 已提交
54
    PXA2xxPCMCIAState *s = (PXA2xxPCMCIAState *) opaque;
55
    PCMCIACardClass *pcc;
56 57

    if (s->slot.attached) {
58 59
        pcc = PCMCIA_CARD_GET_CLASS(s->card);
        pcc->common_write(s->card, offset, value);
60 61 62
    }
}

63
static uint64_t pxa2xx_pcmcia_attr_read(void *opaque,
A
Avi Kivity 已提交
64
                hwaddr offset, unsigned size)
65
{
P
Paul Brook 已提交
66
    PXA2xxPCMCIAState *s = (PXA2xxPCMCIAState *) opaque;
67
    PCMCIACardClass *pcc;
68 69

    if (s->slot.attached) {
70 71
        pcc = PCMCIA_CARD_GET_CLASS(s->card);
        return pcc->attr_read(s->card, offset);
72 73 74 75 76
    }

    return 0;
}

A
Avi Kivity 已提交
77
static void pxa2xx_pcmcia_attr_write(void *opaque, hwaddr offset,
78
                                     uint64_t value, unsigned size)
79
{
P
Paul Brook 已提交
80
    PXA2xxPCMCIAState *s = (PXA2xxPCMCIAState *) opaque;
81
    PCMCIACardClass *pcc;
82 83

    if (s->slot.attached) {
84 85
        pcc = PCMCIA_CARD_GET_CLASS(s->card);
        pcc->attr_write(s->card, offset, value);
86 87 88
    }
}

89
static uint64_t pxa2xx_pcmcia_io_read(void *opaque,
A
Avi Kivity 已提交
90
                hwaddr offset, unsigned size)
91
{
P
Paul Brook 已提交
92
    PXA2xxPCMCIAState *s = (PXA2xxPCMCIAState *) opaque;
93
    PCMCIACardClass *pcc;
94 95

    if (s->slot.attached) {
96 97
        pcc = PCMCIA_CARD_GET_CLASS(s->card);
        return pcc->io_read(s->card, offset);
98 99 100 101 102
    }

    return 0;
}

A
Avi Kivity 已提交
103
static void pxa2xx_pcmcia_io_write(void *opaque, hwaddr offset,
104
                                   uint64_t value, unsigned size)
105
{
P
Paul Brook 已提交
106
    PXA2xxPCMCIAState *s = (PXA2xxPCMCIAState *) opaque;
107
    PCMCIACardClass *pcc;
108 109

    if (s->slot.attached) {
110 111
        pcc = PCMCIA_CARD_GET_CLASS(s->card);
        pcc->io_write(s->card, offset, value);
112 113 114
    }
}

115 116 117 118
static const MemoryRegionOps pxa2xx_pcmcia_common_ops = {
    .read = pxa2xx_pcmcia_common_read,
    .write = pxa2xx_pcmcia_common_write,
    .endianness = DEVICE_NATIVE_ENDIAN
119 120
};

121 122 123 124
static const MemoryRegionOps pxa2xx_pcmcia_attr_ops = {
    .read = pxa2xx_pcmcia_attr_read,
    .write = pxa2xx_pcmcia_attr_write,
    .endianness = DEVICE_NATIVE_ENDIAN
125 126
};

127 128 129 130
static const MemoryRegionOps pxa2xx_pcmcia_io_ops = {
    .read = pxa2xx_pcmcia_io_read,
    .write = pxa2xx_pcmcia_io_write,
    .endianness = DEVICE_NATIVE_ENDIAN
131 132 133 134
};

static void pxa2xx_pcmcia_set_irq(void *opaque, int line, int level)
{
P
Paul Brook 已提交
135
    PXA2xxPCMCIAState *s = (PXA2xxPCMCIAState *) opaque;
136 137 138 139 140 141
    if (!s->irq)
        return;

    qemu_set_irq(s->irq, level);
}

142
PXA2xxPCMCIAState *pxa2xx_pcmcia_init(MemoryRegion *sysmem,
A
Avi Kivity 已提交
143
                                      hwaddr base)
144
{
145
    DeviceState *dev;
P
Paul Brook 已提交
146
    PXA2xxPCMCIAState *s;
147

148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
    dev = qdev_create(NULL, TYPE_PXA2XX_PCMCIA);
    sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base);
    s = PXA2XX_PCMCIA(dev);

    if (base == 0x30000000) {
        s->slot.slot_string = "PXA PC Card Socket 1";
    } else {
        s->slot.slot_string = "PXA PC Card Socket 0";
    }

    qdev_init_nofail(dev);

    return s;
}

static void pxa2xx_pcmcia_realize(DeviceState *dev, Error **errp)
{
    PXA2xxPCMCIAState *s = PXA2XX_PCMCIA(dev);

    pcmcia_socket_register(&s->slot);
}

static void pxa2xx_pcmcia_initfn(Object *obj)
{
    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
    PXA2xxPCMCIAState *s = PXA2XX_PCMCIA(obj);

    memory_region_init(&s->container_mem, obj, "container", 0x10000000);
    sysbus_init_mmio(sbd, &s->container_mem);
177 178

    /* Socket I/O Memory Space */
179
    memory_region_init_io(&s->iomem, NULL, &pxa2xx_pcmcia_io_ops, s,
180
                          "pxa2xx-pcmcia-io", 0x04000000);
181
    memory_region_add_subregion(&s->container_mem, 0x00000000,
182
                                &s->iomem);
183 184 185 186

    /* Then next 64 MB is reserved */

    /* Socket Attribute Memory Space */
187
    memory_region_init_io(&s->attr_iomem, NULL, &pxa2xx_pcmcia_attr_ops, s,
188
                          "pxa2xx-pcmcia-attribute", 0x04000000);
189
    memory_region_add_subregion(&s->container_mem, 0x08000000,
190
                                &s->attr_iomem);
191 192

    /* Socket Common Memory Space */
193
    memory_region_init_io(&s->common_iomem, NULL, &pxa2xx_pcmcia_common_ops, s,
194
                          "pxa2xx-pcmcia-common", 0x04000000);
195
    memory_region_add_subregion(&s->container_mem, 0x0c000000,
196
                                &s->common_iomem);
197 198

    s->slot.irq = qemu_allocate_irqs(pxa2xx_pcmcia_set_irq, s, 1)[0];
199

200
    object_property_add_link(obj, "card", TYPE_PCMCIA_CARD,
201 202 203
                             (Object **)&s->card,
                             NULL, /* read-only property */
                             0, NULL);
204 205 206
}

/* Insert a new card into a slot */
P
Paul Brook 已提交
207
int pxa2xx_pcmcia_attach(void *opaque, PCMCIACardState *card)
208
{
P
Paul Brook 已提交
209
    PXA2xxPCMCIAState *s = (PXA2xxPCMCIAState *) opaque;
210 211 212
    PCMCIACardClass *pcc;

    if (s->slot.attached) {
213
        return -EEXIST;
214
    }
215 216 217 218 219 220

    if (s->cd_irq) {
        qemu_irq_raise(s->cd_irq);
    }

    s->card = card;
221
    pcc = PCMCIA_CARD_GET_CLASS(s->card);
222

223
    s->slot.attached = true;
224
    s->card->slot = &s->slot;
225
    pcc->attach(s->card);
226 227 228 229 230

    return 0;
}

/* Eject card from the slot */
A
Andreas Färber 已提交
231
int pxa2xx_pcmcia_detach(void *opaque)
232
{
P
Paul Brook 已提交
233
    PXA2xxPCMCIAState *s = (PXA2xxPCMCIAState *) opaque;
234 235 236
    PCMCIACardClass *pcc;

    if (!s->slot.attached) {
237
        return -ENOENT;
238
    }
239

240 241
    pcc = PCMCIA_CARD_GET_CLASS(s->card);
    pcc->detach(s->card);
242 243
    s->card->slot = NULL;
    s->card = NULL;
244

245
    s->slot.attached = false;
246

247
    if (s->irq) {
248
        qemu_irq_lower(s->irq);
249 250
    }
    if (s->cd_irq) {
251
        qemu_irq_lower(s->cd_irq);
252
    }
253 254 255 256 257 258 259

    return 0;
}

/* Who to notify on card events */
void pxa2xx_pcmcia_set_irq_cb(void *opaque, qemu_irq irq, qemu_irq cd_irq)
{
P
Paul Brook 已提交
260
    PXA2xxPCMCIAState *s = (PXA2xxPCMCIAState *) opaque;
261 262 263
    s->irq = irq;
    s->cd_irq = cd_irq;
}
264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285

static void pxa2xx_pcmcia_class_init(ObjectClass *oc, void *data)
{
    DeviceClass *dc = DEVICE_CLASS(oc);

    dc->realize = pxa2xx_pcmcia_realize;
}

static const TypeInfo pxa2xx_pcmcia_type_info = {
    .name = TYPE_PXA2XX_PCMCIA,
    .parent = TYPE_SYS_BUS_DEVICE,
    .instance_size = sizeof(PXA2xxPCMCIAState),
    .instance_init = pxa2xx_pcmcia_initfn,
    .class_init = pxa2xx_pcmcia_class_init,
};

static void pxa2xx_pcmcia_register_types(void)
{
    type_register_static(&pxa2xx_pcmcia_type_info);
}

type_init(pxa2xx_pcmcia_register_types)