balrog | 7e7c5e4 | 2008-04-14 21:57:44 +0000 | [diff] [blame] | 1 | /* |
| 2 | * CBUS three-pin bus and the Retu / Betty / Tahvo / Vilma / Avilma / |
| 3 | * Hinku / Vinku / Ahne / Pihi chips used in various Nokia platforms. |
| 4 | * Based on reverse-engineering of a linux driver. |
| 5 | * |
| 6 | * Copyright (C) 2008 Nokia Corporation |
| 7 | * Written by Andrzej Zaborowski <andrew@openedhand.com> |
| 8 | * |
| 9 | * This program is free software; you can redistribute it and/or |
| 10 | * modify it under the terms of the GNU General Public License as |
| 11 | * published by the Free Software Foundation; either version 2 or |
| 12 | * (at your option) version 3 of the License. |
| 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. See the |
| 17 | * GNU General Public License for more details. |
| 18 | * |
aurel32 | fad6cb1 | 2009-01-04 22:05:52 +0000 | [diff] [blame] | 19 | * You should have received a copy of the GNU General Public License along |
Blue Swirl | 8167ee8 | 2009-07-16 20:47:01 +0000 | [diff] [blame] | 20 | * with this program; if not, see <http://www.gnu.org/licenses/>. |
balrog | 7e7c5e4 | 2008-04-14 21:57:44 +0000 | [diff] [blame] | 21 | */ |
| 22 | |
| 23 | #include "qemu-common.h" |
| 24 | #include "irq.h" |
| 25 | #include "devices.h" |
| 26 | #include "sysemu.h" |
| 27 | |
| 28 | //#define DEBUG |
| 29 | |
Paul Brook | bc24a22 | 2009-05-10 01:44:56 +0100 | [diff] [blame] | 30 | typedef struct { |
| 31 | void *opaque; |
| 32 | void (*io)(void *opaque, int rw, int reg, uint16_t *val); |
| 33 | int addr; |
| 34 | } CBusSlave; |
| 35 | |
| 36 | typedef struct { |
| 37 | CBus cbus; |
balrog | 7e7c5e4 | 2008-04-14 21:57:44 +0000 | [diff] [blame] | 38 | |
| 39 | int sel; |
| 40 | int dat; |
| 41 | int clk; |
| 42 | int bit; |
| 43 | int dir; |
| 44 | uint16_t val; |
| 45 | qemu_irq dat_out; |
| 46 | |
| 47 | int addr; |
| 48 | int reg; |
| 49 | int rw; |
| 50 | enum { |
| 51 | cbus_address, |
| 52 | cbus_value, |
| 53 | } cycle; |
| 54 | |
Paul Brook | bc24a22 | 2009-05-10 01:44:56 +0100 | [diff] [blame] | 55 | CBusSlave *slave[8]; |
| 56 | } CBusPriv; |
balrog | 7e7c5e4 | 2008-04-14 21:57:44 +0000 | [diff] [blame] | 57 | |
Paul Brook | bc24a22 | 2009-05-10 01:44:56 +0100 | [diff] [blame] | 58 | static void cbus_io(CBusPriv *s) |
balrog | 7e7c5e4 | 2008-04-14 21:57:44 +0000 | [diff] [blame] | 59 | { |
| 60 | if (s->slave[s->addr]) |
| 61 | s->slave[s->addr]->io(s->slave[s->addr]->opaque, |
| 62 | s->rw, s->reg, &s->val); |
| 63 | else |
Paul Brook | 2ac7117 | 2009-05-08 02:35:15 +0100 | [diff] [blame] | 64 | hw_error("%s: bad slave address %i\n", __FUNCTION__, s->addr); |
balrog | 7e7c5e4 | 2008-04-14 21:57:44 +0000 | [diff] [blame] | 65 | } |
| 66 | |
Paul Brook | bc24a22 | 2009-05-10 01:44:56 +0100 | [diff] [blame] | 67 | static void cbus_cycle(CBusPriv *s) |
balrog | 7e7c5e4 | 2008-04-14 21:57:44 +0000 | [diff] [blame] | 68 | { |
| 69 | switch (s->cycle) { |
| 70 | case cbus_address: |
| 71 | s->addr = (s->val >> 6) & 7; |
| 72 | s->rw = (s->val >> 5) & 1; |
| 73 | s->reg = (s->val >> 0) & 0x1f; |
| 74 | |
| 75 | s->cycle = cbus_value; |
| 76 | s->bit = 15; |
| 77 | s->dir = !s->rw; |
| 78 | s->val = 0; |
| 79 | |
| 80 | if (s->rw) |
| 81 | cbus_io(s); |
| 82 | break; |
| 83 | |
| 84 | case cbus_value: |
| 85 | if (!s->rw) |
| 86 | cbus_io(s); |
| 87 | |
| 88 | s->cycle = cbus_address; |
| 89 | s->bit = 8; |
| 90 | s->dir = 1; |
| 91 | s->val = 0; |
| 92 | break; |
| 93 | } |
| 94 | } |
| 95 | |
| 96 | static void cbus_clk(void *opaque, int line, int level) |
| 97 | { |
Paul Brook | bc24a22 | 2009-05-10 01:44:56 +0100 | [diff] [blame] | 98 | CBusPriv *s = (CBusPriv *) opaque; |
balrog | 7e7c5e4 | 2008-04-14 21:57:44 +0000 | [diff] [blame] | 99 | |
| 100 | if (!s->sel && level && !s->clk) { |
| 101 | if (s->dir) |
| 102 | s->val |= s->dat << (s->bit --); |
| 103 | else |
| 104 | qemu_set_irq(s->dat_out, (s->val >> (s->bit --)) & 1); |
| 105 | |
| 106 | if (s->bit < 0) |
| 107 | cbus_cycle(s); |
| 108 | } |
| 109 | |
| 110 | s->clk = level; |
| 111 | } |
| 112 | |
| 113 | static void cbus_dat(void *opaque, int line, int level) |
| 114 | { |
Paul Brook | bc24a22 | 2009-05-10 01:44:56 +0100 | [diff] [blame] | 115 | CBusPriv *s = (CBusPriv *) opaque; |
balrog | 7e7c5e4 | 2008-04-14 21:57:44 +0000 | [diff] [blame] | 116 | |
| 117 | s->dat = level; |
| 118 | } |
| 119 | |
| 120 | static void cbus_sel(void *opaque, int line, int level) |
| 121 | { |
Paul Brook | bc24a22 | 2009-05-10 01:44:56 +0100 | [diff] [blame] | 122 | CBusPriv *s = (CBusPriv *) opaque; |
balrog | 7e7c5e4 | 2008-04-14 21:57:44 +0000 | [diff] [blame] | 123 | |
| 124 | if (!level) { |
| 125 | s->dir = 1; |
| 126 | s->bit = 8; |
| 127 | s->val = 0; |
| 128 | } |
| 129 | |
| 130 | s->sel = level; |
| 131 | } |
| 132 | |
Paul Brook | bc24a22 | 2009-05-10 01:44:56 +0100 | [diff] [blame] | 133 | CBus *cbus_init(qemu_irq dat) |
balrog | 7e7c5e4 | 2008-04-14 21:57:44 +0000 | [diff] [blame] | 134 | { |
Paul Brook | bc24a22 | 2009-05-10 01:44:56 +0100 | [diff] [blame] | 135 | CBusPriv *s = (CBusPriv *) qemu_mallocz(sizeof(*s)); |
balrog | 7e7c5e4 | 2008-04-14 21:57:44 +0000 | [diff] [blame] | 136 | |
| 137 | s->dat_out = dat; |
| 138 | s->cbus.clk = qemu_allocate_irqs(cbus_clk, s, 1)[0]; |
| 139 | s->cbus.dat = qemu_allocate_irqs(cbus_dat, s, 1)[0]; |
| 140 | s->cbus.sel = qemu_allocate_irqs(cbus_sel, s, 1)[0]; |
| 141 | |
| 142 | s->sel = 1; |
| 143 | s->clk = 0; |
| 144 | s->dat = 0; |
| 145 | |
| 146 | return &s->cbus; |
| 147 | } |
| 148 | |
Paul Brook | bc24a22 | 2009-05-10 01:44:56 +0100 | [diff] [blame] | 149 | void cbus_attach(CBus *bus, void *slave_opaque) |
balrog | 7e7c5e4 | 2008-04-14 21:57:44 +0000 | [diff] [blame] | 150 | { |
Paul Brook | bc24a22 | 2009-05-10 01:44:56 +0100 | [diff] [blame] | 151 | CBusSlave *slave = (CBusSlave *) slave_opaque; |
| 152 | CBusPriv *s = (CBusPriv *) bus; |
balrog | 7e7c5e4 | 2008-04-14 21:57:44 +0000 | [diff] [blame] | 153 | |
| 154 | s->slave[slave->addr] = slave; |
| 155 | } |
| 156 | |
| 157 | /* Retu/Vilma */ |
Paul Brook | bc24a22 | 2009-05-10 01:44:56 +0100 | [diff] [blame] | 158 | typedef struct { |
balrog | 7e7c5e4 | 2008-04-14 21:57:44 +0000 | [diff] [blame] | 159 | uint16_t irqst; |
| 160 | uint16_t irqen; |
| 161 | uint16_t cc[2]; |
| 162 | int channel; |
| 163 | uint16_t result[16]; |
| 164 | uint16_t sample; |
| 165 | uint16_t status; |
| 166 | |
| 167 | struct { |
| 168 | uint16_t cal; |
| 169 | } rtc; |
| 170 | |
| 171 | int is_vilma; |
| 172 | qemu_irq irq; |
Paul Brook | bc24a22 | 2009-05-10 01:44:56 +0100 | [diff] [blame] | 173 | CBusSlave cbus; |
| 174 | } CBusRetu; |
balrog | 7e7c5e4 | 2008-04-14 21:57:44 +0000 | [diff] [blame] | 175 | |
Paul Brook | bc24a22 | 2009-05-10 01:44:56 +0100 | [diff] [blame] | 176 | static void retu_interrupt_update(CBusRetu *s) |
balrog | 7e7c5e4 | 2008-04-14 21:57:44 +0000 | [diff] [blame] | 177 | { |
| 178 | qemu_set_irq(s->irq, s->irqst & ~s->irqen); |
| 179 | } |
| 180 | |
| 181 | #define RETU_REG_ASICR 0x00 /* (RO) ASIC ID & revision */ |
| 182 | #define RETU_REG_IDR 0x01 /* (T) Interrupt ID */ |
| 183 | #define RETU_REG_IMR 0x02 /* (RW) Interrupt mask */ |
| 184 | #define RETU_REG_RTCDSR 0x03 /* (RW) RTC seconds register */ |
| 185 | #define RETU_REG_RTCHMR 0x04 /* (RO) RTC hours and minutes reg */ |
| 186 | #define RETU_REG_RTCHMAR 0x05 /* (RW) RTC hours and minutes set reg */ |
| 187 | #define RETU_REG_RTCCALR 0x06 /* (RW) RTC calibration register */ |
| 188 | #define RETU_REG_ADCR 0x08 /* (RW) ADC result register */ |
| 189 | #define RETU_REG_ADCSCR 0x09 /* (RW) ADC sample control register */ |
| 190 | #define RETU_REG_AFCR 0x0a /* (RW) AFC register */ |
| 191 | #define RETU_REG_ANTIFR 0x0b /* (RW) AntiF register */ |
| 192 | #define RETU_REG_CALIBR 0x0c /* (RW) CalibR register*/ |
| 193 | #define RETU_REG_CCR1 0x0d /* (RW) Common control register 1 */ |
| 194 | #define RETU_REG_CCR2 0x0e /* (RW) Common control register 2 */ |
| 195 | #define RETU_REG_RCTRL_CLR 0x0f /* (T) Regulator clear register */ |
| 196 | #define RETU_REG_RCTRL_SET 0x10 /* (T) Regulator set register */ |
| 197 | #define RETU_REG_TXCR 0x11 /* (RW) TxC register */ |
| 198 | #define RETU_REG_STATUS 0x16 /* (RO) Status register */ |
| 199 | #define RETU_REG_WATCHDOG 0x17 /* (RW) Watchdog register */ |
| 200 | #define RETU_REG_AUDTXR 0x18 /* (RW) Audio Codec Tx register */ |
| 201 | #define RETU_REG_AUDPAR 0x19 /* (RW) AudioPA register */ |
| 202 | #define RETU_REG_AUDRXR1 0x1a /* (RW) Audio receive register 1 */ |
| 203 | #define RETU_REG_AUDRXR2 0x1b /* (RW) Audio receive register 2 */ |
| 204 | #define RETU_REG_SGR1 0x1c /* (RW) */ |
| 205 | #define RETU_REG_SCR1 0x1d /* (RW) */ |
| 206 | #define RETU_REG_SGR2 0x1e /* (RW) */ |
| 207 | #define RETU_REG_SCR2 0x1f /* (RW) */ |
| 208 | |
| 209 | /* Retu Interrupt sources */ |
| 210 | enum { |
| 211 | retu_int_pwr = 0, /* Power button */ |
| 212 | retu_int_char = 1, /* Charger */ |
| 213 | retu_int_rtcs = 2, /* Seconds */ |
| 214 | retu_int_rtcm = 3, /* Minutes */ |
| 215 | retu_int_rtcd = 4, /* Days */ |
| 216 | retu_int_rtca = 5, /* Alarm */ |
| 217 | retu_int_hook = 6, /* Hook */ |
| 218 | retu_int_head = 7, /* Headset */ |
| 219 | retu_int_adcs = 8, /* ADC sample */ |
| 220 | }; |
| 221 | |
| 222 | /* Retu ADC channel wiring */ |
| 223 | enum { |
| 224 | retu_adc_bsi = 1, /* BSI */ |
| 225 | retu_adc_batt_temp = 2, /* Battery temperature */ |
| 226 | retu_adc_chg_volt = 3, /* Charger voltage */ |
| 227 | retu_adc_head_det = 4, /* Headset detection */ |
| 228 | retu_adc_hook_det = 5, /* Hook detection */ |
| 229 | retu_adc_rf_gp = 6, /* RF GP */ |
| 230 | retu_adc_tx_det = 7, /* Wideband Tx detection */ |
| 231 | retu_adc_batt_volt = 8, /* Battery voltage */ |
| 232 | retu_adc_sens = 10, /* Light sensor */ |
| 233 | retu_adc_sens_temp = 11, /* Light sensor temperature */ |
| 234 | retu_adc_bbatt_volt = 12, /* Backup battery voltage */ |
| 235 | retu_adc_self_temp = 13, /* RETU temperature */ |
| 236 | }; |
| 237 | |
Paul Brook | bc24a22 | 2009-05-10 01:44:56 +0100 | [diff] [blame] | 238 | static inline uint16_t retu_read(CBusRetu *s, int reg) |
balrog | 7e7c5e4 | 2008-04-14 21:57:44 +0000 | [diff] [blame] | 239 | { |
| 240 | #ifdef DEBUG |
| 241 | printf("RETU read at %02x\n", reg); |
| 242 | #endif |
| 243 | |
| 244 | switch (reg) { |
| 245 | case RETU_REG_ASICR: |
| 246 | return 0x0215 | (s->is_vilma << 7); |
| 247 | |
| 248 | case RETU_REG_IDR: /* TODO: Or is this ffs(s->irqst)? */ |
| 249 | return s->irqst; |
| 250 | |
| 251 | case RETU_REG_IMR: |
| 252 | return s->irqen; |
| 253 | |
| 254 | case RETU_REG_RTCDSR: |
| 255 | case RETU_REG_RTCHMR: |
| 256 | case RETU_REG_RTCHMAR: |
| 257 | /* TODO */ |
| 258 | return 0x0000; |
| 259 | |
| 260 | case RETU_REG_RTCCALR: |
| 261 | return s->rtc.cal; |
| 262 | |
| 263 | case RETU_REG_ADCR: |
| 264 | return (s->channel << 10) | s->result[s->channel]; |
| 265 | case RETU_REG_ADCSCR: |
| 266 | return s->sample; |
| 267 | |
| 268 | case RETU_REG_AFCR: |
| 269 | case RETU_REG_ANTIFR: |
| 270 | case RETU_REG_CALIBR: |
| 271 | /* TODO */ |
| 272 | return 0x0000; |
| 273 | |
| 274 | case RETU_REG_CCR1: |
| 275 | return s->cc[0]; |
| 276 | case RETU_REG_CCR2: |
| 277 | return s->cc[1]; |
| 278 | |
| 279 | case RETU_REG_RCTRL_CLR: |
| 280 | case RETU_REG_RCTRL_SET: |
| 281 | case RETU_REG_TXCR: |
| 282 | /* TODO */ |
| 283 | return 0x0000; |
| 284 | |
| 285 | case RETU_REG_STATUS: |
| 286 | return s->status; |
| 287 | |
| 288 | case RETU_REG_WATCHDOG: |
| 289 | case RETU_REG_AUDTXR: |
| 290 | case RETU_REG_AUDPAR: |
| 291 | case RETU_REG_AUDRXR1: |
| 292 | case RETU_REG_AUDRXR2: |
| 293 | case RETU_REG_SGR1: |
| 294 | case RETU_REG_SCR1: |
| 295 | case RETU_REG_SGR2: |
| 296 | case RETU_REG_SCR2: |
| 297 | /* TODO */ |
| 298 | return 0x0000; |
| 299 | |
| 300 | default: |
Paul Brook | 2ac7117 | 2009-05-08 02:35:15 +0100 | [diff] [blame] | 301 | hw_error("%s: bad register %02x\n", __FUNCTION__, reg); |
balrog | 7e7c5e4 | 2008-04-14 21:57:44 +0000 | [diff] [blame] | 302 | } |
| 303 | } |
| 304 | |
Paul Brook | bc24a22 | 2009-05-10 01:44:56 +0100 | [diff] [blame] | 305 | static inline void retu_write(CBusRetu *s, int reg, uint16_t val) |
balrog | 7e7c5e4 | 2008-04-14 21:57:44 +0000 | [diff] [blame] | 306 | { |
| 307 | #ifdef DEBUG |
| 308 | printf("RETU write of %04x at %02x\n", val, reg); |
| 309 | #endif |
| 310 | |
| 311 | switch (reg) { |
| 312 | case RETU_REG_IDR: |
| 313 | s->irqst ^= val; |
| 314 | retu_interrupt_update(s); |
| 315 | break; |
| 316 | |
| 317 | case RETU_REG_IMR: |
| 318 | s->irqen = val; |
| 319 | retu_interrupt_update(s); |
| 320 | break; |
| 321 | |
| 322 | case RETU_REG_RTCDSR: |
| 323 | case RETU_REG_RTCHMAR: |
| 324 | /* TODO */ |
| 325 | break; |
| 326 | |
| 327 | case RETU_REG_RTCCALR: |
| 328 | s->rtc.cal = val; |
| 329 | break; |
| 330 | |
| 331 | case RETU_REG_ADCR: |
| 332 | s->channel = (val >> 10) & 0xf; |
| 333 | s->irqst |= 1 << retu_int_adcs; |
| 334 | retu_interrupt_update(s); |
| 335 | break; |
| 336 | case RETU_REG_ADCSCR: |
| 337 | s->sample &= ~val; |
| 338 | break; |
| 339 | |
| 340 | case RETU_REG_AFCR: |
| 341 | case RETU_REG_ANTIFR: |
| 342 | case RETU_REG_CALIBR: |
| 343 | |
| 344 | case RETU_REG_CCR1: |
| 345 | s->cc[0] = val; |
| 346 | break; |
| 347 | case RETU_REG_CCR2: |
| 348 | s->cc[1] = val; |
| 349 | break; |
| 350 | |
| 351 | case RETU_REG_RCTRL_CLR: |
| 352 | case RETU_REG_RCTRL_SET: |
| 353 | /* TODO */ |
| 354 | break; |
| 355 | |
| 356 | case RETU_REG_WATCHDOG: |
| 357 | if (val == 0 && (s->cc[0] & 2)) |
| 358 | qemu_system_shutdown_request(); |
| 359 | break; |
| 360 | |
| 361 | case RETU_REG_TXCR: |
| 362 | case RETU_REG_AUDTXR: |
| 363 | case RETU_REG_AUDPAR: |
| 364 | case RETU_REG_AUDRXR1: |
| 365 | case RETU_REG_AUDRXR2: |
| 366 | case RETU_REG_SGR1: |
| 367 | case RETU_REG_SCR1: |
| 368 | case RETU_REG_SGR2: |
| 369 | case RETU_REG_SCR2: |
| 370 | /* TODO */ |
| 371 | break; |
| 372 | |
| 373 | default: |
Paul Brook | 2ac7117 | 2009-05-08 02:35:15 +0100 | [diff] [blame] | 374 | hw_error("%s: bad register %02x\n", __FUNCTION__, reg); |
balrog | 7e7c5e4 | 2008-04-14 21:57:44 +0000 | [diff] [blame] | 375 | } |
| 376 | } |
| 377 | |
| 378 | static void retu_io(void *opaque, int rw, int reg, uint16_t *val) |
| 379 | { |
Paul Brook | bc24a22 | 2009-05-10 01:44:56 +0100 | [diff] [blame] | 380 | CBusRetu *s = (CBusRetu *) opaque; |
balrog | 7e7c5e4 | 2008-04-14 21:57:44 +0000 | [diff] [blame] | 381 | |
| 382 | if (rw) |
| 383 | *val = retu_read(s, reg); |
| 384 | else |
| 385 | retu_write(s, reg, *val); |
| 386 | } |
| 387 | |
| 388 | void *retu_init(qemu_irq irq, int vilma) |
| 389 | { |
Paul Brook | bc24a22 | 2009-05-10 01:44:56 +0100 | [diff] [blame] | 390 | CBusRetu *s = (CBusRetu *) qemu_mallocz(sizeof(*s)); |
balrog | 7e7c5e4 | 2008-04-14 21:57:44 +0000 | [diff] [blame] | 391 | |
| 392 | s->irq = irq; |
| 393 | s->irqen = 0xffff; |
| 394 | s->irqst = 0x0000; |
| 395 | s->status = 0x0020; |
| 396 | s->is_vilma = !!vilma; |
| 397 | s->rtc.cal = 0x01; |
| 398 | s->result[retu_adc_bsi] = 0x3c2; |
| 399 | s->result[retu_adc_batt_temp] = 0x0fc; |
| 400 | s->result[retu_adc_chg_volt] = 0x165; |
| 401 | s->result[retu_adc_head_det] = 123; |
| 402 | s->result[retu_adc_hook_det] = 1023; |
| 403 | s->result[retu_adc_rf_gp] = 0x11; |
| 404 | s->result[retu_adc_tx_det] = 0x11; |
| 405 | s->result[retu_adc_batt_volt] = 0x250; |
| 406 | s->result[retu_adc_sens] = 2; |
| 407 | s->result[retu_adc_sens_temp] = 0x11; |
| 408 | s->result[retu_adc_bbatt_volt] = 0x3d0; |
| 409 | s->result[retu_adc_self_temp] = 0x330; |
| 410 | |
| 411 | s->cbus.opaque = s; |
| 412 | s->cbus.io = retu_io; |
| 413 | s->cbus.addr = 1; |
| 414 | |
| 415 | return &s->cbus; |
| 416 | } |
| 417 | |
| 418 | void retu_key_event(void *retu, int state) |
| 419 | { |
Paul Brook | bc24a22 | 2009-05-10 01:44:56 +0100 | [diff] [blame] | 420 | CBusSlave *slave = (CBusSlave *) retu; |
| 421 | CBusRetu *s = (CBusRetu *) slave->opaque; |
balrog | 7e7c5e4 | 2008-04-14 21:57:44 +0000 | [diff] [blame] | 422 | |
| 423 | s->irqst |= 1 << retu_int_pwr; |
| 424 | retu_interrupt_update(s); |
| 425 | |
| 426 | if (state) |
| 427 | s->status &= ~(1 << 5); |
| 428 | else |
| 429 | s->status |= 1 << 5; |
| 430 | } |
| 431 | |
blueswir1 | b1d8e52 | 2008-10-26 13:43:07 +0000 | [diff] [blame] | 432 | #if 0 |
| 433 | static void retu_head_event(void *retu, int state) |
balrog | 7e7c5e4 | 2008-04-14 21:57:44 +0000 | [diff] [blame] | 434 | { |
Paul Brook | bc24a22 | 2009-05-10 01:44:56 +0100 | [diff] [blame] | 435 | CBusSlave *slave = (CBusSlave *) retu; |
| 436 | CBusRetu *s = (CBusRetu *) slave->opaque; |
balrog | 7e7c5e4 | 2008-04-14 21:57:44 +0000 | [diff] [blame] | 437 | |
| 438 | if ((s->cc[0] & 0x500) == 0x500) { /* TODO: Which bits? */ |
| 439 | /* TODO: reissue the interrupt every 100ms or so. */ |
| 440 | s->irqst |= 1 << retu_int_head; |
| 441 | retu_interrupt_update(s); |
| 442 | } |
| 443 | |
| 444 | if (state) |
| 445 | s->result[retu_adc_head_det] = 50; |
| 446 | else |
| 447 | s->result[retu_adc_head_det] = 123; |
| 448 | } |
| 449 | |
blueswir1 | b1d8e52 | 2008-10-26 13:43:07 +0000 | [diff] [blame] | 450 | static void retu_hook_event(void *retu, int state) |
balrog | 7e7c5e4 | 2008-04-14 21:57:44 +0000 | [diff] [blame] | 451 | { |
Paul Brook | bc24a22 | 2009-05-10 01:44:56 +0100 | [diff] [blame] | 452 | CBusSlave *slave = (CBusSlave *) retu; |
| 453 | CBusRetu *s = (CBusRetu *) slave->opaque; |
balrog | 7e7c5e4 | 2008-04-14 21:57:44 +0000 | [diff] [blame] | 454 | |
| 455 | if ((s->cc[0] & 0x500) == 0x500) { |
| 456 | /* TODO: reissue the interrupt every 100ms or so. */ |
| 457 | s->irqst |= 1 << retu_int_hook; |
| 458 | retu_interrupt_update(s); |
| 459 | } |
| 460 | |
| 461 | if (state) |
| 462 | s->result[retu_adc_hook_det] = 50; |
| 463 | else |
| 464 | s->result[retu_adc_hook_det] = 123; |
| 465 | } |
blueswir1 | b1d8e52 | 2008-10-26 13:43:07 +0000 | [diff] [blame] | 466 | #endif |
balrog | 7e7c5e4 | 2008-04-14 21:57:44 +0000 | [diff] [blame] | 467 | |
| 468 | /* Tahvo/Betty */ |
Paul Brook | bc24a22 | 2009-05-10 01:44:56 +0100 | [diff] [blame] | 469 | typedef struct { |
balrog | 7e7c5e4 | 2008-04-14 21:57:44 +0000 | [diff] [blame] | 470 | uint16_t irqst; |
| 471 | uint16_t irqen; |
| 472 | uint8_t charger; |
| 473 | uint8_t backlight; |
| 474 | uint16_t usbr; |
| 475 | uint16_t power; |
| 476 | |
| 477 | int is_betty; |
| 478 | qemu_irq irq; |
Paul Brook | bc24a22 | 2009-05-10 01:44:56 +0100 | [diff] [blame] | 479 | CBusSlave cbus; |
| 480 | } CBusTahvo; |
balrog | 7e7c5e4 | 2008-04-14 21:57:44 +0000 | [diff] [blame] | 481 | |
Paul Brook | bc24a22 | 2009-05-10 01:44:56 +0100 | [diff] [blame] | 482 | static void tahvo_interrupt_update(CBusTahvo *s) |
balrog | 7e7c5e4 | 2008-04-14 21:57:44 +0000 | [diff] [blame] | 483 | { |
| 484 | qemu_set_irq(s->irq, s->irqst & ~s->irqen); |
| 485 | } |
| 486 | |
| 487 | #define TAHVO_REG_ASICR 0x00 /* (RO) ASIC ID & revision */ |
| 488 | #define TAHVO_REG_IDR 0x01 /* (T) Interrupt ID */ |
| 489 | #define TAHVO_REG_IDSR 0x02 /* (RO) Interrupt status */ |
| 490 | #define TAHVO_REG_IMR 0x03 /* (RW) Interrupt mask */ |
| 491 | #define TAHVO_REG_CHAPWMR 0x04 /* (RW) Charger PWM */ |
| 492 | #define TAHVO_REG_LEDPWMR 0x05 /* (RW) LED PWM */ |
| 493 | #define TAHVO_REG_USBR 0x06 /* (RW) USB control */ |
| 494 | #define TAHVO_REG_RCR 0x07 /* (RW) Some kind of power management */ |
| 495 | #define TAHVO_REG_CCR1 0x08 /* (RW) Common control register 1 */ |
| 496 | #define TAHVO_REG_CCR2 0x09 /* (RW) Common control register 2 */ |
| 497 | #define TAHVO_REG_TESTR1 0x0a /* (RW) Test register 1 */ |
| 498 | #define TAHVO_REG_TESTR2 0x0b /* (RW) Test register 2 */ |
| 499 | #define TAHVO_REG_NOPR 0x0c /* (RW) Number of periods */ |
| 500 | #define TAHVO_REG_FRR 0x0d /* (RO) FR */ |
| 501 | |
Paul Brook | bc24a22 | 2009-05-10 01:44:56 +0100 | [diff] [blame] | 502 | static inline uint16_t tahvo_read(CBusTahvo *s, int reg) |
balrog | 7e7c5e4 | 2008-04-14 21:57:44 +0000 | [diff] [blame] | 503 | { |
| 504 | #ifdef DEBUG |
| 505 | printf("TAHVO read at %02x\n", reg); |
| 506 | #endif |
| 507 | |
| 508 | switch (reg) { |
| 509 | case TAHVO_REG_ASICR: |
| 510 | return 0x0021 | (s->is_betty ? 0x0b00 : 0x0300); /* 22 in N810 */ |
| 511 | |
| 512 | case TAHVO_REG_IDR: |
| 513 | case TAHVO_REG_IDSR: /* XXX: what does this do? */ |
| 514 | return s->irqst; |
| 515 | |
| 516 | case TAHVO_REG_IMR: |
| 517 | return s->irqen; |
| 518 | |
| 519 | case TAHVO_REG_CHAPWMR: |
| 520 | return s->charger; |
| 521 | |
| 522 | case TAHVO_REG_LEDPWMR: |
| 523 | return s->backlight; |
| 524 | |
| 525 | case TAHVO_REG_USBR: |
| 526 | return s->usbr; |
| 527 | |
| 528 | case TAHVO_REG_RCR: |
| 529 | return s->power; |
| 530 | |
| 531 | case TAHVO_REG_CCR1: |
| 532 | case TAHVO_REG_CCR2: |
| 533 | case TAHVO_REG_TESTR1: |
| 534 | case TAHVO_REG_TESTR2: |
| 535 | case TAHVO_REG_NOPR: |
| 536 | case TAHVO_REG_FRR: |
| 537 | return 0x0000; |
| 538 | |
| 539 | default: |
Paul Brook | 2ac7117 | 2009-05-08 02:35:15 +0100 | [diff] [blame] | 540 | hw_error("%s: bad register %02x\n", __FUNCTION__, reg); |
balrog | 7e7c5e4 | 2008-04-14 21:57:44 +0000 | [diff] [blame] | 541 | } |
| 542 | } |
| 543 | |
Paul Brook | bc24a22 | 2009-05-10 01:44:56 +0100 | [diff] [blame] | 544 | static inline void tahvo_write(CBusTahvo *s, int reg, uint16_t val) |
balrog | 7e7c5e4 | 2008-04-14 21:57:44 +0000 | [diff] [blame] | 545 | { |
| 546 | #ifdef DEBUG |
| 547 | printf("TAHVO write of %04x at %02x\n", val, reg); |
| 548 | #endif |
| 549 | |
| 550 | switch (reg) { |
| 551 | case TAHVO_REG_IDR: |
| 552 | s->irqst ^= val; |
| 553 | tahvo_interrupt_update(s); |
| 554 | break; |
| 555 | |
| 556 | case TAHVO_REG_IMR: |
| 557 | s->irqen = val; |
| 558 | tahvo_interrupt_update(s); |
| 559 | break; |
| 560 | |
| 561 | case TAHVO_REG_CHAPWMR: |
| 562 | s->charger = val; |
| 563 | break; |
| 564 | |
| 565 | case TAHVO_REG_LEDPWMR: |
| 566 | if (s->backlight != (val & 0x7f)) { |
| 567 | s->backlight = val & 0x7f; |
| 568 | printf("%s: LCD backlight now at %i / 127\n", |
| 569 | __FUNCTION__, s->backlight); |
| 570 | } |
| 571 | break; |
| 572 | |
| 573 | case TAHVO_REG_USBR: |
| 574 | s->usbr = val; |
| 575 | break; |
| 576 | |
| 577 | case TAHVO_REG_RCR: |
| 578 | s->power = val; |
| 579 | break; |
| 580 | |
| 581 | case TAHVO_REG_CCR1: |
| 582 | case TAHVO_REG_CCR2: |
| 583 | case TAHVO_REG_TESTR1: |
| 584 | case TAHVO_REG_TESTR2: |
| 585 | case TAHVO_REG_NOPR: |
| 586 | case TAHVO_REG_FRR: |
| 587 | break; |
| 588 | |
| 589 | default: |
Paul Brook | 2ac7117 | 2009-05-08 02:35:15 +0100 | [diff] [blame] | 590 | hw_error("%s: bad register %02x\n", __FUNCTION__, reg); |
balrog | 7e7c5e4 | 2008-04-14 21:57:44 +0000 | [diff] [blame] | 591 | } |
| 592 | } |
| 593 | |
| 594 | static void tahvo_io(void *opaque, int rw, int reg, uint16_t *val) |
| 595 | { |
Paul Brook | bc24a22 | 2009-05-10 01:44:56 +0100 | [diff] [blame] | 596 | CBusTahvo *s = (CBusTahvo *) opaque; |
balrog | 7e7c5e4 | 2008-04-14 21:57:44 +0000 | [diff] [blame] | 597 | |
| 598 | if (rw) |
| 599 | *val = tahvo_read(s, reg); |
| 600 | else |
| 601 | tahvo_write(s, reg, *val); |
| 602 | } |
| 603 | |
| 604 | void *tahvo_init(qemu_irq irq, int betty) |
| 605 | { |
Paul Brook | bc24a22 | 2009-05-10 01:44:56 +0100 | [diff] [blame] | 606 | CBusTahvo *s = (CBusTahvo *) qemu_mallocz(sizeof(*s)); |
balrog | 7e7c5e4 | 2008-04-14 21:57:44 +0000 | [diff] [blame] | 607 | |
| 608 | s->irq = irq; |
| 609 | s->irqen = 0xffff; |
| 610 | s->irqst = 0x0000; |
| 611 | s->is_betty = !!betty; |
| 612 | |
| 613 | s->cbus.opaque = s; |
| 614 | s->cbus.io = tahvo_io; |
| 615 | s->cbus.addr = 2; |
| 616 | |
| 617 | return &s->cbus; |
| 618 | } |