Commit 7e7c5e4c1ba5c9b7efcf1b0c1e34ea150c286e58
1 parent
a5d7eb65
Nokia N800 machine support (ARM).
Also add various peripherals: two miscellaneous Nokia CBUS chips, EPSON S1D13745 LCD/TV remote-framebuffer controller, TWL92230 - standard OMAP2 power management companion chip on i2c. Generic OneNAND flash memory, TMP105 temperature sensor on i2c. git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@4215 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
15 changed files
with
4540 additions
and
3 deletions
Makefile
| @@ -51,7 +51,8 @@ OBJS+=block.o | @@ -51,7 +51,8 @@ OBJS+=block.o | ||
| 51 | 51 | ||
| 52 | OBJS+=irq.o | 52 | OBJS+=irq.o |
| 53 | OBJS+=i2c.o smbus.o smbus_eeprom.o max7310.o max111x.o wm8750.o | 53 | OBJS+=i2c.o smbus.o smbus_eeprom.o max7310.o max111x.o wm8750.o |
| 54 | -OBJS+=ssd0303.o ssd0323.o ads7846.o stellaris_input.o | 54 | +OBJS+=ssd0303.o ssd0323.o ads7846.o stellaris_input.o twl92230.o |
| 55 | +OBJS+=tmp105.o | ||
| 55 | OBJS+=scsi-disk.o cdrom.o | 56 | OBJS+=scsi-disk.o cdrom.o |
| 56 | OBJS+=scsi-generic.o | 57 | OBJS+=scsi-generic.o |
| 57 | OBJS+=usb.o usb-hub.o usb-linux.o usb-hid.o usb-msd.o usb-wacom.o usb-serial.o | 58 | OBJS+=usb.o usb-hub.o usb-linux.o usb-hid.o usb-msd.o usb-wacom.o usb-serial.o |
Makefile.target
| @@ -612,6 +612,7 @@ OBJS+= spitz.o ide.o serial.o nand.o ecc.o | @@ -612,6 +612,7 @@ OBJS+= spitz.o ide.o serial.o nand.o ecc.o | ||
| 612 | OBJS+= omap1.o omap_lcdc.o omap_dma.o omap_clk.o omap_mmc.o omap_i2c.o | 612 | OBJS+= omap1.o omap_lcdc.o omap_dma.o omap_clk.o omap_mmc.o omap_i2c.o |
| 613 | OBJS+= omap2.o omap_dss.o | 613 | OBJS+= omap2.o omap_dss.o |
| 614 | OBJS+= palm.o tsc210x.o | 614 | OBJS+= palm.o tsc210x.o |
| 615 | +OBJS+= nseries.o blizzard.o onenand.o vga.o cbus.o | ||
| 615 | OBJS+= mst_fpga.o mainstone.o | 616 | OBJS+= mst_fpga.o mainstone.o |
| 616 | CPPFLAGS += -DHAS_AUDIO | 617 | CPPFLAGS += -DHAS_AUDIO |
| 617 | endif | 618 | endif |
hw/blizzard.c
0 → 100644
| 1 | +/* | ||
| 2 | + * Epson S1D13744/S1D13745 (Blizzard/Hailstorm/Tornado) LCD/TV controller. | ||
| 3 | + * | ||
| 4 | + * Copyright (C) 2008 Nokia Corporation | ||
| 5 | + * Written by Andrzej Zaborowski <andrew@openedhand.com> | ||
| 6 | + * | ||
| 7 | + * This program is free software; you can redistribute it and/or | ||
| 8 | + * modify it under the terms of the GNU General Public License as | ||
| 9 | + * published by the Free Software Foundation; either version 2 or | ||
| 10 | + * (at your option) version 3 of the License. | ||
| 11 | + * | ||
| 12 | + * This program is distributed in the hope that it will be useful, | ||
| 13 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | + * GNU General Public License for more details. | ||
| 16 | + * | ||
| 17 | + * You should have received a copy of the GNU General Public License | ||
| 18 | + * along with this program; if not, write to the Free Software | ||
| 19 | + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, | ||
| 20 | + * MA 02111-1307 USA | ||
| 21 | + */ | ||
| 22 | + | ||
| 23 | +#include "qemu-common.h" | ||
| 24 | +#include "sysemu.h" | ||
| 25 | +#include "console.h" | ||
| 26 | +#include "devices.h" | ||
| 27 | +#include "vga_int.h" | ||
| 28 | +#include "pixel_ops.h" | ||
| 29 | + | ||
| 30 | +typedef void (*blizzard_fn_t)(uint8_t *, const uint8_t *, unsigned int); | ||
| 31 | + | ||
| 32 | +struct blizzard_s { | ||
| 33 | + uint8_t reg; | ||
| 34 | + uint32_t addr; | ||
| 35 | + int swallow; | ||
| 36 | + | ||
| 37 | + int pll; | ||
| 38 | + int pll_range; | ||
| 39 | + int pll_ctrl; | ||
| 40 | + uint8_t pll_mode; | ||
| 41 | + uint8_t clksel; | ||
| 42 | + int memenable; | ||
| 43 | + int memrefresh; | ||
| 44 | + uint8_t timing[3]; | ||
| 45 | + int priority; | ||
| 46 | + | ||
| 47 | + uint8_t lcd_config; | ||
| 48 | + int x; | ||
| 49 | + int y; | ||
| 50 | + int skipx; | ||
| 51 | + int skipy; | ||
| 52 | + uint8_t hndp; | ||
| 53 | + uint8_t vndp; | ||
| 54 | + uint8_t hsync; | ||
| 55 | + uint8_t vsync; | ||
| 56 | + uint8_t pclk; | ||
| 57 | + uint8_t u; | ||
| 58 | + uint8_t v; | ||
| 59 | + uint8_t yrc[2]; | ||
| 60 | + int ix[2]; | ||
| 61 | + int iy[2]; | ||
| 62 | + int ox[2]; | ||
| 63 | + int oy[2]; | ||
| 64 | + | ||
| 65 | + int enable; | ||
| 66 | + int blank; | ||
| 67 | + int bpp; | ||
| 68 | + int invalidate; | ||
| 69 | + int mx[2]; | ||
| 70 | + int my[2]; | ||
| 71 | + uint8_t mode; | ||
| 72 | + uint8_t effect; | ||
| 73 | + uint8_t iformat; | ||
| 74 | + uint8_t source; | ||
| 75 | + DisplayState *state; | ||
| 76 | + blizzard_fn_t *line_fn_tab[2]; | ||
| 77 | + void *fb; | ||
| 78 | + | ||
| 79 | + uint8_t hssi_config[3]; | ||
| 80 | + uint8_t tv_config; | ||
| 81 | + uint8_t tv_timing[4]; | ||
| 82 | + uint8_t vbi; | ||
| 83 | + uint8_t tv_x; | ||
| 84 | + uint8_t tv_y; | ||
| 85 | + uint8_t tv_test; | ||
| 86 | + uint8_t tv_filter_config; | ||
| 87 | + uint8_t tv_filter_idx; | ||
| 88 | + uint8_t tv_filter_coeff[0x20]; | ||
| 89 | + uint8_t border_r; | ||
| 90 | + uint8_t border_g; | ||
| 91 | + uint8_t border_b; | ||
| 92 | + uint8_t gamma_config; | ||
| 93 | + uint8_t gamma_idx; | ||
| 94 | + uint8_t gamma_lut[0x100]; | ||
| 95 | + uint8_t matrix_ena; | ||
| 96 | + uint8_t matrix_coeff[0x12]; | ||
| 97 | + uint8_t matrix_r; | ||
| 98 | + uint8_t matrix_g; | ||
| 99 | + uint8_t matrix_b; | ||
| 100 | + uint8_t pm; | ||
| 101 | + uint8_t status; | ||
| 102 | + uint8_t rgbgpio_dir; | ||
| 103 | + uint8_t rgbgpio; | ||
| 104 | + uint8_t gpio_dir; | ||
| 105 | + uint8_t gpio; | ||
| 106 | + uint8_t gpio_edge[2]; | ||
| 107 | + uint8_t gpio_irq; | ||
| 108 | + uint8_t gpio_pdown; | ||
| 109 | + | ||
| 110 | + struct { | ||
| 111 | + int x; | ||
| 112 | + int y; | ||
| 113 | + int dx; | ||
| 114 | + int dy; | ||
| 115 | + int len; | ||
| 116 | + int buflen; | ||
| 117 | + void *buf; | ||
| 118 | + void *data; | ||
| 119 | + uint16_t *ptr; | ||
| 120 | + int angle; | ||
| 121 | + int pitch; | ||
| 122 | + blizzard_fn_t line_fn; | ||
| 123 | + } data; | ||
| 124 | +}; | ||
| 125 | + | ||
| 126 | +/* Bytes(!) per pixel */ | ||
| 127 | +static const int blizzard_iformat_bpp[0x10] = { | ||
| 128 | + 0, | ||
| 129 | + 2, /* RGB 5:6:5*/ | ||
| 130 | + 3, /* RGB 6:6:6 mode 1 */ | ||
| 131 | + 3, /* RGB 8:8:8 mode 1 */ | ||
| 132 | + 0, 0, | ||
| 133 | + 4, /* RGB 6:6:6 mode 2 */ | ||
| 134 | + 4, /* RGB 8:8:8 mode 2 */ | ||
| 135 | + 0, /* YUV 4:2:2 */ | ||
| 136 | + 0, /* YUV 4:2:0 */ | ||
| 137 | + 0, 0, 0, 0, 0, 0, | ||
| 138 | +}; | ||
| 139 | + | ||
| 140 | +static inline void blizzard_rgb2yuv(int r, int g, int b, | ||
| 141 | + int *y, int *u, int *v) | ||
| 142 | +{ | ||
| 143 | + *y = 0x10 + ((0x838 * r + 0x1022 * g + 0x322 * b) >> 13); | ||
| 144 | + *u = 0x80 + ((0xe0e * b - 0x04c1 * r - 0x94e * g) >> 13); | ||
| 145 | + *v = 0x80 + ((0xe0e * r - 0x0bc7 * g - 0x247 * b) >> 13); | ||
| 146 | +} | ||
| 147 | + | ||
| 148 | +static void blizzard_window(struct blizzard_s *s) | ||
| 149 | +{ | ||
| 150 | + uint8_t *src, *dst; | ||
| 151 | + int bypp[2]; | ||
| 152 | + int bypl[3]; | ||
| 153 | + int y; | ||
| 154 | + blizzard_fn_t fn = s->data.line_fn; | ||
| 155 | + | ||
| 156 | + if (!fn) | ||
| 157 | + return; | ||
| 158 | + if (s->mx[0] > s->data.x) | ||
| 159 | + s->mx[0] = s->data.x; | ||
| 160 | + if (s->my[0] > s->data.y) | ||
| 161 | + s->my[0] = s->data.y; | ||
| 162 | + if (s->mx[1] < s->data.x + s->data.dx) | ||
| 163 | + s->mx[1] = s->data.x + s->data.dx; | ||
| 164 | + if (s->my[1] < s->data.y + s->data.dy) | ||
| 165 | + s->my[1] = s->data.y + s->data.dy; | ||
| 166 | + | ||
| 167 | + bypp[0] = s->bpp; | ||
| 168 | + bypp[1] = (s->state->depth + 7) >> 3; | ||
| 169 | + bypl[0] = bypp[0] * s->data.pitch; | ||
| 170 | + bypl[1] = bypp[1] * s->x; | ||
| 171 | + bypl[2] = bypp[0] * s->data.dx; | ||
| 172 | + | ||
| 173 | + src = s->data.data; | ||
| 174 | + dst = s->fb + bypl[1] * s->data.y + bypp[1] * s->data.x; | ||
| 175 | + for (y = s->data.dy; y > 0; y --, src += bypl[0], dst += bypl[1]) | ||
| 176 | + fn(dst, src, bypl[2]); | ||
| 177 | +} | ||
| 178 | + | ||
| 179 | +static int blizzard_transfer_setup(struct blizzard_s *s) | ||
| 180 | +{ | ||
| 181 | + if (s->source > 3 || !s->bpp || | ||
| 182 | + s->ix[1] < s->ix[0] || s->iy[1] < s->iy[0]) | ||
| 183 | + return 0; | ||
| 184 | + | ||
| 185 | + s->data.angle = s->effect & 3; | ||
| 186 | + s->data.line_fn = s->line_fn_tab[!!s->data.angle][s->iformat]; | ||
| 187 | + s->data.x = s->ix[0]; | ||
| 188 | + s->data.y = s->iy[0]; | ||
| 189 | + s->data.dx = s->ix[1] - s->ix[0] + 1; | ||
| 190 | + s->data.dy = s->iy[1] - s->iy[0] + 1; | ||
| 191 | + s->data.len = s->bpp * s->data.dx * s->data.dy; | ||
| 192 | + s->data.pitch = s->data.dx; | ||
| 193 | + if (s->data.len > s->data.buflen) { | ||
| 194 | + s->data.buf = realloc(s->data.buf, s->data.len); | ||
| 195 | + s->data.buflen = s->data.len; | ||
| 196 | + } | ||
| 197 | + s->data.ptr = s->data.buf; | ||
| 198 | + s->data.data = s->data.buf; | ||
| 199 | + s->data.len /= 2; | ||
| 200 | + return 1; | ||
| 201 | +} | ||
| 202 | + | ||
| 203 | +static void blizzard_reset(struct blizzard_s *s) | ||
| 204 | +{ | ||
| 205 | + s->reg = 0; | ||
| 206 | + s->swallow = 0; | ||
| 207 | + | ||
| 208 | + s->pll = 9; | ||
| 209 | + s->pll_range = 1; | ||
| 210 | + s->pll_ctrl = 0x14; | ||
| 211 | + s->pll_mode = 0x32; | ||
| 212 | + s->clksel = 0x00; | ||
| 213 | + s->memenable = 0; | ||
| 214 | + s->memrefresh = 0x25c; | ||
| 215 | + s->timing[0] = 0x3f; | ||
| 216 | + s->timing[1] = 0x13; | ||
| 217 | + s->timing[2] = 0x21; | ||
| 218 | + s->priority = 0; | ||
| 219 | + | ||
| 220 | + s->lcd_config = 0x74; | ||
| 221 | + s->x = 8; | ||
| 222 | + s->y = 1; | ||
| 223 | + s->skipx = 0; | ||
| 224 | + s->skipy = 0; | ||
| 225 | + s->hndp = 3; | ||
| 226 | + s->vndp = 2; | ||
| 227 | + s->hsync = 1; | ||
| 228 | + s->vsync = 1; | ||
| 229 | + s->pclk = 0x80; | ||
| 230 | + | ||
| 231 | + s->ix[0] = 0; | ||
| 232 | + s->ix[1] = 0; | ||
| 233 | + s->iy[0] = 0; | ||
| 234 | + s->iy[1] = 0; | ||
| 235 | + s->ox[0] = 0; | ||
| 236 | + s->ox[1] = 0; | ||
| 237 | + s->oy[0] = 0; | ||
| 238 | + s->oy[1] = 0; | ||
| 239 | + | ||
| 240 | + s->yrc[0] = 0x00; | ||
| 241 | + s->yrc[1] = 0x30; | ||
| 242 | + s->u = 0; | ||
| 243 | + s->v = 0; | ||
| 244 | + | ||
| 245 | + s->iformat = 3; | ||
| 246 | + s->source = 0; | ||
| 247 | + s->bpp = blizzard_iformat_bpp[s->iformat]; | ||
| 248 | + | ||
| 249 | + s->hssi_config[0] = 0x00; | ||
| 250 | + s->hssi_config[1] = 0x00; | ||
| 251 | + s->hssi_config[2] = 0x01; | ||
| 252 | + s->tv_config = 0x00; | ||
| 253 | + s->tv_timing[0] = 0x00; | ||
| 254 | + s->tv_timing[1] = 0x00; | ||
| 255 | + s->tv_timing[2] = 0x00; | ||
| 256 | + s->tv_timing[3] = 0x00; | ||
| 257 | + s->vbi = 0x10; | ||
| 258 | + s->tv_x = 0x14; | ||
| 259 | + s->tv_y = 0x03; | ||
| 260 | + s->tv_test = 0x00; | ||
| 261 | + s->tv_filter_config = 0x80; | ||
| 262 | + s->tv_filter_idx = 0x00; | ||
| 263 | + s->border_r = 0x10; | ||
| 264 | + s->border_g = 0x80; | ||
| 265 | + s->border_b = 0x80; | ||
| 266 | + s->gamma_config = 0x00; | ||
| 267 | + s->gamma_idx = 0x00; | ||
| 268 | + s->matrix_ena = 0x00; | ||
| 269 | + memset(&s->matrix_coeff, 0, sizeof(s->matrix_coeff)); | ||
| 270 | + s->matrix_r = 0x00; | ||
| 271 | + s->matrix_g = 0x00; | ||
| 272 | + s->matrix_b = 0x00; | ||
| 273 | + s->pm = 0x02; | ||
| 274 | + s->status = 0x00; | ||
| 275 | + s->rgbgpio_dir = 0x00; | ||
| 276 | + s->gpio_dir = 0x00; | ||
| 277 | + s->gpio_edge[0] = 0x00; | ||
| 278 | + s->gpio_edge[1] = 0x00; | ||
| 279 | + s->gpio_irq = 0x00; | ||
| 280 | + s->gpio_pdown = 0xff; | ||
| 281 | +} | ||
| 282 | + | ||
| 283 | +static inline void blizzard_invalidate_display(void *opaque) { | ||
| 284 | + struct blizzard_s *s = (struct blizzard_s *) opaque; | ||
| 285 | + | ||
| 286 | + s->invalidate = 1; | ||
| 287 | +} | ||
| 288 | + | ||
| 289 | +static uint16_t blizzard_reg_read(void *opaque, uint8_t reg) | ||
| 290 | +{ | ||
| 291 | + struct blizzard_s *s = (struct blizzard_s *) opaque; | ||
| 292 | + | ||
| 293 | + switch (reg) { | ||
| 294 | + case 0x00: /* Revision Code */ | ||
| 295 | + return 0xa5; | ||
| 296 | + | ||
| 297 | + case 0x02: /* Configuration Readback */ | ||
| 298 | + return 0x83; /* Macrovision OK, CNF[2:0] = 3 */ | ||
| 299 | + | ||
| 300 | + case 0x04: /* PLL M-Divider */ | ||
| 301 | + return (s->pll - 1) | (1 << 7); | ||
| 302 | + case 0x06: /* PLL Lock Range Control */ | ||
| 303 | + return s->pll_range; | ||
| 304 | + case 0x08: /* PLL Lock Synthesis Control 0 */ | ||
| 305 | + return s->pll_ctrl & 0xff; | ||
| 306 | + case 0x0a: /* PLL Lock Synthesis Control 1 */ | ||
| 307 | + return s->pll_ctrl >> 8; | ||
| 308 | + case 0x0c: /* PLL Mode Control 0 */ | ||
| 309 | + return s->pll_mode; | ||
| 310 | + | ||
| 311 | + case 0x0e: /* Clock-Source Select */ | ||
| 312 | + return s->clksel; | ||
| 313 | + | ||
| 314 | + case 0x10: /* Memory Controller Activate */ | ||
| 315 | + case 0x14: /* Memory Controller Bank 0 Status Flag */ | ||
| 316 | + return s->memenable; | ||
| 317 | + | ||
| 318 | + case 0x18: /* Auto-Refresh Interval Setting 0 */ | ||
| 319 | + return s->memrefresh & 0xff; | ||
| 320 | + case 0x1a: /* Auto-Refresh Interval Setting 1 */ | ||
| 321 | + return s->memrefresh >> 8; | ||
| 322 | + | ||
| 323 | + case 0x1c: /* Power-On Sequence Timing Control */ | ||
| 324 | + return s->timing[0]; | ||
| 325 | + case 0x1e: /* Timing Control 0 */ | ||
| 326 | + return s->timing[1]; | ||
| 327 | + case 0x20: /* Timing Control 1 */ | ||
| 328 | + return s->timing[2]; | ||
| 329 | + | ||
| 330 | + case 0x24: /* Arbitration Priority Control */ | ||
| 331 | + return s->priority; | ||
| 332 | + | ||
| 333 | + case 0x28: /* LCD Panel Configuration */ | ||
| 334 | + return s->lcd_config; | ||
| 335 | + | ||
| 336 | + case 0x2a: /* LCD Horizontal Display Width */ | ||
| 337 | + return s->x >> 3; | ||
| 338 | + case 0x2c: /* LCD Horizontal Non-display Period */ | ||
| 339 | + return s->hndp; | ||
| 340 | + case 0x2e: /* LCD Vertical Display Height 0 */ | ||
| 341 | + return s->y & 0xff; | ||
| 342 | + case 0x30: /* LCD Vertical Display Height 1 */ | ||
| 343 | + return s->y >> 8; | ||
| 344 | + case 0x32: /* LCD Vertical Non-display Period */ | ||
| 345 | + return s->vndp; | ||
| 346 | + case 0x34: /* LCD HS Pulse-width */ | ||
| 347 | + return s->hsync; | ||
| 348 | + case 0x36: /* LCd HS Pulse Start Position */ | ||
| 349 | + return s->skipx >> 3; | ||
| 350 | + case 0x38: /* LCD VS Pulse-width */ | ||
| 351 | + return s->vsync; | ||
| 352 | + case 0x3a: /* LCD VS Pulse Start Position */ | ||
| 353 | + return s->skipy; | ||
| 354 | + | ||
| 355 | + case 0x3c: /* PCLK Polarity */ | ||
| 356 | + return s->pclk; | ||
| 357 | + | ||
| 358 | + case 0x3e: /* High-speed Serial Interface Tx Configuration Port 0 */ | ||
| 359 | + return s->hssi_config[0]; | ||
| 360 | + case 0x40: /* High-speed Serial Interface Tx Configuration Port 1 */ | ||
| 361 | + return s->hssi_config[1]; | ||
| 362 | + case 0x42: /* High-speed Serial Interface Tx Mode */ | ||
| 363 | + return s->hssi_config[2]; | ||
| 364 | + case 0x44: /* TV Display Configuration */ | ||
| 365 | + return s->tv_config; | ||
| 366 | + case 0x46 ... 0x4c: /* TV Vertical Blanking Interval Data bits */ | ||
| 367 | + return s->tv_timing[(reg - 0x46) >> 1]; | ||
| 368 | + case 0x4e: /* VBI: Closed Caption / XDS Control / Status */ | ||
| 369 | + return s->vbi; | ||
| 370 | + case 0x50: /* TV Horizontal Start Position */ | ||
| 371 | + return s->tv_x; | ||
| 372 | + case 0x52: /* TV Vertical Start Position */ | ||
| 373 | + return s->tv_y; | ||
| 374 | + case 0x54: /* TV Test Pattern Setting */ | ||
| 375 | + return s->tv_test; | ||
| 376 | + case 0x56: /* TV Filter Setting */ | ||
| 377 | + return s->tv_filter_config; | ||
| 378 | + case 0x58: /* TV Filter Coefficient Index */ | ||
| 379 | + return s->tv_filter_idx; | ||
| 380 | + case 0x5a: /* TV Filter Coefficient Data */ | ||
| 381 | + if (s->tv_filter_idx < 0x20) | ||
| 382 | + return s->tv_filter_coeff[s->tv_filter_idx ++]; | ||
| 383 | + return 0; | ||
| 384 | + | ||
| 385 | + case 0x60: /* Input YUV/RGB Translate Mode 0 */ | ||
| 386 | + return s->yrc[0]; | ||
| 387 | + case 0x62: /* Input YUV/RGB Translate Mode 1 */ | ||
| 388 | + return s->yrc[1]; | ||
| 389 | + case 0x64: /* U Data Fix */ | ||
| 390 | + return s->u; | ||
| 391 | + case 0x66: /* V Data Fix */ | ||
| 392 | + return s->v; | ||
| 393 | + | ||
| 394 | + case 0x68: /* Display Mode */ | ||
| 395 | + return s->mode; | ||
| 396 | + | ||
| 397 | + case 0x6a: /* Special Effects */ | ||
| 398 | + return s->effect; | ||
| 399 | + | ||
| 400 | + case 0x6c: /* Input Window X Start Position 0 */ | ||
| 401 | + return s->ix[0] & 0xff; | ||
| 402 | + case 0x6e: /* Input Window X Start Position 1 */ | ||
| 403 | + return s->ix[0] >> 3; | ||
| 404 | + case 0x70: /* Input Window Y Start Position 0 */ | ||
| 405 | + return s->ix[0] & 0xff; | ||
| 406 | + case 0x72: /* Input Window Y Start Position 1 */ | ||
| 407 | + return s->ix[0] >> 3; | ||
| 408 | + case 0x74: /* Input Window X End Position 0 */ | ||
| 409 | + return s->ix[1] & 0xff; | ||
| 410 | + case 0x76: /* Input Window X End Position 1 */ | ||
| 411 | + return s->ix[1] >> 3; | ||
| 412 | + case 0x78: /* Input Window Y End Position 0 */ | ||
| 413 | + return s->ix[1] & 0xff; | ||
| 414 | + case 0x7a: /* Input Window Y End Position 1 */ | ||
| 415 | + return s->ix[1] >> 3; | ||
| 416 | + case 0x7c: /* Output Window X Start Position 0 */ | ||
| 417 | + return s->ox[0] & 0xff; | ||
| 418 | + case 0x7e: /* Output Window X Start Position 1 */ | ||
| 419 | + return s->ox[0] >> 3; | ||
| 420 | + case 0x80: /* Output Window Y Start Position 0 */ | ||
| 421 | + return s->oy[0] & 0xff; | ||
| 422 | + case 0x82: /* Output Window Y Start Position 1 */ | ||
| 423 | + return s->oy[0] >> 3; | ||
| 424 | + case 0x84: /* Output Window X End Position 0 */ | ||
| 425 | + return s->ox[1] & 0xff; | ||
| 426 | + case 0x86: /* Output Window X End Position 1 */ | ||
| 427 | + return s->ox[1] >> 3; | ||
| 428 | + case 0x88: /* Output Window Y End Position 0 */ | ||
| 429 | + return s->oy[1] & 0xff; | ||
| 430 | + case 0x8a: /* Output Window Y End Position 1 */ | ||
| 431 | + return s->oy[1] >> 3; | ||
| 432 | + | ||
| 433 | + case 0x8c: /* Input Data Format */ | ||
| 434 | + return s->iformat; | ||
| 435 | + case 0x8e: /* Data Source Select */ | ||
| 436 | + return s->source; | ||
| 437 | + case 0x90: /* Display Memory Data Port */ | ||
| 438 | + return 0; | ||
| 439 | + | ||
| 440 | + case 0xa8: /* Border Color 0 */ | ||
| 441 | + return s->border_r; | ||
| 442 | + case 0xaa: /* Border Color 1 */ | ||
| 443 | + return s->border_g; | ||
| 444 | + case 0xac: /* Border Color 2 */ | ||
| 445 | + return s->border_b; | ||
| 446 | + | ||
| 447 | + case 0xb4: /* Gamma Correction Enable */ | ||
| 448 | + return s->gamma_config; | ||
| 449 | + case 0xb6: /* Gamma Correction Table Index */ | ||
| 450 | + return s->gamma_idx; | ||
| 451 | + case 0xb8: /* Gamma Correction Table Data */ | ||
| 452 | + return s->gamma_lut[s->gamma_idx ++]; | ||
| 453 | + | ||
| 454 | + case 0xba: /* 3x3 Matrix Enable */ | ||
| 455 | + return s->matrix_ena; | ||
| 456 | + case 0xbc ... 0xde: /* Coefficient Registers */ | ||
| 457 | + return s->matrix_coeff[(reg - 0xbc) >> 1]; | ||
| 458 | + case 0xe0: /* 3x3 Matrix Red Offset */ | ||
| 459 | + return s->matrix_r; | ||
| 460 | + case 0xe2: /* 3x3 Matrix Green Offset */ | ||
| 461 | + return s->matrix_g; | ||
| 462 | + case 0xe4: /* 3x3 Matrix Blue Offset */ | ||
| 463 | + return s->matrix_b; | ||
| 464 | + | ||
| 465 | + case 0xe6: /* Power-save */ | ||
| 466 | + return s->pm; | ||
| 467 | + case 0xe8: /* Non-display Period Control / Status */ | ||
| 468 | + return s->status | (1 << 5); | ||
| 469 | + case 0xea: /* RGB Interface Control */ | ||
| 470 | + return s->rgbgpio_dir; | ||
| 471 | + case 0xec: /* RGB Interface Status */ | ||
| 472 | + return s->rgbgpio; | ||
| 473 | + case 0xee: /* General-purpose IO Pins Configuration */ | ||
| 474 | + return s->gpio_dir; | ||
| 475 | + case 0xf0: /* General-purpose IO Pins Status / Control */ | ||
| 476 | + return s->gpio; | ||
| 477 | + case 0xf2: /* GPIO Positive Edge Interrupt Trigger */ | ||
| 478 | + return s->gpio_edge[0]; | ||
| 479 | + case 0xf4: /* GPIO Negative Edge Interrupt Trigger */ | ||
| 480 | + return s->gpio_edge[1]; | ||
| 481 | + case 0xf6: /* GPIO Interrupt Status */ | ||
| 482 | + return s->gpio_irq; | ||
| 483 | + case 0xf8: /* GPIO Pull-down Control */ | ||
| 484 | + return s->gpio_pdown; | ||
| 485 | + | ||
| 486 | + default: | ||
| 487 | + fprintf(stderr, "%s: unknown register %02x\n", __FUNCTION__, reg); | ||
| 488 | + return 0; | ||
| 489 | + } | ||
| 490 | +} | ||
| 491 | + | ||
| 492 | +static void blizzard_reg_write(void *opaque, uint8_t reg, uint16_t value) | ||
| 493 | +{ | ||
| 494 | + struct blizzard_s *s = (struct blizzard_s *) opaque; | ||
| 495 | + | ||
| 496 | + switch (reg) { | ||
| 497 | + case 0x04: /* PLL M-Divider */ | ||
| 498 | + s->pll = (value & 0x3f) + 1; | ||
| 499 | + break; | ||
| 500 | + case 0x06: /* PLL Lock Range Control */ | ||
| 501 | + s->pll_range = value & 3; | ||
| 502 | + break; | ||
| 503 | + case 0x08: /* PLL Lock Synthesis Control 0 */ | ||
| 504 | + s->pll_ctrl &= 0xf00; | ||
| 505 | + s->pll_ctrl |= (value << 0) & 0x0ff; | ||
| 506 | + break; | ||
| 507 | + case 0x0a: /* PLL Lock Synthesis Control 1 */ | ||
| 508 | + s->pll_ctrl &= 0x0ff; | ||
| 509 | + s->pll_ctrl |= (value << 8) & 0xf00; | ||
| 510 | + break; | ||
| 511 | + case 0x0c: /* PLL Mode Control 0 */ | ||
| 512 | + s->pll_mode = value & 0x77; | ||
| 513 | + if ((value & 3) == 0 || (value & 3) == 3) | ||
| 514 | + fprintf(stderr, "%s: wrong PLL Control bits (%i)\n", | ||
| 515 | + __FUNCTION__, value & 3); | ||
| 516 | + break; | ||
| 517 | + | ||
| 518 | + case 0x0e: /* Clock-Source Select */ | ||
| 519 | + s->clksel = value & 0xff; | ||
| 520 | + break; | ||
| 521 | + | ||
| 522 | + case 0x10: /* Memory Controller Activate */ | ||
| 523 | + s->memenable = value & 1; | ||
| 524 | + break; | ||
| 525 | + case 0x14: /* Memory Controller Bank 0 Status Flag */ | ||
| 526 | + break; | ||
| 527 | + | ||
| 528 | + case 0x18: /* Auto-Refresh Interval Setting 0 */ | ||
| 529 | + s->memrefresh &= 0xf00; | ||
| 530 | + s->memrefresh |= (value << 0) & 0x0ff; | ||
| 531 | + break; | ||
| 532 | + case 0x1a: /* Auto-Refresh Interval Setting 1 */ | ||
| 533 | + s->memrefresh &= 0x0ff; | ||
| 534 | + s->memrefresh |= (value << 8) & 0xf00; | ||
| 535 | + break; | ||
| 536 | + | ||
| 537 | + case 0x1c: /* Power-On Sequence Timing Control */ | ||
| 538 | + s->timing[0] = value & 0x7f; | ||
| 539 | + break; | ||
| 540 | + case 0x1e: /* Timing Control 0 */ | ||
| 541 | + s->timing[1] = value & 0x17; | ||
| 542 | + break; | ||
| 543 | + case 0x20: /* Timing Control 1 */ | ||
| 544 | + s->timing[2] = value & 0x35; | ||
| 545 | + break; | ||
| 546 | + | ||
| 547 | + case 0x24: /* Arbitration Priority Control */ | ||
| 548 | + s->priority = value & 1; | ||
| 549 | + break; | ||
| 550 | + | ||
| 551 | + case 0x28: /* LCD Panel Configuration */ | ||
| 552 | + s->lcd_config = value & 0xff; | ||
| 553 | + if (value & (1 << 7)) | ||
| 554 | + fprintf(stderr, "%s: data swap not supported!\n", __FUNCTION__); | ||
| 555 | + break; | ||
| 556 | + | ||
| 557 | + case 0x2a: /* LCD Horizontal Display Width */ | ||
| 558 | + s->x = value << 3; | ||
| 559 | + break; | ||
| 560 | + case 0x2c: /* LCD Horizontal Non-display Period */ | ||
| 561 | + s->hndp = value & 0xff; | ||
| 562 | + break; | ||
| 563 | + case 0x2e: /* LCD Vertical Display Height 0 */ | ||
| 564 | + s->y &= 0x300; | ||
| 565 | + s->y |= (value << 0) & 0x0ff; | ||
| 566 | + break; | ||
| 567 | + case 0x30: /* LCD Vertical Display Height 1 */ | ||
| 568 | + s->y &= 0x0ff; | ||
| 569 | + s->y |= (value << 8) & 0x300; | ||
| 570 | + break; | ||
| 571 | + case 0x32: /* LCD Vertical Non-display Period */ | ||
| 572 | + s->vndp = value & 0xff; | ||
| 573 | + break; | ||
| 574 | + case 0x34: /* LCD HS Pulse-width */ | ||
| 575 | + s->hsync = value & 0xff; | ||
| 576 | + break; | ||
| 577 | + case 0x36: /* LCD HS Pulse Start Position */ | ||
| 578 | + s->skipx = value & 0xff; | ||
| 579 | + break; | ||
| 580 | + case 0x38: /* LCD VS Pulse-width */ | ||
| 581 | + s->vsync = value & 0xbf; | ||
| 582 | + break; | ||
| 583 | + case 0x3a: /* LCD VS Pulse Start Position */ | ||
| 584 | + s->skipy = value & 0xff; | ||
| 585 | + break; | ||
| 586 | + | ||
| 587 | + case 0x3c: /* PCLK Polarity */ | ||
| 588 | + s->pclk = value & 0x82; | ||
| 589 | + /* Affects calculation of s->hndp, s->hsync and s->skipx. */ | ||
| 590 | + break; | ||
| 591 | + | ||
| 592 | + case 0x3e: /* High-speed Serial Interface Tx Configuration Port 0 */ | ||
| 593 | + s->hssi_config[0] = value; | ||
| 594 | + break; | ||
| 595 | + case 0x40: /* High-speed Serial Interface Tx Configuration Port 1 */ | ||
| 596 | + s->hssi_config[1] = value; | ||
| 597 | + if (((value >> 4) & 3) == 3) | ||
| 598 | + fprintf(stderr, "%s: Illegal active-data-links value\n", | ||
| 599 | + __FUNCTION__); | ||
| 600 | + break; | ||
| 601 | + case 0x42: /* High-speed Serial Interface Tx Mode */ | ||
| 602 | + s->hssi_config[2] = value & 0xbd; | ||
| 603 | + break; | ||
| 604 | + | ||
| 605 | + case 0x44: /* TV Display Configuration */ | ||
| 606 | + s->tv_config = value & 0xfe; | ||
| 607 | + break; | ||
| 608 | + case 0x46 ... 0x4c: /* TV Vertical Blanking Interval Data bits 0 */ | ||
| 609 | + s->tv_timing[(reg - 0x46) >> 1] = value; | ||
| 610 | + break; | ||
| 611 | + case 0x4e: /* VBI: Closed Caption / XDS Control / Status */ | ||
| 612 | + s->vbi = value; | ||
| 613 | + break; | ||
| 614 | + case 0x50: /* TV Horizontal Start Position */ | ||
| 615 | + s->tv_x = value; | ||
| 616 | + break; | ||
| 617 | + case 0x52: /* TV Vertical Start Position */ | ||
| 618 | + s->tv_y = value & 0x7f; | ||
| 619 | + break; | ||
| 620 | + case 0x54: /* TV Test Pattern Setting */ | ||
| 621 | + s->tv_test = value; | ||
| 622 | + break; | ||
| 623 | + case 0x56: /* TV Filter Setting */ | ||
| 624 | + s->tv_filter_config = value & 0xbf; | ||
| 625 | + break; | ||
| 626 | + case 0x58: /* TV Filter Coefficient Index */ | ||
| 627 | + s->tv_filter_idx = value & 0x1f; | ||
| 628 | + break; | ||
| 629 | + case 0x5a: /* TV Filter Coefficient Data */ | ||
| 630 | + if (s->tv_filter_idx < 0x20) | ||
| 631 | + s->tv_filter_coeff[s->tv_filter_idx ++] = value; | ||
| 632 | + break; | ||
| 633 | + | ||
| 634 | + case 0x60: /* Input YUV/RGB Translate Mode 0 */ | ||
| 635 | + s->yrc[0] = value & 0xb0; | ||
| 636 | + break; | ||
| 637 | + case 0x62: /* Input YUV/RGB Translate Mode 1 */ | ||
| 638 | + s->yrc[1] = value & 0x30; | ||
| 639 | + break; | ||
| 640 | + case 0x64: /* U Data Fix */ | ||
| 641 | + s->u = value & 0xff; | ||
| 642 | + break; | ||
| 643 | + case 0x66: /* V Data Fix */ | ||
| 644 | + s->v = value & 0xff; | ||
| 645 | + break; | ||
| 646 | + | ||
| 647 | + case 0x68: /* Display Mode */ | ||
| 648 | + if ((s->mode ^ value) & 3) | ||
| 649 | + s->invalidate = 1; | ||
| 650 | + s->mode = value & 0xb7; | ||
| 651 | + s->enable = value & 1; | ||
| 652 | + s->blank = (value >> 1) & 1; | ||
| 653 | + if (value & (1 << 4)) | ||
| 654 | + fprintf(stderr, "%s: Macrovision enable attempt!\n", __FUNCTION__); | ||
| 655 | + break; | ||
| 656 | + | ||
| 657 | + case 0x6a: /* Special Effects */ | ||
| 658 | + s->effect = value & 0xfb; | ||
| 659 | + break; | ||
| 660 | + | ||
| 661 | + case 0x6c: /* Input Window X Start Position 0 */ | ||
| 662 | + s->ix[0] &= 0x300; | ||
| 663 | + s->ix[0] |= (value << 0) & 0x0ff; | ||
| 664 | + break; | ||
| 665 | + case 0x6e: /* Input Window X Start Position 1 */ | ||
| 666 | + s->ix[0] &= 0x0ff; | ||
| 667 | + s->ix[0] |= (value << 8) & 0x300; | ||
| 668 | + break; | ||
| 669 | + case 0x70: /* Input Window Y Start Position 0 */ | ||
| 670 | + s->iy[0] &= 0x300; | ||
| 671 | + s->iy[0] |= (value << 0) & 0x0ff; | ||
| 672 | + break; | ||
| 673 | + case 0x72: /* Input Window Y Start Position 1 */ | ||
| 674 | + s->iy[0] &= 0x0ff; | ||
| 675 | + s->iy[0] |= (value << 8) & 0x300; | ||
| 676 | + break; | ||
| 677 | + case 0x74: /* Input Window X End Position 0 */ | ||
| 678 | + s->ix[1] &= 0x300; | ||
| 679 | + s->ix[1] |= (value << 0) & 0x0ff; | ||
| 680 | + break; | ||
| 681 | + case 0x76: /* Input Window X End Position 1 */ | ||
| 682 | + s->ix[1] &= 0x0ff; | ||
| 683 | + s->ix[1] |= (value << 8) & 0x300; | ||
| 684 | + break; | ||
| 685 | + case 0x78: /* Input Window Y End Position 0 */ | ||
| 686 | + s->iy[1] &= 0x300; | ||
| 687 | + s->iy[1] |= (value << 0) & 0x0ff; | ||
| 688 | + break; | ||
| 689 | + case 0x7a: /* Input Window Y End Position 1 */ | ||
| 690 | + s->iy[1] &= 0x0ff; | ||
| 691 | + s->iy[1] |= (value << 8) & 0x300; | ||
| 692 | + break; | ||
| 693 | + case 0x7c: /* Output Window X Start Position 0 */ | ||
| 694 | + s->ox[0] &= 0x300; | ||
| 695 | + s->ox[0] |= (value << 0) & 0x0ff; | ||
| 696 | + break; | ||
| 697 | + case 0x7e: /* Output Window X Start Position 1 */ | ||
| 698 | + s->ox[0] &= 0x0ff; | ||
| 699 | + s->ox[0] |= (value << 8) & 0x300; | ||
| 700 | + break; | ||
| 701 | + case 0x80: /* Output Window Y Start Position 0 */ | ||
| 702 | + s->oy[0] &= 0x300; | ||
| 703 | + s->oy[0] |= (value << 0) & 0x0ff; | ||
| 704 | + break; | ||
| 705 | + case 0x82: /* Output Window Y Start Position 1 */ | ||
| 706 | + s->oy[0] &= 0x0ff; | ||
| 707 | + s->oy[0] |= (value << 8) & 0x300; | ||
| 708 | + break; | ||
| 709 | + case 0x84: /* Output Window X End Position 0 */ | ||
| 710 | + s->ox[1] &= 0x300; | ||
| 711 | + s->ox[1] |= (value << 0) & 0x0ff; | ||
| 712 | + break; | ||
| 713 | + case 0x86: /* Output Window X End Position 1 */ | ||
| 714 | + s->ox[1] &= 0x0ff; | ||
| 715 | + s->ox[1] |= (value << 8) & 0x300; | ||
| 716 | + break; | ||
| 717 | + case 0x88: /* Output Window Y End Position 0 */ | ||
| 718 | + s->oy[1] &= 0x300; | ||
| 719 | + s->oy[1] |= (value << 0) & 0x0ff; | ||
| 720 | + break; | ||
| 721 | + case 0x8a: /* Output Window Y End Position 1 */ | ||
| 722 | + s->oy[1] &= 0x0ff; | ||
| 723 | + s->oy[1] |= (value << 8) & 0x300; | ||
| 724 | + break; | ||
| 725 | + | ||
| 726 | + case 0x8c: /* Input Data Format */ | ||
| 727 | + s->iformat = value & 0xf; | ||
| 728 | + s->bpp = blizzard_iformat_bpp[s->iformat]; | ||
| 729 | + if (!s->bpp) | ||
| 730 | + fprintf(stderr, "%s: Illegal or unsupported input format %x\n", | ||
| 731 | + __FUNCTION__, s->iformat); | ||
| 732 | + break; | ||
| 733 | + case 0x8e: /* Data Source Select */ | ||
| 734 | + s->source = value & 7; | ||
| 735 | + /* Currently all windows will be "destructive overlays". */ | ||
| 736 | + if ((!(s->effect & (1 << 3)) && (s->ix[0] != s->ox[0] || | ||
| 737 | + s->iy[0] != s->oy[0] || | ||
| 738 | + s->ix[1] != s->ox[1] || | ||
| 739 | + s->iy[1] != s->oy[1])) || | ||
| 740 | + !((s->ix[1] - s->ix[0]) & (s->iy[1] - s->iy[0]) & | ||
| 741 | + (s->ox[1] - s->ox[0]) & (s->oy[1] - s->oy[0]) & 1)) | ||
| 742 | + fprintf(stderr, "%s: Illegal input/output window positions\n", | ||
| 743 | + __FUNCTION__); | ||
| 744 | + | ||
| 745 | + blizzard_transfer_setup(s); | ||
| 746 | + break; | ||
| 747 | + | ||
| 748 | + case 0x90: /* Display Memory Data Port */ | ||
| 749 | + if (!s->data.len && !blizzard_transfer_setup(s)) | ||
| 750 | + break; | ||
| 751 | + | ||
| 752 | + *s->data.ptr ++ = value; | ||
| 753 | + if (-- s->data.len == 0) | ||
| 754 | + blizzard_window(s); | ||
| 755 | + break; | ||
| 756 | + | ||
| 757 | + case 0xa8: /* Border Color 0 */ | ||
| 758 | + s->border_r = value; | ||
| 759 | + break; | ||
| 760 | + case 0xaa: /* Border Color 1 */ | ||
| 761 | + s->border_g = value; | ||
| 762 | + break; | ||
| 763 | + case 0xac: /* Border Color 2 */ | ||
| 764 | + s->border_b = value; | ||
| 765 | + break; | ||
| 766 | + | ||
| 767 | + case 0xb4: /* Gamma Correction Enable */ | ||
| 768 | + s->gamma_config = value & 0x87; | ||
| 769 | + break; | ||
| 770 | + case 0xb6: /* Gamma Correction Table Index */ | ||
| 771 | + s->gamma_idx = value; | ||
| 772 | + break; | ||
| 773 | + case 0xb8: /* Gamma Correction Table Data */ | ||
| 774 | + s->gamma_lut[s->gamma_idx ++] = value; | ||
| 775 | + break; | ||
| 776 | + | ||
| 777 | + case 0xba: /* 3x3 Matrix Enable */ | ||
| 778 | + s->matrix_ena = value & 1; | ||
| 779 | + break; | ||
| 780 | + case 0xbc ... 0xde: /* Coefficient Registers */ | ||
| 781 | + s->matrix_coeff[(reg - 0xbc) >> 1] = value & ((reg & 2) ? 0x80 : 0xff); | ||
| 782 | + break; | ||
| 783 | + case 0xe0: /* 3x3 Matrix Red Offset */ | ||
| 784 | + s->matrix_r = value; | ||
| 785 | + break; | ||
| 786 | + case 0xe2: /* 3x3 Matrix Green Offset */ | ||
| 787 | + s->matrix_g = value; | ||
| 788 | + break; | ||
| 789 | + case 0xe4: /* 3x3 Matrix Blue Offset */ | ||
| 790 | + s->matrix_b = value; | ||
| 791 | + break; | ||
| 792 | + | ||
| 793 | + case 0xe6: /* Power-save */ | ||
| 794 | + s->pm = value & 0x83; | ||
| 795 | + if (value & s->mode & 1) | ||
| 796 | + fprintf(stderr, "%s: The display must be disabled before entering " | ||
| 797 | + "Standby Mode\n", __FUNCTION__); | ||
| 798 | + break; | ||
| 799 | + case 0xe8: /* Non-display Period Control / Status */ | ||
| 800 | + s->status = value & 0x1b; | ||
| 801 | + break; | ||
| 802 | + case 0xea: /* RGB Interface Control */ | ||
| 803 | + s->rgbgpio_dir = value & 0x8f; | ||
| 804 | + break; | ||
| 805 | + case 0xec: /* RGB Interface Status */ | ||
| 806 | + s->rgbgpio = value & 0xcf; | ||
| 807 | + break; | ||
| 808 | + case 0xee: /* General-purpose IO Pins Configuration */ | ||
| 809 | + s->gpio_dir = value; | ||
| 810 | + break; | ||
| 811 | + case 0xf0: /* General-purpose IO Pins Status / Control */ | ||
| 812 | + s->gpio = value; | ||
| 813 | + break; | ||
| 814 | + case 0xf2: /* GPIO Positive Edge Interrupt Trigger */ | ||
| 815 | + s->gpio_edge[0] = value; | ||
| 816 | + break; | ||
| 817 | + case 0xf4: /* GPIO Negative Edge Interrupt Trigger */ | ||
| 818 | + s->gpio_edge[1] = value; | ||
| 819 | + break; | ||
| 820 | + case 0xf6: /* GPIO Interrupt Status */ | ||
| 821 | + s->gpio_irq &= value; | ||
| 822 | + break; | ||
| 823 | + case 0xf8: /* GPIO Pull-down Control */ | ||
| 824 | + s->gpio_pdown = value; | ||
| 825 | + break; | ||
| 826 | + | ||
| 827 | + default: | ||
| 828 | + fprintf(stderr, "%s: unknown register %02x\n", __FUNCTION__, reg); | ||
| 829 | + break; | ||
| 830 | + } | ||
| 831 | +} | ||
| 832 | + | ||
| 833 | +uint16_t s1d13745_read(void *opaque, int dc) | ||
| 834 | +{ | ||
| 835 | + struct blizzard_s *s = (struct blizzard_s *) opaque; | ||
| 836 | + uint16_t value = blizzard_reg_read(s, s->reg); | ||
| 837 | + | ||
| 838 | + if (s->swallow -- > 0) | ||
| 839 | + return 0; | ||
| 840 | + if (dc) | ||
| 841 | + s->reg ++; | ||
| 842 | + | ||
| 843 | + return value; | ||
| 844 | +} | ||
| 845 | + | ||
| 846 | +void s1d13745_write(void *opaque, int dc, uint16_t value) | ||
| 847 | +{ | ||
| 848 | + struct blizzard_s *s = (struct blizzard_s *) opaque; | ||
| 849 | + | ||
| 850 | + if (s->swallow -- > 0) | ||
| 851 | + return; | ||
| 852 | + if (dc) { | ||
| 853 | + blizzard_reg_write(s, s->reg, value); | ||
| 854 | + | ||
| 855 | + if (s->reg != 0x90 && s->reg != 0x5a && s->reg != 0xb8) | ||
| 856 | + s->reg += 2; | ||
| 857 | + } else | ||
| 858 | + s->reg = value & 0xff; | ||
| 859 | +} | ||
| 860 | + | ||
| 861 | +void s1d13745_write_block(void *opaque, int dc, | ||
| 862 | + void *buf, size_t len, int pitch) | ||
| 863 | +{ | ||
| 864 | + struct blizzard_s *s = (struct blizzard_s *) opaque; | ||
| 865 | + | ||
| 866 | + while (len > 0) { | ||
| 867 | + if (s->reg == 0x90 && dc && | ||
| 868 | + (s->data.len || blizzard_transfer_setup(s)) && | ||
| 869 | + len >= (s->data.len << 1)) { | ||
| 870 | + len -= s->data.len << 1; | ||
| 871 | + s->data.len = 0; | ||
| 872 | + s->data.data = buf; | ||
| 873 | + if (pitch) | ||
| 874 | + s->data.pitch = pitch; | ||
| 875 | + blizzard_window(s); | ||
| 876 | + s->data.data = s->data.buf; | ||
| 877 | + continue; | ||
| 878 | + } | ||
| 879 | + | ||
| 880 | + s1d13745_write(opaque, dc, *(uint16_t *) buf); | ||
| 881 | + len -= 2; | ||
| 882 | + buf += 2; | ||
| 883 | + } | ||
| 884 | + | ||
| 885 | + return; | ||
| 886 | +} | ||
| 887 | + | ||
| 888 | +static void blizzard_update_display(void *opaque) | ||
| 889 | +{ | ||
| 890 | + struct blizzard_s *s = (struct blizzard_s *) opaque; | ||
| 891 | + int y, bypp, bypl, bwidth; | ||
| 892 | + uint8_t *src, *dst; | ||
| 893 | + | ||
| 894 | + if (!s->enable) | ||
| 895 | + return; | ||
| 896 | + | ||
| 897 | + if (s->x != s->state->width || s->y != s->state->height) { | ||
| 898 | + s->invalidate = 1; | ||
| 899 | + dpy_resize(s->state, s->x, s->y); | ||
| 900 | + } | ||
| 901 | + | ||
| 902 | + if (s->invalidate) { | ||
| 903 | + s->invalidate = 0; | ||
| 904 | + | ||
| 905 | + if (s->blank) { | ||
| 906 | + bypp = (s->state->depth + 7) >> 3; | ||
| 907 | + memset(s->state->data, 0, bypp * s->x * s->y); | ||
| 908 | + return; | ||
| 909 | + } | ||
| 910 | + | ||
| 911 | + s->mx[0] = 0; | ||
| 912 | + s->mx[1] = s->x; | ||
| 913 | + s->my[0] = 0; | ||
| 914 | + s->my[1] = s->y; | ||
| 915 | + } | ||
| 916 | + | ||
| 917 | + if (s->mx[1] <= s->mx[0]) | ||
| 918 | + return; | ||
| 919 | + | ||
| 920 | + bypp = (s->state->depth + 7) >> 3; | ||
| 921 | + bypl = bypp * s->x; | ||
| 922 | + bwidth = bypp * (s->mx[1] - s->mx[0]); | ||
| 923 | + y = s->my[0]; | ||
| 924 | + src = s->fb + bypl * y + bypp * s->mx[0]; | ||
| 925 | + dst = s->state->data + bypl * y + bypp * s->mx[0]; | ||
| 926 | + for (; y < s->my[1]; y ++, src += bypl, dst += bypl) | ||
| 927 | + memcpy(dst, src, bwidth); | ||
| 928 | + | ||
| 929 | + dpy_update(s->state, s->mx[0], s->my[0], | ||
| 930 | + s->mx[1] - s->mx[0], y - s->my[0]); | ||
| 931 | + | ||
| 932 | + s->mx[0] = s->x; | ||
| 933 | + s->mx[1] = 0; | ||
| 934 | + s->my[0] = s->y; | ||
| 935 | + s->my[1] = 0; | ||
| 936 | +} | ||
| 937 | + | ||
| 938 | +static void blizzard_screen_dump(void *opaque, const char *filename) { | ||
| 939 | + struct blizzard_s *s = (struct blizzard_s *) opaque; | ||
| 940 | + | ||
| 941 | + blizzard_update_display(opaque); | ||
| 942 | + if (s && s->state->data) | ||
| 943 | + ppm_save(filename, s->state->data, s->x, s->y, s->state->linesize); | ||
| 944 | +} | ||
| 945 | + | ||
| 946 | +#define DEPTH 8 | ||
| 947 | +#include "blizzard_template.h" | ||
| 948 | +#define DEPTH 15 | ||
| 949 | +#include "blizzard_template.h" | ||
| 950 | +#define DEPTH 16 | ||
| 951 | +#include "blizzard_template.h" | ||
| 952 | +#define DEPTH 24 | ||
| 953 | +#include "blizzard_template.h" | ||
| 954 | +#define DEPTH 32 | ||
| 955 | +#include "blizzard_template.h" | ||
| 956 | + | ||
| 957 | +void *s1d13745_init(qemu_irq gpio_int, DisplayState *ds) | ||
| 958 | +{ | ||
| 959 | + struct blizzard_s *s = (struct blizzard_s *) qemu_mallocz(sizeof(*s)); | ||
| 960 | + | ||
| 961 | + s->state = ds; | ||
| 962 | + s->fb = qemu_malloc(0x180000); | ||
| 963 | + | ||
| 964 | + switch (s->state->depth) { | ||
| 965 | + case 0: | ||
| 966 | + s->line_fn_tab[0] = s->line_fn_tab[1] = | ||
| 967 | + qemu_mallocz(sizeof(blizzard_fn_t) * 0x10); | ||
| 968 | + break; | ||
| 969 | + case 8: | ||
| 970 | + s->line_fn_tab[0] = blizzard_draw_fn_8; | ||
| 971 | + s->line_fn_tab[1] = blizzard_draw_fn_r_8; | ||
| 972 | + break; | ||
| 973 | + case 15: | ||
| 974 | + s->line_fn_tab[0] = blizzard_draw_fn_15; | ||
| 975 | + s->line_fn_tab[1] = blizzard_draw_fn_r_15; | ||
| 976 | + break; | ||
| 977 | + case 16: | ||
| 978 | + s->line_fn_tab[0] = blizzard_draw_fn_16; | ||
| 979 | + s->line_fn_tab[1] = blizzard_draw_fn_r_16; | ||
| 980 | + break; | ||
| 981 | + case 24: | ||
| 982 | + s->line_fn_tab[0] = blizzard_draw_fn_24; | ||
| 983 | + s->line_fn_tab[1] = blizzard_draw_fn_r_24; | ||
| 984 | + break; | ||
| 985 | + case 32: | ||
| 986 | + s->line_fn_tab[0] = blizzard_draw_fn_32; | ||
| 987 | + s->line_fn_tab[1] = blizzard_draw_fn_r_32; | ||
| 988 | + break; | ||
| 989 | + default: | ||
| 990 | + fprintf(stderr, "%s: Bad color depth\n", __FUNCTION__); | ||
| 991 | + exit(1); | ||
| 992 | + } | ||
| 993 | + | ||
| 994 | + blizzard_reset(s); | ||
| 995 | + | ||
| 996 | + graphic_console_init(s->state, blizzard_update_display, | ||
| 997 | + blizzard_invalidate_display, blizzard_screen_dump, | ||
| 998 | + NULL, s); | ||
| 999 | + | ||
| 1000 | + return s; | ||
| 1001 | +} |
hw/blizzard_template.h
0 → 100644
| 1 | +/* | ||
| 2 | + * QEMU Epson S1D13744/S1D13745 templates | ||
| 3 | + * | ||
| 4 | + * Copyright (C) 2008 Nokia Corporation | ||
| 5 | + * Written by Andrzej Zaborowski <andrew@openedhand.com> | ||
| 6 | + * | ||
| 7 | + * This program is free software; you can redistribute it and/or | ||
| 8 | + * modify it under the terms of the GNU General Public License as | ||
| 9 | + * published by the Free Software Foundation; either version 2 or | ||
| 10 | + * (at your option) version 3 of the License. | ||
| 11 | + * | ||
| 12 | + * This program is distributed in the hope that it will be useful, | ||
| 13 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | + * GNU General Public License for more details. | ||
| 16 | + * | ||
| 17 | + * You should have received a copy of the GNU General Public License | ||
| 18 | + * along with this program; if not, write to the Free Software | ||
| 19 | + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, | ||
| 20 | + * MA 02111-1307 USA | ||
| 21 | + */ | ||
| 22 | + | ||
| 23 | +#define SKIP_PIXEL(to) to += deststep | ||
| 24 | +#if DEPTH == 8 | ||
| 25 | +# define PIXEL_TYPE uint8_t | ||
| 26 | +# define COPY_PIXEL(to, from) *to = from; SKIP_PIXEL(to) | ||
| 27 | +# define COPY_PIXEL1(to, from) *to ++ = from | ||
| 28 | +#elif DEPTH == 15 || DEPTH == 16 | ||
| 29 | +# define PIXEL_TYPE uint16_t | ||
| 30 | +# define COPY_PIXEL(to, from) *to = from; SKIP_PIXEL(to) | ||
| 31 | +# define COPY_PIXEL1(to, from) *to ++ = from | ||
| 32 | +#elif DEPTH == 24 | ||
| 33 | +# define PIXEL_TYPE uint8_t | ||
| 34 | +# define COPY_PIXEL(to, from) \ | ||
| 35 | + to[0] = from; to[1] = (from) >> 8; to[2] = (from) >> 16; SKIP_PIXEL(to) | ||
| 36 | +# define COPY_PIXEL1(to, from) \ | ||
| 37 | + *to ++ = from; *to ++ = (from) >> 8; *to ++ = (from) >> 16 | ||
| 38 | +#elif DEPTH == 32 | ||
| 39 | +# define PIXEL_TYPE uint32_t | ||
| 40 | +# define COPY_PIXEL(to, from) *to = from; SKIP_PIXEL(to) | ||
| 41 | +# define COPY_PIXEL1(to, from) *to ++ = from | ||
| 42 | +#else | ||
| 43 | +# error unknown bit depth | ||
| 44 | +#endif | ||
| 45 | + | ||
| 46 | +#ifdef WORDS_BIGENDIAN | ||
| 47 | +# define SWAP_WORDS 1 | ||
| 48 | +#endif | ||
| 49 | + | ||
| 50 | +static void glue(blizzard_draw_line16_, DEPTH)(PIXEL_TYPE *dest, | ||
| 51 | + const uint16_t *src, unsigned int width) | ||
| 52 | +{ | ||
| 53 | +#if !defined(SWAP_WORDS) && DEPTH == 16 | ||
| 54 | + memcpy(dest, src, width << 1); | ||
| 55 | +#else | ||
| 56 | + uint16_t data; | ||
| 57 | + unsigned int r, g, b; | ||
| 58 | + const uint16_t *end = (void *) src + width; | ||
| 59 | + while (src < end) { | ||
| 60 | + data = lduw_raw(src ++); | ||
| 61 | + b = (data & 0x1f) << 3; | ||
| 62 | + data >>= 5; | ||
| 63 | + g = (data & 0x3f) << 2; | ||
| 64 | + data >>= 6; | ||
| 65 | + r = (data & 0x1f) << 3; | ||
| 66 | + data >>= 5; | ||
| 67 | + COPY_PIXEL1(dest, glue(rgb_to_pixel, DEPTH)(r, g, b)); | ||
| 68 | + } | ||
| 69 | +#endif | ||
| 70 | +} | ||
| 71 | + | ||
| 72 | +static void glue(blizzard_draw_line24mode1_, DEPTH)(PIXEL_TYPE *dest, | ||
| 73 | + const uint8_t *src, unsigned int width) | ||
| 74 | +{ | ||
| 75 | + /* TODO: check if SDL 24-bit planes are not in the same format and | ||
| 76 | + * if so, use memcpy */ | ||
| 77 | + unsigned int r[2], g[2], b[2]; | ||
| 78 | + const uint8_t *end = src + width; | ||
| 79 | + while (src < end) { | ||
| 80 | + g[0] = *src ++; | ||
| 81 | + r[0] = *src ++; | ||
| 82 | + r[1] = *src ++; | ||
| 83 | + b[0] = *src ++; | ||
| 84 | + COPY_PIXEL1(dest, glue(rgb_to_pixel, DEPTH)(r[0], g[0], b[0])); | ||
| 85 | + b[1] = *src ++; | ||
| 86 | + g[1] = *src ++; | ||
| 87 | + COPY_PIXEL1(dest, glue(rgb_to_pixel, DEPTH)(r[1], g[1], b[1])); | ||
| 88 | + } | ||
| 89 | +} | ||
| 90 | + | ||
| 91 | +static void glue(blizzard_draw_line24mode2_, DEPTH)(PIXEL_TYPE *dest, | ||
| 92 | + const uint8_t *src, unsigned int width) | ||
| 93 | +{ | ||
| 94 | + unsigned int r, g, b; | ||
| 95 | + const uint8_t *end = src + width; | ||
| 96 | + while (src < end) { | ||
| 97 | + r = *src ++; | ||
| 98 | + src ++; | ||
| 99 | + b = *src ++; | ||
| 100 | + g = *src ++; | ||
| 101 | + COPY_PIXEL1(dest, glue(rgb_to_pixel, DEPTH)(r, g, b)); | ||
| 102 | + } | ||
| 103 | +} | ||
| 104 | + | ||
| 105 | +/* No rotation */ | ||
| 106 | +static blizzard_fn_t glue(blizzard_draw_fn_, DEPTH)[0x10] = { | ||
| 107 | + NULL, | ||
| 108 | + /* RGB 5:6:5*/ | ||
| 109 | + (blizzard_fn_t) glue(blizzard_draw_line16_, DEPTH), | ||
| 110 | + /* RGB 6:6:6 mode 1 */ | ||
| 111 | + (blizzard_fn_t) glue(blizzard_draw_line24mode1_, DEPTH), | ||
| 112 | + /* RGB 8:8:8 mode 1 */ | ||
| 113 | + (blizzard_fn_t) glue(blizzard_draw_line24mode1_, DEPTH), | ||
| 114 | + NULL, NULL, | ||
| 115 | + /* RGB 6:6:6 mode 2 */ | ||
| 116 | + (blizzard_fn_t) glue(blizzard_draw_line24mode2_, DEPTH), | ||
| 117 | + /* RGB 8:8:8 mode 2 */ | ||
| 118 | + (blizzard_fn_t) glue(blizzard_draw_line24mode2_, DEPTH), | ||
| 119 | + /* YUV 4:2:2 */ | ||
| 120 | + NULL, | ||
| 121 | + /* YUV 4:2:0 */ | ||
| 122 | + NULL, | ||
| 123 | + NULL, NULL, NULL, NULL, NULL, NULL, | ||
| 124 | +}; | ||
| 125 | + | ||
| 126 | +/* 90deg, 180deg and 270deg rotation */ | ||
| 127 | +static blizzard_fn_t glue(blizzard_draw_fn_r_, DEPTH)[0x10] = { | ||
| 128 | + /* TODO */ | ||
| 129 | + [0 ... 0xf] = NULL, | ||
| 130 | +}; | ||
| 131 | + | ||
| 132 | +#undef DEPTH | ||
| 133 | +#undef SKIP_PIXEL | ||
| 134 | +#undef COPY_PIXEL | ||
| 135 | +#undef COPY_PIXEL1 | ||
| 136 | +#undef PIXEL_TYPE | ||
| 137 | + | ||
| 138 | +#undef SWAP_WORDS |
hw/boards.h
| @@ -81,6 +81,9 @@ extern QEMUMachine terrierpda_machine; | @@ -81,6 +81,9 @@ extern QEMUMachine terrierpda_machine; | ||
| 81 | /* palm.c */ | 81 | /* palm.c */ |
| 82 | extern QEMUMachine palmte_machine; | 82 | extern QEMUMachine palmte_machine; |
| 83 | 83 | ||
| 84 | +/* nseries.c */ | ||
| 85 | +extern QEMUMachine n800_machine; | ||
| 86 | + | ||
| 84 | /* gumstix.c */ | 87 | /* gumstix.c */ |
| 85 | extern QEMUMachine connex_machine; | 88 | extern QEMUMachine connex_machine; |
| 86 | extern QEMUMachine verdex_machine; | 89 | extern QEMUMachine verdex_machine; |
hw/cbus.c
0 → 100644
| 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 | + * | ||
| 19 | + * You should have received a copy of the GNU General Public License | ||
| 20 | + * along with this program; if not, write to the Free Software | ||
| 21 | + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, | ||
| 22 | + * MA 02111-1307 USA | ||
| 23 | + */ | ||
| 24 | + | ||
| 25 | +#include "qemu-common.h" | ||
| 26 | +#include "irq.h" | ||
| 27 | +#include "devices.h" | ||
| 28 | +#include "sysemu.h" | ||
| 29 | + | ||
| 30 | +//#define DEBUG | ||
| 31 | + | ||
| 32 | +struct cbus_slave_s; | ||
| 33 | +struct cbus_priv_s { | ||
| 34 | + struct cbus_s cbus; | ||
| 35 | + | ||
| 36 | + int sel; | ||
| 37 | + int dat; | ||
| 38 | + int clk; | ||
| 39 | + int bit; | ||
| 40 | + int dir; | ||
| 41 | + uint16_t val; | ||
| 42 | + qemu_irq dat_out; | ||
| 43 | + | ||
| 44 | + int addr; | ||
| 45 | + int reg; | ||
| 46 | + int rw; | ||
| 47 | + enum { | ||
| 48 | + cbus_address, | ||
| 49 | + cbus_value, | ||
| 50 | + } cycle; | ||
| 51 | + | ||
| 52 | + struct cbus_slave_s *slave[8]; | ||
| 53 | +}; | ||
| 54 | + | ||
| 55 | +struct cbus_slave_s { | ||
| 56 | + void *opaque; | ||
| 57 | + void (*io)(void *opaque, int rw, int reg, uint16_t *val); | ||
| 58 | + int addr; | ||
| 59 | +}; | ||
| 60 | + | ||
| 61 | +static void cbus_io(struct cbus_priv_s *s) | ||
| 62 | +{ | ||
| 63 | + if (s->slave[s->addr]) | ||
| 64 | + s->slave[s->addr]->io(s->slave[s->addr]->opaque, | ||
| 65 | + s->rw, s->reg, &s->val); | ||
| 66 | + else | ||
| 67 | + cpu_abort(cpu_single_env, "%s: bad slave address %i\n", | ||
| 68 | + __FUNCTION__, s->addr); | ||
| 69 | +} | ||
| 70 | + | ||
| 71 | +static void cbus_cycle(struct cbus_priv_s *s) | ||
| 72 | +{ | ||
| 73 | + switch (s->cycle) { | ||
| 74 | + case cbus_address: | ||
| 75 | + s->addr = (s->val >> 6) & 7; | ||
| 76 | + s->rw = (s->val >> 5) & 1; | ||
| 77 | + s->reg = (s->val >> 0) & 0x1f; | ||
| 78 | + | ||
| 79 | + s->cycle = cbus_value; | ||
| 80 | + s->bit = 15; | ||
| 81 | + s->dir = !s->rw; | ||
| 82 | + s->val = 0; | ||
| 83 | + | ||
| 84 | + if (s->rw) | ||
| 85 | + cbus_io(s); | ||
| 86 | + break; | ||
| 87 | + | ||
| 88 | + case cbus_value: | ||
| 89 | + if (!s->rw) | ||
| 90 | + cbus_io(s); | ||
| 91 | + | ||
| 92 | + s->cycle = cbus_address; | ||
| 93 | + s->bit = 8; | ||
| 94 | + s->dir = 1; | ||
| 95 | + s->val = 0; | ||
| 96 | + break; | ||
| 97 | + } | ||
| 98 | +} | ||
| 99 | + | ||
| 100 | +static void cbus_clk(void *opaque, int line, int level) | ||
| 101 | +{ | ||
| 102 | + struct cbus_priv_s *s = (struct cbus_priv_s *) opaque; | ||
| 103 | + | ||
| 104 | + if (!s->sel && level && !s->clk) { | ||
| 105 | + if (s->dir) | ||
| 106 | + s->val |= s->dat << (s->bit --); | ||
| 107 | + else | ||
| 108 | + qemu_set_irq(s->dat_out, (s->val >> (s->bit --)) & 1); | ||
| 109 | + | ||
| 110 | + if (s->bit < 0) | ||
| 111 | + cbus_cycle(s); | ||
| 112 | + } | ||
| 113 | + | ||
| 114 | + s->clk = level; | ||
| 115 | +} | ||
| 116 | + | ||
| 117 | +static void cbus_dat(void *opaque, int line, int level) | ||
| 118 | +{ | ||
| 119 | + struct cbus_priv_s *s = (struct cbus_priv_s *) opaque; | ||
| 120 | + | ||
| 121 | + s->dat = level; | ||
| 122 | +} | ||
| 123 | + | ||
| 124 | +static void cbus_sel(void *opaque, int line, int level) | ||
| 125 | +{ | ||
| 126 | + struct cbus_priv_s *s = (struct cbus_priv_s *) opaque; | ||
| 127 | + | ||
| 128 | + if (!level) { | ||
| 129 | + s->dir = 1; | ||
| 130 | + s->bit = 8; | ||
| 131 | + s->val = 0; | ||
| 132 | + } | ||
| 133 | + | ||
| 134 | + s->sel = level; | ||
| 135 | +} | ||
| 136 | + | ||
| 137 | +struct cbus_s *cbus_init(qemu_irq dat) | ||
| 138 | +{ | ||
| 139 | + struct cbus_priv_s *s = (struct cbus_priv_s *) qemu_mallocz(sizeof(*s)); | ||
| 140 | + | ||
| 141 | + s->dat_out = dat; | ||
| 142 | + s->cbus.clk = qemu_allocate_irqs(cbus_clk, s, 1)[0]; | ||
| 143 | + s->cbus.dat = qemu_allocate_irqs(cbus_dat, s, 1)[0]; | ||
| 144 | + s->cbus.sel = qemu_allocate_irqs(cbus_sel, s, 1)[0]; | ||
| 145 | + | ||
| 146 | + s->sel = 1; | ||
| 147 | + s->clk = 0; | ||
| 148 | + s->dat = 0; | ||
| 149 | + | ||
| 150 | + return &s->cbus; | ||
| 151 | +} | ||
| 152 | + | ||
| 153 | +void cbus_attach(struct cbus_s *bus, void *slave_opaque) | ||
| 154 | +{ | ||
| 155 | + struct cbus_slave_s *slave = (struct cbus_slave_s *) slave_opaque; | ||
| 156 | + struct cbus_priv_s *s = (struct cbus_priv_s *) bus; | ||
| 157 | + | ||
| 158 | + s->slave[slave->addr] = slave; | ||
| 159 | +} | ||
| 160 | + | ||
| 161 | +/* Retu/Vilma */ | ||
| 162 | +struct cbus_retu_s { | ||
| 163 | + uint16_t irqst; | ||
| 164 | + uint16_t irqen; | ||
| 165 | + uint16_t cc[2]; | ||
| 166 | + int channel; | ||
| 167 | + uint16_t result[16]; | ||
| 168 | + uint16_t sample; | ||
| 169 | + uint16_t status; | ||
| 170 | + | ||
| 171 | + struct { | ||
| 172 | + uint16_t cal; | ||
| 173 | + } rtc; | ||
| 174 | + | ||
| 175 | + int is_vilma; | ||
| 176 | + qemu_irq irq; | ||
| 177 | + struct cbus_slave_s cbus; | ||
| 178 | +}; | ||
| 179 | + | ||
| 180 | +static void retu_interrupt_update(struct cbus_retu_s *s) | ||
| 181 | +{ | ||
| 182 | + qemu_set_irq(s->irq, s->irqst & ~s->irqen); | ||
| 183 | +} | ||
| 184 | + | ||
| 185 | +#define RETU_REG_ASICR 0x00 /* (RO) ASIC ID & revision */ | ||
| 186 | +#define RETU_REG_IDR 0x01 /* (T) Interrupt ID */ | ||
| 187 | +#define RETU_REG_IMR 0x02 /* (RW) Interrupt mask */ | ||
| 188 | +#define RETU_REG_RTCDSR 0x03 /* (RW) RTC seconds register */ | ||
| 189 | +#define RETU_REG_RTCHMR 0x04 /* (RO) RTC hours and minutes reg */ | ||
| 190 | +#define RETU_REG_RTCHMAR 0x05 /* (RW) RTC hours and minutes set reg */ | ||
| 191 | +#define RETU_REG_RTCCALR 0x06 /* (RW) RTC calibration register */ | ||
| 192 | +#define RETU_REG_ADCR 0x08 /* (RW) ADC result register */ | ||
| 193 | +#define RETU_REG_ADCSCR 0x09 /* (RW) ADC sample control register */ | ||
| 194 | +#define RETU_REG_AFCR 0x0a /* (RW) AFC register */ | ||
| 195 | +#define RETU_REG_ANTIFR 0x0b /* (RW) AntiF register */ | ||
| 196 | +#define RETU_REG_CALIBR 0x0c /* (RW) CalibR register*/ | ||
| 197 | +#define RETU_REG_CCR1 0x0d /* (RW) Common control register 1 */ | ||
| 198 | +#define RETU_REG_CCR2 0x0e /* (RW) Common control register 2 */ | ||
| 199 | +#define RETU_REG_RCTRL_CLR 0x0f /* (T) Regulator clear register */ | ||
| 200 | +#define RETU_REG_RCTRL_SET 0x10 /* (T) Regulator set register */ | ||
| 201 | +#define RETU_REG_TXCR 0x11 /* (RW) TxC register */ | ||
| 202 | +#define RETU_REG_STATUS 0x16 /* (RO) Status register */ | ||
| 203 | +#define RETU_REG_WATCHDOG 0x17 /* (RW) Watchdog register */ | ||
| 204 | +#define RETU_REG_AUDTXR 0x18 /* (RW) Audio Codec Tx register */ | ||
| 205 | +#define RETU_REG_AUDPAR 0x19 /* (RW) AudioPA register */ | ||
| 206 | +#define RETU_REG_AUDRXR1 0x1a /* (RW) Audio receive register 1 */ | ||
| 207 | +#define RETU_REG_AUDRXR2 0x1b /* (RW) Audio receive register 2 */ | ||
| 208 | +#define RETU_REG_SGR1 0x1c /* (RW) */ | ||
| 209 | +#define RETU_REG_SCR1 0x1d /* (RW) */ | ||
| 210 | +#define RETU_REG_SGR2 0x1e /* (RW) */ | ||
| 211 | +#define RETU_REG_SCR2 0x1f /* (RW) */ | ||
| 212 | + | ||
| 213 | +/* Retu Interrupt sources */ | ||
| 214 | +enum { | ||
| 215 | + retu_int_pwr = 0, /* Power button */ | ||
| 216 | + retu_int_char = 1, /* Charger */ | ||
| 217 | + retu_int_rtcs = 2, /* Seconds */ | ||
| 218 | + retu_int_rtcm = 3, /* Minutes */ | ||
| 219 | + retu_int_rtcd = 4, /* Days */ | ||
| 220 | + retu_int_rtca = 5, /* Alarm */ | ||
| 221 | + retu_int_hook = 6, /* Hook */ | ||
| 222 | + retu_int_head = 7, /* Headset */ | ||
| 223 | + retu_int_adcs = 8, /* ADC sample */ | ||
| 224 | +}; | ||
| 225 | + | ||
| 226 | +/* Retu ADC channel wiring */ | ||
| 227 | +enum { | ||
| 228 | + retu_adc_bsi = 1, /* BSI */ | ||
| 229 | + retu_adc_batt_temp = 2, /* Battery temperature */ | ||
| 230 | + retu_adc_chg_volt = 3, /* Charger voltage */ | ||
| 231 | + retu_adc_head_det = 4, /* Headset detection */ | ||
| 232 | + retu_adc_hook_det = 5, /* Hook detection */ | ||
| 233 | + retu_adc_rf_gp = 6, /* RF GP */ | ||
| 234 | + retu_adc_tx_det = 7, /* Wideband Tx detection */ | ||
| 235 | + retu_adc_batt_volt = 8, /* Battery voltage */ | ||
| 236 | + retu_adc_sens = 10, /* Light sensor */ | ||
| 237 | + retu_adc_sens_temp = 11, /* Light sensor temperature */ | ||
| 238 | + retu_adc_bbatt_volt = 12, /* Backup battery voltage */ | ||
| 239 | + retu_adc_self_temp = 13, /* RETU temperature */ | ||
| 240 | +}; | ||
| 241 | + | ||
| 242 | +static inline uint16_t retu_read(struct cbus_retu_s *s, int reg) | ||
| 243 | +{ | ||
| 244 | +#ifdef DEBUG | ||
| 245 | + printf("RETU read at %02x\n", reg); | ||
| 246 | +#endif | ||
| 247 | + | ||
| 248 | + switch (reg) { | ||
| 249 | + case RETU_REG_ASICR: | ||
| 250 | + return 0x0215 | (s->is_vilma << 7); | ||
| 251 | + | ||
| 252 | + case RETU_REG_IDR: /* TODO: Or is this ffs(s->irqst)? */ | ||
| 253 | + return s->irqst; | ||
| 254 | + | ||
| 255 | + case RETU_REG_IMR: | ||
| 256 | + return s->irqen; | ||
| 257 | + | ||
| 258 | + case RETU_REG_RTCDSR: | ||
| 259 | + case RETU_REG_RTCHMR: | ||
| 260 | + case RETU_REG_RTCHMAR: | ||
| 261 | + /* TODO */ | ||
| 262 | + return 0x0000; | ||
| 263 | + | ||
| 264 | + case RETU_REG_RTCCALR: | ||
| 265 | + return s->rtc.cal; | ||
| 266 | + | ||
| 267 | + case RETU_REG_ADCR: | ||
| 268 | + return (s->channel << 10) | s->result[s->channel]; | ||
| 269 | + case RETU_REG_ADCSCR: | ||
| 270 | + return s->sample; | ||
| 271 | + | ||
| 272 | + case RETU_REG_AFCR: | ||
| 273 | + case RETU_REG_ANTIFR: | ||
| 274 | + case RETU_REG_CALIBR: | ||
| 275 | + /* TODO */ | ||
| 276 | + return 0x0000; | ||
| 277 | + | ||
| 278 | + case RETU_REG_CCR1: | ||
| 279 | + return s->cc[0]; | ||
| 280 | + case RETU_REG_CCR2: | ||
| 281 | + return s->cc[1]; | ||
| 282 | + | ||
| 283 | + case RETU_REG_RCTRL_CLR: | ||
| 284 | + case RETU_REG_RCTRL_SET: | ||
| 285 | + case RETU_REG_TXCR: | ||
| 286 | + /* TODO */ | ||
| 287 | + return 0x0000; | ||
| 288 | + | ||
| 289 | + case RETU_REG_STATUS: | ||
| 290 | + return s->status; | ||
| 291 | + | ||
| 292 | + case RETU_REG_WATCHDOG: | ||
| 293 | + case RETU_REG_AUDTXR: | ||
| 294 | + case RETU_REG_AUDPAR: | ||
| 295 | + case RETU_REG_AUDRXR1: | ||
| 296 | + case RETU_REG_AUDRXR2: | ||
| 297 | + case RETU_REG_SGR1: | ||
| 298 | + case RETU_REG_SCR1: | ||
| 299 | + case RETU_REG_SGR2: | ||
| 300 | + case RETU_REG_SCR2: | ||
| 301 | + /* TODO */ | ||
| 302 | + return 0x0000; | ||
| 303 | + | ||
| 304 | + default: | ||
| 305 | + cpu_abort(cpu_single_env, "%s: bad register %02x\n", | ||
| 306 | + __FUNCTION__, reg); | ||
| 307 | + } | ||
| 308 | +} | ||
| 309 | + | ||
| 310 | +static inline void retu_write(struct cbus_retu_s *s, int reg, uint16_t val) | ||
| 311 | +{ | ||
| 312 | +#ifdef DEBUG | ||
| 313 | + printf("RETU write of %04x at %02x\n", val, reg); | ||
| 314 | +#endif | ||
| 315 | + | ||
| 316 | + switch (reg) { | ||
| 317 | + case RETU_REG_IDR: | ||
| 318 | + s->irqst ^= val; | ||
| 319 | + retu_interrupt_update(s); | ||
| 320 | + break; | ||
| 321 | + | ||
| 322 | + case RETU_REG_IMR: | ||
| 323 | + s->irqen = val; | ||
| 324 | + retu_interrupt_update(s); | ||
| 325 | + break; | ||
| 326 | + | ||
| 327 | + case RETU_REG_RTCDSR: | ||
| 328 | + case RETU_REG_RTCHMAR: | ||
| 329 | + /* TODO */ | ||
| 330 | + break; | ||
| 331 | + | ||
| 332 | + case RETU_REG_RTCCALR: | ||
| 333 | + s->rtc.cal = val; | ||
| 334 | + break; | ||
| 335 | + | ||
| 336 | + case RETU_REG_ADCR: | ||
| 337 | + s->channel = (val >> 10) & 0xf; | ||
| 338 | + s->irqst |= 1 << retu_int_adcs; | ||
| 339 | + retu_interrupt_update(s); | ||
| 340 | + break; | ||
| 341 | + case RETU_REG_ADCSCR: | ||
| 342 | + s->sample &= ~val; | ||
| 343 | + break; | ||
| 344 | + | ||
| 345 | + case RETU_REG_AFCR: | ||
| 346 | + case RETU_REG_ANTIFR: | ||
| 347 | + case RETU_REG_CALIBR: | ||
| 348 | + | ||
| 349 | + case RETU_REG_CCR1: | ||
| 350 | + s->cc[0] = val; | ||
| 351 | + break; | ||
| 352 | + case RETU_REG_CCR2: | ||
| 353 | + s->cc[1] = val; | ||
| 354 | + break; | ||
| 355 | + | ||
| 356 | + case RETU_REG_RCTRL_CLR: | ||
| 357 | + case RETU_REG_RCTRL_SET: | ||
| 358 | + /* TODO */ | ||
| 359 | + break; | ||
| 360 | + | ||
| 361 | + case RETU_REG_WATCHDOG: | ||
| 362 | + if (val == 0 && (s->cc[0] & 2)) | ||
| 363 | + qemu_system_shutdown_request(); | ||
| 364 | + break; | ||
| 365 | + | ||
| 366 | + case RETU_REG_TXCR: | ||
| 367 | + case RETU_REG_AUDTXR: | ||
| 368 | + case RETU_REG_AUDPAR: | ||
| 369 | + case RETU_REG_AUDRXR1: | ||
| 370 | + case RETU_REG_AUDRXR2: | ||
| 371 | + case RETU_REG_SGR1: | ||
| 372 | + case RETU_REG_SCR1: | ||
| 373 | + case RETU_REG_SGR2: | ||
| 374 | + case RETU_REG_SCR2: | ||
| 375 | + /* TODO */ | ||
| 376 | + break; | ||
| 377 | + | ||
| 378 | + default: | ||
| 379 | + cpu_abort(cpu_single_env, "%s: bad register %02x\n", | ||
| 380 | + __FUNCTION__, reg); | ||
| 381 | + } | ||
| 382 | +} | ||
| 383 | + | ||
| 384 | +static void retu_io(void *opaque, int rw, int reg, uint16_t *val) | ||
| 385 | +{ | ||
| 386 | + struct cbus_retu_s *s = (struct cbus_retu_s *) opaque; | ||
| 387 | + | ||
| 388 | + if (rw) | ||
| 389 | + *val = retu_read(s, reg); | ||
| 390 | + else | ||
| 391 | + retu_write(s, reg, *val); | ||
| 392 | +} | ||
| 393 | + | ||
| 394 | +void *retu_init(qemu_irq irq, int vilma) | ||
| 395 | +{ | ||
| 396 | + struct cbus_retu_s *s = (struct cbus_retu_s *) qemu_mallocz(sizeof(*s)); | ||
| 397 | + | ||
| 398 | + s->irq = irq; | ||
| 399 | + s->irqen = 0xffff; | ||
| 400 | + s->irqst = 0x0000; | ||
| 401 | + s->status = 0x0020; | ||
| 402 | + s->is_vilma = !!vilma; | ||
| 403 | + s->rtc.cal = 0x01; | ||
| 404 | + s->result[retu_adc_bsi] = 0x3c2; | ||
| 405 | + s->result[retu_adc_batt_temp] = 0x0fc; | ||
| 406 | + s->result[retu_adc_chg_volt] = 0x165; | ||
| 407 | + s->result[retu_adc_head_det] = 123; | ||
| 408 | + s->result[retu_adc_hook_det] = 1023; | ||
| 409 | + s->result[retu_adc_rf_gp] = 0x11; | ||
| 410 | + s->result[retu_adc_tx_det] = 0x11; | ||
| 411 | + s->result[retu_adc_batt_volt] = 0x250; | ||
| 412 | + s->result[retu_adc_sens] = 2; | ||
| 413 | + s->result[retu_adc_sens_temp] = 0x11; | ||
| 414 | + s->result[retu_adc_bbatt_volt] = 0x3d0; | ||
| 415 | + s->result[retu_adc_self_temp] = 0x330; | ||
| 416 | + | ||
| 417 | + s->cbus.opaque = s; | ||
| 418 | + s->cbus.io = retu_io; | ||
| 419 | + s->cbus.addr = 1; | ||
| 420 | + | ||
| 421 | + return &s->cbus; | ||
| 422 | +} | ||
| 423 | + | ||
| 424 | +void retu_key_event(void *retu, int state) | ||
| 425 | +{ | ||
| 426 | + struct cbus_slave_s *slave = (struct cbus_slave_s *) retu; | ||
| 427 | + struct cbus_retu_s *s = (struct cbus_retu_s *) slave->opaque; | ||
| 428 | + | ||
| 429 | + s->irqst |= 1 << retu_int_pwr; | ||
| 430 | + retu_interrupt_update(s); | ||
| 431 | + | ||
| 432 | + if (state) | ||
| 433 | + s->status &= ~(1 << 5); | ||
| 434 | + else | ||
| 435 | + s->status |= 1 << 5; | ||
| 436 | +} | ||
| 437 | + | ||
| 438 | +void retu_head_event(void *retu, int state) | ||
| 439 | +{ | ||
| 440 | + struct cbus_slave_s *slave = (struct cbus_slave_s *) retu; | ||
| 441 | + struct cbus_retu_s *s = (struct cbus_retu_s *) slave->opaque; | ||
| 442 | + | ||
| 443 | + if ((s->cc[0] & 0x500) == 0x500) { /* TODO: Which bits? */ | ||
| 444 | + /* TODO: reissue the interrupt every 100ms or so. */ | ||
| 445 | + s->irqst |= 1 << retu_int_head; | ||
| 446 | + retu_interrupt_update(s); | ||
| 447 | + } | ||
| 448 | + | ||
| 449 | + if (state) | ||
| 450 | + s->result[retu_adc_head_det] = 50; | ||
| 451 | + else | ||
| 452 | + s->result[retu_adc_head_det] = 123; | ||
| 453 | +} | ||
| 454 | + | ||
| 455 | +void retu_hook_event(void *retu, int state) | ||
| 456 | +{ | ||
| 457 | + struct cbus_slave_s *slave = (struct cbus_slave_s *) retu; | ||
| 458 | + struct cbus_retu_s *s = (struct cbus_retu_s *) slave->opaque; | ||
| 459 | + | ||
| 460 | + if ((s->cc[0] & 0x500) == 0x500) { | ||
| 461 | + /* TODO: reissue the interrupt every 100ms or so. */ | ||
| 462 | + s->irqst |= 1 << retu_int_hook; | ||
| 463 | + retu_interrupt_update(s); | ||
| 464 | + } | ||
| 465 | + | ||
| 466 | + if (state) | ||
| 467 | + s->result[retu_adc_hook_det] = 50; | ||
| 468 | + else | ||
| 469 | + s->result[retu_adc_hook_det] = 123; | ||
| 470 | +} | ||
| 471 | + | ||
| 472 | +/* Tahvo/Betty */ | ||
| 473 | +struct cbus_tahvo_s { | ||
| 474 | + uint16_t irqst; | ||
| 475 | + uint16_t irqen; | ||
| 476 | + uint8_t charger; | ||
| 477 | + uint8_t backlight; | ||
| 478 | + uint16_t usbr; | ||
| 479 | + uint16_t power; | ||
| 480 | + | ||
| 481 | + int is_betty; | ||
| 482 | + qemu_irq irq; | ||
| 483 | + struct cbus_slave_s cbus; | ||
| 484 | +}; | ||
| 485 | + | ||
| 486 | +static void tahvo_interrupt_update(struct cbus_tahvo_s *s) | ||
| 487 | +{ | ||
| 488 | + qemu_set_irq(s->irq, s->irqst & ~s->irqen); | ||
| 489 | +} | ||
| 490 | + | ||
| 491 | +#define TAHVO_REG_ASICR 0x00 /* (RO) ASIC ID & revision */ | ||
| 492 | +#define TAHVO_REG_IDR 0x01 /* (T) Interrupt ID */ | ||
| 493 | +#define TAHVO_REG_IDSR 0x02 /* (RO) Interrupt status */ | ||
| 494 | +#define TAHVO_REG_IMR 0x03 /* (RW) Interrupt mask */ | ||
| 495 | +#define TAHVO_REG_CHAPWMR 0x04 /* (RW) Charger PWM */ | ||
| 496 | +#define TAHVO_REG_LEDPWMR 0x05 /* (RW) LED PWM */ | ||
| 497 | +#define TAHVO_REG_USBR 0x06 /* (RW) USB control */ | ||
| 498 | +#define TAHVO_REG_RCR 0x07 /* (RW) Some kind of power management */ | ||
| 499 | +#define TAHVO_REG_CCR1 0x08 /* (RW) Common control register 1 */ | ||
| 500 | +#define TAHVO_REG_CCR2 0x09 /* (RW) Common control register 2 */ | ||
| 501 | +#define TAHVO_REG_TESTR1 0x0a /* (RW) Test register 1 */ | ||
| 502 | +#define TAHVO_REG_TESTR2 0x0b /* (RW) Test register 2 */ | ||
| 503 | +#define TAHVO_REG_NOPR 0x0c /* (RW) Number of periods */ | ||
| 504 | +#define TAHVO_REG_FRR 0x0d /* (RO) FR */ | ||
| 505 | + | ||
| 506 | +static inline uint16_t tahvo_read(struct cbus_tahvo_s *s, int reg) | ||
| 507 | +{ | ||
| 508 | +#ifdef DEBUG | ||
| 509 | + printf("TAHVO read at %02x\n", reg); | ||
| 510 | +#endif | ||
| 511 | + | ||
| 512 | + switch (reg) { | ||
| 513 | + case TAHVO_REG_ASICR: | ||
| 514 | + return 0x0021 | (s->is_betty ? 0x0b00 : 0x0300); /* 22 in N810 */ | ||
| 515 | + | ||
| 516 | + case TAHVO_REG_IDR: | ||
| 517 | + case TAHVO_REG_IDSR: /* XXX: what does this do? */ | ||
| 518 | + return s->irqst; | ||
| 519 | + | ||
| 520 | + case TAHVO_REG_IMR: | ||
| 521 | + return s->irqen; | ||
| 522 | + | ||
| 523 | + case TAHVO_REG_CHAPWMR: | ||
| 524 | + return s->charger; | ||
| 525 | + | ||
| 526 | + case TAHVO_REG_LEDPWMR: | ||
| 527 | + return s->backlight; | ||
| 528 | + | ||
| 529 | + case TAHVO_REG_USBR: | ||
| 530 | + return s->usbr; | ||
| 531 | + | ||
| 532 | + case TAHVO_REG_RCR: | ||
| 533 | + return s->power; | ||
| 534 | + | ||
| 535 | + case TAHVO_REG_CCR1: | ||
| 536 | + case TAHVO_REG_CCR2: | ||
| 537 | + case TAHVO_REG_TESTR1: | ||
| 538 | + case TAHVO_REG_TESTR2: | ||
| 539 | + case TAHVO_REG_NOPR: | ||
| 540 | + case TAHVO_REG_FRR: | ||
| 541 | + return 0x0000; | ||
| 542 | + | ||
| 543 | + default: | ||
| 544 | + cpu_abort(cpu_single_env, "%s: bad register %02x\n", | ||
| 545 | + __FUNCTION__, reg); | ||
| 546 | + } | ||
| 547 | +} | ||
| 548 | + | ||
| 549 | +static inline void tahvo_write(struct cbus_tahvo_s *s, int reg, uint16_t val) | ||
| 550 | +{ | ||
| 551 | +#ifdef DEBUG | ||
| 552 | + printf("TAHVO write of %04x at %02x\n", val, reg); | ||
| 553 | +#endif | ||
| 554 | + | ||
| 555 | + switch (reg) { | ||
| 556 | + case TAHVO_REG_IDR: | ||
| 557 | + s->irqst ^= val; | ||
| 558 | + tahvo_interrupt_update(s); | ||
| 559 | + break; | ||
| 560 | + | ||
| 561 | + case TAHVO_REG_IMR: | ||
| 562 | + s->irqen = val; | ||
| 563 | + tahvo_interrupt_update(s); | ||
| 564 | + break; | ||
| 565 | + | ||
| 566 | + case TAHVO_REG_CHAPWMR: | ||
| 567 | + s->charger = val; | ||
| 568 | + break; | ||
| 569 | + | ||
| 570 | + case TAHVO_REG_LEDPWMR: | ||
| 571 | + if (s->backlight != (val & 0x7f)) { | ||
| 572 | + s->backlight = val & 0x7f; | ||
| 573 | + printf("%s: LCD backlight now at %i / 127\n", | ||
| 574 | + __FUNCTION__, s->backlight); | ||
| 575 | + } | ||
| 576 | + break; | ||
| 577 | + | ||
| 578 | + case TAHVO_REG_USBR: | ||
| 579 | + s->usbr = val; | ||
| 580 | + break; | ||
| 581 | + | ||
| 582 | + case TAHVO_REG_RCR: | ||
| 583 | + s->power = val; | ||
| 584 | + break; | ||
| 585 | + | ||
| 586 | + case TAHVO_REG_CCR1: | ||
| 587 | + case TAHVO_REG_CCR2: | ||
| 588 | + case TAHVO_REG_TESTR1: | ||
| 589 | + case TAHVO_REG_TESTR2: | ||
| 590 | + case TAHVO_REG_NOPR: | ||
| 591 | + case TAHVO_REG_FRR: | ||
| 592 | + break; | ||
| 593 | + | ||
| 594 | + default: | ||
| 595 | + cpu_abort(cpu_single_env, "%s: bad register %02x\n", | ||
| 596 | + __FUNCTION__, reg); | ||
| 597 | + } | ||
| 598 | +} | ||
| 599 | + | ||
| 600 | +static void tahvo_io(void *opaque, int rw, int reg, uint16_t *val) | ||
| 601 | +{ | ||
| 602 | + struct cbus_tahvo_s *s = (struct cbus_tahvo_s *) opaque; | ||
| 603 | + | ||
| 604 | + if (rw) | ||
| 605 | + *val = tahvo_read(s, reg); | ||
| 606 | + else | ||
| 607 | + tahvo_write(s, reg, *val); | ||
| 608 | +} | ||
| 609 | + | ||
| 610 | +void *tahvo_init(qemu_irq irq, int betty) | ||
| 611 | +{ | ||
| 612 | + struct cbus_tahvo_s *s = (struct cbus_tahvo_s *) qemu_mallocz(sizeof(*s)); | ||
| 613 | + | ||
| 614 | + s->irq = irq; | ||
| 615 | + s->irqen = 0xffff; | ||
| 616 | + s->irqst = 0x0000; | ||
| 617 | + s->is_betty = !!betty; | ||
| 618 | + | ||
| 619 | + s->cbus.opaque = s; | ||
| 620 | + s->cbus.io = tahvo_io; | ||
| 621 | + s->cbus.addr = 2; | ||
| 622 | + | ||
| 623 | + return &s->cbus; | ||
| 624 | +} |
hw/devices.h
| @@ -31,4 +31,25 @@ void tsc210x_key_event(struct uwire_slave_s *chip, int key, int down); | @@ -31,4 +31,25 @@ void tsc210x_key_event(struct uwire_slave_s *chip, int key, int down); | ||
| 31 | /* stellaris_input.c */ | 31 | /* stellaris_input.c */ |
| 32 | void stellaris_gamepad_init(int n, qemu_irq *irq, const int *keycode); | 32 | void stellaris_gamepad_init(int n, qemu_irq *irq, const int *keycode); |
| 33 | 33 | ||
| 34 | +/* blizzard.c */ | ||
| 35 | +void *s1d13745_init(qemu_irq gpio_int, DisplayState *ds); | ||
| 36 | +void s1d13745_write(void *opaque, int dc, uint16_t value); | ||
| 37 | +void s1d13745_write_block(void *opaque, int dc, | ||
| 38 | + void *buf, size_t len, int pitch); | ||
| 39 | +uint16_t s1d13745_read(void *opaque, int dc); | ||
| 40 | + | ||
| 41 | +/* cbus.c */ | ||
| 42 | +struct cbus_s { | ||
| 43 | + qemu_irq clk; | ||
| 44 | + qemu_irq dat; | ||
| 45 | + qemu_irq sel; | ||
| 46 | +}; | ||
| 47 | +struct cbus_s *cbus_init(qemu_irq dat_out); | ||
| 48 | +void cbus_attach(struct cbus_s *bus, void *slave_opaque); | ||
| 49 | + | ||
| 50 | +void *retu_init(qemu_irq irq, int vilma); | ||
| 51 | +void *tahvo_init(qemu_irq irq, int betty); | ||
| 52 | + | ||
| 53 | +void retu_key_event(void *retu, int state); | ||
| 54 | + | ||
| 34 | #endif | 55 | #endif |
hw/flash.h
| @@ -34,6 +34,11 @@ uint8_t nand_getio(struct nand_flash_s *s); | @@ -34,6 +34,11 @@ uint8_t nand_getio(struct nand_flash_s *s); | ||
| 34 | #define NAND_MFR_HYNIX 0xad | 34 | #define NAND_MFR_HYNIX 0xad |
| 35 | #define NAND_MFR_MICRON 0x2c | 35 | #define NAND_MFR_MICRON 0x2c |
| 36 | 36 | ||
| 37 | +/* onenand.c */ | ||
| 38 | +void onenand_base_update(void *opaque, target_phys_addr_t new); | ||
| 39 | +void onenand_base_unmap(void *opaque); | ||
| 40 | +void *onenand_init(uint32_t id, int regshift, qemu_irq irq); | ||
| 41 | + | ||
| 37 | /* ecc.c */ | 42 | /* ecc.c */ |
| 38 | struct ecc_state_s { | 43 | struct ecc_state_s { |
| 39 | uint8_t cp; /* Column parity */ | 44 | uint8_t cp; /* Column parity */ |
hw/i2c.h
| @@ -71,4 +71,14 @@ uint32_t wm8750_adc_dat(void *opaque); | @@ -71,4 +71,14 @@ uint32_t wm8750_adc_dat(void *opaque); | ||
| 71 | /* ssd0303.c */ | 71 | /* ssd0303.c */ |
| 72 | void ssd0303_init(DisplayState *ds, i2c_bus *bus, int address); | 72 | void ssd0303_init(DisplayState *ds, i2c_bus *bus, int address); |
| 73 | 73 | ||
| 74 | +/* twl92230.c */ | ||
| 75 | +i2c_slave *twl92230_init(i2c_bus *bus, qemu_irq irq); | ||
| 76 | +qemu_irq *twl92230_gpio_in_get(i2c_slave *i2c); | ||
| 77 | +void twl92230_gpio_out_set(i2c_slave *i2c, int line, qemu_irq handler); | ||
| 78 | + | ||
| 79 | +/* tmp105.c */ | ||
| 80 | +struct i2c_slave *tmp105_init(i2c_bus *bus, qemu_irq alarm); | ||
| 81 | +void tmp105_reset(i2c_slave *i2c); | ||
| 82 | +void tmp105_set(i2c_slave *i2c, int temp); | ||
| 83 | + | ||
| 74 | #endif | 84 | #endif |
hw/nseries.c
0 → 100644
| 1 | +/* | ||
| 2 | + * Nokia N-series internet tablets. | ||
| 3 | + * | ||
| 4 | + * Copyright (C) 2007 Nokia Corporation | ||
| 5 | + * Written by Andrzej Zaborowski <andrew@openedhand.com> | ||
| 6 | + * | ||
| 7 | + * This program is free software; you can redistribute it and/or | ||
| 8 | + * modify it under the terms of the GNU General Public License as | ||
| 9 | + * published by the Free Software Foundation; either version 2 or | ||
| 10 | + * (at your option) version 3 of the License. | ||
| 11 | + * | ||
| 12 | + * This program is distributed in the hope that it will be useful, | ||
| 13 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | + * GNU General Public License for more details. | ||
| 16 | + * | ||
| 17 | + * You should have received a copy of the GNU General Public License | ||
| 18 | + * along with this program; if not, write to the Free Software | ||
| 19 | + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, | ||
| 20 | + * MA 02111-1307 USA | ||
| 21 | + */ | ||
| 22 | + | ||
| 23 | +#include "qemu-common.h" | ||
| 24 | +#include "sysemu.h" | ||
| 25 | +#include "omap.h" | ||
| 26 | +#include "arm-misc.h" | ||
| 27 | +#include "irq.h" | ||
| 28 | +#include "console.h" | ||
| 29 | +#include "boards.h" | ||
| 30 | +#include "i2c.h" | ||
| 31 | +#include "devices.h" | ||
| 32 | +#include "flash.h" | ||
| 33 | +#include "hw.h" | ||
| 34 | + | ||
| 35 | +/* Nokia N8x0 support */ | ||
| 36 | +struct n800_s { | ||
| 37 | + struct omap_mpu_state_s *cpu; | ||
| 38 | + | ||
| 39 | + struct rfbi_chip_s blizzard; | ||
| 40 | + struct uwire_slave_s *ts; | ||
| 41 | + i2c_bus *i2c; | ||
| 42 | + | ||
| 43 | + int keymap[0x80]; | ||
| 44 | + | ||
| 45 | + void *retu; | ||
| 46 | + void *tahvo; | ||
| 47 | +}; | ||
| 48 | + | ||
| 49 | +/* GPIO pins */ | ||
| 50 | +#define N800_TUSB_ENABLE_GPIO 0 | ||
| 51 | +#define N800_MMC2_WP_GPIO 8 | ||
| 52 | +#define N800_UNKNOWN_GPIO0 9 /* out */ | ||
| 53 | +#define N800_UNKNOWN_GPIO1 10 /* out */ | ||
| 54 | +#define N800_CAM_TURN_GPIO 12 | ||
| 55 | +#define N800_BLIZZARD_POWERDOWN_GPIO 15 | ||
| 56 | +#define N800_MMC1_WP_GPIO 23 | ||
| 57 | +#define N8X0_ONENAND_GPIO 26 | ||
| 58 | +#define N800_UNKNOWN_GPIO2 53 /* out */ | ||
| 59 | +#define N8X0_TUSB_INT_GPIO 58 | ||
| 60 | +#define N800_BT_WKUP_GPIO 61 | ||
| 61 | +#define N800_STI_GPIO 62 | ||
| 62 | +#define N8X0_CBUS_SEL_GPIO 64 | ||
| 63 | +#define N8X0_CBUS_CLK_GPIO 65 /* sure? */ | ||
| 64 | +#define N8X0_CBUS_DAT_GPIO 66 | ||
| 65 | +#define N800_WLAN_IRQ_GPIO 87 | ||
| 66 | +#define N800_BT_RESET_GPIO 92 | ||
| 67 | +#define N800_TEA5761_CS_GPIO 93 | ||
| 68 | +#define N800_UNKNOWN_GPIO 94 | ||
| 69 | +#define N800_CAM_ACT_GPIO 95 | ||
| 70 | +#define N800_MMC_CS_GPIO 96 | ||
| 71 | +#define N800_WLAN_PWR_GPIO 97 | ||
| 72 | +#define N8X0_BT_HOST_WKUP_GPIO 98 | ||
| 73 | +#define N800_UNKNOWN_GPIO3 101 /* out */ | ||
| 74 | +#define N810_KB_LOCK_GPIO 102 | ||
| 75 | +#define N800_TSC_TS_GPIO 103 | ||
| 76 | +#define N810_TSC2005_GPIO 106 | ||
| 77 | +#define N800_HEADPHONE_GPIO 107 | ||
| 78 | +#define N8X0_RETU_GPIO 108 | ||
| 79 | +#define N800_TSC_KP_IRQ_GPIO 109 | ||
| 80 | +#define N810_KEYBOARD_GPIO 109 | ||
| 81 | +#define N800_BAT_COVER_GPIO 110 | ||
| 82 | +#define N810_SLIDE_GPIO 110 | ||
| 83 | +#define N8X0_TAHVO_GPIO 111 | ||
| 84 | +#define N800_UNKNOWN_GPIO4 112 /* out */ | ||
| 85 | +#define N810_TSC_RESET_GPIO 118 | ||
| 86 | +#define N800_TSC_RESET_GPIO 119 /* ? */ | ||
| 87 | +#define N8X0_TMP105_GPIO 125 | ||
| 88 | + | ||
| 89 | +/* Config */ | ||
| 90 | +#define XLDR_LL_UART 1 | ||
| 91 | + | ||
| 92 | +/* Addresses on the I2C bus */ | ||
| 93 | +#define N8X0_TMP105_ADDR 0x48 | ||
| 94 | +#define N8X0_MENELAUS_ADDR 0x72 | ||
| 95 | + | ||
| 96 | +/* Chipselects on GPMC NOR interface */ | ||
| 97 | +#define N8X0_ONENAND_CS 0 | ||
| 98 | +#define N8X0_USB_ASYNC_CS 1 | ||
| 99 | +#define N8X0_USB_SYNC_CS 4 | ||
| 100 | + | ||
| 101 | +static void n800_mmc_cs_cb(void *opaque, int line, int level) | ||
| 102 | +{ | ||
| 103 | + /* TODO: this seems to actually be connected to the menelaus, to | ||
| 104 | + * which also both MMC slots connect. */ | ||
| 105 | + omap_mmc_enable((struct omap_mmc_s *) opaque, !level); | ||
| 106 | + | ||
| 107 | + printf("%s: MMC slot %i active\n", __FUNCTION__, level + 1); | ||
| 108 | +} | ||
| 109 | + | ||
| 110 | +static void n800_gpio_setup(struct n800_s *s) | ||
| 111 | +{ | ||
| 112 | + qemu_irq *mmc_cs = qemu_allocate_irqs(n800_mmc_cs_cb, s->cpu->mmc, 1); | ||
| 113 | + omap2_gpio_out_set(s->cpu->gpif, N800_MMC_CS_GPIO, mmc_cs[0]); | ||
| 114 | + | ||
| 115 | + qemu_irq_lower(omap2_gpio_in_get(s->cpu->gpif, N800_BAT_COVER_GPIO)[0]); | ||
| 116 | +} | ||
| 117 | + | ||
| 118 | +static void n8x0_nand_setup(struct n800_s *s) | ||
| 119 | +{ | ||
| 120 | + /* Either ec40xx or ec48xx are OK for the ID */ | ||
| 121 | + omap_gpmc_attach(s->cpu->gpmc, N8X0_ONENAND_CS, 0, onenand_base_update, | ||
| 122 | + onenand_base_unmap, | ||
| 123 | + onenand_init(0xec4800, 1, | ||
| 124 | + omap2_gpio_in_get(s->cpu->gpif, | ||
| 125 | + N8X0_ONENAND_GPIO)[0])); | ||
| 126 | +} | ||
| 127 | + | ||
| 128 | +static void n800_i2c_setup(struct n800_s *s) | ||
| 129 | +{ | ||
| 130 | + qemu_irq tmp_irq = omap2_gpio_in_get(s->cpu->gpif, N8X0_TMP105_GPIO)[0]; | ||
| 131 | + | ||
| 132 | + /* Attach the CPU on one end of our I2C bus. */ | ||
| 133 | + s->i2c = omap_i2c_bus(s->cpu->i2c[0]); | ||
| 134 | + | ||
| 135 | + /* Attach a menelaus PM chip */ | ||
| 136 | + i2c_set_slave_address( | ||
| 137 | + twl92230_init(s->i2c, | ||
| 138 | + s->cpu->irq[0][OMAP_INT_24XX_SYS_NIRQ]), | ||
| 139 | + N8X0_MENELAUS_ADDR); | ||
| 140 | + | ||
| 141 | + /* Attach a TMP105 PM chip (A0 wired to ground) */ | ||
| 142 | + i2c_set_slave_address(tmp105_init(s->i2c, tmp_irq), N8X0_TMP105_ADDR); | ||
| 143 | +} | ||
| 144 | + | ||
| 145 | +/* Touchscreen and keypad controller */ | ||
| 146 | +#define RETU_KEYCODE 61 /* F3 */ | ||
| 147 | + | ||
| 148 | +static void n800_key_event(void *opaque, int keycode) | ||
| 149 | +{ | ||
| 150 | + struct n800_s *s = (struct n800_s *) opaque; | ||
| 151 | + int code = s->keymap[keycode & 0x7f]; | ||
| 152 | + | ||
| 153 | + if (code == -1) { | ||
| 154 | + if ((keycode & 0x7f) == RETU_KEYCODE) | ||
| 155 | + retu_key_event(s->retu, !(keycode & 0x80)); | ||
| 156 | + return; | ||
| 157 | + } | ||
| 158 | + | ||
| 159 | + tsc210x_key_event(s->ts, code, !(keycode & 0x80)); | ||
| 160 | +} | ||
| 161 | + | ||
| 162 | +static const int n800_keys[16] = { | ||
| 163 | + -1, | ||
| 164 | + 72, /* Up */ | ||
| 165 | + 63, /* Home (F5) */ | ||
| 166 | + -1, | ||
| 167 | + 75, /* Left */ | ||
| 168 | + 28, /* Enter */ | ||
| 169 | + 77, /* Right */ | ||
| 170 | + -1, | ||
| 171 | + 1, /* Cycle (ESC) */ | ||
| 172 | + 80, /* Down */ | ||
| 173 | + 62, /* Menu (F4) */ | ||
| 174 | + -1, | ||
| 175 | + 66, /* Zoom- (F8) */ | ||
| 176 | + 64, /* FS (F6) */ | ||
| 177 | + 65, /* Zoom+ (F7) */ | ||
| 178 | + -1, | ||
| 179 | +}; | ||
| 180 | + | ||
| 181 | +static struct mouse_transform_info_s n800_pointercal = { | ||
| 182 | + .x = 800, | ||
| 183 | + .y = 480, | ||
| 184 | + .a = { 14560, -68, -3455208, -39, -9621, 35152972, 65536 }, | ||
| 185 | +}; | ||
| 186 | + | ||
| 187 | +static void n800_tsc_setup(struct n800_s *s) | ||
| 188 | +{ | ||
| 189 | + int i; | ||
| 190 | + | ||
| 191 | + /* XXX: are the three pins inverted inside the chip between the | ||
| 192 | + * tsc and the cpu (N4111)? */ | ||
| 193 | + qemu_irq penirq = 0; /* NC */ | ||
| 194 | + qemu_irq kbirq = omap2_gpio_in_get(s->cpu->gpif, N800_TSC_KP_IRQ_GPIO)[0]; | ||
| 195 | + qemu_irq dav = omap2_gpio_in_get(s->cpu->gpif, N800_TSC_TS_GPIO)[0]; | ||
| 196 | + | ||
| 197 | + s->ts = tsc2301_init(penirq, kbirq, dav, 0); | ||
| 198 | + | ||
| 199 | + for (i = 0; i < 0x80; i ++) | ||
| 200 | + s->keymap[i] = -1; | ||
| 201 | + for (i = 0; i < 0x10; i ++) | ||
| 202 | + if (n800_keys[i] >= 0) | ||
| 203 | + s->keymap[n800_keys[i]] = i; | ||
| 204 | + | ||
| 205 | + qemu_add_kbd_event_handler(n800_key_event, s); | ||
| 206 | + | ||
| 207 | + tsc210x_set_transform(s->ts, &n800_pointercal); | ||
| 208 | +} | ||
| 209 | + | ||
| 210 | +/* LCD MIPI DBI-C controller (URAL) */ | ||
| 211 | +struct mipid_s { | ||
| 212 | + int resp[4]; | ||
| 213 | + int param[4]; | ||
| 214 | + int p; | ||
| 215 | + int pm; | ||
| 216 | + int cmd; | ||
| 217 | + | ||
| 218 | + int sleep; | ||
| 219 | + int booster; | ||
| 220 | + int te; | ||
| 221 | + int selfcheck; | ||
| 222 | + int partial; | ||
| 223 | + int normal; | ||
| 224 | + int vscr; | ||
| 225 | + int invert; | ||
| 226 | + int onoff; | ||
| 227 | + int gamma; | ||
| 228 | + uint32_t id; | ||
| 229 | +}; | ||
| 230 | + | ||
| 231 | +static void mipid_reset(struct mipid_s *s) | ||
| 232 | +{ | ||
| 233 | + if (!s->sleep) | ||
| 234 | + fprintf(stderr, "%s: Display off\n", __FUNCTION__); | ||
| 235 | + | ||
| 236 | + s->pm = 0; | ||
| 237 | + s->cmd = 0; | ||
| 238 | + | ||
| 239 | + s->sleep = 1; | ||
| 240 | + s->booster = 0; | ||
| 241 | + s->selfcheck = | ||
| 242 | + (1 << 7) | /* Register loading OK. */ | ||
| 243 | + (1 << 5) | /* The chip is attached. */ | ||
| 244 | + (1 << 4); /* Display glass still in one piece. */ | ||
| 245 | + s->te = 0; | ||
| 246 | + s->partial = 0; | ||
| 247 | + s->normal = 1; | ||
| 248 | + s->vscr = 0; | ||
| 249 | + s->invert = 0; | ||
| 250 | + s->onoff = 1; | ||
| 251 | + s->gamma = 0; | ||
| 252 | +} | ||
| 253 | + | ||
| 254 | +static uint32_t mipid_txrx(void *opaque, uint32_t cmd) | ||
| 255 | +{ | ||
| 256 | + struct mipid_s *s = (struct mipid_s *) opaque; | ||
| 257 | + uint8_t ret; | ||
| 258 | + | ||
| 259 | + if (s->p >= sizeof(s->resp) / sizeof(*s->resp)) | ||
| 260 | + ret = 0; | ||
| 261 | + else | ||
| 262 | + ret = s->resp[s->p ++]; | ||
| 263 | + if (s->pm --> 0) | ||
| 264 | + s->param[s->pm] = cmd; | ||
| 265 | + else | ||
| 266 | + s->cmd = cmd; | ||
| 267 | + | ||
| 268 | + switch (s->cmd) { | ||
| 269 | + case 0x00: /* NOP */ | ||
| 270 | + break; | ||
| 271 | + | ||
| 272 | + case 0x01: /* SWRESET */ | ||
| 273 | + mipid_reset(s); | ||
| 274 | + break; | ||
| 275 | + | ||
| 276 | + case 0x02: /* BSTROFF */ | ||
| 277 | + s->booster = 0; | ||
| 278 | + break; | ||
| 279 | + case 0x03: /* BSTRON */ | ||
| 280 | + s->booster = 1; | ||
| 281 | + break; | ||
| 282 | + | ||
| 283 | + case 0x04: /* RDDID */ | ||
| 284 | + s->p = 0; | ||
| 285 | + s->resp[0] = (s->id >> 16) & 0xff; | ||
| 286 | + s->resp[1] = (s->id >> 8) & 0xff; | ||
| 287 | + s->resp[2] = (s->id >> 0) & 0xff; | ||
| 288 | + break; | ||
| 289 | + | ||
| 290 | + case 0x06: /* RD_RED */ | ||
| 291 | + case 0x07: /* RD_GREEN */ | ||
| 292 | + /* XXX the bootloader sometimes issues RD_BLUE meaning RDDID so | ||
| 293 | + * for the bootloader one needs to change this. */ | ||
| 294 | + case 0x08: /* RD_BLUE */ | ||
| 295 | + s->p = 0; | ||
| 296 | + /* TODO: return first pixel components */ | ||
| 297 | + s->resp[0] = 0x01; | ||
| 298 | + break; | ||
| 299 | + | ||
| 300 | + case 0x09: /* RDDST */ | ||
| 301 | + s->p = 0; | ||
| 302 | + s->resp[0] = s->booster << 7; | ||
| 303 | + s->resp[1] = (5 << 4) | (s->partial << 2) | | ||
| 304 | + (s->sleep << 1) | s->normal; | ||
| 305 | + s->resp[2] = (s->vscr << 7) | (s->invert << 5) | | ||
| 306 | + (s->onoff << 2) | (s->te << 1) | (s->gamma >> 2); | ||
| 307 | + s->resp[3] = s->gamma << 6; | ||
| 308 | + break; | ||
| 309 | + | ||
| 310 | + case 0x0a: /* RDDPM */ | ||
| 311 | + s->p = 0; | ||
| 312 | + s->resp[0] = (s->onoff << 2) | (s->normal << 3) | (s->sleep << 4) | | ||
| 313 | + (s->partial << 5) | (s->sleep << 6) | (s->booster << 7); | ||
| 314 | + break; | ||
| 315 | + case 0x0b: /* RDDMADCTR */ | ||
| 316 | + s->p = 0; | ||
| 317 | + s->resp[0] = 0; | ||
| 318 | + break; | ||
| 319 | + case 0x0c: /* RDDCOLMOD */ | ||
| 320 | + s->p = 0; | ||
| 321 | + s->resp[0] = 5; /* 65K colours */ | ||
| 322 | + break; | ||
| 323 | + case 0x0d: /* RDDIM */ | ||
| 324 | + s->p = 0; | ||
| 325 | + s->resp[0] = (s->invert << 5) | (s->vscr << 7) | s->gamma; | ||
| 326 | + break; | ||
| 327 | + case 0x0e: /* RDDSM */ | ||
| 328 | + s->p = 0; | ||
| 329 | + s->resp[0] = s->te << 7; | ||
| 330 | + break; | ||
| 331 | + case 0x0f: /* RDDSDR */ | ||
| 332 | + s->p = 0; | ||
| 333 | + s->resp[0] = s->selfcheck; | ||
| 334 | + break; | ||
| 335 | + | ||
| 336 | + case 0x10: /* SLPIN */ | ||
| 337 | + s->sleep = 1; | ||
| 338 | + break; | ||
| 339 | + case 0x11: /* SLPOUT */ | ||
| 340 | + s->sleep = 0; | ||
| 341 | + s->selfcheck ^= 1 << 6; /* POFF self-diagnosis Ok */ | ||
| 342 | + break; | ||
| 343 | + | ||
| 344 | + case 0x12: /* PTLON */ | ||
| 345 | + s->partial = 1; | ||
| 346 | + s->normal = 0; | ||
| 347 | + s->vscr = 0; | ||
| 348 | + break; | ||
| 349 | + case 0x13: /* NORON */ | ||
| 350 | + s->partial = 0; | ||
| 351 | + s->normal = 1; | ||
| 352 | + s->vscr = 0; | ||
| 353 | + break; | ||
| 354 | + | ||
| 355 | + case 0x20: /* INVOFF */ | ||
| 356 | + s->invert = 0; | ||
| 357 | + break; | ||
| 358 | + case 0x21: /* INVON */ | ||
| 359 | + s->invert = 1; | ||
| 360 | + break; | ||
| 361 | + | ||
| 362 | + case 0x22: /* APOFF */ | ||
| 363 | + case 0x23: /* APON */ | ||
| 364 | + goto bad_cmd; | ||
| 365 | + | ||
| 366 | + case 0x25: /* WRCNTR */ | ||
| 367 | + if (s->pm < 0) | ||
| 368 | + s->pm = 1; | ||
| 369 | + goto bad_cmd; | ||
| 370 | + | ||
| 371 | + case 0x26: /* GAMSET */ | ||
| 372 | + if (!s->pm) | ||
| 373 | + s->gamma = ffs(s->param[0] & 0xf) - 1; | ||
| 374 | + else if (s->pm < 0) | ||
| 375 | + s->pm = 1; | ||
| 376 | + break; | ||
| 377 | + | ||
| 378 | + case 0x28: /* DISPOFF */ | ||
| 379 | + s->onoff = 0; | ||
| 380 | + fprintf(stderr, "%s: Display off\n", __FUNCTION__); | ||
| 381 | + break; | ||
| 382 | + case 0x29: /* DISPON */ | ||
| 383 | + s->onoff = 1; | ||
| 384 | + fprintf(stderr, "%s: Display on\n", __FUNCTION__); | ||
| 385 | + break; | ||
| 386 | + | ||
| 387 | + case 0x2a: /* CASET */ | ||
| 388 | + case 0x2b: /* RASET */ | ||
| 389 | + case 0x2c: /* RAMWR */ | ||
| 390 | + case 0x2d: /* RGBSET */ | ||
| 391 | + case 0x2e: /* RAMRD */ | ||
| 392 | + case 0x30: /* PTLAR */ | ||
| 393 | + case 0x33: /* SCRLAR */ | ||
| 394 | + goto bad_cmd; | ||
| 395 | + | ||
| 396 | + case 0x34: /* TEOFF */ | ||
| 397 | + s->te = 0; | ||
| 398 | + break; | ||
| 399 | + case 0x35: /* TEON */ | ||
| 400 | + if (!s->pm) | ||
| 401 | + s->te = 1; | ||
| 402 | + else if (s->pm < 0) | ||
| 403 | + s->pm = 1; | ||
| 404 | + break; | ||
| 405 | + | ||
| 406 | + case 0x36: /* MADCTR */ | ||
| 407 | + goto bad_cmd; | ||
| 408 | + | ||
| 409 | + case 0x37: /* VSCSAD */ | ||
| 410 | + s->partial = 0; | ||
| 411 | + s->normal = 0; | ||
| 412 | + s->vscr = 1; | ||
| 413 | + break; | ||
| 414 | + | ||
| 415 | + case 0x38: /* IDMOFF */ | ||
| 416 | + case 0x39: /* IDMON */ | ||
| 417 | + case 0x3a: /* COLMOD */ | ||
| 418 | + goto bad_cmd; | ||
| 419 | + | ||
| 420 | + case 0xb0: /* CLKINT / DISCTL */ | ||
| 421 | + case 0xb1: /* CLKEXT */ | ||
| 422 | + if (s->pm < 0) | ||
| 423 | + s->pm = 2; | ||
| 424 | + break; | ||
| 425 | + | ||
| 426 | + case 0xb4: /* FRMSEL */ | ||
| 427 | + break; | ||
| 428 | + | ||
| 429 | + case 0xb5: /* FRM8SEL */ | ||
| 430 | + case 0xb6: /* TMPRNG / INIESC */ | ||
| 431 | + case 0xb7: /* TMPHIS / NOP2 */ | ||
| 432 | + case 0xb8: /* TMPREAD / MADCTL */ | ||
| 433 | + case 0xba: /* DISTCTR */ | ||
| 434 | + case 0xbb: /* EPVOL */ | ||
| 435 | + goto bad_cmd; | ||
| 436 | + | ||
| 437 | + case 0xbd: /* Unknown */ | ||
| 438 | + s->p = 0; | ||
| 439 | + s->resp[0] = 0; | ||
| 440 | + s->resp[1] = 1; | ||
| 441 | + break; | ||
| 442 | + | ||
| 443 | + case 0xc2: /* IFMOD */ | ||
| 444 | + if (s->pm < 0) | ||
| 445 | + s->pm = 2; | ||
| 446 | + break; | ||
| 447 | + | ||
| 448 | + case 0xc6: /* PWRCTL */ | ||
| 449 | + case 0xc7: /* PPWRCTL */ | ||
| 450 | + case 0xd0: /* EPWROUT */ | ||
| 451 | + case 0xd1: /* EPWRIN */ | ||
| 452 | + case 0xd4: /* RDEV */ | ||
| 453 | + case 0xd5: /* RDRR */ | ||
| 454 | + goto bad_cmd; | ||
| 455 | + | ||
| 456 | + case 0xda: /* RDID1 */ | ||
| 457 | + s->p = 0; | ||
| 458 | + s->resp[0] = (s->id >> 16) & 0xff; | ||
| 459 | + break; | ||
| 460 | + case 0xdb: /* RDID2 */ | ||
| 461 | + s->p = 0; | ||
| 462 | + s->resp[0] = (s->id >> 8) & 0xff; | ||
| 463 | + break; | ||
| 464 | + case 0xdc: /* RDID3 */ | ||
| 465 | + s->p = 0; | ||
| 466 | + s->resp[0] = (s->id >> 0) & 0xff; | ||
| 467 | + break; | ||
| 468 | + | ||
| 469 | + default: | ||
| 470 | + bad_cmd: | ||
| 471 | + fprintf(stderr, "%s: unknown command %02x\n", __FUNCTION__, s->cmd); | ||
| 472 | + break; | ||
| 473 | + } | ||
| 474 | + | ||
| 475 | + return ret; | ||
| 476 | +} | ||
| 477 | + | ||
| 478 | +static void *mipid_init(void) | ||
| 479 | +{ | ||
| 480 | + struct mipid_s *s = (struct mipid_s *) qemu_mallocz(sizeof(*s)); | ||
| 481 | + | ||
| 482 | + s->id = 0x838f03; | ||
| 483 | + mipid_reset(s); | ||
| 484 | + | ||
| 485 | + return s; | ||
| 486 | +} | ||
| 487 | + | ||
| 488 | +static void n800_spi_setup(struct n800_s *s) | ||
| 489 | +{ | ||
| 490 | + void *tsc2301 = s->ts->opaque; | ||
| 491 | + void *mipid = mipid_init(); | ||
| 492 | + | ||
| 493 | + omap_mcspi_attach(s->cpu->mcspi[0], tsc210x_txrx, tsc2301, 0); | ||
| 494 | + omap_mcspi_attach(s->cpu->mcspi[0], mipid_txrx, mipid, 1); | ||
| 495 | +} | ||
| 496 | + | ||
| 497 | +/* This task is normally performed by the bootloader. If we're loading | ||
| 498 | + * a kernel directly, we need to enable the Blizzard ourselves. */ | ||
| 499 | +static void n800_dss_init(struct rfbi_chip_s *chip) | ||
| 500 | +{ | ||
| 501 | + uint8_t *fb_blank; | ||
| 502 | + | ||
| 503 | + chip->write(chip->opaque, 0, 0x2a); /* LCD Width register */ | ||
| 504 | + chip->write(chip->opaque, 1, 0x64); | ||
| 505 | + chip->write(chip->opaque, 0, 0x2c); /* LCD HNDP register */ | ||
| 506 | + chip->write(chip->opaque, 1, 0x1e); | ||
| 507 | + chip->write(chip->opaque, 0, 0x2e); /* LCD Height 0 register */ | ||
| 508 | + chip->write(chip->opaque, 1, 0xe0); | ||
| 509 | + chip->write(chip->opaque, 0, 0x30); /* LCD Height 1 register */ | ||
| 510 | + chip->write(chip->opaque, 1, 0x01); | ||
| 511 | + chip->write(chip->opaque, 0, 0x32); /* LCD VNDP register */ | ||
| 512 | + chip->write(chip->opaque, 1, 0x06); | ||
| 513 | + chip->write(chip->opaque, 0, 0x68); /* Display Mode register */ | ||
| 514 | + chip->write(chip->opaque, 1, 1); /* Enable bit */ | ||
| 515 | + | ||
| 516 | + chip->write(chip->opaque, 0, 0x6c); | ||
| 517 | + chip->write(chip->opaque, 1, 0x00); /* Input X Start Position */ | ||
| 518 | + chip->write(chip->opaque, 1, 0x00); /* Input X Start Position */ | ||
| 519 | + chip->write(chip->opaque, 1, 0x00); /* Input Y Start Position */ | ||
| 520 | + chip->write(chip->opaque, 1, 0x00); /* Input Y Start Position */ | ||
| 521 | + chip->write(chip->opaque, 1, 0x1f); /* Input X End Position */ | ||
| 522 | + chip->write(chip->opaque, 1, 0x03); /* Input X End Position */ | ||
| 523 | + chip->write(chip->opaque, 1, 0xdf); /* Input Y End Position */ | ||
| 524 | + chip->write(chip->opaque, 1, 0x01); /* Input Y End Position */ | ||
| 525 | + chip->write(chip->opaque, 1, 0x00); /* Output X Start Position */ | ||
| 526 | + chip->write(chip->opaque, 1, 0x00); /* Output X Start Position */ | ||
| 527 | + chip->write(chip->opaque, 1, 0x00); /* Output Y Start Position */ | ||
| 528 | + chip->write(chip->opaque, 1, 0x00); /* Output Y Start Position */ | ||
| 529 | + chip->write(chip->opaque, 1, 0x1f); /* Output X End Position */ | ||
| 530 | + chip->write(chip->opaque, 1, 0x03); /* Output X End Position */ | ||
| 531 | + chip->write(chip->opaque, 1, 0xdf); /* Output Y End Position */ | ||
| 532 | + chip->write(chip->opaque, 1, 0x01); /* Output Y End Position */ | ||
| 533 | + chip->write(chip->opaque, 1, 0x01); /* Input Data Format */ | ||
| 534 | + chip->write(chip->opaque, 1, 0x01); /* Data Source Select */ | ||
| 535 | + | ||
| 536 | + fb_blank = memset(qemu_malloc(800 * 480 * 2), 0xff, 800 * 480 * 2); | ||
| 537 | + /* Display Memory Data Port */ | ||
| 538 | + chip->block(chip->opaque, 1, fb_blank, 800 * 480 * 2, 800); | ||
| 539 | + free(fb_blank); | ||
| 540 | +} | ||
| 541 | + | ||
| 542 | +static void n800_dss_setup(struct n800_s *s, DisplayState *ds) | ||
| 543 | +{ | ||
| 544 | + s->blizzard.opaque = s1d13745_init(0, ds); | ||
| 545 | + s->blizzard.block = s1d13745_write_block; | ||
| 546 | + s->blizzard.write = s1d13745_write; | ||
| 547 | + s->blizzard.read = s1d13745_read; | ||
| 548 | + | ||
| 549 | + omap_rfbi_attach(s->cpu->dss, 0, &s->blizzard); | ||
| 550 | +} | ||
| 551 | + | ||
| 552 | +static void n800_cbus_setup(struct n800_s *s) | ||
| 553 | +{ | ||
| 554 | + qemu_irq dat_out = omap2_gpio_in_get(s->cpu->gpif, N8X0_CBUS_DAT_GPIO)[0]; | ||
| 555 | + qemu_irq retu_irq = omap2_gpio_in_get(s->cpu->gpif, N8X0_RETU_GPIO)[0]; | ||
| 556 | + qemu_irq tahvo_irq = omap2_gpio_in_get(s->cpu->gpif, N8X0_TAHVO_GPIO)[0]; | ||
| 557 | + | ||
| 558 | + struct cbus_s *cbus = cbus_init(dat_out); | ||
| 559 | + | ||
| 560 | + omap2_gpio_out_set(s->cpu->gpif, N8X0_CBUS_CLK_GPIO, cbus->clk); | ||
| 561 | + omap2_gpio_out_set(s->cpu->gpif, N8X0_CBUS_DAT_GPIO, cbus->dat); | ||
| 562 | + omap2_gpio_out_set(s->cpu->gpif, N8X0_CBUS_SEL_GPIO, cbus->sel); | ||
| 563 | + | ||
| 564 | + cbus_attach(cbus, s->retu = retu_init(retu_irq, 1)); | ||
| 565 | + cbus_attach(cbus, s->tahvo = tahvo_init(tahvo_irq, 1)); | ||
| 566 | +} | ||
| 567 | + | ||
| 568 | +/* This task is normally performed by the bootloader. If we're loading | ||
| 569 | + * a kernel directly, we need to set up GPMC mappings ourselves. */ | ||
| 570 | +static void n800_gpmc_init(struct n800_s *s) | ||
| 571 | +{ | ||
| 572 | + uint32_t config7 = | ||
| 573 | + (0xf << 8) | /* MASKADDRESS */ | ||
| 574 | + (1 << 6) | /* CSVALID */ | ||
| 575 | + (4 << 0); /* BASEADDRESS */ | ||
| 576 | + | ||
| 577 | + cpu_physical_memory_write(0x6800a078, /* GPMC_CONFIG7_0 */ | ||
| 578 | + (void *) &config7, sizeof(config7)); | ||
| 579 | +} | ||
| 580 | + | ||
| 581 | +/* Setup sequence done by the bootloader */ | ||
| 582 | +static void n800_boot_init(void *opaque) | ||
| 583 | +{ | ||
| 584 | + struct n800_s *s = (struct n800_s *) opaque; | ||
| 585 | + uint32_t buf; | ||
| 586 | + | ||
| 587 | + /* PRCM setup */ | ||
| 588 | +#define omap_writel(addr, val) \ | ||
| 589 | + buf = (val); \ | ||
| 590 | + cpu_physical_memory_write(addr, (void *) &buf, sizeof(buf)) | ||
| 591 | + | ||
| 592 | + omap_writel(0x48008060, 0x41); /* PRCM_CLKSRC_CTRL */ | ||
| 593 | + omap_writel(0x48008070, 1); /* PRCM_CLKOUT_CTRL */ | ||
| 594 | + omap_writel(0x48008078, 0); /* PRCM_CLKEMUL_CTRL */ | ||
| 595 | + omap_writel(0x48008090, 0); /* PRCM_VOLTSETUP */ | ||
| 596 | + omap_writel(0x48008094, 0); /* PRCM_CLKSSETUP */ | ||
| 597 | + omap_writel(0x48008098, 0); /* PRCM_POLCTRL */ | ||
| 598 | + omap_writel(0x48008140, 2); /* CM_CLKSEL_MPU */ | ||
| 599 | + omap_writel(0x48008148, 0); /* CM_CLKSTCTRL_MPU */ | ||
| 600 | + omap_writel(0x48008158, 1); /* RM_RSTST_MPU */ | ||
| 601 | + omap_writel(0x480081c8, 0x15); /* PM_WKDEP_MPU */ | ||
| 602 | + omap_writel(0x480081d4, 0x1d4); /* PM_EVGENCTRL_MPU */ | ||
| 603 | + omap_writel(0x480081d8, 0); /* PM_EVEGENONTIM_MPU */ | ||
| 604 | + omap_writel(0x480081dc, 0); /* PM_EVEGENOFFTIM_MPU */ | ||
| 605 | + omap_writel(0x480081e0, 0xc); /* PM_PWSTCTRL_MPU */ | ||
| 606 | + omap_writel(0x48008200, 0x047e7ff7); /* CM_FCLKEN1_CORE */ | ||
| 607 | + omap_writel(0x48008204, 0x00000004); /* CM_FCLKEN2_CORE */ | ||
| 608 | + omap_writel(0x48008210, 0x047e7ff1); /* CM_ICLKEN1_CORE */ | ||
| 609 | + omap_writel(0x48008214, 0x00000004); /* CM_ICLKEN2_CORE */ | ||
| 610 | + omap_writel(0x4800821c, 0x00000000); /* CM_ICLKEN4_CORE */ | ||
| 611 | + omap_writel(0x48008230, 0); /* CM_AUTOIDLE1_CORE */ | ||
| 612 | + omap_writel(0x48008234, 0); /* CM_AUTOIDLE2_CORE */ | ||
| 613 | + omap_writel(0x48008238, 7); /* CM_AUTOIDLE3_CORE */ | ||
| 614 | + omap_writel(0x4800823c, 0); /* CM_AUTOIDLE4_CORE */ | ||
| 615 | + omap_writel(0x48008240, 0x04360626); /* CM_CLKSEL1_CORE */ | ||
| 616 | + omap_writel(0x48008244, 0x00000014); /* CM_CLKSEL2_CORE */ | ||
| 617 | + omap_writel(0x48008248, 0); /* CM_CLKSTCTRL_CORE */ | ||
| 618 | + omap_writel(0x48008300, 0x00000000); /* CM_FCLKEN_GFX */ | ||
| 619 | + omap_writel(0x48008310, 0x00000000); /* CM_ICLKEN_GFX */ | ||
| 620 | + omap_writel(0x48008340, 0x00000001); /* CM_CLKSEL_GFX */ | ||
| 621 | + omap_writel(0x48008400, 0x00000004); /* CM_FCLKEN_WKUP */ | ||
| 622 | + omap_writel(0x48008410, 0x00000004); /* CM_ICLKEN_WKUP */ | ||
| 623 | + omap_writel(0x48008440, 0x00000000); /* CM_CLKSEL_WKUP */ | ||
| 624 | + omap_writel(0x48008500, 0x000000cf); /* CM_CLKEN_PLL */ | ||
| 625 | + omap_writel(0x48008530, 0x0000000c); /* CM_AUTOIDLE_PLL */ | ||
| 626 | + omap_writel(0x48008540, /* CM_CLKSEL1_PLL */ | ||
| 627 | + (0x78 << 12) | (6 << 8)); | ||
| 628 | + omap_writel(0x48008544, 2); /* CM_CLKSEL2_PLL */ | ||
| 629 | + | ||
| 630 | + /* GPMC setup */ | ||
| 631 | + n800_gpmc_init(s); | ||
| 632 | + | ||
| 633 | + /* Video setup */ | ||
| 634 | + n800_dss_init(&s->blizzard); | ||
| 635 | + | ||
| 636 | + /* CPU setup */ | ||
| 637 | + s->cpu->env->regs[15] = s->cpu->env->boot_info->loader_start; | ||
| 638 | + s->cpu->env->GE = 0x5; | ||
| 639 | +} | ||
| 640 | + | ||
| 641 | +#define OMAP_TAG_NOKIA_BT 0x4e01 | ||
| 642 | +#define OMAP_TAG_WLAN_CX3110X 0x4e02 | ||
| 643 | +#define OMAP_TAG_CBUS 0x4e03 | ||
| 644 | +#define OMAP_TAG_EM_ASIC_BB5 0x4e04 | ||
| 645 | + | ||
| 646 | +static int n800_atag_setup(struct arm_boot_info *info, void *p) | ||
| 647 | +{ | ||
| 648 | + uint8_t *b; | ||
| 649 | + uint16_t *w; | ||
| 650 | + uint32_t *l; | ||
| 651 | + | ||
| 652 | + w = p; | ||
| 653 | + | ||
| 654 | + stw_raw(w ++, OMAP_TAG_UART); /* u16 tag */ | ||
| 655 | + stw_raw(w ++, 4); /* u16 len */ | ||
| 656 | + stw_raw(w ++, (1 << 2) | (1 << 1) | (1 << 0)); /* uint enabled_uarts */ | ||
| 657 | + w ++; | ||
| 658 | + | ||
| 659 | + stw_raw(w ++, OMAP_TAG_EM_ASIC_BB5); /* u16 tag */ | ||
| 660 | + stw_raw(w ++, 4); /* u16 len */ | ||
| 661 | + stw_raw(w ++, N8X0_RETU_GPIO); /* s16 retu_irq_gpio */ | ||
| 662 | + stw_raw(w ++, N8X0_TAHVO_GPIO); /* s16 tahvo_irq_gpio */ | ||
| 663 | + | ||
| 664 | + stw_raw(w ++, OMAP_TAG_CBUS); /* u16 tag */ | ||
| 665 | + stw_raw(w ++, 8); /* u16 len */ | ||
| 666 | + stw_raw(w ++, N8X0_CBUS_CLK_GPIO); /* s16 clk_gpio */ | ||
| 667 | + stw_raw(w ++, N8X0_CBUS_DAT_GPIO); /* s16 dat_gpio */ | ||
| 668 | + stw_raw(w ++, N8X0_CBUS_SEL_GPIO); /* s16 sel_gpio */ | ||
| 669 | + w ++; | ||
| 670 | + | ||
| 671 | + stw_raw(w ++, OMAP_TAG_GPIO_SWITCH); /* u16 tag */ | ||
| 672 | + stw_raw(w ++, 20); /* u16 len */ | ||
| 673 | + strcpy((void *) w, "bat_cover"); /* char name[12] */ | ||
| 674 | + w += 6; | ||
| 675 | + stw_raw(w ++, N800_BAT_COVER_GPIO); /* u16 gpio */ | ||
| 676 | + stw_raw(w ++, 0x01); | ||
| 677 | + stw_raw(w ++, 0); | ||
| 678 | + stw_raw(w ++, 0); | ||
| 679 | + | ||
| 680 | + stw_raw(w ++, OMAP_TAG_GPIO_SWITCH); /* u16 tag */ | ||
| 681 | + stw_raw(w ++, 20); /* u16 len */ | ||
| 682 | + strcpy((void *) w, "cam_act"); /* char name[12] */ | ||
| 683 | + w += 6; | ||
| 684 | + stw_raw(w ++, N800_CAM_ACT_GPIO); /* u16 gpio */ | ||
| 685 | + stw_raw(w ++, 0x20); | ||
| 686 | + stw_raw(w ++, 0); | ||
| 687 | + stw_raw(w ++, 0); | ||
| 688 | + | ||
| 689 | + stw_raw(w ++, OMAP_TAG_GPIO_SWITCH); /* u16 tag */ | ||
| 690 | + stw_raw(w ++, 20); /* u16 len */ | ||
| 691 | + strcpy((void *) w, "cam_turn"); /* char name[12] */ | ||
| 692 | + w += 6; | ||
| 693 | + stw_raw(w ++, N800_CAM_TURN_GPIO); /* u16 gpio */ | ||
| 694 | + stw_raw(w ++, 0x21); | ||
| 695 | + stw_raw(w ++, 0); | ||
| 696 | + stw_raw(w ++, 0); | ||
| 697 | + | ||
| 698 | + stw_raw(w ++, OMAP_TAG_GPIO_SWITCH); /* u16 tag */ | ||
| 699 | + stw_raw(w ++, 20); /* u16 len */ | ||
| 700 | + strcpy((void *) w, "headphone"); /* char name[12] */ | ||
| 701 | + w += 6; | ||
| 702 | + stw_raw(w ++, N800_HEADPHONE_GPIO); /* u16 gpio */ | ||
| 703 | + stw_raw(w ++, 0x11); | ||
| 704 | + stw_raw(w ++, 0); | ||
| 705 | + stw_raw(w ++, 0); | ||
| 706 | + | ||
| 707 | + stw_raw(w ++, OMAP_TAG_NOKIA_BT); /* u16 tag */ | ||
| 708 | + stw_raw(w ++, 12); /* u16 len */ | ||
| 709 | + b = (void *) w; | ||
| 710 | + stb_raw(b ++, 0x01); /* u8 chip_type (CSR) */ | ||
| 711 | + stb_raw(b ++, N800_BT_WKUP_GPIO); /* u8 bt_wakeup_gpio */ | ||
| 712 | + stb_raw(b ++, N8X0_BT_HOST_WKUP_GPIO); /* u8 host_wakeup_gpio */ | ||
| 713 | + stb_raw(b ++, N800_BT_RESET_GPIO); /* u8 reset_gpio */ | ||
| 714 | + stb_raw(b ++, 1); /* u8 bt_uart */ | ||
| 715 | + memset(b, 0, 6); /* u8 bd_addr[6] */ | ||
| 716 | + b += 6; | ||
| 717 | + stb_raw(b ++, 0x02); /* u8 bt_sysclk (38.4) */ | ||
| 718 | + w = (void *) b; | ||
| 719 | + | ||
| 720 | + stw_raw(w ++, OMAP_TAG_WLAN_CX3110X); /* u16 tag */ | ||
| 721 | + stw_raw(w ++, 8); /* u16 len */ | ||
| 722 | + stw_raw(w ++, 0x25); /* u8 chip_type */ | ||
| 723 | + stw_raw(w ++, N800_WLAN_PWR_GPIO); /* s16 power_gpio */ | ||
| 724 | + stw_raw(w ++, N800_WLAN_IRQ_GPIO); /* s16 irq_gpio */ | ||
| 725 | + stw_raw(w ++, -1); /* s16 spi_cs_gpio */ | ||
| 726 | + | ||
| 727 | + stw_raw(w ++, OMAP_TAG_MMC); /* u16 tag */ | ||
| 728 | + stw_raw(w ++, 16); /* u16 len */ | ||
| 729 | + stw_raw(w ++, 0xf); /* unsigned flags */ | ||
| 730 | + stw_raw(w ++, -1); /* s16 power_pin */ | ||
| 731 | + stw_raw(w ++, -1); /* s16 switch_pin */ | ||
| 732 | + stw_raw(w ++, -1); /* s16 wp_pin */ | ||
| 733 | + stw_raw(w ++, 0); /* unsigned flags */ | ||
| 734 | + stw_raw(w ++, 0); /* s16 power_pin */ | ||
| 735 | + stw_raw(w ++, 0); /* s16 switch_pin */ | ||
| 736 | + stw_raw(w ++, 0); /* s16 wp_pin */ | ||
| 737 | + | ||
| 738 | + stw_raw(w ++, OMAP_TAG_TEA5761); /* u16 tag */ | ||
| 739 | + stw_raw(w ++, 4); /* u16 len */ | ||
| 740 | + stw_raw(w ++, N800_TEA5761_CS_GPIO); /* u16 enable_gpio */ | ||
| 741 | + w ++; | ||
| 742 | + | ||
| 743 | + stw_raw(w ++, OMAP_TAG_PARTITION); /* u16 tag */ | ||
| 744 | + stw_raw(w ++, 28); /* u16 len */ | ||
| 745 | + strcpy((void *) w, "bootloader"); /* char name[16] */ | ||
| 746 | + l = (void *) (w + 8); | ||
| 747 | + stl_raw(l ++, 0x00020000); /* unsigned int size */ | ||
| 748 | + stl_raw(l ++, 0x00000000); /* unsigned int offset */ | ||
| 749 | + stl_raw(l ++, 0x3); /* unsigned int mask_flags */ | ||
| 750 | + w = (void *) l; | ||
| 751 | + | ||
| 752 | + stw_raw(w ++, OMAP_TAG_PARTITION); /* u16 tag */ | ||
| 753 | + stw_raw(w ++, 28); /* u16 len */ | ||
| 754 | + strcpy((void *) w, "config"); /* char name[16] */ | ||
| 755 | + l = (void *) (w + 8); | ||
| 756 | + stl_raw(l ++, 0x00060000); /* unsigned int size */ | ||
| 757 | + stl_raw(l ++, 0x00020000); /* unsigned int offset */ | ||
| 758 | + stl_raw(l ++, 0x0); /* unsigned int mask_flags */ | ||
| 759 | + w = (void *) l; | ||
| 760 | + | ||
| 761 | + stw_raw(w ++, OMAP_TAG_PARTITION); /* u16 tag */ | ||
| 762 | + stw_raw(w ++, 28); /* u16 len */ | ||
| 763 | + strcpy((void *) w, "kernel"); /* char name[16] */ | ||
| 764 | + l = (void *) (w + 8); | ||
| 765 | + stl_raw(l ++, 0x00200000); /* unsigned int size */ | ||
| 766 | + stl_raw(l ++, 0x00080000); /* unsigned int offset */ | ||
| 767 | + stl_raw(l ++, 0x0); /* unsigned int mask_flags */ | ||
| 768 | + w = (void *) l; | ||
| 769 | + | ||
| 770 | + stw_raw(w ++, OMAP_TAG_PARTITION); /* u16 tag */ | ||
| 771 | + stw_raw(w ++, 28); /* u16 len */ | ||
| 772 | + strcpy((void *) w, "initfs"); /* char name[16] */ | ||
| 773 | + l = (void *) (w + 8); | ||
| 774 | + stl_raw(l ++, 0x00200000); /* unsigned int size */ | ||
| 775 | + stl_raw(l ++, 0x00280000); /* unsigned int offset */ | ||
| 776 | + stl_raw(l ++, 0x3); /* unsigned int mask_flags */ | ||
| 777 | + w = (void *) l; | ||
| 778 | + | ||
| 779 | + stw_raw(w ++, OMAP_TAG_PARTITION); /* u16 tag */ | ||
| 780 | + stw_raw(w ++, 28); /* u16 len */ | ||
| 781 | + strcpy((void *) w, "rootfs"); /* char name[16] */ | ||
| 782 | + l = (void *) (w + 8); | ||
| 783 | + stl_raw(l ++, 0x0fb80000); /* unsigned int size */ | ||
| 784 | + stl_raw(l ++, 0x00480000); /* unsigned int offset */ | ||
| 785 | + stl_raw(l ++, 0x3); /* unsigned int mask_flags */ | ||
| 786 | + w = (void *) l; | ||
| 787 | + | ||
| 788 | + stw_raw(w ++, OMAP_TAG_BOOT_REASON); /* u16 tag */ | ||
| 789 | + stw_raw(w ++, 12); /* u16 len */ | ||
| 790 | +#if 0 | ||
| 791 | + strcpy((void *) w, "por"); /* char reason_str[12] */ | ||
| 792 | + strcpy((void *) w, "charger"); /* char reason_str[12] */ | ||
| 793 | + strcpy((void *) w, "32wd_to"); /* char reason_str[12] */ | ||
| 794 | + strcpy((void *) w, "sw_rst"); /* char reason_str[12] */ | ||
| 795 | + strcpy((void *) w, "mbus"); /* char reason_str[12] */ | ||
| 796 | + strcpy((void *) w, "unknown"); /* char reason_str[12] */ | ||
| 797 | + strcpy((void *) w, "swdg_to"); /* char reason_str[12] */ | ||
| 798 | + strcpy((void *) w, "sec_vio"); /* char reason_str[12] */ | ||
| 799 | + strcpy((void *) w, "pwr_key"); /* char reason_str[12] */ | ||
| 800 | + strcpy((void *) w, "rtc_alarm"); /* char reason_str[12] */ | ||
| 801 | +#else | ||
| 802 | + strcpy((void *) w, "pwr_key"); /* char reason_str[12] */ | ||
| 803 | +#endif | ||
| 804 | + w += 6; | ||
| 805 | + | ||
| 806 | +#if 0 /* N810 */ | ||
| 807 | + stw_raw(w ++, OMAP_TAG_VERSION_STR); /* u16 tag */ | ||
| 808 | + stw_raw(w ++, 24); /* u16 len */ | ||
| 809 | + strcpy((void *) w, "product"); /* char component[12] */ | ||
| 810 | + w += 6; | ||
| 811 | + strcpy((void *) w, "RX-44"); /* char version[12] */ | ||
| 812 | + w += 6; | ||
| 813 | + | ||
| 814 | + stw_raw(w ++, OMAP_TAG_VERSION_STR); /* u16 tag */ | ||
| 815 | + stw_raw(w ++, 24); /* u16 len */ | ||
| 816 | + strcpy((void *) w, "hw-build"); /* char component[12] */ | ||
| 817 | + w += 6; | ||
| 818 | + strcpy((void *) w, "QEMU"); /* char version[12] */ | ||
| 819 | + w += 6; | ||
| 820 | + | ||
| 821 | + stw_raw(w ++, OMAP_TAG_VERSION_STR); /* u16 tag */ | ||
| 822 | + stw_raw(w ++, 24); /* u16 len */ | ||
| 823 | + strcpy((void *) w, "nolo"); /* char component[12] */ | ||
| 824 | + w += 6; | ||
| 825 | + strcpy((void *) w, "1.1.10-qemu"); /* char version[12] */ | ||
| 826 | + w += 6; | ||
| 827 | +#else | ||
| 828 | + stw_raw(w ++, OMAP_TAG_VERSION_STR); /* u16 tag */ | ||
| 829 | + stw_raw(w ++, 24); /* u16 len */ | ||
| 830 | + strcpy((void *) w, "product"); /* char component[12] */ | ||
| 831 | + w += 6; | ||
| 832 | + strcpy((void *) w, "RX-34"); /* char version[12] */ | ||
| 833 | + w += 6; | ||
| 834 | + | ||
| 835 | + stw_raw(w ++, OMAP_TAG_VERSION_STR); /* u16 tag */ | ||
| 836 | + stw_raw(w ++, 24); /* u16 len */ | ||
| 837 | + strcpy((void *) w, "hw-build"); /* char component[12] */ | ||
| 838 | + w += 6; | ||
| 839 | + strcpy((void *) w, "QEMU"); /* char version[12] */ | ||
| 840 | + w += 6; | ||
| 841 | + | ||
| 842 | + stw_raw(w ++, OMAP_TAG_VERSION_STR); /* u16 tag */ | ||
| 843 | + stw_raw(w ++, 24); /* u16 len */ | ||
| 844 | + strcpy((void *) w, "nolo"); /* char component[12] */ | ||
| 845 | + w += 6; | ||
| 846 | + strcpy((void *) w, "1.1.6-qemu"); /* char version[12] */ | ||
| 847 | + w += 6; | ||
| 848 | +#endif | ||
| 849 | + | ||
| 850 | + stw_raw(w ++, OMAP_TAG_LCD); /* u16 tag */ | ||
| 851 | + stw_raw(w ++, 36); /* u16 len */ | ||
| 852 | + strcpy((void *) w, "QEMU LCD panel"); /* char panel_name[16] */ | ||
| 853 | + w += 8; | ||
| 854 | + strcpy((void *) w, "blizzard"); /* char ctrl_name[16] */ | ||
| 855 | + w += 8; | ||
| 856 | + stw_raw(w ++, 5); /* TODO s16 nreset_gpio */ | ||
| 857 | + stw_raw(w ++, 16); /* u8 data_lines */ | ||
| 858 | + | ||
| 859 | + return (void *) w - p; | ||
| 860 | +} | ||
| 861 | + | ||
| 862 | +static struct arm_boot_info n800_binfo = { | ||
| 863 | + .loader_start = OMAP2_Q2_BASE, | ||
| 864 | + /* Actually two chips of 0x4000000 bytes each */ | ||
| 865 | + .ram_size = 0x08000000, | ||
| 866 | + .board_id = 0x4f7, | ||
| 867 | + .atag_board = n800_atag_setup, | ||
| 868 | +}; | ||
| 869 | + | ||
| 870 | +static void n800_init(int ram_size, int vga_ram_size, | ||
| 871 | + const char *boot_device, DisplayState *ds, | ||
| 872 | + const char *kernel_filename, const char *kernel_cmdline, | ||
| 873 | + const char *initrd_filename, const char *cpu_model) | ||
| 874 | +{ | ||
| 875 | + struct n800_s *s = (struct n800_s *) qemu_mallocz(sizeof(*s)); | ||
| 876 | + int sdram_size = n800_binfo.ram_size; | ||
| 877 | + int onenandram_size = 0x00010000; | ||
| 878 | + | ||
| 879 | + if (ram_size < sdram_size + onenandram_size + OMAP242X_SRAM_SIZE) { | ||
| 880 | + fprintf(stderr, "This architecture uses %i bytes of memory\n", | ||
| 881 | + sdram_size + onenandram_size + OMAP242X_SRAM_SIZE); | ||
| 882 | + exit(1); | ||
| 883 | + } | ||
| 884 | + | ||
| 885 | + s->cpu = omap2420_mpu_init(sdram_size, NULL, cpu_model); | ||
| 886 | + | ||
| 887 | + n800_gpio_setup(s); | ||
| 888 | + n8x0_nand_setup(s); | ||
| 889 | + n800_i2c_setup(s); | ||
| 890 | + n800_tsc_setup(s); | ||
| 891 | + n800_spi_setup(s); | ||
| 892 | + n800_dss_setup(s, ds); | ||
| 893 | + n800_cbus_setup(s); | ||
| 894 | + | ||
| 895 | + /* Setup initial (reset) machine state */ | ||
| 896 | + | ||
| 897 | + /* Start at the OneNAND bootloader. */ | ||
| 898 | + s->cpu->env->regs[15] = 0; | ||
| 899 | + | ||
| 900 | + if (kernel_filename) { | ||
| 901 | + /* Or at the linux loader. */ | ||
| 902 | + n800_binfo.kernel_filename = kernel_filename; | ||
| 903 | + n800_binfo.kernel_cmdline = kernel_cmdline; | ||
| 904 | + n800_binfo.initrd_filename = initrd_filename; | ||
| 905 | + arm_load_kernel(s->cpu->env, &n800_binfo); | ||
| 906 | + | ||
| 907 | + qemu_register_reset(n800_boot_init, s); | ||
| 908 | + n800_boot_init(s); | ||
| 909 | + } | ||
| 910 | + | ||
| 911 | + dpy_resize(ds, 800, 480); | ||
| 912 | +} | ||
| 913 | + | ||
| 914 | +QEMUMachine n800_machine = { | ||
| 915 | + "n800", | ||
| 916 | + "Nokia N800 aka. RX-34 tablet (OMAP2420)", | ||
| 917 | + n800_init, | ||
| 918 | +}; |
hw/omap2.c
| @@ -3496,7 +3496,7 @@ struct omap_mpu_state_s *omap2420_mpu_init(unsigned long sdram_size, | @@ -3496,7 +3496,7 @@ struct omap_mpu_state_s *omap2420_mpu_init(unsigned long sdram_size, | ||
| 3496 | { | 3496 | { |
| 3497 | struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) | 3497 | struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) |
| 3498 | qemu_mallocz(sizeof(struct omap_mpu_state_s)); | 3498 | qemu_mallocz(sizeof(struct omap_mpu_state_s)); |
| 3499 | - ram_addr_t sram_base, q3_base; | 3499 | + ram_addr_t sram_base, q2_base; |
| 3500 | qemu_irq *cpu_irq; | 3500 | qemu_irq *cpu_irq; |
| 3501 | qemu_irq dma_irqs[4]; | 3501 | qemu_irq dma_irqs[4]; |
| 3502 | omap_clk gpio_clks[4]; | 3502 | omap_clk gpio_clks[4]; |
| @@ -3520,7 +3520,7 @@ struct omap_mpu_state_s *omap2420_mpu_init(unsigned long sdram_size, | @@ -3520,7 +3520,7 @@ struct omap_mpu_state_s *omap2420_mpu_init(unsigned long sdram_size, | ||
| 3520 | 3520 | ||
| 3521 | /* Memory-mapped stuff */ | 3521 | /* Memory-mapped stuff */ |
| 3522 | cpu_register_physical_memory(OMAP2_Q2_BASE, s->sdram_size, | 3522 | cpu_register_physical_memory(OMAP2_Q2_BASE, s->sdram_size, |
| 3523 | - (q3_base = qemu_ram_alloc(s->sdram_size)) | IO_MEM_RAM); | 3523 | + (q2_base = qemu_ram_alloc(s->sdram_size)) | IO_MEM_RAM); |
| 3524 | cpu_register_physical_memory(OMAP2_SRAM_BASE, s->sram_size, | 3524 | cpu_register_physical_memory(OMAP2_SRAM_BASE, s->sram_size, |
| 3525 | (sram_base = qemu_ram_alloc(s->sram_size)) | IO_MEM_RAM); | 3525 | (sram_base = qemu_ram_alloc(s->sram_size)) | IO_MEM_RAM); |
| 3526 | 3526 |
hw/onenand.c
0 → 100644
| 1 | +/* | ||
| 2 | + * OneNAND flash memories emulation. | ||
| 3 | + * | ||
| 4 | + * Copyright (C) 2008 Nokia Corporation | ||
| 5 | + * Written by Andrzej Zaborowski <andrew@openedhand.com> | ||
| 6 | + * | ||
| 7 | + * This program is free software; you can redistribute it and/or | ||
| 8 | + * modify it under the terms of the GNU General Public License as | ||
| 9 | + * published by the Free Software Foundation; either version 2 or | ||
| 10 | + * (at your option) version 3 of the License. | ||
| 11 | + * | ||
| 12 | + * This program is distributed in the hope that it will be useful, | ||
| 13 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | + * GNU General Public License for more details. | ||
| 16 | + * | ||
| 17 | + * You should have received a copy of the GNU General Public License | ||
| 18 | + * along with this program; if not, write to the Free Software | ||
| 19 | + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, | ||
| 20 | + * MA 02111-1307 USA | ||
| 21 | + */ | ||
| 22 | + | ||
| 23 | +#include "qemu-common.h" | ||
| 24 | +#include "flash.h" | ||
| 25 | +#include "irq.h" | ||
| 26 | +#include "sysemu.h" | ||
| 27 | +#include "block.h" | ||
| 28 | + | ||
| 29 | +/* 11 for 2kB-page OneNAND ("2nd generation") and 10 for 1kB-page chips */ | ||
| 30 | +#define PAGE_SHIFT 11 | ||
| 31 | + | ||
| 32 | +/* Fixed */ | ||
| 33 | +#define BLOCK_SHIFT (PAGE_SHIFT + 6) | ||
| 34 | + | ||
| 35 | +struct onenand_s { | ||
| 36 | + uint32_t id; | ||
| 37 | + int shift; | ||
| 38 | + target_phys_addr_t base; | ||
| 39 | + qemu_irq intr; | ||
| 40 | + qemu_irq rdy; | ||
| 41 | + BlockDriverState *bdrv; | ||
| 42 | + BlockDriverState *bdrv_cur; | ||
| 43 | + uint8_t *image; | ||
| 44 | + uint8_t *otp; | ||
| 45 | + uint8_t *current; | ||
| 46 | + ram_addr_t ram; | ||
| 47 | + uint8_t *boot[2]; | ||
| 48 | + uint8_t *data[2][2]; | ||
| 49 | + int iomemtype; | ||
| 50 | + int cycle; | ||
| 51 | + int otpmode; | ||
| 52 | + | ||
| 53 | + uint16_t addr[8]; | ||
| 54 | + uint16_t unladdr[8]; | ||
| 55 | + int bufaddr; | ||
| 56 | + int count; | ||
| 57 | + uint16_t command; | ||
| 58 | + uint16_t config[2]; | ||
| 59 | + uint16_t status; | ||
| 60 | + uint16_t intstatus; | ||
| 61 | + uint16_t wpstatus; | ||
| 62 | + | ||
| 63 | + struct ecc_state_s ecc; | ||
| 64 | + | ||
| 65 | + int density_mask; | ||
| 66 | + int secs; | ||
| 67 | + int secs_cur; | ||
| 68 | + int blocks; | ||
| 69 | + uint8_t *blockwp; | ||
| 70 | +}; | ||
| 71 | + | ||
| 72 | +enum { | ||
| 73 | + ONEN_BUF_BLOCK = 0, | ||
| 74 | + ONEN_BUF_BLOCK2 = 1, | ||
| 75 | + ONEN_BUF_DEST_BLOCK = 2, | ||
| 76 | + ONEN_BUF_DEST_PAGE = 3, | ||
| 77 | + ONEN_BUF_PAGE = 7, | ||
| 78 | +}; | ||
| 79 | + | ||
| 80 | +enum { | ||
| 81 | + ONEN_ERR_CMD = 1 << 10, | ||
| 82 | + ONEN_ERR_ERASE = 1 << 11, | ||
| 83 | + ONEN_ERR_PROG = 1 << 12, | ||
| 84 | + ONEN_ERR_LOAD = 1 << 13, | ||
| 85 | +}; | ||
| 86 | + | ||
| 87 | +enum { | ||
| 88 | + ONEN_INT_RESET = 1 << 4, | ||
| 89 | + ONEN_INT_ERASE = 1 << 5, | ||
| 90 | + ONEN_INT_PROG = 1 << 6, | ||
| 91 | + ONEN_INT_LOAD = 1 << 7, | ||
| 92 | + ONEN_INT = 1 << 15, | ||
| 93 | +}; | ||
| 94 | + | ||
| 95 | +enum { | ||
| 96 | + ONEN_LOCK_LOCKTIGHTEN = 1 << 0, | ||
| 97 | + ONEN_LOCK_LOCKED = 1 << 1, | ||
| 98 | + ONEN_LOCK_UNLOCKED = 1 << 2, | ||
| 99 | +}; | ||
| 100 | + | ||
| 101 | +void onenand_base_update(void *opaque, target_phys_addr_t new) | ||
| 102 | +{ | ||
| 103 | + struct onenand_s *s = (struct onenand_s *) opaque; | ||
| 104 | + | ||
| 105 | + s->base = new; | ||
| 106 | + | ||
| 107 | + /* XXX: We should use IO_MEM_ROMD but we broke it earlier... | ||
| 108 | + * Both 0x0000 ... 0x01ff and 0x8000 ... 0x800f can be used to | ||
| 109 | + * write boot commands. Also take note of the BWPS bit. */ | ||
| 110 | + cpu_register_physical_memory(s->base + (0x0000 << s->shift), | ||
| 111 | + 0x0200 << s->shift, s->iomemtype); | ||
| 112 | + cpu_register_physical_memory(s->base + (0x0200 << s->shift), | ||
| 113 | + 0xbe00 << s->shift, | ||
| 114 | + (s->ram +(0x0200 << s->shift)) | IO_MEM_RAM); | ||
| 115 | + if (s->iomemtype) | ||
| 116 | + cpu_register_physical_memory(s->base + (0xc000 << s->shift), | ||
| 117 | + 0x4000 << s->shift, s->iomemtype); | ||
| 118 | +} | ||
| 119 | + | ||
| 120 | +void onenand_base_unmap(void *opaque) | ||
| 121 | +{ | ||
| 122 | + struct onenand_s *s = (struct onenand_s *) opaque; | ||
| 123 | + | ||
| 124 | + cpu_register_physical_memory(s->base, | ||
| 125 | + 0x10000 << s->shift, IO_MEM_UNASSIGNED); | ||
| 126 | +} | ||
| 127 | + | ||
| 128 | +static void onenand_intr_update(struct onenand_s *s) | ||
| 129 | +{ | ||
| 130 | + qemu_set_irq(s->intr, ((s->intstatus >> 15) ^ (~s->config[0] >> 6)) & 1); | ||
| 131 | +} | ||
| 132 | + | ||
| 133 | +/* Hot reset (Reset OneNAND command) or warm reset (RP pin low) */ | ||
| 134 | +static void onenand_reset(struct onenand_s *s, int cold) | ||
| 135 | +{ | ||
| 136 | + memset(&s->addr, 0, sizeof(s->addr)); | ||
| 137 | + s->command = 0; | ||
| 138 | + s->count = 1; | ||
| 139 | + s->bufaddr = 0; | ||
| 140 | + s->config[0] = 0x40c0; | ||
| 141 | + s->config[1] = 0x0000; | ||
| 142 | + onenand_intr_update(s); | ||
| 143 | + qemu_irq_raise(s->rdy); | ||
| 144 | + s->status = 0x0000; | ||
| 145 | + s->intstatus = cold ? 0x8080 : 0x8010; | ||
| 146 | + s->unladdr[0] = 0; | ||
| 147 | + s->unladdr[1] = 0; | ||
| 148 | + s->wpstatus = 0x0002; | ||
| 149 | + s->cycle = 0; | ||
| 150 | + s->otpmode = 0; | ||
| 151 | + s->bdrv_cur = s->bdrv; | ||
| 152 | + s->current = s->image; | ||
| 153 | + s->secs_cur = s->secs; | ||
| 154 | + | ||
| 155 | + if (cold) { | ||
| 156 | + /* Lock the whole flash */ | ||
| 157 | + memset(s->blockwp, ONEN_LOCK_LOCKED, s->blocks); | ||
| 158 | + | ||
| 159 | + if (s->bdrv && bdrv_read(s->bdrv, 0, s->boot[0], 8) < 0) | ||
| 160 | + cpu_abort(cpu_single_env, "%s: Loading the BootRAM failed.\n", | ||
| 161 | + __FUNCTION__); | ||
| 162 | + } | ||
| 163 | +} | ||
| 164 | + | ||
| 165 | +static inline int onenand_load_main(struct onenand_s *s, int sec, int secn, | ||
| 166 | + void *dest) | ||
| 167 | +{ | ||
| 168 | + if (s->bdrv_cur) | ||
| 169 | + return bdrv_read(s->bdrv_cur, sec, dest, secn) < 0; | ||
| 170 | + else if (sec + secn > s->secs_cur) | ||
| 171 | + return 1; | ||
| 172 | + | ||
| 173 | + memcpy(dest, s->current + (sec << 9), secn << 9); | ||
| 174 | + | ||
| 175 | + return 0; | ||
| 176 | +} | ||
| 177 | + | ||
| 178 | +static inline int onenand_prog_main(struct onenand_s *s, int sec, int secn, | ||
| 179 | + void *src) | ||
| 180 | +{ | ||
| 181 | + if (s->bdrv_cur) | ||
| 182 | + return bdrv_write(s->bdrv_cur, sec, src, secn) < 0; | ||
| 183 | + else if (sec + secn > s->secs_cur) | ||
| 184 | + return 1; | ||
| 185 | + | ||
| 186 | + memcpy(s->current + (sec << 9), src, secn << 9); | ||
| 187 | + | ||
| 188 | + return 0; | ||
| 189 | +} | ||
| 190 | + | ||
| 191 | +static inline int onenand_load_spare(struct onenand_s *s, int sec, int secn, | ||
| 192 | + void *dest) | ||
| 193 | +{ | ||
| 194 | + uint8_t buf[512]; | ||
| 195 | + | ||
| 196 | + if (s->bdrv_cur) { | ||
| 197 | + if (bdrv_read(s->bdrv_cur, s->secs_cur + (sec >> 5), buf, 1) < 0) | ||
| 198 | + return 1; | ||
| 199 | + memcpy(dest, buf + ((sec & 31) << 4), secn << 4); | ||
| 200 | + } else if (sec + secn > s->secs_cur) | ||
| 201 | + return 1; | ||
| 202 | + else | ||
| 203 | + memcpy(dest, s->current + (s->secs_cur << 9) + (sec << 4), secn << 4); | ||
| 204 | + | ||
| 205 | + return 0; | ||
| 206 | +} | ||
| 207 | + | ||
| 208 | +static inline int onenand_prog_spare(struct onenand_s *s, int sec, int secn, | ||
| 209 | + void *src) | ||
| 210 | +{ | ||
| 211 | + uint8_t buf[512]; | ||
| 212 | + | ||
| 213 | + if (s->bdrv_cur) { | ||
| 214 | + if (bdrv_read(s->bdrv_cur, s->secs_cur + (sec >> 5), buf, 1) < 0) | ||
| 215 | + return 1; | ||
| 216 | + memcpy(buf + ((sec & 31) << 4), src, secn << 4); | ||
| 217 | + return bdrv_write(s->bdrv_cur, s->secs_cur + (sec >> 5), buf, 1) < 0; | ||
| 218 | + } else if (sec + secn > s->secs_cur) | ||
| 219 | + return 1; | ||
| 220 | + | ||
| 221 | + memcpy(s->current + (s->secs_cur << 9) + (sec << 4), src, secn << 4); | ||
| 222 | + | ||
| 223 | + return 0; | ||
| 224 | +} | ||
| 225 | + | ||
| 226 | +static inline int onenand_erase(struct onenand_s *s, int sec, int num) | ||
| 227 | +{ | ||
| 228 | + /* TODO: optimise */ | ||
| 229 | + uint8_t buf[512]; | ||
| 230 | + | ||
| 231 | + memset(buf, 0xff, sizeof(buf)); | ||
| 232 | + for (; num > 0; num --, sec ++) { | ||
| 233 | + if (onenand_prog_main(s, sec, 1, buf)) | ||
| 234 | + return 1; | ||
| 235 | + if (onenand_prog_spare(s, sec, 1, buf)) | ||
| 236 | + return 1; | ||
| 237 | + } | ||
| 238 | + | ||
| 239 | + return 0; | ||
| 240 | +} | ||
| 241 | + | ||
| 242 | +static void onenand_command(struct onenand_s *s, int cmd) | ||
| 243 | +{ | ||
| 244 | + int b; | ||
| 245 | + int sec; | ||
| 246 | + void *buf; | ||
| 247 | +#define SETADDR(block, page) \ | ||
| 248 | + sec = (s->addr[page] & 3) + \ | ||
| 249 | + ((((s->addr[page] >> 2) & 0x3f) + \ | ||
| 250 | + (((s->addr[block] & 0xfff) | \ | ||
| 251 | + (s->addr[block] >> 15 ? \ | ||
| 252 | + s->density_mask : 0)) << 6)) << (PAGE_SHIFT - 9)); | ||
| 253 | +#define SETBUF_M() \ | ||
| 254 | + buf = (s->bufaddr & 8) ? \ | ||
| 255 | + s->data[(s->bufaddr >> 2) & 1][0] : s->boot[0]; \ | ||
| 256 | + buf += (s->bufaddr & 3) << 9; | ||
| 257 | +#define SETBUF_S() \ | ||
| 258 | + buf = (s->bufaddr & 8) ? \ | ||
| 259 | + s->data[(s->bufaddr >> 2) & 1][1] : s->boot[1]; \ | ||
| 260 | + buf += (s->bufaddr & 3) << 4; | ||
| 261 | + | ||
| 262 | + switch (cmd) { | ||
| 263 | + case 0x00: /* Load single/multiple sector data unit into buffer */ | ||
| 264 | + SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE) | ||
| 265 | + | ||
| 266 | + SETBUF_M() | ||
| 267 | + if (onenand_load_main(s, sec, s->count, buf)) | ||
| 268 | + s->status |= ONEN_ERR_CMD | ONEN_ERR_LOAD; | ||
| 269 | + | ||
| 270 | +#if 0 | ||
| 271 | + SETBUF_S() | ||
| 272 | + if (onenand_load_spare(s, sec, s->count, buf)) | ||
| 273 | + s->status |= ONEN_ERR_CMD | ONEN_ERR_LOAD; | ||
| 274 | +#endif | ||
| 275 | + | ||
| 276 | + /* TODO: if (s->bufaddr & 3) + s->count was > 4 (2k-pages) | ||
| 277 | + * or if (s->bufaddr & 1) + s->count was > 2 (1k-pages) | ||
| 278 | + * then we need two split the read/write into two chunks. | ||
| 279 | + */ | ||
| 280 | + s->intstatus |= ONEN_INT | ONEN_INT_LOAD; | ||
| 281 | + break; | ||
| 282 | + case 0x13: /* Load single/multiple spare sector into buffer */ | ||
| 283 | + SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE) | ||
| 284 | + | ||
| 285 | + SETBUF_S() | ||
| 286 | + if (onenand_load_spare(s, sec, s->count, buf)) | ||
| 287 | + s->status |= ONEN_ERR_CMD | ONEN_ERR_LOAD; | ||
| 288 | + | ||
| 289 | + /* TODO: if (s->bufaddr & 3) + s->count was > 4 (2k-pages) | ||
| 290 | + * or if (s->bufaddr & 1) + s->count was > 2 (1k-pages) | ||
| 291 | + * then we need two split the read/write into two chunks. | ||
| 292 | + */ | ||
| 293 | + s->intstatus |= ONEN_INT | ONEN_INT_LOAD; | ||
| 294 | + break; | ||
| 295 | + case 0x80: /* Program single/multiple sector data unit from buffer */ | ||
| 296 | + SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE) | ||
| 297 | + | ||
| 298 | + SETBUF_M() | ||
| 299 | + if (onenand_prog_main(s, sec, s->count, buf)) | ||
| 300 | + s->status |= ONEN_ERR_CMD | ONEN_ERR_PROG; | ||
| 301 | + | ||
| 302 | +#if 0 | ||
| 303 | + SETBUF_S() | ||
| 304 | + if (onenand_prog_spare(s, sec, s->count, buf)) | ||
| 305 | + s->status |= ONEN_ERR_CMD | ONEN_ERR_PROG; | ||
| 306 | +#endif | ||
| 307 | + | ||
| 308 | + /* TODO: if (s->bufaddr & 3) + s->count was > 4 (2k-pages) | ||
| 309 | + * or if (s->bufaddr & 1) + s->count was > 2 (1k-pages) | ||
| 310 | + * then we need two split the read/write into two chunks. | ||
| 311 | + */ | ||
| 312 | + s->intstatus |= ONEN_INT | ONEN_INT_PROG; | ||
| 313 | + break; | ||
| 314 | + case 0x1a: /* Program single/multiple spare area sector from buffer */ | ||
| 315 | + SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE) | ||
| 316 | + | ||
| 317 | + SETBUF_S() | ||
| 318 | + if (onenand_prog_spare(s, sec, s->count, buf)) | ||
| 319 | + s->status |= ONEN_ERR_CMD | ONEN_ERR_PROG; | ||
| 320 | + | ||
| 321 | + /* TODO: if (s->bufaddr & 3) + s->count was > 4 (2k-pages) | ||
| 322 | + * or if (s->bufaddr & 1) + s->count was > 2 (1k-pages) | ||
| 323 | + * then we need two split the read/write into two chunks. | ||
| 324 | + */ | ||
| 325 | + s->intstatus |= ONEN_INT | ONEN_INT_PROG; | ||
| 326 | + break; | ||
| 327 | + case 0x1b: /* Copy-back program */ | ||
| 328 | + SETBUF_S() | ||
| 329 | + | ||
| 330 | + SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE) | ||
| 331 | + if (onenand_load_main(s, sec, s->count, buf)) | ||
| 332 | + s->status |= ONEN_ERR_CMD | ONEN_ERR_PROG; | ||
| 333 | + | ||
| 334 | + SETADDR(ONEN_BUF_DEST_BLOCK, ONEN_BUF_DEST_PAGE) | ||
| 335 | + if (onenand_prog_main(s, sec, s->count, buf)) | ||
| 336 | + s->status |= ONEN_ERR_CMD | ONEN_ERR_PROG; | ||
| 337 | + | ||
| 338 | + /* TODO: spare areas */ | ||
| 339 | + | ||
| 340 | + s->intstatus |= ONEN_INT | ONEN_INT_PROG; | ||
| 341 | + break; | ||
| 342 | + | ||
| 343 | + case 0x23: /* Unlock NAND array block(s) */ | ||
| 344 | + s->intstatus |= ONEN_INT; | ||
| 345 | + | ||
| 346 | + /* XXX the previous (?) area should be locked automatically */ | ||
| 347 | + for (b = s->unladdr[0]; b <= s->unladdr[1]; b ++) { | ||
| 348 | + if (b >= s->blocks) { | ||
| 349 | + s->status |= ONEN_ERR_CMD; | ||
| 350 | + break; | ||
| 351 | + } | ||
| 352 | + if (s->blockwp[b] == ONEN_LOCK_LOCKTIGHTEN) | ||
| 353 | + break; | ||
| 354 | + | ||
| 355 | + s->wpstatus = s->blockwp[b] = ONEN_LOCK_UNLOCKED; | ||
| 356 | + } | ||
| 357 | + break; | ||
| 358 | + case 0x2a: /* Lock NAND array block(s) */ | ||
| 359 | + s->intstatus |= ONEN_INT; | ||
| 360 | + | ||
| 361 | + for (b = s->unladdr[0]; b <= s->unladdr[1]; b ++) { | ||
| 362 | + if (b >= s->blocks) { | ||
| 363 | + s->status |= ONEN_ERR_CMD; | ||
| 364 | + break; | ||
| 365 | + } | ||
| 366 | + if (s->blockwp[b] == ONEN_LOCK_LOCKTIGHTEN) | ||
| 367 | + break; | ||
| 368 | + | ||
| 369 | + s->wpstatus = s->blockwp[b] = ONEN_LOCK_LOCKED; | ||
| 370 | + } | ||
| 371 | + break; | ||
| 372 | + case 0x2c: /* Lock-tight NAND array block(s) */ | ||
| 373 | + s->intstatus |= ONEN_INT; | ||
| 374 | + | ||
| 375 | + for (b = s->unladdr[0]; b <= s->unladdr[1]; b ++) { | ||
| 376 | + if (b >= s->blocks) { | ||
| 377 | + s->status |= ONEN_ERR_CMD; | ||
| 378 | + break; | ||
| 379 | + } | ||
| 380 | + if (s->blockwp[b] == ONEN_LOCK_UNLOCKED) | ||
| 381 | + continue; | ||
| 382 | + | ||
| 383 | + s->wpstatus = s->blockwp[b] = ONEN_LOCK_LOCKTIGHTEN; | ||
| 384 | + } | ||
| 385 | + break; | ||
| 386 | + | ||
| 387 | + case 0x71: /* Erase-Verify-Read */ | ||
| 388 | + s->intstatus |= ONEN_INT; | ||
| 389 | + break; | ||
| 390 | + case 0x95: /* Multi-block erase */ | ||
| 391 | + qemu_irq_pulse(s->intr); | ||
| 392 | + /* Fall through. */ | ||
| 393 | + case 0x94: /* Block erase */ | ||
| 394 | + sec = ((s->addr[ONEN_BUF_BLOCK] & 0xfff) | | ||
| 395 | + (s->addr[ONEN_BUF_BLOCK] >> 15 ? s->density_mask : 0)) | ||
| 396 | + << (BLOCK_SHIFT - 9); | ||
| 397 | + if (onenand_erase(s, sec, 1 << (BLOCK_SHIFT - 9))) | ||
| 398 | + s->status |= ONEN_ERR_CMD | ONEN_ERR_ERASE; | ||
| 399 | + | ||
| 400 | + s->intstatus |= ONEN_INT | ONEN_INT_ERASE; | ||
| 401 | + break; | ||
| 402 | + case 0xb0: /* Erase suspend */ | ||
| 403 | + break; | ||
| 404 | + case 0x30: /* Erase resume */ | ||
| 405 | + s->intstatus |= ONEN_INT | ONEN_INT_ERASE; | ||
| 406 | + break; | ||
| 407 | + | ||
| 408 | + case 0xf0: /* Reset NAND Flash core */ | ||
| 409 | + onenand_reset(s, 0); | ||
| 410 | + break; | ||
| 411 | + case 0xf3: /* Reset OneNAND */ | ||
| 412 | + onenand_reset(s, 0); | ||
| 413 | + break; | ||
| 414 | + | ||
| 415 | + case 0x65: /* OTP Access */ | ||
| 416 | + s->intstatus |= ONEN_INT; | ||
| 417 | + s->bdrv_cur = 0; | ||
| 418 | + s->current = s->otp; | ||
| 419 | + s->secs_cur = 1 << (BLOCK_SHIFT - 9); | ||
| 420 | + s->addr[ONEN_BUF_BLOCK] = 0; | ||
| 421 | + s->otpmode = 1; | ||
| 422 | + break; | ||
| 423 | + | ||
| 424 | + default: | ||
| 425 | + s->status |= ONEN_ERR_CMD; | ||
| 426 | + s->intstatus |= ONEN_INT; | ||
| 427 | + fprintf(stderr, "%s: unknown OneNAND command %x\n", | ||
| 428 | + __FUNCTION__, cmd); | ||
| 429 | + } | ||
| 430 | + | ||
| 431 | + onenand_intr_update(s); | ||
| 432 | +} | ||
| 433 | + | ||
| 434 | +static uint32_t onenand_read(void *opaque, target_phys_addr_t addr) | ||
| 435 | +{ | ||
| 436 | + struct onenand_s *s = (struct onenand_s *) opaque; | ||
| 437 | + int offset = (addr - s->base) >> s->shift; | ||
| 438 | + | ||
| 439 | + switch (offset) { | ||
| 440 | + case 0x0000 ... 0xc000: | ||
| 441 | + return lduw_le_p(s->boot[0] + (addr - s->base)); | ||
| 442 | + | ||
| 443 | + case 0xf000: /* Manufacturer ID */ | ||
| 444 | + return (s->id >> 16) & 0xff; | ||
| 445 | + case 0xf001: /* Device ID */ | ||
| 446 | + return (s->id >> 8) & 0xff; | ||
| 447 | + /* TODO: get the following values from a real chip! */ | ||
| 448 | + case 0xf002: /* Version ID */ | ||
| 449 | + return (s->id >> 0) & 0xff; | ||
| 450 | + case 0xf003: /* Data Buffer size */ | ||
| 451 | + return 1 << PAGE_SHIFT; | ||
| 452 | + case 0xf004: /* Boot Buffer size */ | ||
| 453 | + return 0x200; | ||
| 454 | + case 0xf005: /* Amount of buffers */ | ||
| 455 | + return 1 | (2 << 8); | ||
| 456 | + case 0xf006: /* Technology */ | ||
| 457 | + return 0; | ||
| 458 | + | ||
| 459 | + case 0xf100 ... 0xf107: /* Start addresses */ | ||
| 460 | + return s->addr[offset - 0xf100]; | ||
| 461 | + | ||
| 462 | + case 0xf200: /* Start buffer */ | ||
| 463 | + return (s->bufaddr << 8) | ((s->count - 1) & (1 << (PAGE_SHIFT - 10))); | ||
| 464 | + | ||
| 465 | + case 0xf220: /* Command */ | ||
| 466 | + return s->command; | ||
| 467 | + case 0xf221: /* System Configuration 1 */ | ||
| 468 | + return s->config[0] & 0xffe0; | ||
| 469 | + case 0xf222: /* System Configuration 2 */ | ||
| 470 | + return s->config[1]; | ||
| 471 | + | ||
| 472 | + case 0xf240: /* Controller Status */ | ||
| 473 | + return s->status; | ||
| 474 | + case 0xf241: /* Interrupt */ | ||
| 475 | + return s->intstatus; | ||
| 476 | + case 0xf24c: /* Unlock Start Block Address */ | ||
| 477 | + return s->unladdr[0]; | ||
| 478 | + case 0xf24d: /* Unlock End Block Address */ | ||
| 479 | + return s->unladdr[1]; | ||
| 480 | + case 0xf24e: /* Write Protection Status */ | ||
| 481 | + return s->wpstatus; | ||
| 482 | + | ||
| 483 | + case 0xff00: /* ECC Status */ | ||
| 484 | + return 0x00; | ||
| 485 | + case 0xff01: /* ECC Result of main area data */ | ||
| 486 | + case 0xff02: /* ECC Result of spare area data */ | ||
| 487 | + case 0xff03: /* ECC Result of main area data */ | ||
| 488 | + case 0xff04: /* ECC Result of spare area data */ | ||
| 489 | + cpu_abort(cpu_single_env, "%s: imeplement ECC\n", __FUNCTION__); | ||
| 490 | + return 0x0000; | ||
| 491 | + } | ||
| 492 | + | ||
| 493 | + fprintf(stderr, "%s: unknown OneNAND register %x\n", | ||
| 494 | + __FUNCTION__, offset); | ||
| 495 | + return 0; | ||
| 496 | +} | ||
| 497 | + | ||
| 498 | +static void onenand_write(void *opaque, target_phys_addr_t addr, | ||
| 499 | + uint32_t value) | ||
| 500 | +{ | ||
| 501 | + struct onenand_s *s = (struct onenand_s *) opaque; | ||
| 502 | + int offset = (addr - s->base) >> s->shift; | ||
| 503 | + int sec; | ||
| 504 | + | ||
| 505 | + switch (offset) { | ||
| 506 | + case 0x0000 ... 0x01ff: | ||
| 507 | + case 0x8000 ... 0x800f: | ||
| 508 | + if (s->cycle) { | ||
| 509 | + s->cycle = 0; | ||
| 510 | + | ||
| 511 | + if (value == 0x0000) { | ||
| 512 | + SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE) | ||
| 513 | + onenand_load_main(s, sec, | ||
| 514 | + 1 << (PAGE_SHIFT - 9), s->data[0][0]); | ||
| 515 | + s->addr[ONEN_BUF_PAGE] += 4; | ||
| 516 | + s->addr[ONEN_BUF_PAGE] &= 0xff; | ||
| 517 | + } | ||
| 518 | + break; | ||
| 519 | + } | ||
| 520 | + | ||
| 521 | + switch (value) { | ||
| 522 | + case 0x00f0: /* Reset OneNAND */ | ||
| 523 | + onenand_reset(s, 0); | ||
| 524 | + break; | ||
| 525 | + | ||
| 526 | + case 0x00e0: /* Load Data into Buffer */ | ||
| 527 | + s->cycle = 1; | ||
| 528 | + break; | ||
| 529 | + | ||
| 530 | + case 0x0090: /* Read Identification Data */ | ||
| 531 | + memset(s->boot[0], 0, 3 << s->shift); | ||
| 532 | + s->boot[0][0 << s->shift] = (s->id >> 16) & 0xff; | ||
| 533 | + s->boot[0][1 << s->shift] = (s->id >> 8) & 0xff; | ||
| 534 | + s->boot[0][2 << s->shift] = s->wpstatus & 0xff; | ||
| 535 | + break; | ||
| 536 | + | ||
| 537 | + default: | ||
| 538 | + fprintf(stderr, "%s: unknown OneNAND boot command %x\n", | ||
| 539 | + __FUNCTION__, value); | ||
| 540 | + } | ||
| 541 | + break; | ||
| 542 | + | ||
| 543 | + case 0xf100 ... 0xf107: /* Start addresses */ | ||
| 544 | + s->addr[offset - 0xf100] = value; | ||
| 545 | + break; | ||
| 546 | + | ||
| 547 | + case 0xf200: /* Start buffer */ | ||
| 548 | + s->bufaddr = (value >> 8) & 0xf; | ||
| 549 | + if (PAGE_SHIFT == 11) | ||
| 550 | + s->count = (value & 3) ?: 4; | ||
| 551 | + else if (PAGE_SHIFT == 10) | ||
| 552 | + s->count = (value & 1) ?: 2; | ||
| 553 | + break; | ||
| 554 | + | ||
| 555 | + case 0xf220: /* Command */ | ||
| 556 | + if (s->intstatus & (1 << 15)) | ||
| 557 | + break; | ||
| 558 | + s->command = value; | ||
| 559 | + onenand_command(s, s->command); | ||
| 560 | + break; | ||
| 561 | + case 0xf221: /* System Configuration 1 */ | ||
| 562 | + s->config[0] = value; | ||
| 563 | + onenand_intr_update(s); | ||
| 564 | + qemu_set_irq(s->rdy, (s->config[0] >> 7) & 1); | ||
| 565 | + break; | ||
| 566 | + case 0xf222: /* System Configuration 2 */ | ||
| 567 | + s->config[1] = value; | ||
| 568 | + break; | ||
| 569 | + | ||
| 570 | + case 0xf241: /* Interrupt */ | ||
| 571 | + s->intstatus &= value; | ||
| 572 | + if ((1 << 15) & ~s->intstatus) | ||
| 573 | + s->status &= ~(ONEN_ERR_CMD | ONEN_ERR_ERASE | | ||
| 574 | + ONEN_ERR_PROG | ONEN_ERR_LOAD); | ||
| 575 | + onenand_intr_update(s); | ||
| 576 | + break; | ||
| 577 | + case 0xf24c: /* Unlock Start Block Address */ | ||
| 578 | + s->unladdr[0] = value & (s->blocks - 1); | ||
| 579 | + /* For some reason we have to set the end address to by default | ||
| 580 | + * be same as start because the software forgets to write anything | ||
| 581 | + * in there. */ | ||
| 582 | + s->unladdr[1] = value & (s->blocks - 1); | ||
| 583 | + break; | ||
| 584 | + case 0xf24d: /* Unlock End Block Address */ | ||
| 585 | + s->unladdr[1] = value & (s->blocks - 1); | ||
| 586 | + break; | ||
| 587 | + | ||
| 588 | + default: | ||
| 589 | + fprintf(stderr, "%s: unknown OneNAND register %x\n", | ||
| 590 | + __FUNCTION__, offset); | ||
| 591 | + } | ||
| 592 | +} | ||
| 593 | + | ||
| 594 | +static CPUReadMemoryFunc *onenand_readfn[] = { | ||
| 595 | + onenand_read, /* TODO */ | ||
| 596 | + onenand_read, | ||
| 597 | + onenand_read, | ||
| 598 | +}; | ||
| 599 | + | ||
| 600 | +static CPUWriteMemoryFunc *onenand_writefn[] = { | ||
| 601 | + onenand_write, /* TODO */ | ||
| 602 | + onenand_write, | ||
| 603 | + onenand_write, | ||
| 604 | +}; | ||
| 605 | + | ||
| 606 | +void *onenand_init(uint32_t id, int regshift, qemu_irq irq) | ||
| 607 | +{ | ||
| 608 | + struct onenand_s *s = (struct onenand_s *) qemu_mallocz(sizeof(*s)); | ||
| 609 | + int bdrv_index = drive_get_index(IF_MTD, 0, 0); | ||
| 610 | + uint32_t size = 1 << (24 + ((id >> 12) & 7)); | ||
| 611 | + void *ram; | ||
| 612 | + | ||
| 613 | + s->shift = regshift; | ||
| 614 | + s->intr = irq; | ||
| 615 | + s->rdy = 0; | ||
| 616 | + s->id = id; | ||
| 617 | + s->blocks = size >> BLOCK_SHIFT; | ||
| 618 | + s->secs = size >> 9; | ||
| 619 | + s->blockwp = qemu_malloc(s->blocks); | ||
| 620 | + s->density_mask = (id & (1 << 11)) ? (1 << (6 + ((id >> 12) & 7))) : 0; | ||
| 621 | + s->iomemtype = cpu_register_io_memory(0, onenand_readfn, | ||
| 622 | + onenand_writefn, s); | ||
| 623 | + if (bdrv_index == -1) | ||
| 624 | + s->image = memset(qemu_malloc(size + (size >> 5)), | ||
| 625 | + 0xff, size + (size >> 5)); | ||
| 626 | + else | ||
| 627 | + s->bdrv = drives_table[bdrv_index].bdrv; | ||
| 628 | + s->otp = memset(qemu_malloc((64 + 2) << PAGE_SHIFT), | ||
| 629 | + 0xff, (64 + 2) << PAGE_SHIFT); | ||
| 630 | + s->ram = qemu_ram_alloc(0xc000 << s->shift); | ||
| 631 | + ram = phys_ram_base + s->ram; | ||
| 632 | + s->boot[0] = ram + (0x0000 << s->shift); | ||
| 633 | + s->boot[1] = ram + (0x8000 << s->shift); | ||
| 634 | + s->data[0][0] = ram + ((0x0200 + (0 << (PAGE_SHIFT - 1))) << s->shift); | ||
| 635 | + s->data[0][1] = ram + ((0x8010 + (0 << (PAGE_SHIFT - 6))) << s->shift); | ||
| 636 | + s->data[1][0] = ram + ((0x0200 + (1 << (PAGE_SHIFT - 1))) << s->shift); | ||
| 637 | + s->data[1][1] = ram + ((0x8010 + (1 << (PAGE_SHIFT - 6))) << s->shift); | ||
| 638 | + | ||
| 639 | + onenand_reset(s, 1); | ||
| 640 | + | ||
| 641 | + return s; | ||
| 642 | +} |
hw/tmp105.c
0 → 100644
| 1 | +/* | ||
| 2 | + * Texas Instruments TMP105 temperature sensor. | ||
| 3 | + * | ||
| 4 | + * Copyright (C) 2008 Nokia Corporation | ||
| 5 | + * Written by Andrzej Zaborowski <andrew@openedhand.com> | ||
| 6 | + * | ||
| 7 | + * This program is free software; you can redistribute it and/or | ||
| 8 | + * modify it under the terms of the GNU General Public License as | ||
| 9 | + * published by the Free Software Foundation; either version 2 or | ||
| 10 | + * (at your option) version 3 of the License. | ||
| 11 | + * | ||
| 12 | + * This program is distributed in the hope that it will be useful, | ||
| 13 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | + * GNU General Public License for more details. | ||
| 16 | + * | ||
| 17 | + * You should have received a copy of the GNU General Public License | ||
| 18 | + * along with this program; if not, write to the Free Software | ||
| 19 | + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, | ||
| 20 | + * MA 02111-1307 USA | ||
| 21 | + */ | ||
| 22 | + | ||
| 23 | +#include "hw.h" | ||
| 24 | +#include "i2c.h" | ||
| 25 | + | ||
| 26 | +struct tmp105_s { | ||
| 27 | + i2c_slave i2c; | ||
| 28 | + int len; | ||
| 29 | + uint8_t buf[2]; | ||
| 30 | + qemu_irq pin; | ||
| 31 | + | ||
| 32 | + uint8_t pointer; | ||
| 33 | + uint8_t config; | ||
| 34 | + int16_t temperature; | ||
| 35 | + int16_t limit[2]; | ||
| 36 | + int faults; | ||
| 37 | + int alarm; | ||
| 38 | +}; | ||
| 39 | + | ||
| 40 | +static void tmp105_interrupt_update(struct tmp105_s *s) | ||
| 41 | +{ | ||
| 42 | + qemu_set_irq(s->pin, s->alarm ^ ((~s->config >> 2) & 1)); /* POL */ | ||
| 43 | +} | ||
| 44 | + | ||
| 45 | +static void tmp105_alarm_update(struct tmp105_s *s) | ||
| 46 | +{ | ||
| 47 | + if ((s->config >> 0) & 1) { /* SD */ | ||
| 48 | + if ((s->config >> 7) & 1) /* OS */ | ||
| 49 | + s->config &= ~(1 << 7); /* OS */ | ||
| 50 | + else | ||
| 51 | + return; | ||
| 52 | + } | ||
| 53 | + | ||
| 54 | + if ((s->config >> 1) & 1) { /* TM */ | ||
| 55 | + if (s->temperature >= s->limit[1]) | ||
| 56 | + s->alarm = 1; | ||
| 57 | + else if (s->temperature < s->limit[0]) | ||
| 58 | + s->alarm = 1; | ||
| 59 | + } else { | ||
| 60 | + if (s->temperature >= s->limit[1]) | ||
| 61 | + s->alarm = 1; | ||
| 62 | + else if (s->temperature < s->limit[0]) | ||
| 63 | + s->alarm = 0; | ||
| 64 | + } | ||
| 65 | + | ||
| 66 | + tmp105_interrupt_update(s); | ||
| 67 | +} | ||
| 68 | + | ||
| 69 | +/* Units are 0.001 centigrades relative to 0 C. */ | ||
| 70 | +void tmp105_set(i2c_slave *i2c, int temp) | ||
| 71 | +{ | ||
| 72 | + struct tmp105_s *s = (struct tmp105_s *) i2c; | ||
| 73 | + | ||
| 74 | + if (temp >= 128000 || temp < -128000) { | ||
| 75 | + fprintf(stderr, "%s: values is out of range (%i.%03i C)\n", | ||
| 76 | + __FUNCTION__, temp / 1000, temp % 1000); | ||
| 77 | + exit(-1); | ||
| 78 | + } | ||
| 79 | + | ||
| 80 | + s->temperature = ((int16_t) (temp * 0x800 / 128000)) << 4; | ||
| 81 | + | ||
| 82 | + tmp105_alarm_update(s); | ||
| 83 | +} | ||
| 84 | + | ||
| 85 | +static const int tmp105_faultq[4] = { 1, 2, 4, 6 }; | ||
| 86 | + | ||
| 87 | +static void tmp105_read(struct tmp105_s *s) | ||
| 88 | +{ | ||
| 89 | + s->len = 0; | ||
| 90 | + | ||
| 91 | + if ((s->config >> 1) & 1) { /* TM */ | ||
| 92 | + s->alarm = 0; | ||
| 93 | + tmp105_interrupt_update(s); | ||
| 94 | + } | ||
| 95 | + | ||
| 96 | + switch (s->pointer & 3) { | ||
| 97 | + case 0: /* Temperature */ | ||
| 98 | + s->buf[s->len ++] = (((uint16_t) s->temperature) >> 8); | ||
| 99 | + s->buf[s->len ++] = (((uint16_t) s->temperature) >> 0) & | ||
| 100 | + (0xf0 << ((~s->config >> 5) & 3)); /* R */ | ||
| 101 | + break; | ||
| 102 | + | ||
| 103 | + case 1: /* Configuration */ | ||
| 104 | + s->buf[s->len ++] = s->config; | ||
| 105 | + break; | ||
| 106 | + | ||
| 107 | + case 2: /* T_LOW */ | ||
| 108 | + s->buf[s->len ++] = ((uint16_t) s->limit[0]) >> 8; | ||
| 109 | + s->buf[s->len ++] = ((uint16_t) s->limit[0]) >> 0; | ||
| 110 | + break; | ||
| 111 | + | ||
| 112 | + case 3: /* T_HIGH */ | ||
| 113 | + s->buf[s->len ++] = ((uint16_t) s->limit[1]) >> 8; | ||
| 114 | + s->buf[s->len ++] = ((uint16_t) s->limit[1]) >> 0; | ||
| 115 | + break; | ||
| 116 | + } | ||
| 117 | +} | ||
| 118 | + | ||
| 119 | +static void tmp105_write(struct tmp105_s *s) | ||
| 120 | +{ | ||
| 121 | + switch (s->pointer & 3) { | ||
| 122 | + case 0: /* Temperature */ | ||
| 123 | + break; | ||
| 124 | + | ||
| 125 | + case 1: /* Configuration */ | ||
| 126 | + if (s->buf[0] & ~s->config & (1 << 0)) /* SD */ | ||
| 127 | + printf("%s: TMP105 shutdown\n", __FUNCTION__); | ||
| 128 | + s->config = s->buf[0]; | ||
| 129 | + s->faults = tmp105_faultq[(s->config >> 3) & 3]; /* F */ | ||
| 130 | + tmp105_alarm_update(s); | ||
| 131 | + break; | ||
| 132 | + | ||
| 133 | + case 2: /* T_LOW */ | ||
| 134 | + case 3: /* T_HIGH */ | ||
| 135 | + if (s->len >= 3) | ||
| 136 | + s->limit[s->pointer & 1] = (int16_t) | ||
| 137 | + ((((uint16_t) s->buf[0]) << 8) | s->buf[1]); | ||
| 138 | + tmp105_alarm_update(s); | ||
| 139 | + break; | ||
| 140 | + } | ||
| 141 | +} | ||
| 142 | + | ||
| 143 | +static int tmp105_rx(i2c_slave *i2c) | ||
| 144 | +{ | ||
| 145 | + struct tmp105_s *s = (struct tmp105_s *) i2c; | ||
| 146 | + | ||
| 147 | + if (s->len < 2) | ||
| 148 | + return s->buf[s->len ++]; | ||
| 149 | + else | ||
| 150 | + return 0xff; | ||
| 151 | +} | ||
| 152 | + | ||
| 153 | +static int tmp105_tx(i2c_slave *i2c, uint8_t data) | ||
| 154 | +{ | ||
| 155 | + struct tmp105_s *s = (struct tmp105_s *) i2c; | ||
| 156 | + | ||
| 157 | + if (!s->len ++) | ||
| 158 | + s->pointer = data; | ||
| 159 | + else { | ||
| 160 | + if (s->len <= 2) | ||
| 161 | + s->buf[s->len - 1] = data; | ||
| 162 | + tmp105_write(s); | ||
| 163 | + } | ||
| 164 | + | ||
| 165 | + return 0; | ||
| 166 | +} | ||
| 167 | + | ||
| 168 | +static void tmp105_event(i2c_slave *i2c, enum i2c_event event) | ||
| 169 | +{ | ||
| 170 | + struct tmp105_s *s = (struct tmp105_s *) i2c; | ||
| 171 | + | ||
| 172 | + if (event == I2C_START_RECV) | ||
| 173 | + tmp105_read(s); | ||
| 174 | + | ||
| 175 | + s->len = 0; | ||
| 176 | +} | ||
| 177 | + | ||
| 178 | +static void tmp105_save(QEMUFile *f, void *opaque) | ||
| 179 | +{ | ||
| 180 | + struct tmp105_s *s = (struct tmp105_s *) opaque; | ||
| 181 | + | ||
| 182 | + qemu_put_byte(f, s->len); | ||
| 183 | + qemu_put_8s(f, &s->buf[0]); | ||
| 184 | + qemu_put_8s(f, &s->buf[1]); | ||
| 185 | + | ||
| 186 | + qemu_put_8s(f, &s->pointer); | ||
| 187 | + qemu_put_8s(f, &s->config); | ||
| 188 | + qemu_put_be16s(f, &s->temperature); | ||
| 189 | + qemu_put_be16s(f, &s->limit[0]); | ||
| 190 | + qemu_put_be16s(f, &s->limit[1]); | ||
| 191 | + qemu_put_byte(f, s->alarm); | ||
| 192 | + s->faults = tmp105_faultq[(s->config >> 3) & 3]; /* F */ | ||
| 193 | + | ||
| 194 | + i2c_slave_save(f, &s->i2c); | ||
| 195 | +} | ||
| 196 | + | ||
| 197 | +static int tmp105_load(QEMUFile *f, void *opaque, int version_id) | ||
| 198 | +{ | ||
| 199 | + struct tmp105_s *s = (struct tmp105_s *) opaque; | ||
| 200 | + | ||
| 201 | + s->len = qemu_get_byte(f); | ||
| 202 | + qemu_get_8s(f, &s->buf[0]); | ||
| 203 | + qemu_get_8s(f, &s->buf[1]); | ||
| 204 | + | ||
| 205 | + qemu_get_8s(f, &s->pointer); | ||
| 206 | + qemu_get_8s(f, &s->config); | ||
| 207 | + qemu_get_be16s(f, &s->temperature); | ||
| 208 | + qemu_get_be16s(f, &s->limit[0]); | ||
| 209 | + qemu_get_be16s(f, &s->limit[1]); | ||
| 210 | + s->alarm = qemu_get_byte(f); | ||
| 211 | + | ||
| 212 | + tmp105_interrupt_update(s); | ||
| 213 | + | ||
| 214 | + i2c_slave_load(f, &s->i2c); | ||
| 215 | + return 0; | ||
| 216 | +} | ||
| 217 | + | ||
| 218 | +void tmp105_reset(i2c_slave *i2c) | ||
| 219 | +{ | ||
| 220 | + struct tmp105_s *s = (struct tmp105_s *) i2c; | ||
| 221 | + | ||
| 222 | + s->temperature = 0; | ||
| 223 | + s->pointer = 0; | ||
| 224 | + s->config = 0; | ||
| 225 | + s->faults = tmp105_faultq[(s->config >> 3) & 3]; | ||
| 226 | + s->alarm = 0; | ||
| 227 | + | ||
| 228 | + tmp105_interrupt_update(s); | ||
| 229 | +} | ||
| 230 | + | ||
| 231 | +static int tmp105_iid = 0; | ||
| 232 | + | ||
| 233 | +struct i2c_slave *tmp105_init(i2c_bus *bus, qemu_irq alarm) | ||
| 234 | +{ | ||
| 235 | + struct tmp105_s *s = (struct tmp105_s *) | ||
| 236 | + i2c_slave_init(bus, 0, sizeof(struct tmp105_s)); | ||
| 237 | + | ||
| 238 | + s->i2c.event = tmp105_event; | ||
| 239 | + s->i2c.recv = tmp105_rx; | ||
| 240 | + s->i2c.send = tmp105_tx; | ||
| 241 | + s->pin = alarm; | ||
| 242 | + | ||
| 243 | + tmp105_reset(&s->i2c); | ||
| 244 | + | ||
| 245 | + register_savevm("TMP105", tmp105_iid ++, 0, | ||
| 246 | + tmp105_save, tmp105_load, s); | ||
| 247 | + | ||
| 248 | + return &s->i2c; | ||
| 249 | +} |
hw/twl92230.c
0 → 100644
| 1 | +/* | ||
| 2 | + * TI TWL92230C energy-management companion device for the OMAP24xx. | ||
| 3 | + * Aka. Menelaus (N4200 MENELAUS1_V2.2) | ||
| 4 | + * | ||
| 5 | + * Copyright (C) 2008 Nokia Corporation | ||
| 6 | + * Written by Andrzej Zaborowski <andrew@openedhand.com> | ||
| 7 | + * | ||
| 8 | + * This program is free software; you can redistribute it and/or | ||
| 9 | + * modify it under the terms of the GNU General Public License as | ||
| 10 | + * published by the Free Software Foundation; either version 2 or | ||
| 11 | + * (at your option) version 3 of the License. | ||
| 12 | + * | ||
| 13 | + * This program is distributed in the hope that it will be useful, | ||
| 14 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 15 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 16 | + * GNU General Public License for more details. | ||
| 17 | + * | ||
| 18 | + * You should have received a copy of the GNU General Public License | ||
| 19 | + * along with this program; if not, write to the Free Software | ||
| 20 | + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, | ||
| 21 | + * MA 02111-1307 USA | ||
| 22 | + */ | ||
| 23 | + | ||
| 24 | +#include "hw.h" | ||
| 25 | +#include "qemu-timer.h" | ||
| 26 | +#include "i2c.h" | ||
| 27 | +#include "sysemu.h" | ||
| 28 | +#include "console.h" | ||
| 29 | + | ||
| 30 | +#define VERBOSE 1 | ||
| 31 | + | ||
| 32 | +struct menelaus_s { | ||
| 33 | + i2c_slave i2c; | ||
| 34 | + qemu_irq irq; | ||
| 35 | + | ||
| 36 | + int firstbyte; | ||
| 37 | + uint8_t reg; | ||
| 38 | + | ||
| 39 | + uint8_t vcore[5]; | ||
| 40 | + uint8_t dcdc[3]; | ||
| 41 | + uint8_t ldo[8]; | ||
| 42 | + uint8_t sleep[2]; | ||
| 43 | + uint8_t osc; | ||
| 44 | + uint8_t detect; | ||
| 45 | + uint16_t mask; | ||
| 46 | + uint16_t status; | ||
| 47 | + uint8_t dir; | ||
| 48 | + uint8_t inputs; | ||
| 49 | + uint8_t outputs; | ||
| 50 | + uint8_t bbsms; | ||
| 51 | + uint8_t pull[4]; | ||
| 52 | + uint8_t mmc_ctrl[3]; | ||
| 53 | + uint8_t mmc_debounce; | ||
| 54 | + struct { | ||
| 55 | + uint8_t ctrl; | ||
| 56 | + uint16_t comp; | ||
| 57 | + QEMUTimer *hz; | ||
| 58 | + int64_t next; | ||
| 59 | + struct tm tm; | ||
| 60 | + struct tm new; | ||
| 61 | + struct tm alm; | ||
| 62 | + time_t sec; | ||
| 63 | + time_t alm_sec; | ||
| 64 | + time_t next_comp; | ||
| 65 | + struct tm *(*gettime)(const time_t *timep, struct tm *result); | ||
| 66 | + } rtc; | ||
| 67 | + qemu_irq handler[3]; | ||
| 68 | + qemu_irq *in; | ||
| 69 | + int pwrbtn_state; | ||
| 70 | + qemu_irq pwrbtn; | ||
| 71 | +}; | ||
| 72 | + | ||
| 73 | +static inline void menelaus_update(struct menelaus_s *s) | ||
| 74 | +{ | ||
| 75 | + qemu_set_irq(s->irq, s->status & ~s->mask); | ||
| 76 | +} | ||
| 77 | + | ||
| 78 | +static inline void menelaus_rtc_start(struct menelaus_s *s) | ||
| 79 | +{ | ||
| 80 | + s->rtc.next =+ qemu_get_clock(rt_clock); | ||
| 81 | + qemu_mod_timer(s->rtc.hz, s->rtc.next); | ||
| 82 | +} | ||
| 83 | + | ||
| 84 | +static inline void menelaus_rtc_stop(struct menelaus_s *s) | ||
| 85 | +{ | ||
| 86 | + qemu_del_timer(s->rtc.hz); | ||
| 87 | + s->rtc.next =- qemu_get_clock(rt_clock); | ||
| 88 | + if (s->rtc.next < 1) | ||
| 89 | + s->rtc.next = 1; | ||
| 90 | +} | ||
| 91 | + | ||
| 92 | +static void menelaus_rtc_update(struct menelaus_s *s) | ||
| 93 | +{ | ||
| 94 | + s->rtc.gettime(&s->rtc.sec, &s->rtc.tm); | ||
| 95 | +} | ||
| 96 | + | ||
| 97 | +static void menelaus_alm_update(struct menelaus_s *s) | ||
| 98 | +{ | ||
| 99 | + if ((s->rtc.ctrl & 3) == 3) | ||
| 100 | + s->rtc.alm_sec = mktime(&s->rtc.alm); | ||
| 101 | +} | ||
| 102 | + | ||
| 103 | +static void menelaus_rtc_hz(void *opaque) | ||
| 104 | +{ | ||
| 105 | + struct menelaus_s *s = (struct menelaus_s *) opaque; | ||
| 106 | + | ||
| 107 | + s->rtc.sec ++; | ||
| 108 | + s->rtc.next += 1000; | ||
| 109 | + qemu_mod_timer(s->rtc.hz, s->rtc.next); | ||
| 110 | + if ((s->rtc.ctrl >> 3) & 3) { /* EVERY */ | ||
| 111 | + menelaus_rtc_update(s); | ||
| 112 | + if (((s->rtc.ctrl >> 3) & 3) == 1 && !s->rtc.tm.tm_sec) | ||
| 113 | + s->status |= 1 << 8; /* RTCTMR */ | ||
| 114 | + else if (((s->rtc.ctrl >> 3) & 3) == 2 && !s->rtc.tm.tm_min) | ||
| 115 | + s->status |= 1 << 8; /* RTCTMR */ | ||
| 116 | + else if (!s->rtc.tm.tm_hour) | ||
| 117 | + s->status |= 1 << 8; /* RTCTMR */ | ||
| 118 | + } else | ||
| 119 | + s->status |= 1 << 8; /* RTCTMR */ | ||
| 120 | + if ((s->rtc.ctrl >> 1) & 1) { /* RTC_AL_EN */ | ||
| 121 | + if (s->rtc.sec == s->rtc.alm_sec) | ||
| 122 | + s->status |= 1 << 9; /* RTCALM */ | ||
| 123 | + /* TODO: wake-up */ | ||
| 124 | + } | ||
| 125 | + if (s->rtc.next_comp >= s->rtc.sec) { | ||
| 126 | + s->rtc.next -= muldiv64((int16_t) s->rtc.comp, 1000, 0x8000); | ||
| 127 | + s->rtc.next_comp = s->rtc.sec + 3600; | ||
| 128 | + } | ||
| 129 | + menelaus_update(s); | ||
| 130 | +} | ||
| 131 | + | ||
| 132 | +void menelaus_reset(i2c_slave *i2c) | ||
| 133 | +{ | ||
| 134 | + struct menelaus_s *s = (struct menelaus_s *) i2c; | ||
| 135 | + time_t ti; | ||
| 136 | + s->reg = 0x00; | ||
| 137 | + | ||
| 138 | + s->vcore[0] = 0x0c; /* XXX: X-loader needs 0x8c? check! */ | ||
| 139 | + s->vcore[1] = 0x05; | ||
| 140 | + s->vcore[2] = 0x02; | ||
| 141 | + s->vcore[3] = 0x0c; | ||
| 142 | + s->vcore[4] = 0x03; | ||
| 143 | + s->dcdc[0] = 0x33; /* Depends on wiring */ | ||
| 144 | + s->dcdc[1] = 0x03; | ||
| 145 | + s->dcdc[2] = 0x00; | ||
| 146 | + s->ldo[0] = 0x95; | ||
| 147 | + s->ldo[1] = 0x7e; | ||
| 148 | + s->ldo[2] = 0x00; | ||
| 149 | + s->ldo[3] = 0x00; /* Depends on wiring */ | ||
| 150 | + s->ldo[4] = 0x03; /* Depends on wiring */ | ||
| 151 | + s->ldo[5] = 0x00; | ||
| 152 | + s->ldo[6] = 0x00; | ||
| 153 | + s->ldo[7] = 0x00; | ||
| 154 | + s->sleep[0] = 0x00; | ||
| 155 | + s->sleep[1] = 0x00; | ||
| 156 | + s->osc = 0x01; | ||
| 157 | + s->detect = 0x09; | ||
| 158 | + s->mask = 0x0fff; | ||
| 159 | + s->status = 0; | ||
| 160 | + s->dir = 0x07; | ||
| 161 | + s->outputs = 0x00; | ||
| 162 | + s->bbsms = 0x00; | ||
| 163 | + s->pull[0] = 0x00; | ||
| 164 | + s->pull[1] = 0x00; | ||
| 165 | + s->pull[2] = 0x00; | ||
| 166 | + s->pull[3] = 0x00; | ||
| 167 | + s->mmc_ctrl[0] = 0x03; | ||
| 168 | + s->mmc_ctrl[1] = 0xc0; | ||
| 169 | + s->mmc_ctrl[2] = 0x00; | ||
| 170 | + s->mmc_debounce = 0x05; | ||
| 171 | + | ||
| 172 | + time(&ti); | ||
| 173 | + if (s->rtc.ctrl & 1) | ||
| 174 | + menelaus_rtc_stop(s); | ||
| 175 | + s->rtc.ctrl = 0x00; | ||
| 176 | + s->rtc.comp = 0x0000; | ||
| 177 | + s->rtc.next = 1000; | ||
| 178 | + s->rtc.sec = ti; | ||
| 179 | + s->rtc.next_comp = s->rtc.sec + 1800; | ||
| 180 | + s->rtc.alm.tm_sec = 0x00; | ||
| 181 | + s->rtc.alm.tm_min = 0x00; | ||
| 182 | + s->rtc.alm.tm_hour = 0x00; | ||
| 183 | + s->rtc.alm.tm_mday = 0x01; | ||
| 184 | + s->rtc.alm.tm_mon = 0x00; | ||
| 185 | + s->rtc.alm.tm_year = 2004; | ||
| 186 | + menelaus_update(s); | ||
| 187 | +} | ||
| 188 | + | ||
| 189 | +static inline uint8_t to_bcd(int val) | ||
| 190 | +{ | ||
| 191 | + return ((val / 10) << 4) | (val % 10); | ||
| 192 | +} | ||
| 193 | + | ||
| 194 | +static inline int from_bcd(uint8_t val) | ||
| 195 | +{ | ||
| 196 | + return ((val >> 4) * 10) + (val & 0x0f); | ||
| 197 | +} | ||
| 198 | + | ||
| 199 | +static void menelaus_gpio_set(void *opaque, int line, int level) | ||
| 200 | +{ | ||
| 201 | + struct menelaus_s *s = (struct menelaus_s *) opaque; | ||
| 202 | + | ||
| 203 | + /* No interrupt generated */ | ||
| 204 | + s->inputs &= ~(1 << line); | ||
| 205 | + s->inputs |= level << line; | ||
| 206 | +} | ||
| 207 | + | ||
| 208 | +static void menelaus_pwrbtn_set(void *opaque, int line, int level) | ||
| 209 | +{ | ||
| 210 | + struct menelaus_s *s = (struct menelaus_s *) opaque; | ||
| 211 | + | ||
| 212 | + if (!s->pwrbtn_state && level) { | ||
| 213 | + s->status |= 1 << 11; /* PSHBTN */ | ||
| 214 | + menelaus_update(s); | ||
| 215 | + } | ||
| 216 | + s->pwrbtn_state = level; | ||
| 217 | +} | ||
| 218 | + | ||
| 219 | +#define MENELAUS_REV 0x01 | ||
| 220 | +#define MENELAUS_VCORE_CTRL1 0x02 | ||
| 221 | +#define MENELAUS_VCORE_CTRL2 0x03 | ||
| 222 | +#define MENELAUS_VCORE_CTRL3 0x04 | ||
| 223 | +#define MENELAUS_VCORE_CTRL4 0x05 | ||
| 224 | +#define MENELAUS_VCORE_CTRL5 0x06 | ||
| 225 | +#define MENELAUS_DCDC_CTRL1 0x07 | ||
| 226 | +#define MENELAUS_DCDC_CTRL2 0x08 | ||
| 227 | +#define MENELAUS_DCDC_CTRL3 0x09 | ||
| 228 | +#define MENELAUS_LDO_CTRL1 0x0a | ||
| 229 | +#define MENELAUS_LDO_CTRL2 0x0b | ||
| 230 | +#define MENELAUS_LDO_CTRL3 0x0c | ||
| 231 | +#define MENELAUS_LDO_CTRL4 0x0d | ||
| 232 | +#define MENELAUS_LDO_CTRL5 0x0e | ||
| 233 | +#define MENELAUS_LDO_CTRL6 0x0f | ||
| 234 | +#define MENELAUS_LDO_CTRL7 0x10 | ||
| 235 | +#define MENELAUS_LDO_CTRL8 0x11 | ||
| 236 | +#define MENELAUS_SLEEP_CTRL1 0x12 | ||
| 237 | +#define MENELAUS_SLEEP_CTRL2 0x13 | ||
| 238 | +#define MENELAUS_DEVICE_OFF 0x14 | ||
| 239 | +#define MENELAUS_OSC_CTRL 0x15 | ||
| 240 | +#define MENELAUS_DETECT_CTRL 0x16 | ||
| 241 | +#define MENELAUS_INT_MASK1 0x17 | ||
| 242 | +#define MENELAUS_INT_MASK2 0x18 | ||
| 243 | +#define MENELAUS_INT_STATUS1 0x19 | ||
| 244 | +#define MENELAUS_INT_STATUS2 0x1a | ||
| 245 | +#define MENELAUS_INT_ACK1 0x1b | ||
| 246 | +#define MENELAUS_INT_ACK2 0x1c | ||
| 247 | +#define MENELAUS_GPIO_CTRL 0x1d | ||
| 248 | +#define MENELAUS_GPIO_IN 0x1e | ||
| 249 | +#define MENELAUS_GPIO_OUT 0x1f | ||
| 250 | +#define MENELAUS_BBSMS 0x20 | ||
| 251 | +#define MENELAUS_RTC_CTRL 0x21 | ||
| 252 | +#define MENELAUS_RTC_UPDATE 0x22 | ||
| 253 | +#define MENELAUS_RTC_SEC 0x23 | ||
| 254 | +#define MENELAUS_RTC_MIN 0x24 | ||
| 255 | +#define MENELAUS_RTC_HR 0x25 | ||
| 256 | +#define MENELAUS_RTC_DAY 0x26 | ||
| 257 | +#define MENELAUS_RTC_MON 0x27 | ||
| 258 | +#define MENELAUS_RTC_YR 0x28 | ||
| 259 | +#define MENELAUS_RTC_WKDAY 0x29 | ||
| 260 | +#define MENELAUS_RTC_AL_SEC 0x2a | ||
| 261 | +#define MENELAUS_RTC_AL_MIN 0x2b | ||
| 262 | +#define MENELAUS_RTC_AL_HR 0x2c | ||
| 263 | +#define MENELAUS_RTC_AL_DAY 0x2d | ||
| 264 | +#define MENELAUS_RTC_AL_MON 0x2e | ||
| 265 | +#define MENELAUS_RTC_AL_YR 0x2f | ||
| 266 | +#define MENELAUS_RTC_COMP_MSB 0x30 | ||
| 267 | +#define MENELAUS_RTC_COMP_LSB 0x31 | ||
| 268 | +#define MENELAUS_S1_PULL_EN 0x32 | ||
| 269 | +#define MENELAUS_S1_PULL_DIR 0x33 | ||
| 270 | +#define MENELAUS_S2_PULL_EN 0x34 | ||
| 271 | +#define MENELAUS_S2_PULL_DIR 0x35 | ||
| 272 | +#define MENELAUS_MCT_CTRL1 0x36 | ||
| 273 | +#define MENELAUS_MCT_CTRL2 0x37 | ||
| 274 | +#define MENELAUS_MCT_CTRL3 0x38 | ||
| 275 | +#define MENELAUS_MCT_PIN_ST 0x39 | ||
| 276 | +#define MENELAUS_DEBOUNCE1 0x3a | ||
| 277 | + | ||
| 278 | +static uint8_t menelaus_read(void *opaque, uint8_t addr) | ||
| 279 | +{ | ||
| 280 | + struct menelaus_s *s = (struct menelaus_s *) opaque; | ||
| 281 | + int reg = 0; | ||
| 282 | + | ||
| 283 | + switch (addr) { | ||
| 284 | + case MENELAUS_REV: | ||
| 285 | + return 0x22; | ||
| 286 | + | ||
| 287 | + case MENELAUS_VCORE_CTRL5: reg ++; | ||
| 288 | + case MENELAUS_VCORE_CTRL4: reg ++; | ||
| 289 | + case MENELAUS_VCORE_CTRL3: reg ++; | ||
| 290 | + case MENELAUS_VCORE_CTRL2: reg ++; | ||
| 291 | + case MENELAUS_VCORE_CTRL1: | ||
| 292 | + return s->vcore[reg]; | ||
| 293 | + | ||
| 294 | + case MENELAUS_DCDC_CTRL3: reg ++; | ||
| 295 | + case MENELAUS_DCDC_CTRL2: reg ++; | ||
| 296 | + case MENELAUS_DCDC_CTRL1: | ||
| 297 | + return s->dcdc[reg]; | ||
| 298 | + | ||
| 299 | + case MENELAUS_LDO_CTRL8: reg ++; | ||
| 300 | + case MENELAUS_LDO_CTRL7: reg ++; | ||
| 301 | + case MENELAUS_LDO_CTRL6: reg ++; | ||
| 302 | + case MENELAUS_LDO_CTRL5: reg ++; | ||
| 303 | + case MENELAUS_LDO_CTRL4: reg ++; | ||
| 304 | + case MENELAUS_LDO_CTRL3: reg ++; | ||
| 305 | + case MENELAUS_LDO_CTRL2: reg ++; | ||
| 306 | + case MENELAUS_LDO_CTRL1: | ||
| 307 | + return s->ldo[reg]; | ||
| 308 | + | ||
| 309 | + case MENELAUS_SLEEP_CTRL2: reg ++; | ||
| 310 | + case MENELAUS_SLEEP_CTRL1: | ||
| 311 | + return s->sleep[reg]; | ||
| 312 | + | ||
| 313 | + case MENELAUS_DEVICE_OFF: | ||
| 314 | + return 0; | ||
| 315 | + | ||
| 316 | + case MENELAUS_OSC_CTRL: | ||
| 317 | + return s->osc | (1 << 7); /* CLK32K_GOOD */ | ||
| 318 | + | ||
| 319 | + case MENELAUS_DETECT_CTRL: | ||
| 320 | + return s->detect; | ||
| 321 | + | ||
| 322 | + case MENELAUS_INT_MASK1: | ||
| 323 | + return (s->mask >> 0) & 0xff; | ||
| 324 | + case MENELAUS_INT_MASK2: | ||
| 325 | + return (s->mask >> 8) & 0xff; | ||
| 326 | + | ||
| 327 | + case MENELAUS_INT_STATUS1: | ||
| 328 | + return (s->status >> 0) & 0xff; | ||
| 329 | + case MENELAUS_INT_STATUS2: | ||
| 330 | + return (s->status >> 8) & 0xff; | ||
| 331 | + | ||
| 332 | + case MENELAUS_INT_ACK1: | ||
| 333 | + case MENELAUS_INT_ACK2: | ||
| 334 | + return 0; | ||
| 335 | + | ||
| 336 | + case MENELAUS_GPIO_CTRL: | ||
| 337 | + return s->dir; | ||
| 338 | + case MENELAUS_GPIO_IN: | ||
| 339 | + return s->inputs | (~s->dir & s->outputs); | ||
| 340 | + case MENELAUS_GPIO_OUT: | ||
| 341 | + return s->outputs; | ||
| 342 | + | ||
| 343 | + case MENELAUS_BBSMS: | ||
| 344 | + return s->bbsms; | ||
| 345 | + | ||
| 346 | + case MENELAUS_RTC_CTRL: | ||
| 347 | + return s->rtc.ctrl; | ||
| 348 | + case MENELAUS_RTC_UPDATE: | ||
| 349 | + return 0x00; | ||
| 350 | + case MENELAUS_RTC_SEC: | ||
| 351 | + menelaus_rtc_update(s); | ||
| 352 | + return to_bcd(s->rtc.tm.tm_sec); | ||
| 353 | + case MENELAUS_RTC_MIN: | ||
| 354 | + menelaus_rtc_update(s); | ||
| 355 | + return to_bcd(s->rtc.tm.tm_min); | ||
| 356 | + case MENELAUS_RTC_HR: | ||
| 357 | + menelaus_rtc_update(s); | ||
| 358 | + if ((s->rtc.ctrl >> 2) & 1) /* MODE12_n24 */ | ||
| 359 | + return to_bcd((s->rtc.tm.tm_hour % 12) + 1) | | ||
| 360 | + (!!(s->rtc.tm.tm_hour >= 12) << 7); /* PM_nAM */ | ||
| 361 | + else | ||
| 362 | + return to_bcd(s->rtc.tm.tm_hour); | ||
| 363 | + case MENELAUS_RTC_DAY: | ||
| 364 | + menelaus_rtc_update(s); | ||
| 365 | + return to_bcd(s->rtc.tm.tm_mday); | ||
| 366 | + case MENELAUS_RTC_MON: | ||
| 367 | + menelaus_rtc_update(s); | ||
| 368 | + return to_bcd(s->rtc.tm.tm_mon + 1); | ||
| 369 | + case MENELAUS_RTC_YR: | ||
| 370 | + menelaus_rtc_update(s); | ||
| 371 | + return to_bcd(s->rtc.tm.tm_year - 2000); | ||
| 372 | + case MENELAUS_RTC_WKDAY: | ||
| 373 | + menelaus_rtc_update(s); | ||
| 374 | + return to_bcd(s->rtc.tm.tm_wday); | ||
| 375 | + case MENELAUS_RTC_AL_SEC: | ||
| 376 | + return to_bcd(s->rtc.alm.tm_sec); | ||
| 377 | + case MENELAUS_RTC_AL_MIN: | ||
| 378 | + return to_bcd(s->rtc.alm.tm_min); | ||
| 379 | + case MENELAUS_RTC_AL_HR: | ||
| 380 | + if ((s->rtc.ctrl >> 2) & 1) /* MODE12_n24 */ | ||
| 381 | + return to_bcd((s->rtc.alm.tm_hour % 12) + 1) | | ||
| 382 | + (!!(s->rtc.alm.tm_hour >= 12) << 7);/* AL_PM_nAM */ | ||
| 383 | + else | ||
| 384 | + return to_bcd(s->rtc.alm.tm_hour); | ||
| 385 | + case MENELAUS_RTC_AL_DAY: | ||
| 386 | + return to_bcd(s->rtc.alm.tm_mday); | ||
| 387 | + case MENELAUS_RTC_AL_MON: | ||
| 388 | + return to_bcd(s->rtc.alm.tm_mon + 1); | ||
| 389 | + case MENELAUS_RTC_AL_YR: | ||
| 390 | + return to_bcd(s->rtc.alm.tm_year - 2000); | ||
| 391 | + case MENELAUS_RTC_COMP_MSB: | ||
| 392 | + return (s->rtc.comp >> 8) & 0xff; | ||
| 393 | + case MENELAUS_RTC_COMP_LSB: | ||
| 394 | + return (s->rtc.comp >> 0) & 0xff; | ||
| 395 | + | ||
| 396 | + case MENELAUS_S1_PULL_EN: | ||
| 397 | + return s->pull[0]; | ||
| 398 | + case MENELAUS_S1_PULL_DIR: | ||
| 399 | + return s->pull[1]; | ||
| 400 | + case MENELAUS_S2_PULL_EN: | ||
| 401 | + return s->pull[2]; | ||
| 402 | + case MENELAUS_S2_PULL_DIR: | ||
| 403 | + return s->pull[3]; | ||
| 404 | + | ||
| 405 | + case MENELAUS_MCT_CTRL3: reg ++; | ||
| 406 | + case MENELAUS_MCT_CTRL2: reg ++; | ||
| 407 | + case MENELAUS_MCT_CTRL1: | ||
| 408 | + return s->mmc_ctrl[reg]; | ||
| 409 | + case MENELAUS_MCT_PIN_ST: | ||
| 410 | + /* TODO: return the real Card Detect */ | ||
| 411 | + return 0; | ||
| 412 | + case MENELAUS_DEBOUNCE1: | ||
| 413 | + return s->mmc_debounce; | ||
| 414 | + | ||
| 415 | + default: | ||
| 416 | +#ifdef VERBOSE | ||
| 417 | + printf("%s: unknown register %02x\n", __FUNCTION__, addr); | ||
| 418 | +#endif | ||
| 419 | + break; | ||
| 420 | + } | ||
| 421 | + return 0; | ||
| 422 | +} | ||
| 423 | + | ||
| 424 | +static void menelaus_write(void *opaque, uint8_t addr, uint8_t value) | ||
| 425 | +{ | ||
| 426 | + struct menelaus_s *s = (struct menelaus_s *) opaque; | ||
| 427 | + int line; | ||
| 428 | + int reg = 0; | ||
| 429 | + struct tm tm; | ||
| 430 | + | ||
| 431 | + switch (addr) { | ||
| 432 | + case MENELAUS_VCORE_CTRL1: | ||
| 433 | + s->vcore[0] = (value & 0xe) | MIN(value & 0x1f, 0x12); | ||
| 434 | + break; | ||
| 435 | + case MENELAUS_VCORE_CTRL2: | ||
| 436 | + s->vcore[1] = value; | ||
| 437 | + break; | ||
| 438 | + case MENELAUS_VCORE_CTRL3: | ||
| 439 | + s->vcore[2] = MIN(value & 0x1f, 0x12); | ||
| 440 | + break; | ||
| 441 | + case MENELAUS_VCORE_CTRL4: | ||
| 442 | + s->vcore[3] = MIN(value & 0x1f, 0x12); | ||
| 443 | + break; | ||
| 444 | + case MENELAUS_VCORE_CTRL5: | ||
| 445 | + s->vcore[4] = value & 3; | ||
| 446 | + /* XXX | ||
| 447 | + * auto set to 3 on M_Active, nRESWARM | ||
| 448 | + * auto set to 0 on M_WaitOn, M_Backup | ||
| 449 | + */ | ||
| 450 | + break; | ||
| 451 | + | ||
| 452 | + case MENELAUS_DCDC_CTRL1: | ||
| 453 | + s->dcdc[0] = value & 0x3f; | ||
| 454 | + break; | ||
| 455 | + case MENELAUS_DCDC_CTRL2: | ||
| 456 | + s->dcdc[1] = value & 0x07; | ||
| 457 | + /* XXX | ||
| 458 | + * auto set to 3 on M_Active, nRESWARM | ||
| 459 | + * auto set to 0 on M_WaitOn, M_Backup | ||
| 460 | + */ | ||
| 461 | + break; | ||
| 462 | + case MENELAUS_DCDC_CTRL3: | ||
| 463 | + s->dcdc[2] = value & 0x07; | ||
| 464 | + break; | ||
| 465 | + | ||
| 466 | + case MENELAUS_LDO_CTRL1: | ||
| 467 | + s->ldo[0] = value; | ||
| 468 | + break; | ||
| 469 | + case MENELAUS_LDO_CTRL2: | ||
| 470 | + s->ldo[1] = value & 0x7f; | ||
| 471 | + /* XXX | ||
| 472 | + * auto set to 0x7e on M_WaitOn, M_Backup | ||
| 473 | + */ | ||
| 474 | + break; | ||
| 475 | + case MENELAUS_LDO_CTRL3: | ||
| 476 | + s->ldo[2] = value & 3; | ||
| 477 | + /* XXX | ||
| 478 | + * auto set to 3 on M_Active, nRESWARM | ||
| 479 | + * auto set to 0 on M_WaitOn, M_Backup | ||
| 480 | + */ | ||
| 481 | + break; | ||
| 482 | + case MENELAUS_LDO_CTRL4: | ||
| 483 | + s->ldo[3] = value & 3; | ||
| 484 | + /* XXX | ||
| 485 | + * auto set to 3 on M_Active, nRESWARM | ||
| 486 | + * auto set to 0 on M_WaitOn, M_Backup | ||
| 487 | + */ | ||
| 488 | + break; | ||
| 489 | + case MENELAUS_LDO_CTRL5: | ||
| 490 | + s->ldo[4] = value & 3; | ||
| 491 | + /* XXX | ||
| 492 | + * auto set to 3 on M_Active, nRESWARM | ||
| 493 | + * auto set to 0 on M_WaitOn, M_Backup | ||
| 494 | + */ | ||
| 495 | + break; | ||
| 496 | + case MENELAUS_LDO_CTRL6: | ||
| 497 | + s->ldo[5] = value & 3; | ||
| 498 | + break; | ||
| 499 | + case MENELAUS_LDO_CTRL7: | ||
| 500 | + s->ldo[6] = value & 3; | ||
| 501 | + break; | ||
| 502 | + case MENELAUS_LDO_CTRL8: | ||
| 503 | + s->ldo[7] = value & 3; | ||
| 504 | + break; | ||
| 505 | + | ||
| 506 | + case MENELAUS_SLEEP_CTRL2: reg ++; | ||
| 507 | + case MENELAUS_SLEEP_CTRL1: | ||
| 508 | + s->sleep[reg] = value; | ||
| 509 | + break; | ||
| 510 | + | ||
| 511 | + case MENELAUS_DEVICE_OFF: | ||
| 512 | + if (value & 1) | ||
| 513 | + menelaus_reset(&s->i2c); | ||
| 514 | + break; | ||
| 515 | + | ||
| 516 | + case MENELAUS_OSC_CTRL: | ||
| 517 | + s->osc = value & 7; | ||
| 518 | + break; | ||
| 519 | + | ||
| 520 | + case MENELAUS_DETECT_CTRL: | ||
| 521 | + s->detect = value & 0x7f; | ||
| 522 | + break; | ||
| 523 | + | ||
| 524 | + case MENELAUS_INT_MASK1: | ||
| 525 | + s->mask &= 0xf00; | ||
| 526 | + s->mask |= value << 0; | ||
| 527 | + menelaus_update(s); | ||
| 528 | + break; | ||
| 529 | + case MENELAUS_INT_MASK2: | ||
| 530 | + s->mask &= 0x0ff; | ||
| 531 | + s->mask |= value << 8; | ||
| 532 | + menelaus_update(s); | ||
| 533 | + break; | ||
| 534 | + | ||
| 535 | + case MENELAUS_INT_ACK1: | ||
| 536 | + s->status &= ~(((uint16_t) value) << 0); | ||
| 537 | + menelaus_update(s); | ||
| 538 | + break; | ||
| 539 | + case MENELAUS_INT_ACK2: | ||
| 540 | + s->status &= ~(((uint16_t) value) << 8); | ||
| 541 | + menelaus_update(s); | ||
| 542 | + break; | ||
| 543 | + | ||
| 544 | + case MENELAUS_GPIO_CTRL: | ||
| 545 | + for (line = 0; line < 3; line ++) | ||
| 546 | + if (((s->dir ^ value) >> line) & 1) | ||
| 547 | + if (s->handler[line]) | ||
| 548 | + qemu_set_irq(s->handler[line], | ||
| 549 | + ((s->outputs & ~s->dir) >> line) & 1); | ||
| 550 | + s->dir = value & 0x67; | ||
| 551 | + break; | ||
| 552 | + case MENELAUS_GPIO_OUT: | ||
| 553 | + for (line = 0; line < 3; line ++) | ||
| 554 | + if ((((s->outputs ^ value) & ~s->dir) >> line) & 1) | ||
| 555 | + if (s->handler[line]) | ||
| 556 | + qemu_set_irq(s->handler[line], (s->outputs >> line) & 1); | ||
| 557 | + s->outputs = value & 0x07; | ||
| 558 | + break; | ||
| 559 | + | ||
| 560 | + case MENELAUS_BBSMS: | ||
| 561 | + s->bbsms = 0x0d; | ||
| 562 | + break; | ||
| 563 | + | ||
| 564 | + case MENELAUS_RTC_CTRL: | ||
| 565 | + if ((s->rtc.ctrl ^ value) & 1) { /* RTC_EN */ | ||
| 566 | + if (value & 1) | ||
| 567 | + menelaus_rtc_start(s); | ||
| 568 | + else | ||
| 569 | + menelaus_rtc_stop(s); | ||
| 570 | + } | ||
| 571 | + s->rtc.ctrl = value & 0x1f; | ||
| 572 | + menelaus_alm_update(s); | ||
| 573 | + break; | ||
| 574 | + case MENELAUS_RTC_UPDATE: | ||
| 575 | + menelaus_rtc_update(s); | ||
| 576 | + memcpy(&tm, &s->rtc.tm, sizeof(tm)); | ||
| 577 | + switch (value & 0xf) { | ||
| 578 | + case 0: | ||
| 579 | + break; | ||
| 580 | + case 1: | ||
| 581 | + tm.tm_sec = s->rtc.new.tm_sec; | ||
| 582 | + break; | ||
| 583 | + case 2: | ||
| 584 | + tm.tm_min = s->rtc.new.tm_min; | ||
| 585 | + break; | ||
| 586 | + case 3: | ||
| 587 | + if (s->rtc.new.tm_hour > 23) | ||
| 588 | + goto rtc_badness; | ||
| 589 | + tm.tm_hour = s->rtc.new.tm_hour; | ||
| 590 | + break; | ||
| 591 | + case 4: | ||
| 592 | + if (s->rtc.new.tm_mday < 1) | ||
| 593 | + goto rtc_badness; | ||
| 594 | + /* TODO check range */ | ||
| 595 | + tm.tm_mday = s->rtc.new.tm_mday; | ||
| 596 | + break; | ||
| 597 | + case 5: | ||
| 598 | + if (s->rtc.new.tm_mon < 0 || s->rtc.new.tm_mon > 11) | ||
| 599 | + goto rtc_badness; | ||
| 600 | + tm.tm_mon = s->rtc.new.tm_mon; | ||
| 601 | + break; | ||
| 602 | + case 6: | ||
| 603 | + tm.tm_year = s->rtc.new.tm_year; | ||
| 604 | + break; | ||
| 605 | + case 7: | ||
| 606 | + /* TODO set .tm_mday instead */ | ||
| 607 | + tm.tm_wday = s->rtc.new.tm_wday; | ||
| 608 | + break; | ||
| 609 | + case 8: | ||
| 610 | + if (s->rtc.new.tm_hour > 23) | ||
| 611 | + goto rtc_badness; | ||
| 612 | + if (s->rtc.new.tm_mday < 1) | ||
| 613 | + goto rtc_badness; | ||
| 614 | + if (s->rtc.new.tm_mon < 0 || s->rtc.new.tm_mon > 11) | ||
| 615 | + goto rtc_badness; | ||
| 616 | + tm.tm_sec = s->rtc.new.tm_sec; | ||
| 617 | + tm.tm_min = s->rtc.new.tm_min; | ||
| 618 | + tm.tm_hour = s->rtc.new.tm_hour; | ||
| 619 | + tm.tm_mday = s->rtc.new.tm_mday; | ||
| 620 | + tm.tm_mon = s->rtc.new.tm_mon; | ||
| 621 | + tm.tm_year = s->rtc.new.tm_year; | ||
| 622 | + break; | ||
| 623 | + rtc_badness: | ||
| 624 | + default: | ||
| 625 | + fprintf(stderr, "%s: bad RTC_UPDATE value %02x\n", | ||
| 626 | + __FUNCTION__, value); | ||
| 627 | + s->status |= 1 << 10; /* RTCERR */ | ||
| 628 | + menelaus_update(s); | ||
| 629 | + } | ||
| 630 | + s->rtc.sec += difftime(mktime(&tm), mktime(&s->rtc.tm)); | ||
| 631 | + break; | ||
| 632 | + case MENELAUS_RTC_SEC: | ||
| 633 | + s->rtc.tm.tm_sec = from_bcd(value & 0x7f); | ||
| 634 | + break; | ||
| 635 | + case MENELAUS_RTC_MIN: | ||
| 636 | + s->rtc.tm.tm_min = from_bcd(value & 0x7f); | ||
| 637 | + break; | ||
| 638 | + case MENELAUS_RTC_HR: | ||
| 639 | + s->rtc.tm.tm_hour = (s->rtc.ctrl & (1 << 2)) ? /* MODE12_n24 */ | ||
| 640 | + MIN(from_bcd(value & 0x3f), 12) + ((value >> 7) ? 11 : -1) : | ||
| 641 | + from_bcd(value & 0x3f); | ||
| 642 | + break; | ||
| 643 | + case MENELAUS_RTC_DAY: | ||
| 644 | + s->rtc.tm.tm_mday = from_bcd(value); | ||
| 645 | + break; | ||
| 646 | + case MENELAUS_RTC_MON: | ||
| 647 | + s->rtc.tm.tm_mon = MAX(1, from_bcd(value)) - 1; | ||
| 648 | + break; | ||
| 649 | + case MENELAUS_RTC_YR: | ||
| 650 | + s->rtc.tm.tm_year = 2000 + from_bcd(value); | ||
| 651 | + break; | ||
| 652 | + case MENELAUS_RTC_WKDAY: | ||
| 653 | + s->rtc.tm.tm_mday = from_bcd(value); | ||
| 654 | + break; | ||
| 655 | + case MENELAUS_RTC_AL_SEC: | ||
| 656 | + s->rtc.alm.tm_sec = from_bcd(value & 0x7f); | ||
| 657 | + menelaus_alm_update(s); | ||
| 658 | + break; | ||
| 659 | + case MENELAUS_RTC_AL_MIN: | ||
| 660 | + s->rtc.alm.tm_min = from_bcd(value & 0x7f); | ||
| 661 | + menelaus_alm_update(s); | ||
| 662 | + break; | ||
| 663 | + case MENELAUS_RTC_AL_HR: | ||
| 664 | + s->rtc.alm.tm_hour = (s->rtc.ctrl & (1 << 2)) ? /* MODE12_n24 */ | ||
| 665 | + MIN(from_bcd(value & 0x3f), 12) + ((value >> 7) ? 11 : -1) : | ||
| 666 | + from_bcd(value & 0x3f); | ||
| 667 | + menelaus_alm_update(s); | ||
| 668 | + break; | ||
| 669 | + case MENELAUS_RTC_AL_DAY: | ||
| 670 | + s->rtc.alm.tm_mday = from_bcd(value); | ||
| 671 | + menelaus_alm_update(s); | ||
| 672 | + break; | ||
| 673 | + case MENELAUS_RTC_AL_MON: | ||
| 674 | + s->rtc.alm.tm_mon = MAX(1, from_bcd(value)) - 1; | ||
| 675 | + menelaus_alm_update(s); | ||
| 676 | + break; | ||
| 677 | + case MENELAUS_RTC_AL_YR: | ||
| 678 | + s->rtc.alm.tm_year = 2000 + from_bcd(value); | ||
| 679 | + menelaus_alm_update(s); | ||
| 680 | + break; | ||
| 681 | + case MENELAUS_RTC_COMP_MSB: | ||
| 682 | + s->rtc.comp &= 0xff; | ||
| 683 | + s->rtc.comp |= value << 8; | ||
| 684 | + break; | ||
| 685 | + case MENELAUS_RTC_COMP_LSB: | ||
| 686 | + s->rtc.comp &= 0xff << 8; | ||
| 687 | + s->rtc.comp |= value; | ||
| 688 | + break; | ||
| 689 | + | ||
| 690 | + case MENELAUS_S1_PULL_EN: | ||
| 691 | + s->pull[0] = value; | ||
| 692 | + break; | ||
| 693 | + case MENELAUS_S1_PULL_DIR: | ||
| 694 | + s->pull[1] = value & 0x1f; | ||
| 695 | + break; | ||
| 696 | + case MENELAUS_S2_PULL_EN: | ||
| 697 | + s->pull[2] = value; | ||
| 698 | + break; | ||
| 699 | + case MENELAUS_S2_PULL_DIR: | ||
| 700 | + s->pull[3] = value & 0x1f; | ||
| 701 | + break; | ||
| 702 | + | ||
| 703 | + case MENELAUS_MCT_CTRL1: | ||
| 704 | + s->mmc_ctrl[0] = value & 0x7f; | ||
| 705 | + break; | ||
| 706 | + case MENELAUS_MCT_CTRL2: | ||
| 707 | + s->mmc_ctrl[1] = value; | ||
| 708 | + /* TODO update Card Detect interrupts */ | ||
| 709 | + break; | ||
| 710 | + case MENELAUS_MCT_CTRL3: | ||
| 711 | + s->mmc_ctrl[2] = value & 0xf; | ||
| 712 | + break; | ||
| 713 | + case MENELAUS_DEBOUNCE1: | ||
| 714 | + s->mmc_debounce = value & 0x3f; | ||
| 715 | + break; | ||
| 716 | + | ||
| 717 | + default: | ||
| 718 | +#ifdef VERBOSE | ||
| 719 | + printf("%s: unknown register %02x\n", __FUNCTION__, addr); | ||
| 720 | +#endif | ||
| 721 | + } | ||
| 722 | +} | ||
| 723 | + | ||
| 724 | +static void menelaus_event(i2c_slave *i2c, enum i2c_event event) | ||
| 725 | +{ | ||
| 726 | + struct menelaus_s *s = (struct menelaus_s *) i2c; | ||
| 727 | + | ||
| 728 | + if (event == I2C_START_SEND) | ||
| 729 | + s->firstbyte = 1; | ||
| 730 | +} | ||
| 731 | + | ||
| 732 | +static int menelaus_tx(i2c_slave *i2c, uint8_t data) | ||
| 733 | +{ | ||
| 734 | + struct menelaus_s *s = (struct menelaus_s *) i2c; | ||
| 735 | + /* Interpret register address byte */ | ||
| 736 | + if (s->firstbyte) { | ||
| 737 | + s->reg = data; | ||
| 738 | + s->firstbyte = 0; | ||
| 739 | + } else | ||
| 740 | + menelaus_write(s, s->reg ++, data); | ||
| 741 | + | ||
| 742 | + return 0; | ||
| 743 | +} | ||
| 744 | + | ||
| 745 | +static int menelaus_rx(i2c_slave *i2c) | ||
| 746 | +{ | ||
| 747 | + struct menelaus_s *s = (struct menelaus_s *) i2c; | ||
| 748 | + | ||
| 749 | + return menelaus_read(s, s->reg ++); | ||
| 750 | +} | ||
| 751 | + | ||
| 752 | +static void tm_put(QEMUFile *f, struct tm *tm) { | ||
| 753 | + qemu_put_be16(f, tm->tm_sec); | ||
| 754 | + qemu_put_be16(f, tm->tm_min); | ||
| 755 | + qemu_put_be16(f, tm->tm_hour); | ||
| 756 | + qemu_put_be16(f, tm->tm_mday); | ||
| 757 | + qemu_put_be16(f, tm->tm_min); | ||
| 758 | + qemu_put_be16(f, tm->tm_year); | ||
| 759 | +} | ||
| 760 | + | ||
| 761 | +static void tm_get(QEMUFile *f, struct tm *tm) { | ||
| 762 | + tm->tm_sec = qemu_get_be16(f); | ||
| 763 | + tm->tm_min = qemu_get_be16(f); | ||
| 764 | + tm->tm_hour = qemu_get_be16(f); | ||
| 765 | + tm->tm_mday = qemu_get_be16(f); | ||
| 766 | + tm->tm_min = qemu_get_be16(f); | ||
| 767 | + tm->tm_year = qemu_get_be16(f); | ||
| 768 | +} | ||
| 769 | + | ||
| 770 | +static void menelaus_save(QEMUFile *f, void *opaque) | ||
| 771 | +{ | ||
| 772 | + struct menelaus_s *s = (struct menelaus_s *) opaque; | ||
| 773 | + | ||
| 774 | + qemu_put_be32(f, s->firstbyte); | ||
| 775 | + qemu_put_8s(f, &s->reg); | ||
| 776 | + | ||
| 777 | + qemu_put_8s(f, &s->vcore[0]); | ||
| 778 | + qemu_put_8s(f, &s->vcore[1]); | ||
| 779 | + qemu_put_8s(f, &s->vcore[2]); | ||
| 780 | + qemu_put_8s(f, &s->vcore[3]); | ||
| 781 | + qemu_put_8s(f, &s->vcore[4]); | ||
| 782 | + qemu_put_8s(f, &s->dcdc[3]); | ||
| 783 | + qemu_put_8s(f, &s->dcdc[3]); | ||
| 784 | + qemu_put_8s(f, &s->dcdc[3]); | ||
| 785 | + qemu_put_8s(f, &s->ldo[0]); | ||
| 786 | + qemu_put_8s(f, &s->ldo[1]); | ||
| 787 | + qemu_put_8s(f, &s->ldo[2]); | ||
| 788 | + qemu_put_8s(f, &s->ldo[3]); | ||
| 789 | + qemu_put_8s(f, &s->ldo[4]); | ||
| 790 | + qemu_put_8s(f, &s->ldo[5]); | ||
| 791 | + qemu_put_8s(f, &s->ldo[6]); | ||
| 792 | + qemu_put_8s(f, &s->ldo[7]); | ||
| 793 | + qemu_put_8s(f, &s->sleep[0]); | ||
| 794 | + qemu_put_8s(f, &s->sleep[1]); | ||
| 795 | + qemu_put_8s(f, &s->osc); | ||
| 796 | + qemu_put_8s(f, &s->detect); | ||
| 797 | + qemu_put_be16s(f, &s->mask); | ||
| 798 | + qemu_put_be16s(f, &s->status); | ||
| 799 | + qemu_put_8s(f, &s->dir); | ||
| 800 | + qemu_put_8s(f, &s->inputs); | ||
| 801 | + qemu_put_8s(f, &s->outputs); | ||
| 802 | + qemu_put_8s(f, &s->bbsms); | ||
| 803 | + qemu_put_8s(f, &s->pull[0]); | ||
| 804 | + qemu_put_8s(f, &s->pull[1]); | ||
| 805 | + qemu_put_8s(f, &s->pull[2]); | ||
| 806 | + qemu_put_8s(f, &s->pull[3]); | ||
| 807 | + qemu_put_8s(f, &s->mmc_ctrl[0]); | ||
| 808 | + qemu_put_8s(f, &s->mmc_ctrl[1]); | ||
| 809 | + qemu_put_8s(f, &s->mmc_ctrl[2]); | ||
| 810 | + qemu_put_8s(f, &s->mmc_debounce); | ||
| 811 | + qemu_put_8s(f, &s->rtc.ctrl); | ||
| 812 | + qemu_put_be16s(f, &s->rtc.comp); | ||
| 813 | + /* Should be <= 1000 */ | ||
| 814 | + qemu_put_be16(f, s->rtc.next - qemu_get_clock(rt_clock)); | ||
| 815 | + tm_put(f, &s->rtc.new); | ||
| 816 | + tm_put(f, &s->rtc.alm); | ||
| 817 | + qemu_put_byte(f, s->pwrbtn_state); | ||
| 818 | + | ||
| 819 | + i2c_slave_save(f, &s->i2c); | ||
| 820 | +} | ||
| 821 | + | ||
| 822 | +static int menelaus_load(QEMUFile *f, void *opaque, int version_id) | ||
| 823 | +{ | ||
| 824 | + struct menelaus_s *s = (struct menelaus_s *) opaque; | ||
| 825 | + | ||
| 826 | + s->firstbyte = qemu_get_be32(f); | ||
| 827 | + qemu_get_8s(f, &s->reg); | ||
| 828 | + | ||
| 829 | + if (s->rtc.ctrl & 1) /* RTC_EN */ | ||
| 830 | + menelaus_rtc_stop(s); | ||
| 831 | + qemu_get_8s(f, &s->vcore[0]); | ||
| 832 | + qemu_get_8s(f, &s->vcore[1]); | ||
| 833 | + qemu_get_8s(f, &s->vcore[2]); | ||
| 834 | + qemu_get_8s(f, &s->vcore[3]); | ||
| 835 | + qemu_get_8s(f, &s->vcore[4]); | ||
| 836 | + qemu_get_8s(f, &s->dcdc[3]); | ||
| 837 | + qemu_get_8s(f, &s->dcdc[3]); | ||
| 838 | + qemu_get_8s(f, &s->dcdc[3]); | ||
| 839 | + qemu_get_8s(f, &s->ldo[0]); | ||
| 840 | + qemu_get_8s(f, &s->ldo[1]); | ||
| 841 | + qemu_get_8s(f, &s->ldo[2]); | ||
| 842 | + qemu_get_8s(f, &s->ldo[3]); | ||
| 843 | + qemu_get_8s(f, &s->ldo[4]); | ||
| 844 | + qemu_get_8s(f, &s->ldo[5]); | ||
| 845 | + qemu_get_8s(f, &s->ldo[6]); | ||
| 846 | + qemu_get_8s(f, &s->ldo[7]); | ||
| 847 | + qemu_get_8s(f, &s->sleep[0]); | ||
| 848 | + qemu_get_8s(f, &s->sleep[1]); | ||
| 849 | + qemu_get_8s(f, &s->osc); | ||
| 850 | + qemu_get_8s(f, &s->detect); | ||
| 851 | + qemu_get_be16s(f, &s->mask); | ||
| 852 | + qemu_get_be16s(f, &s->status); | ||
| 853 | + qemu_get_8s(f, &s->dir); | ||
| 854 | + qemu_get_8s(f, &s->inputs); | ||
| 855 | + qemu_get_8s(f, &s->outputs); | ||
| 856 | + qemu_get_8s(f, &s->bbsms); | ||
| 857 | + qemu_get_8s(f, &s->pull[0]); | ||
| 858 | + qemu_get_8s(f, &s->pull[1]); | ||
| 859 | + qemu_get_8s(f, &s->pull[2]); | ||
| 860 | + qemu_get_8s(f, &s->pull[3]); | ||
| 861 | + qemu_get_8s(f, &s->mmc_ctrl[0]); | ||
| 862 | + qemu_get_8s(f, &s->mmc_ctrl[1]); | ||
| 863 | + qemu_get_8s(f, &s->mmc_ctrl[2]); | ||
| 864 | + qemu_get_8s(f, &s->mmc_debounce); | ||
| 865 | + qemu_get_8s(f, &s->rtc.ctrl); | ||
| 866 | + qemu_get_be16s(f, &s->rtc.comp); | ||
| 867 | + s->rtc.next = qemu_get_be16(f); | ||
| 868 | + tm_get(f, &s->rtc.new); | ||
| 869 | + tm_get(f, &s->rtc.alm); | ||
| 870 | + s->pwrbtn_state = qemu_get_byte(f); | ||
| 871 | + menelaus_alm_update(s); | ||
| 872 | + menelaus_update(s); | ||
| 873 | + if (s->rtc.ctrl & 1) /* RTC_EN */ | ||
| 874 | + menelaus_rtc_start(s); | ||
| 875 | + | ||
| 876 | + i2c_slave_load(f, &s->i2c); | ||
| 877 | + return 0; | ||
| 878 | +} | ||
| 879 | + | ||
| 880 | +static int menelaus_iid = 0; | ||
| 881 | + | ||
| 882 | +i2c_slave *twl92230_init(i2c_bus *bus, qemu_irq irq) | ||
| 883 | +{ | ||
| 884 | + struct menelaus_s *s = (struct menelaus_s *) | ||
| 885 | + i2c_slave_init(bus, 0, sizeof(struct menelaus_s)); | ||
| 886 | + | ||
| 887 | + s->i2c.event = menelaus_event; | ||
| 888 | + s->i2c.recv = menelaus_rx; | ||
| 889 | + s->i2c.send = menelaus_tx; | ||
| 890 | + | ||
| 891 | + /* TODO: use the qemu gettime functions */ | ||
| 892 | + s->rtc.gettime = localtime_r; | ||
| 893 | + | ||
| 894 | + s->irq = irq; | ||
| 895 | + s->rtc.hz = qemu_new_timer(rt_clock, menelaus_rtc_hz, s); | ||
| 896 | + s->in = qemu_allocate_irqs(menelaus_gpio_set, s, 3); | ||
| 897 | + s->pwrbtn = qemu_allocate_irqs(menelaus_pwrbtn_set, s, 1)[0]; | ||
| 898 | + | ||
| 899 | + menelaus_reset(&s->i2c); | ||
| 900 | + | ||
| 901 | + register_savevm("menelaus", menelaus_iid ++, | ||
| 902 | + 0, menelaus_save, menelaus_load, s); | ||
| 903 | + | ||
| 904 | + return &s->i2c; | ||
| 905 | +} | ||
| 906 | + | ||
| 907 | +qemu_irq *twl92230_gpio_in_get(i2c_slave *i2c) | ||
| 908 | +{ | ||
| 909 | + struct menelaus_s *s = (struct menelaus_s *) i2c; | ||
| 910 | + | ||
| 911 | + return s->in; | ||
| 912 | +} | ||
| 913 | + | ||
| 914 | +void twl92230_gpio_out_set(i2c_slave *i2c, int line, qemu_irq handler) | ||
| 915 | +{ | ||
| 916 | + struct menelaus_s *s = (struct menelaus_s *) i2c; | ||
| 917 | + | ||
| 918 | + if (line >= 3 || line < 0) { | ||
| 919 | + fprintf(stderr, "%s: No GPO line %i\n", __FUNCTION__, line); | ||
| 920 | + exit(-1); | ||
| 921 | + } | ||
| 922 | + s->handler[line] = handler; | ||
| 923 | +} |
vl.c
| @@ -8051,6 +8051,7 @@ static void register_machines(void) | @@ -8051,6 +8051,7 @@ static void register_machines(void) | ||
| 8051 | qemu_register_machine(&borzoipda_machine); | 8051 | qemu_register_machine(&borzoipda_machine); |
| 8052 | qemu_register_machine(&terrierpda_machine); | 8052 | qemu_register_machine(&terrierpda_machine); |
| 8053 | qemu_register_machine(&palmte_machine); | 8053 | qemu_register_machine(&palmte_machine); |
| 8054 | + qemu_register_machine(&n800_machine); | ||
| 8054 | qemu_register_machine(&lm3s811evb_machine); | 8055 | qemu_register_machine(&lm3s811evb_machine); |
| 8055 | qemu_register_machine(&lm3s6965evb_machine); | 8056 | qemu_register_machine(&lm3s6965evb_machine); |
| 8056 | qemu_register_machine(&connex_machine); | 8057 | qemu_register_machine(&connex_machine); |