xref: /qemu/hw/intc/exynos4210_gic.c (revision ccd241b5a2223c502441714d413d569565e4a3b4)
1  /*
2   * Samsung exynos4210 GIC implementation. Based on hw/arm_gic.c
3   *
4   * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd.
5   * All rights reserved.
6   *
7   * Evgeny Voevodin <e.voevodin@samsung.com>
8   *
9   * This program is free software; you can redistribute it and/or modify it
10   * under the terms of the GNU General Public License as published by the
11   * Free Software Foundation; either version 2 of the License, or (at your
12   * option) any later version.
13   *
14   * This program is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17   * See the GNU General Public License for more details.
18   *
19   * You should have received a copy of the GNU General Public License along
20   * with this program; if not, see <http://www.gnu.org/licenses/>.
21   */
22  
23  #include "qemu/osdep.h"
24  #include "hw/sysbus.h"
25  #include "qemu-common.h"
26  #include "hw/irq.h"
27  #include "hw/arm/exynos4210.h"
28  
29  enum ExtGicId {
30      EXT_GIC_ID_MDMA_LCD0 = 66,
31      EXT_GIC_ID_PDMA0,
32      EXT_GIC_ID_PDMA1,
33      EXT_GIC_ID_TIMER0,
34      EXT_GIC_ID_TIMER1,
35      EXT_GIC_ID_TIMER2,
36      EXT_GIC_ID_TIMER3,
37      EXT_GIC_ID_TIMER4,
38      EXT_GIC_ID_MCT_L0,
39      EXT_GIC_ID_WDT,
40      EXT_GIC_ID_RTC_ALARM,
41      EXT_GIC_ID_RTC_TIC,
42      EXT_GIC_ID_GPIO_XB,
43      EXT_GIC_ID_GPIO_XA,
44      EXT_GIC_ID_MCT_L1,
45      EXT_GIC_ID_IEM_APC,
46      EXT_GIC_ID_IEM_IEC,
47      EXT_GIC_ID_NFC,
48      EXT_GIC_ID_UART0,
49      EXT_GIC_ID_UART1,
50      EXT_GIC_ID_UART2,
51      EXT_GIC_ID_UART3,
52      EXT_GIC_ID_UART4,
53      EXT_GIC_ID_MCT_G0,
54      EXT_GIC_ID_I2C0,
55      EXT_GIC_ID_I2C1,
56      EXT_GIC_ID_I2C2,
57      EXT_GIC_ID_I2C3,
58      EXT_GIC_ID_I2C4,
59      EXT_GIC_ID_I2C5,
60      EXT_GIC_ID_I2C6,
61      EXT_GIC_ID_I2C7,
62      EXT_GIC_ID_SPI0,
63      EXT_GIC_ID_SPI1,
64      EXT_GIC_ID_SPI2,
65      EXT_GIC_ID_MCT_G1,
66      EXT_GIC_ID_USB_HOST,
67      EXT_GIC_ID_USB_DEVICE,
68      EXT_GIC_ID_MODEMIF,
69      EXT_GIC_ID_HSMMC0,
70      EXT_GIC_ID_HSMMC1,
71      EXT_GIC_ID_HSMMC2,
72      EXT_GIC_ID_HSMMC3,
73      EXT_GIC_ID_SDMMC,
74      EXT_GIC_ID_MIPI_CSI_4LANE,
75      EXT_GIC_ID_MIPI_DSI_4LANE,
76      EXT_GIC_ID_MIPI_CSI_2LANE,
77      EXT_GIC_ID_MIPI_DSI_2LANE,
78      EXT_GIC_ID_ONENAND_AUDI,
79      EXT_GIC_ID_ROTATOR,
80      EXT_GIC_ID_FIMC0,
81      EXT_GIC_ID_FIMC1,
82      EXT_GIC_ID_FIMC2,
83      EXT_GIC_ID_FIMC3,
84      EXT_GIC_ID_JPEG,
85      EXT_GIC_ID_2D,
86      EXT_GIC_ID_PCIe,
87      EXT_GIC_ID_MIXER,
88      EXT_GIC_ID_HDMI,
89      EXT_GIC_ID_HDMI_I2C,
90      EXT_GIC_ID_MFC,
91      EXT_GIC_ID_TVENC,
92  };
93  
94  enum ExtInt {
95      EXT_GIC_ID_EXTINT0 = 48,
96      EXT_GIC_ID_EXTINT1,
97      EXT_GIC_ID_EXTINT2,
98      EXT_GIC_ID_EXTINT3,
99      EXT_GIC_ID_EXTINT4,
100      EXT_GIC_ID_EXTINT5,
101      EXT_GIC_ID_EXTINT6,
102      EXT_GIC_ID_EXTINT7,
103      EXT_GIC_ID_EXTINT8,
104      EXT_GIC_ID_EXTINT9,
105      EXT_GIC_ID_EXTINT10,
106      EXT_GIC_ID_EXTINT11,
107      EXT_GIC_ID_EXTINT12,
108      EXT_GIC_ID_EXTINT13,
109      EXT_GIC_ID_EXTINT14,
110      EXT_GIC_ID_EXTINT15
111  };
112  
113  /*
114   * External GIC sources which are not from External Interrupt Combiner or
115   * External Interrupts are starting from EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ,
116   * which is INTG16 in Internal Interrupt Combiner.
117   */
118  
119  static uint32_t
120  combiner_grp_to_gic_id[64-EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ][8] = {
121      /* int combiner groups 16-19 */
122      { }, { }, { }, { },
123      /* int combiner group 20 */
124      { 0, EXT_GIC_ID_MDMA_LCD0 },
125      /* int combiner group 21 */
126      { EXT_GIC_ID_PDMA0, EXT_GIC_ID_PDMA1 },
127      /* int combiner group 22 */
128      { EXT_GIC_ID_TIMER0, EXT_GIC_ID_TIMER1, EXT_GIC_ID_TIMER2,
129              EXT_GIC_ID_TIMER3, EXT_GIC_ID_TIMER4 },
130      /* int combiner group 23 */
131      { EXT_GIC_ID_RTC_ALARM, EXT_GIC_ID_RTC_TIC },
132      /* int combiner group 24 */
133      { EXT_GIC_ID_GPIO_XB, EXT_GIC_ID_GPIO_XA },
134      /* int combiner group 25 */
135      { EXT_GIC_ID_IEM_APC, EXT_GIC_ID_IEM_IEC },
136      /* int combiner group 26 */
137      { EXT_GIC_ID_UART0, EXT_GIC_ID_UART1, EXT_GIC_ID_UART2, EXT_GIC_ID_UART3,
138              EXT_GIC_ID_UART4 },
139      /* int combiner group 27 */
140      { EXT_GIC_ID_I2C0, EXT_GIC_ID_I2C1, EXT_GIC_ID_I2C2, EXT_GIC_ID_I2C3,
141              EXT_GIC_ID_I2C4, EXT_GIC_ID_I2C5, EXT_GIC_ID_I2C6,
142              EXT_GIC_ID_I2C7 },
143      /* int combiner group 28 */
144      { EXT_GIC_ID_SPI0, EXT_GIC_ID_SPI1, EXT_GIC_ID_SPI2 , EXT_GIC_ID_USB_HOST},
145      /* int combiner group 29 */
146      { EXT_GIC_ID_HSMMC0, EXT_GIC_ID_HSMMC1, EXT_GIC_ID_HSMMC2,
147       EXT_GIC_ID_HSMMC3, EXT_GIC_ID_SDMMC },
148      /* int combiner group 30 */
149      { EXT_GIC_ID_MIPI_CSI_4LANE, EXT_GIC_ID_MIPI_CSI_2LANE },
150      /* int combiner group 31 */
151      { EXT_GIC_ID_MIPI_DSI_4LANE, EXT_GIC_ID_MIPI_DSI_2LANE },
152      /* int combiner group 32 */
153      { EXT_GIC_ID_FIMC0, EXT_GIC_ID_FIMC1 },
154      /* int combiner group 33 */
155      { EXT_GIC_ID_FIMC2, EXT_GIC_ID_FIMC3 },
156      /* int combiner group 34 */
157      { EXT_GIC_ID_ONENAND_AUDI, EXT_GIC_ID_NFC },
158      /* int combiner group 35 */
159      { 0, 0, 0, EXT_GIC_ID_MCT_L1, EXT_GIC_ID_MCT_G0, EXT_GIC_ID_MCT_G1 },
160      /* int combiner group 36 */
161      { EXT_GIC_ID_MIXER },
162      /* int combiner group 37 */
163      { EXT_GIC_ID_EXTINT4, EXT_GIC_ID_EXTINT5, EXT_GIC_ID_EXTINT6,
164       EXT_GIC_ID_EXTINT7 },
165      /* groups 38-50 */
166      { }, { }, { }, { }, { }, { }, { }, { }, { }, { }, { }, { }, { },
167      /* int combiner group 51 */
168      { EXT_GIC_ID_MCT_L0, 0, 0, 0, EXT_GIC_ID_MCT_G0, EXT_GIC_ID_MCT_G1 },
169      /* group 52 */
170      { },
171      /* int combiner group 53 */
172      { EXT_GIC_ID_WDT, 0, 0, 0, EXT_GIC_ID_MCT_G0, EXT_GIC_ID_MCT_G1 },
173      /* groups 54-63 */
174      { }, { }, { }, { }, { }, { }, { }, { }, { }, { }
175  };
176  
177  #define EXYNOS4210_GIC_NIRQ 160
178  
179  #define EXYNOS4210_EXT_GIC_CPU_REGION_SIZE     0x10000
180  #define EXYNOS4210_EXT_GIC_DIST_REGION_SIZE    0x10000
181  
182  #define EXYNOS4210_EXT_GIC_PER_CPU_OFFSET      0x8000
183  #define EXYNOS4210_EXT_GIC_CPU_GET_OFFSET(n) \
184      ((n) * EXYNOS4210_EXT_GIC_PER_CPU_OFFSET)
185  #define EXYNOS4210_EXT_GIC_DIST_GET_OFFSET(n) \
186      ((n) * EXYNOS4210_EXT_GIC_PER_CPU_OFFSET)
187  
188  #define EXYNOS4210_GIC_CPU_REGION_SIZE  0x100
189  #define EXYNOS4210_GIC_DIST_REGION_SIZE 0x1000
190  
191  static void exynos4210_irq_handler(void *opaque, int irq, int level)
192  {
193      Exynos4210Irq *s = (Exynos4210Irq *)opaque;
194  
195      /* Bypass */
196      qemu_set_irq(s->board_irqs[irq], level);
197  }
198  
199  /*
200   * Initialize exynos4210 IRQ subsystem stub.
201   */
202  qemu_irq *exynos4210_init_irq(Exynos4210Irq *s)
203  {
204      return qemu_allocate_irqs(exynos4210_irq_handler, s,
205              EXYNOS4210_MAX_INT_COMBINER_IN_IRQ);
206  }
207  
208  /*
209   * Initialize board IRQs.
210   * These IRQs contain splitted Int/External Combiner and External Gic IRQs.
211   */
212  void exynos4210_init_board_irqs(Exynos4210Irq *s)
213  {
214      uint32_t grp, bit, irq_id, n;
215  
216      for (n = 0; n < EXYNOS4210_MAX_EXT_COMBINER_IN_IRQ; n++) {
217          irq_id = 0;
218          if (n == EXYNOS4210_COMBINER_GET_IRQ_NUM(1, 4) ||
219                  n == EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 4)) {
220              /* MCT_G0 is passed to External GIC */
221              irq_id = EXT_GIC_ID_MCT_G0;
222          }
223          if (n == EXYNOS4210_COMBINER_GET_IRQ_NUM(1, 5) ||
224                  n == EXYNOS4210_COMBINER_GET_IRQ_NUM(12, 5)) {
225              /* MCT_G1 is passed to External and GIC */
226              irq_id = EXT_GIC_ID_MCT_G1;
227          }
228          if (irq_id) {
229              s->board_irqs[n] = qemu_irq_split(s->int_combiner_irq[n],
230                      s->ext_gic_irq[irq_id-32]);
231          } else {
232              s->board_irqs[n] = qemu_irq_split(s->int_combiner_irq[n],
233                      s->ext_combiner_irq[n]);
234          }
235      }
236      for (; n < EXYNOS4210_MAX_INT_COMBINER_IN_IRQ; n++) {
237          /* these IDs are passed to Internal Combiner and External GIC */
238          grp = EXYNOS4210_COMBINER_GET_GRP_NUM(n);
239          bit = EXYNOS4210_COMBINER_GET_BIT_NUM(n);
240          irq_id = combiner_grp_to_gic_id[grp -
241                       EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ][bit];
242  
243          if (irq_id) {
244              s->board_irqs[n] = qemu_irq_split(s->int_combiner_irq[n],
245                      s->ext_gic_irq[irq_id-32]);
246          }
247      }
248  }
249  
250  /*
251   * Get IRQ number from exynos4210 IRQ subsystem stub.
252   * To identify IRQ source use internal combiner group and bit number
253   *  grp - group number
254   *  bit - bit number inside group
255   */
256  uint32_t exynos4210_get_irq(uint32_t grp, uint32_t bit)
257  {
258      return EXYNOS4210_COMBINER_GET_IRQ_NUM(grp, bit);
259  }
260  
261  /********* GIC part *********/
262  
263  #define TYPE_EXYNOS4210_GIC "exynos4210.gic"
264  #define EXYNOS4210_GIC(obj) \
265      OBJECT_CHECK(Exynos4210GicState, (obj), TYPE_EXYNOS4210_GIC)
266  
267  typedef struct {
268      SysBusDevice parent_obj;
269  
270      MemoryRegion cpu_container;
271      MemoryRegion dist_container;
272      MemoryRegion cpu_alias[EXYNOS4210_NCPUS];
273      MemoryRegion dist_alias[EXYNOS4210_NCPUS];
274      uint32_t num_cpu;
275      DeviceState *gic;
276  } Exynos4210GicState;
277  
278  static void exynos4210_gic_set_irq(void *opaque, int irq, int level)
279  {
280      Exynos4210GicState *s = (Exynos4210GicState *)opaque;
281      qemu_set_irq(qdev_get_gpio_in(s->gic, irq), level);
282  }
283  
284  static int exynos4210_gic_init(SysBusDevice *sbd)
285  {
286      DeviceState *dev = DEVICE(sbd);
287      Exynos4210GicState *s = EXYNOS4210_GIC(dev);
288      uint32_t i;
289      const char cpu_prefix[] = "exynos4210-gic-alias_cpu";
290      const char dist_prefix[] = "exynos4210-gic-alias_dist";
291      char cpu_alias_name[sizeof(cpu_prefix) + 3];
292      char dist_alias_name[sizeof(cpu_prefix) + 3];
293      SysBusDevice *busdev;
294  
295      s->gic = qdev_create(NULL, "arm_gic");
296      qdev_prop_set_uint32(s->gic, "num-cpu", s->num_cpu);
297      qdev_prop_set_uint32(s->gic, "num-irq", EXYNOS4210_GIC_NIRQ);
298      qdev_init_nofail(s->gic);
299      busdev = SYS_BUS_DEVICE(s->gic);
300  
301      /* Pass through outbound IRQ lines from the GIC */
302      sysbus_pass_irq(sbd, busdev);
303  
304      /* Pass through inbound GPIO lines to the GIC */
305      qdev_init_gpio_in(dev, exynos4210_gic_set_irq,
306                        EXYNOS4210_GIC_NIRQ - 32);
307  
308      memory_region_init(&s->cpu_container, OBJECT(s), "exynos4210-cpu-container",
309              EXYNOS4210_EXT_GIC_CPU_REGION_SIZE);
310      memory_region_init(&s->dist_container, OBJECT(s), "exynos4210-dist-container",
311              EXYNOS4210_EXT_GIC_DIST_REGION_SIZE);
312  
313      for (i = 0; i < s->num_cpu; i++) {
314          /* Map CPU interface per SMP Core */
315          sprintf(cpu_alias_name, "%s%x", cpu_prefix, i);
316          memory_region_init_alias(&s->cpu_alias[i], OBJECT(s),
317                                   cpu_alias_name,
318                                   sysbus_mmio_get_region(busdev, 1),
319                                   0,
320                                   EXYNOS4210_GIC_CPU_REGION_SIZE);
321          memory_region_add_subregion(&s->cpu_container,
322                  EXYNOS4210_EXT_GIC_CPU_GET_OFFSET(i), &s->cpu_alias[i]);
323  
324          /* Map Distributor per SMP Core */
325          sprintf(dist_alias_name, "%s%x", dist_prefix, i);
326          memory_region_init_alias(&s->dist_alias[i], OBJECT(s),
327                                   dist_alias_name,
328                                   sysbus_mmio_get_region(busdev, 0),
329                                   0,
330                                   EXYNOS4210_GIC_DIST_REGION_SIZE);
331          memory_region_add_subregion(&s->dist_container,
332                  EXYNOS4210_EXT_GIC_DIST_GET_OFFSET(i), &s->dist_alias[i]);
333      }
334  
335      sysbus_init_mmio(sbd, &s->cpu_container);
336      sysbus_init_mmio(sbd, &s->dist_container);
337  
338      return 0;
339  }
340  
341  static Property exynos4210_gic_properties[] = {
342      DEFINE_PROP_UINT32("num-cpu", Exynos4210GicState, num_cpu, 1),
343      DEFINE_PROP_END_OF_LIST(),
344  };
345  
346  static void exynos4210_gic_class_init(ObjectClass *klass, void *data)
347  {
348      DeviceClass *dc = DEVICE_CLASS(klass);
349      SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
350  
351      k->init = exynos4210_gic_init;
352      dc->props = exynos4210_gic_properties;
353  }
354  
355  static const TypeInfo exynos4210_gic_info = {
356      .name          = TYPE_EXYNOS4210_GIC,
357      .parent        = TYPE_SYS_BUS_DEVICE,
358      .instance_size = sizeof(Exynos4210GicState),
359      .class_init    = exynos4210_gic_class_init,
360  };
361  
362  static void exynos4210_gic_register_types(void)
363  {
364      type_register_static(&exynos4210_gic_info);
365  }
366  
367  type_init(exynos4210_gic_register_types)
368  
369  /* IRQ OR Gate struct.
370   *
371   * This device models an OR gate. There are n_in input qdev gpio lines and one
372   * output sysbus IRQ line. The output IRQ level is formed as OR between all
373   * gpio inputs.
374   */
375  
376  #define TYPE_EXYNOS4210_IRQ_GATE "exynos4210.irq_gate"
377  #define EXYNOS4210_IRQ_GATE(obj) \
378      OBJECT_CHECK(Exynos4210IRQGateState, (obj), TYPE_EXYNOS4210_IRQ_GATE)
379  
380  typedef struct Exynos4210IRQGateState {
381      SysBusDevice parent_obj;
382  
383      uint32_t n_in;      /* inputs amount */
384      uint32_t *level;    /* input levels */
385      qemu_irq out;       /* output IRQ */
386  } Exynos4210IRQGateState;
387  
388  static Property exynos4210_irq_gate_properties[] = {
389      DEFINE_PROP_UINT32("n_in", Exynos4210IRQGateState, n_in, 1),
390      DEFINE_PROP_END_OF_LIST(),
391  };
392  
393  static const VMStateDescription vmstate_exynos4210_irq_gate = {
394      .name = "exynos4210.irq_gate",
395      .version_id = 2,
396      .minimum_version_id = 2,
397      .fields = (VMStateField[]) {
398          VMSTATE_VBUFFER_UINT32(level, Exynos4210IRQGateState, 1, NULL, 0, n_in),
399          VMSTATE_END_OF_LIST()
400      }
401  };
402  
403  /* Process a change in IRQ input. */
404  static void exynos4210_irq_gate_handler(void *opaque, int irq, int level)
405  {
406      Exynos4210IRQGateState *s = (Exynos4210IRQGateState *)opaque;
407      uint32_t i;
408  
409      assert(irq < s->n_in);
410  
411      s->level[irq] = level;
412  
413      for (i = 0; i < s->n_in; i++) {
414          if (s->level[i] >= 1) {
415              qemu_irq_raise(s->out);
416              return;
417          }
418      }
419  
420      qemu_irq_lower(s->out);
421  }
422  
423  static void exynos4210_irq_gate_reset(DeviceState *d)
424  {
425      Exynos4210IRQGateState *s = EXYNOS4210_IRQ_GATE(d);
426  
427      memset(s->level, 0, s->n_in * sizeof(*s->level));
428  }
429  
430  /*
431   * IRQ Gate initialization.
432   */
433  static int exynos4210_irq_gate_init(SysBusDevice *sbd)
434  {
435      DeviceState *dev = DEVICE(sbd);
436      Exynos4210IRQGateState *s = EXYNOS4210_IRQ_GATE(dev);
437  
438      /* Allocate general purpose input signals and connect a handler to each of
439       * them */
440      qdev_init_gpio_in(dev, exynos4210_irq_gate_handler, s->n_in);
441  
442      s->level = g_malloc0(s->n_in * sizeof(*s->level));
443  
444      sysbus_init_irq(sbd, &s->out);
445  
446      return 0;
447  }
448  
449  static void exynos4210_irq_gate_class_init(ObjectClass *klass, void *data)
450  {
451      DeviceClass *dc = DEVICE_CLASS(klass);
452      SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
453  
454      k->init = exynos4210_irq_gate_init;
455      dc->reset = exynos4210_irq_gate_reset;
456      dc->vmsd = &vmstate_exynos4210_irq_gate;
457      dc->props = exynos4210_irq_gate_properties;
458  }
459  
460  static const TypeInfo exynos4210_irq_gate_info = {
461      .name          = TYPE_EXYNOS4210_IRQ_GATE,
462      .parent        = TYPE_SYS_BUS_DEVICE,
463      .instance_size = sizeof(Exynos4210IRQGateState),
464      .class_init    = exynos4210_irq_gate_class_init,
465  };
466  
467  static void exynos4210_irq_gate_register_types(void)
468  {
469      type_register_static(&exynos4210_irq_gate_info);
470  }
471  
472  type_init(exynos4210_irq_gate_register_types)
473