Merge remote-tracking branch 'pmaydell/arm-devs.for-upstream' into staging
* pmaydell/arm-devs.for-upstream: add L2x0/PL310 cache controller device arm: add dummy gic security registers arm: Set frequencies for arm_timer arm: add missing scu registers hw/omap_gpmc: Fix region map/unmap when configuring prefetch engine hw/omap1.c: Drop unused includes hw/omap1.c: Separate dpll_ctl from omap_mpu_state hw/omap1.c: Separate PWT from omap_mpu_state hw/omap1.c: Separate PWL from omap_mpu_state hw/omap1.c: omap_mpuio_init() need not be public hw/pl110.c: Add post-load hook to invalidate display hw/pl181.c: Add save/load support
This commit is contained in:
		
						commit
						c47f322365
					
				| @ -336,6 +336,7 @@ obj-arm-y = integratorcp.o versatilepb.o arm_pic.o arm_timer.o | ||||
| obj-arm-y += arm_boot.o pl011.o pl031.o pl050.o pl080.o pl110.o pl181.o pl190.o | ||||
| obj-arm-y += versatile_pci.o | ||||
| obj-arm-y += realview_gic.o realview.o arm_sysctl.o arm11mpcore.o a9mpcore.o | ||||
| obj-arm-y += arm_l2x0.o | ||||
| obj-arm-y += arm_mptimer.o | ||||
| obj-arm-y += armv7m.o armv7m_nvic.o stellaris.o pl022.o stellaris_enet.o | ||||
| obj-arm-y += pl061.o | ||||
|  | ||||
| @ -29,6 +29,7 @@ gic_get_current_cpu(void) | ||||
| typedef struct a9mp_priv_state { | ||||
|     gic_state gic; | ||||
|     uint32_t scu_control; | ||||
|     uint32_t scu_status; | ||||
|     uint32_t old_timer_status[8]; | ||||
|     uint32_t num_cpu; | ||||
|     qemu_irq *timer_irq; | ||||
| @ -48,7 +49,13 @@ static uint64_t a9_scu_read(void *opaque, target_phys_addr_t offset, | ||||
|     case 0x04: /* Configuration */ | ||||
|         return (((1 << s->num_cpu) - 1) << 4) | (s->num_cpu - 1); | ||||
|     case 0x08: /* CPU Power Status */ | ||||
|         return 0; | ||||
|         return s->scu_status; | ||||
|     case 0x09: /* CPU status.  */ | ||||
|         return s->scu_status >> 8; | ||||
|     case 0x0a: /* CPU status.  */ | ||||
|         return s->scu_status >> 16; | ||||
|     case 0x0b: /* CPU status.  */ | ||||
|         return s->scu_status >> 24; | ||||
|     case 0x0c: /* Invalidate All Registers In Secure State */ | ||||
|         return 0; | ||||
|     case 0x40: /* Filtering Start Address Register */ | ||||
| @ -67,12 +74,35 @@ static void a9_scu_write(void *opaque, target_phys_addr_t offset, | ||||
|                          uint64_t value, unsigned size) | ||||
| { | ||||
|     a9mp_priv_state *s = (a9mp_priv_state *)opaque; | ||||
|     uint32_t mask; | ||||
|     uint32_t shift; | ||||
|     switch (size) { | ||||
|     case 1: | ||||
|         mask = 0xff; | ||||
|         break; | ||||
|     case 2: | ||||
|         mask = 0xffff; | ||||
|         break; | ||||
|     case 4: | ||||
|         mask = 0xffffffff; | ||||
|         break; | ||||
|     default: | ||||
|         fprintf(stderr, "Invalid size %u in write to a9 scu register %x\n", | ||||
|                 size, offset); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     switch (offset) { | ||||
|     case 0x00: /* Control */ | ||||
|         s->scu_control = value & 1; | ||||
|         break; | ||||
|     case 0x4: /* Configuration: RO */ | ||||
|         break; | ||||
|     case 0x08: case 0x09: case 0x0A: case 0x0B: /* Power Control */ | ||||
|         shift = (offset - 0x8) * 8; | ||||
|         s->scu_status &= ~(mask << shift); | ||||
|         s->scu_status |= ((value & mask) << shift); | ||||
|         break; | ||||
|     case 0x0c: /* Invalidate All Registers In Secure State */ | ||||
|         /* no-op as we do not implement caches */ | ||||
|         break; | ||||
| @ -80,7 +110,6 @@ static void a9_scu_write(void *opaque, target_phys_addr_t offset, | ||||
|     case 0x44: /* Filtering End Address Register */ | ||||
|         /* RAZ/WI, like an implementation with only one AXI master */ | ||||
|         break; | ||||
|     case 0x8: /* CPU Power Status */ | ||||
|     case 0x50: /* SCU Access Control Register */ | ||||
|     case 0x54: /* SCU Non-secure Access Control Register */ | ||||
|         /* unimplemented, fall through */ | ||||
| @ -169,11 +198,12 @@ static int a9mp_priv_init(SysBusDevice *dev) | ||||
| 
 | ||||
| static const VMStateDescription vmstate_a9mp_priv = { | ||||
|     .name = "a9mpcore_priv", | ||||
|     .version_id = 1, | ||||
|     .version_id = 2, | ||||
|     .minimum_version_id = 1, | ||||
|     .fields = (VMStateField[]) { | ||||
|         VMSTATE_UINT32(scu_control, a9mp_priv_state), | ||||
|         VMSTATE_UINT32_ARRAY(old_timer_status, a9mp_priv_state, 8), | ||||
|         VMSTATE_UINT32_V(scu_status, a9mp_priv_state, 2), | ||||
|         VMSTATE_END_OF_LIST() | ||||
|     } | ||||
| }; | ||||
|  | ||||
| @ -282,6 +282,10 @@ static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset) | ||||
|             return ((GIC_NIRQ / 32) - 1) | ((NUM_CPU(s) - 1) << 5); | ||||
|         if (offset < 0x08) | ||||
|             return 0; | ||||
|         if (offset >= 0x80) { | ||||
|             /* Interrupt Security , RAZ/WI */ | ||||
|             return 0; | ||||
|         } | ||||
| #endif | ||||
|         goto bad_reg; | ||||
|     } else if (offset < 0x200) { | ||||
| @ -413,6 +417,8 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset, | ||||
|             DPRINTF("Distribution %sabled\n", s->enabled ? "En" : "Dis"); | ||||
|         } else if (offset < 4) { | ||||
|             /* ignored.  */ | ||||
|         } else if (offset >= 0x80) { | ||||
|             /* Interrupt Security Registers, RAZ/WI */ | ||||
|         } else { | ||||
|             goto bad_reg; | ||||
|         } | ||||
|  | ||||
							
								
								
									
										181
									
								
								hw/arm_l2x0.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										181
									
								
								hw/arm_l2x0.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,181 @@ | ||||
| /*
 | ||||
|  * ARM dummy L210, L220, PL310 cache controller. | ||||
|  * | ||||
|  * Copyright (c) 2010-2012 Calxeda | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify it | ||||
|  * under the terms and conditions of the GNU General Public License, | ||||
|  * version 2 or any later version, as published by the Free Software | ||||
|  * Foundation. | ||||
|  * | ||||
|  * This program is distributed in the hope it will be useful, but WITHOUT | ||||
|  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for | ||||
|  * more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License along with | ||||
|  * this program.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include "sysbus.h" | ||||
| 
 | ||||
| /* L2C-310 r3p2 */ | ||||
| #define CACHE_ID 0x410000c8 | ||||
| 
 | ||||
| typedef struct l2x0_state { | ||||
|     SysBusDevice busdev; | ||||
|     MemoryRegion iomem; | ||||
|     uint32_t cache_type; | ||||
|     uint32_t ctrl; | ||||
|     uint32_t aux_ctrl; | ||||
|     uint32_t data_ctrl; | ||||
|     uint32_t tag_ctrl; | ||||
|     uint32_t filter_start; | ||||
|     uint32_t filter_end; | ||||
| } l2x0_state; | ||||
| 
 | ||||
| static const VMStateDescription vmstate_l2x0 = { | ||||
|     .name = "l2x0", | ||||
|     .version_id = 1, | ||||
|     .minimum_version_id = 1, | ||||
|     .fields = (VMStateField[]) { | ||||
|         VMSTATE_UINT32(ctrl, l2x0_state), | ||||
|         VMSTATE_UINT32(aux_ctrl, l2x0_state), | ||||
|         VMSTATE_UINT32(data_ctrl, l2x0_state), | ||||
|         VMSTATE_UINT32(tag_ctrl, l2x0_state), | ||||
|         VMSTATE_UINT32(filter_start, l2x0_state), | ||||
|         VMSTATE_UINT32(filter_end, l2x0_state), | ||||
|         VMSTATE_END_OF_LIST() | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| static uint64_t l2x0_priv_read(void *opaque, target_phys_addr_t offset, | ||||
|                                unsigned size) | ||||
| { | ||||
|     uint32_t cache_data; | ||||
|     l2x0_state *s = (l2x0_state *)opaque; | ||||
|     offset &= 0xfff; | ||||
|     if (offset >= 0x730 && offset < 0x800) { | ||||
|         return 0; /* cache ops complete */ | ||||
|     } | ||||
|     switch (offset) { | ||||
|     case 0: | ||||
|         return CACHE_ID; | ||||
|     case 0x4: | ||||
|         /* aux_ctrl values affect cache_type values */ | ||||
|         cache_data = (s->aux_ctrl & (7 << 17)) >> 15; | ||||
|         cache_data |= (s->aux_ctrl & (1 << 16)) >> 16; | ||||
|         return s->cache_type |= (cache_data << 18) | (cache_data << 6); | ||||
|     case 0x100: | ||||
|         return s->ctrl; | ||||
|     case 0x104: | ||||
|         return s->aux_ctrl; | ||||
|     case 0x108: | ||||
|         return s->tag_ctrl; | ||||
|     case 0x10C: | ||||
|         return s->data_ctrl; | ||||
|     case 0xC00: | ||||
|         return s->filter_start; | ||||
|     case 0xC04: | ||||
|         return s->filter_end; | ||||
|     case 0xF40: | ||||
|         return 0; | ||||
|     case 0xF60: | ||||
|         return 0; | ||||
|     case 0xF80: | ||||
|         return 0; | ||||
|     default: | ||||
|         fprintf(stderr, "l2x0_priv_read: Bad offset %x\n", (int)offset); | ||||
|         break; | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static void l2x0_priv_write(void *opaque, target_phys_addr_t offset, | ||||
|                             uint64_t value, unsigned size) | ||||
| { | ||||
|     l2x0_state *s = (l2x0_state *)opaque; | ||||
|     offset &= 0xfff; | ||||
|     if (offset >= 0x730 && offset < 0x800) { | ||||
|         /* ignore */ | ||||
|         return; | ||||
|     } | ||||
|     switch (offset) { | ||||
|     case 0x100: | ||||
|         s->ctrl = value & 1; | ||||
|         break; | ||||
|     case 0x104: | ||||
|         s->aux_ctrl = value; | ||||
|         break; | ||||
|     case 0x108: | ||||
|         s->tag_ctrl = value; | ||||
|         break; | ||||
|     case 0x10C: | ||||
|         s->data_ctrl = value; | ||||
|         break; | ||||
|     case 0xC00: | ||||
|         s->filter_start = value; | ||||
|         break; | ||||
|     case 0xC04: | ||||
|         s->filter_end = value; | ||||
|         break; | ||||
|     case 0xF40: | ||||
|         return; | ||||
|     case 0xF60: | ||||
|         return; | ||||
|     case 0xF80: | ||||
|         return; | ||||
|     default: | ||||
|         fprintf(stderr, "l2x0_priv_write: Bad offset %x\n", (int)offset); | ||||
|         break; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void l2x0_priv_reset(DeviceState *dev) | ||||
| { | ||||
|     l2x0_state *s = DO_UPCAST(l2x0_state, busdev.qdev, dev); | ||||
| 
 | ||||
|     s->ctrl = 0; | ||||
|     s->aux_ctrl = 0x02020000; | ||||
|     s->tag_ctrl = 0; | ||||
|     s->data_ctrl = 0; | ||||
|     s->filter_start = 0; | ||||
|     s->filter_end = 0; | ||||
| } | ||||
| 
 | ||||
| static const MemoryRegionOps l2x0_mem_ops = { | ||||
|     .read = l2x0_priv_read, | ||||
|     .write = l2x0_priv_write, | ||||
|     .endianness = DEVICE_NATIVE_ENDIAN, | ||||
|  }; | ||||
| 
 | ||||
| static int l2x0_priv_init(SysBusDevice *dev) | ||||
| { | ||||
|     l2x0_state *s = FROM_SYSBUS(l2x0_state, dev); | ||||
| 
 | ||||
|     memory_region_init_io(&s->iomem, &l2x0_mem_ops, s, "l2x0_cc", 0x1000); | ||||
|     sysbus_init_mmio(dev, &s->iomem); | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static SysBusDeviceInfo l2x0_info = { | ||||
|     .init = l2x0_priv_init, | ||||
|     .qdev.name = "l2x0", | ||||
|     .qdev.size = sizeof(l2x0_state), | ||||
|     .qdev.vmsd = &vmstate_l2x0, | ||||
|     .qdev.no_user = 1, | ||||
|     .qdev.props = (Property[]) { | ||||
|         DEFINE_PROP_UINT32("type", l2x0_state, cache_type, 0x1c100100), | ||||
|         DEFINE_PROP_END_OF_LIST(), | ||||
|     }, | ||||
|     .qdev.reset = l2x0_priv_reset, | ||||
| }; | ||||
| 
 | ||||
| static void l2x0_register_device(void) | ||||
| { | ||||
|     sysbus_register_withprop(&l2x0_info); | ||||
| } | ||||
| 
 | ||||
| device_init(l2x0_register_device) | ||||
| @ -9,6 +9,8 @@ | ||||
| 
 | ||||
| #include "sysbus.h" | ||||
| #include "qemu-timer.h" | ||||
| #include "qemu-common.h" | ||||
| #include "qdev.h" | ||||
| 
 | ||||
| /* Common timer implementation.  */ | ||||
| 
 | ||||
| @ -178,6 +180,7 @@ typedef struct { | ||||
|     SysBusDevice busdev; | ||||
|     MemoryRegion iomem; | ||||
|     arm_timer_state *timer[2]; | ||||
|     uint32_t freq0, freq1; | ||||
|     int level[2]; | ||||
|     qemu_irq irq; | ||||
| } sp804_state; | ||||
| @ -269,10 +272,11 @@ static int sp804_init(SysBusDevice *dev) | ||||
| 
 | ||||
|     qi = qemu_allocate_irqs(sp804_set_irq, s, 2); | ||||
|     sysbus_init_irq(dev, &s->irq); | ||||
|     /* ??? The timers are actually configurable between 32kHz and 1MHz, but
 | ||||
|        we don't implement that.  */ | ||||
|     s->timer[0] = arm_timer_init(1000000); | ||||
|     s->timer[1] = arm_timer_init(1000000); | ||||
|     /* The timers are configurable between 32kHz and 1MHz
 | ||||
|      * defaulting to 1MHz but overrideable as individual properties */ | ||||
|     s->timer[0] = arm_timer_init(s->freq0); | ||||
|     s->timer[1] = arm_timer_init(s->freq1); | ||||
| 
 | ||||
|     s->timer[0]->irq = qi[0]; | ||||
|     s->timer[1]->irq = qi[1]; | ||||
|     memory_region_init_io(&s->iomem, &sp804_ops, s, "sp804", 0x1000); | ||||
| @ -281,6 +285,16 @@ static int sp804_init(SysBusDevice *dev) | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static SysBusDeviceInfo sp804_info = { | ||||
|     .init = sp804_init, | ||||
|     .qdev.name = "sp804", | ||||
|     .qdev.size = sizeof(sp804_state), | ||||
|     .qdev.props = (Property[]) { | ||||
|         DEFINE_PROP_UINT32("freq0", sp804_state, freq0, 1000000), | ||||
|         DEFINE_PROP_UINT32("freq1", sp804_state, freq1, 1000000), | ||||
|         DEFINE_PROP_END_OF_LIST(), | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| /* Integrator/CP timer module.  */ | ||||
| 
 | ||||
| @ -349,7 +363,7 @@ static int icp_pit_init(SysBusDevice *dev) | ||||
| static void arm_timer_register_devices(void) | ||||
| { | ||||
|     sysbus_register_dev("integrator_pit", sizeof(icp_pit_state), icp_pit_init); | ||||
|     sysbus_register_dev("sp804", sizeof(sp804_state), sp804_init); | ||||
|     sysbus_register_withprop(&sp804_info); | ||||
| } | ||||
| 
 | ||||
| device_init(arm_timer_register_devices) | ||||
|  | ||||
							
								
								
									
										28
									
								
								hw/omap.h
									
									
									
									
									
								
							
							
						
						
									
										28
									
								
								hw/omap.h
									
									
									
									
									
								
							| @ -672,10 +672,6 @@ void omap_uart_reset(struct omap_uart_s *s); | ||||
| void omap_uart_attach(struct omap_uart_s *s, CharDriverState *chr); | ||||
| 
 | ||||
| struct omap_mpuio_s; | ||||
| struct omap_mpuio_s *omap_mpuio_init(MemoryRegion *system_memory, | ||||
|                 target_phys_addr_t base, | ||||
|                 qemu_irq kbd_int, qemu_irq gpio_int, qemu_irq wakeup, | ||||
|                 omap_clk clk); | ||||
| qemu_irq *omap_mpuio_in_get(struct omap_mpuio_s *s); | ||||
| void omap_mpuio_out_set(struct omap_mpuio_s *s, int line, qemu_irq handler); | ||||
| void omap_mpuio_key(struct omap_mpuio_s *s, int row, int col, int down); | ||||
| @ -833,8 +829,6 @@ struct omap_mpu_state_s { | ||||
|     MemoryRegion tcmi_iomem; | ||||
|     MemoryRegion clkm_iomem; | ||||
|     MemoryRegion clkdsp_iomem; | ||||
|     MemoryRegion pwl_iomem; | ||||
|     MemoryRegion pwt_iomem; | ||||
|     MemoryRegion mpui_io_iomem; | ||||
|     MemoryRegion tap_iomem; | ||||
|     MemoryRegion imif_ram; | ||||
| @ -871,20 +865,8 @@ struct omap_mpu_state_s { | ||||
| 
 | ||||
|     struct omap_uwire_s *microwire; | ||||
| 
 | ||||
|     struct { | ||||
|         uint8_t output; | ||||
|         uint8_t level; | ||||
|         uint8_t enable; | ||||
|         int clk; | ||||
|     } pwl; | ||||
| 
 | ||||
|     struct { | ||||
|         uint8_t frc; | ||||
|         uint8_t vrc; | ||||
|         uint8_t gcr; | ||||
|         omap_clk clk; | ||||
|     } pwt; | ||||
| 
 | ||||
|     struct omap_pwl_s *pwl; | ||||
|     struct omap_pwt_s *pwt; | ||||
|     struct omap_i2c_s *i2c[2]; | ||||
| 
 | ||||
|     struct omap_rtc_s *rtc; | ||||
| @ -922,11 +904,7 @@ struct omap_mpu_state_s { | ||||
| 
 | ||||
|     uint32_t tcmi_regs[17]; | ||||
| 
 | ||||
|     struct dpll_ctl_s { | ||||
|         MemoryRegion iomem; | ||||
|         uint16_t mode; | ||||
|         omap_clk dpll; | ||||
|     } dpll[3]; | ||||
|     struct dpll_ctl_s *dpll[3]; | ||||
| 
 | ||||
|     omap_clk clks; | ||||
|     struct { | ||||
|  | ||||
							
								
								
									
										153
									
								
								hw/omap1.c
									
									
									
									
									
								
							
							
						
						
									
										153
									
								
								hw/omap1.c
									
									
									
									
									
								
							| @ -20,11 +20,7 @@ | ||||
| #include "arm-misc.h" | ||||
| #include "omap.h" | ||||
| #include "sysemu.h" | ||||
| #include "qemu-timer.h" | ||||
| #include "qemu-char.h" | ||||
| #include "soc_dma.h" | ||||
| /* We use pc-style serial ports.  */ | ||||
| #include "pc.h" | ||||
| #include "blockdev.h" | ||||
| #include "range.h" | ||||
| #include "sysbus.h" | ||||
| @ -1344,6 +1340,12 @@ static void omap_tcmi_init(MemoryRegion *memory, target_phys_addr_t base, | ||||
| } | ||||
| 
 | ||||
| /* Digital phase-locked loops control */ | ||||
| struct dpll_ctl_s { | ||||
|     MemoryRegion iomem; | ||||
|     uint16_t mode; | ||||
|     omap_clk dpll; | ||||
| }; | ||||
| 
 | ||||
| static uint64_t omap_dpll_read(void *opaque, target_phys_addr_t addr, | ||||
|                                unsigned size) | ||||
| { | ||||
| @ -1409,15 +1411,17 @@ static void omap_dpll_reset(struct dpll_ctl_s *s) | ||||
|     omap_clk_setrate(s->dpll, 1, 1); | ||||
| } | ||||
| 
 | ||||
| static void omap_dpll_init(MemoryRegion *memory, struct dpll_ctl_s *s, | ||||
| static struct dpll_ctl_s  *omap_dpll_init(MemoryRegion *memory, | ||||
|                            target_phys_addr_t base, omap_clk clk) | ||||
| { | ||||
|     struct dpll_ctl_s *s = g_malloc0(sizeof(*s)); | ||||
|     memory_region_init_io(&s->iomem, &omap_dpll_ops, s, "omap-dpll", 0x100); | ||||
| 
 | ||||
|     s->dpll = clk; | ||||
|     omap_dpll_reset(s); | ||||
| 
 | ||||
|     memory_region_add_subregion(memory, base, &s->iomem); | ||||
|     return s; | ||||
| } | ||||
| 
 | ||||
| /* MPU Clock/Reset/Power Mode Control */ | ||||
| @ -2066,7 +2070,7 @@ static void omap_mpuio_onoff(void *opaque, int line, int on) | ||||
|         omap_mpuio_kbd_update(s); | ||||
| } | ||||
| 
 | ||||
| struct omap_mpuio_s *omap_mpuio_init(MemoryRegion *memory, | ||||
| static struct omap_mpuio_s *omap_mpuio_init(MemoryRegion *memory, | ||||
|                 target_phys_addr_t base, | ||||
|                 qemu_irq kbd_int, qemu_irq gpio_int, qemu_irq wakeup, | ||||
|                 omap_clk clk) | ||||
| @ -2289,12 +2293,20 @@ void omap_uwire_attach(struct omap_uwire_s *s, | ||||
| } | ||||
| 
 | ||||
| /* Pseudonoise Pulse-Width Light Modulator */ | ||||
| static void omap_pwl_update(struct omap_mpu_state_s *s) | ||||
| { | ||||
|     int output = (s->pwl.clk && s->pwl.enable) ? s->pwl.level : 0; | ||||
| struct omap_pwl_s { | ||||
|     MemoryRegion iomem; | ||||
|     uint8_t output; | ||||
|     uint8_t level; | ||||
|     uint8_t enable; | ||||
|     int clk; | ||||
| }; | ||||
| 
 | ||||
|     if (output != s->pwl.output) { | ||||
|         s->pwl.output = output; | ||||
| static void omap_pwl_update(struct omap_pwl_s *s) | ||||
| { | ||||
|     int output = (s->clk && s->enable) ? s->level : 0; | ||||
| 
 | ||||
|     if (output != s->output) { | ||||
|         s->output = output; | ||||
|         printf("%s: Backlight now at %i/256\n", __FUNCTION__, output); | ||||
|     } | ||||
| } | ||||
| @ -2302,7 +2314,7 @@ static void omap_pwl_update(struct omap_mpu_state_s *s) | ||||
| static uint64_t omap_pwl_read(void *opaque, target_phys_addr_t addr, | ||||
|                               unsigned size) | ||||
| { | ||||
|     struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque; | ||||
|     struct omap_pwl_s *s = (struct omap_pwl_s *) opaque; | ||||
|     int offset = addr & OMAP_MPUI_REG_MASK; | ||||
| 
 | ||||
|     if (size != 1) { | ||||
| @ -2311,9 +2323,9 @@ static uint64_t omap_pwl_read(void *opaque, target_phys_addr_t addr, | ||||
| 
 | ||||
|     switch (offset) { | ||||
|     case 0x00:	/* PWL_LEVEL */ | ||||
|         return s->pwl.level; | ||||
|         return s->level; | ||||
|     case 0x04:	/* PWL_CTRL */ | ||||
|         return s->pwl.enable; | ||||
|         return s->enable; | ||||
|     } | ||||
|     OMAP_BAD_REG(addr); | ||||
|     return 0; | ||||
| @ -2322,7 +2334,7 @@ static uint64_t omap_pwl_read(void *opaque, target_phys_addr_t addr, | ||||
| static void omap_pwl_write(void *opaque, target_phys_addr_t addr, | ||||
|                            uint64_t value, unsigned size) | ||||
| { | ||||
|     struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque; | ||||
|     struct omap_pwl_s *s = (struct omap_pwl_s *) opaque; | ||||
|     int offset = addr & OMAP_MPUI_REG_MASK; | ||||
| 
 | ||||
|     if (size != 1) { | ||||
| @ -2331,11 +2343,11 @@ static void omap_pwl_write(void *opaque, target_phys_addr_t addr, | ||||
| 
 | ||||
|     switch (offset) { | ||||
|     case 0x00:	/* PWL_LEVEL */ | ||||
|         s->pwl.level = value; | ||||
|         s->level = value; | ||||
|         omap_pwl_update(s); | ||||
|         break; | ||||
|     case 0x04:	/* PWL_CTRL */ | ||||
|         s->pwl.enable = value & 1; | ||||
|         s->enable = value & 1; | ||||
|         omap_pwl_update(s); | ||||
|         break; | ||||
|     default: | ||||
| @ -2350,41 +2362,52 @@ static const MemoryRegionOps omap_pwl_ops = { | ||||
|     .endianness = DEVICE_NATIVE_ENDIAN, | ||||
| }; | ||||
| 
 | ||||
| static void omap_pwl_reset(struct omap_mpu_state_s *s) | ||||
| static void omap_pwl_reset(struct omap_pwl_s *s) | ||||
| { | ||||
|     s->pwl.output = 0; | ||||
|     s->pwl.level = 0; | ||||
|     s->pwl.enable = 0; | ||||
|     s->pwl.clk = 1; | ||||
|     s->output = 0; | ||||
|     s->level = 0; | ||||
|     s->enable = 0; | ||||
|     s->clk = 1; | ||||
|     omap_pwl_update(s); | ||||
| } | ||||
| 
 | ||||
| static void omap_pwl_clk_update(void *opaque, int line, int on) | ||||
| { | ||||
|     struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque; | ||||
|     struct omap_pwl_s *s = (struct omap_pwl_s *) opaque; | ||||
| 
 | ||||
|     s->pwl.clk = on; | ||||
|     s->clk = on; | ||||
|     omap_pwl_update(s); | ||||
| } | ||||
| 
 | ||||
| static void omap_pwl_init(MemoryRegion *system_memory, | ||||
|                 target_phys_addr_t base, struct omap_mpu_state_s *s, | ||||
|                 omap_clk clk) | ||||
| static struct omap_pwl_s *omap_pwl_init(MemoryRegion *system_memory, | ||||
|                                         target_phys_addr_t base, | ||||
|                                         omap_clk clk) | ||||
| { | ||||
|     struct omap_pwl_s *s = g_malloc0(sizeof(*s)); | ||||
| 
 | ||||
|     omap_pwl_reset(s); | ||||
| 
 | ||||
|     memory_region_init_io(&s->pwl_iomem, &omap_pwl_ops, s, | ||||
|     memory_region_init_io(&s->iomem, &omap_pwl_ops, s, | ||||
|                           "omap-pwl", 0x800); | ||||
|     memory_region_add_subregion(system_memory, base, &s->pwl_iomem); | ||||
|     memory_region_add_subregion(system_memory, base, &s->iomem); | ||||
| 
 | ||||
|     omap_clk_adduser(clk, qemu_allocate_irqs(omap_pwl_clk_update, s, 1)[0]); | ||||
|     return s; | ||||
| } | ||||
| 
 | ||||
| /* Pulse-Width Tone module */ | ||||
| struct omap_pwt_s { | ||||
|     MemoryRegion iomem; | ||||
|     uint8_t frc; | ||||
|     uint8_t vrc; | ||||
|     uint8_t gcr; | ||||
|     omap_clk clk; | ||||
| }; | ||||
| 
 | ||||
| static uint64_t omap_pwt_read(void *opaque, target_phys_addr_t addr, | ||||
|                               unsigned size) | ||||
| { | ||||
|     struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque; | ||||
|     struct omap_pwt_s *s = (struct omap_pwt_s *) opaque; | ||||
|     int offset = addr & OMAP_MPUI_REG_MASK; | ||||
| 
 | ||||
|     if (size != 1) { | ||||
| @ -2393,11 +2416,11 @@ static uint64_t omap_pwt_read(void *opaque, target_phys_addr_t addr, | ||||
| 
 | ||||
|     switch (offset) { | ||||
|     case 0x00:	/* FRC */ | ||||
|         return s->pwt.frc; | ||||
|         return s->frc; | ||||
|     case 0x04:	/* VCR */ | ||||
|         return s->pwt.vrc; | ||||
|         return s->vrc; | ||||
|     case 0x08:	/* GCR */ | ||||
|         return s->pwt.gcr; | ||||
|         return s->gcr; | ||||
|     } | ||||
|     OMAP_BAD_REG(addr); | ||||
|     return 0; | ||||
| @ -2406,7 +2429,7 @@ static uint64_t omap_pwt_read(void *opaque, target_phys_addr_t addr, | ||||
| static void omap_pwt_write(void *opaque, target_phys_addr_t addr, | ||||
|                            uint64_t value, unsigned size) | ||||
| { | ||||
|     struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque; | ||||
|     struct omap_pwt_s *s = (struct omap_pwt_s *) opaque; | ||||
|     int offset = addr & OMAP_MPUI_REG_MASK; | ||||
| 
 | ||||
|     if (size != 1) { | ||||
| @ -2415,16 +2438,16 @@ static void omap_pwt_write(void *opaque, target_phys_addr_t addr, | ||||
| 
 | ||||
|     switch (offset) { | ||||
|     case 0x00:	/* FRC */ | ||||
|         s->pwt.frc = value & 0x3f; | ||||
|         s->frc = value & 0x3f; | ||||
|         break; | ||||
|     case 0x04:	/* VRC */ | ||||
|         if ((value ^ s->pwt.vrc) & 1) { | ||||
|         if ((value ^ s->vrc) & 1) { | ||||
|             if (value & 1) | ||||
|                 printf("%s: %iHz buzz on\n", __FUNCTION__, (int) | ||||
|                                 /* 1.5 MHz from a 12-MHz or 13-MHz PWT_CLK */ | ||||
|                                 ((omap_clk_getrate(s->pwt.clk) >> 3) / | ||||
|                                 ((omap_clk_getrate(s->clk) >> 3) / | ||||
|                                  /* Pre-multiplexer divider */ | ||||
|                                  ((s->pwt.gcr & 2) ? 1 : 154) / | ||||
|                                  ((s->gcr & 2) ? 1 : 154) / | ||||
|                                  /* Octave multiplexer */ | ||||
|                                  (2 << (value & 3)) * | ||||
|                                  /* 101/107 divider */ | ||||
| @ -2439,10 +2462,10 @@ static void omap_pwt_write(void *opaque, target_phys_addr_t addr, | ||||
|             else | ||||
|                 printf("%s: silence!\n", __FUNCTION__); | ||||
|         } | ||||
|         s->pwt.vrc = value & 0x7f; | ||||
|         s->vrc = value & 0x7f; | ||||
|         break; | ||||
|     case 0x08:	/* GCR */ | ||||
|         s->pwt.gcr = value & 3; | ||||
|         s->gcr = value & 3; | ||||
|         break; | ||||
|     default: | ||||
|         OMAP_BAD_REG(addr); | ||||
| @ -2456,23 +2479,25 @@ static const MemoryRegionOps omap_pwt_ops = { | ||||
|     .endianness = DEVICE_NATIVE_ENDIAN, | ||||
| }; | ||||
| 
 | ||||
| static void omap_pwt_reset(struct omap_mpu_state_s *s) | ||||
| static void omap_pwt_reset(struct omap_pwt_s *s) | ||||
| { | ||||
|     s->pwt.frc = 0; | ||||
|     s->pwt.vrc = 0; | ||||
|     s->pwt.gcr = 0; | ||||
|     s->frc = 0; | ||||
|     s->vrc = 0; | ||||
|     s->gcr = 0; | ||||
| } | ||||
| 
 | ||||
| static void omap_pwt_init(MemoryRegion *system_memory, | ||||
|                 target_phys_addr_t base, struct omap_mpu_state_s *s, | ||||
|                 omap_clk clk) | ||||
| static struct omap_pwt_s *omap_pwt_init(MemoryRegion *system_memory, | ||||
|                                         target_phys_addr_t base, | ||||
|                                         omap_clk clk) | ||||
| { | ||||
|     s->pwt.clk = clk; | ||||
|     struct omap_pwt_s *s = g_malloc0(sizeof(*s)); | ||||
|     s->clk = clk; | ||||
|     omap_pwt_reset(s); | ||||
| 
 | ||||
|     memory_region_init_io(&s->pwt_iomem, &omap_pwt_ops, s, | ||||
|     memory_region_init_io(&s->iomem, &omap_pwt_ops, s, | ||||
|                           "omap-pwt", 0x800); | ||||
|     memory_region_add_subregion(system_memory, base, &s->pwt_iomem); | ||||
|     memory_region_add_subregion(system_memory, base, &s->iomem); | ||||
|     return s; | ||||
| } | ||||
| 
 | ||||
| /* Real-time Clock module */ | ||||
| @ -3658,17 +3683,17 @@ static void omap1_mpu_reset(void *opaque) | ||||
|     omap_mpui_reset(mpu); | ||||
|     omap_tipb_bridge_reset(mpu->private_tipb); | ||||
|     omap_tipb_bridge_reset(mpu->public_tipb); | ||||
|     omap_dpll_reset(&mpu->dpll[0]); | ||||
|     omap_dpll_reset(&mpu->dpll[1]); | ||||
|     omap_dpll_reset(&mpu->dpll[2]); | ||||
|     omap_dpll_reset(mpu->dpll[0]); | ||||
|     omap_dpll_reset(mpu->dpll[1]); | ||||
|     omap_dpll_reset(mpu->dpll[2]); | ||||
|     omap_uart_reset(mpu->uart[0]); | ||||
|     omap_uart_reset(mpu->uart[1]); | ||||
|     omap_uart_reset(mpu->uart[2]); | ||||
|     omap_mmc_reset(mpu->mmc); | ||||
|     omap_mpuio_reset(mpu->mpuio); | ||||
|     omap_uwire_reset(mpu->microwire); | ||||
|     omap_pwl_reset(mpu); | ||||
|     omap_pwt_reset(mpu); | ||||
|     omap_pwl_reset(mpu->pwl); | ||||
|     omap_pwt_reset(mpu->pwt); | ||||
|     omap_i2c_reset(mpu->i2c[0]); | ||||
|     omap_rtc_reset(mpu->rtc); | ||||
|     omap_mcbsp_reset(mpu->mcbsp1); | ||||
| @ -3928,12 +3953,12 @@ struct omap_mpu_state_s *omap310_mpu_init(MemoryRegion *system_memory, | ||||
|                     "uart3", | ||||
|                     serial_hds[0] && serial_hds[1] ? serial_hds[2] : NULL); | ||||
| 
 | ||||
|     omap_dpll_init(system_memory, | ||||
|                    &s->dpll[0], 0xfffecf00, omap_findclk(s, "dpll1")); | ||||
|     omap_dpll_init(system_memory, | ||||
|                    &s->dpll[1], 0xfffed000, omap_findclk(s, "dpll2")); | ||||
|     omap_dpll_init(system_memory, | ||||
|                    &s->dpll[2], 0xfffed100, omap_findclk(s, "dpll3")); | ||||
|     s->dpll[0] = omap_dpll_init(system_memory, 0xfffecf00, | ||||
|                                 omap_findclk(s, "dpll1")); | ||||
|     s->dpll[1] = omap_dpll_init(system_memory, 0xfffed000, | ||||
|                                 omap_findclk(s, "dpll2")); | ||||
|     s->dpll[2] = omap_dpll_init(system_memory, 0xfffed100, | ||||
|                                 omap_findclk(s, "dpll3")); | ||||
| 
 | ||||
|     dinfo = drive_get(IF_SD, 0, 0); | ||||
|     if (!dinfo) { | ||||
| @ -3963,8 +3988,10 @@ struct omap_mpu_state_s *omap310_mpu_init(MemoryRegion *system_memory, | ||||
|                                    qdev_get_gpio_in(s->ih[1], OMAP_INT_uWireRX), | ||||
|                     s->drq[OMAP_DMA_UWIRE_TX], omap_findclk(s, "mpuper_ck")); | ||||
| 
 | ||||
|     omap_pwl_init(system_memory, 0xfffb5800, s, omap_findclk(s, "armxor_ck")); | ||||
|     omap_pwt_init(system_memory, 0xfffb6000, s, omap_findclk(s, "armxor_ck")); | ||||
|     s->pwl = omap_pwl_init(system_memory, 0xfffb5800, | ||||
|                            omap_findclk(s, "armxor_ck")); | ||||
|     s->pwt = omap_pwt_init(system_memory, 0xfffb6000, | ||||
|                            omap_findclk(s, "armxor_ck")); | ||||
| 
 | ||||
|     s->i2c[0] = omap_i2c_init(system_memory, 0xfffb3800, | ||||
|                               qdev_get_gpio_in(s->ih[1], OMAP_INT_I2C), | ||||
|  | ||||
| @ -443,6 +443,12 @@ void omap_gpmc_reset(struct omap_gpmc_s *s) | ||||
|     s->irqst = 0; | ||||
|     s->irqen = 0; | ||||
|     omap_gpmc_int_update(s); | ||||
|     for (i = 0; i < 8; i++) { | ||||
|         /* This has to happen before we change any of the config
 | ||||
|          * used to determine which memory regions are mapped or unmapped. | ||||
|          */ | ||||
|         omap_gpmc_cs_unmap(s, i); | ||||
|     } | ||||
|     s->timeout = 0; | ||||
|     s->config = 0xa00; | ||||
|     s->prefetch.config1 = 0x00004000; | ||||
| @ -451,7 +457,6 @@ void omap_gpmc_reset(struct omap_gpmc_s *s) | ||||
|     s->prefetch.fifopointer = 0; | ||||
|     s->prefetch.count = 0; | ||||
|     for (i = 0; i < 8; i ++) { | ||||
|         omap_gpmc_cs_unmap(s, i); | ||||
|         s->cs_file[i].config[1] = 0x101001; | ||||
|         s->cs_file[i].config[2] = 0x020201; | ||||
|         s->cs_file[i].config[3] = 0x10031003; | ||||
| @ -716,24 +721,31 @@ static void omap_gpmc_write(void *opaque, target_phys_addr_t addr, | ||||
| 
 | ||||
|     case 0x1e0:	/* GPMC_PREFETCH_CONFIG1 */ | ||||
|         if (!s->prefetch.startengine) { | ||||
|             uint32_t oldconfig1 = s->prefetch.config1; | ||||
|             uint32_t newconfig1 = value & 0x7f8f7fbf; | ||||
|             uint32_t changed; | ||||
|             s->prefetch.config1 = value & 0x7f8f7fbf; | ||||
|             changed = oldconfig1 ^ s->prefetch.config1; | ||||
|             changed = newconfig1 ^ s->prefetch.config1; | ||||
|             if (changed & (0x80 | 0x7000000)) { | ||||
|                 /* Turning the engine on or off, or mapping it somewhere else.
 | ||||
|                  * cs_map() and cs_unmap() check the prefetch config and | ||||
|                  * overall CSVALID bits, so it is sufficient to unmap-and-map | ||||
|                  * both the old cs and the new one. | ||||
|                  * both the old cs and the new one. Note that we adhere to | ||||
|                  * the "unmap/change config/map" order (and not unmap twice | ||||
|                  * if newcs == oldcs), otherwise we'll try to delete the wrong | ||||
|                  * memory region. | ||||
|                  */ | ||||
|                 int oldcs = prefetch_cs(oldconfig1); | ||||
|                 int newcs = prefetch_cs(s->prefetch.config1); | ||||
|                 int oldcs = prefetch_cs(s->prefetch.config1); | ||||
|                 int newcs = prefetch_cs(newconfig1); | ||||
|                 omap_gpmc_cs_unmap(s, oldcs); | ||||
|                 omap_gpmc_cs_map(s, oldcs); | ||||
|                 if (newcs != oldcs) { | ||||
|                 if (oldcs != newcs) { | ||||
|                     omap_gpmc_cs_unmap(s, newcs); | ||||
|                 } | ||||
|                 s->prefetch.config1 = newconfig1; | ||||
|                 omap_gpmc_cs_map(s, oldcs); | ||||
|                 if (oldcs != newcs) { | ||||
|                     omap_gpmc_cs_map(s, newcs); | ||||
|                 } | ||||
|             } else { | ||||
|                 s->prefetch.config1 = newconfig1; | ||||
|             } | ||||
|         } | ||||
|         break; | ||||
|  | ||||
							
								
								
									
										11
									
								
								hw/pl110.c
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								hw/pl110.c
									
									
									
									
									
								
							| @ -60,10 +60,13 @@ typedef struct { | ||||
|     qemu_irq irq; | ||||
| } pl110_state; | ||||
| 
 | ||||
| static int vmstate_pl110_post_load(void *opaque, int version_id); | ||||
| 
 | ||||
| static const VMStateDescription vmstate_pl110 = { | ||||
|     .name = "pl110", | ||||
|     .version_id = 2, | ||||
|     .minimum_version_id = 1, | ||||
|     .post_load = vmstate_pl110_post_load, | ||||
|     .fields = (VMStateField[]) { | ||||
|         VMSTATE_INT32(version, pl110_state), | ||||
|         VMSTATE_UINT32_ARRAY(timing, pl110_state, 4), | ||||
| @ -430,6 +433,14 @@ static void pl110_mux_ctrl_set(void *opaque, int line, int level) | ||||
|     s->mux_ctrl = level; | ||||
| } | ||||
| 
 | ||||
| static int vmstate_pl110_post_load(void *opaque, int version_id) | ||||
| { | ||||
|     pl110_state *s = opaque; | ||||
|     /* Make sure we redraw, and at the right size */ | ||||
|     pl110_invalidate_display(s); | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static int pl110_init(SysBusDevice *dev) | ||||
| { | ||||
|     pl110_state *s = FROM_SYSBUS(pl110_state, dev); | ||||
|  | ||||
							
								
								
									
										49
									
								
								hw/pl181.c
									
									
									
									
									
								
							
							
						
						
									
										49
									
								
								hw/pl181.c
									
									
									
									
									
								
							| @ -38,20 +38,45 @@ typedef struct { | ||||
|     uint32_t datacnt; | ||||
|     uint32_t status; | ||||
|     uint32_t mask[2]; | ||||
|     int fifo_pos; | ||||
|     int fifo_len; | ||||
|     int32_t fifo_pos; | ||||
|     int32_t fifo_len; | ||||
|     /* The linux 2.6.21 driver is buggy, and misbehaves if new data arrives
 | ||||
|        while it is reading the FIFO.  We hack around this be defering | ||||
|        subsequent transfers until after the driver polls the status word. | ||||
|        http://www.arm.linux.org.uk/developer/patches/viewpatch.php?id=4446/1
 | ||||
|      */ | ||||
|     int linux_hack; | ||||
|     int32_t linux_hack; | ||||
|     uint32_t fifo[PL181_FIFO_LEN]; | ||||
|     qemu_irq irq[2]; | ||||
|     /* GPIO outputs for 'card is readonly' and 'card inserted' */ | ||||
|     qemu_irq cardstatus[2]; | ||||
| } pl181_state; | ||||
| 
 | ||||
| static const VMStateDescription vmstate_pl181 = { | ||||
|     .name = "pl181", | ||||
|     .version_id = 1, | ||||
|     .minimum_version_id = 1, | ||||
|     .fields = (VMStateField[]) { | ||||
|         VMSTATE_UINT32(clock, pl181_state), | ||||
|         VMSTATE_UINT32(power, pl181_state), | ||||
|         VMSTATE_UINT32(cmdarg, pl181_state), | ||||
|         VMSTATE_UINT32(cmd, pl181_state), | ||||
|         VMSTATE_UINT32(datatimer, pl181_state), | ||||
|         VMSTATE_UINT32(datalength, pl181_state), | ||||
|         VMSTATE_UINT32(respcmd, pl181_state), | ||||
|         VMSTATE_UINT32_ARRAY(response, pl181_state, 4), | ||||
|         VMSTATE_UINT32(datactrl, pl181_state), | ||||
|         VMSTATE_UINT32(datacnt, pl181_state), | ||||
|         VMSTATE_UINT32(status, pl181_state), | ||||
|         VMSTATE_UINT32_ARRAY(mask, pl181_state, 2), | ||||
|         VMSTATE_INT32(fifo_pos, pl181_state), | ||||
|         VMSTATE_INT32(fifo_len, pl181_state), | ||||
|         VMSTATE_INT32(linux_hack, pl181_state), | ||||
|         VMSTATE_UINT32_ARRAY(fifo, pl181_state, PL181_FIFO_LEN), | ||||
|         VMSTATE_END_OF_LIST() | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| #define PL181_CMD_INDEX     0x3f | ||||
| #define PL181_CMD_RESPONSE  (1 << 6) | ||||
| #define PL181_CMD_LONGRESP  (1 << 7) | ||||
| @ -420,9 +445,9 @@ static const MemoryRegionOps pl181_ops = { | ||||
|     .endianness = DEVICE_NATIVE_ENDIAN, | ||||
| }; | ||||
| 
 | ||||
| static void pl181_reset(void *opaque) | ||||
| static void pl181_reset(DeviceState *d) | ||||
| { | ||||
|     pl181_state *s = (pl181_state *)opaque; | ||||
|     pl181_state *s = DO_UPCAST(pl181_state, busdev.qdev, d); | ||||
| 
 | ||||
|     s->power = 0; | ||||
|     s->cmdarg = 0; | ||||
| @ -459,15 +484,21 @@ static int pl181_init(SysBusDevice *dev) | ||||
|     qdev_init_gpio_out(&s->busdev.qdev, s->cardstatus, 2); | ||||
|     dinfo = drive_get_next(IF_SD); | ||||
|     s->card = sd_init(dinfo ? dinfo->bdrv : NULL, 0); | ||||
|     qemu_register_reset(pl181_reset, s); | ||||
|     pl181_reset(s); | ||||
|     /* ??? Save/restore.  */ | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static SysBusDeviceInfo pl181_info = { | ||||
|     .init = pl181_init, | ||||
|     .qdev.name = "pl181", | ||||
|     .qdev.size = sizeof(pl181_state), | ||||
|     .qdev.vmsd = &vmstate_pl181, | ||||
|     .qdev.reset = pl181_reset, | ||||
|     .qdev.no_user = 1, | ||||
| }; | ||||
| 
 | ||||
| static void pl181_register_devices(void) | ||||
| { | ||||
|     sysbus_register_dev("pl181", sizeof(pl181_state), pl181_init); | ||||
|     sysbus_register_withprop(&pl181_info); | ||||
| } | ||||
| 
 | ||||
| device_init(pl181_register_devices) | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Anthony Liguori
						Anthony Liguori