Commit d8f699cb32c8c418b65aa6a2c252e097ae4716ae
1 parent
bfa30a38
Zeroing ITR shouldn't ack irq zero.
Fix PWT & PWL clocks, fix user refcounting for clocks, add 'hsab_ck' and 'usb_w2fc_ck'. Fix TCMI register addresses. Implement OMAP McBSP controller and connection to I2S-compatible CODECs. Add audio support for TSC2102 as an I2S CODEC. Connect TSC2102 I2S interface to CPU's McBSP1 interface in the Palm Tungsten|E. Correct '>' instead of '>>' typos. Implement GPIO PIN_CONTROL register (not in OMAP310 TRM, from OMAP1510). git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3534 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
6 changed files
with
858 additions
and
61 deletions
hw/omap.c
| ... | ... | @@ -254,7 +254,7 @@ static void omap_inth_write(void *opaque, target_phys_addr_t addr, |
| 254 | 254 | |
| 255 | 255 | switch (offset) { |
| 256 | 256 | case 0x00: /* ITR */ |
| 257 | - s->irqs &= value; | |
| 257 | + s->irqs &= value | 1; | |
| 258 | 258 | omap_inth_sir_update(s); |
| 259 | 259 | omap_inth_update(s); |
| 260 | 260 | return; |
| ... | ... | @@ -992,7 +992,7 @@ static void omap_dma_clk_update(void *opaque, int line, int on) |
| 992 | 992 | struct omap_dma_s *s = (struct omap_dma_s *) opaque; |
| 993 | 993 | |
| 994 | 994 | if (on) { |
| 995 | - s->delay = ticks_per_sec >> 5; | |
| 995 | + s->delay = ticks_per_sec >> 7; | |
| 996 | 996 | if (s->run_count) |
| 997 | 997 | qemu_mod_timer(s->tm, qemu_get_clock(vm_clock) + s->delay); |
| 998 | 998 | } else { |
| ... | ... | @@ -1325,8 +1325,10 @@ static void omap_wd_timer_write(void *opaque, target_phys_addr_t addr, |
| 1325 | 1325 | s->mode |= (value >> 15) & 1; |
| 1326 | 1326 | if (s->last_wr == 0xf5) { |
| 1327 | 1327 | if ((value & 0xff) == 0xa0) { |
| 1328 | - s->mode = 0; | |
| 1329 | - omap_clk_put(s->timer.clk); | |
| 1328 | + if (s->mode) { | |
| 1329 | + s->mode = 0; | |
| 1330 | + omap_clk_put(s->timer.clk); | |
| 1331 | + } | |
| 1330 | 1332 | } else { |
| 1331 | 1333 | /* XXX: on T|E hardware somehow this has no effect, |
| 1332 | 1334 | * on Zire 71 it works as specified. */ |
| ... | ... | @@ -2217,23 +2219,23 @@ static uint32_t omap_tcmi_read(void *opaque, target_phys_addr_t addr) |
| 2217 | 2219 | uint32_t ret; |
| 2218 | 2220 | |
| 2219 | 2221 | switch (offset) { |
| 2220 | - case 0xfffecc00: /* IMIF_PRIO */ | |
| 2221 | - case 0xfffecc04: /* EMIFS_PRIO */ | |
| 2222 | - case 0xfffecc08: /* EMIFF_PRIO */ | |
| 2223 | - case 0xfffecc0c: /* EMIFS_CONFIG */ | |
| 2224 | - case 0xfffecc10: /* EMIFS_CS0_CONFIG */ | |
| 2225 | - case 0xfffecc14: /* EMIFS_CS1_CONFIG */ | |
| 2226 | - case 0xfffecc18: /* EMIFS_CS2_CONFIG */ | |
| 2227 | - case 0xfffecc1c: /* EMIFS_CS3_CONFIG */ | |
| 2228 | - case 0xfffecc24: /* EMIFF_MRS */ | |
| 2229 | - case 0xfffecc28: /* TIMEOUT1 */ | |
| 2230 | - case 0xfffecc2c: /* TIMEOUT2 */ | |
| 2231 | - case 0xfffecc30: /* TIMEOUT3 */ | |
| 2232 | - case 0xfffecc3c: /* EMIFF_SDRAM_CONFIG_2 */ | |
| 2233 | - case 0xfffecc40: /* EMIFS_CFG_DYN_WAIT */ | |
| 2222 | + case 0x00: /* IMIF_PRIO */ | |
| 2223 | + case 0x04: /* EMIFS_PRIO */ | |
| 2224 | + case 0x08: /* EMIFF_PRIO */ | |
| 2225 | + case 0x0c: /* EMIFS_CONFIG */ | |
| 2226 | + case 0x10: /* EMIFS_CS0_CONFIG */ | |
| 2227 | + case 0x14: /* EMIFS_CS1_CONFIG */ | |
| 2228 | + case 0x18: /* EMIFS_CS2_CONFIG */ | |
| 2229 | + case 0x1c: /* EMIFS_CS3_CONFIG */ | |
| 2230 | + case 0x24: /* EMIFF_MRS */ | |
| 2231 | + case 0x28: /* TIMEOUT1 */ | |
| 2232 | + case 0x2c: /* TIMEOUT2 */ | |
| 2233 | + case 0x30: /* TIMEOUT3 */ | |
| 2234 | + case 0x3c: /* EMIFF_SDRAM_CONFIG_2 */ | |
| 2235 | + case 0x40: /* EMIFS_CFG_DYN_WAIT */ | |
| 2234 | 2236 | return s->tcmi_regs[offset >> 2]; |
| 2235 | 2237 | |
| 2236 | - case 0xfffecc20: /* EMIFF_SDRAM_CONFIG */ | |
| 2238 | + case 0x20: /* EMIFF_SDRAM_CONFIG */ | |
| 2237 | 2239 | ret = s->tcmi_regs[offset >> 2]; |
| 2238 | 2240 | s->tcmi_regs[offset >> 2] &= ~1; /* XXX: Clear SLRF on SDRAM access */ |
| 2239 | 2241 | /* XXX: We can try using the VGA_DIRTY flag for this */ |
| ... | ... | @@ -2251,23 +2253,23 @@ static void omap_tcmi_write(void *opaque, target_phys_addr_t addr, |
| 2251 | 2253 | int offset = addr - s->tcmi_base; |
| 2252 | 2254 | |
| 2253 | 2255 | switch (offset) { |
| 2254 | - case 0xfffecc00: /* IMIF_PRIO */ | |
| 2255 | - case 0xfffecc04: /* EMIFS_PRIO */ | |
| 2256 | - case 0xfffecc08: /* EMIFF_PRIO */ | |
| 2257 | - case 0xfffecc10: /* EMIFS_CS0_CONFIG */ | |
| 2258 | - case 0xfffecc14: /* EMIFS_CS1_CONFIG */ | |
| 2259 | - case 0xfffecc18: /* EMIFS_CS2_CONFIG */ | |
| 2260 | - case 0xfffecc1c: /* EMIFS_CS3_CONFIG */ | |
| 2261 | - case 0xfffecc20: /* EMIFF_SDRAM_CONFIG */ | |
| 2262 | - case 0xfffecc24: /* EMIFF_MRS */ | |
| 2263 | - case 0xfffecc28: /* TIMEOUT1 */ | |
| 2264 | - case 0xfffecc2c: /* TIMEOUT2 */ | |
| 2265 | - case 0xfffecc30: /* TIMEOUT3 */ | |
| 2266 | - case 0xfffecc3c: /* EMIFF_SDRAM_CONFIG_2 */ | |
| 2267 | - case 0xfffecc40: /* EMIFS_CFG_DYN_WAIT */ | |
| 2256 | + case 0x00: /* IMIF_PRIO */ | |
| 2257 | + case 0x04: /* EMIFS_PRIO */ | |
| 2258 | + case 0x08: /* EMIFF_PRIO */ | |
| 2259 | + case 0x10: /* EMIFS_CS0_CONFIG */ | |
| 2260 | + case 0x14: /* EMIFS_CS1_CONFIG */ | |
| 2261 | + case 0x18: /* EMIFS_CS2_CONFIG */ | |
| 2262 | + case 0x1c: /* EMIFS_CS3_CONFIG */ | |
| 2263 | + case 0x20: /* EMIFF_SDRAM_CONFIG */ | |
| 2264 | + case 0x24: /* EMIFF_MRS */ | |
| 2265 | + case 0x28: /* TIMEOUT1 */ | |
| 2266 | + case 0x2c: /* TIMEOUT2 */ | |
| 2267 | + case 0x30: /* TIMEOUT3 */ | |
| 2268 | + case 0x3c: /* EMIFF_SDRAM_CONFIG_2 */ | |
| 2269 | + case 0x40: /* EMIFS_CFG_DYN_WAIT */ | |
| 2268 | 2270 | s->tcmi_regs[offset >> 2] = value; |
| 2269 | 2271 | break; |
| 2270 | - case 0xfffecc0c: /* EMIFS_CONFIG */ | |
| 2272 | + case 0x0c: /* EMIFS_CONFIG */ | |
| 2271 | 2273 | s->tcmi_regs[offset >> 2] = (value & 0xf) | (1 << 4); |
| 2272 | 2274 | break; |
| 2273 | 2275 | |
| ... | ... | @@ -2441,7 +2443,7 @@ static uint32_t omap_clkm_read(void *opaque, target_phys_addr_t addr) |
| 2441 | 2443 | return s->clkm.arm_rstct2; |
| 2442 | 2444 | |
| 2443 | 2445 | case 0x18: /* ARM_SYSST */ |
| 2444 | - return (s->clkm.clocking_scheme < 11) | s->clkm.cold_start; | |
| 2446 | + return (s->clkm.clocking_scheme << 11) | s->clkm.cold_start; | |
| 2445 | 2447 | |
| 2446 | 2448 | case 0x1c: /* ARM_CKOUT1 */ |
| 2447 | 2449 | return s->clkm.arm_ckout1; |
| ... | ... | @@ -2720,7 +2722,7 @@ static uint32_t omap_clkdsp_read(void *opaque, target_phys_addr_t addr) |
| 2720 | 2722 | return s->clkm.dsp_rstct2; |
| 2721 | 2723 | |
| 2722 | 2724 | case 0x18: /* DSP_SYSST */ |
| 2723 | - return (s->clkm.clocking_scheme < 11) | s->clkm.cold_start | | |
| 2725 | + return (s->clkm.clocking_scheme << 11) | s->clkm.cold_start | | |
| 2724 | 2726 | (s->env->halted << 6); /* Quite useless... */ |
| 2725 | 2727 | } |
| 2726 | 2728 | |
| ... | ... | @@ -2796,9 +2798,9 @@ static void omap_clkm_reset(struct omap_mpu_state_s *s) |
| 2796 | 2798 | s->clkm.clocking_scheme = 0; |
| 2797 | 2799 | omap_clkm_ckctl_update(s, ~0, 0x3000); |
| 2798 | 2800 | s->clkm.arm_ckctl = 0x3000; |
| 2799 | - omap_clkm_idlect1_update(s, s->clkm.arm_idlect1 & 0x0400, 0x0400); | |
| 2801 | + omap_clkm_idlect1_update(s, s->clkm.arm_idlect1 ^ 0x0400, 0x0400); | |
| 2800 | 2802 | s->clkm.arm_idlect1 = 0x0400; |
| 2801 | - omap_clkm_idlect2_update(s, s->clkm.arm_idlect2 & 0x0100, 0x0100); | |
| 2803 | + omap_clkm_idlect2_update(s, s->clkm.arm_idlect2 ^ 0x0100, 0x0100); | |
| 2802 | 2804 | s->clkm.arm_idlect2 = 0x0100; |
| 2803 | 2805 | s->clkm.arm_ewupct = 0x003f; |
| 2804 | 2806 | s->clkm.arm_rstct1 = 0x0000; |
| ... | ... | @@ -2822,8 +2824,11 @@ static void omap_clkm_init(target_phys_addr_t mpu_base, |
| 2822 | 2824 | |
| 2823 | 2825 | s->clkm.mpu_base = mpu_base; |
| 2824 | 2826 | s->clkm.dsp_base = dsp_base; |
| 2825 | - s->clkm.cold_start = 0x3a; | |
| 2827 | + s->clkm.arm_idlect1 = 0x03ff; | |
| 2828 | + s->clkm.arm_idlect2 = 0x0100; | |
| 2829 | + s->clkm.dsp_idlect1 = 0x0002; | |
| 2826 | 2830 | omap_clkm_reset(s); |
| 2831 | + s->clkm.cold_start = 0x3a; | |
| 2827 | 2832 | |
| 2828 | 2833 | cpu_register_physical_memory(s->clkm.mpu_base, 0x100, iomemtype[0]); |
| 2829 | 2834 | cpu_register_physical_memory(s->clkm.dsp_base, 0x1000, iomemtype[1]); |
| ... | ... | @@ -2956,9 +2961,8 @@ static void omap_mpuio_write(void *opaque, target_phys_addr_t addr, |
| 2956 | 2961 | |
| 2957 | 2962 | switch (offset) { |
| 2958 | 2963 | case 0x04: /* OUTPUT_REG */ |
| 2959 | - diff = s->outputs ^ (value & ~s->dir); | |
| 2964 | + diff = (s->outputs ^ value) & ~s->dir; | |
| 2960 | 2965 | s->outputs = value; |
| 2961 | - value &= ~s->dir; | |
| 2962 | 2966 | while ((ln = ffs(diff))) { |
| 2963 | 2967 | ln --; |
| 2964 | 2968 | if (s->handler[ln]) |
| ... | ... | @@ -3120,6 +3124,7 @@ struct omap_gpio_s { |
| 3120 | 3124 | uint16_t edge; |
| 3121 | 3125 | uint16_t mask; |
| 3122 | 3126 | uint16_t ints; |
| 3127 | + uint16_t pins; | |
| 3123 | 3128 | }; |
| 3124 | 3129 | |
| 3125 | 3130 | static void omap_gpio_set(void *opaque, int line, int level) |
| ... | ... | @@ -3146,7 +3151,7 @@ static uint32_t omap_gpio_read(void *opaque, target_phys_addr_t addr) |
| 3146 | 3151 | |
| 3147 | 3152 | switch (offset) { |
| 3148 | 3153 | case 0x00: /* DATA_INPUT */ |
| 3149 | - return s->inputs; | |
| 3154 | + return s->inputs & s->pins; | |
| 3150 | 3155 | |
| 3151 | 3156 | case 0x04: /* DATA_OUTPUT */ |
| 3152 | 3157 | return s->outputs; |
| ... | ... | @@ -3162,6 +3167,10 @@ static uint32_t omap_gpio_read(void *opaque, target_phys_addr_t addr) |
| 3162 | 3167 | |
| 3163 | 3168 | case 0x14: /* INTERRUPT_STATUS */ |
| 3164 | 3169 | return s->ints; |
| 3170 | + | |
| 3171 | + case 0x18: /* PIN_CONTROL (not in OMAP310) */ | |
| 3172 | + OMAP_BAD_REG(addr); | |
| 3173 | + return s->pins; | |
| 3165 | 3174 | } |
| 3166 | 3175 | |
| 3167 | 3176 | OMAP_BAD_REG(addr); |
| ... | ... | @@ -3219,6 +3228,11 @@ static void omap_gpio_write(void *opaque, target_phys_addr_t addr, |
| 3219 | 3228 | qemu_irq_lower(s->irq); |
| 3220 | 3229 | break; |
| 3221 | 3230 | |
| 3231 | + case 0x18: /* PIN_CONTROL (not in OMAP310 TRM) */ | |
| 3232 | + OMAP_BAD_REG(addr); | |
| 3233 | + s->pins = value; | |
| 3234 | + break; | |
| 3235 | + | |
| 3222 | 3236 | default: |
| 3223 | 3237 | OMAP_BAD_REG(addr); |
| 3224 | 3238 | return; |
| ... | ... | @@ -3246,6 +3260,7 @@ void omap_gpio_reset(struct omap_gpio_s *s) |
| 3246 | 3260 | s->edge = ~0; |
| 3247 | 3261 | s->mask = ~0; |
| 3248 | 3262 | s->ints = 0; |
| 3263 | + s->pins = ~0; | |
| 3249 | 3264 | } |
| 3250 | 3265 | |
| 3251 | 3266 | struct omap_gpio_s *omap_gpio_init(target_phys_addr_t base, |
| ... | ... | @@ -4058,6 +4073,458 @@ struct omap_rtc_s *omap_rtc_init(target_phys_addr_t base, |
| 4058 | 4073 | return s; |
| 4059 | 4074 | } |
| 4060 | 4075 | |
| 4076 | +/* Multi-channel Buffered Serial Port interfaces */ | |
| 4077 | +struct omap_mcbsp_s { | |
| 4078 | + target_phys_addr_t base; | |
| 4079 | + qemu_irq txirq; | |
| 4080 | + qemu_irq rxirq; | |
| 4081 | + qemu_irq txdrq; | |
| 4082 | + qemu_irq rxdrq; | |
| 4083 | + | |
| 4084 | + uint16_t spcr[2]; | |
| 4085 | + uint16_t rcr[2]; | |
| 4086 | + uint16_t xcr[2]; | |
| 4087 | + uint16_t srgr[2]; | |
| 4088 | + uint16_t mcr[2]; | |
| 4089 | + uint16_t pcr; | |
| 4090 | + uint16_t rcer[8]; | |
| 4091 | + uint16_t xcer[8]; | |
| 4092 | + int tx_rate; | |
| 4093 | + int rx_rate; | |
| 4094 | + int tx_req; | |
| 4095 | + | |
| 4096 | + struct i2s_codec_s *codec; | |
| 4097 | +}; | |
| 4098 | + | |
| 4099 | +static void omap_mcbsp_intr_update(struct omap_mcbsp_s *s) | |
| 4100 | +{ | |
| 4101 | + int irq; | |
| 4102 | + | |
| 4103 | + switch ((s->spcr[0] >> 4) & 3) { /* RINTM */ | |
| 4104 | + case 0: | |
| 4105 | + irq = (s->spcr[0] >> 1) & 1; /* RRDY */ | |
| 4106 | + break; | |
| 4107 | + case 3: | |
| 4108 | + irq = (s->spcr[0] >> 3) & 1; /* RSYNCERR */ | |
| 4109 | + break; | |
| 4110 | + default: | |
| 4111 | + irq = 0; | |
| 4112 | + break; | |
| 4113 | + } | |
| 4114 | + | |
| 4115 | + qemu_set_irq(s->rxirq, irq); | |
| 4116 | + | |
| 4117 | + switch ((s->spcr[1] >> 4) & 3) { /* XINTM */ | |
| 4118 | + case 0: | |
| 4119 | + irq = (s->spcr[1] >> 1) & 1; /* XRDY */ | |
| 4120 | + break; | |
| 4121 | + case 3: | |
| 4122 | + irq = (s->spcr[1] >> 3) & 1; /* XSYNCERR */ | |
| 4123 | + break; | |
| 4124 | + default: | |
| 4125 | + irq = 0; | |
| 4126 | + break; | |
| 4127 | + } | |
| 4128 | + | |
| 4129 | + qemu_set_irq(s->txirq, irq); | |
| 4130 | +} | |
| 4131 | + | |
| 4132 | +static void omap_mcbsp_req_update(struct omap_mcbsp_s *s) | |
| 4133 | +{ | |
| 4134 | + int prev = s->tx_req; | |
| 4135 | + | |
| 4136 | + s->tx_req = (s->tx_rate || | |
| 4137 | + (s->spcr[0] & (1 << 12))) && /* CLKSTP */ | |
| 4138 | + (s->spcr[1] & (1 << 6)) && /* GRST */ | |
| 4139 | + (s->spcr[1] & (1 << 0)); /* XRST */ | |
| 4140 | + | |
| 4141 | + if (!s->tx_req && prev) { | |
| 4142 | + s->spcr[1] &= ~(1 << 1); /* XRDY */ | |
| 4143 | + qemu_irq_lower(s->txdrq); | |
| 4144 | + omap_mcbsp_intr_update(s); | |
| 4145 | + | |
| 4146 | + if (s->codec) | |
| 4147 | + s->codec->tx_swallow(s->codec->opaque); | |
| 4148 | + } else if (s->codec && s->tx_req && !prev) { | |
| 4149 | + s->spcr[1] |= 1 << 1; /* XRDY */ | |
| 4150 | + qemu_irq_raise(s->txdrq); | |
| 4151 | + omap_mcbsp_intr_update(s); | |
| 4152 | + } | |
| 4153 | +} | |
| 4154 | + | |
| 4155 | +static void omap_mcbsp_rate_update(struct omap_mcbsp_s *s) | |
| 4156 | +{ | |
| 4157 | + int rx_clk = 0, tx_clk = 0; | |
| 4158 | + int cpu_rate = 1500000; /* XXX */ | |
| 4159 | + if (!s->codec) | |
| 4160 | + return; | |
| 4161 | + | |
| 4162 | + if (s->spcr[1] & (1 << 6)) { /* GRST */ | |
| 4163 | + if (s->spcr[0] & (1 << 0)) /* RRST */ | |
| 4164 | + if ((s->srgr[1] & (1 << 13)) && /* CLKSM */ | |
| 4165 | + (s->pcr & (1 << 8))) /* CLKRM */ | |
| 4166 | + if (~s->pcr & (1 << 7)) /* SCLKME */ | |
| 4167 | + rx_clk = cpu_rate / | |
| 4168 | + ((s->srgr[0] & 0xff) + 1); /* CLKGDV */ | |
| 4169 | + if (s->spcr[1] & (1 << 0)) /* XRST */ | |
| 4170 | + if ((s->srgr[1] & (1 << 13)) && /* CLKSM */ | |
| 4171 | + (s->pcr & (1 << 9))) /* CLKXM */ | |
| 4172 | + if (~s->pcr & (1 << 7)) /* SCLKME */ | |
| 4173 | + tx_clk = cpu_rate / | |
| 4174 | + ((s->srgr[0] & 0xff) + 1); /* CLKGDV */ | |
| 4175 | + } | |
| 4176 | + | |
| 4177 | + s->codec->set_rate(s->codec->opaque, rx_clk, tx_clk); | |
| 4178 | +} | |
| 4179 | + | |
| 4180 | +static void omap_mcbsp_rx_start(struct omap_mcbsp_s *s) | |
| 4181 | +{ | |
| 4182 | + if (!(s->spcr[0] & 1)) { /* RRST */ | |
| 4183 | + if (s->codec) | |
| 4184 | + s->codec->in.len = 0; | |
| 4185 | + return; | |
| 4186 | + } | |
| 4187 | + | |
| 4188 | + if ((s->spcr[0] >> 1) & 1) /* RRDY */ | |
| 4189 | + s->spcr[0] |= 1 << 2; /* RFULL */ | |
| 4190 | + s->spcr[0] |= 1 << 1; /* RRDY */ | |
| 4191 | + qemu_irq_raise(s->rxdrq); | |
| 4192 | + omap_mcbsp_intr_update(s); | |
| 4193 | +} | |
| 4194 | + | |
| 4195 | +static void omap_mcbsp_rx_stop(struct omap_mcbsp_s *s) | |
| 4196 | +{ | |
| 4197 | + s->spcr[0] &= ~(1 << 1); /* RRDY */ | |
| 4198 | + qemu_irq_lower(s->rxdrq); | |
| 4199 | + omap_mcbsp_intr_update(s); | |
| 4200 | +} | |
| 4201 | + | |
| 4202 | +static void omap_mcbsp_tx_start(struct omap_mcbsp_s *s) | |
| 4203 | +{ | |
| 4204 | + if (s->tx_rate) | |
| 4205 | + return; | |
| 4206 | + s->tx_rate = 1; | |
| 4207 | + omap_mcbsp_req_update(s); | |
| 4208 | +} | |
| 4209 | + | |
| 4210 | +static void omap_mcbsp_tx_stop(struct omap_mcbsp_s *s) | |
| 4211 | +{ | |
| 4212 | + s->tx_rate = 0; | |
| 4213 | + omap_mcbsp_req_update(s); | |
| 4214 | +} | |
| 4215 | + | |
| 4216 | +static uint32_t omap_mcbsp_read(void *opaque, target_phys_addr_t addr) | |
| 4217 | +{ | |
| 4218 | + struct omap_mcbsp_s *s = (struct omap_mcbsp_s *) opaque; | |
| 4219 | + int offset = addr & OMAP_MPUI_REG_MASK; | |
| 4220 | + uint16_t ret; | |
| 4221 | + | |
| 4222 | + switch (offset) { | |
| 4223 | + case 0x00: /* DRR2 */ | |
| 4224 | + if (((s->rcr[0] >> 5) & 7) < 3) /* RWDLEN1 */ | |
| 4225 | + return 0x0000; | |
| 4226 | + /* Fall through. */ | |
| 4227 | + case 0x02: /* DRR1 */ | |
| 4228 | + if (!s->codec) | |
| 4229 | + return 0x0000; | |
| 4230 | + if (s->codec->in.len < 2) { | |
| 4231 | + printf("%s: Rx FIFO underrun\n", __FUNCTION__); | |
| 4232 | + omap_mcbsp_rx_stop(s); | |
| 4233 | + } else { | |
| 4234 | + s->codec->in.len -= 2; | |
| 4235 | + ret = s->codec->in.fifo[s->codec->in.start ++] << 8; | |
| 4236 | + ret |= s->codec->in.fifo[s->codec->in.start ++]; | |
| 4237 | + if (!s->codec->in.len) | |
| 4238 | + omap_mcbsp_rx_stop(s); | |
| 4239 | + return ret; | |
| 4240 | + } | |
| 4241 | + return 0x0000; | |
| 4242 | + | |
| 4243 | + case 0x04: /* DXR2 */ | |
| 4244 | + case 0x06: /* DXR1 */ | |
| 4245 | + return 0x0000; | |
| 4246 | + | |
| 4247 | + case 0x08: /* SPCR2 */ | |
| 4248 | + return s->spcr[1]; | |
| 4249 | + case 0x0a: /* SPCR1 */ | |
| 4250 | + return s->spcr[0]; | |
| 4251 | + case 0x0c: /* RCR2 */ | |
| 4252 | + return s->rcr[1]; | |
| 4253 | + case 0x0e: /* RCR1 */ | |
| 4254 | + return s->rcr[0]; | |
| 4255 | + case 0x10: /* XCR2 */ | |
| 4256 | + return s->xcr[1]; | |
| 4257 | + case 0x12: /* XCR1 */ | |
| 4258 | + return s->xcr[0]; | |
| 4259 | + case 0x14: /* SRGR2 */ | |
| 4260 | + return s->srgr[1]; | |
| 4261 | + case 0x16: /* SRGR1 */ | |
| 4262 | + return s->srgr[0]; | |
| 4263 | + case 0x18: /* MCR2 */ | |
| 4264 | + return s->mcr[1]; | |
| 4265 | + case 0x1a: /* MCR1 */ | |
| 4266 | + return s->mcr[0]; | |
| 4267 | + case 0x1c: /* RCERA */ | |
| 4268 | + return s->rcer[0]; | |
| 4269 | + case 0x1e: /* RCERB */ | |
| 4270 | + return s->rcer[1]; | |
| 4271 | + case 0x20: /* XCERA */ | |
| 4272 | + return s->xcer[0]; | |
| 4273 | + case 0x22: /* XCERB */ | |
| 4274 | + return s->xcer[1]; | |
| 4275 | + case 0x24: /* PCR0 */ | |
| 4276 | + return s->pcr; | |
| 4277 | + case 0x26: /* RCERC */ | |
| 4278 | + return s->rcer[2]; | |
| 4279 | + case 0x28: /* RCERD */ | |
| 4280 | + return s->rcer[3]; | |
| 4281 | + case 0x2a: /* XCERC */ | |
| 4282 | + return s->xcer[2]; | |
| 4283 | + case 0x2c: /* XCERD */ | |
| 4284 | + return s->xcer[3]; | |
| 4285 | + case 0x2e: /* RCERE */ | |
| 4286 | + return s->rcer[4]; | |
| 4287 | + case 0x30: /* RCERF */ | |
| 4288 | + return s->rcer[5]; | |
| 4289 | + case 0x32: /* XCERE */ | |
| 4290 | + return s->xcer[4]; | |
| 4291 | + case 0x34: /* XCERF */ | |
| 4292 | + return s->xcer[5]; | |
| 4293 | + case 0x36: /* RCERG */ | |
| 4294 | + return s->rcer[6]; | |
| 4295 | + case 0x38: /* RCERH */ | |
| 4296 | + return s->rcer[7]; | |
| 4297 | + case 0x3a: /* XCERG */ | |
| 4298 | + return s->xcer[6]; | |
| 4299 | + case 0x3c: /* XCERH */ | |
| 4300 | + return s->xcer[7]; | |
| 4301 | + } | |
| 4302 | + | |
| 4303 | + OMAP_BAD_REG(addr); | |
| 4304 | + return 0; | |
| 4305 | +} | |
| 4306 | + | |
| 4307 | +static void omap_mcbsp_write(void *opaque, target_phys_addr_t addr, | |
| 4308 | + uint32_t value) | |
| 4309 | +{ | |
| 4310 | + struct omap_mcbsp_s *s = (struct omap_mcbsp_s *) opaque; | |
| 4311 | + int offset = addr & OMAP_MPUI_REG_MASK; | |
| 4312 | + | |
| 4313 | + switch (offset) { | |
| 4314 | + case 0x00: /* DRR2 */ | |
| 4315 | + case 0x02: /* DRR1 */ | |
| 4316 | + OMAP_RO_REG(addr); | |
| 4317 | + return; | |
| 4318 | + | |
| 4319 | + case 0x04: /* DXR2 */ | |
| 4320 | + if (((s->xcr[0] >> 5) & 7) < 3) /* XWDLEN1 */ | |
| 4321 | + return; | |
| 4322 | + /* Fall through. */ | |
| 4323 | + case 0x06: /* DXR1 */ | |
| 4324 | + if (!s->codec) | |
| 4325 | + return; | |
| 4326 | + if (s->tx_req) { | |
| 4327 | + if (s->codec->out.len > s->codec->out.size - 2) { | |
| 4328 | + printf("%s: Tx FIFO overrun\n", __FUNCTION__); | |
| 4329 | + omap_mcbsp_tx_stop(s); | |
| 4330 | + } else { | |
| 4331 | + s->codec->out.fifo[s->codec->out.len ++] = (value >> 8) & 0xff; | |
| 4332 | + s->codec->out.fifo[s->codec->out.len ++] = (value >> 0) & 0xff; | |
| 4333 | + if (s->codec->out.len >= s->codec->out.size) | |
| 4334 | + omap_mcbsp_tx_stop(s); | |
| 4335 | + } | |
| 4336 | + } else | |
| 4337 | + printf("%s: Tx FIFO overrun\n", __FUNCTION__); | |
| 4338 | + return; | |
| 4339 | + | |
| 4340 | + case 0x08: /* SPCR2 */ | |
| 4341 | + s->spcr[1] &= 0x0002; | |
| 4342 | + s->spcr[1] |= 0x03f9 & value; | |
| 4343 | + s->spcr[1] |= 0x0004 & (value << 2); /* XEMPTY := XRST */ | |
| 4344 | + if (~value & 1) { /* XRST */ | |
| 4345 | + s->spcr[1] &= ~6; | |
| 4346 | + qemu_irq_lower(s->rxdrq); | |
| 4347 | + if (s->codec) | |
| 4348 | + s->codec->out.len = 0; | |
| 4349 | + } | |
| 4350 | + if (s->codec) | |
| 4351 | + omap_mcbsp_rate_update(s); | |
| 4352 | + omap_mcbsp_req_update(s); | |
| 4353 | + return; | |
| 4354 | + case 0x0a: /* SPCR1 */ | |
| 4355 | + s->spcr[0] &= 0x0006; | |
| 4356 | + s->spcr[0] |= 0xf8f9 & value; | |
| 4357 | + if (value & (1 << 15)) /* DLB */ | |
| 4358 | + printf("%s: Digital Loopback mode enable attempt\n", __FUNCTION__); | |
| 4359 | + if (~value & 1) { /* RRST */ | |
| 4360 | + s->spcr[0] &= ~6; | |
| 4361 | + qemu_irq_lower(s->txdrq); | |
| 4362 | + if (s->codec) | |
| 4363 | + s->codec->in.len = 0; | |
| 4364 | + } | |
| 4365 | + if (s->codec) | |
| 4366 | + omap_mcbsp_rate_update(s); | |
| 4367 | + omap_mcbsp_req_update(s); | |
| 4368 | + return; | |
| 4369 | + | |
| 4370 | + case 0x0c: /* RCR2 */ | |
| 4371 | + s->rcr[1] = value & 0xffff; | |
| 4372 | + return; | |
| 4373 | + case 0x0e: /* RCR1 */ | |
| 4374 | + s->rcr[0] = value & 0x7fe0; | |
| 4375 | + return; | |
| 4376 | + case 0x10: /* XCR2 */ | |
| 4377 | + s->xcr[1] = value & 0xffff; | |
| 4378 | + return; | |
| 4379 | + case 0x12: /* XCR1 */ | |
| 4380 | + s->xcr[0] = value & 0x7fe0; | |
| 4381 | + return; | |
| 4382 | + case 0x14: /* SRGR2 */ | |
| 4383 | + s->srgr[1] = value & 0xffff; | |
| 4384 | + omap_mcbsp_rate_update(s); | |
| 4385 | + return; | |
| 4386 | + case 0x16: /* SRGR1 */ | |
| 4387 | + s->srgr[0] = value & 0xffff; | |
| 4388 | + omap_mcbsp_rate_update(s); | |
| 4389 | + return; | |
| 4390 | + case 0x18: /* MCR2 */ | |
| 4391 | + s->mcr[1] = value & 0x03e3; | |
| 4392 | + if (value & 3) /* XMCM */ | |
| 4393 | + printf("%s: Tx channel selection mode enable attempt\n", | |
| 4394 | + __FUNCTION__); | |
| 4395 | + return; | |
| 4396 | + case 0x1a: /* MCR1 */ | |
| 4397 | + s->mcr[0] = value & 0x03e1; | |
| 4398 | + if (value & 1) /* RMCM */ | |
| 4399 | + printf("%s: Rx channel selection mode enable attempt\n", | |
| 4400 | + __FUNCTION__); | |
| 4401 | + return; | |
| 4402 | + case 0x1c: /* RCERA */ | |
| 4403 | + s->rcer[0] = value & 0xffff; | |
| 4404 | + return; | |
| 4405 | + case 0x1e: /* RCERB */ | |
| 4406 | + s->rcer[1] = value & 0xffff; | |
| 4407 | + return; | |
| 4408 | + case 0x20: /* XCERA */ | |
| 4409 | + s->xcer[0] = value & 0xffff; | |
| 4410 | + return; | |
| 4411 | + case 0x22: /* XCERB */ | |
| 4412 | + s->xcer[1] = value & 0xffff; | |
| 4413 | + return; | |
| 4414 | + case 0x24: /* PCR0 */ | |
| 4415 | + s->pcr = value & 0x7faf; | |
| 4416 | + return; | |
| 4417 | + case 0x26: /* RCERC */ | |
| 4418 | + s->rcer[2] = value & 0xffff; | |
| 4419 | + return; | |
| 4420 | + case 0x28: /* RCERD */ | |
| 4421 | + s->rcer[3] = value & 0xffff; | |
| 4422 | + return; | |
| 4423 | + case 0x2a: /* XCERC */ | |
| 4424 | + s->xcer[2] = value & 0xffff; | |
| 4425 | + return; | |
| 4426 | + case 0x2c: /* XCERD */ | |
| 4427 | + s->xcer[3] = value & 0xffff; | |
| 4428 | + return; | |
| 4429 | + case 0x2e: /* RCERE */ | |
| 4430 | + s->rcer[4] = value & 0xffff; | |
| 4431 | + return; | |
| 4432 | + case 0x30: /* RCERF */ | |
| 4433 | + s->rcer[5] = value & 0xffff; | |
| 4434 | + return; | |
| 4435 | + case 0x32: /* XCERE */ | |
| 4436 | + s->xcer[4] = value & 0xffff; | |
| 4437 | + return; | |
| 4438 | + case 0x34: /* XCERF */ | |
| 4439 | + s->xcer[5] = value & 0xffff; | |
| 4440 | + return; | |
| 4441 | + case 0x36: /* RCERG */ | |
| 4442 | + s->rcer[6] = value & 0xffff; | |
| 4443 | + return; | |
| 4444 | + case 0x38: /* RCERH */ | |
| 4445 | + s->rcer[7] = value & 0xffff; | |
| 4446 | + return; | |
| 4447 | + case 0x3a: /* XCERG */ | |
| 4448 | + s->xcer[6] = value & 0xffff; | |
| 4449 | + return; | |
| 4450 | + case 0x3c: /* XCERH */ | |
| 4451 | + s->xcer[7] = value & 0xffff; | |
| 4452 | + return; | |
| 4453 | + } | |
| 4454 | + | |
| 4455 | + OMAP_BAD_REG(addr); | |
| 4456 | +} | |
| 4457 | + | |
| 4458 | +static CPUReadMemoryFunc *omap_mcbsp_readfn[] = { | |
| 4459 | + omap_badwidth_read16, | |
| 4460 | + omap_mcbsp_read, | |
| 4461 | + omap_badwidth_read16, | |
| 4462 | +}; | |
| 4463 | + | |
| 4464 | +static CPUWriteMemoryFunc *omap_mcbsp_writefn[] = { | |
| 4465 | + omap_badwidth_write16, | |
| 4466 | + omap_mcbsp_write, | |
| 4467 | + omap_badwidth_write16, | |
| 4468 | +}; | |
| 4469 | + | |
| 4470 | +static void omap_mcbsp_reset(struct omap_mcbsp_s *s) | |
| 4471 | +{ | |
| 4472 | + memset(&s->spcr, 0, sizeof(s->spcr)); | |
| 4473 | + memset(&s->rcr, 0, sizeof(s->rcr)); | |
| 4474 | + memset(&s->xcr, 0, sizeof(s->xcr)); | |
| 4475 | + s->srgr[0] = 0x0001; | |
| 4476 | + s->srgr[1] = 0x2000; | |
| 4477 | + memset(&s->mcr, 0, sizeof(s->mcr)); | |
| 4478 | + memset(&s->pcr, 0, sizeof(s->pcr)); | |
| 4479 | + memset(&s->rcer, 0, sizeof(s->rcer)); | |
| 4480 | + memset(&s->xcer, 0, sizeof(s->xcer)); | |
| 4481 | + s->tx_req = 0; | |
| 4482 | + s->tx_rate = 0; | |
| 4483 | + s->rx_rate = 0; | |
| 4484 | +} | |
| 4485 | + | |
| 4486 | +struct omap_mcbsp_s *omap_mcbsp_init(target_phys_addr_t base, | |
| 4487 | + qemu_irq *irq, qemu_irq *dma, omap_clk clk) | |
| 4488 | +{ | |
| 4489 | + int iomemtype; | |
| 4490 | + struct omap_mcbsp_s *s = (struct omap_mcbsp_s *) | |
| 4491 | + qemu_mallocz(sizeof(struct omap_mcbsp_s)); | |
| 4492 | + | |
| 4493 | + s->base = base; | |
| 4494 | + s->txirq = irq[0]; | |
| 4495 | + s->rxirq = irq[1]; | |
| 4496 | + s->txdrq = dma[0]; | |
| 4497 | + s->rxdrq = dma[1]; | |
| 4498 | + omap_mcbsp_reset(s); | |
| 4499 | + | |
| 4500 | + iomemtype = cpu_register_io_memory(0, omap_mcbsp_readfn, | |
| 4501 | + omap_mcbsp_writefn, s); | |
| 4502 | + cpu_register_physical_memory(s->base, 0x800, iomemtype); | |
| 4503 | + | |
| 4504 | + return s; | |
| 4505 | +} | |
| 4506 | + | |
| 4507 | +void omap_mcbsp_i2s_swallow(void *opaque, int line, int level) | |
| 4508 | +{ | |
| 4509 | + struct omap_mcbsp_s *s = (struct omap_mcbsp_s *) opaque; | |
| 4510 | + | |
| 4511 | + omap_mcbsp_rx_start(s); | |
| 4512 | +} | |
| 4513 | + | |
| 4514 | +void omap_mcbsp_i2s_start(void *opaque, int line, int level) | |
| 4515 | +{ | |
| 4516 | + struct omap_mcbsp_s *s = (struct omap_mcbsp_s *) opaque; | |
| 4517 | + | |
| 4518 | + omap_mcbsp_tx_start(s); | |
| 4519 | +} | |
| 4520 | + | |
| 4521 | +void omap_mcbsp_i2s_attach(struct omap_mcbsp_s *s, struct i2s_codec_s *slave) | |
| 4522 | +{ | |
| 4523 | + s->codec = slave; | |
| 4524 | + slave->rx_swallow = qemu_allocate_irqs(omap_mcbsp_i2s_swallow, s, 1)[0]; | |
| 4525 | + slave->tx_start = qemu_allocate_irqs(omap_mcbsp_i2s_start, s, 1)[0]; | |
| 4526 | +} | |
| 4527 | + | |
| 4061 | 4528 | /* General chip reset */ |
| 4062 | 4529 | static void omap_mpu_reset(void *opaque) |
| 4063 | 4530 | { |
| ... | ... | @@ -4092,6 +4559,9 @@ static void omap_mpu_reset(void *opaque) |
| 4092 | 4559 | omap_pwt_reset(mpu); |
| 4093 | 4560 | omap_i2c_reset(mpu->i2c); |
| 4094 | 4561 | omap_rtc_reset(mpu->rtc); |
| 4562 | + omap_mcbsp_reset(mpu->mcbsp1); | |
| 4563 | + omap_mcbsp_reset(mpu->mcbsp2); | |
| 4564 | + omap_mcbsp_reset(mpu->mcbsp3); | |
| 4095 | 4565 | cpu_reset(mpu->env); |
| 4096 | 4566 | } |
| 4097 | 4567 | |
| ... | ... | @@ -4254,8 +4724,8 @@ struct omap_mpu_state_s *omap310_mpu_init(unsigned long sdram_size, |
| 4254 | 4724 | s->microwire = omap_uwire_init(0xfffb3000, &s->irq[1][OMAP_INT_uWireTX], |
| 4255 | 4725 | s->drq[OMAP_DMA_UWIRE_TX], omap_findclk(s, "mpuper_ck")); |
| 4256 | 4726 | |
| 4257 | - omap_pwl_init(0xfffb5800, s, omap_findclk(s, "clk32-kHz")); | |
| 4258 | - omap_pwt_init(0xfffb6000, s, omap_findclk(s, "xtal_osc_12m")); | |
| 4727 | + omap_pwl_init(0xfffb5800, s, omap_findclk(s, "armxor_ck")); | |
| 4728 | + omap_pwt_init(0xfffb6000, s, omap_findclk(s, "armxor_ck")); | |
| 4259 | 4729 | |
| 4260 | 4730 | s->i2c = omap_i2c_init(0xfffb3800, s->irq[1][OMAP_INT_I2C], |
| 4261 | 4731 | &s->drq[OMAP_DMA_I2C_RX], omap_findclk(s, "mpuper_ck")); |
| ... | ... | @@ -4263,14 +4733,18 @@ struct omap_mpu_state_s *omap310_mpu_init(unsigned long sdram_size, |
| 4263 | 4733 | s->rtc = omap_rtc_init(0xfffb4800, &s->irq[1][OMAP_INT_RTC_TIMER], |
| 4264 | 4734 | omap_findclk(s, "clk32-kHz")); |
| 4265 | 4735 | |
| 4736 | + s->mcbsp1 = omap_mcbsp_init(0xfffb1800, &s->irq[1][OMAP_INT_McBSP1TX], | |
| 4737 | + &s->drq[OMAP_DMA_MCBSP1_TX], omap_findclk(s, "dspxor_ck")); | |
| 4738 | + s->mcbsp2 = omap_mcbsp_init(0xfffb1000, &s->irq[0][OMAP_INT_310_McBSP2_TX], | |
| 4739 | + &s->drq[OMAP_DMA_MCBSP2_TX], omap_findclk(s, "mpuper_ck")); | |
| 4740 | + s->mcbsp3 = omap_mcbsp_init(0xfffb7000, &s->irq[1][OMAP_INT_McBSP3TX], | |
| 4741 | + &s->drq[OMAP_DMA_MCBSP3_TX], omap_findclk(s, "dspxor_ck")); | |
| 4742 | + | |
| 4266 | 4743 | /* Register mappings not currenlty implemented: |
| 4267 | - * McBSP2 Comm fffb1000 - fffb17ff | |
| 4268 | - * McBSP1 Audio fffb1800 - fffb1fff (not mapped on OMAP310) | |
| 4269 | 4744 | * MCSI2 Comm fffb2000 - fffb27ff (not mapped on OMAP310) |
| 4270 | 4745 | * MCSI1 Bluetooth fffb2800 - fffb2fff (not mapped on OMAP310) |
| 4271 | 4746 | * USB W2FC fffb4000 - fffb47ff |
| 4272 | 4747 | * Camera Interface fffb6800 - fffb6fff |
| 4273 | - * McBSP3 fffb7000 - fffb77ff (not mapped on OMAP310) | |
| 4274 | 4748 | * USB Host fffba000 - fffba7ff |
| 4275 | 4749 | * FAC fffba800 - fffbafff |
| 4276 | 4750 | * HDQ/1-Wire fffbc000 - fffbc7ff | ... | ... |
hw/omap.h
| ... | ... | @@ -479,6 +479,30 @@ struct omap_rtc_s; |
| 479 | 479 | struct omap_rtc_s *omap_rtc_init(target_phys_addr_t base, |
| 480 | 480 | qemu_irq *irq, omap_clk clk); |
| 481 | 481 | |
| 482 | +struct i2s_codec_s { | |
| 483 | + void *opaque; | |
| 484 | + | |
| 485 | + /* The CPU can call this if it is generating the clock signal on the | |
| 486 | + * i2s port. The CODEC can ignore it if it is set up as a clock | |
| 487 | + * master and generates its own clock. */ | |
| 488 | + void (*set_rate)(void *opaque, int in, int out); | |
| 489 | + | |
| 490 | + void (*tx_swallow)(void *opaque); | |
| 491 | + qemu_irq rx_swallow; | |
| 492 | + qemu_irq tx_start; | |
| 493 | + | |
| 494 | + struct i2s_fifo_s { | |
| 495 | + uint8_t *fifo; | |
| 496 | + int len; | |
| 497 | + int start; | |
| 498 | + int size; | |
| 499 | + } in, out; | |
| 500 | +}; | |
| 501 | +struct omap_mcbsp_s; | |
| 502 | +struct omap_mcbsp_s *omap_mcbsp_init(target_phys_addr_t base, | |
| 503 | + qemu_irq *irq, qemu_irq *dma, omap_clk clk); | |
| 504 | +void omap_mcbsp_i2s_attach(struct omap_mcbsp_s *s, struct i2s_codec_s *slave); | |
| 505 | + | |
| 482 | 506 | /* omap_lcdc.c */ |
| 483 | 507 | struct omap_lcd_panel_s; |
| 484 | 508 | void omap_lcdc_reset(struct omap_lcd_panel_s *s); |
| ... | ... | @@ -536,6 +560,9 @@ struct omap_mpu_state_s { |
| 536 | 560 | |
| 537 | 561 | struct omap_gpio_s *gpio; |
| 538 | 562 | |
| 563 | + struct omap_mcbsp_s *mcbsp1; | |
| 564 | + struct omap_mcbsp_s *mcbsp3; | |
| 565 | + | |
| 539 | 566 | /* MPU public TIPB peripherals */ |
| 540 | 567 | struct omap_32khz_timer_s *os_timer; |
| 541 | 568 | |
| ... | ... | @@ -563,6 +590,8 @@ struct omap_mpu_state_s { |
| 563 | 590 | |
| 564 | 591 | struct omap_rtc_s *rtc; |
| 565 | 592 | |
| 593 | + struct omap_mcbsp_s *mcbsp2; | |
| 594 | + | |
| 566 | 595 | /* MPU private TIPB peripherals */ |
| 567 | 596 | struct omap_intr_handler_s *ih[2]; |
| 568 | 597 | |
| ... | ... | @@ -646,6 +675,7 @@ void omap_badwidth_write32(void *opaque, target_phys_addr_t addr, |
| 646 | 675 | __FUNCTION__, paddr) |
| 647 | 676 | |
| 648 | 677 | # define TCMI_VERBOSE 1 |
| 678 | +//# define MEM_VERBOSE 1 | |
| 649 | 679 | |
| 650 | 680 | # ifdef TCMI_VERBOSE |
| 651 | 681 | # define OMAP_8B_REG(paddr) \ |
| ... | ... | @@ -665,4 +695,97 @@ void omap_badwidth_write32(void *opaque, target_phys_addr_t addr, |
| 665 | 695 | |
| 666 | 696 | # define OMAP_MPUI_REG_MASK 0x000007ff |
| 667 | 697 | |
| 698 | +# ifdef MEM_VERBOSE | |
| 699 | +struct io_fn { | |
| 700 | + CPUReadMemoryFunc **mem_read; | |
| 701 | + CPUWriteMemoryFunc **mem_write; | |
| 702 | + void *opaque; | |
| 703 | + int in; | |
| 704 | +}; | |
| 705 | + | |
| 706 | +static uint32_t io_readb(void *opaque, target_phys_addr_t addr) | |
| 707 | +{ | |
| 708 | + struct io_fn *s = opaque; | |
| 709 | + uint32_t ret; | |
| 710 | + | |
| 711 | + s->in ++; | |
| 712 | + ret = s->mem_read[0](s->opaque, addr); | |
| 713 | + s->in --; | |
| 714 | + if (!s->in) | |
| 715 | + fprintf(stderr, "%08x ---> %02x\n", (uint32_t) addr, ret); | |
| 716 | + return ret; | |
| 717 | +} | |
| 718 | +static uint32_t io_readh(void *opaque, target_phys_addr_t addr) | |
| 719 | +{ | |
| 720 | + struct io_fn *s = opaque; | |
| 721 | + uint32_t ret; | |
| 722 | + | |
| 723 | + s->in ++; | |
| 724 | + ret = s->mem_read[1](s->opaque, addr); | |
| 725 | + s->in --; | |
| 726 | + if (!s->in) | |
| 727 | + fprintf(stderr, "%08x ---> %04x\n", (uint32_t) addr, ret); | |
| 728 | + return ret; | |
| 729 | +} | |
| 730 | +static uint32_t io_readw(void *opaque, target_phys_addr_t addr) | |
| 731 | +{ | |
| 732 | + struct io_fn *s = opaque; | |
| 733 | + uint32_t ret; | |
| 734 | + | |
| 735 | + s->in ++; | |
| 736 | + ret = s->mem_read[2](s->opaque, addr); | |
| 737 | + s->in --; | |
| 738 | + if (!s->in) | |
| 739 | + fprintf(stderr, "%08x ---> %08x\n", (uint32_t) addr, ret); | |
| 740 | + return ret; | |
| 741 | +} | |
| 742 | +static void io_writeb(void *opaque, target_phys_addr_t addr, uint32_t value) | |
| 743 | +{ | |
| 744 | + struct io_fn *s = opaque; | |
| 745 | + | |
| 746 | + if (!s->in) | |
| 747 | + fprintf(stderr, "%08x <--- %02x\n", (uint32_t) addr, value); | |
| 748 | + s->in ++; | |
| 749 | + s->mem_write[0](s->opaque, addr, value); | |
| 750 | + s->in --; | |
| 751 | +} | |
| 752 | +static void io_writeh(void *opaque, target_phys_addr_t addr, uint32_t value) | |
| 753 | +{ | |
| 754 | + struct io_fn *s = opaque; | |
| 755 | + | |
| 756 | + if (!s->in) | |
| 757 | + fprintf(stderr, "%08x <--- %04x\n", (uint32_t) addr, value); | |
| 758 | + s->in ++; | |
| 759 | + s->mem_write[1](s->opaque, addr, value); | |
| 760 | + s->in --; | |
| 761 | +} | |
| 762 | +static void io_writew(void *opaque, target_phys_addr_t addr, uint32_t value) | |
| 763 | +{ | |
| 764 | + struct io_fn *s = opaque; | |
| 765 | + | |
| 766 | + if (!s->in) | |
| 767 | + fprintf(stderr, "%08x <--- %08x\n", (uint32_t) addr, value); | |
| 768 | + s->in ++; | |
| 769 | + s->mem_write[2](s->opaque, addr, value); | |
| 770 | + s->in --; | |
| 771 | +} | |
| 772 | + | |
| 773 | +static CPUReadMemoryFunc *io_readfn[] = { io_readb, io_readh, io_readw, }; | |
| 774 | +static CPUWriteMemoryFunc *io_writefn[] = { io_writeb, io_writeh, io_writew, }; | |
| 775 | + | |
| 776 | +inline static int debug_register_io_memory(int io_index, | |
| 777 | + CPUReadMemoryFunc **mem_read, CPUWriteMemoryFunc **mem_write, | |
| 778 | + void *opaque) | |
| 779 | +{ | |
| 780 | + struct io_fn *s = qemu_malloc(sizeof(struct io_fn)); | |
| 781 | + | |
| 782 | + s->mem_read = mem_read; | |
| 783 | + s->mem_write = mem_write; | |
| 784 | + s->opaque = opaque; | |
| 785 | + s->in = 0; | |
| 786 | + return cpu_register_io_memory(io_index, io_readfn, io_writefn, s); | |
| 787 | +} | |
| 788 | +# define cpu_register_io_memory debug_register_io_memory | |
| 789 | +# endif | |
| 790 | + | |
| 668 | 791 | #endif /* hw_omap_h */ | ... | ... |
hw/omap1_clk.c
| ... | ... | @@ -307,6 +307,12 @@ static struct clk lbfree_ck = { |
| 307 | 307 | .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310, |
| 308 | 308 | }; |
| 309 | 309 | |
| 310 | +static struct clk hsab_ck = { | |
| 311 | + .name = "hsab_ck", | |
| 312 | + .parent = &tc_ck, | |
| 313 | + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310, | |
| 314 | +}; | |
| 315 | + | |
| 310 | 316 | static struct clk rhea1_ck = { |
| 311 | 317 | .name = "rhea1_ck", |
| 312 | 318 | .parent = &tc_ck, |
| ... | ... | @@ -359,7 +365,7 @@ static struct clk uart2_ck = { |
| 359 | 365 | static struct clk uart3_1510 = { |
| 360 | 366 | .name = "uart3_ck", |
| 361 | 367 | /* Direct from ULPD, no real parent */ |
| 362 | - .parent = &armper_ck,/* either armper_ck or dpll4 */ | |
| 368 | + .parent = &armper_ck, /* either armper_ck or dpll4 */ | |
| 363 | 369 | .rate = 12000000, |
| 364 | 370 | .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310 | ALWAYS_ENABLED, |
| 365 | 371 | }; |
| ... | ... | @@ -395,11 +401,12 @@ static struct clk usb_hhc_ck16xx = { |
| 395 | 401 | .flags = CLOCK_IN_OMAP16XX, |
| 396 | 402 | }; |
| 397 | 403 | |
| 398 | -static struct clk usb_dc_ck = { | |
| 399 | - .name = "usb_dc_ck", | |
| 400 | - /* Direct from ULPD, no parent */ | |
| 404 | +static struct clk usb_w2fc_mclk = { | |
| 405 | + .name = "usb_w2fc_mclk", | |
| 406 | + .alias = "usb_w2fc_ck", | |
| 407 | + .parent = &ck_48m, | |
| 401 | 408 | .rate = 48000000, |
| 402 | - .flags = CLOCK_IN_OMAP16XX, | |
| 409 | + .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX, | |
| 403 | 410 | }; |
| 404 | 411 | |
| 405 | 412 | static struct clk mclk_1510 = { |
| ... | ... | @@ -539,6 +546,7 @@ static struct clk *onchip_clks[] = { |
| 539 | 546 | &api_ck, |
| 540 | 547 | &lb_ck, |
| 541 | 548 | &lbfree_ck, |
| 549 | + &hsab_ck, | |
| 542 | 550 | &rhea1_ck, |
| 543 | 551 | &rhea2_ck, |
| 544 | 552 | &lcd_ck_16xx, |
| ... | ... | @@ -551,7 +559,6 @@ static struct clk *onchip_clks[] = { |
| 551 | 559 | &uart3_16xx, |
| 552 | 560 | &usb_clk0, |
| 553 | 561 | &usb_hhc_ck1510, &usb_hhc_ck16xx, |
| 554 | - &usb_dc_ck, | |
| 555 | 562 | &mclk_1510, &mclk_16xx, &mclk_310, |
| 556 | 563 | &bclk_1510, &bclk_16xx, &bclk_310, |
| 557 | 564 | &mmc1_ck, |
| ... | ... | @@ -560,6 +567,7 @@ static struct clk *onchip_clks[] = { |
| 560 | 567 | &cam_exclk, |
| 561 | 568 | &cam_lclk, |
| 562 | 569 | &clk32k, |
| 570 | + &usb_w2fc_mclk, | |
| 563 | 571 | /* Virtual clocks */ |
| 564 | 572 | &i2c_fck, |
| 565 | 573 | &i2c_ick, | ... | ... |
hw/palm.c
| ... | ... | @@ -78,11 +78,18 @@ static CPUWriteMemoryFunc *static_writefn[] = { |
| 78 | 78 | |
| 79 | 79 | static void palmte_microwire_setup(struct omap_mpu_state_s *cpu) |
| 80 | 80 | { |
| 81 | - omap_uwire_attach( | |
| 82 | - cpu->microwire, | |
| 83 | - tsc2102_init( | |
| 84 | - omap_gpio_in_get(cpu->gpio)[PALMTE_PINTDAV_GPIO]), | |
| 85 | - 0); | |
| 81 | + struct uwire_slave_s *tsc; | |
| 82 | + AudioState *audio = 0; | |
| 83 | + | |
| 84 | +#ifdef HAS_AUDIO | |
| 85 | + audio = AUD_init(); | |
| 86 | +#endif | |
| 87 | + | |
| 88 | + tsc = tsc2102_init(omap_gpio_in_get(cpu->gpio)[PALMTE_PINTDAV_GPIO], | |
| 89 | + audio); | |
| 90 | + | |
| 91 | + omap_uwire_attach(cpu->microwire, tsc, 0); | |
| 92 | + omap_mcbsp_i2s_attach(cpu->mcbsp1, tsc210x_codec(tsc)); | |
| 86 | 93 | } |
| 87 | 94 | |
| 88 | 95 | static struct { | ... | ... |
hw/tsc210x.c
| ... | ... | @@ -32,7 +32,11 @@ |
| 32 | 32 | struct tsc210x_state_s { |
| 33 | 33 | qemu_irq pint; |
| 34 | 34 | QEMUTimer *timer; |
| 35 | + QEMUSoundCard card; | |
| 35 | 36 | struct uwire_slave_s chip; |
| 37 | + struct i2s_codec_s codec; | |
| 38 | + uint8_t in_fifo[16384]; | |
| 39 | + uint8_t out_fifo[16384]; | |
| 36 | 40 | |
| 37 | 41 | int x, y; |
| 38 | 42 | int pressure; |
| ... | ... | @@ -63,6 +67,13 @@ struct tsc210x_state_s { |
| 63 | 67 | uint16_t dac_power; |
| 64 | 68 | int64_t powerdown; |
| 65 | 69 | uint16_t filter_data[0x14]; |
| 70 | + | |
| 71 | + const char *name; | |
| 72 | + SWVoiceIn *adc_voice[1]; | |
| 73 | + SWVoiceOut *dac_voice[1]; | |
| 74 | + int i2s_rx_rate; | |
| 75 | + int i2s_tx_rate; | |
| 76 | + AudioState *audio; | |
| 66 | 77 | }; |
| 67 | 78 | |
| 68 | 79 | static const int resolution[4] = { 12, 8, 10, 12 }; |
| ... | ... | @@ -171,9 +182,144 @@ static void tsc210x_reset(struct tsc210x_state_s *s) |
| 171 | 182 | s->filter_data[0x12] = 0x7d83; |
| 172 | 183 | s->filter_data[0x13] = 0x84ee; |
| 173 | 184 | |
| 185 | + s->i2s_tx_rate = 0; | |
| 186 | + s->i2s_rx_rate = 0; | |
| 187 | + | |
| 174 | 188 | qemu_set_irq(s->pint, !s->irq); |
| 175 | 189 | } |
| 176 | 190 | |
| 191 | +struct tsc210x_rate_info_s { | |
| 192 | + int rate; | |
| 193 | + int dsor; | |
| 194 | + int fsref; | |
| 195 | +}; | |
| 196 | + | |
| 197 | +/* { rate, dsor, fsref } */ | |
| 198 | +static const struct tsc210x_rate_info_s tsc2101_rates[] = { | |
| 199 | + /* Fsref / 6.0 */ | |
| 200 | + { 7350, 7, 1 }, | |
| 201 | + { 8000, 7, 0 }, | |
| 202 | + /* Fsref / 5.5 */ | |
| 203 | + { 8018, 6, 1 }, | |
| 204 | + { 8727, 6, 0 }, | |
| 205 | + /* Fsref / 5.0 */ | |
| 206 | + { 8820, 5, 1 }, | |
| 207 | + { 9600, 5, 0 }, | |
| 208 | + /* Fsref / 4.0 */ | |
| 209 | + { 11025, 4, 1 }, | |
| 210 | + { 12000, 4, 0 }, | |
| 211 | + /* Fsref / 3.0 */ | |
| 212 | + { 14700, 3, 1 }, | |
| 213 | + { 16000, 3, 0 }, | |
| 214 | + /* Fsref / 2.0 */ | |
| 215 | + { 22050, 2, 1 }, | |
| 216 | + { 24000, 2, 0 }, | |
| 217 | + /* Fsref / 1.5 */ | |
| 218 | + { 29400, 1, 1 }, | |
| 219 | + { 32000, 1, 0 }, | |
| 220 | + /* Fsref */ | |
| 221 | + { 44100, 0, 1 }, | |
| 222 | + { 48000, 0, 0 }, | |
| 223 | + | |
| 224 | + { 0, 0, 0 }, | |
| 225 | +}; | |
| 226 | + | |
| 227 | +/* { rate, dsor, fsref } */ | |
| 228 | +static const struct tsc210x_rate_info_s tsc2102_rates[] = { | |
| 229 | + /* Fsref / 6.0 */ | |
| 230 | + { 7350, 63, 1 }, | |
| 231 | + { 8000, 63, 0 }, | |
| 232 | + /* Fsref / 6.0 */ | |
| 233 | + { 7350, 54, 1 }, | |
| 234 | + { 8000, 54, 0 }, | |
| 235 | + /* Fsref / 5.0 */ | |
| 236 | + { 8820, 45, 1 }, | |
| 237 | + { 9600, 45, 0 }, | |
| 238 | + /* Fsref / 4.0 */ | |
| 239 | + { 11025, 36, 1 }, | |
| 240 | + { 12000, 36, 0 }, | |
| 241 | + /* Fsref / 3.0 */ | |
| 242 | + { 14700, 27, 1 }, | |
| 243 | + { 16000, 27, 0 }, | |
| 244 | + /* Fsref / 2.0 */ | |
| 245 | + { 22050, 18, 1 }, | |
| 246 | + { 24000, 18, 0 }, | |
| 247 | + /* Fsref / 1.5 */ | |
| 248 | + { 29400, 9, 1 }, | |
| 249 | + { 32000, 9, 0 }, | |
| 250 | + /* Fsref */ | |
| 251 | + { 44100, 0, 1 }, | |
| 252 | + { 48000, 0, 0 }, | |
| 253 | + | |
| 254 | + { 0, 0, 0 }, | |
| 255 | +}; | |
| 256 | + | |
| 257 | +static inline void tsc210x_out_flush(struct tsc210x_state_s *s, int len) | |
| 258 | +{ | |
| 259 | + uint8_t *data = s->codec.out.fifo + s->codec.out.start; | |
| 260 | + uint8_t *end = data + len; | |
| 261 | + | |
| 262 | + while (data < end) | |
| 263 | + data += AUD_write(s->dac_voice[0], data, end - data) ?: (end - data); | |
| 264 | + | |
| 265 | + s->codec.out.len -= len; | |
| 266 | + if (s->codec.out.len) | |
| 267 | + memmove(s->codec.out.fifo, end, s->codec.out.len); | |
| 268 | + s->codec.out.start = 0; | |
| 269 | +} | |
| 270 | + | |
| 271 | +static void tsc210x_audio_out_cb(struct tsc210x_state_s *s, int free_b) | |
| 272 | +{ | |
| 273 | + if (s->codec.out.len >= free_b) { | |
| 274 | + tsc210x_out_flush(s, free_b); | |
| 275 | + return; | |
| 276 | + } | |
| 277 | + | |
| 278 | + s->codec.out.size = MIN(free_b, 16384); | |
| 279 | + qemu_irq_raise(s->codec.tx_start); | |
| 280 | +} | |
| 281 | + | |
| 282 | +static void tsc2102_audio_set_format(struct tsc210x_state_s *s) | |
| 283 | +{ | |
| 284 | + int enable; | |
| 285 | + const struct tsc210x_rate_info_s *rate; | |
| 286 | + audsettings_t fmt; | |
| 287 | + | |
| 288 | + if (s->dac_voice[0]) { | |
| 289 | + tsc210x_out_flush(s, s->codec.out.len); | |
| 290 | + s->codec.out.size = 0; | |
| 291 | + AUD_set_active_out(s->dac_voice[0], 0); | |
| 292 | + AUD_close_out(&s->card, s->dac_voice[0]); | |
| 293 | + s->dac_voice[0] = 0; | |
| 294 | + } | |
| 295 | + | |
| 296 | + enable = | |
| 297 | + (~s->dac_power & (1 << 15)) && /* PWDNC */ | |
| 298 | + (~s->dac_power & (1 << 10)); /* DAPWDN */ | |
| 299 | + if (!enable) | |
| 300 | + return; | |
| 301 | + | |
| 302 | + for (rate = tsc2102_rates; rate->rate; rate ++) | |
| 303 | + if (rate->dsor == (s->audio_ctrl1 & 0x3f) && /* DACFS */ | |
| 304 | + rate->fsref == ((s->audio_ctrl3 >> 13) & 1))/* REFFS */ | |
| 305 | + break; | |
| 306 | + if (!rate->rate) { | |
| 307 | + printf("%s: unknown sampling rate configured\n", __FUNCTION__); | |
| 308 | + return; | |
| 309 | + } | |
| 310 | + | |
| 311 | + /* Force our own sampling rate even in slave DAC mode */ | |
| 312 | + fmt.endianness = 0; | |
| 313 | + fmt.nchannels = 2; | |
| 314 | + fmt.freq = rate->rate; | |
| 315 | + fmt.fmt = AUD_FMT_S16; | |
| 316 | + | |
| 317 | + s->dac_voice[0] = AUD_open_out(&s->card, s->dac_voice[0], | |
| 318 | + "tsc2102.sink", s, (void *) tsc210x_audio_out_cb, &fmt); | |
| 319 | + if (s->dac_voice[0]) | |
| 320 | + AUD_set_active_out(s->dac_voice[0], 1); | |
| 321 | +} | |
| 322 | + | |
| 177 | 323 | static uint16_t tsc2102_data_register_read(struct tsc210x_state_s *s, int reg) |
| 178 | 324 | { |
| 179 | 325 | switch (reg) { |
| ... | ... | @@ -437,6 +583,8 @@ static void tsc2102_audio_register_write( |
| 437 | 583 | fprintf(stderr, "tsc2102_audio_register_write: " |
| 438 | 584 | "wrong value written into Audio 1\n"); |
| 439 | 585 | #endif |
| 586 | + if (s->audio) | |
| 587 | + tsc2102_audio_set_format(s); | |
| 440 | 588 | return; |
| 441 | 589 | |
| 442 | 590 | case 0x01: |
| ... | ... | @@ -479,6 +627,8 @@ static void tsc2102_audio_register_write( |
| 479 | 627 | fprintf(stderr, "tsc2102_audio_register_write: " |
| 480 | 628 | "wrong value written into Power\n"); |
| 481 | 629 | #endif |
| 630 | + if (s->audio) | |
| 631 | + tsc2102_audio_set_format(s); | |
| 482 | 632 | return; |
| 483 | 633 | |
| 484 | 634 | case 0x06: /* Audio Control 3 */ |
| ... | ... | @@ -489,6 +639,8 @@ static void tsc2102_audio_register_write( |
| 489 | 639 | fprintf(stderr, "tsc2102_audio_register_write: " |
| 490 | 640 | "wrong value written into Audio 3\n"); |
| 491 | 641 | #endif |
| 642 | + if (s->audio) | |
| 643 | + tsc2102_audio_set_format(s); | |
| 492 | 644 | return; |
| 493 | 645 | |
| 494 | 646 | case 0x07: /* LCH_BASS_BOOST_N0 */ |
| ... | ... | @@ -718,6 +870,20 @@ static void tsc210x_touchscreen_event(void *opaque, |
| 718 | 870 | tsc210x_pin_update(s); |
| 719 | 871 | } |
| 720 | 872 | |
| 873 | +static void tsc210x_i2s_swallow(struct tsc210x_state_s *s) | |
| 874 | +{ | |
| 875 | + if (s->dac_voice[0]) | |
| 876 | + tsc210x_out_flush(s, s->codec.out.len); | |
| 877 | + else | |
| 878 | + s->codec.out.len = 0; | |
| 879 | +} | |
| 880 | + | |
| 881 | +static void tsc210x_i2s_set_rate(struct tsc210x_state_s *s, int in, int out) | |
| 882 | +{ | |
| 883 | + s->i2s_tx_rate = out; | |
| 884 | + s->i2s_rx_rate = in; | |
| 885 | +} | |
| 886 | + | |
| 721 | 887 | static void tsc210x_save(QEMUFile *f, void *opaque) |
| 722 | 888 | { |
| 723 | 889 | struct tsc210x_state_s *s = (struct tsc210x_state_s *) opaque; |
| ... | ... | @@ -817,7 +983,7 @@ static int tsc210x_load(QEMUFile *f, void *opaque, int version_id) |
| 817 | 983 | |
| 818 | 984 | static int tsc2102_iid = 0; |
| 819 | 985 | |
| 820 | -struct uwire_slave_s *tsc2102_init(qemu_irq pint) | |
| 986 | +struct uwire_slave_s *tsc2102_init(qemu_irq pint, AudioState *audio) | |
| 821 | 987 | { |
| 822 | 988 | struct tsc210x_state_s *s; |
| 823 | 989 | |
| ... | ... | @@ -830,19 +996,37 @@ struct uwire_slave_s *tsc2102_init(qemu_irq pint) |
| 830 | 996 | s->precision = s->nextprecision = 0; |
| 831 | 997 | s->timer = qemu_new_timer(vm_clock, tsc210x_timer_tick, s); |
| 832 | 998 | s->pint = pint; |
| 999 | + s->name = "tsc2102"; | |
| 1000 | + s->audio = audio; | |
| 833 | 1001 | |
| 834 | 1002 | s->chip.opaque = s; |
| 835 | 1003 | s->chip.send = (void *) tsc210x_write; |
| 836 | 1004 | s->chip.receive = (void *) tsc210x_read; |
| 837 | 1005 | |
| 1006 | + s->codec.opaque = s; | |
| 1007 | + s->codec.tx_swallow = (void *) tsc210x_i2s_swallow; | |
| 1008 | + s->codec.set_rate = (void *) tsc210x_i2s_set_rate; | |
| 1009 | + s->codec.in.fifo = s->in_fifo; | |
| 1010 | + s->codec.out.fifo = s->out_fifo; | |
| 1011 | + | |
| 838 | 1012 | tsc210x_reset(s); |
| 839 | 1013 | |
| 840 | 1014 | qemu_add_mouse_event_handler(tsc210x_touchscreen_event, s, 1, |
| 841 | 1015 | "QEMU TSC2102-driven Touchscreen"); |
| 842 | 1016 | |
| 1017 | + if (s->audio) | |
| 1018 | + AUD_register_card(s->audio, s->name, &s->card); | |
| 1019 | + | |
| 843 | 1020 | qemu_register_reset((void *) tsc210x_reset, s); |
| 844 | - register_savevm("tsc2102", tsc2102_iid ++, 0, | |
| 1021 | + register_savevm(s->name, tsc2102_iid ++, 0, | |
| 845 | 1022 | tsc210x_save, tsc210x_load, s); |
| 846 | 1023 | |
| 847 | 1024 | return &s->chip; |
| 848 | 1025 | } |
| 1026 | + | |
| 1027 | +struct i2s_codec_s *tsc210x_codec(struct uwire_slave_s *chip) | |
| 1028 | +{ | |
| 1029 | + struct tsc210x_state_s *s = (struct tsc210x_state_s *) chip->opaque; | |
| 1030 | + | |
| 1031 | + return &s->codec; | |
| 1032 | +} | ... | ... |
vl.h
| ... | ... | @@ -1667,7 +1667,8 @@ void qemu_get_ptimer(QEMUFile *f, ptimer_state *s); |
| 1667 | 1667 | #include "hw/omap.h" |
| 1668 | 1668 | |
| 1669 | 1669 | /* tsc210x.c */ |
| 1670 | -struct uwire_slave_s *tsc2102_init(qemu_irq pint); | |
| 1670 | +struct uwire_slave_s *tsc2102_init(qemu_irq pint, AudioState *audio); | |
| 1671 | +struct i2s_codec_s *tsc210x_codec(struct uwire_slave_s *chip); | |
| 1671 | 1672 | |
| 1672 | 1673 | /* mcf_uart.c */ |
| 1673 | 1674 | uint32_t mcf_uart_read(void *opaque, target_phys_addr_t addr); | ... | ... |