xref: /openbmc/qemu/hw/pcmcia/pxa2xx.c (revision 406d2aa2)
1 /*
2  * Intel XScale PXA255/270 PC Card and CompactFlash Interface.
3  *
4  * Copyright (c) 2006 Openedhand Ltd.
5  * Written by Andrzej Zaborowski <balrog@zabor.org>
6  *
7  * This code is licensed under the GPLv2.
8  *
9  * Contributions after 2012-01-13 are licensed under the terms of the
10  * GNU GPL, version 2 or (at your option) any later version.
11  */
12 
13 #include "qemu/osdep.h"
14 #include "hw/hw.h"
15 #include "hw/sysbus.h"
16 #include "hw/pcmcia.h"
17 #include "hw/arm/pxa.h"
18 
19 #define TYPE_PXA2XX_PCMCIA "pxa2xx-pcmcia"
20 #define PXA2XX_PCMCIA(obj) \
21     OBJECT_CHECK(PXA2xxPCMCIAState, obj, TYPE_PXA2XX_PCMCIA)
22 
23 struct PXA2xxPCMCIAState {
24     SysBusDevice parent_obj;
25 
26     PCMCIASocket slot;
27     MemoryRegion container_mem;
28     MemoryRegion common_iomem;
29     MemoryRegion attr_iomem;
30     MemoryRegion iomem;
31 
32     qemu_irq irq;
33     qemu_irq cd_irq;
34 
35     PCMCIACardState *card;
36 };
37 
38 static uint64_t pxa2xx_pcmcia_common_read(void *opaque,
39                 hwaddr offset, unsigned size)
40 {
41     PXA2xxPCMCIAState *s = (PXA2xxPCMCIAState *) opaque;
42     PCMCIACardClass *pcc;
43 
44     if (s->slot.attached) {
45         pcc = PCMCIA_CARD_GET_CLASS(s->card);
46         return pcc->common_read(s->card, offset);
47     }
48 
49     return 0;
50 }
51 
52 static void pxa2xx_pcmcia_common_write(void *opaque, hwaddr offset,
53                                        uint64_t value, unsigned size)
54 {
55     PXA2xxPCMCIAState *s = (PXA2xxPCMCIAState *) opaque;
56     PCMCIACardClass *pcc;
57 
58     if (s->slot.attached) {
59         pcc = PCMCIA_CARD_GET_CLASS(s->card);
60         pcc->common_write(s->card, offset, value);
61     }
62 }
63 
64 static uint64_t pxa2xx_pcmcia_attr_read(void *opaque,
65                 hwaddr offset, unsigned size)
66 {
67     PXA2xxPCMCIAState *s = (PXA2xxPCMCIAState *) opaque;
68     PCMCIACardClass *pcc;
69 
70     if (s->slot.attached) {
71         pcc = PCMCIA_CARD_GET_CLASS(s->card);
72         return pcc->attr_read(s->card, offset);
73     }
74 
75     return 0;
76 }
77 
78 static void pxa2xx_pcmcia_attr_write(void *opaque, hwaddr offset,
79                                      uint64_t value, unsigned size)
80 {
81     PXA2xxPCMCIAState *s = (PXA2xxPCMCIAState *) opaque;
82     PCMCIACardClass *pcc;
83 
84     if (s->slot.attached) {
85         pcc = PCMCIA_CARD_GET_CLASS(s->card);
86         pcc->attr_write(s->card, offset, value);
87     }
88 }
89 
90 static uint64_t pxa2xx_pcmcia_io_read(void *opaque,
91                 hwaddr offset, unsigned size)
92 {
93     PXA2xxPCMCIAState *s = (PXA2xxPCMCIAState *) opaque;
94     PCMCIACardClass *pcc;
95 
96     if (s->slot.attached) {
97         pcc = PCMCIA_CARD_GET_CLASS(s->card);
98         return pcc->io_read(s->card, offset);
99     }
100 
101     return 0;
102 }
103 
104 static void pxa2xx_pcmcia_io_write(void *opaque, hwaddr offset,
105                                    uint64_t value, unsigned size)
106 {
107     PXA2xxPCMCIAState *s = (PXA2xxPCMCIAState *) opaque;
108     PCMCIACardClass *pcc;
109 
110     if (s->slot.attached) {
111         pcc = PCMCIA_CARD_GET_CLASS(s->card);
112         pcc->io_write(s->card, offset, value);
113     }
114 }
115 
116 static const MemoryRegionOps pxa2xx_pcmcia_common_ops = {
117     .read = pxa2xx_pcmcia_common_read,
118     .write = pxa2xx_pcmcia_common_write,
119     .endianness = DEVICE_NATIVE_ENDIAN
120 };
121 
122 static const MemoryRegionOps pxa2xx_pcmcia_attr_ops = {
123     .read = pxa2xx_pcmcia_attr_read,
124     .write = pxa2xx_pcmcia_attr_write,
125     .endianness = DEVICE_NATIVE_ENDIAN
126 };
127 
128 static const MemoryRegionOps pxa2xx_pcmcia_io_ops = {
129     .read = pxa2xx_pcmcia_io_read,
130     .write = pxa2xx_pcmcia_io_write,
131     .endianness = DEVICE_NATIVE_ENDIAN
132 };
133 
134 static void pxa2xx_pcmcia_set_irq(void *opaque, int line, int level)
135 {
136     PXA2xxPCMCIAState *s = (PXA2xxPCMCIAState *) opaque;
137     if (!s->irq)
138         return;
139 
140     qemu_set_irq(s->irq, level);
141 }
142 
143 PXA2xxPCMCIAState *pxa2xx_pcmcia_init(MemoryRegion *sysmem,
144                                       hwaddr base)
145 {
146     DeviceState *dev;
147     PXA2xxPCMCIAState *s;
148 
149     dev = qdev_create(NULL, TYPE_PXA2XX_PCMCIA);
150     sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base);
151     s = PXA2XX_PCMCIA(dev);
152 
153     qdev_init_nofail(dev);
154 
155     return s;
156 }
157 
158 static void pxa2xx_pcmcia_initfn(Object *obj)
159 {
160     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
161     PXA2xxPCMCIAState *s = PXA2XX_PCMCIA(obj);
162 
163     memory_region_init(&s->container_mem, obj, "container", 0x10000000);
164     sysbus_init_mmio(sbd, &s->container_mem);
165 
166     /* Socket I/O Memory Space */
167     memory_region_init_io(&s->iomem, obj, &pxa2xx_pcmcia_io_ops, s,
168                           "pxa2xx-pcmcia-io", 0x04000000);
169     memory_region_add_subregion(&s->container_mem, 0x00000000,
170                                 &s->iomem);
171 
172     /* Then next 64 MB is reserved */
173 
174     /* Socket Attribute Memory Space */
175     memory_region_init_io(&s->attr_iomem, obj, &pxa2xx_pcmcia_attr_ops, s,
176                           "pxa2xx-pcmcia-attribute", 0x04000000);
177     memory_region_add_subregion(&s->container_mem, 0x08000000,
178                                 &s->attr_iomem);
179 
180     /* Socket Common Memory Space */
181     memory_region_init_io(&s->common_iomem, obj, &pxa2xx_pcmcia_common_ops, s,
182                           "pxa2xx-pcmcia-common", 0x04000000);
183     memory_region_add_subregion(&s->container_mem, 0x0c000000,
184                                 &s->common_iomem);
185 
186     s->slot.irq = qemu_allocate_irq(pxa2xx_pcmcia_set_irq, s, 0);
187 
188     object_property_add_link(obj, "card", TYPE_PCMCIA_CARD,
189                              (Object **)&s->card,
190                              NULL, /* read-only property */
191                              0, NULL);
192 }
193 
194 /* Insert a new card into a slot */
195 int pxa2xx_pcmcia_attach(void *opaque, PCMCIACardState *card)
196 {
197     PXA2xxPCMCIAState *s = (PXA2xxPCMCIAState *) opaque;
198     PCMCIACardClass *pcc;
199 
200     if (s->slot.attached) {
201         return -EEXIST;
202     }
203 
204     if (s->cd_irq) {
205         qemu_irq_raise(s->cd_irq);
206     }
207 
208     s->card = card;
209     pcc = PCMCIA_CARD_GET_CLASS(s->card);
210 
211     s->slot.attached = true;
212     s->card->slot = &s->slot;
213     pcc->attach(s->card);
214 
215     return 0;
216 }
217 
218 /* Eject card from the slot */
219 int pxa2xx_pcmcia_detach(void *opaque)
220 {
221     PXA2xxPCMCIAState *s = (PXA2xxPCMCIAState *) opaque;
222     PCMCIACardClass *pcc;
223 
224     if (!s->slot.attached) {
225         return -ENOENT;
226     }
227 
228     pcc = PCMCIA_CARD_GET_CLASS(s->card);
229     pcc->detach(s->card);
230     s->card->slot = NULL;
231     s->card = NULL;
232 
233     s->slot.attached = false;
234 
235     if (s->irq) {
236         qemu_irq_lower(s->irq);
237     }
238     if (s->cd_irq) {
239         qemu_irq_lower(s->cd_irq);
240     }
241 
242     return 0;
243 }
244 
245 /* Who to notify on card events */
246 void pxa2xx_pcmcia_set_irq_cb(void *opaque, qemu_irq irq, qemu_irq cd_irq)
247 {
248     PXA2xxPCMCIAState *s = (PXA2xxPCMCIAState *) opaque;
249     s->irq = irq;
250     s->cd_irq = cd_irq;
251 }
252 
253 static const TypeInfo pxa2xx_pcmcia_type_info = {
254     .name = TYPE_PXA2XX_PCMCIA,
255     .parent = TYPE_SYS_BUS_DEVICE,
256     .instance_size = sizeof(PXA2xxPCMCIAState),
257     .instance_init = pxa2xx_pcmcia_initfn,
258 };
259 
260 static void pxa2xx_pcmcia_register_types(void)
261 {
262     type_register_static(&pxa2xx_pcmcia_type_info);
263 }
264 
265 type_init(pxa2xx_pcmcia_register_types)
266