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