* [Qemu-devel] [4215] Nokia N800 machine support (ARM).
@ 2008-04-14 21:57 Andrzej Zaborowski
2008-04-15 8:00 ` [Qemu-devel] " John R. Hogerhuis
2008-04-17 20:59 ` consul
0 siblings, 2 replies; 7+ messages in thread
From: Andrzej Zaborowski @ 2008-04-14 21:57 UTC (permalink / raw)
To: qemu-devel
Revision: 4215
http://svn.sv.gnu.org/viewvc/?view=rev&root=qemu&revision=4215
Author: balrog
Date: 2008-04-14 21:57:44 +0000 (Mon, 14 Apr 2008)
Log Message:
-----------
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.
Modified Paths:
--------------
trunk/Makefile
trunk/Makefile.target
trunk/hw/boards.h
trunk/hw/devices.h
trunk/hw/flash.h
trunk/hw/i2c.h
trunk/hw/omap2.c
trunk/vl.c
Added Paths:
-----------
trunk/hw/blizzard.c
trunk/hw/blizzard_template.h
trunk/hw/cbus.c
trunk/hw/nseries.c
trunk/hw/onenand.c
trunk/hw/tmp105.c
trunk/hw/twl92230.c
Modified: trunk/Makefile
===================================================================
--- trunk/Makefile 2008-04-14 21:28:11 UTC (rev 4214)
+++ trunk/Makefile 2008-04-14 21:57:44 UTC (rev 4215)
@@ -51,7 +51,8 @@
OBJS+=irq.o
OBJS+=i2c.o smbus.o smbus_eeprom.o max7310.o max111x.o wm8750.o
-OBJS+=ssd0303.o ssd0323.o ads7846.o stellaris_input.o
+OBJS+=ssd0303.o ssd0323.o ads7846.o stellaris_input.o twl92230.o
+OBJS+=tmp105.o
OBJS+=scsi-disk.o cdrom.o
OBJS+=scsi-generic.o
OBJS+=usb.o usb-hub.o usb-linux.o usb-hid.o usb-msd.o usb-wacom.o usb-serial.o
Modified: trunk/Makefile.target
===================================================================
--- trunk/Makefile.target 2008-04-14 21:28:11 UTC (rev 4214)
+++ trunk/Makefile.target 2008-04-14 21:57:44 UTC (rev 4215)
@@ -612,6 +612,7 @@
OBJS+= omap1.o omap_lcdc.o omap_dma.o omap_clk.o omap_mmc.o omap_i2c.o
OBJS+= omap2.o omap_dss.o
OBJS+= palm.o tsc210x.o
+OBJS+= nseries.o blizzard.o onenand.o vga.o cbus.o
OBJS+= mst_fpga.o mainstone.o
CPPFLAGS += -DHAS_AUDIO
endif
Added: trunk/hw/blizzard.c
===================================================================
--- trunk/hw/blizzard.c (rev 0)
+++ trunk/hw/blizzard.c 2008-04-14 21:57:44 UTC (rev 4215)
@@ -0,0 +1,1001 @@
+/*
+ * Epson S1D13744/S1D13745 (Blizzard/Hailstorm/Tornado) LCD/TV controller.
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ * Written by Andrzej Zaborowski <andrew@openedhand.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include "qemu-common.h"
+#include "sysemu.h"
+#include "console.h"
+#include "devices.h"
+#include "vga_int.h"
+#include "pixel_ops.h"
+
+typedef void (*blizzard_fn_t)(uint8_t *, const uint8_t *, unsigned int);
+
+struct blizzard_s {
+ uint8_t reg;
+ uint32_t addr;
+ int swallow;
+
+ int pll;
+ int pll_range;
+ int pll_ctrl;
+ uint8_t pll_mode;
+ uint8_t clksel;
+ int memenable;
+ int memrefresh;
+ uint8_t timing[3];
+ int priority;
+
+ uint8_t lcd_config;
+ int x;
+ int y;
+ int skipx;
+ int skipy;
+ uint8_t hndp;
+ uint8_t vndp;
+ uint8_t hsync;
+ uint8_t vsync;
+ uint8_t pclk;
+ uint8_t u;
+ uint8_t v;
+ uint8_t yrc[2];
+ int ix[2];
+ int iy[2];
+ int ox[2];
+ int oy[2];
+
+ int enable;
+ int blank;
+ int bpp;
+ int invalidate;
+ int mx[2];
+ int my[2];
+ uint8_t mode;
+ uint8_t effect;
+ uint8_t iformat;
+ uint8_t source;
+ DisplayState *state;
+ blizzard_fn_t *line_fn_tab[2];
+ void *fb;
+
+ uint8_t hssi_config[3];
+ uint8_t tv_config;
+ uint8_t tv_timing[4];
+ uint8_t vbi;
+ uint8_t tv_x;
+ uint8_t tv_y;
+ uint8_t tv_test;
+ uint8_t tv_filter_config;
+ uint8_t tv_filter_idx;
+ uint8_t tv_filter_coeff[0x20];
+ uint8_t border_r;
+ uint8_t border_g;
+ uint8_t border_b;
+ uint8_t gamma_config;
+ uint8_t gamma_idx;
+ uint8_t gamma_lut[0x100];
+ uint8_t matrix_ena;
+ uint8_t matrix_coeff[0x12];
+ uint8_t matrix_r;
+ uint8_t matrix_g;
+ uint8_t matrix_b;
+ uint8_t pm;
+ uint8_t status;
+ uint8_t rgbgpio_dir;
+ uint8_t rgbgpio;
+ uint8_t gpio_dir;
+ uint8_t gpio;
+ uint8_t gpio_edge[2];
+ uint8_t gpio_irq;
+ uint8_t gpio_pdown;
+
+ struct {
+ int x;
+ int y;
+ int dx;
+ int dy;
+ int len;
+ int buflen;
+ void *buf;
+ void *data;
+ uint16_t *ptr;
+ int angle;
+ int pitch;
+ blizzard_fn_t line_fn;
+ } data;
+};
+
+/* Bytes(!) per pixel */
+static const int blizzard_iformat_bpp[0x10] = {
+ 0,
+ 2, /* RGB 5:6:5*/
+ 3, /* RGB 6:6:6 mode 1 */
+ 3, /* RGB 8:8:8 mode 1 */
+ 0, 0,
+ 4, /* RGB 6:6:6 mode 2 */
+ 4, /* RGB 8:8:8 mode 2 */
+ 0, /* YUV 4:2:2 */
+ 0, /* YUV 4:2:0 */
+ 0, 0, 0, 0, 0, 0,
+};
+
+static inline void blizzard_rgb2yuv(int r, int g, int b,
+ int *y, int *u, int *v)
+{
+ *y = 0x10 + ((0x838 * r + 0x1022 * g + 0x322 * b) >> 13);
+ *u = 0x80 + ((0xe0e * b - 0x04c1 * r - 0x94e * g) >> 13);
+ *v = 0x80 + ((0xe0e * r - 0x0bc7 * g - 0x247 * b) >> 13);
+}
+
+static void blizzard_window(struct blizzard_s *s)
+{
+ uint8_t *src, *dst;
+ int bypp[2];
+ int bypl[3];
+ int y;
+ blizzard_fn_t fn = s->data.line_fn;
+
+ if (!fn)
+ return;
+ if (s->mx[0] > s->data.x)
+ s->mx[0] = s->data.x;
+ if (s->my[0] > s->data.y)
+ s->my[0] = s->data.y;
+ if (s->mx[1] < s->data.x + s->data.dx)
+ s->mx[1] = s->data.x + s->data.dx;
+ if (s->my[1] < s->data.y + s->data.dy)
+ s->my[1] = s->data.y + s->data.dy;
+
+ bypp[0] = s->bpp;
+ bypp[1] = (s->state->depth + 7) >> 3;
+ bypl[0] = bypp[0] * s->data.pitch;
+ bypl[1] = bypp[1] * s->x;
+ bypl[2] = bypp[0] * s->data.dx;
+
+ src = s->data.data;
+ dst = s->fb + bypl[1] * s->data.y + bypp[1] * s->data.x;
+ for (y = s->data.dy; y > 0; y --, src += bypl[0], dst += bypl[1])
+ fn(dst, src, bypl[2]);
+}
+
+static int blizzard_transfer_setup(struct blizzard_s *s)
+{
+ if (s->source > 3 || !s->bpp ||
+ s->ix[1] < s->ix[0] || s->iy[1] < s->iy[0])
+ return 0;
+
+ s->data.angle = s->effect & 3;
+ s->data.line_fn = s->line_fn_tab[!!s->data.angle][s->iformat];
+ s->data.x = s->ix[0];
+ s->data.y = s->iy[0];
+ s->data.dx = s->ix[1] - s->ix[0] + 1;
+ s->data.dy = s->iy[1] - s->iy[0] + 1;
+ s->data.len = s->bpp * s->data.dx * s->data.dy;
+ s->data.pitch = s->data.dx;
+ if (s->data.len > s->data.buflen) {
+ s->data.buf = realloc(s->data.buf, s->data.len);
+ s->data.buflen = s->data.len;
+ }
+ s->data.ptr = s->data.buf;
+ s->data.data = s->data.buf;
+ s->data.len /= 2;
+ return 1;
+}
+
+static void blizzard_reset(struct blizzard_s *s)
+{
+ s->reg = 0;
+ s->swallow = 0;
+
+ s->pll = 9;
+ s->pll_range = 1;
+ s->pll_ctrl = 0x14;
+ s->pll_mode = 0x32;
+ s->clksel = 0x00;
+ s->memenable = 0;
+ s->memrefresh = 0x25c;
+ s->timing[0] = 0x3f;
+ s->timing[1] = 0x13;
+ s->timing[2] = 0x21;
+ s->priority = 0;
+
+ s->lcd_config = 0x74;
+ s->x = 8;
+ s->y = 1;
+ s->skipx = 0;
+ s->skipy = 0;
+ s->hndp = 3;
+ s->vndp = 2;
+ s->hsync = 1;
+ s->vsync = 1;
+ s->pclk = 0x80;
+
+ s->ix[0] = 0;
+ s->ix[1] = 0;
+ s->iy[0] = 0;
+ s->iy[1] = 0;
+ s->ox[0] = 0;
+ s->ox[1] = 0;
+ s->oy[0] = 0;
+ s->oy[1] = 0;
+
+ s->yrc[0] = 0x00;
+ s->yrc[1] = 0x30;
+ s->u = 0;
+ s->v = 0;
+
+ s->iformat = 3;
+ s->source = 0;
+ s->bpp = blizzard_iformat_bpp[s->iformat];
+
+ s->hssi_config[0] = 0x00;
+ s->hssi_config[1] = 0x00;
+ s->hssi_config[2] = 0x01;
+ s->tv_config = 0x00;
+ s->tv_timing[0] = 0x00;
+ s->tv_timing[1] = 0x00;
+ s->tv_timing[2] = 0x00;
+ s->tv_timing[3] = 0x00;
+ s->vbi = 0x10;
+ s->tv_x = 0x14;
+ s->tv_y = 0x03;
+ s->tv_test = 0x00;
+ s->tv_filter_config = 0x80;
+ s->tv_filter_idx = 0x00;
+ s->border_r = 0x10;
+ s->border_g = 0x80;
+ s->border_b = 0x80;
+ s->gamma_config = 0x00;
+ s->gamma_idx = 0x00;
+ s->matrix_ena = 0x00;
+ memset(&s->matrix_coeff, 0, sizeof(s->matrix_coeff));
+ s->matrix_r = 0x00;
+ s->matrix_g = 0x00;
+ s->matrix_b = 0x00;
+ s->pm = 0x02;
+ s->status = 0x00;
+ s->rgbgpio_dir = 0x00;
+ s->gpio_dir = 0x00;
+ s->gpio_edge[0] = 0x00;
+ s->gpio_edge[1] = 0x00;
+ s->gpio_irq = 0x00;
+ s->gpio_pdown = 0xff;
+}
+
+static inline void blizzard_invalidate_display(void *opaque) {
+ struct blizzard_s *s = (struct blizzard_s *) opaque;
+
+ s->invalidate = 1;
+}
+
+static uint16_t blizzard_reg_read(void *opaque, uint8_t reg)
+{
+ struct blizzard_s *s = (struct blizzard_s *) opaque;
+
+ switch (reg) {
+ case 0x00: /* Revision Code */
+ return 0xa5;
+
+ case 0x02: /* Configuration Readback */
+ return 0x83; /* Macrovision OK, CNF[2:0] = 3 */
+
+ case 0x04: /* PLL M-Divider */
+ return (s->pll - 1) | (1 << 7);
+ case 0x06: /* PLL Lock Range Control */
+ return s->pll_range;
+ case 0x08: /* PLL Lock Synthesis Control 0 */
+ return s->pll_ctrl & 0xff;
+ case 0x0a: /* PLL Lock Synthesis Control 1 */
+ return s->pll_ctrl >> 8;
+ case 0x0c: /* PLL Mode Control 0 */
+ return s->pll_mode;
+
+ case 0x0e: /* Clock-Source Select */
+ return s->clksel;
+
+ case 0x10: /* Memory Controller Activate */
+ case 0x14: /* Memory Controller Bank 0 Status Flag */
+ return s->memenable;
+
+ case 0x18: /* Auto-Refresh Interval Setting 0 */
+ return s->memrefresh & 0xff;
+ case 0x1a: /* Auto-Refresh Interval Setting 1 */
+ return s->memrefresh >> 8;
+
+ case 0x1c: /* Power-On Sequence Timing Control */
+ return s->timing[0];
+ case 0x1e: /* Timing Control 0 */
+ return s->timing[1];
+ case 0x20: /* Timing Control 1 */
+ return s->timing[2];
+
+ case 0x24: /* Arbitration Priority Control */
+ return s->priority;
+
+ case 0x28: /* LCD Panel Configuration */
+ return s->lcd_config;
+
+ case 0x2a: /* LCD Horizontal Display Width */
+ return s->x >> 3;
+ case 0x2c: /* LCD Horizontal Non-display Period */
+ return s->hndp;
+ case 0x2e: /* LCD Vertical Display Height 0 */
+ return s->y & 0xff;
+ case 0x30: /* LCD Vertical Display Height 1 */
+ return s->y >> 8;
+ case 0x32: /* LCD Vertical Non-display Period */
+ return s->vndp;
+ case 0x34: /* LCD HS Pulse-width */
+ return s->hsync;
+ case 0x36: /* LCd HS Pulse Start Position */
+ return s->skipx >> 3;
+ case 0x38: /* LCD VS Pulse-width */
+ return s->vsync;
+ case 0x3a: /* LCD VS Pulse Start Position */
+ return s->skipy;
+
+ case 0x3c: /* PCLK Polarity */
+ return s->pclk;
+
+ case 0x3e: /* High-speed Serial Interface Tx Configuration Port 0 */
+ return s->hssi_config[0];
+ case 0x40: /* High-speed Serial Interface Tx Configuration Port 1 */
+ return s->hssi_config[1];
+ case 0x42: /* High-speed Serial Interface Tx Mode */
+ return s->hssi_config[2];
+ case 0x44: /* TV Display Configuration */
+ return s->tv_config;
+ case 0x46 ... 0x4c: /* TV Vertical Blanking Interval Data bits */
+ return s->tv_timing[(reg - 0x46) >> 1];
+ case 0x4e: /* VBI: Closed Caption / XDS Control / Status */
+ return s->vbi;
+ case 0x50: /* TV Horizontal Start Position */
+ return s->tv_x;
+ case 0x52: /* TV Vertical Start Position */
+ return s->tv_y;
+ case 0x54: /* TV Test Pattern Setting */
+ return s->tv_test;
+ case 0x56: /* TV Filter Setting */
+ return s->tv_filter_config;
+ case 0x58: /* TV Filter Coefficient Index */
+ return s->tv_filter_idx;
+ case 0x5a: /* TV Filter Coefficient Data */
+ if (s->tv_filter_idx < 0x20)
+ return s->tv_filter_coeff[s->tv_filter_idx ++];
+ return 0;
+
+ case 0x60: /* Input YUV/RGB Translate Mode 0 */
+ return s->yrc[0];
+ case 0x62: /* Input YUV/RGB Translate Mode 1 */
+ return s->yrc[1];
+ case 0x64: /* U Data Fix */
+ return s->u;
+ case 0x66: /* V Data Fix */
+ return s->v;
+
+ case 0x68: /* Display Mode */
+ return s->mode;
+
+ case 0x6a: /* Special Effects */
+ return s->effect;
+
+ case 0x6c: /* Input Window X Start Position 0 */
+ return s->ix[0] & 0xff;
+ case 0x6e: /* Input Window X Start Position 1 */
+ return s->ix[0] >> 3;
+ case 0x70: /* Input Window Y Start Position 0 */
+ return s->ix[0] & 0xff;
+ case 0x72: /* Input Window Y Start Position 1 */
+ return s->ix[0] >> 3;
+ case 0x74: /* Input Window X End Position 0 */
+ return s->ix[1] & 0xff;
+ case 0x76: /* Input Window X End Position 1 */
+ return s->ix[1] >> 3;
+ case 0x78: /* Input Window Y End Position 0 */
+ return s->ix[1] & 0xff;
+ case 0x7a: /* Input Window Y End Position 1 */
+ return s->ix[1] >> 3;
+ case 0x7c: /* Output Window X Start Position 0 */
+ return s->ox[0] & 0xff;
+ case 0x7e: /* Output Window X Start Position 1 */
+ return s->ox[0] >> 3;
+ case 0x80: /* Output Window Y Start Position 0 */
+ return s->oy[0] & 0xff;
+ case 0x82: /* Output Window Y Start Position 1 */
+ return s->oy[0] >> 3;
+ case 0x84: /* Output Window X End Position 0 */
+ return s->ox[1] & 0xff;
+ case 0x86: /* Output Window X End Position 1 */
+ return s->ox[1] >> 3;
+ case 0x88: /* Output Window Y End Position 0 */
+ return s->oy[1] & 0xff;
+ case 0x8a: /* Output Window Y End Position 1 */
+ return s->oy[1] >> 3;
+
+ case 0x8c: /* Input Data Format */
+ return s->iformat;
+ case 0x8e: /* Data Source Select */
+ return s->source;
+ case 0x90: /* Display Memory Data Port */
+ return 0;
+
+ case 0xa8: /* Border Color 0 */
+ return s->border_r;
+ case 0xaa: /* Border Color 1 */
+ return s->border_g;
+ case 0xac: /* Border Color 2 */
+ return s->border_b;
+
+ case 0xb4: /* Gamma Correction Enable */
+ return s->gamma_config;
+ case 0xb6: /* Gamma Correction Table Index */
+ return s->gamma_idx;
+ case 0xb8: /* Gamma Correction Table Data */
+ return s->gamma_lut[s->gamma_idx ++];
+
+ case 0xba: /* 3x3 Matrix Enable */
+ return s->matrix_ena;
+ case 0xbc ... 0xde: /* Coefficient Registers */
+ return s->matrix_coeff[(reg - 0xbc) >> 1];
+ case 0xe0: /* 3x3 Matrix Red Offset */
+ return s->matrix_r;
+ case 0xe2: /* 3x3 Matrix Green Offset */
+ return s->matrix_g;
+ case 0xe4: /* 3x3 Matrix Blue Offset */
+ return s->matrix_b;
+
+ case 0xe6: /* Power-save */
+ return s->pm;
+ case 0xe8: /* Non-display Period Control / Status */
+ return s->status | (1 << 5);
+ case 0xea: /* RGB Interface Control */
+ return s->rgbgpio_dir;
+ case 0xec: /* RGB Interface Status */
+ return s->rgbgpio;
+ case 0xee: /* General-purpose IO Pins Configuration */
+ return s->gpio_dir;
+ case 0xf0: /* General-purpose IO Pins Status / Control */
+ return s->gpio;
+ case 0xf2: /* GPIO Positive Edge Interrupt Trigger */
+ return s->gpio_edge[0];
+ case 0xf4: /* GPIO Negative Edge Interrupt Trigger */
+ return s->gpio_edge[1];
+ case 0xf6: /* GPIO Interrupt Status */
+ return s->gpio_irq;
+ case 0xf8: /* GPIO Pull-down Control */
+ return s->gpio_pdown;
+
+ default:
+ fprintf(stderr, "%s: unknown register %02x\n", __FUNCTION__, reg);
+ return 0;
+ }
+}
+
+static void blizzard_reg_write(void *opaque, uint8_t reg, uint16_t value)
+{
+ struct blizzard_s *s = (struct blizzard_s *) opaque;
+
+ switch (reg) {
+ case 0x04: /* PLL M-Divider */
+ s->pll = (value & 0x3f) + 1;
+ break;
+ case 0x06: /* PLL Lock Range Control */
+ s->pll_range = value & 3;
+ break;
+ case 0x08: /* PLL Lock Synthesis Control 0 */
+ s->pll_ctrl &= 0xf00;
+ s->pll_ctrl |= (value << 0) & 0x0ff;
+ break;
+ case 0x0a: /* PLL Lock Synthesis Control 1 */
+ s->pll_ctrl &= 0x0ff;
+ s->pll_ctrl |= (value << 8) & 0xf00;
+ break;
+ case 0x0c: /* PLL Mode Control 0 */
+ s->pll_mode = value & 0x77;
+ if ((value & 3) == 0 || (value & 3) == 3)
+ fprintf(stderr, "%s: wrong PLL Control bits (%i)\n",
+ __FUNCTION__, value & 3);
+ break;
+
+ case 0x0e: /* Clock-Source Select */
+ s->clksel = value & 0xff;
+ break;
+
+ case 0x10: /* Memory Controller Activate */
+ s->memenable = value & 1;
+ break;
+ case 0x14: /* Memory Controller Bank 0 Status Flag */
+ break;
+
+ case 0x18: /* Auto-Refresh Interval Setting 0 */
+ s->memrefresh &= 0xf00;
+ s->memrefresh |= (value << 0) & 0x0ff;
+ break;
+ case 0x1a: /* Auto-Refresh Interval Setting 1 */
+ s->memrefresh &= 0x0ff;
+ s->memrefresh |= (value << 8) & 0xf00;
+ break;
+
+ case 0x1c: /* Power-On Sequence Timing Control */
+ s->timing[0] = value & 0x7f;
+ break;
+ case 0x1e: /* Timing Control 0 */
+ s->timing[1] = value & 0x17;
+ break;
+ case 0x20: /* Timing Control 1 */
+ s->timing[2] = value & 0x35;
+ break;
+
+ case 0x24: /* Arbitration Priority Control */
+ s->priority = value & 1;
+ break;
+
+ case 0x28: /* LCD Panel Configuration */
+ s->lcd_config = value & 0xff;
+ if (value & (1 << 7))
+ fprintf(stderr, "%s: data swap not supported!\n", __FUNCTION__);
+ break;
+
+ case 0x2a: /* LCD Horizontal Display Width */
+ s->x = value << 3;
+ break;
+ case 0x2c: /* LCD Horizontal Non-display Period */
+ s->hndp = value & 0xff;
+ break;
+ case 0x2e: /* LCD Vertical Display Height 0 */
+ s->y &= 0x300;
+ s->y |= (value << 0) & 0x0ff;
+ break;
+ case 0x30: /* LCD Vertical Display Height 1 */
+ s->y &= 0x0ff;
+ s->y |= (value << 8) & 0x300;
+ break;
+ case 0x32: /* LCD Vertical Non-display Period */
+ s->vndp = value & 0xff;
+ break;
+ case 0x34: /* LCD HS Pulse-width */
+ s->hsync = value & 0xff;
+ break;
+ case 0x36: /* LCD HS Pulse Start Position */
+ s->skipx = value & 0xff;
+ break;
+ case 0x38: /* LCD VS Pulse-width */
+ s->vsync = value & 0xbf;
+ break;
+ case 0x3a: /* LCD VS Pulse Start Position */
+ s->skipy = value & 0xff;
+ break;
+
+ case 0x3c: /* PCLK Polarity */
+ s->pclk = value & 0x82;
+ /* Affects calculation of s->hndp, s->hsync and s->skipx. */
+ break;
+
+ case 0x3e: /* High-speed Serial Interface Tx Configuration Port 0 */
+ s->hssi_config[0] = value;
+ break;
+ case 0x40: /* High-speed Serial Interface Tx Configuration Port 1 */
+ s->hssi_config[1] = value;
+ if (((value >> 4) & 3) == 3)
+ fprintf(stderr, "%s: Illegal active-data-links value\n",
+ __FUNCTION__);
+ break;
+ case 0x42: /* High-speed Serial Interface Tx Mode */
+ s->hssi_config[2] = value & 0xbd;
+ break;
+
+ case 0x44: /* TV Display Configuration */
+ s->tv_config = value & 0xfe;
+ break;
+ case 0x46 ... 0x4c: /* TV Vertical Blanking Interval Data bits 0 */
+ s->tv_timing[(reg - 0x46) >> 1] = value;
+ break;
+ case 0x4e: /* VBI: Closed Caption / XDS Control / Status */
+ s->vbi = value;
+ break;
+ case 0x50: /* TV Horizontal Start Position */
+ s->tv_x = value;
+ break;
+ case 0x52: /* TV Vertical Start Position */
+ s->tv_y = value & 0x7f;
+ break;
+ case 0x54: /* TV Test Pattern Setting */
+ s->tv_test = value;
+ break;
+ case 0x56: /* TV Filter Setting */
+ s->tv_filter_config = value & 0xbf;
+ break;
+ case 0x58: /* TV Filter Coefficient Index */
+ s->tv_filter_idx = value & 0x1f;
+ break;
+ case 0x5a: /* TV Filter Coefficient Data */
+ if (s->tv_filter_idx < 0x20)
+ s->tv_filter_coeff[s->tv_filter_idx ++] = value;
+ break;
+
+ case 0x60: /* Input YUV/RGB Translate Mode 0 */
+ s->yrc[0] = value & 0xb0;
+ break;
+ case 0x62: /* Input YUV/RGB Translate Mode 1 */
+ s->yrc[1] = value & 0x30;
+ break;
+ case 0x64: /* U Data Fix */
+ s->u = value & 0xff;
+ break;
+ case 0x66: /* V Data Fix */
+ s->v = value & 0xff;
+ break;
+
+ case 0x68: /* Display Mode */
+ if ((s->mode ^ value) & 3)
+ s->invalidate = 1;
+ s->mode = value & 0xb7;
+ s->enable = value & 1;
+ s->blank = (value >> 1) & 1;
+ if (value & (1 << 4))
+ fprintf(stderr, "%s: Macrovision enable attempt!\n", __FUNCTION__);
+ break;
+
+ case 0x6a: /* Special Effects */
+ s->effect = value & 0xfb;
+ break;
+
+ case 0x6c: /* Input Window X Start Position 0 */
+ s->ix[0] &= 0x300;
+ s->ix[0] |= (value << 0) & 0x0ff;
+ break;
+ case 0x6e: /* Input Window X Start Position 1 */
+ s->ix[0] &= 0x0ff;
+ s->ix[0] |= (value << 8) & 0x300;
+ break;
+ case 0x70: /* Input Window Y Start Position 0 */
+ s->iy[0] &= 0x300;
+ s->iy[0] |= (value << 0) & 0x0ff;
+ break;
+ case 0x72: /* Input Window Y Start Position 1 */
+ s->iy[0] &= 0x0ff;
+ s->iy[0] |= (value << 8) & 0x300;
+ break;
+ case 0x74: /* Input Window X End Position 0 */
+ s->ix[1] &= 0x300;
+ s->ix[1] |= (value << 0) & 0x0ff;
+ break;
+ case 0x76: /* Input Window X End Position 1 */
+ s->ix[1] &= 0x0ff;
+ s->ix[1] |= (value << 8) & 0x300;
+ break;
+ case 0x78: /* Input Window Y End Position 0 */
+ s->iy[1] &= 0x300;
+ s->iy[1] |= (value << 0) & 0x0ff;
+ break;
+ case 0x7a: /* Input Window Y End Position 1 */
+ s->iy[1] &= 0x0ff;
+ s->iy[1] |= (value << 8) & 0x300;
+ break;
+ case 0x7c: /* Output Window X Start Position 0 */
+ s->ox[0] &= 0x300;
+ s->ox[0] |= (value << 0) & 0x0ff;
+ break;
+ case 0x7e: /* Output Window X Start Position 1 */
+ s->ox[0] &= 0x0ff;
+ s->ox[0] |= (value << 8) & 0x300;
+ break;
+ case 0x80: /* Output Window Y Start Position 0 */
+ s->oy[0] &= 0x300;
+ s->oy[0] |= (value << 0) & 0x0ff;
+ break;
+ case 0x82: /* Output Window Y Start Position 1 */
+ s->oy[0] &= 0x0ff;
+ s->oy[0] |= (value << 8) & 0x300;
+ break;
+ case 0x84: /* Output Window X End Position 0 */
+ s->ox[1] &= 0x300;
+ s->ox[1] |= (value << 0) & 0x0ff;
+ break;
+ case 0x86: /* Output Window X End Position 1 */
+ s->ox[1] &= 0x0ff;
+ s->ox[1] |= (value << 8) & 0x300;
+ break;
+ case 0x88: /* Output Window Y End Position 0 */
+ s->oy[1] &= 0x300;
+ s->oy[1] |= (value << 0) & 0x0ff;
+ break;
+ case 0x8a: /* Output Window Y End Position 1 */
+ s->oy[1] &= 0x0ff;
+ s->oy[1] |= (value << 8) & 0x300;
+ break;
+
+ case 0x8c: /* Input Data Format */
+ s->iformat = value & 0xf;
+ s->bpp = blizzard_iformat_bpp[s->iformat];
+ if (!s->bpp)
+ fprintf(stderr, "%s: Illegal or unsupported input format %x\n",
+ __FUNCTION__, s->iformat);
+ break;
+ case 0x8e: /* Data Source Select */
+ s->source = value & 7;
+ /* Currently all windows will be "destructive overlays". */
+ if ((!(s->effect & (1 << 3)) && (s->ix[0] != s->ox[0] ||
+ s->iy[0] != s->oy[0] ||
+ s->ix[1] != s->ox[1] ||
+ s->iy[1] != s->oy[1])) ||
+ !((s->ix[1] - s->ix[0]) & (s->iy[1] - s->iy[0]) &
+ (s->ox[1] - s->ox[0]) & (s->oy[1] - s->oy[0]) & 1))
+ fprintf(stderr, "%s: Illegal input/output window positions\n",
+ __FUNCTION__);
+
+ blizzard_transfer_setup(s);
+ break;
+
+ case 0x90: /* Display Memory Data Port */
+ if (!s->data.len && !blizzard_transfer_setup(s))
+ break;
+
+ *s->data.ptr ++ = value;
+ if (-- s->data.len == 0)
+ blizzard_window(s);
+ break;
+
+ case 0xa8: /* Border Color 0 */
+ s->border_r = value;
+ break;
+ case 0xaa: /* Border Color 1 */
+ s->border_g = value;
+ break;
+ case 0xac: /* Border Color 2 */
+ s->border_b = value;
+ break;
+
+ case 0xb4: /* Gamma Correction Enable */
+ s->gamma_config = value & 0x87;
+ break;
+ case 0xb6: /* Gamma Correction Table Index */
+ s->gamma_idx = value;
+ break;
+ case 0xb8: /* Gamma Correction Table Data */
+ s->gamma_lut[s->gamma_idx ++] = value;
+ break;
+
+ case 0xba: /* 3x3 Matrix Enable */
+ s->matrix_ena = value & 1;
+ break;
+ case 0xbc ... 0xde: /* Coefficient Registers */
+ s->matrix_coeff[(reg - 0xbc) >> 1] = value & ((reg & 2) ? 0x80 : 0xff);
+ break;
+ case 0xe0: /* 3x3 Matrix Red Offset */
+ s->matrix_r = value;
+ break;
+ case 0xe2: /* 3x3 Matrix Green Offset */
+ s->matrix_g = value;
+ break;
+ case 0xe4: /* 3x3 Matrix Blue Offset */
+ s->matrix_b = value;
+ break;
+
+ case 0xe6: /* Power-save */
+ s->pm = value & 0x83;
+ if (value & s->mode & 1)
+ fprintf(stderr, "%s: The display must be disabled before entering "
+ "Standby Mode\n", __FUNCTION__);
+ break;
+ case 0xe8: /* Non-display Period Control / Status */
+ s->status = value & 0x1b;
+ break;
+ case 0xea: /* RGB Interface Control */
+ s->rgbgpio_dir = value & 0x8f;
+ break;
+ case 0xec: /* RGB Interface Status */
+ s->rgbgpio = value & 0xcf;
+ break;
+ case 0xee: /* General-purpose IO Pins Configuration */
+ s->gpio_dir = value;
+ break;
+ case 0xf0: /* General-purpose IO Pins Status / Control */
+ s->gpio = value;
+ break;
+ case 0xf2: /* GPIO Positive Edge Interrupt Trigger */
+ s->gpio_edge[0] = value;
+ break;
+ case 0xf4: /* GPIO Negative Edge Interrupt Trigger */
+ s->gpio_edge[1] = value;
+ break;
+ case 0xf6: /* GPIO Interrupt Status */
+ s->gpio_irq &= value;
+ break;
+ case 0xf8: /* GPIO Pull-down Control */
+ s->gpio_pdown = value;
+ break;
+
+ default:
+ fprintf(stderr, "%s: unknown register %02x\n", __FUNCTION__, reg);
+ break;
+ }
+}
+
+uint16_t s1d13745_read(void *opaque, int dc)
+{
+ struct blizzard_s *s = (struct blizzard_s *) opaque;
+ uint16_t value = blizzard_reg_read(s, s->reg);
+
+ if (s->swallow -- > 0)
+ return 0;
+ if (dc)
+ s->reg ++;
+
+ return value;
+}
+
+void s1d13745_write(void *opaque, int dc, uint16_t value)
+{
+ struct blizzard_s *s = (struct blizzard_s *) opaque;
+
+ if (s->swallow -- > 0)
+ return;
+ if (dc) {
+ blizzard_reg_write(s, s->reg, value);
+
+ if (s->reg != 0x90 && s->reg != 0x5a && s->reg != 0xb8)
+ s->reg += 2;
+ } else
+ s->reg = value & 0xff;
+}
+
+void s1d13745_write_block(void *opaque, int dc,
+ void *buf, size_t len, int pitch)
+{
+ struct blizzard_s *s = (struct blizzard_s *) opaque;
+
+ while (len > 0) {
+ if (s->reg == 0x90 && dc &&
+ (s->data.len || blizzard_transfer_setup(s)) &&
+ len >= (s->data.len << 1)) {
+ len -= s->data.len << 1;
+ s->data.len = 0;
+ s->data.data = buf;
+ if (pitch)
+ s->data.pitch = pitch;
+ blizzard_window(s);
+ s->data.data = s->data.buf;
+ continue;
+ }
+
+ s1d13745_write(opaque, dc, *(uint16_t *) buf);
+ len -= 2;
+ buf += 2;
+ }
+
+ return;
+}
+
+static void blizzard_update_display(void *opaque)
+{
+ struct blizzard_s *s = (struct blizzard_s *) opaque;
+ int y, bypp, bypl, bwidth;
+ uint8_t *src, *dst;
+
+ if (!s->enable)
+ return;
+
+ if (s->x != s->state->width || s->y != s->state->height) {
+ s->invalidate = 1;
+ dpy_resize(s->state, s->x, s->y);
+ }
+
+ if (s->invalidate) {
+ s->invalidate = 0;
+
+ if (s->blank) {
+ bypp = (s->state->depth + 7) >> 3;
+ memset(s->state->data, 0, bypp * s->x * s->y);
+ return;
+ }
+
+ s->mx[0] = 0;
+ s->mx[1] = s->x;
+ s->my[0] = 0;
+ s->my[1] = s->y;
+ }
+
+ if (s->mx[1] <= s->mx[0])
+ return;
+
+ bypp = (s->state->depth + 7) >> 3;
+ bypl = bypp * s->x;
+ bwidth = bypp * (s->mx[1] - s->mx[0]);
+ y = s->my[0];
+ src = s->fb + bypl * y + bypp * s->mx[0];
+ dst = s->state->data + bypl * y + bypp * s->mx[0];
+ for (; y < s->my[1]; y ++, src += bypl, dst += bypl)
+ memcpy(dst, src, bwidth);
+
+ dpy_update(s->state, s->mx[0], s->my[0],
+ s->mx[1] - s->mx[0], y - s->my[0]);
+
+ s->mx[0] = s->x;
+ s->mx[1] = 0;
+ s->my[0] = s->y;
+ s->my[1] = 0;
+}
+
+static void blizzard_screen_dump(void *opaque, const char *filename) {
+ struct blizzard_s *s = (struct blizzard_s *) opaque;
+
+ blizzard_update_display(opaque);
+ if (s && s->state->data)
+ ppm_save(filename, s->state->data, s->x, s->y, s->state->linesize);
+}
+
+#define DEPTH 8
+#include "blizzard_template.h"
+#define DEPTH 15
+#include "blizzard_template.h"
+#define DEPTH 16
+#include "blizzard_template.h"
+#define DEPTH 24
+#include "blizzard_template.h"
+#define DEPTH 32
+#include "blizzard_template.h"
+
+void *s1d13745_init(qemu_irq gpio_int, DisplayState *ds)
+{
+ struct blizzard_s *s = (struct blizzard_s *) qemu_mallocz(sizeof(*s));
+
+ s->state = ds;
+ s->fb = qemu_malloc(0x180000);
+
+ switch (s->state->depth) {
+ case 0:
+ s->line_fn_tab[0] = s->line_fn_tab[1] =
+ qemu_mallocz(sizeof(blizzard_fn_t) * 0x10);
+ break;
+ case 8:
+ s->line_fn_tab[0] = blizzard_draw_fn_8;
+ s->line_fn_tab[1] = blizzard_draw_fn_r_8;
+ break;
+ case 15:
+ s->line_fn_tab[0] = blizzard_draw_fn_15;
+ s->line_fn_tab[1] = blizzard_draw_fn_r_15;
+ break;
+ case 16:
+ s->line_fn_tab[0] = blizzard_draw_fn_16;
+ s->line_fn_tab[1] = blizzard_draw_fn_r_16;
+ break;
+ case 24:
+ s->line_fn_tab[0] = blizzard_draw_fn_24;
+ s->line_fn_tab[1] = blizzard_draw_fn_r_24;
+ break;
+ case 32:
+ s->line_fn_tab[0] = blizzard_draw_fn_32;
+ s->line_fn_tab[1] = blizzard_draw_fn_r_32;
+ break;
+ default:
+ fprintf(stderr, "%s: Bad color depth\n", __FUNCTION__);
+ exit(1);
+ }
+
+ blizzard_reset(s);
+
+ graphic_console_init(s->state, blizzard_update_display,
+ blizzard_invalidate_display, blizzard_screen_dump,
+ NULL, s);
+
+ return s;
+}
Added: trunk/hw/blizzard_template.h
===================================================================
--- trunk/hw/blizzard_template.h (rev 0)
+++ trunk/hw/blizzard_template.h 2008-04-14 21:57:44 UTC (rev 4215)
@@ -0,0 +1,138 @@
+/*
+ * QEMU Epson S1D13744/S1D13745 templates
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ * Written by Andrzej Zaborowski <andrew@openedhand.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#define SKIP_PIXEL(to) to += deststep
+#if DEPTH == 8
+# define PIXEL_TYPE uint8_t
+# define COPY_PIXEL(to, from) *to = from; SKIP_PIXEL(to)
+# define COPY_PIXEL1(to, from) *to ++ = from
+#elif DEPTH == 15 || DEPTH == 16
+# define PIXEL_TYPE uint16_t
+# define COPY_PIXEL(to, from) *to = from; SKIP_PIXEL(to)
+# define COPY_PIXEL1(to, from) *to ++ = from
+#elif DEPTH == 24
+# define PIXEL_TYPE uint8_t
+# define COPY_PIXEL(to, from) \
+ to[0] = from; to[1] = (from) >> 8; to[2] = (from) >> 16; SKIP_PIXEL(to)
+# define COPY_PIXEL1(to, from) \
+ *to ++ = from; *to ++ = (from) >> 8; *to ++ = (from) >> 16
+#elif DEPTH == 32
+# define PIXEL_TYPE uint32_t
+# define COPY_PIXEL(to, from) *to = from; SKIP_PIXEL(to)
+# define COPY_PIXEL1(to, from) *to ++ = from
+#else
+# error unknown bit depth
+#endif
+
+#ifdef WORDS_BIGENDIAN
+# define SWAP_WORDS 1
+#endif
+
+static void glue(blizzard_draw_line16_, DEPTH)(PIXEL_TYPE *dest,
+ const uint16_t *src, unsigned int width)
+{
+#if !defined(SWAP_WORDS) && DEPTH == 16
+ memcpy(dest, src, width << 1);
+#else
+ uint16_t data;
+ unsigned int r, g, b;
+ const uint16_t *end = (void *) src + width;
+ while (src < end) {
+ data = lduw_raw(src ++);
+ b = (data & 0x1f) << 3;
+ data >>= 5;
+ g = (data & 0x3f) << 2;
+ data >>= 6;
+ r = (data & 0x1f) << 3;
+ data >>= 5;
+ COPY_PIXEL1(dest, glue(rgb_to_pixel, DEPTH)(r, g, b));
+ }
+#endif
+}
+
+static void glue(blizzard_draw_line24mode1_, DEPTH)(PIXEL_TYPE *dest,
+ const uint8_t *src, unsigned int width)
+{
+ /* TODO: check if SDL 24-bit planes are not in the same format and
+ * if so, use memcpy */
+ unsigned int r[2], g[2], b[2];
+ const uint8_t *end = src + width;
+ while (src < end) {
+ g[0] = *src ++;
+ r[0] = *src ++;
+ r[1] = *src ++;
+ b[0] = *src ++;
+ COPY_PIXEL1(dest, glue(rgb_to_pixel, DEPTH)(r[0], g[0], b[0]));
+ b[1] = *src ++;
+ g[1] = *src ++;
+ COPY_PIXEL1(dest, glue(rgb_to_pixel, DEPTH)(r[1], g[1], b[1]));
+ }
+}
+
+static void glue(blizzard_draw_line24mode2_, DEPTH)(PIXEL_TYPE *dest,
+ const uint8_t *src, unsigned int width)
+{
+ unsigned int r, g, b;
+ const uint8_t *end = src + width;
+ while (src < end) {
+ r = *src ++;
+ src ++;
+ b = *src ++;
+ g = *src ++;
+ COPY_PIXEL1(dest, glue(rgb_to_pixel, DEPTH)(r, g, b));
+ }
+}
+
+/* No rotation */
+static blizzard_fn_t glue(blizzard_draw_fn_, DEPTH)[0x10] = {
+ NULL,
+ /* RGB 5:6:5*/
+ (blizzard_fn_t) glue(blizzard_draw_line16_, DEPTH),
+ /* RGB 6:6:6 mode 1 */
+ (blizzard_fn_t) glue(blizzard_draw_line24mode1_, DEPTH),
+ /* RGB 8:8:8 mode 1 */
+ (blizzard_fn_t) glue(blizzard_draw_line24mode1_, DEPTH),
+ NULL, NULL,
+ /* RGB 6:6:6 mode 2 */
+ (blizzard_fn_t) glue(blizzard_draw_line24mode2_, DEPTH),
+ /* RGB 8:8:8 mode 2 */
+ (blizzard_fn_t) glue(blizzard_draw_line24mode2_, DEPTH),
+ /* YUV 4:2:2 */
+ NULL,
+ /* YUV 4:2:0 */
+ NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL,
+};
+
+/* 90deg, 180deg and 270deg rotation */
+static blizzard_fn_t glue(blizzard_draw_fn_r_, DEPTH)[0x10] = {
+ /* TODO */
+ [0 ... 0xf] = NULL,
+};
+
+#undef DEPTH
+#undef SKIP_PIXEL
+#undef COPY_PIXEL
+#undef COPY_PIXEL1
+#undef PIXEL_TYPE
+
+#undef SWAP_WORDS
Modified: trunk/hw/boards.h
===================================================================
--- trunk/hw/boards.h 2008-04-14 21:28:11 UTC (rev 4214)
+++ trunk/hw/boards.h 2008-04-14 21:57:44 UTC (rev 4215)
@@ -81,6 +81,9 @@
/* palm.c */
extern QEMUMachine palmte_machine;
+/* nseries.c */
+extern QEMUMachine n800_machine;
+
/* gumstix.c */
extern QEMUMachine connex_machine;
extern QEMUMachine verdex_machine;
Added: trunk/hw/cbus.c
===================================================================
--- trunk/hw/cbus.c (rev 0)
+++ trunk/hw/cbus.c 2008-04-14 21:57:44 UTC (rev 4215)
@@ -0,0 +1,624 @@
+/*
+ * CBUS three-pin bus and the Retu / Betty / Tahvo / Vilma / Avilma /
+ * Hinku / Vinku / Ahne / Pihi chips used in various Nokia platforms.
+ * Based on reverse-engineering of a linux driver.
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ * Written by Andrzej Zaborowski <andrew@openedhand.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include "qemu-common.h"
+#include "irq.h"
+#include "devices.h"
+#include "sysemu.h"
+
+//#define DEBUG
+
+struct cbus_slave_s;
+struct cbus_priv_s {
+ struct cbus_s cbus;
+
+ int sel;
+ int dat;
+ int clk;
+ int bit;
+ int dir;
+ uint16_t val;
+ qemu_irq dat_out;
+
+ int addr;
+ int reg;
+ int rw;
+ enum {
+ cbus_address,
+ cbus_value,
+ } cycle;
+
+ struct cbus_slave_s *slave[8];
+};
+
+struct cbus_slave_s {
+ void *opaque;
+ void (*io)(void *opaque, int rw, int reg, uint16_t *val);
+ int addr;
+};
+
+static void cbus_io(struct cbus_priv_s *s)
+{
+ if (s->slave[s->addr])
+ s->slave[s->addr]->io(s->slave[s->addr]->opaque,
+ s->rw, s->reg, &s->val);
+ else
+ cpu_abort(cpu_single_env, "%s: bad slave address %i\n",
+ __FUNCTION__, s->addr);
+}
+
+static void cbus_cycle(struct cbus_priv_s *s)
+{
+ switch (s->cycle) {
+ case cbus_address:
+ s->addr = (s->val >> 6) & 7;
+ s->rw = (s->val >> 5) & 1;
+ s->reg = (s->val >> 0) & 0x1f;
+
+ s->cycle = cbus_value;
+ s->bit = 15;
+ s->dir = !s->rw;
+ s->val = 0;
+
+ if (s->rw)
+ cbus_io(s);
+ break;
+
+ case cbus_value:
+ if (!s->rw)
+ cbus_io(s);
+
+ s->cycle = cbus_address;
+ s->bit = 8;
+ s->dir = 1;
+ s->val = 0;
+ break;
+ }
+}
+
+static void cbus_clk(void *opaque, int line, int level)
+{
+ struct cbus_priv_s *s = (struct cbus_priv_s *) opaque;
+
+ if (!s->sel && level && !s->clk) {
+ if (s->dir)
+ s->val |= s->dat << (s->bit --);
+ else
+ qemu_set_irq(s->dat_out, (s->val >> (s->bit --)) & 1);
+
+ if (s->bit < 0)
+ cbus_cycle(s);
+ }
+
+ s->clk = level;
+}
+
+static void cbus_dat(void *opaque, int line, int level)
+{
+ struct cbus_priv_s *s = (struct cbus_priv_s *) opaque;
+
+ s->dat = level;
+}
+
+static void cbus_sel(void *opaque, int line, int level)
+{
+ struct cbus_priv_s *s = (struct cbus_priv_s *) opaque;
+
+ if (!level) {
+ s->dir = 1;
+ s->bit = 8;
+ s->val = 0;
+ }
+
+ s->sel = level;
+}
+
+struct cbus_s *cbus_init(qemu_irq dat)
+{
+ struct cbus_priv_s *s = (struct cbus_priv_s *) qemu_mallocz(sizeof(*s));
+
+ s->dat_out = dat;
+ s->cbus.clk = qemu_allocate_irqs(cbus_clk, s, 1)[0];
+ s->cbus.dat = qemu_allocate_irqs(cbus_dat, s, 1)[0];
+ s->cbus.sel = qemu_allocate_irqs(cbus_sel, s, 1)[0];
+
+ s->sel = 1;
+ s->clk = 0;
+ s->dat = 0;
+
+ return &s->cbus;
+}
+
+void cbus_attach(struct cbus_s *bus, void *slave_opaque)
+{
+ struct cbus_slave_s *slave = (struct cbus_slave_s *) slave_opaque;
+ struct cbus_priv_s *s = (struct cbus_priv_s *) bus;
+
+ s->slave[slave->addr] = slave;
+}
+
+/* Retu/Vilma */
+struct cbus_retu_s {
+ uint16_t irqst;
+ uint16_t irqen;
+ uint16_t cc[2];
+ int channel;
+ uint16_t result[16];
+ uint16_t sample;
+ uint16_t status;
+
+ struct {
+ uint16_t cal;
+ } rtc;
+
+ int is_vilma;
+ qemu_irq irq;
+ struct cbus_slave_s cbus;
+};
+
+static void retu_interrupt_update(struct cbus_retu_s *s)
+{
+ qemu_set_irq(s->irq, s->irqst & ~s->irqen);
+}
+
+#define RETU_REG_ASICR 0x00 /* (RO) ASIC ID & revision */
+#define RETU_REG_IDR 0x01 /* (T) Interrupt ID */
+#define RETU_REG_IMR 0x02 /* (RW) Interrupt mask */
+#define RETU_REG_RTCDSR 0x03 /* (RW) RTC seconds register */
+#define RETU_REG_RTCHMR 0x04 /* (RO) RTC hours and minutes reg */
+#define RETU_REG_RTCHMAR 0x05 /* (RW) RTC hours and minutes set reg */
+#define RETU_REG_RTCCALR 0x06 /* (RW) RTC calibration register */
+#define RETU_REG_ADCR 0x08 /* (RW) ADC result register */
+#define RETU_REG_ADCSCR 0x09 /* (RW) ADC sample control register */
+#define RETU_REG_AFCR 0x0a /* (RW) AFC register */
+#define RETU_REG_ANTIFR 0x0b /* (RW) AntiF register */
+#define RETU_REG_CALIBR 0x0c /* (RW) CalibR register*/
+#define RETU_REG_CCR1 0x0d /* (RW) Common control register 1 */
+#define RETU_REG_CCR2 0x0e /* (RW) Common control register 2 */
+#define RETU_REG_RCTRL_CLR 0x0f /* (T) Regulator clear register */
+#define RETU_REG_RCTRL_SET 0x10 /* (T) Regulator set register */
+#define RETU_REG_TXCR 0x11 /* (RW) TxC register */
+#define RETU_REG_STATUS 0x16 /* (RO) Status register */
+#define RETU_REG_WATCHDOG 0x17 /* (RW) Watchdog register */
+#define RETU_REG_AUDTXR 0x18 /* (RW) Audio Codec Tx register */
+#define RETU_REG_AUDPAR 0x19 /* (RW) AudioPA register */
+#define RETU_REG_AUDRXR1 0x1a /* (RW) Audio receive register 1 */
+#define RETU_REG_AUDRXR2 0x1b /* (RW) Audio receive register 2 */
+#define RETU_REG_SGR1 0x1c /* (RW) */
+#define RETU_REG_SCR1 0x1d /* (RW) */
+#define RETU_REG_SGR2 0x1e /* (RW) */
+#define RETU_REG_SCR2 0x1f /* (RW) */
+
+/* Retu Interrupt sources */
+enum {
+ retu_int_pwr = 0, /* Power button */
+ retu_int_char = 1, /* Charger */
+ retu_int_rtcs = 2, /* Seconds */
+ retu_int_rtcm = 3, /* Minutes */
+ retu_int_rtcd = 4, /* Days */
+ retu_int_rtca = 5, /* Alarm */
+ retu_int_hook = 6, /* Hook */
+ retu_int_head = 7, /* Headset */
+ retu_int_adcs = 8, /* ADC sample */
+};
+
+/* Retu ADC channel wiring */
+enum {
+ retu_adc_bsi = 1, /* BSI */
+ retu_adc_batt_temp = 2, /* Battery temperature */
+ retu_adc_chg_volt = 3, /* Charger voltage */
+ retu_adc_head_det = 4, /* Headset detection */
+ retu_adc_hook_det = 5, /* Hook detection */
+ retu_adc_rf_gp = 6, /* RF GP */
+ retu_adc_tx_det = 7, /* Wideband Tx detection */
+ retu_adc_batt_volt = 8, /* Battery voltage */
+ retu_adc_sens = 10, /* Light sensor */
+ retu_adc_sens_temp = 11, /* Light sensor temperature */
+ retu_adc_bbatt_volt = 12, /* Backup battery voltage */
+ retu_adc_self_temp = 13, /* RETU temperature */
+};
+
+static inline uint16_t retu_read(struct cbus_retu_s *s, int reg)
+{
+#ifdef DEBUG
+ printf("RETU read at %02x\n", reg);
+#endif
+
+ switch (reg) {
+ case RETU_REG_ASICR:
+ return 0x0215 | (s->is_vilma << 7);
+
+ case RETU_REG_IDR: /* TODO: Or is this ffs(s->irqst)? */
+ return s->irqst;
+
+ case RETU_REG_IMR:
+ return s->irqen;
+
+ case RETU_REG_RTCDSR:
+ case RETU_REG_RTCHMR:
+ case RETU_REG_RTCHMAR:
+ /* TODO */
+ return 0x0000;
+
+ case RETU_REG_RTCCALR:
+ return s->rtc.cal;
+
+ case RETU_REG_ADCR:
+ return (s->channel << 10) | s->result[s->channel];
+ case RETU_REG_ADCSCR:
+ return s->sample;
+
+ case RETU_REG_AFCR:
+ case RETU_REG_ANTIFR:
+ case RETU_REG_CALIBR:
+ /* TODO */
+ return 0x0000;
+
+ case RETU_REG_CCR1:
+ return s->cc[0];
+ case RETU_REG_CCR2:
+ return s->cc[1];
+
+ case RETU_REG_RCTRL_CLR:
+ case RETU_REG_RCTRL_SET:
+ case RETU_REG_TXCR:
+ /* TODO */
+ return 0x0000;
+
+ case RETU_REG_STATUS:
+ return s->status;
+
+ case RETU_REG_WATCHDOG:
+ case RETU_REG_AUDTXR:
+ case RETU_REG_AUDPAR:
+ case RETU_REG_AUDRXR1:
+ case RETU_REG_AUDRXR2:
+ case RETU_REG_SGR1:
+ case RETU_REG_SCR1:
+ case RETU_REG_SGR2:
+ case RETU_REG_SCR2:
+ /* TODO */
+ return 0x0000;
+
+ default:
+ cpu_abort(cpu_single_env, "%s: bad register %02x\n",
+ __FUNCTION__, reg);
+ }
+}
+
+static inline void retu_write(struct cbus_retu_s *s, int reg, uint16_t val)
+{
+#ifdef DEBUG
+ printf("RETU write of %04x at %02x\n", val, reg);
+#endif
+
+ switch (reg) {
+ case RETU_REG_IDR:
+ s->irqst ^= val;
+ retu_interrupt_update(s);
+ break;
+
+ case RETU_REG_IMR:
+ s->irqen = val;
+ retu_interrupt_update(s);
+ break;
+
+ case RETU_REG_RTCDSR:
+ case RETU_REG_RTCHMAR:
+ /* TODO */
+ break;
+
+ case RETU_REG_RTCCALR:
+ s->rtc.cal = val;
+ break;
+
+ case RETU_REG_ADCR:
+ s->channel = (val >> 10) & 0xf;
+ s->irqst |= 1 << retu_int_adcs;
+ retu_interrupt_update(s);
+ break;
+ case RETU_REG_ADCSCR:
+ s->sample &= ~val;
+ break;
+
+ case RETU_REG_AFCR:
+ case RETU_REG_ANTIFR:
+ case RETU_REG_CALIBR:
+
+ case RETU_REG_CCR1:
+ s->cc[0] = val;
+ break;
+ case RETU_REG_CCR2:
+ s->cc[1] = val;
+ break;
+
+ case RETU_REG_RCTRL_CLR:
+ case RETU_REG_RCTRL_SET:
+ /* TODO */
+ break;
+
+ case RETU_REG_WATCHDOG:
+ if (val == 0 && (s->cc[0] & 2))
+ qemu_system_shutdown_request();
+ break;
+
+ case RETU_REG_TXCR:
+ case RETU_REG_AUDTXR:
+ case RETU_REG_AUDPAR:
+ case RETU_REG_AUDRXR1:
+ case RETU_REG_AUDRXR2:
+ case RETU_REG_SGR1:
+ case RETU_REG_SCR1:
+ case RETU_REG_SGR2:
+ case RETU_REG_SCR2:
+ /* TODO */
+ break;
+
+ default:
+ cpu_abort(cpu_single_env, "%s: bad register %02x\n",
+ __FUNCTION__, reg);
+ }
+}
+
+static void retu_io(void *opaque, int rw, int reg, uint16_t *val)
+{
+ struct cbus_retu_s *s = (struct cbus_retu_s *) opaque;
+
+ if (rw)
+ *val = retu_read(s, reg);
+ else
+ retu_write(s, reg, *val);
+}
+
+void *retu_init(qemu_irq irq, int vilma)
+{
+ struct cbus_retu_s *s = (struct cbus_retu_s *) qemu_mallocz(sizeof(*s));
+
+ s->irq = irq;
+ s->irqen = 0xffff;
+ s->irqst = 0x0000;
+ s->status = 0x0020;
+ s->is_vilma = !!vilma;
+ s->rtc.cal = 0x01;
+ s->result[retu_adc_bsi] = 0x3c2;
+ s->result[retu_adc_batt_temp] = 0x0fc;
+ s->result[retu_adc_chg_volt] = 0x165;
+ s->result[retu_adc_head_det] = 123;
+ s->result[retu_adc_hook_det] = 1023;
+ s->result[retu_adc_rf_gp] = 0x11;
+ s->result[retu_adc_tx_det] = 0x11;
+ s->result[retu_adc_batt_volt] = 0x250;
+ s->result[retu_adc_sens] = 2;
+ s->result[retu_adc_sens_temp] = 0x11;
+ s->result[retu_adc_bbatt_volt] = 0x3d0;
+ s->result[retu_adc_self_temp] = 0x330;
+
+ s->cbus.opaque = s;
+ s->cbus.io = retu_io;
+ s->cbus.addr = 1;
+
+ return &s->cbus;
+}
+
+void retu_key_event(void *retu, int state)
+{
+ struct cbus_slave_s *slave = (struct cbus_slave_s *) retu;
+ struct cbus_retu_s *s = (struct cbus_retu_s *) slave->opaque;
+
+ s->irqst |= 1 << retu_int_pwr;
+ retu_interrupt_update(s);
+
+ if (state)
+ s->status &= ~(1 << 5);
+ else
+ s->status |= 1 << 5;
+}
+
+void retu_head_event(void *retu, int state)
+{
+ struct cbus_slave_s *slave = (struct cbus_slave_s *) retu;
+ struct cbus_retu_s *s = (struct cbus_retu_s *) slave->opaque;
+
+ if ((s->cc[0] & 0x500) == 0x500) { /* TODO: Which bits? */
+ /* TODO: reissue the interrupt every 100ms or so. */
+ s->irqst |= 1 << retu_int_head;
+ retu_interrupt_update(s);
+ }
+
+ if (state)
+ s->result[retu_adc_head_det] = 50;
+ else
+ s->result[retu_adc_head_det] = 123;
+}
+
+void retu_hook_event(void *retu, int state)
+{
+ struct cbus_slave_s *slave = (struct cbus_slave_s *) retu;
+ struct cbus_retu_s *s = (struct cbus_retu_s *) slave->opaque;
+
+ if ((s->cc[0] & 0x500) == 0x500) {
+ /* TODO: reissue the interrupt every 100ms or so. */
+ s->irqst |= 1 << retu_int_hook;
+ retu_interrupt_update(s);
+ }
+
+ if (state)
+ s->result[retu_adc_hook_det] = 50;
+ else
+ s->result[retu_adc_hook_det] = 123;
+}
+
+/* Tahvo/Betty */
+struct cbus_tahvo_s {
+ uint16_t irqst;
+ uint16_t irqen;
+ uint8_t charger;
+ uint8_t backlight;
+ uint16_t usbr;
+ uint16_t power;
+
+ int is_betty;
+ qemu_irq irq;
+ struct cbus_slave_s cbus;
+};
+
+static void tahvo_interrupt_update(struct cbus_tahvo_s *s)
+{
+ qemu_set_irq(s->irq, s->irqst & ~s->irqen);
+}
+
+#define TAHVO_REG_ASICR 0x00 /* (RO) ASIC ID & revision */
+#define TAHVO_REG_IDR 0x01 /* (T) Interrupt ID */
+#define TAHVO_REG_IDSR 0x02 /* (RO) Interrupt status */
+#define TAHVO_REG_IMR 0x03 /* (RW) Interrupt mask */
+#define TAHVO_REG_CHAPWMR 0x04 /* (RW) Charger PWM */
+#define TAHVO_REG_LEDPWMR 0x05 /* (RW) LED PWM */
+#define TAHVO_REG_USBR 0x06 /* (RW) USB control */
+#define TAHVO_REG_RCR 0x07 /* (RW) Some kind of power management */
+#define TAHVO_REG_CCR1 0x08 /* (RW) Common control register 1 */
+#define TAHVO_REG_CCR2 0x09 /* (RW) Common control register 2 */
+#define TAHVO_REG_TESTR1 0x0a /* (RW) Test register 1 */
+#define TAHVO_REG_TESTR2 0x0b /* (RW) Test register 2 */
+#define TAHVO_REG_NOPR 0x0c /* (RW) Number of periods */
+#define TAHVO_REG_FRR 0x0d /* (RO) FR */
+
+static inline uint16_t tahvo_read(struct cbus_tahvo_s *s, int reg)
+{
+#ifdef DEBUG
+ printf("TAHVO read at %02x\n", reg);
+#endif
+
+ switch (reg) {
+ case TAHVO_REG_ASICR:
+ return 0x0021 | (s->is_betty ? 0x0b00 : 0x0300); /* 22 in N810 */
+
+ case TAHVO_REG_IDR:
+ case TAHVO_REG_IDSR: /* XXX: what does this do? */
+ return s->irqst;
+
+ case TAHVO_REG_IMR:
+ return s->irqen;
+
+ case TAHVO_REG_CHAPWMR:
+ return s->charger;
+
+ case TAHVO_REG_LEDPWMR:
+ return s->backlight;
+
+ case TAHVO_REG_USBR:
+ return s->usbr;
+
+ case TAHVO_REG_RCR:
+ return s->power;
+
+ case TAHVO_REG_CCR1:
+ case TAHVO_REG_CCR2:
+ case TAHVO_REG_TESTR1:
+ case TAHVO_REG_TESTR2:
+ case TAHVO_REG_NOPR:
+ case TAHVO_REG_FRR:
+ return 0x0000;
+
+ default:
+ cpu_abort(cpu_single_env, "%s: bad register %02x\n",
+ __FUNCTION__, reg);
+ }
+}
+
+static inline void tahvo_write(struct cbus_tahvo_s *s, int reg, uint16_t val)
+{
+#ifdef DEBUG
+ printf("TAHVO write of %04x at %02x\n", val, reg);
+#endif
+
+ switch (reg) {
+ case TAHVO_REG_IDR:
+ s->irqst ^= val;
+ tahvo_interrupt_update(s);
+ break;
+
+ case TAHVO_REG_IMR:
+ s->irqen = val;
+ tahvo_interrupt_update(s);
+ break;
+
+ case TAHVO_REG_CHAPWMR:
+ s->charger = val;
+ break;
+
+ case TAHVO_REG_LEDPWMR:
+ if (s->backlight != (val & 0x7f)) {
+ s->backlight = val & 0x7f;
+ printf("%s: LCD backlight now at %i / 127\n",
+ __FUNCTION__, s->backlight);
+ }
+ break;
+
+ case TAHVO_REG_USBR:
+ s->usbr = val;
+ break;
+
+ case TAHVO_REG_RCR:
+ s->power = val;
+ break;
+
+ case TAHVO_REG_CCR1:
+ case TAHVO_REG_CCR2:
+ case TAHVO_REG_TESTR1:
+ case TAHVO_REG_TESTR2:
+ case TAHVO_REG_NOPR:
+ case TAHVO_REG_FRR:
+ break;
+
+ default:
+ cpu_abort(cpu_single_env, "%s: bad register %02x\n",
+ __FUNCTION__, reg);
+ }
+}
+
+static void tahvo_io(void *opaque, int rw, int reg, uint16_t *val)
+{
+ struct cbus_tahvo_s *s = (struct cbus_tahvo_s *) opaque;
+
+ if (rw)
+ *val = tahvo_read(s, reg);
+ else
+ tahvo_write(s, reg, *val);
+}
+
+void *tahvo_init(qemu_irq irq, int betty)
+{
+ struct cbus_tahvo_s *s = (struct cbus_tahvo_s *) qemu_mallocz(sizeof(*s));
+
+ s->irq = irq;
+ s->irqen = 0xffff;
+ s->irqst = 0x0000;
+ s->is_betty = !!betty;
+
+ s->cbus.opaque = s;
+ s->cbus.io = tahvo_io;
+ s->cbus.addr = 2;
+
+ return &s->cbus;
+}
Modified: trunk/hw/devices.h
===================================================================
--- trunk/hw/devices.h 2008-04-14 21:28:11 UTC (rev 4214)
+++ trunk/hw/devices.h 2008-04-14 21:57:44 UTC (rev 4215)
@@ -31,4 +31,25 @@
/* stellaris_input.c */
void stellaris_gamepad_init(int n, qemu_irq *irq, const int *keycode);
+/* blizzard.c */
+void *s1d13745_init(qemu_irq gpio_int, DisplayState *ds);
+void s1d13745_write(void *opaque, int dc, uint16_t value);
+void s1d13745_write_block(void *opaque, int dc,
+ void *buf, size_t len, int pitch);
+uint16_t s1d13745_read(void *opaque, int dc);
+
+/* cbus.c */
+struct cbus_s {
+ qemu_irq clk;
+ qemu_irq dat;
+ qemu_irq sel;
+};
+struct cbus_s *cbus_init(qemu_irq dat_out);
+void cbus_attach(struct cbus_s *bus, void *slave_opaque);
+
+void *retu_init(qemu_irq irq, int vilma);
+void *tahvo_init(qemu_irq irq, int betty);
+
+void retu_key_event(void *retu, int state);
+
#endif
Modified: trunk/hw/flash.h
===================================================================
--- trunk/hw/flash.h 2008-04-14 21:28:11 UTC (rev 4214)
+++ trunk/hw/flash.h 2008-04-14 21:57:44 UTC (rev 4215)
@@ -34,6 +34,11 @@
#define NAND_MFR_HYNIX 0xad
#define NAND_MFR_MICRON 0x2c
+/* onenand.c */
+void onenand_base_update(void *opaque, target_phys_addr_t new);
+void onenand_base_unmap(void *opaque);
+void *onenand_init(uint32_t id, int regshift, qemu_irq irq);
+
/* ecc.c */
struct ecc_state_s {
uint8_t cp; /* Column parity */
Modified: trunk/hw/i2c.h
===================================================================
--- trunk/hw/i2c.h 2008-04-14 21:28:11 UTC (rev 4214)
+++ trunk/hw/i2c.h 2008-04-14 21:57:44 UTC (rev 4215)
@@ -71,4 +71,14 @@
/* ssd0303.c */
void ssd0303_init(DisplayState *ds, i2c_bus *bus, int address);
+/* twl92230.c */
+i2c_slave *twl92230_init(i2c_bus *bus, qemu_irq irq);
+qemu_irq *twl92230_gpio_in_get(i2c_slave *i2c);
+void twl92230_gpio_out_set(i2c_slave *i2c, int line, qemu_irq handler);
+
+/* tmp105.c */
+struct i2c_slave *tmp105_init(i2c_bus *bus, qemu_irq alarm);
+void tmp105_reset(i2c_slave *i2c);
+void tmp105_set(i2c_slave *i2c, int temp);
+
#endif
Added: trunk/hw/nseries.c
===================================================================
--- trunk/hw/nseries.c (rev 0)
+++ trunk/hw/nseries.c 2008-04-14 21:57:44 UTC (rev 4215)
@@ -0,0 +1,918 @@
+/*
+ * Nokia N-series internet tablets.
+ *
+ * Copyright (C) 2007 Nokia Corporation
+ * Written by Andrzej Zaborowski <andrew@openedhand.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include "qemu-common.h"
+#include "sysemu.h"
+#include "omap.h"
+#include "arm-misc.h"
+#include "irq.h"
+#include "console.h"
+#include "boards.h"
+#include "i2c.h"
+#include "devices.h"
+#include "flash.h"
+#include "hw.h"
+
+/* Nokia N8x0 support */
+struct n800_s {
+ struct omap_mpu_state_s *cpu;
+
+ struct rfbi_chip_s blizzard;
+ struct uwire_slave_s *ts;
+ i2c_bus *i2c;
+
+ int keymap[0x80];
+
+ void *retu;
+ void *tahvo;
+};
+
+/* GPIO pins */
+#define N800_TUSB_ENABLE_GPIO 0
+#define N800_MMC2_WP_GPIO 8
+#define N800_UNKNOWN_GPIO0 9 /* out */
+#define N800_UNKNOWN_GPIO1 10 /* out */
+#define N800_CAM_TURN_GPIO 12
+#define N800_BLIZZARD_POWERDOWN_GPIO 15
+#define N800_MMC1_WP_GPIO 23
+#define N8X0_ONENAND_GPIO 26
+#define N800_UNKNOWN_GPIO2 53 /* out */
+#define N8X0_TUSB_INT_GPIO 58
+#define N800_BT_WKUP_GPIO 61
+#define N800_STI_GPIO 62
+#define N8X0_CBUS_SEL_GPIO 64
+#define N8X0_CBUS_CLK_GPIO 65 /* sure? */
+#define N8X0_CBUS_DAT_GPIO 66
+#define N800_WLAN_IRQ_GPIO 87
+#define N800_BT_RESET_GPIO 92
+#define N800_TEA5761_CS_GPIO 93
+#define N800_UNKNOWN_GPIO 94
+#define N800_CAM_ACT_GPIO 95
+#define N800_MMC_CS_GPIO 96
+#define N800_WLAN_PWR_GPIO 97
+#define N8X0_BT_HOST_WKUP_GPIO 98
+#define N800_UNKNOWN_GPIO3 101 /* out */
+#define N810_KB_LOCK_GPIO 102
+#define N800_TSC_TS_GPIO 103
+#define N810_TSC2005_GPIO 106
+#define N800_HEADPHONE_GPIO 107
+#define N8X0_RETU_GPIO 108
+#define N800_TSC_KP_IRQ_GPIO 109
+#define N810_KEYBOARD_GPIO 109
+#define N800_BAT_COVER_GPIO 110
+#define N810_SLIDE_GPIO 110
+#define N8X0_TAHVO_GPIO 111
+#define N800_UNKNOWN_GPIO4 112 /* out */
+#define N810_TSC_RESET_GPIO 118
+#define N800_TSC_RESET_GPIO 119 /* ? */
+#define N8X0_TMP105_GPIO 125
+
+/* Config */
+#define XLDR_LL_UART 1
+
+/* Addresses on the I2C bus */
+#define N8X0_TMP105_ADDR 0x48
+#define N8X0_MENELAUS_ADDR 0x72
+
+/* Chipselects on GPMC NOR interface */
+#define N8X0_ONENAND_CS 0
+#define N8X0_USB_ASYNC_CS 1
+#define N8X0_USB_SYNC_CS 4
+
+static void n800_mmc_cs_cb(void *opaque, int line, int level)
+{
+ /* TODO: this seems to actually be connected to the menelaus, to
+ * which also both MMC slots connect. */
+ omap_mmc_enable((struct omap_mmc_s *) opaque, !level);
+
+ printf("%s: MMC slot %i active\n", __FUNCTION__, level + 1);
+}
+
+static void n800_gpio_setup(struct n800_s *s)
+{
+ qemu_irq *mmc_cs = qemu_allocate_irqs(n800_mmc_cs_cb, s->cpu->mmc, 1);
+ omap2_gpio_out_set(s->cpu->gpif, N800_MMC_CS_GPIO, mmc_cs[0]);
+
+ qemu_irq_lower(omap2_gpio_in_get(s->cpu->gpif, N800_BAT_COVER_GPIO)[0]);
+}
+
+static void n8x0_nand_setup(struct n800_s *s)
+{
+ /* Either ec40xx or ec48xx are OK for the ID */
+ omap_gpmc_attach(s->cpu->gpmc, N8X0_ONENAND_CS, 0, onenand_base_update,
+ onenand_base_unmap,
+ onenand_init(0xec4800, 1,
+ omap2_gpio_in_get(s->cpu->gpif,
+ N8X0_ONENAND_GPIO)[0]));
+}
+
+static void n800_i2c_setup(struct n800_s *s)
+{
+ qemu_irq tmp_irq = omap2_gpio_in_get(s->cpu->gpif, N8X0_TMP105_GPIO)[0];
+
+ /* Attach the CPU on one end of our I2C bus. */
+ s->i2c = omap_i2c_bus(s->cpu->i2c[0]);
+
+ /* Attach a menelaus PM chip */
+ i2c_set_slave_address(
+ twl92230_init(s->i2c,
+ s->cpu->irq[0][OMAP_INT_24XX_SYS_NIRQ]),
+ N8X0_MENELAUS_ADDR);
+
+ /* Attach a TMP105 PM chip (A0 wired to ground) */
+ i2c_set_slave_address(tmp105_init(s->i2c, tmp_irq), N8X0_TMP105_ADDR);
+}
+
+/* Touchscreen and keypad controller */
+#define RETU_KEYCODE 61 /* F3 */
+
+static void n800_key_event(void *opaque, int keycode)
+{
+ struct n800_s *s = (struct n800_s *) opaque;
+ int code = s->keymap[keycode & 0x7f];
+
+ if (code == -1) {
+ if ((keycode & 0x7f) == RETU_KEYCODE)
+ retu_key_event(s->retu, !(keycode & 0x80));
+ return;
+ }
+
+ tsc210x_key_event(s->ts, code, !(keycode & 0x80));
+}
+
+static const int n800_keys[16] = {
+ -1,
+ 72, /* Up */
+ 63, /* Home (F5) */
+ -1,
+ 75, /* Left */
+ 28, /* Enter */
+ 77, /* Right */
+ -1,
+ 1, /* Cycle (ESC) */
+ 80, /* Down */
+ 62, /* Menu (F4) */
+ -1,
+ 66, /* Zoom- (F8) */
+ 64, /* FS (F6) */
+ 65, /* Zoom+ (F7) */
+ -1,
+};
+
+static struct mouse_transform_info_s n800_pointercal = {
+ .x = 800,
+ .y = 480,
+ .a = { 14560, -68, -3455208, -39, -9621, 35152972, 65536 },
+};
+
+static void n800_tsc_setup(struct n800_s *s)
+{
+ int i;
+
+ /* XXX: are the three pins inverted inside the chip between the
+ * tsc and the cpu (N4111)? */
+ qemu_irq penirq = 0; /* NC */
+ qemu_irq kbirq = omap2_gpio_in_get(s->cpu->gpif, N800_TSC_KP_IRQ_GPIO)[0];
+ qemu_irq dav = omap2_gpio_in_get(s->cpu->gpif, N800_TSC_TS_GPIO)[0];
+
+ s->ts = tsc2301_init(penirq, kbirq, dav, 0);
+
+ for (i = 0; i < 0x80; i ++)
+ s->keymap[i] = -1;
+ for (i = 0; i < 0x10; i ++)
+ if (n800_keys[i] >= 0)
+ s->keymap[n800_keys[i]] = i;
+
+ qemu_add_kbd_event_handler(n800_key_event, s);
+
+ tsc210x_set_transform(s->ts, &n800_pointercal);
+}
+
+/* LCD MIPI DBI-C controller (URAL) */
+struct mipid_s {
+ int resp[4];
+ int param[4];
+ int p;
+ int pm;
+ int cmd;
+
+ int sleep;
+ int booster;
+ int te;
+ int selfcheck;
+ int partial;
+ int normal;
+ int vscr;
+ int invert;
+ int onoff;
+ int gamma;
+ uint32_t id;
+};
+
+static void mipid_reset(struct mipid_s *s)
+{
+ if (!s->sleep)
+ fprintf(stderr, "%s: Display off\n", __FUNCTION__);
+
+ s->pm = 0;
+ s->cmd = 0;
+
+ s->sleep = 1;
+ s->booster = 0;
+ s->selfcheck =
+ (1 << 7) | /* Register loading OK. */
+ (1 << 5) | /* The chip is attached. */
+ (1 << 4); /* Display glass still in one piece. */
+ s->te = 0;
+ s->partial = 0;
+ s->normal = 1;
+ s->vscr = 0;
+ s->invert = 0;
+ s->onoff = 1;
+ s->gamma = 0;
+}
+
+static uint32_t mipid_txrx(void *opaque, uint32_t cmd)
+{
+ struct mipid_s *s = (struct mipid_s *) opaque;
+ uint8_t ret;
+
+ if (s->p >= sizeof(s->resp) / sizeof(*s->resp))
+ ret = 0;
+ else
+ ret = s->resp[s->p ++];
+ if (s->pm --> 0)
+ s->param[s->pm] = cmd;
+ else
+ s->cmd = cmd;
+
+ switch (s->cmd) {
+ case 0x00: /* NOP */
+ break;
+
+ case 0x01: /* SWRESET */
+ mipid_reset(s);
+ break;
+
+ case 0x02: /* BSTROFF */
+ s->booster = 0;
+ break;
+ case 0x03: /* BSTRON */
+ s->booster = 1;
+ break;
+
+ case 0x04: /* RDDID */
+ s->p = 0;
+ s->resp[0] = (s->id >> 16) & 0xff;
+ s->resp[1] = (s->id >> 8) & 0xff;
+ s->resp[2] = (s->id >> 0) & 0xff;
+ break;
+
+ case 0x06: /* RD_RED */
+ case 0x07: /* RD_GREEN */
+ /* XXX the bootloader sometimes issues RD_BLUE meaning RDDID so
+ * for the bootloader one needs to change this. */
+ case 0x08: /* RD_BLUE */
+ s->p = 0;
+ /* TODO: return first pixel components */
+ s->resp[0] = 0x01;
+ break;
+
+ case 0x09: /* RDDST */
+ s->p = 0;
+ s->resp[0] = s->booster << 7;
+ s->resp[1] = (5 << 4) | (s->partial << 2) |
+ (s->sleep << 1) | s->normal;
+ s->resp[2] = (s->vscr << 7) | (s->invert << 5) |
+ (s->onoff << 2) | (s->te << 1) | (s->gamma >> 2);
+ s->resp[3] = s->gamma << 6;
+ break;
+
+ case 0x0a: /* RDDPM */
+ s->p = 0;
+ s->resp[0] = (s->onoff << 2) | (s->normal << 3) | (s->sleep << 4) |
+ (s->partial << 5) | (s->sleep << 6) | (s->booster << 7);
+ break;
+ case 0x0b: /* RDDMADCTR */
+ s->p = 0;
+ s->resp[0] = 0;
+ break;
+ case 0x0c: /* RDDCOLMOD */
+ s->p = 0;
+ s->resp[0] = 5; /* 65K colours */
+ break;
+ case 0x0d: /* RDDIM */
+ s->p = 0;
+ s->resp[0] = (s->invert << 5) | (s->vscr << 7) | s->gamma;
+ break;
+ case 0x0e: /* RDDSM */
+ s->p = 0;
+ s->resp[0] = s->te << 7;
+ break;
+ case 0x0f: /* RDDSDR */
+ s->p = 0;
+ s->resp[0] = s->selfcheck;
+ break;
+
+ case 0x10: /* SLPIN */
+ s->sleep = 1;
+ break;
+ case 0x11: /* SLPOUT */
+ s->sleep = 0;
+ s->selfcheck ^= 1 << 6; /* POFF self-diagnosis Ok */
+ break;
+
+ case 0x12: /* PTLON */
+ s->partial = 1;
+ s->normal = 0;
+ s->vscr = 0;
+ break;
+ case 0x13: /* NORON */
+ s->partial = 0;
+ s->normal = 1;
+ s->vscr = 0;
+ break;
+
+ case 0x20: /* INVOFF */
+ s->invert = 0;
+ break;
+ case 0x21: /* INVON */
+ s->invert = 1;
+ break;
+
+ case 0x22: /* APOFF */
+ case 0x23: /* APON */
+ goto bad_cmd;
+
+ case 0x25: /* WRCNTR */
+ if (s->pm < 0)
+ s->pm = 1;
+ goto bad_cmd;
+
+ case 0x26: /* GAMSET */
+ if (!s->pm)
+ s->gamma = ffs(s->param[0] & 0xf) - 1;
+ else if (s->pm < 0)
+ s->pm = 1;
+ break;
+
+ case 0x28: /* DISPOFF */
+ s->onoff = 0;
+ fprintf(stderr, "%s: Display off\n", __FUNCTION__);
+ break;
+ case 0x29: /* DISPON */
+ s->onoff = 1;
+ fprintf(stderr, "%s: Display on\n", __FUNCTION__);
+ break;
+
+ case 0x2a: /* CASET */
+ case 0x2b: /* RASET */
+ case 0x2c: /* RAMWR */
+ case 0x2d: /* RGBSET */
+ case 0x2e: /* RAMRD */
+ case 0x30: /* PTLAR */
+ case 0x33: /* SCRLAR */
+ goto bad_cmd;
+
+ case 0x34: /* TEOFF */
+ s->te = 0;
+ break;
+ case 0x35: /* TEON */
+ if (!s->pm)
+ s->te = 1;
+ else if (s->pm < 0)
+ s->pm = 1;
+ break;
+
+ case 0x36: /* MADCTR */
+ goto bad_cmd;
+
+ case 0x37: /* VSCSAD */
+ s->partial = 0;
+ s->normal = 0;
+ s->vscr = 1;
+ break;
+
+ case 0x38: /* IDMOFF */
+ case 0x39: /* IDMON */
+ case 0x3a: /* COLMOD */
+ goto bad_cmd;
+
+ case 0xb0: /* CLKINT / DISCTL */
+ case 0xb1: /* CLKEXT */
+ if (s->pm < 0)
+ s->pm = 2;
+ break;
+
+ case 0xb4: /* FRMSEL */
+ break;
+
+ case 0xb5: /* FRM8SEL */
+ case 0xb6: /* TMPRNG / INIESC */
+ case 0xb7: /* TMPHIS / NOP2 */
+ case 0xb8: /* TMPREAD / MADCTL */
+ case 0xba: /* DISTCTR */
+ case 0xbb: /* EPVOL */
+ goto bad_cmd;
+
+ case 0xbd: /* Unknown */
+ s->p = 0;
+ s->resp[0] = 0;
+ s->resp[1] = 1;
+ break;
+
+ case 0xc2: /* IFMOD */
+ if (s->pm < 0)
+ s->pm = 2;
+ break;
+
+ case 0xc6: /* PWRCTL */
+ case 0xc7: /* PPWRCTL */
+ case 0xd0: /* EPWROUT */
+ case 0xd1: /* EPWRIN */
+ case 0xd4: /* RDEV */
+ case 0xd5: /* RDRR */
+ goto bad_cmd;
+
+ case 0xda: /* RDID1 */
+ s->p = 0;
+ s->resp[0] = (s->id >> 16) & 0xff;
+ break;
+ case 0xdb: /* RDID2 */
+ s->p = 0;
+ s->resp[0] = (s->id >> 8) & 0xff;
+ break;
+ case 0xdc: /* RDID3 */
+ s->p = 0;
+ s->resp[0] = (s->id >> 0) & 0xff;
+ break;
+
+ default:
+ bad_cmd:
+ fprintf(stderr, "%s: unknown command %02x\n", __FUNCTION__, s->cmd);
+ break;
+ }
+
+ return ret;
+}
+
+static void *mipid_init(void)
+{
+ struct mipid_s *s = (struct mipid_s *) qemu_mallocz(sizeof(*s));
+
+ s->id = 0x838f03;
+ mipid_reset(s);
+
+ return s;
+}
+
+static void n800_spi_setup(struct n800_s *s)
+{
+ void *tsc2301 = s->ts->opaque;
+ void *mipid = mipid_init();
+
+ omap_mcspi_attach(s->cpu->mcspi[0], tsc210x_txrx, tsc2301, 0);
+ omap_mcspi_attach(s->cpu->mcspi[0], mipid_txrx, mipid, 1);
+}
+
+/* This task is normally performed by the bootloader. If we're loading
+ * a kernel directly, we need to enable the Blizzard ourselves. */
+static void n800_dss_init(struct rfbi_chip_s *chip)
+{
+ uint8_t *fb_blank;
+
+ chip->write(chip->opaque, 0, 0x2a); /* LCD Width register */
+ chip->write(chip->opaque, 1, 0x64);
+ chip->write(chip->opaque, 0, 0x2c); /* LCD HNDP register */
+ chip->write(chip->opaque, 1, 0x1e);
+ chip->write(chip->opaque, 0, 0x2e); /* LCD Height 0 register */
+ chip->write(chip->opaque, 1, 0xe0);
+ chip->write(chip->opaque, 0, 0x30); /* LCD Height 1 register */
+ chip->write(chip->opaque, 1, 0x01);
+ chip->write(chip->opaque, 0, 0x32); /* LCD VNDP register */
+ chip->write(chip->opaque, 1, 0x06);
+ chip->write(chip->opaque, 0, 0x68); /* Display Mode register */
+ chip->write(chip->opaque, 1, 1); /* Enable bit */
+
+ chip->write(chip->opaque, 0, 0x6c);
+ chip->write(chip->opaque, 1, 0x00); /* Input X Start Position */
+ chip->write(chip->opaque, 1, 0x00); /* Input X Start Position */
+ chip->write(chip->opaque, 1, 0x00); /* Input Y Start Position */
+ chip->write(chip->opaque, 1, 0x00); /* Input Y Start Position */
+ chip->write(chip->opaque, 1, 0x1f); /* Input X End Position */
+ chip->write(chip->opaque, 1, 0x03); /* Input X End Position */
+ chip->write(chip->opaque, 1, 0xdf); /* Input Y End Position */
+ chip->write(chip->opaque, 1, 0x01); /* Input Y End Position */
+ chip->write(chip->opaque, 1, 0x00); /* Output X Start Position */
+ chip->write(chip->opaque, 1, 0x00); /* Output X Start Position */
+ chip->write(chip->opaque, 1, 0x00); /* Output Y Start Position */
+ chip->write(chip->opaque, 1, 0x00); /* Output Y Start Position */
+ chip->write(chip->opaque, 1, 0x1f); /* Output X End Position */
+ chip->write(chip->opaque, 1, 0x03); /* Output X End Position */
+ chip->write(chip->opaque, 1, 0xdf); /* Output Y End Position */
+ chip->write(chip->opaque, 1, 0x01); /* Output Y End Position */
+ chip->write(chip->opaque, 1, 0x01); /* Input Data Format */
+ chip->write(chip->opaque, 1, 0x01); /* Data Source Select */
+
+ fb_blank = memset(qemu_malloc(800 * 480 * 2), 0xff, 800 * 480 * 2);
+ /* Display Memory Data Port */
+ chip->block(chip->opaque, 1, fb_blank, 800 * 480 * 2, 800);
+ free(fb_blank);
+}
+
+static void n800_dss_setup(struct n800_s *s, DisplayState *ds)
+{
+ s->blizzard.opaque = s1d13745_init(0, ds);
+ s->blizzard.block = s1d13745_write_block;
+ s->blizzard.write = s1d13745_write;
+ s->blizzard.read = s1d13745_read;
+
+ omap_rfbi_attach(s->cpu->dss, 0, &s->blizzard);
+}
+
+static void n800_cbus_setup(struct n800_s *s)
+{
+ qemu_irq dat_out = omap2_gpio_in_get(s->cpu->gpif, N8X0_CBUS_DAT_GPIO)[0];
+ qemu_irq retu_irq = omap2_gpio_in_get(s->cpu->gpif, N8X0_RETU_GPIO)[0];
+ qemu_irq tahvo_irq = omap2_gpio_in_get(s->cpu->gpif, N8X0_TAHVO_GPIO)[0];
+
+ struct cbus_s *cbus = cbus_init(dat_out);
+
+ omap2_gpio_out_set(s->cpu->gpif, N8X0_CBUS_CLK_GPIO, cbus->clk);
+ omap2_gpio_out_set(s->cpu->gpif, N8X0_CBUS_DAT_GPIO, cbus->dat);
+ omap2_gpio_out_set(s->cpu->gpif, N8X0_CBUS_SEL_GPIO, cbus->sel);
+
+ cbus_attach(cbus, s->retu = retu_init(retu_irq, 1));
+ cbus_attach(cbus, s->tahvo = tahvo_init(tahvo_irq, 1));
+}
+
+/* This task is normally performed by the bootloader. If we're loading
+ * a kernel directly, we need to set up GPMC mappings ourselves. */
+static void n800_gpmc_init(struct n800_s *s)
+{
+ uint32_t config7 =
+ (0xf << 8) | /* MASKADDRESS */
+ (1 << 6) | /* CSVALID */
+ (4 << 0); /* BASEADDRESS */
+
+ cpu_physical_memory_write(0x6800a078, /* GPMC_CONFIG7_0 */
+ (void *) &config7, sizeof(config7));
+}
+
+/* Setup sequence done by the bootloader */
+static void n800_boot_init(void *opaque)
+{
+ struct n800_s *s = (struct n800_s *) opaque;
+ uint32_t buf;
+
+ /* PRCM setup */
+#define omap_writel(addr, val) \
+ buf = (val); \
+ cpu_physical_memory_write(addr, (void *) &buf, sizeof(buf))
+
+ omap_writel(0x48008060, 0x41); /* PRCM_CLKSRC_CTRL */
+ omap_writel(0x48008070, 1); /* PRCM_CLKOUT_CTRL */
+ omap_writel(0x48008078, 0); /* PRCM_CLKEMUL_CTRL */
+ omap_writel(0x48008090, 0); /* PRCM_VOLTSETUP */
+ omap_writel(0x48008094, 0); /* PRCM_CLKSSETUP */
+ omap_writel(0x48008098, 0); /* PRCM_POLCTRL */
+ omap_writel(0x48008140, 2); /* CM_CLKSEL_MPU */
+ omap_writel(0x48008148, 0); /* CM_CLKSTCTRL_MPU */
+ omap_writel(0x48008158, 1); /* RM_RSTST_MPU */
+ omap_writel(0x480081c8, 0x15); /* PM_WKDEP_MPU */
+ omap_writel(0x480081d4, 0x1d4); /* PM_EVGENCTRL_MPU */
+ omap_writel(0x480081d8, 0); /* PM_EVEGENONTIM_MPU */
+ omap_writel(0x480081dc, 0); /* PM_EVEGENOFFTIM_MPU */
+ omap_writel(0x480081e0, 0xc); /* PM_PWSTCTRL_MPU */
+ omap_writel(0x48008200, 0x047e7ff7); /* CM_FCLKEN1_CORE */
+ omap_writel(0x48008204, 0x00000004); /* CM_FCLKEN2_CORE */
+ omap_writel(0x48008210, 0x047e7ff1); /* CM_ICLKEN1_CORE */
+ omap_writel(0x48008214, 0x00000004); /* CM_ICLKEN2_CORE */
+ omap_writel(0x4800821c, 0x00000000); /* CM_ICLKEN4_CORE */
+ omap_writel(0x48008230, 0); /* CM_AUTOIDLE1_CORE */
+ omap_writel(0x48008234, 0); /* CM_AUTOIDLE2_CORE */
+ omap_writel(0x48008238, 7); /* CM_AUTOIDLE3_CORE */
+ omap_writel(0x4800823c, 0); /* CM_AUTOIDLE4_CORE */
+ omap_writel(0x48008240, 0x04360626); /* CM_CLKSEL1_CORE */
+ omap_writel(0x48008244, 0x00000014); /* CM_CLKSEL2_CORE */
+ omap_writel(0x48008248, 0); /* CM_CLKSTCTRL_CORE */
+ omap_writel(0x48008300, 0x00000000); /* CM_FCLKEN_GFX */
+ omap_writel(0x48008310, 0x00000000); /* CM_ICLKEN_GFX */
+ omap_writel(0x48008340, 0x00000001); /* CM_CLKSEL_GFX */
+ omap_writel(0x48008400, 0x00000004); /* CM_FCLKEN_WKUP */
+ omap_writel(0x48008410, 0x00000004); /* CM_ICLKEN_WKUP */
+ omap_writel(0x48008440, 0x00000000); /* CM_CLKSEL_WKUP */
+ omap_writel(0x48008500, 0x000000cf); /* CM_CLKEN_PLL */
+ omap_writel(0x48008530, 0x0000000c); /* CM_AUTOIDLE_PLL */
+ omap_writel(0x48008540, /* CM_CLKSEL1_PLL */
+ (0x78 << 12) | (6 << 8));
+ omap_writel(0x48008544, 2); /* CM_CLKSEL2_PLL */
+
+ /* GPMC setup */
+ n800_gpmc_init(s);
+
+ /* Video setup */
+ n800_dss_init(&s->blizzard);
+
+ /* CPU setup */
+ s->cpu->env->regs[15] = s->cpu->env->boot_info->loader_start;
+ s->cpu->env->GE = 0x5;
+}
+
+#define OMAP_TAG_NOKIA_BT 0x4e01
+#define OMAP_TAG_WLAN_CX3110X 0x4e02
+#define OMAP_TAG_CBUS 0x4e03
+#define OMAP_TAG_EM_ASIC_BB5 0x4e04
+
+static int n800_atag_setup(struct arm_boot_info *info, void *p)
+{
+ uint8_t *b;
+ uint16_t *w;
+ uint32_t *l;
+
+ w = p;
+
+ stw_raw(w ++, OMAP_TAG_UART); /* u16 tag */
+ stw_raw(w ++, 4); /* u16 len */
+ stw_raw(w ++, (1 << 2) | (1 << 1) | (1 << 0)); /* uint enabled_uarts */
+ w ++;
+
+ stw_raw(w ++, OMAP_TAG_EM_ASIC_BB5); /* u16 tag */
+ stw_raw(w ++, 4); /* u16 len */
+ stw_raw(w ++, N8X0_RETU_GPIO); /* s16 retu_irq_gpio */
+ stw_raw(w ++, N8X0_TAHVO_GPIO); /* s16 tahvo_irq_gpio */
+
+ stw_raw(w ++, OMAP_TAG_CBUS); /* u16 tag */
+ stw_raw(w ++, 8); /* u16 len */
+ stw_raw(w ++, N8X0_CBUS_CLK_GPIO); /* s16 clk_gpio */
+ stw_raw(w ++, N8X0_CBUS_DAT_GPIO); /* s16 dat_gpio */
+ stw_raw(w ++, N8X0_CBUS_SEL_GPIO); /* s16 sel_gpio */
+ w ++;
+
+ stw_raw(w ++, OMAP_TAG_GPIO_SWITCH); /* u16 tag */
+ stw_raw(w ++, 20); /* u16 len */
+ strcpy((void *) w, "bat_cover"); /* char name[12] */
+ w += 6;
+ stw_raw(w ++, N800_BAT_COVER_GPIO); /* u16 gpio */
+ stw_raw(w ++, 0x01);
+ stw_raw(w ++, 0);
+ stw_raw(w ++, 0);
+
+ stw_raw(w ++, OMAP_TAG_GPIO_SWITCH); /* u16 tag */
+ stw_raw(w ++, 20); /* u16 len */
+ strcpy((void *) w, "cam_act"); /* char name[12] */
+ w += 6;
+ stw_raw(w ++, N800_CAM_ACT_GPIO); /* u16 gpio */
+ stw_raw(w ++, 0x20);
+ stw_raw(w ++, 0);
+ stw_raw(w ++, 0);
+
+ stw_raw(w ++, OMAP_TAG_GPIO_SWITCH); /* u16 tag */
+ stw_raw(w ++, 20); /* u16 len */
+ strcpy((void *) w, "cam_turn"); /* char name[12] */
+ w += 6;
+ stw_raw(w ++, N800_CAM_TURN_GPIO); /* u16 gpio */
+ stw_raw(w ++, 0x21);
+ stw_raw(w ++, 0);
+ stw_raw(w ++, 0);
+
+ stw_raw(w ++, OMAP_TAG_GPIO_SWITCH); /* u16 tag */
+ stw_raw(w ++, 20); /* u16 len */
+ strcpy((void *) w, "headphone"); /* char name[12] */
+ w += 6;
+ stw_raw(w ++, N800_HEADPHONE_GPIO); /* u16 gpio */
+ stw_raw(w ++, 0x11);
+ stw_raw(w ++, 0);
+ stw_raw(w ++, 0);
+
+ stw_raw(w ++, OMAP_TAG_NOKIA_BT); /* u16 tag */
+ stw_raw(w ++, 12); /* u16 len */
+ b = (void *) w;
+ stb_raw(b ++, 0x01); /* u8 chip_type (CSR) */
+ stb_raw(b ++, N800_BT_WKUP_GPIO); /* u8 bt_wakeup_gpio */
+ stb_raw(b ++, N8X0_BT_HOST_WKUP_GPIO); /* u8 host_wakeup_gpio */
+ stb_raw(b ++, N800_BT_RESET_GPIO); /* u8 reset_gpio */
+ stb_raw(b ++, 1); /* u8 bt_uart */
+ memset(b, 0, 6); /* u8 bd_addr[6] */
+ b += 6;
+ stb_raw(b ++, 0x02); /* u8 bt_sysclk (38.4) */
+ w = (void *) b;
+
+ stw_raw(w ++, OMAP_TAG_WLAN_CX3110X); /* u16 tag */
+ stw_raw(w ++, 8); /* u16 len */
+ stw_raw(w ++, 0x25); /* u8 chip_type */
+ stw_raw(w ++, N800_WLAN_PWR_GPIO); /* s16 power_gpio */
+ stw_raw(w ++, N800_WLAN_IRQ_GPIO); /* s16 irq_gpio */
+ stw_raw(w ++, -1); /* s16 spi_cs_gpio */
+
+ stw_raw(w ++, OMAP_TAG_MMC); /* u16 tag */
+ stw_raw(w ++, 16); /* u16 len */
+ stw_raw(w ++, 0xf); /* unsigned flags */
+ stw_raw(w ++, -1); /* s16 power_pin */
+ stw_raw(w ++, -1); /* s16 switch_pin */
+ stw_raw(w ++, -1); /* s16 wp_pin */
+ stw_raw(w ++, 0); /* unsigned flags */
+ stw_raw(w ++, 0); /* s16 power_pin */
+ stw_raw(w ++, 0); /* s16 switch_pin */
+ stw_raw(w ++, 0); /* s16 wp_pin */
+
+ stw_raw(w ++, OMAP_TAG_TEA5761); /* u16 tag */
+ stw_raw(w ++, 4); /* u16 len */
+ stw_raw(w ++, N800_TEA5761_CS_GPIO); /* u16 enable_gpio */
+ w ++;
+
+ stw_raw(w ++, OMAP_TAG_PARTITION); /* u16 tag */
+ stw_raw(w ++, 28); /* u16 len */
+ strcpy((void *) w, "bootloader"); /* char name[16] */
+ l = (void *) (w + 8);
+ stl_raw(l ++, 0x00020000); /* unsigned int size */
+ stl_raw(l ++, 0x00000000); /* unsigned int offset */
+ stl_raw(l ++, 0x3); /* unsigned int mask_flags */
+ w = (void *) l;
+
+ stw_raw(w ++, OMAP_TAG_PARTITION); /* u16 tag */
+ stw_raw(w ++, 28); /* u16 len */
+ strcpy((void *) w, "config"); /* char name[16] */
+ l = (void *) (w + 8);
+ stl_raw(l ++, 0x00060000); /* unsigned int size */
+ stl_raw(l ++, 0x00020000); /* unsigned int offset */
+ stl_raw(l ++, 0x0); /* unsigned int mask_flags */
+ w = (void *) l;
+
+ stw_raw(w ++, OMAP_TAG_PARTITION); /* u16 tag */
+ stw_raw(w ++, 28); /* u16 len */
+ strcpy((void *) w, "kernel"); /* char name[16] */
+ l = (void *) (w + 8);
+ stl_raw(l ++, 0x00200000); /* unsigned int size */
+ stl_raw(l ++, 0x00080000); /* unsigned int offset */
+ stl_raw(l ++, 0x0); /* unsigned int mask_flags */
+ w = (void *) l;
+
+ stw_raw(w ++, OMAP_TAG_PARTITION); /* u16 tag */
+ stw_raw(w ++, 28); /* u16 len */
+ strcpy((void *) w, "initfs"); /* char name[16] */
+ l = (void *) (w + 8);
+ stl_raw(l ++, 0x00200000); /* unsigned int size */
+ stl_raw(l ++, 0x00280000); /* unsigned int offset */
+ stl_raw(l ++, 0x3); /* unsigned int mask_flags */
+ w = (void *) l;
+
+ stw_raw(w ++, OMAP_TAG_PARTITION); /* u16 tag */
+ stw_raw(w ++, 28); /* u16 len */
+ strcpy((void *) w, "rootfs"); /* char name[16] */
+ l = (void *) (w + 8);
+ stl_raw(l ++, 0x0fb80000); /* unsigned int size */
+ stl_raw(l ++, 0x00480000); /* unsigned int offset */
+ stl_raw(l ++, 0x3); /* unsigned int mask_flags */
+ w = (void *) l;
+
+ stw_raw(w ++, OMAP_TAG_BOOT_REASON); /* u16 tag */
+ stw_raw(w ++, 12); /* u16 len */
+#if 0
+ strcpy((void *) w, "por"); /* char reason_str[12] */
+ strcpy((void *) w, "charger"); /* char reason_str[12] */
+ strcpy((void *) w, "32wd_to"); /* char reason_str[12] */
+ strcpy((void *) w, "sw_rst"); /* char reason_str[12] */
+ strcpy((void *) w, "mbus"); /* char reason_str[12] */
+ strcpy((void *) w, "unknown"); /* char reason_str[12] */
+ strcpy((void *) w, "swdg_to"); /* char reason_str[12] */
+ strcpy((void *) w, "sec_vio"); /* char reason_str[12] */
+ strcpy((void *) w, "pwr_key"); /* char reason_str[12] */
+ strcpy((void *) w, "rtc_alarm"); /* char reason_str[12] */
+#else
+ strcpy((void *) w, "pwr_key"); /* char reason_str[12] */
+#endif
+ w += 6;
+
+#if 0 /* N810 */
+ stw_raw(w ++, OMAP_TAG_VERSION_STR); /* u16 tag */
+ stw_raw(w ++, 24); /* u16 len */
+ strcpy((void *) w, "product"); /* char component[12] */
+ w += 6;
+ strcpy((void *) w, "RX-44"); /* char version[12] */
+ w += 6;
+
+ stw_raw(w ++, OMAP_TAG_VERSION_STR); /* u16 tag */
+ stw_raw(w ++, 24); /* u16 len */
+ strcpy((void *) w, "hw-build"); /* char component[12] */
+ w += 6;
+ strcpy((void *) w, "QEMU"); /* char version[12] */
+ w += 6;
+
+ stw_raw(w ++, OMAP_TAG_VERSION_STR); /* u16 tag */
+ stw_raw(w ++, 24); /* u16 len */
+ strcpy((void *) w, "nolo"); /* char component[12] */
+ w += 6;
+ strcpy((void *) w, "1.1.10-qemu"); /* char version[12] */
+ w += 6;
+#else
+ stw_raw(w ++, OMAP_TAG_VERSION_STR); /* u16 tag */
+ stw_raw(w ++, 24); /* u16 len */
+ strcpy((void *) w, "product"); /* char component[12] */
+ w += 6;
+ strcpy((void *) w, "RX-34"); /* char version[12] */
+ w += 6;
+
+ stw_raw(w ++, OMAP_TAG_VERSION_STR); /* u16 tag */
+ stw_raw(w ++, 24); /* u16 len */
+ strcpy((void *) w, "hw-build"); /* char component[12] */
+ w += 6;
+ strcpy((void *) w, "QEMU"); /* char version[12] */
+ w += 6;
+
+ stw_raw(w ++, OMAP_TAG_VERSION_STR); /* u16 tag */
+ stw_raw(w ++, 24); /* u16 len */
+ strcpy((void *) w, "nolo"); /* char component[12] */
+ w += 6;
+ strcpy((void *) w, "1.1.6-qemu"); /* char version[12] */
+ w += 6;
+#endif
+
+ stw_raw(w ++, OMAP_TAG_LCD); /* u16 tag */
+ stw_raw(w ++, 36); /* u16 len */
+ strcpy((void *) w, "QEMU LCD panel"); /* char panel_name[16] */
+ w += 8;
+ strcpy((void *) w, "blizzard"); /* char ctrl_name[16] */
+ w += 8;
+ stw_raw(w ++, 5); /* TODO s16 nreset_gpio */
+ stw_raw(w ++, 16); /* u8 data_lines */
+
+ return (void *) w - p;
+}
+
+static struct arm_boot_info n800_binfo = {
+ .loader_start = OMAP2_Q2_BASE,
+ /* Actually two chips of 0x4000000 bytes each */
+ .ram_size = 0x08000000,
+ .board_id = 0x4f7,
+ .atag_board = n800_atag_setup,
+};
+
+static void n800_init(int ram_size, int vga_ram_size,
+ const char *boot_device, DisplayState *ds,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ struct n800_s *s = (struct n800_s *) qemu_mallocz(sizeof(*s));
+ int sdram_size = n800_binfo.ram_size;
+ int onenandram_size = 0x00010000;
+
+ if (ram_size < sdram_size + onenandram_size + OMAP242X_SRAM_SIZE) {
+ fprintf(stderr, "This architecture uses %i bytes of memory\n",
+ sdram_size + onenandram_size + OMAP242X_SRAM_SIZE);
+ exit(1);
+ }
+
+ s->cpu = omap2420_mpu_init(sdram_size, NULL, cpu_model);
+
+ n800_gpio_setup(s);
+ n8x0_nand_setup(s);
+ n800_i2c_setup(s);
+ n800_tsc_setup(s);
+ n800_spi_setup(s);
+ n800_dss_setup(s, ds);
+ n800_cbus_setup(s);
+
+ /* Setup initial (reset) machine state */
+
+ /* Start at the OneNAND bootloader. */
+ s->cpu->env->regs[15] = 0;
+
+ if (kernel_filename) {
+ /* Or at the linux loader. */
+ n800_binfo.kernel_filename = kernel_filename;
+ n800_binfo.kernel_cmdline = kernel_cmdline;
+ n800_binfo.initrd_filename = initrd_filename;
+ arm_load_kernel(s->cpu->env, &n800_binfo);
+
+ qemu_register_reset(n800_boot_init, s);
+ n800_boot_init(s);
+ }
+
+ dpy_resize(ds, 800, 480);
+}
+
+QEMUMachine n800_machine = {
+ "n800",
+ "Nokia N800 aka. RX-34 tablet (OMAP2420)",
+ n800_init,
+};
Modified: trunk/hw/omap2.c
===================================================================
--- trunk/hw/omap2.c 2008-04-14 21:28:11 UTC (rev 4214)
+++ trunk/hw/omap2.c 2008-04-14 21:57:44 UTC (rev 4215)
@@ -3496,7 +3496,7 @@
{
struct omap_mpu_state_s *s = (struct omap_mpu_state_s *)
qemu_mallocz(sizeof(struct omap_mpu_state_s));
- ram_addr_t sram_base, q3_base;
+ ram_addr_t sram_base, q2_base;
qemu_irq *cpu_irq;
qemu_irq dma_irqs[4];
omap_clk gpio_clks[4];
@@ -3520,7 +3520,7 @@
/* Memory-mapped stuff */
cpu_register_physical_memory(OMAP2_Q2_BASE, s->sdram_size,
- (q3_base = qemu_ram_alloc(s->sdram_size)) | IO_MEM_RAM);
+ (q2_base = qemu_ram_alloc(s->sdram_size)) | IO_MEM_RAM);
cpu_register_physical_memory(OMAP2_SRAM_BASE, s->sram_size,
(sram_base = qemu_ram_alloc(s->sram_size)) | IO_MEM_RAM);
Added: trunk/hw/onenand.c
===================================================================
--- trunk/hw/onenand.c (rev 0)
+++ trunk/hw/onenand.c 2008-04-14 21:57:44 UTC (rev 4215)
@@ -0,0 +1,642 @@
+/*
+ * OneNAND flash memories emulation.
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ * Written by Andrzej Zaborowski <andrew@openedhand.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include "qemu-common.h"
+#include "flash.h"
+#include "irq.h"
+#include "sysemu.h"
+#include "block.h"
+
+/* 11 for 2kB-page OneNAND ("2nd generation") and 10 for 1kB-page chips */
+#define PAGE_SHIFT 11
+
+/* Fixed */
+#define BLOCK_SHIFT (PAGE_SHIFT + 6)
+
+struct onenand_s {
+ uint32_t id;
+ int shift;
+ target_phys_addr_t base;
+ qemu_irq intr;
+ qemu_irq rdy;
+ BlockDriverState *bdrv;
+ BlockDriverState *bdrv_cur;
+ uint8_t *image;
+ uint8_t *otp;
+ uint8_t *current;
+ ram_addr_t ram;
+ uint8_t *boot[2];
+ uint8_t *data[2][2];
+ int iomemtype;
+ int cycle;
+ int otpmode;
+
+ uint16_t addr[8];
+ uint16_t unladdr[8];
+ int bufaddr;
+ int count;
+ uint16_t command;
+ uint16_t config[2];
+ uint16_t status;
+ uint16_t intstatus;
+ uint16_t wpstatus;
+
+ struct ecc_state_s ecc;
+
+ int density_mask;
+ int secs;
+ int secs_cur;
+ int blocks;
+ uint8_t *blockwp;
+};
+
+enum {
+ ONEN_BUF_BLOCK = 0,
+ ONEN_BUF_BLOCK2 = 1,
+ ONEN_BUF_DEST_BLOCK = 2,
+ ONEN_BUF_DEST_PAGE = 3,
+ ONEN_BUF_PAGE = 7,
+};
+
+enum {
+ ONEN_ERR_CMD = 1 << 10,
+ ONEN_ERR_ERASE = 1 << 11,
+ ONEN_ERR_PROG = 1 << 12,
+ ONEN_ERR_LOAD = 1 << 13,
+};
+
+enum {
+ ONEN_INT_RESET = 1 << 4,
+ ONEN_INT_ERASE = 1 << 5,
+ ONEN_INT_PROG = 1 << 6,
+ ONEN_INT_LOAD = 1 << 7,
+ ONEN_INT = 1 << 15,
+};
+
+enum {
+ ONEN_LOCK_LOCKTIGHTEN = 1 << 0,
+ ONEN_LOCK_LOCKED = 1 << 1,
+ ONEN_LOCK_UNLOCKED = 1 << 2,
+};
+
+void onenand_base_update(void *opaque, target_phys_addr_t new)
+{
+ struct onenand_s *s = (struct onenand_s *) opaque;
+
+ s->base = new;
+
+ /* XXX: We should use IO_MEM_ROMD but we broke it earlier...
+ * Both 0x0000 ... 0x01ff and 0x8000 ... 0x800f can be used to
+ * write boot commands. Also take note of the BWPS bit. */
+ cpu_register_physical_memory(s->base + (0x0000 << s->shift),
+ 0x0200 << s->shift, s->iomemtype);
+ cpu_register_physical_memory(s->base + (0x0200 << s->shift),
+ 0xbe00 << s->shift,
+ (s->ram +(0x0200 << s->shift)) | IO_MEM_RAM);
+ if (s->iomemtype)
+ cpu_register_physical_memory(s->base + (0xc000 << s->shift),
+ 0x4000 << s->shift, s->iomemtype);
+}
+
+void onenand_base_unmap(void *opaque)
+{
+ struct onenand_s *s = (struct onenand_s *) opaque;
+
+ cpu_register_physical_memory(s->base,
+ 0x10000 << s->shift, IO_MEM_UNASSIGNED);
+}
+
+static void onenand_intr_update(struct onenand_s *s)
+{
+ qemu_set_irq(s->intr, ((s->intstatus >> 15) ^ (~s->config[0] >> 6)) & 1);
+}
+
+/* Hot reset (Reset OneNAND command) or warm reset (RP pin low) */
+static void onenand_reset(struct onenand_s *s, int cold)
+{
+ memset(&s->addr, 0, sizeof(s->addr));
+ s->command = 0;
+ s->count = 1;
+ s->bufaddr = 0;
+ s->config[0] = 0x40c0;
+ s->config[1] = 0x0000;
+ onenand_intr_update(s);
+ qemu_irq_raise(s->rdy);
+ s->status = 0x0000;
+ s->intstatus = cold ? 0x8080 : 0x8010;
+ s->unladdr[0] = 0;
+ s->unladdr[1] = 0;
+ s->wpstatus = 0x0002;
+ s->cycle = 0;
+ s->otpmode = 0;
+ s->bdrv_cur = s->bdrv;
+ s->current = s->image;
+ s->secs_cur = s->secs;
+
+ if (cold) {
+ /* Lock the whole flash */
+ memset(s->blockwp, ONEN_LOCK_LOCKED, s->blocks);
+
+ if (s->bdrv && bdrv_read(s->bdrv, 0, s->boot[0], 8) < 0)
+ cpu_abort(cpu_single_env, "%s: Loading the BootRAM failed.\n",
+ __FUNCTION__);
+ }
+}
+
+static inline int onenand_load_main(struct onenand_s *s, int sec, int secn,
+ void *dest)
+{
+ if (s->bdrv_cur)
+ return bdrv_read(s->bdrv_cur, sec, dest, secn) < 0;
+ else if (sec + secn > s->secs_cur)
+ return 1;
+
+ memcpy(dest, s->current + (sec << 9), secn << 9);
+
+ return 0;
+}
+
+static inline int onenand_prog_main(struct onenand_s *s, int sec, int secn,
+ void *src)
+{
+ if (s->bdrv_cur)
+ return bdrv_write(s->bdrv_cur, sec, src, secn) < 0;
+ else if (sec + secn > s->secs_cur)
+ return 1;
+
+ memcpy(s->current + (sec << 9), src, secn << 9);
+
+ return 0;
+}
+
+static inline int onenand_load_spare(struct onenand_s *s, int sec, int secn,
+ void *dest)
+{
+ uint8_t buf[512];
+
+ if (s->bdrv_cur) {
+ if (bdrv_read(s->bdrv_cur, s->secs_cur + (sec >> 5), buf, 1) < 0)
+ return 1;
+ memcpy(dest, buf + ((sec & 31) << 4), secn << 4);
+ } else if (sec + secn > s->secs_cur)
+ return 1;
+ else
+ memcpy(dest, s->current + (s->secs_cur << 9) + (sec << 4), secn << 4);
+
+ return 0;
+}
+
+static inline int onenand_prog_spare(struct onenand_s *s, int sec, int secn,
+ void *src)
+{
+ uint8_t buf[512];
+
+ if (s->bdrv_cur) {
+ if (bdrv_read(s->bdrv_cur, s->secs_cur + (sec >> 5), buf, 1) < 0)
+ return 1;
+ memcpy(buf + ((sec & 31) << 4), src, secn << 4);
+ return bdrv_write(s->bdrv_cur, s->secs_cur + (sec >> 5), buf, 1) < 0;
+ } else if (sec + secn > s->secs_cur)
+ return 1;
+
+ memcpy(s->current + (s->secs_cur << 9) + (sec << 4), src, secn << 4);
+
+ return 0;
+}
+
+static inline int onenand_erase(struct onenand_s *s, int sec, int num)
+{
+ /* TODO: optimise */
+ uint8_t buf[512];
+
+ memset(buf, 0xff, sizeof(buf));
+ for (; num > 0; num --, sec ++) {
+ if (onenand_prog_main(s, sec, 1, buf))
+ return 1;
+ if (onenand_prog_spare(s, sec, 1, buf))
+ return 1;
+ }
+
+ return 0;
+}
+
+static void onenand_command(struct onenand_s *s, int cmd)
+{
+ int b;
+ int sec;
+ void *buf;
+#define SETADDR(block, page) \
+ sec = (s->addr[page] & 3) + \
+ ((((s->addr[page] >> 2) & 0x3f) + \
+ (((s->addr[block] & 0xfff) | \
+ (s->addr[block] >> 15 ? \
+ s->density_mask : 0)) << 6)) << (PAGE_SHIFT - 9));
+#define SETBUF_M() \
+ buf = (s->bufaddr & 8) ? \
+ s->data[(s->bufaddr >> 2) & 1][0] : s->boot[0]; \
+ buf += (s->bufaddr & 3) << 9;
+#define SETBUF_S() \
+ buf = (s->bufaddr & 8) ? \
+ s->data[(s->bufaddr >> 2) & 1][1] : s->boot[1]; \
+ buf += (s->bufaddr & 3) << 4;
+
+ switch (cmd) {
+ case 0x00: /* Load single/multiple sector data unit into buffer */
+ SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE)
+
+ SETBUF_M()
+ if (onenand_load_main(s, sec, s->count, buf))
+ s->status |= ONEN_ERR_CMD | ONEN_ERR_LOAD;
+
+#if 0
+ SETBUF_S()
+ if (onenand_load_spare(s, sec, s->count, buf))
+ s->status |= ONEN_ERR_CMD | ONEN_ERR_LOAD;
+#endif
+
+ /* TODO: if (s->bufaddr & 3) + s->count was > 4 (2k-pages)
+ * or if (s->bufaddr & 1) + s->count was > 2 (1k-pages)
+ * then we need two split the read/write into two chunks.
+ */
+ s->intstatus |= ONEN_INT | ONEN_INT_LOAD;
+ break;
+ case 0x13: /* Load single/multiple spare sector into buffer */
+ SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE)
+
+ SETBUF_S()
+ if (onenand_load_spare(s, sec, s->count, buf))
+ s->status |= ONEN_ERR_CMD | ONEN_ERR_LOAD;
+
+ /* TODO: if (s->bufaddr & 3) + s->count was > 4 (2k-pages)
+ * or if (s->bufaddr & 1) + s->count was > 2 (1k-pages)
+ * then we need two split the read/write into two chunks.
+ */
+ s->intstatus |= ONEN_INT | ONEN_INT_LOAD;
+ break;
+ case 0x80: /* Program single/multiple sector data unit from buffer */
+ SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE)
+
+ SETBUF_M()
+ if (onenand_prog_main(s, sec, s->count, buf))
+ s->status |= ONEN_ERR_CMD | ONEN_ERR_PROG;
+
+#if 0
+ SETBUF_S()
+ if (onenand_prog_spare(s, sec, s->count, buf))
+ s->status |= ONEN_ERR_CMD | ONEN_ERR_PROG;
+#endif
+
+ /* TODO: if (s->bufaddr & 3) + s->count was > 4 (2k-pages)
+ * or if (s->bufaddr & 1) + s->count was > 2 (1k-pages)
+ * then we need two split the read/write into two chunks.
+ */
+ s->intstatus |= ONEN_INT | ONEN_INT_PROG;
+ break;
+ case 0x1a: /* Program single/multiple spare area sector from buffer */
+ SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE)
+
+ SETBUF_S()
+ if (onenand_prog_spare(s, sec, s->count, buf))
+ s->status |= ONEN_ERR_CMD | ONEN_ERR_PROG;
+
+ /* TODO: if (s->bufaddr & 3) + s->count was > 4 (2k-pages)
+ * or if (s->bufaddr & 1) + s->count was > 2 (1k-pages)
+ * then we need two split the read/write into two chunks.
+ */
+ s->intstatus |= ONEN_INT | ONEN_INT_PROG;
+ break;
+ case 0x1b: /* Copy-back program */
+ SETBUF_S()
+
+ SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE)
+ if (onenand_load_main(s, sec, s->count, buf))
+ s->status |= ONEN_ERR_CMD | ONEN_ERR_PROG;
+
+ SETADDR(ONEN_BUF_DEST_BLOCK, ONEN_BUF_DEST_PAGE)
+ if (onenand_prog_main(s, sec, s->count, buf))
+ s->status |= ONEN_ERR_CMD | ONEN_ERR_PROG;
+
+ /* TODO: spare areas */
+
+ s->intstatus |= ONEN_INT | ONEN_INT_PROG;
+ break;
+
+ case 0x23: /* Unlock NAND array block(s) */
+ s->intstatus |= ONEN_INT;
+
+ /* XXX the previous (?) area should be locked automatically */
+ for (b = s->unladdr[0]; b <= s->unladdr[1]; b ++) {
+ if (b >= s->blocks) {
+ s->status |= ONEN_ERR_CMD;
+ break;
+ }
+ if (s->blockwp[b] == ONEN_LOCK_LOCKTIGHTEN)
+ break;
+
+ s->wpstatus = s->blockwp[b] = ONEN_LOCK_UNLOCKED;
+ }
+ break;
+ case 0x2a: /* Lock NAND array block(s) */
+ s->intstatus |= ONEN_INT;
+
+ for (b = s->unladdr[0]; b <= s->unladdr[1]; b ++) {
+ if (b >= s->blocks) {
+ s->status |= ONEN_ERR_CMD;
+ break;
+ }
+ if (s->blockwp[b] == ONEN_LOCK_LOCKTIGHTEN)
+ break;
+
+ s->wpstatus = s->blockwp[b] = ONEN_LOCK_LOCKED;
+ }
+ break;
+ case 0x2c: /* Lock-tight NAND array block(s) */
+ s->intstatus |= ONEN_INT;
+
+ for (b = s->unladdr[0]; b <= s->unladdr[1]; b ++) {
+ if (b >= s->blocks) {
+ s->status |= ONEN_ERR_CMD;
+ break;
+ }
+ if (s->blockwp[b] == ONEN_LOCK_UNLOCKED)
+ continue;
+
+ s->wpstatus = s->blockwp[b] = ONEN_LOCK_LOCKTIGHTEN;
+ }
+ break;
+
+ case 0x71: /* Erase-Verify-Read */
+ s->intstatus |= ONEN_INT;
+ break;
+ case 0x95: /* Multi-block erase */
+ qemu_irq_pulse(s->intr);
+ /* Fall through. */
+ case 0x94: /* Block erase */
+ sec = ((s->addr[ONEN_BUF_BLOCK] & 0xfff) |
+ (s->addr[ONEN_BUF_BLOCK] >> 15 ? s->density_mask : 0))
+ << (BLOCK_SHIFT - 9);
+ if (onenand_erase(s, sec, 1 << (BLOCK_SHIFT - 9)))
+ s->status |= ONEN_ERR_CMD | ONEN_ERR_ERASE;
+
+ s->intstatus |= ONEN_INT | ONEN_INT_ERASE;
+ break;
+ case 0xb0: /* Erase suspend */
+ break;
+ case 0x30: /* Erase resume */
+ s->intstatus |= ONEN_INT | ONEN_INT_ERASE;
+ break;
+
+ case 0xf0: /* Reset NAND Flash core */
+ onenand_reset(s, 0);
+ break;
+ case 0xf3: /* Reset OneNAND */
+ onenand_reset(s, 0);
+ break;
+
+ case 0x65: /* OTP Access */
+ s->intstatus |= ONEN_INT;
+ s->bdrv_cur = 0;
+ s->current = s->otp;
+ s->secs_cur = 1 << (BLOCK_SHIFT - 9);
+ s->addr[ONEN_BUF_BLOCK] = 0;
+ s->otpmode = 1;
+ break;
+
+ default:
+ s->status |= ONEN_ERR_CMD;
+ s->intstatus |= ONEN_INT;
+ fprintf(stderr, "%s: unknown OneNAND command %x\n",
+ __FUNCTION__, cmd);
+ }
+
+ onenand_intr_update(s);
+}
+
+static uint32_t onenand_read(void *opaque, target_phys_addr_t addr)
+{
+ struct onenand_s *s = (struct onenand_s *) opaque;
+ int offset = (addr - s->base) >> s->shift;
+
+ switch (offset) {
+ case 0x0000 ... 0xc000:
+ return lduw_le_p(s->boot[0] + (addr - s->base));
+
+ case 0xf000: /* Manufacturer ID */
+ return (s->id >> 16) & 0xff;
+ case 0xf001: /* Device ID */
+ return (s->id >> 8) & 0xff;
+ /* TODO: get the following values from a real chip! */
+ case 0xf002: /* Version ID */
+ return (s->id >> 0) & 0xff;
+ case 0xf003: /* Data Buffer size */
+ return 1 << PAGE_SHIFT;
+ case 0xf004: /* Boot Buffer size */
+ return 0x200;
+ case 0xf005: /* Amount of buffers */
+ return 1 | (2 << 8);
+ case 0xf006: /* Technology */
+ return 0;
+
+ case 0xf100 ... 0xf107: /* Start addresses */
+ return s->addr[offset - 0xf100];
+
+ case 0xf200: /* Start buffer */
+ return (s->bufaddr << 8) | ((s->count - 1) & (1 << (PAGE_SHIFT - 10)));
+
+ case 0xf220: /* Command */
+ return s->command;
+ case 0xf221: /* System Configuration 1 */
+ return s->config[0] & 0xffe0;
+ case 0xf222: /* System Configuration 2 */
+ return s->config[1];
+
+ case 0xf240: /* Controller Status */
+ return s->status;
+ case 0xf241: /* Interrupt */
+ return s->intstatus;
+ case 0xf24c: /* Unlock Start Block Address */
+ return s->unladdr[0];
+ case 0xf24d: /* Unlock End Block Address */
+ return s->unladdr[1];
+ case 0xf24e: /* Write Protection Status */
+ return s->wpstatus;
+
+ case 0xff00: /* ECC Status */
+ return 0x00;
+ case 0xff01: /* ECC Result of main area data */
+ case 0xff02: /* ECC Result of spare area data */
+ case 0xff03: /* ECC Result of main area data */
+ case 0xff04: /* ECC Result of spare area data */
+ cpu_abort(cpu_single_env, "%s: imeplement ECC\n", __FUNCTION__);
+ return 0x0000;
+ }
+
+ fprintf(stderr, "%s: unknown OneNAND register %x\n",
+ __FUNCTION__, offset);
+ return 0;
+}
+
+static void onenand_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ struct onenand_s *s = (struct onenand_s *) opaque;
+ int offset = (addr - s->base) >> s->shift;
+ int sec;
+
+ switch (offset) {
+ case 0x0000 ... 0x01ff:
+ case 0x8000 ... 0x800f:
+ if (s->cycle) {
+ s->cycle = 0;
+
+ if (value == 0x0000) {
+ SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE)
+ onenand_load_main(s, sec,
+ 1 << (PAGE_SHIFT - 9), s->data[0][0]);
+ s->addr[ONEN_BUF_PAGE] += 4;
+ s->addr[ONEN_BUF_PAGE] &= 0xff;
+ }
+ break;
+ }
+
+ switch (value) {
+ case 0x00f0: /* Reset OneNAND */
+ onenand_reset(s, 0);
+ break;
+
+ case 0x00e0: /* Load Data into Buffer */
+ s->cycle = 1;
+ break;
+
+ case 0x0090: /* Read Identification Data */
+ memset(s->boot[0], 0, 3 << s->shift);
+ s->boot[0][0 << s->shift] = (s->id >> 16) & 0xff;
+ s->boot[0][1 << s->shift] = (s->id >> 8) & 0xff;
+ s->boot[0][2 << s->shift] = s->wpstatus & 0xff;
+ break;
+
+ default:
+ fprintf(stderr, "%s: unknown OneNAND boot command %x\n",
+ __FUNCTION__, value);
+ }
+ break;
+
+ case 0xf100 ... 0xf107: /* Start addresses */
+ s->addr[offset - 0xf100] = value;
+ break;
+
+ case 0xf200: /* Start buffer */
+ s->bufaddr = (value >> 8) & 0xf;
+ if (PAGE_SHIFT == 11)
+ s->count = (value & 3) ?: 4;
+ else if (PAGE_SHIFT == 10)
+ s->count = (value & 1) ?: 2;
+ break;
+
+ case 0xf220: /* Command */
+ if (s->intstatus & (1 << 15))
+ break;
+ s->command = value;
+ onenand_command(s, s->command);
+ break;
+ case 0xf221: /* System Configuration 1 */
+ s->config[0] = value;
+ onenand_intr_update(s);
+ qemu_set_irq(s->rdy, (s->config[0] >> 7) & 1);
+ break;
+ case 0xf222: /* System Configuration 2 */
+ s->config[1] = value;
+ break;
+
+ case 0xf241: /* Interrupt */
+ s->intstatus &= value;
+ if ((1 << 15) & ~s->intstatus)
+ s->status &= ~(ONEN_ERR_CMD | ONEN_ERR_ERASE |
+ ONEN_ERR_PROG | ONEN_ERR_LOAD);
+ onenand_intr_update(s);
+ break;
+ case 0xf24c: /* Unlock Start Block Address */
+ s->unladdr[0] = value & (s->blocks - 1);
+ /* For some reason we have to set the end address to by default
+ * be same as start because the software forgets to write anything
+ * in there. */
+ s->unladdr[1] = value & (s->blocks - 1);
+ break;
+ case 0xf24d: /* Unlock End Block Address */
+ s->unladdr[1] = value & (s->blocks - 1);
+ break;
+
+ default:
+ fprintf(stderr, "%s: unknown OneNAND register %x\n",
+ __FUNCTION__, offset);
+ }
+}
+
+static CPUReadMemoryFunc *onenand_readfn[] = {
+ onenand_read, /* TODO */
+ onenand_read,
+ onenand_read,
+};
+
+static CPUWriteMemoryFunc *onenand_writefn[] = {
+ onenand_write, /* TODO */
+ onenand_write,
+ onenand_write,
+};
+
+void *onenand_init(uint32_t id, int regshift, qemu_irq irq)
+{
+ struct onenand_s *s = (struct onenand_s *) qemu_mallocz(sizeof(*s));
+ int bdrv_index = drive_get_index(IF_MTD, 0, 0);
+ uint32_t size = 1 << (24 + ((id >> 12) & 7));
+ void *ram;
+
+ s->shift = regshift;
+ s->intr = irq;
+ s->rdy = 0;
+ s->id = id;
+ s->blocks = size >> BLOCK_SHIFT;
+ s->secs = size >> 9;
+ s->blockwp = qemu_malloc(s->blocks);
+ s->density_mask = (id & (1 << 11)) ? (1 << (6 + ((id >> 12) & 7))) : 0;
+ s->iomemtype = cpu_register_io_memory(0, onenand_readfn,
+ onenand_writefn, s);
+ if (bdrv_index == -1)
+ s->image = memset(qemu_malloc(size + (size >> 5)),
+ 0xff, size + (size >> 5));
+ else
+ s->bdrv = drives_table[bdrv_index].bdrv;
+ s->otp = memset(qemu_malloc((64 + 2) << PAGE_SHIFT),
+ 0xff, (64 + 2) << PAGE_SHIFT);
+ s->ram = qemu_ram_alloc(0xc000 << s->shift);
+ ram = phys_ram_base + s->ram;
+ s->boot[0] = ram + (0x0000 << s->shift);
+ s->boot[1] = ram + (0x8000 << s->shift);
+ s->data[0][0] = ram + ((0x0200 + (0 << (PAGE_SHIFT - 1))) << s->shift);
+ s->data[0][1] = ram + ((0x8010 + (0 << (PAGE_SHIFT - 6))) << s->shift);
+ s->data[1][0] = ram + ((0x0200 + (1 << (PAGE_SHIFT - 1))) << s->shift);
+ s->data[1][1] = ram + ((0x8010 + (1 << (PAGE_SHIFT - 6))) << s->shift);
+
+ onenand_reset(s, 1);
+
+ return s;
+}
Added: trunk/hw/tmp105.c
===================================================================
--- trunk/hw/tmp105.c (rev 0)
+++ trunk/hw/tmp105.c 2008-04-14 21:57:44 UTC (rev 4215)
@@ -0,0 +1,249 @@
+/*
+ * Texas Instruments TMP105 temperature sensor.
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ * Written by Andrzej Zaborowski <andrew@openedhand.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include "hw.h"
+#include "i2c.h"
+
+struct tmp105_s {
+ i2c_slave i2c;
+ int len;
+ uint8_t buf[2];
+ qemu_irq pin;
+
+ uint8_t pointer;
+ uint8_t config;
+ int16_t temperature;
+ int16_t limit[2];
+ int faults;
+ int alarm;
+};
+
+static void tmp105_interrupt_update(struct tmp105_s *s)
+{
+ qemu_set_irq(s->pin, s->alarm ^ ((~s->config >> 2) & 1)); /* POL */
+}
+
+static void tmp105_alarm_update(struct tmp105_s *s)
+{
+ if ((s->config >> 0) & 1) { /* SD */
+ if ((s->config >> 7) & 1) /* OS */
+ s->config &= ~(1 << 7); /* OS */
+ else
+ return;
+ }
+
+ if ((s->config >> 1) & 1) { /* TM */
+ if (s->temperature >= s->limit[1])
+ s->alarm = 1;
+ else if (s->temperature < s->limit[0])
+ s->alarm = 1;
+ } else {
+ if (s->temperature >= s->limit[1])
+ s->alarm = 1;
+ else if (s->temperature < s->limit[0])
+ s->alarm = 0;
+ }
+
+ tmp105_interrupt_update(s);
+}
+
+/* Units are 0.001 centigrades relative to 0 C. */
+void tmp105_set(i2c_slave *i2c, int temp)
+{
+ struct tmp105_s *s = (struct tmp105_s *) i2c;
+
+ if (temp >= 128000 || temp < -128000) {
+ fprintf(stderr, "%s: values is out of range (%i.%03i C)\n",
+ __FUNCTION__, temp / 1000, temp % 1000);
+ exit(-1);
+ }
+
+ s->temperature = ((int16_t) (temp * 0x800 / 128000)) << 4;
+
+ tmp105_alarm_update(s);
+}
+
+static const int tmp105_faultq[4] = { 1, 2, 4, 6 };
+
+static void tmp105_read(struct tmp105_s *s)
+{
+ s->len = 0;
+
+ if ((s->config >> 1) & 1) { /* TM */
+ s->alarm = 0;
+ tmp105_interrupt_update(s);
+ }
+
+ switch (s->pointer & 3) {
+ case 0: /* Temperature */
+ s->buf[s->len ++] = (((uint16_t) s->temperature) >> 8);
+ s->buf[s->len ++] = (((uint16_t) s->temperature) >> 0) &
+ (0xf0 << ((~s->config >> 5) & 3)); /* R */
+ break;
+
+ case 1: /* Configuration */
+ s->buf[s->len ++] = s->config;
+ break;
+
+ case 2: /* T_LOW */
+ s->buf[s->len ++] = ((uint16_t) s->limit[0]) >> 8;
+ s->buf[s->len ++] = ((uint16_t) s->limit[0]) >> 0;
+ break;
+
+ case 3: /* T_HIGH */
+ s->buf[s->len ++] = ((uint16_t) s->limit[1]) >> 8;
+ s->buf[s->len ++] = ((uint16_t) s->limit[1]) >> 0;
+ break;
+ }
+}
+
+static void tmp105_write(struct tmp105_s *s)
+{
+ switch (s->pointer & 3) {
+ case 0: /* Temperature */
+ break;
+
+ case 1: /* Configuration */
+ if (s->buf[0] & ~s->config & (1 << 0)) /* SD */
+ printf("%s: TMP105 shutdown\n", __FUNCTION__);
+ s->config = s->buf[0];
+ s->faults = tmp105_faultq[(s->config >> 3) & 3]; /* F */
+ tmp105_alarm_update(s);
+ break;
+
+ case 2: /* T_LOW */
+ case 3: /* T_HIGH */
+ if (s->len >= 3)
+ s->limit[s->pointer & 1] = (int16_t)
+ ((((uint16_t) s->buf[0]) << 8) | s->buf[1]);
+ tmp105_alarm_update(s);
+ break;
+ }
+}
+
+static int tmp105_rx(i2c_slave *i2c)
+{
+ struct tmp105_s *s = (struct tmp105_s *) i2c;
+
+ if (s->len < 2)
+ return s->buf[s->len ++];
+ else
+ return 0xff;
+}
+
+static int tmp105_tx(i2c_slave *i2c, uint8_t data)
+{
+ struct tmp105_s *s = (struct tmp105_s *) i2c;
+
+ if (!s->len ++)
+ s->pointer = data;
+ else {
+ if (s->len <= 2)
+ s->buf[s->len - 1] = data;
+ tmp105_write(s);
+ }
+
+ return 0;
+}
+
+static void tmp105_event(i2c_slave *i2c, enum i2c_event event)
+{
+ struct tmp105_s *s = (struct tmp105_s *) i2c;
+
+ if (event == I2C_START_RECV)
+ tmp105_read(s);
+
+ s->len = 0;
+}
+
+static void tmp105_save(QEMUFile *f, void *opaque)
+{
+ struct tmp105_s *s = (struct tmp105_s *) opaque;
+
+ qemu_put_byte(f, s->len);
+ qemu_put_8s(f, &s->buf[0]);
+ qemu_put_8s(f, &s->buf[1]);
+
+ qemu_put_8s(f, &s->pointer);
+ qemu_put_8s(f, &s->config);
+ qemu_put_be16s(f, &s->temperature);
+ qemu_put_be16s(f, &s->limit[0]);
+ qemu_put_be16s(f, &s->limit[1]);
+ qemu_put_byte(f, s->alarm);
+ s->faults = tmp105_faultq[(s->config >> 3) & 3]; /* F */
+
+ i2c_slave_save(f, &s->i2c);
+}
+
+static int tmp105_load(QEMUFile *f, void *opaque, int version_id)
+{
+ struct tmp105_s *s = (struct tmp105_s *) opaque;
+
+ s->len = qemu_get_byte(f);
+ qemu_get_8s(f, &s->buf[0]);
+ qemu_get_8s(f, &s->buf[1]);
+
+ qemu_get_8s(f, &s->pointer);
+ qemu_get_8s(f, &s->config);
+ qemu_get_be16s(f, &s->temperature);
+ qemu_get_be16s(f, &s->limit[0]);
+ qemu_get_be16s(f, &s->limit[1]);
+ s->alarm = qemu_get_byte(f);
+
+ tmp105_interrupt_update(s);
+
+ i2c_slave_load(f, &s->i2c);
+ return 0;
+}
+
+void tmp105_reset(i2c_slave *i2c)
+{
+ struct tmp105_s *s = (struct tmp105_s *) i2c;
+
+ s->temperature = 0;
+ s->pointer = 0;
+ s->config = 0;
+ s->faults = tmp105_faultq[(s->config >> 3) & 3];
+ s->alarm = 0;
+
+ tmp105_interrupt_update(s);
+}
+
+static int tmp105_iid = 0;
+
+struct i2c_slave *tmp105_init(i2c_bus *bus, qemu_irq alarm)
+{
+ struct tmp105_s *s = (struct tmp105_s *)
+ i2c_slave_init(bus, 0, sizeof(struct tmp105_s));
+
+ s->i2c.event = tmp105_event;
+ s->i2c.recv = tmp105_rx;
+ s->i2c.send = tmp105_tx;
+ s->pin = alarm;
+
+ tmp105_reset(&s->i2c);
+
+ register_savevm("TMP105", tmp105_iid ++, 0,
+ tmp105_save, tmp105_load, s);
+
+ return &s->i2c;
+}
Added: trunk/hw/twl92230.c
===================================================================
--- trunk/hw/twl92230.c (rev 0)
+++ trunk/hw/twl92230.c 2008-04-14 21:57:44 UTC (rev 4215)
@@ -0,0 +1,923 @@
+/*
+ * TI TWL92230C energy-management companion device for the OMAP24xx.
+ * Aka. Menelaus (N4200 MENELAUS1_V2.2)
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ * Written by Andrzej Zaborowski <andrew@openedhand.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include "hw.h"
+#include "qemu-timer.h"
+#include "i2c.h"
+#include "sysemu.h"
+#include "console.h"
+
+#define VERBOSE 1
+
+struct menelaus_s {
+ i2c_slave i2c;
+ qemu_irq irq;
+
+ int firstbyte;
+ uint8_t reg;
+
+ uint8_t vcore[5];
+ uint8_t dcdc[3];
+ uint8_t ldo[8];
+ uint8_t sleep[2];
+ uint8_t osc;
+ uint8_t detect;
+ uint16_t mask;
+ uint16_t status;
+ uint8_t dir;
+ uint8_t inputs;
+ uint8_t outputs;
+ uint8_t bbsms;
+ uint8_t pull[4];
+ uint8_t mmc_ctrl[3];
+ uint8_t mmc_debounce;
+ struct {
+ uint8_t ctrl;
+ uint16_t comp;
+ QEMUTimer *hz;
+ int64_t next;
+ struct tm tm;
+ struct tm new;
+ struct tm alm;
+ time_t sec;
+ time_t alm_sec;
+ time_t next_comp;
+ struct tm *(*gettime)(const time_t *timep, struct tm *result);
+ } rtc;
+ qemu_irq handler[3];
+ qemu_irq *in;
+ int pwrbtn_state;
+ qemu_irq pwrbtn;
+};
+
+static inline void menelaus_update(struct menelaus_s *s)
+{
+ qemu_set_irq(s->irq, s->status & ~s->mask);
+}
+
+static inline void menelaus_rtc_start(struct menelaus_s *s)
+{
+ s->rtc.next =+ qemu_get_clock(rt_clock);
+ qemu_mod_timer(s->rtc.hz, s->rtc.next);
+}
+
+static inline void menelaus_rtc_stop(struct menelaus_s *s)
+{
+ qemu_del_timer(s->rtc.hz);
+ s->rtc.next =- qemu_get_clock(rt_clock);
+ if (s->rtc.next < 1)
+ s->rtc.next = 1;
+}
+
+static void menelaus_rtc_update(struct menelaus_s *s)
+{
+ s->rtc.gettime(&s->rtc.sec, &s->rtc.tm);
+}
+
+static void menelaus_alm_update(struct menelaus_s *s)
+{
+ if ((s->rtc.ctrl & 3) == 3)
+ s->rtc.alm_sec = mktime(&s->rtc.alm);
+}
+
+static void menelaus_rtc_hz(void *opaque)
+{
+ struct menelaus_s *s = (struct menelaus_s *) opaque;
+
+ s->rtc.sec ++;
+ s->rtc.next += 1000;
+ qemu_mod_timer(s->rtc.hz, s->rtc.next);
+ if ((s->rtc.ctrl >> 3) & 3) { /* EVERY */
+ menelaus_rtc_update(s);
+ if (((s->rtc.ctrl >> 3) & 3) == 1 && !s->rtc.tm.tm_sec)
+ s->status |= 1 << 8; /* RTCTMR */
+ else if (((s->rtc.ctrl >> 3) & 3) == 2 && !s->rtc.tm.tm_min)
+ s->status |= 1 << 8; /* RTCTMR */
+ else if (!s->rtc.tm.tm_hour)
+ s->status |= 1 << 8; /* RTCTMR */
+ } else
+ s->status |= 1 << 8; /* RTCTMR */
+ if ((s->rtc.ctrl >> 1) & 1) { /* RTC_AL_EN */
+ if (s->rtc.sec == s->rtc.alm_sec)
+ s->status |= 1 << 9; /* RTCALM */
+ /* TODO: wake-up */
+ }
+ if (s->rtc.next_comp >= s->rtc.sec) {
+ s->rtc.next -= muldiv64((int16_t) s->rtc.comp, 1000, 0x8000);
+ s->rtc.next_comp = s->rtc.sec + 3600;
+ }
+ menelaus_update(s);
+}
+
+void menelaus_reset(i2c_slave *i2c)
+{
+ struct menelaus_s *s = (struct menelaus_s *) i2c;
+ time_t ti;
+ s->reg = 0x00;
+
+ s->vcore[0] = 0x0c; /* XXX: X-loader needs 0x8c? check! */
+ s->vcore[1] = 0x05;
+ s->vcore[2] = 0x02;
+ s->vcore[3] = 0x0c;
+ s->vcore[4] = 0x03;
+ s->dcdc[0] = 0x33; /* Depends on wiring */
+ s->dcdc[1] = 0x03;
+ s->dcdc[2] = 0x00;
+ s->ldo[0] = 0x95;
+ s->ldo[1] = 0x7e;
+ s->ldo[2] = 0x00;
+ s->ldo[3] = 0x00; /* Depends on wiring */
+ s->ldo[4] = 0x03; /* Depends on wiring */
+ s->ldo[5] = 0x00;
+ s->ldo[6] = 0x00;
+ s->ldo[7] = 0x00;
+ s->sleep[0] = 0x00;
+ s->sleep[1] = 0x00;
+ s->osc = 0x01;
+ s->detect = 0x09;
+ s->mask = 0x0fff;
+ s->status = 0;
+ s->dir = 0x07;
+ s->outputs = 0x00;
+ s->bbsms = 0x00;
+ s->pull[0] = 0x00;
+ s->pull[1] = 0x00;
+ s->pull[2] = 0x00;
+ s->pull[3] = 0x00;
+ s->mmc_ctrl[0] = 0x03;
+ s->mmc_ctrl[1] = 0xc0;
+ s->mmc_ctrl[2] = 0x00;
+ s->mmc_debounce = 0x05;
+
+ time(&ti);
+ if (s->rtc.ctrl & 1)
+ menelaus_rtc_stop(s);
+ s->rtc.ctrl = 0x00;
+ s->rtc.comp = 0x0000;
+ s->rtc.next = 1000;
+ s->rtc.sec = ti;
+ s->rtc.next_comp = s->rtc.sec + 1800;
+ s->rtc.alm.tm_sec = 0x00;
+ s->rtc.alm.tm_min = 0x00;
+ s->rtc.alm.tm_hour = 0x00;
+ s->rtc.alm.tm_mday = 0x01;
+ s->rtc.alm.tm_mon = 0x00;
+ s->rtc.alm.tm_year = 2004;
+ menelaus_update(s);
+}
+
+static inline uint8_t to_bcd(int val)
+{
+ return ((val / 10) << 4) | (val % 10);
+}
+
+static inline int from_bcd(uint8_t val)
+{
+ return ((val >> 4) * 10) + (val & 0x0f);
+}
+
+static void menelaus_gpio_set(void *opaque, int line, int level)
+{
+ struct menelaus_s *s = (struct menelaus_s *) opaque;
+
+ /* No interrupt generated */
+ s->inputs &= ~(1 << line);
+ s->inputs |= level << line;
+}
+
+static void menelaus_pwrbtn_set(void *opaque, int line, int level)
+{
+ struct menelaus_s *s = (struct menelaus_s *) opaque;
+
+ if (!s->pwrbtn_state && level) {
+ s->status |= 1 << 11; /* PSHBTN */
+ menelaus_update(s);
+ }
+ s->pwrbtn_state = level;
+}
+
+#define MENELAUS_REV 0x01
+#define MENELAUS_VCORE_CTRL1 0x02
+#define MENELAUS_VCORE_CTRL2 0x03
+#define MENELAUS_VCORE_CTRL3 0x04
+#define MENELAUS_VCORE_CTRL4 0x05
+#define MENELAUS_VCORE_CTRL5 0x06
+#define MENELAUS_DCDC_CTRL1 0x07
+#define MENELAUS_DCDC_CTRL2 0x08
+#define MENELAUS_DCDC_CTRL3 0x09
+#define MENELAUS_LDO_CTRL1 0x0a
+#define MENELAUS_LDO_CTRL2 0x0b
+#define MENELAUS_LDO_CTRL3 0x0c
+#define MENELAUS_LDO_CTRL4 0x0d
+#define MENELAUS_LDO_CTRL5 0x0e
+#define MENELAUS_LDO_CTRL6 0x0f
+#define MENELAUS_LDO_CTRL7 0x10
+#define MENELAUS_LDO_CTRL8 0x11
+#define MENELAUS_SLEEP_CTRL1 0x12
+#define MENELAUS_SLEEP_CTRL2 0x13
+#define MENELAUS_DEVICE_OFF 0x14
+#define MENELAUS_OSC_CTRL 0x15
+#define MENELAUS_DETECT_CTRL 0x16
+#define MENELAUS_INT_MASK1 0x17
+#define MENELAUS_INT_MASK2 0x18
+#define MENELAUS_INT_STATUS1 0x19
+#define MENELAUS_INT_STATUS2 0x1a
+#define MENELAUS_INT_ACK1 0x1b
+#define MENELAUS_INT_ACK2 0x1c
+#define MENELAUS_GPIO_CTRL 0x1d
+#define MENELAUS_GPIO_IN 0x1e
+#define MENELAUS_GPIO_OUT 0x1f
+#define MENELAUS_BBSMS 0x20
+#define MENELAUS_RTC_CTRL 0x21
+#define MENELAUS_RTC_UPDATE 0x22
+#define MENELAUS_RTC_SEC 0x23
+#define MENELAUS_RTC_MIN 0x24
+#define MENELAUS_RTC_HR 0x25
+#define MENELAUS_RTC_DAY 0x26
+#define MENELAUS_RTC_MON 0x27
+#define MENELAUS_RTC_YR 0x28
+#define MENELAUS_RTC_WKDAY 0x29
+#define MENELAUS_RTC_AL_SEC 0x2a
+#define MENELAUS_RTC_AL_MIN 0x2b
+#define MENELAUS_RTC_AL_HR 0x2c
+#define MENELAUS_RTC_AL_DAY 0x2d
+#define MENELAUS_RTC_AL_MON 0x2e
+#define MENELAUS_RTC_AL_YR 0x2f
+#define MENELAUS_RTC_COMP_MSB 0x30
+#define MENELAUS_RTC_COMP_LSB 0x31
+#define MENELAUS_S1_PULL_EN 0x32
+#define MENELAUS_S1_PULL_DIR 0x33
+#define MENELAUS_S2_PULL_EN 0x34
+#define MENELAUS_S2_PULL_DIR 0x35
+#define MENELAUS_MCT_CTRL1 0x36
+#define MENELAUS_MCT_CTRL2 0x37
+#define MENELAUS_MCT_CTRL3 0x38
+#define MENELAUS_MCT_PIN_ST 0x39
+#define MENELAUS_DEBOUNCE1 0x3a
+
+static uint8_t menelaus_read(void *opaque, uint8_t addr)
+{
+ struct menelaus_s *s = (struct menelaus_s *) opaque;
+ int reg = 0;
+
+ switch (addr) {
+ case MENELAUS_REV:
+ return 0x22;
+
+ case MENELAUS_VCORE_CTRL5: reg ++;
+ case MENELAUS_VCORE_CTRL4: reg ++;
+ case MENELAUS_VCORE_CTRL3: reg ++;
+ case MENELAUS_VCORE_CTRL2: reg ++;
+ case MENELAUS_VCORE_CTRL1:
+ return s->vcore[reg];
+
+ case MENELAUS_DCDC_CTRL3: reg ++;
+ case MENELAUS_DCDC_CTRL2: reg ++;
+ case MENELAUS_DCDC_CTRL1:
+ return s->dcdc[reg];
+
+ case MENELAUS_LDO_CTRL8: reg ++;
+ case MENELAUS_LDO_CTRL7: reg ++;
+ case MENELAUS_LDO_CTRL6: reg ++;
+ case MENELAUS_LDO_CTRL5: reg ++;
+ case MENELAUS_LDO_CTRL4: reg ++;
+ case MENELAUS_LDO_CTRL3: reg ++;
+ case MENELAUS_LDO_CTRL2: reg ++;
+ case MENELAUS_LDO_CTRL1:
+ return s->ldo[reg];
+
+ case MENELAUS_SLEEP_CTRL2: reg ++;
+ case MENELAUS_SLEEP_CTRL1:
+ return s->sleep[reg];
+
+ case MENELAUS_DEVICE_OFF:
+ return 0;
+
+ case MENELAUS_OSC_CTRL:
+ return s->osc | (1 << 7); /* CLK32K_GOOD */
+
+ case MENELAUS_DETECT_CTRL:
+ return s->detect;
+
+ case MENELAUS_INT_MASK1:
+ return (s->mask >> 0) & 0xff;
+ case MENELAUS_INT_MASK2:
+ return (s->mask >> 8) & 0xff;
+
+ case MENELAUS_INT_STATUS1:
+ return (s->status >> 0) & 0xff;
+ case MENELAUS_INT_STATUS2:
+ return (s->status >> 8) & 0xff;
+
+ case MENELAUS_INT_ACK1:
+ case MENELAUS_INT_ACK2:
+ return 0;
+
+ case MENELAUS_GPIO_CTRL:
+ return s->dir;
+ case MENELAUS_GPIO_IN:
+ return s->inputs | (~s->dir & s->outputs);
+ case MENELAUS_GPIO_OUT:
+ return s->outputs;
+
+ case MENELAUS_BBSMS:
+ return s->bbsms;
+
+ case MENELAUS_RTC_CTRL:
+ return s->rtc.ctrl;
+ case MENELAUS_RTC_UPDATE:
+ return 0x00;
+ case MENELAUS_RTC_SEC:
+ menelaus_rtc_update(s);
+ return to_bcd(s->rtc.tm.tm_sec);
+ case MENELAUS_RTC_MIN:
+ menelaus_rtc_update(s);
+ return to_bcd(s->rtc.tm.tm_min);
+ case MENELAUS_RTC_HR:
+ menelaus_rtc_update(s);
+ if ((s->rtc.ctrl >> 2) & 1) /* MODE12_n24 */
+ return to_bcd((s->rtc.tm.tm_hour % 12) + 1) |
+ (!!(s->rtc.tm.tm_hour >= 12) << 7); /* PM_nAM */
+ else
+ return to_bcd(s->rtc.tm.tm_hour);
+ case MENELAUS_RTC_DAY:
+ menelaus_rtc_update(s);
+ return to_bcd(s->rtc.tm.tm_mday);
+ case MENELAUS_RTC_MON:
+ menelaus_rtc_update(s);
+ return to_bcd(s->rtc.tm.tm_mon + 1);
+ case MENELAUS_RTC_YR:
+ menelaus_rtc_update(s);
+ return to_bcd(s->rtc.tm.tm_year - 2000);
+ case MENELAUS_RTC_WKDAY:
+ menelaus_rtc_update(s);
+ return to_bcd(s->rtc.tm.tm_wday);
+ case MENELAUS_RTC_AL_SEC:
+ return to_bcd(s->rtc.alm.tm_sec);
+ case MENELAUS_RTC_AL_MIN:
+ return to_bcd(s->rtc.alm.tm_min);
+ case MENELAUS_RTC_AL_HR:
+ if ((s->rtc.ctrl >> 2) & 1) /* MODE12_n24 */
+ return to_bcd((s->rtc.alm.tm_hour % 12) + 1) |
+ (!!(s->rtc.alm.tm_hour >= 12) << 7);/* AL_PM_nAM */
+ else
+ return to_bcd(s->rtc.alm.tm_hour);
+ case MENELAUS_RTC_AL_DAY:
+ return to_bcd(s->rtc.alm.tm_mday);
+ case MENELAUS_RTC_AL_MON:
+ return to_bcd(s->rtc.alm.tm_mon + 1);
+ case MENELAUS_RTC_AL_YR:
+ return to_bcd(s->rtc.alm.tm_year - 2000);
+ case MENELAUS_RTC_COMP_MSB:
+ return (s->rtc.comp >> 8) & 0xff;
+ case MENELAUS_RTC_COMP_LSB:
+ return (s->rtc.comp >> 0) & 0xff;
+
+ case MENELAUS_S1_PULL_EN:
+ return s->pull[0];
+ case MENELAUS_S1_PULL_DIR:
+ return s->pull[1];
+ case MENELAUS_S2_PULL_EN:
+ return s->pull[2];
+ case MENELAUS_S2_PULL_DIR:
+ return s->pull[3];
+
+ case MENELAUS_MCT_CTRL3: reg ++;
+ case MENELAUS_MCT_CTRL2: reg ++;
+ case MENELAUS_MCT_CTRL1:
+ return s->mmc_ctrl[reg];
+ case MENELAUS_MCT_PIN_ST:
+ /* TODO: return the real Card Detect */
+ return 0;
+ case MENELAUS_DEBOUNCE1:
+ return s->mmc_debounce;
+
+ default:
+#ifdef VERBOSE
+ printf("%s: unknown register %02x\n", __FUNCTION__, addr);
+#endif
+ break;
+ }
+ return 0;
+}
+
+static void menelaus_write(void *opaque, uint8_t addr, uint8_t value)
+{
+ struct menelaus_s *s = (struct menelaus_s *) opaque;
+ int line;
+ int reg = 0;
+ struct tm tm;
+
+ switch (addr) {
+ case MENELAUS_VCORE_CTRL1:
+ s->vcore[0] = (value & 0xe) | MIN(value & 0x1f, 0x12);
+ break;
+ case MENELAUS_VCORE_CTRL2:
+ s->vcore[1] = value;
+ break;
+ case MENELAUS_VCORE_CTRL3:
+ s->vcore[2] = MIN(value & 0x1f, 0x12);
+ break;
+ case MENELAUS_VCORE_CTRL4:
+ s->vcore[3] = MIN(value & 0x1f, 0x12);
+ break;
+ case MENELAUS_VCORE_CTRL5:
+ s->vcore[4] = value & 3;
+ /* XXX
+ * auto set to 3 on M_Active, nRESWARM
+ * auto set to 0 on M_WaitOn, M_Backup
+ */
+ break;
+
+ case MENELAUS_DCDC_CTRL1:
+ s->dcdc[0] = value & 0x3f;
+ break;
+ case MENELAUS_DCDC_CTRL2:
+ s->dcdc[1] = value & 0x07;
+ /* XXX
+ * auto set to 3 on M_Active, nRESWARM
+ * auto set to 0 on M_WaitOn, M_Backup
+ */
+ break;
+ case MENELAUS_DCDC_CTRL3:
+ s->dcdc[2] = value & 0x07;
+ break;
+
+ case MENELAUS_LDO_CTRL1:
+ s->ldo[0] = value;
+ break;
+ case MENELAUS_LDO_CTRL2:
+ s->ldo[1] = value & 0x7f;
+ /* XXX
+ * auto set to 0x7e on M_WaitOn, M_Backup
+ */
+ break;
+ case MENELAUS_LDO_CTRL3:
+ s->ldo[2] = value & 3;
+ /* XXX
+ * auto set to 3 on M_Active, nRESWARM
+ * auto set to 0 on M_WaitOn, M_Backup
+ */
+ break;
+ case MENELAUS_LDO_CTRL4:
+ s->ldo[3] = value & 3;
+ /* XXX
+ * auto set to 3 on M_Active, nRESWARM
+ * auto set to 0 on M_WaitOn, M_Backup
+ */
+ break;
+ case MENELAUS_LDO_CTRL5:
+ s->ldo[4] = value & 3;
+ /* XXX
+ * auto set to 3 on M_Active, nRESWARM
+ * auto set to 0 on M_WaitOn, M_Backup
+ */
+ break;
+ case MENELAUS_LDO_CTRL6:
+ s->ldo[5] = value & 3;
+ break;
+ case MENELAUS_LDO_CTRL7:
+ s->ldo[6] = value & 3;
+ break;
+ case MENELAUS_LDO_CTRL8:
+ s->ldo[7] = value & 3;
+ break;
+
+ case MENELAUS_SLEEP_CTRL2: reg ++;
+ case MENELAUS_SLEEP_CTRL1:
+ s->sleep[reg] = value;
+ break;
+
+ case MENELAUS_DEVICE_OFF:
+ if (value & 1)
+ menelaus_reset(&s->i2c);
+ break;
+
+ case MENELAUS_OSC_CTRL:
+ s->osc = value & 7;
+ break;
+
+ case MENELAUS_DETECT_CTRL:
+ s->detect = value & 0x7f;
+ break;
+
+ case MENELAUS_INT_MASK1:
+ s->mask &= 0xf00;
+ s->mask |= value << 0;
+ menelaus_update(s);
+ break;
+ case MENELAUS_INT_MASK2:
+ s->mask &= 0x0ff;
+ s->mask |= value << 8;
+ menelaus_update(s);
+ break;
+
+ case MENELAUS_INT_ACK1:
+ s->status &= ~(((uint16_t) value) << 0);
+ menelaus_update(s);
+ break;
+ case MENELAUS_INT_ACK2:
+ s->status &= ~(((uint16_t) value) << 8);
+ menelaus_update(s);
+ break;
+
+ case MENELAUS_GPIO_CTRL:
+ for (line = 0; line < 3; line ++)
+ if (((s->dir ^ value) >> line) & 1)
+ if (s->handler[line])
+ qemu_set_irq(s->handler[line],
+ ((s->outputs & ~s->dir) >> line) & 1);
+ s->dir = value & 0x67;
+ break;
+ case MENELAUS_GPIO_OUT:
+ for (line = 0; line < 3; line ++)
+ if ((((s->outputs ^ value) & ~s->dir) >> line) & 1)
+ if (s->handler[line])
+ qemu_set_irq(s->handler[line], (s->outputs >> line) & 1);
+ s->outputs = value & 0x07;
+ break;
+
+ case MENELAUS_BBSMS:
+ s->bbsms = 0x0d;
+ break;
+
+ case MENELAUS_RTC_CTRL:
+ if ((s->rtc.ctrl ^ value) & 1) { /* RTC_EN */
+ if (value & 1)
+ menelaus_rtc_start(s);
+ else
+ menelaus_rtc_stop(s);
+ }
+ s->rtc.ctrl = value & 0x1f;
+ menelaus_alm_update(s);
+ break;
+ case MENELAUS_RTC_UPDATE:
+ menelaus_rtc_update(s);
+ memcpy(&tm, &s->rtc.tm, sizeof(tm));
+ switch (value & 0xf) {
+ case 0:
+ break;
+ case 1:
+ tm.tm_sec = s->rtc.new.tm_sec;
+ break;
+ case 2:
+ tm.tm_min = s->rtc.new.tm_min;
+ break;
+ case 3:
+ if (s->rtc.new.tm_hour > 23)
+ goto rtc_badness;
+ tm.tm_hour = s->rtc.new.tm_hour;
+ break;
+ case 4:
+ if (s->rtc.new.tm_mday < 1)
+ goto rtc_badness;
+ /* TODO check range */
+ tm.tm_mday = s->rtc.new.tm_mday;
+ break;
+ case 5:
+ if (s->rtc.new.tm_mon < 0 || s->rtc.new.tm_mon > 11)
+ goto rtc_badness;
+ tm.tm_mon = s->rtc.new.tm_mon;
+ break;
+ case 6:
+ tm.tm_year = s->rtc.new.tm_year;
+ break;
+ case 7:
+ /* TODO set .tm_mday instead */
+ tm.tm_wday = s->rtc.new.tm_wday;
+ break;
+ case 8:
+ if (s->rtc.new.tm_hour > 23)
+ goto rtc_badness;
+ if (s->rtc.new.tm_mday < 1)
+ goto rtc_badness;
+ if (s->rtc.new.tm_mon < 0 || s->rtc.new.tm_mon > 11)
+ goto rtc_badness;
+ tm.tm_sec = s->rtc.new.tm_sec;
+ tm.tm_min = s->rtc.new.tm_min;
+ tm.tm_hour = s->rtc.new.tm_hour;
+ tm.tm_mday = s->rtc.new.tm_mday;
+ tm.tm_mon = s->rtc.new.tm_mon;
+ tm.tm_year = s->rtc.new.tm_year;
+ break;
+ rtc_badness:
+ default:
+ fprintf(stderr, "%s: bad RTC_UPDATE value %02x\n",
+ __FUNCTION__, value);
+ s->status |= 1 << 10; /* RTCERR */
+ menelaus_update(s);
+ }
+ s->rtc.sec += difftime(mktime(&tm), mktime(&s->rtc.tm));
+ break;
+ case MENELAUS_RTC_SEC:
+ s->rtc.tm.tm_sec = from_bcd(value & 0x7f);
+ break;
+ case MENELAUS_RTC_MIN:
+ s->rtc.tm.tm_min = from_bcd(value & 0x7f);
+ break;
+ case MENELAUS_RTC_HR:
+ s->rtc.tm.tm_hour = (s->rtc.ctrl & (1 << 2)) ? /* MODE12_n24 */
+ MIN(from_bcd(value & 0x3f), 12) + ((value >> 7) ? 11 : -1) :
+ from_bcd(value & 0x3f);
+ break;
+ case MENELAUS_RTC_DAY:
+ s->rtc.tm.tm_mday = from_bcd(value);
+ break;
+ case MENELAUS_RTC_MON:
+ s->rtc.tm.tm_mon = MAX(1, from_bcd(value)) - 1;
+ break;
+ case MENELAUS_RTC_YR:
+ s->rtc.tm.tm_year = 2000 + from_bcd(value);
+ break;
+ case MENELAUS_RTC_WKDAY:
+ s->rtc.tm.tm_mday = from_bcd(value);
+ break;
+ case MENELAUS_RTC_AL_SEC:
+ s->rtc.alm.tm_sec = from_bcd(value & 0x7f);
+ menelaus_alm_update(s);
+ break;
+ case MENELAUS_RTC_AL_MIN:
+ s->rtc.alm.tm_min = from_bcd(value & 0x7f);
+ menelaus_alm_update(s);
+ break;
+ case MENELAUS_RTC_AL_HR:
+ s->rtc.alm.tm_hour = (s->rtc.ctrl & (1 << 2)) ? /* MODE12_n24 */
+ MIN(from_bcd(value & 0x3f), 12) + ((value >> 7) ? 11 : -1) :
+ from_bcd(value & 0x3f);
+ menelaus_alm_update(s);
+ break;
+ case MENELAUS_RTC_AL_DAY:
+ s->rtc.alm.tm_mday = from_bcd(value);
+ menelaus_alm_update(s);
+ break;
+ case MENELAUS_RTC_AL_MON:
+ s->rtc.alm.tm_mon = MAX(1, from_bcd(value)) - 1;
+ menelaus_alm_update(s);
+ break;
+ case MENELAUS_RTC_AL_YR:
+ s->rtc.alm.tm_year = 2000 + from_bcd(value);
+ menelaus_alm_update(s);
+ break;
+ case MENELAUS_RTC_COMP_MSB:
+ s->rtc.comp &= 0xff;
+ s->rtc.comp |= value << 8;
+ break;
+ case MENELAUS_RTC_COMP_LSB:
+ s->rtc.comp &= 0xff << 8;
+ s->rtc.comp |= value;
+ break;
+
+ case MENELAUS_S1_PULL_EN:
+ s->pull[0] = value;
+ break;
+ case MENELAUS_S1_PULL_DIR:
+ s->pull[1] = value & 0x1f;
+ break;
+ case MENELAUS_S2_PULL_EN:
+ s->pull[2] = value;
+ break;
+ case MENELAUS_S2_PULL_DIR:
+ s->pull[3] = value & 0x1f;
+ break;
+
+ case MENELAUS_MCT_CTRL1:
+ s->mmc_ctrl[0] = value & 0x7f;
+ break;
+ case MENELAUS_MCT_CTRL2:
+ s->mmc_ctrl[1] = value;
+ /* TODO update Card Detect interrupts */
+ break;
+ case MENELAUS_MCT_CTRL3:
+ s->mmc_ctrl[2] = value & 0xf;
+ break;
+ case MENELAUS_DEBOUNCE1:
+ s->mmc_debounce = value & 0x3f;
+ break;
+
+ default:
+#ifdef VERBOSE
+ printf("%s: unknown register %02x\n", __FUNCTION__, addr);
+#endif
+ }
+}
+
+static void menelaus_event(i2c_slave *i2c, enum i2c_event event)
+{
+ struct menelaus_s *s = (struct menelaus_s *) i2c;
+
+ if (event == I2C_START_SEND)
+ s->firstbyte = 1;
+}
+
+static int menelaus_tx(i2c_slave *i2c, uint8_t data)
+{
+ struct menelaus_s *s = (struct menelaus_s *) i2c;
+ /* Interpret register address byte */
+ if (s->firstbyte) {
+ s->reg = data;
+ s->firstbyte = 0;
+ } else
+ menelaus_write(s, s->reg ++, data);
+
+ return 0;
+}
+
+static int menelaus_rx(i2c_slave *i2c)
+{
+ struct menelaus_s *s = (struct menelaus_s *) i2c;
+
+ return menelaus_read(s, s->reg ++);
+}
+
+static void tm_put(QEMUFile *f, struct tm *tm) {
+ qemu_put_be16(f, tm->tm_sec);
+ qemu_put_be16(f, tm->tm_min);
+ qemu_put_be16(f, tm->tm_hour);
+ qemu_put_be16(f, tm->tm_mday);
+ qemu_put_be16(f, tm->tm_min);
+ qemu_put_be16(f, tm->tm_year);
+}
+
+static void tm_get(QEMUFile *f, struct tm *tm) {
+ tm->tm_sec = qemu_get_be16(f);
+ tm->tm_min = qemu_get_be16(f);
+ tm->tm_hour = qemu_get_be16(f);
+ tm->tm_mday = qemu_get_be16(f);
+ tm->tm_min = qemu_get_be16(f);
+ tm->tm_year = qemu_get_be16(f);
+}
+
+static void menelaus_save(QEMUFile *f, void *opaque)
+{
+ struct menelaus_s *s = (struct menelaus_s *) opaque;
+
+ qemu_put_be32(f, s->firstbyte);
+ qemu_put_8s(f, &s->reg);
+
+ qemu_put_8s(f, &s->vcore[0]);
+ qemu_put_8s(f, &s->vcore[1]);
+ qemu_put_8s(f, &s->vcore[2]);
+ qemu_put_8s(f, &s->vcore[3]);
+ qemu_put_8s(f, &s->vcore[4]);
+ qemu_put_8s(f, &s->dcdc[3]);
+ qemu_put_8s(f, &s->dcdc[3]);
+ qemu_put_8s(f, &s->dcdc[3]);
+ qemu_put_8s(f, &s->ldo[0]);
+ qemu_put_8s(f, &s->ldo[1]);
+ qemu_put_8s(f, &s->ldo[2]);
+ qemu_put_8s(f, &s->ldo[3]);
+ qemu_put_8s(f, &s->ldo[4]);
+ qemu_put_8s(f, &s->ldo[5]);
+ qemu_put_8s(f, &s->ldo[6]);
+ qemu_put_8s(f, &s->ldo[7]);
+ qemu_put_8s(f, &s->sleep[0]);
+ qemu_put_8s(f, &s->sleep[1]);
+ qemu_put_8s(f, &s->osc);
+ qemu_put_8s(f, &s->detect);
+ qemu_put_be16s(f, &s->mask);
+ qemu_put_be16s(f, &s->status);
+ qemu_put_8s(f, &s->dir);
+ qemu_put_8s(f, &s->inputs);
+ qemu_put_8s(f, &s->outputs);
+ qemu_put_8s(f, &s->bbsms);
+ qemu_put_8s(f, &s->pull[0]);
+ qemu_put_8s(f, &s->pull[1]);
+ qemu_put_8s(f, &s->pull[2]);
+ qemu_put_8s(f, &s->pull[3]);
+ qemu_put_8s(f, &s->mmc_ctrl[0]);
+ qemu_put_8s(f, &s->mmc_ctrl[1]);
+ qemu_put_8s(f, &s->mmc_ctrl[2]);
+ qemu_put_8s(f, &s->mmc_debounce);
+ qemu_put_8s(f, &s->rtc.ctrl);
+ qemu_put_be16s(f, &s->rtc.comp);
+ /* Should be <= 1000 */
+ qemu_put_be16(f, s->rtc.next - qemu_get_clock(rt_clock));
+ tm_put(f, &s->rtc.new);
+ tm_put(f, &s->rtc.alm);
+ qemu_put_byte(f, s->pwrbtn_state);
+
+ i2c_slave_save(f, &s->i2c);
+}
+
+static int menelaus_load(QEMUFile *f, void *opaque, int version_id)
+{
+ struct menelaus_s *s = (struct menelaus_s *) opaque;
+
+ s->firstbyte = qemu_get_be32(f);
+ qemu_get_8s(f, &s->reg);
+
+ if (s->rtc.ctrl & 1) /* RTC_EN */
+ menelaus_rtc_stop(s);
+ qemu_get_8s(f, &s->vcore[0]);
+ qemu_get_8s(f, &s->vcore[1]);
+ qemu_get_8s(f, &s->vcore[2]);
+ qemu_get_8s(f, &s->vcore[3]);
+ qemu_get_8s(f, &s->vcore[4]);
+ qemu_get_8s(f, &s->dcdc[3]);
+ qemu_get_8s(f, &s->dcdc[3]);
+ qemu_get_8s(f, &s->dcdc[3]);
+ qemu_get_8s(f, &s->ldo[0]);
+ qemu_get_8s(f, &s->ldo[1]);
+ qemu_get_8s(f, &s->ldo[2]);
+ qemu_get_8s(f, &s->ldo[3]);
+ qemu_get_8s(f, &s->ldo[4]);
+ qemu_get_8s(f, &s->ldo[5]);
+ qemu_get_8s(f, &s->ldo[6]);
+ qemu_get_8s(f, &s->ldo[7]);
+ qemu_get_8s(f, &s->sleep[0]);
+ qemu_get_8s(f, &s->sleep[1]);
+ qemu_get_8s(f, &s->osc);
+ qemu_get_8s(f, &s->detect);
+ qemu_get_be16s(f, &s->mask);
+ qemu_get_be16s(f, &s->status);
+ qemu_get_8s(f, &s->dir);
+ qemu_get_8s(f, &s->inputs);
+ qemu_get_8s(f, &s->outputs);
+ qemu_get_8s(f, &s->bbsms);
+ qemu_get_8s(f, &s->pull[0]);
+ qemu_get_8s(f, &s->pull[1]);
+ qemu_get_8s(f, &s->pull[2]);
+ qemu_get_8s(f, &s->pull[3]);
+ qemu_get_8s(f, &s->mmc_ctrl[0]);
+ qemu_get_8s(f, &s->mmc_ctrl[1]);
+ qemu_get_8s(f, &s->mmc_ctrl[2]);
+ qemu_get_8s(f, &s->mmc_debounce);
+ qemu_get_8s(f, &s->rtc.ctrl);
+ qemu_get_be16s(f, &s->rtc.comp);
+ s->rtc.next = qemu_get_be16(f);
+ tm_get(f, &s->rtc.new);
+ tm_get(f, &s->rtc.alm);
+ s->pwrbtn_state = qemu_get_byte(f);
+ menelaus_alm_update(s);
+ menelaus_update(s);
+ if (s->rtc.ctrl & 1) /* RTC_EN */
+ menelaus_rtc_start(s);
+
+ i2c_slave_load(f, &s->i2c);
+ return 0;
+}
+
+static int menelaus_iid = 0;
+
+i2c_slave *twl92230_init(i2c_bus *bus, qemu_irq irq)
+{
+ struct menelaus_s *s = (struct menelaus_s *)
+ i2c_slave_init(bus, 0, sizeof(struct menelaus_s));
+
+ s->i2c.event = menelaus_event;
+ s->i2c.recv = menelaus_rx;
+ s->i2c.send = menelaus_tx;
+
+ /* TODO: use the qemu gettime functions */
+ s->rtc.gettime = localtime_r;
+
+ s->irq = irq;
+ s->rtc.hz = qemu_new_timer(rt_clock, menelaus_rtc_hz, s);
+ s->in = qemu_allocate_irqs(menelaus_gpio_set, s, 3);
+ s->pwrbtn = qemu_allocate_irqs(menelaus_pwrbtn_set, s, 1)[0];
+
+ menelaus_reset(&s->i2c);
+
+ register_savevm("menelaus", menelaus_iid ++,
+ 0, menelaus_save, menelaus_load, s);
+
+ return &s->i2c;
+}
+
+qemu_irq *twl92230_gpio_in_get(i2c_slave *i2c)
+{
+ struct menelaus_s *s = (struct menelaus_s *) i2c;
+
+ return s->in;
+}
+
+void twl92230_gpio_out_set(i2c_slave *i2c, int line, qemu_irq handler)
+{
+ struct menelaus_s *s = (struct menelaus_s *) i2c;
+
+ if (line >= 3 || line < 0) {
+ fprintf(stderr, "%s: No GPO line %i\n", __FUNCTION__, line);
+ exit(-1);
+ }
+ s->handler[line] = handler;
+}
Modified: trunk/vl.c
===================================================================
--- trunk/vl.c 2008-04-14 21:28:11 UTC (rev 4214)
+++ trunk/vl.c 2008-04-14 21:57:44 UTC (rev 4215)
@@ -8051,6 +8051,7 @@
qemu_register_machine(&borzoipda_machine);
qemu_register_machine(&terrierpda_machine);
qemu_register_machine(&palmte_machine);
+ qemu_register_machine(&n800_machine);
qemu_register_machine(&lm3s811evb_machine);
qemu_register_machine(&lm3s6965evb_machine);
qemu_register_machine(&connex_machine);
^ permalink raw reply [flat|nested] 7+ messages in thread
* [Qemu-devel] Re: [4215] Nokia N800 machine support (ARM).
2008-04-14 21:57 [Qemu-devel] [4215] Nokia N800 machine support (ARM) Andrzej Zaborowski
@ 2008-04-15 8:00 ` John R. Hogerhuis
2008-04-17 21:58 ` andrzej zaborowski
2008-04-17 20:59 ` consul
1 sibling, 1 reply; 7+ messages in thread
From: John R. Hogerhuis @ 2008-04-15 8:00 UTC (permalink / raw)
To: qemu-devel
Any idea how close the N800 is to the 770?
The 770's are cheaper, (so) I have one. It would be nice to be able to
experiment in QEMU with new software loads.
Thanks,
-- John.
^ permalink raw reply [flat|nested] 7+ messages in thread
* [Qemu-devel] Re: [4215] Nokia N800 machine support (ARM).
2008-04-15 8:00 ` [Qemu-devel] " John R. Hogerhuis
@ 2008-04-17 21:58 ` andrzej zaborowski
0 siblings, 0 replies; 7+ messages in thread
From: andrzej zaborowski @ 2008-04-17 21:58 UTC (permalink / raw)
To: qemu-devel
On 15/04/2008, John R. Hogerhuis <jhoger@pobox.com> wrote:
> Any idea how close the N800 is to the 770?
The CPU is different (omap1710 I think?) and various peripherals,
fortunately some work has been on it earlier (see
http://lists.gnu.org/archive/html/qemu-devel/2007-09/msg00035.html).
Still it's not a trivial amount of work to add 770 now I think.
>
> The 770's are cheaper, (so) I have one. It would be nice to be able to
> experiment in QEMU with new software loads.
A 770 maemo rootfs may be bootable on the n800 if the initfs/kernel
are from an n800, but I'm not sure.
Regards
--
Please do not print this email unless absolutely necessary. Spread
environmental awareness.
^ permalink raw reply [flat|nested] 7+ messages in thread
* [Qemu-devel] Re: [4215] Nokia N800 machine support (ARM).
2008-04-14 21:57 [Qemu-devel] [4215] Nokia N800 machine support (ARM) Andrzej Zaborowski
2008-04-15 8:00 ` [Qemu-devel] " John R. Hogerhuis
@ 2008-04-17 20:59 ` consul
2008-04-17 23:03 ` andrzej zaborowski
1 sibling, 1 reply; 7+ messages in thread
From: consul @ 2008-04-17 20:59 UTC (permalink / raw)
To: qemu-devel
I'm getting an error when executing
qemu-system-arm -M n800 -kernel c:\test\zImage -m 130 ...
mipid_reset: Display off
omap_l4ta_write: Bad register 0x6800a078
zImage is the firmware kernel.
On both Windows and Linux hosts I get the same error and the VM hangs.
Am I missing something? What command line do you use?
"Andrzej Zaborowski" <balrogg@gmail.com> wrote in message
news:E1JlWgL-0001QS-KC@cvs.savannah.gnu.org...
> Revision: 4215
> http://svn.sv.gnu.org/viewvc/?view=rev&root=qemu&revision=4215
> Author: balrog
> Date: 2008-04-14 21:57:44 +0000 (Mon, 14 Apr 2008)
>
> Log Message:
> -----------
> 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.
>
> Modified Paths:
> --------------
> trunk/Makefile
> trunk/Makefile.target
> trunk/hw/boards.h
> trunk/hw/devices.h
> trunk/hw/flash.h
> trunk/hw/i2c.h
> trunk/hw/omap2.c
> trunk/vl.c
>
> Added Paths:
> -----------
> trunk/hw/blizzard.c
> trunk/hw/blizzard_template.h
> trunk/hw/cbus.c
> trunk/hw/nseries.c
> trunk/hw/onenand.c
> trunk/hw/tmp105.c
> trunk/hw/twl92230.c
>
> Modified: trunk/Makefile
> ===================================================================
> --- trunk/Makefile 2008-04-14 21:28:11 UTC (rev 4214)
> +++ trunk/Makefile 2008-04-14 21:57:44 UTC (rev 4215)
> @@ -51,7 +51,8 @@
>
> OBJS+=irq.o
> OBJS+=i2c.o smbus.o smbus_eeprom.o max7310.o max111x.o wm8750.o
> -OBJS+=ssd0303.o ssd0323.o ads7846.o stellaris_input.o
> +OBJS+=ssd0303.o ssd0323.o ads7846.o stellaris_input.o twl92230.o
> +OBJS+=tmp105.o
> OBJS+=scsi-disk.o cdrom.o
> OBJS+=scsi-generic.o
> OBJS+=usb.o usb-hub.o usb-linux.o usb-hid.o usb-msd.o usb-wacom.o
> usb-serial.o
>
> Modified: trunk/Makefile.target
> ===================================================================
> --- trunk/Makefile.target 2008-04-14 21:28:11 UTC (rev 4214)
> +++ trunk/Makefile.target 2008-04-14 21:57:44 UTC (rev 4215)
> @@ -612,6 +612,7 @@
> OBJS+= omap1.o omap_lcdc.o omap_dma.o omap_clk.o omap_mmc.o omap_i2c.o
> OBJS+= omap2.o omap_dss.o
> OBJS+= palm.o tsc210x.o
> +OBJS+= nseries.o blizzard.o onenand.o vga.o cbus.o
> OBJS+= mst_fpga.o mainstone.o
> CPPFLAGS += -DHAS_AUDIO
> endif
>
> Added: trunk/hw/blizzard.c
> ===================================================================
> --- trunk/hw/blizzard.c (rev 0)
> +++ trunk/hw/blizzard.c 2008-04-14 21:57:44 UTC (rev 4215)
> @@ -0,0 +1,1001 @@
> +/*
> + * Epson S1D13744/S1D13745 (Blizzard/Hailstorm/Tornado) LCD/TV
> controller.
> + *
> + * Copyright (C) 2008 Nokia Corporation
> + * Written by Andrzej Zaborowski <andrew@openedhand.com>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation; either version 2 or
> + * (at your option) version 3 of the License.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
> + * MA 02111-1307 USA
> + */
> +
> +#include "qemu-common.h"
> +#include "sysemu.h"
> +#include "console.h"
> +#include "devices.h"
> +#include "vga_int.h"
> +#include "pixel_ops.h"
> +
> +typedef void (*blizzard_fn_t)(uint8_t *, const uint8_t *, unsigned int);
> +
> +struct blizzard_s {
> + uint8_t reg;
> + uint32_t addr;
> + int swallow;
> +
> + int pll;
> + int pll_range;
> + int pll_ctrl;
> + uint8_t pll_mode;
> + uint8_t clksel;
> + int memenable;
> + int memrefresh;
> + uint8_t timing[3];
> + int priority;
> +
> + uint8_t lcd_config;
> + int x;
> + int y;
> + int skipx;
> + int skipy;
> + uint8_t hndp;
> + uint8_t vndp;
> + uint8_t hsync;
> + uint8_t vsync;
> + uint8_t pclk;
> + uint8_t u;
> + uint8_t v;
> + uint8_t yrc[2];
> + int ix[2];
> + int iy[2];
> + int ox[2];
> + int oy[2];
> +
> + int enable;
> + int blank;
> + int bpp;
> + int invalidate;
> + int mx[2];
> + int my[2];
> + uint8_t mode;
> + uint8_t effect;
> + uint8_t iformat;
> + uint8_t source;
> + DisplayState *state;
> + blizzard_fn_t *line_fn_tab[2];
> + void *fb;
> +
> + uint8_t hssi_config[3];
> + uint8_t tv_config;
> + uint8_t tv_timing[4];
> + uint8_t vbi;
> + uint8_t tv_x;
> + uint8_t tv_y;
> + uint8_t tv_test;
> + uint8_t tv_filter_config;
> + uint8_t tv_filter_idx;
> + uint8_t tv_filter_coeff[0x20];
> + uint8_t border_r;
> + uint8_t border_g;
> + uint8_t border_b;
> + uint8_t gamma_config;
> + uint8_t gamma_idx;
> + uint8_t gamma_lut[0x100];
> + uint8_t matrix_ena;
> + uint8_t matrix_coeff[0x12];
> + uint8_t matrix_r;
> + uint8_t matrix_g;
> + uint8_t matrix_b;
> + uint8_t pm;
> + uint8_t status;
> + uint8_t rgbgpio_dir;
> + uint8_t rgbgpio;
> + uint8_t gpio_dir;
> + uint8_t gpio;
> + uint8_t gpio_edge[2];
> + uint8_t gpio_irq;
> + uint8_t gpio_pdown;
> +
> + struct {
> + int x;
> + int y;
> + int dx;
> + int dy;
> + int len;
> + int buflen;
> + void *buf;
> + void *data;
> + uint16_t *ptr;
> + int angle;
> + int pitch;
> + blizzard_fn_t line_fn;
> + } data;
> +};
> +
> +/* Bytes(!) per pixel */
> +static const int blizzard_iformat_bpp[0x10] = {
> + 0,
> + 2, /* RGB 5:6:5*/
> + 3, /* RGB 6:6:6 mode 1 */
> + 3, /* RGB 8:8:8 mode 1 */
> + 0, 0,
> + 4, /* RGB 6:6:6 mode 2 */
> + 4, /* RGB 8:8:8 mode 2 */
> + 0, /* YUV 4:2:2 */
> + 0, /* YUV 4:2:0 */
> + 0, 0, 0, 0, 0, 0,
> +};
> +
> +static inline void blizzard_rgb2yuv(int r, int g, int b,
> + int *y, int *u, int *v)
> +{
> + *y = 0x10 + ((0x838 * r + 0x1022 * g + 0x322 * b) >> 13);
> + *u = 0x80 + ((0xe0e * b - 0x04c1 * r - 0x94e * g) >> 13);
> + *v = 0x80 + ((0xe0e * r - 0x0bc7 * g - 0x247 * b) >> 13);
> +}
> +
> +static void blizzard_window(struct blizzard_s *s)
> +{
> + uint8_t *src, *dst;
> + int bypp[2];
> + int bypl[3];
> + int y;
> + blizzard_fn_t fn = s->data.line_fn;
> +
> + if (!fn)
> + return;
> + if (s->mx[0] > s->data.x)
> + s->mx[0] = s->data.x;
> + if (s->my[0] > s->data.y)
> + s->my[0] = s->data.y;
> + if (s->mx[1] < s->data.x + s->data.dx)
> + s->mx[1] = s->data.x + s->data.dx;
> + if (s->my[1] < s->data.y + s->data.dy)
> + s->my[1] = s->data.y + s->data.dy;
> +
> + bypp[0] = s->bpp;
> + bypp[1] = (s->state->depth + 7) >> 3;
> + bypl[0] = bypp[0] * s->data.pitch;
> + bypl[1] = bypp[1] * s->x;
> + bypl[2] = bypp[0] * s->data.dx;
> +
> + src = s->data.data;
> + dst = s->fb + bypl[1] * s->data.y + bypp[1] * s->data.x;
> + for (y = s->data.dy; y > 0; y --, src += bypl[0], dst += bypl[1])
> + fn(dst, src, bypl[2]);
> +}
> +
> +static int blizzard_transfer_setup(struct blizzard_s *s)
> +{
> + if (s->source > 3 || !s->bpp ||
> + s->ix[1] < s->ix[0] || s->iy[1] < s->iy[0])
> + return 0;
> +
> + s->data.angle = s->effect & 3;
> + s->data.line_fn = s->line_fn_tab[!!s->data.angle][s->iformat];
> + s->data.x = s->ix[0];
> + s->data.y = s->iy[0];
> + s->data.dx = s->ix[1] - s->ix[0] + 1;
> + s->data.dy = s->iy[1] - s->iy[0] + 1;
> + s->data.len = s->bpp * s->data.dx * s->data.dy;
> + s->data.pitch = s->data.dx;
> + if (s->data.len > s->data.buflen) {
> + s->data.buf = realloc(s->data.buf, s->data.len);
> + s->data.buflen = s->data.len;
> + }
> + s->data.ptr = s->data.buf;
> + s->data.data = s->data.buf;
> + s->data.len /= 2;
> + return 1;
> +}
> +
> +static void blizzard_reset(struct blizzard_s *s)
> +{
> + s->reg = 0;
> + s->swallow = 0;
> +
> + s->pll = 9;
> + s->pll_range = 1;
> + s->pll_ctrl = 0x14;
> + s->pll_mode = 0x32;
> + s->clksel = 0x00;
> + s->memenable = 0;
> + s->memrefresh = 0x25c;
> + s->timing[0] = 0x3f;
> + s->timing[1] = 0x13;
> + s->timing[2] = 0x21;
> + s->priority = 0;
> +
> + s->lcd_config = 0x74;
> + s->x = 8;
> + s->y = 1;
> + s->skipx = 0;
> + s->skipy = 0;
> + s->hndp = 3;
> + s->vndp = 2;
> + s->hsync = 1;
> + s->vsync = 1;
> + s->pclk = 0x80;
> +
> + s->ix[0] = 0;
> + s->ix[1] = 0;
> + s->iy[0] = 0;
> + s->iy[1] = 0;
> + s->ox[0] = 0;
> + s->ox[1] = 0;
> + s->oy[0] = 0;
> + s->oy[1] = 0;
> +
> + s->yrc[0] = 0x00;
> + s->yrc[1] = 0x30;
> + s->u = 0;
> + s->v = 0;
> +
> + s->iformat = 3;
> + s->source = 0;
> + s->bpp = blizzard_iformat_bpp[s->iformat];
> +
> + s->hssi_config[0] = 0x00;
> + s->hssi_config[1] = 0x00;
> + s->hssi_config[2] = 0x01;
> + s->tv_config = 0x00;
> + s->tv_timing[0] = 0x00;
> + s->tv_timing[1] = 0x00;
> + s->tv_timing[2] = 0x00;
> + s->tv_timing[3] = 0x00;
> + s->vbi = 0x10;
> + s->tv_x = 0x14;
> + s->tv_y = 0x03;
> + s->tv_test = 0x00;
> + s->tv_filter_config = 0x80;
> + s->tv_filter_idx = 0x00;
> + s->border_r = 0x10;
> + s->border_g = 0x80;
> + s->border_b = 0x80;
> + s->gamma_config = 0x00;
> + s->gamma_idx = 0x00;
> + s->matrix_ena = 0x00;
> + memset(&s->matrix_coeff, 0, sizeof(s->matrix_coeff));
> + s->matrix_r = 0x00;
> + s->matrix_g = 0x00;
> + s->matrix_b = 0x00;
> + s->pm = 0x02;
> + s->status = 0x00;
> + s->rgbgpio_dir = 0x00;
> + s->gpio_dir = 0x00;
> + s->gpio_edge[0] = 0x00;
> + s->gpio_edge[1] = 0x00;
> + s->gpio_irq = 0x00;
> + s->gpio_pdown = 0xff;
> +}
> +
> +static inline void blizzard_invalidate_display(void *opaque) {
> + struct blizzard_s *s = (struct blizzard_s *) opaque;
> +
> + s->invalidate = 1;
> +}
> +
> +static uint16_t blizzard_reg_read(void *opaque, uint8_t reg)
> +{
> + struct blizzard_s *s = (struct blizzard_s *) opaque;
> +
> + switch (reg) {
> + case 0x00: /* Revision Code */
> + return 0xa5;
> +
> + case 0x02: /* Configuration Readback */
> + return 0x83; /* Macrovision OK, CNF[2:0] = 3 */
> +
> + case 0x04: /* PLL M-Divider */
> + return (s->pll - 1) | (1 << 7);
> + case 0x06: /* PLL Lock Range Control */
> + return s->pll_range;
> + case 0x08: /* PLL Lock Synthesis Control 0 */
> + return s->pll_ctrl & 0xff;
> + case 0x0a: /* PLL Lock Synthesis Control 1 */
> + return s->pll_ctrl >> 8;
> + case 0x0c: /* PLL Mode Control 0 */
> + return s->pll_mode;
> +
> + case 0x0e: /* Clock-Source Select */
> + return s->clksel;
> +
> + case 0x10: /* Memory Controller Activate */
> + case 0x14: /* Memory Controller Bank 0 Status Flag */
> + return s->memenable;
> +
> + case 0x18: /* Auto-Refresh Interval Setting 0 */
> + return s->memrefresh & 0xff;
> + case 0x1a: /* Auto-Refresh Interval Setting 1 */
> + return s->memrefresh >> 8;
> +
> + case 0x1c: /* Power-On Sequence Timing Control */
> + return s->timing[0];
> + case 0x1e: /* Timing Control 0 */
> + return s->timing[1];
> + case 0x20: /* Timing Control 1 */
> + return s->timing[2];
> +
> + case 0x24: /* Arbitration Priority Control */
> + return s->priority;
> +
> + case 0x28: /* LCD Panel Configuration */
> + return s->lcd_config;
> +
> + case 0x2a: /* LCD Horizontal Display Width */
> + return s->x >> 3;
> + case 0x2c: /* LCD Horizontal Non-display Period */
> + return s->hndp;
> + case 0x2e: /* LCD Vertical Display Height 0 */
> + return s->y & 0xff;
> + case 0x30: /* LCD Vertical Display Height 1 */
> + return s->y >> 8;
> + case 0x32: /* LCD Vertical Non-display Period */
> + return s->vndp;
> + case 0x34: /* LCD HS Pulse-width */
> + return s->hsync;
> + case 0x36: /* LCd HS Pulse Start Position */
> + return s->skipx >> 3;
> + case 0x38: /* LCD VS Pulse-width */
> + return s->vsync;
> + case 0x3a: /* LCD VS Pulse Start Position */
> + return s->skipy;
> +
> + case 0x3c: /* PCLK Polarity */
> + return s->pclk;
> +
> + case 0x3e: /* High-speed Serial Interface Tx Configuration Port 0 */
> + return s->hssi_config[0];
> + case 0x40: /* High-speed Serial Interface Tx Configuration Port 1 */
> + return s->hssi_config[1];
> + case 0x42: /* High-speed Serial Interface Tx Mode */
> + return s->hssi_config[2];
> + case 0x44: /* TV Display Configuration */
> + return s->tv_config;
> + case 0x46 ... 0x4c: /* TV Vertical Blanking Interval Data bits */
> + return s->tv_timing[(reg - 0x46) >> 1];
> + case 0x4e: /* VBI: Closed Caption / XDS Control / Status */
> + return s->vbi;
> + case 0x50: /* TV Horizontal Start Position */
> + return s->tv_x;
> + case 0x52: /* TV Vertical Start Position */
> + return s->tv_y;
> + case 0x54: /* TV Test Pattern Setting */
> + return s->tv_test;
> + case 0x56: /* TV Filter Setting */
> + return s->tv_filter_config;
> + case 0x58: /* TV Filter Coefficient Index */
> + return s->tv_filter_idx;
> + case 0x5a: /* TV Filter Coefficient Data */
> + if (s->tv_filter_idx < 0x20)
> + return s->tv_filter_coeff[s->tv_filter_idx ++];
> + return 0;
> +
> + case 0x60: /* Input YUV/RGB Translate Mode 0 */
> + return s->yrc[0];
> + case 0x62: /* Input YUV/RGB Translate Mode 1 */
> + return s->yrc[1];
> + case 0x64: /* U Data Fix */
> + return s->u;
> + case 0x66: /* V Data Fix */
> + return s->v;
> +
> + case 0x68: /* Display Mode */
> + return s->mode;
> +
> + case 0x6a: /* Special Effects */
> + return s->effect;
> +
> + case 0x6c: /* Input Window X Start Position 0 */
> + return s->ix[0] & 0xff;
> + case 0x6e: /* Input Window X Start Position 1 */
> + return s->ix[0] >> 3;
> + case 0x70: /* Input Window Y Start Position 0 */
> + return s->ix[0] & 0xff;
> + case 0x72: /* Input Window Y Start Position 1 */
> + return s->ix[0] >> 3;
> + case 0x74: /* Input Window X End Position 0 */
> + return s->ix[1] & 0xff;
> + case 0x76: /* Input Window X End Position 1 */
> + return s->ix[1] >> 3;
> + case 0x78: /* Input Window Y End Position 0 */
> + return s->ix[1] & 0xff;
> + case 0x7a: /* Input Window Y End Position 1 */
> + return s->ix[1] >> 3;
> + case 0x7c: /* Output Window X Start Position 0 */
> + return s->ox[0] & 0xff;
> + case 0x7e: /* Output Window X Start Position 1 */
> + return s->ox[0] >> 3;
> + case 0x80: /* Output Window Y Start Position 0 */
> + return s->oy[0] & 0xff;
> + case 0x82: /* Output Window Y Start Position 1 */
> + return s->oy[0] >> 3;
> + case 0x84: /* Output Window X End Position 0 */
> + return s->ox[1] & 0xff;
> + case 0x86: /* Output Window X End Position 1 */
> + return s->ox[1] >> 3;
> + case 0x88: /* Output Window Y End Position 0 */
> + return s->oy[1] & 0xff;
> + case 0x8a: /* Output Window Y End Position 1 */
> + return s->oy[1] >> 3;
> +
> + case 0x8c: /* Input Data Format */
> + return s->iformat;
> + case 0x8e: /* Data Source Select */
> + return s->source;
> + case 0x90: /* Display Memory Data Port */
> + return 0;
> +
> + case 0xa8: /* Border Color 0 */
> + return s->border_r;
> + case 0xaa: /* Border Color 1 */
> + return s->border_g;
> + case 0xac: /* Border Color 2 */
> + return s->border_b;
> +
> + case 0xb4: /* Gamma Correction Enable */
> + return s->gamma_config;
> + case 0xb6: /* Gamma Correction Table Index */
> + return s->gamma_idx;
> + case 0xb8: /* Gamma Correction Table Data */
> + return s->gamma_lut[s->gamma_idx ++];
> +
> + case 0xba: /* 3x3 Matrix Enable */
> + return s->matrix_ena;
> + case 0xbc ... 0xde: /* Coefficient Registers */
> + return s->matrix_coeff[(reg - 0xbc) >> 1];
> + case 0xe0: /* 3x3 Matrix Red Offset */
> + return s->matrix_r;
> + case 0xe2: /* 3x3 Matrix Green Offset */
> + return s->matrix_g;
> + case 0xe4: /* 3x3 Matrix Blue Offset */
> + return s->matrix_b;
> +
> + case 0xe6: /* Power-save */
> + return s->pm;
> + case 0xe8: /* Non-display Period Control / Status */
> + return s->status | (1 << 5);
> + case 0xea: /* RGB Interface Control */
> + return s->rgbgpio_dir;
> + case 0xec: /* RGB Interface Status */
> + return s->rgbgpio;
> + case 0xee: /* General-purpose IO Pins Configuration */
> + return s->gpio_dir;
> + case 0xf0: /* General-purpose IO Pins Status / Control */
> + return s->gpio;
> + case 0xf2: /* GPIO Positive Edge Interrupt Trigger */
> + return s->gpio_edge[0];
> + case 0xf4: /* GPIO Negative Edge Interrupt Trigger */
> + return s->gpio_edge[1];
> + case 0xf6: /* GPIO Interrupt Status */
> + return s->gpio_irq;
> + case 0xf8: /* GPIO Pull-down Control */
> + return s->gpio_pdown;
> +
> + default:
> + fprintf(stderr, "%s: unknown register %02x\n", __FUNCTION__,
> reg);
> + return 0;
> + }
> +}
> +
> +static void blizzard_reg_write(void *opaque, uint8_t reg, uint16_t value)
> +{
> + struct blizzard_s *s = (struct blizzard_s *) opaque;
> +
> + switch (reg) {
> + case 0x04: /* PLL M-Divider */
> + s->pll = (value & 0x3f) + 1;
> + break;
> + case 0x06: /* PLL Lock Range Control */
> + s->pll_range = value & 3;
> + break;
> + case 0x08: /* PLL Lock Synthesis Control 0 */
> + s->pll_ctrl &= 0xf00;
> + s->pll_ctrl |= (value << 0) & 0x0ff;
> + break;
> + case 0x0a: /* PLL Lock Synthesis Control 1 */
> + s->pll_ctrl &= 0x0ff;
> + s->pll_ctrl |= (value << 8) & 0xf00;
> + break;
> + case 0x0c: /* PLL Mode Control 0 */
> + s->pll_mode = value & 0x77;
> + if ((value & 3) == 0 || (value & 3) == 3)
> + fprintf(stderr, "%s: wrong PLL Control bits (%i)\n",
> + __FUNCTION__, value & 3);
> + break;
> +
> + case 0x0e: /* Clock-Source Select */
> + s->clksel = value & 0xff;
> + break;
> +
> + case 0x10: /* Memory Controller Activate */
> + s->memenable = value & 1;
> + break;
> + case 0x14: /* Memory Controller Bank 0 Status Flag */
> + break;
> +
> + case 0x18: /* Auto-Refresh Interval Setting 0 */
> + s->memrefresh &= 0xf00;
> + s->memrefresh |= (value << 0) & 0x0ff;
> + break;
> + case 0x1a: /* Auto-Refresh Interval Setting 1 */
> + s->memrefresh &= 0x0ff;
> + s->memrefresh |= (value << 8) & 0xf00;
> + break;
> +
> + case 0x1c: /* Power-On Sequence Timing Control */
> + s->timing[0] = value & 0x7f;
> + break;
> + case 0x1e: /* Timing Control 0 */
> + s->timing[1] = value & 0x17;
> + break;
> + case 0x20: /* Timing Control 1 */
> + s->timing[2] = value & 0x35;
> + break;
> +
> + case 0x24: /* Arbitration Priority Control */
> + s->priority = value & 1;
> + break;
> +
> + case 0x28: /* LCD Panel Configuration */
> + s->lcd_config = value & 0xff;
> + if (value & (1 << 7))
> + fprintf(stderr, "%s: data swap not supported!\n",
> __FUNCTION__);
> + break;
> +
> + case 0x2a: /* LCD Horizontal Display Width */
> + s->x = value << 3;
> + break;
> + case 0x2c: /* LCD Horizontal Non-display Period */
> + s->hndp = value & 0xff;
> + break;
> + case 0x2e: /* LCD Vertical Display Height 0 */
> + s->y &= 0x300;
> + s->y |= (value << 0) & 0x0ff;
> + break;
> + case 0x30: /* LCD Vertical Display Height 1 */
> + s->y &= 0x0ff;
> + s->y |= (value << 8) & 0x300;
> + break;
> + case 0x32: /* LCD Vertical Non-display Period */
> + s->vndp = value & 0xff;
> + break;
> + case 0x34: /* LCD HS Pulse-width */
> + s->hsync = value & 0xff;
> + break;
> + case 0x36: /* LCD HS Pulse Start Position */
> + s->skipx = value & 0xff;
> + break;
> + case 0x38: /* LCD VS Pulse-width */
> + s->vsync = value & 0xbf;
> + break;
> + case 0x3a: /* LCD VS Pulse Start Position */
> + s->skipy = value & 0xff;
> + break;
> +
> + case 0x3c: /* PCLK Polarity */
> + s->pclk = value & 0x82;
> + /* Affects calculation of s->hndp, s->hsync and s->skipx. */
> + break;
> +
> + case 0x3e: /* High-speed Serial Interface Tx Configuration Port 0 */
> + s->hssi_config[0] = value;
> + break;
> + case 0x40: /* High-speed Serial Interface Tx Configuration Port 1 */
> + s->hssi_config[1] = value;
> + if (((value >> 4) & 3) == 3)
> + fprintf(stderr, "%s: Illegal active-data-links value\n",
> + __FUNCTION__);
> + break;
> + case 0x42: /* High-speed Serial Interface Tx Mode */
> + s->hssi_config[2] = value & 0xbd;
> + break;
> +
> + case 0x44: /* TV Display Configuration */
> + s->tv_config = value & 0xfe;
> + break;
> + case 0x46 ... 0x4c: /* TV Vertical Blanking Interval Data bits 0 */
> + s->tv_timing[(reg - 0x46) >> 1] = value;
> + break;
> + case 0x4e: /* VBI: Closed Caption / XDS Control / Status */
> + s->vbi = value;
> + break;
> + case 0x50: /* TV Horizontal Start Position */
> + s->tv_x = value;
> + break;
> + case 0x52: /* TV Vertical Start Position */
> + s->tv_y = value & 0x7f;
> + break;
> + case 0x54: /* TV Test Pattern Setting */
> + s->tv_test = value;
> + break;
> + case 0x56: /* TV Filter Setting */
> + s->tv_filter_config = value & 0xbf;
> + break;
> + case 0x58: /* TV Filter Coefficient Index */
> + s->tv_filter_idx = value & 0x1f;
> + break;
> + case 0x5a: /* TV Filter Coefficient Data */
> + if (s->tv_filter_idx < 0x20)
> + s->tv_filter_coeff[s->tv_filter_idx ++] = value;
> + break;
> +
> + case 0x60: /* Input YUV/RGB Translate Mode 0 */
> + s->yrc[0] = value & 0xb0;
> + break;
> + case 0x62: /* Input YUV/RGB Translate Mode 1 */
> + s->yrc[1] = value & 0x30;
> + break;
> + case 0x64: /* U Data Fix */
> + s->u = value & 0xff;
> + break;
> + case 0x66: /* V Data Fix */
> + s->v = value & 0xff;
> + break;
> +
> + case 0x68: /* Display Mode */
> + if ((s->mode ^ value) & 3)
> + s->invalidate = 1;
> + s->mode = value & 0xb7;
> + s->enable = value & 1;
> + s->blank = (value >> 1) & 1;
> + if (value & (1 << 4))
> + fprintf(stderr, "%s: Macrovision enable attempt!\n",
> __FUNCTION__);
> + break;
> +
> + case 0x6a: /* Special Effects */
> + s->effect = value & 0xfb;
> + break;
> +
> + case 0x6c: /* Input Window X Start Position 0 */
> + s->ix[0] &= 0x300;
> + s->ix[0] |= (value << 0) & 0x0ff;
> + break;
> + case 0x6e: /* Input Window X Start Position 1 */
> + s->ix[0] &= 0x0ff;
> + s->ix[0] |= (value << 8) & 0x300;
> + break;
> + case 0x70: /* Input Window Y Start Position 0 */
> + s->iy[0] &= 0x300;
> + s->iy[0] |= (value << 0) & 0x0ff;
> + break;
> + case 0x72: /* Input Window Y Start Position 1 */
> + s->iy[0] &= 0x0ff;
> + s->iy[0] |= (value << 8) & 0x300;
> + break;
> + case 0x74: /* Input Window X End Position 0 */
> + s->ix[1] &= 0x300;
> + s->ix[1] |= (value << 0) & 0x0ff;
> + break;
> + case 0x76: /* Input Window X End Position 1 */
> + s->ix[1] &= 0x0ff;
> + s->ix[1] |= (value << 8) & 0x300;
> + break;
> + case 0x78: /* Input Window Y End Position 0 */
> + s->iy[1] &= 0x300;
> + s->iy[1] |= (value << 0) & 0x0ff;
> + break;
> + case 0x7a: /* Input Window Y End Position 1 */
> + s->iy[1] &= 0x0ff;
> + s->iy[1] |= (value << 8) & 0x300;
> + break;
> + case 0x7c: /* Output Window X Start Position 0 */
> + s->ox[0] &= 0x300;
> + s->ox[0] |= (value << 0) & 0x0ff;
> + break;
> + case 0x7e: /* Output Window X Start Position 1 */
> + s->ox[0] &= 0x0ff;
> + s->ox[0] |= (value << 8) & 0x300;
> + break;
> + case 0x80: /* Output Window Y Start Position 0 */
> + s->oy[0] &= 0x300;
> + s->oy[0] |= (value << 0) & 0x0ff;
> + break;
> + case 0x82: /* Output Window Y Start Position 1 */
> + s->oy[0] &= 0x0ff;
> + s->oy[0] |= (value << 8) & 0x300;
> + break;
> + case 0x84: /* Output Window X End Position 0 */
> + s->ox[1] &= 0x300;
> + s->ox[1] |= (value << 0) & 0x0ff;
> + break;
> + case 0x86: /* Output Window X End Position 1 */
> + s->ox[1] &= 0x0ff;
> + s->ox[1] |= (value << 8) & 0x300;
> + break;
> + case 0x88: /* Output Window Y End Position 0 */
> + s->oy[1] &= 0x300;
> + s->oy[1] |= (value << 0) & 0x0ff;
> + break;
> + case 0x8a: /* Output Window Y End Position 1 */
> + s->oy[1] &= 0x0ff;
> + s->oy[1] |= (value << 8) & 0x300;
> + break;
> +
> + case 0x8c: /* Input Data Format */
> + s->iformat = value & 0xf;
> + s->bpp = blizzard_iformat_bpp[s->iformat];
> + if (!s->bpp)
> + fprintf(stderr, "%s: Illegal or unsupported input format
> %x\n",
> + __FUNCTION__, s->iformat);
> + break;
> + case 0x8e: /* Data Source Select */
> + s->source = value & 7;
> + /* Currently all windows will be "destructive overlays". */
> + if ((!(s->effect & (1 << 3)) && (s->ix[0] != s->ox[0] ||
> + s->iy[0] != s->oy[0] ||
> + s->ix[1] != s->ox[1] ||
> + s->iy[1] != s->oy[1])) ||
> + !((s->ix[1] - s->ix[0]) & (s->iy[1] - s->iy[0]) &
> + (s->ox[1] - s->ox[0]) & (s->oy[1] - s->oy[0]) &
> 1))
> + fprintf(stderr, "%s: Illegal input/output window
> positions\n",
> + __FUNCTION__);
> +
> + blizzard_transfer_setup(s);
> + break;
> +
> + case 0x90: /* Display Memory Data Port */
> + if (!s->data.len && !blizzard_transfer_setup(s))
> + break;
> +
> + *s->data.ptr ++ = value;
> + if (-- s->data.len == 0)
> + blizzard_window(s);
> + break;
> +
> + case 0xa8: /* Border Color 0 */
> + s->border_r = value;
> + break;
> + case 0xaa: /* Border Color 1 */
> + s->border_g = value;
> + break;
> + case 0xac: /* Border Color 2 */
> + s->border_b = value;
> + break;
> +
> + case 0xb4: /* Gamma Correction Enable */
> + s->gamma_config = value & 0x87;
> + break;
> + case 0xb6: /* Gamma Correction Table Index */
> + s->gamma_idx = value;
> + break;
> + case 0xb8: /* Gamma Correction Table Data */
> + s->gamma_lut[s->gamma_idx ++] = value;
> + break;
> +
> + case 0xba: /* 3x3 Matrix Enable */
> + s->matrix_ena = value & 1;
> + break;
> + case 0xbc ... 0xde: /* Coefficient Registers */
> + s->matrix_coeff[(reg - 0xbc) >> 1] = value & ((reg & 2) ? 0x80 :
> 0xff);
> + break;
> + case 0xe0: /* 3x3 Matrix Red Offset */
> + s->matrix_r = value;
> + break;
> + case 0xe2: /* 3x3 Matrix Green Offset */
> + s->matrix_g = value;
> + break;
> + case 0xe4: /* 3x3 Matrix Blue Offset */
> + s->matrix_b = value;
> + break;
> +
> + case 0xe6: /* Power-save */
> + s->pm = value & 0x83;
> + if (value & s->mode & 1)
> + fprintf(stderr, "%s: The display must be disabled before
> entering "
> + "Standby Mode\n", __FUNCTION__);
> + break;
> + case 0xe8: /* Non-display Period Control / Status */
> + s->status = value & 0x1b;
> + break;
> + case 0xea: /* RGB Interface Control */
> + s->rgbgpio_dir = value & 0x8f;
> + break;
> + case 0xec: /* RGB Interface Status */
> + s->rgbgpio = value & 0xcf;
> + break;
> + case 0xee: /* General-purpose IO Pins Configuration */
> + s->gpio_dir = value;
> + break;
> + case 0xf0: /* General-purpose IO Pins Status / Control */
> + s->gpio = value;
> + break;
> + case 0xf2: /* GPIO Positive Edge Interrupt Trigger */
> + s->gpio_edge[0] = value;
> + break;
> + case 0xf4: /* GPIO Negative Edge Interrupt Trigger */
> + s->gpio_edge[1] = value;
> + break;
> + case 0xf6: /* GPIO Interrupt Status */
> + s->gpio_irq &= value;
> + break;
> + case 0xf8: /* GPIO Pull-down Control */
> + s->gpio_pdown = value;
> + break;
> +
> + default:
> + fprintf(stderr, "%s: unknown register %02x\n", __FUNCTION__,
> reg);
> + break;
> + }
> +}
> +
> +uint16_t s1d13745_read(void *opaque, int dc)
> +{
> + struct blizzard_s *s = (struct blizzard_s *) opaque;
> + uint16_t value = blizzard_reg_read(s, s->reg);
> +
> + if (s->swallow -- > 0)
> + return 0;
> + if (dc)
> + s->reg ++;
> +
> + return value;
> +}
> +
> +void s1d13745_write(void *opaque, int dc, uint16_t value)
> +{
> + struct blizzard_s *s = (struct blizzard_s *) opaque;
> +
> + if (s->swallow -- > 0)
> + return;
> + if (dc) {
> + blizzard_reg_write(s, s->reg, value);
> +
> + if (s->reg != 0x90 && s->reg != 0x5a && s->reg != 0xb8)
> + s->reg += 2;
> + } else
> + s->reg = value & 0xff;
> +}
> +
> +void s1d13745_write_block(void *opaque, int dc,
> + void *buf, size_t len, int pitch)
> +{
> + struct blizzard_s *s = (struct blizzard_s *) opaque;
> +
> + while (len > 0) {
> + if (s->reg == 0x90 && dc &&
> + (s->data.len || blizzard_transfer_setup(s)) &&
> + len >= (s->data.len << 1)) {
> + len -= s->data.len << 1;
> + s->data.len = 0;
> + s->data.data = buf;
> + if (pitch)
> + s->data.pitch = pitch;
> + blizzard_window(s);
> + s->data.data = s->data.buf;
> + continue;
> + }
> +
> + s1d13745_write(opaque, dc, *(uint16_t *) buf);
> + len -= 2;
> + buf += 2;
> + }
> +
> + return;
> +}
> +
> +static void blizzard_update_display(void *opaque)
> +{
> + struct blizzard_s *s = (struct blizzard_s *) opaque;
> + int y, bypp, bypl, bwidth;
> + uint8_t *src, *dst;
> +
> + if (!s->enable)
> + return;
> +
> + if (s->x != s->state->width || s->y != s->state->height) {
> + s->invalidate = 1;
> + dpy_resize(s->state, s->x, s->y);
> + }
> +
> + if (s->invalidate) {
> + s->invalidate = 0;
> +
> + if (s->blank) {
> + bypp = (s->state->depth + 7) >> 3;
> + memset(s->state->data, 0, bypp * s->x * s->y);
> + return;
> + }
> +
> + s->mx[0] = 0;
> + s->mx[1] = s->x;
> + s->my[0] = 0;
> + s->my[1] = s->y;
> + }
> +
> + if (s->mx[1] <= s->mx[0])
> + return;
> +
> + bypp = (s->state->depth + 7) >> 3;
> + bypl = bypp * s->x;
> + bwidth = bypp * (s->mx[1] - s->mx[0]);
> + y = s->my[0];
> + src = s->fb + bypl * y + bypp * s->mx[0];
> + dst = s->state->data + bypl * y + bypp * s->mx[0];
> + for (; y < s->my[1]; y ++, src += bypl, dst += bypl)
> + memcpy(dst, src, bwidth);
> +
> + dpy_update(s->state, s->mx[0], s->my[0],
> + s->mx[1] - s->mx[0], y - s->my[0]);
> +
> + s->mx[0] = s->x;
> + s->mx[1] = 0;
> + s->my[0] = s->y;
> + s->my[1] = 0;
> +}
> +
> +static void blizzard_screen_dump(void *opaque, const char *filename) {
> + struct blizzard_s *s = (struct blizzard_s *) opaque;
> +
> + blizzard_update_display(opaque);
> + if (s && s->state->data)
> + ppm_save(filename, s->state->data, s->x, s->y,
> s->state->linesize);
> +}
> +
> +#define DEPTH 8
> +#include "blizzard_template.h"
> +#define DEPTH 15
> +#include "blizzard_template.h"
> +#define DEPTH 16
> +#include "blizzard_template.h"
> +#define DEPTH 24
> +#include "blizzard_template.h"
> +#define DEPTH 32
> +#include "blizzard_template.h"
> +
> +void *s1d13745_init(qemu_irq gpio_int, DisplayState *ds)
> +{
> + struct blizzard_s *s = (struct blizzard_s *)
> qemu_mallocz(sizeof(*s));
> +
> + s->state = ds;
> + s->fb = qemu_malloc(0x180000);
> +
> + switch (s->state->depth) {
> + case 0:
> + s->line_fn_tab[0] = s->line_fn_tab[1] =
> + qemu_mallocz(sizeof(blizzard_fn_t) * 0x10);
> + break;
> + case 8:
> + s->line_fn_tab[0] = blizzard_draw_fn_8;
> + s->line_fn_tab[1] = blizzard_draw_fn_r_8;
> + break;
> + case 15:
> + s->line_fn_tab[0] = blizzard_draw_fn_15;
> + s->line_fn_tab[1] = blizzard_draw_fn_r_15;
> + break;
> + case 16:
> + s->line_fn_tab[0] = blizzard_draw_fn_16;
> + s->line_fn_tab[1] = blizzard_draw_fn_r_16;
> + break;
> + case 24:
> + s->line_fn_tab[0] = blizzard_draw_fn_24;
> + s->line_fn_tab[1] = blizzard_draw_fn_r_24;
> + break;
> + case 32:
> + s->line_fn_tab[0] = blizzard_draw_fn_32;
> + s->line_fn_tab[1] = blizzard_draw_fn_r_32;
> + break;
> + default:
> + fprintf(stderr, "%s: Bad color depth\n", __FUNCTION__);
> + exit(1);
> + }
> +
> + blizzard_reset(s);
> +
> + graphic_console_init(s->state, blizzard_update_display,
> + blizzard_invalidate_display, blizzard_screen_dump,
> + NULL, s);
> +
> + return s;
> +}
>
> Added: trunk/hw/blizzard_template.h
> ===================================================================
> --- trunk/hw/blizzard_template.h (rev 0)
> +++ trunk/hw/blizzard_template.h 2008-04-14 21:57:44 UTC (rev 4215)
> @@ -0,0 +1,138 @@
> +/*
> + * QEMU Epson S1D13744/S1D13745 templates
> + *
> + * Copyright (C) 2008 Nokia Corporation
> + * Written by Andrzej Zaborowski <andrew@openedhand.com>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation; either version 2 or
> + * (at your option) version 3 of the License.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
> + * MA 02111-1307 USA
> + */
> +
> +#define SKIP_PIXEL(to) to += deststep
> +#if DEPTH == 8
> +# define PIXEL_TYPE uint8_t
> +# define COPY_PIXEL(to, from) *to = from; SKIP_PIXEL(to)
> +# define COPY_PIXEL1(to, from) *to ++ = from
> +#elif DEPTH == 15 || DEPTH == 16
> +# define PIXEL_TYPE uint16_t
> +# define COPY_PIXEL(to, from) *to = from; SKIP_PIXEL(to)
> +# define COPY_PIXEL1(to, from) *to ++ = from
> +#elif DEPTH == 24
> +# define PIXEL_TYPE uint8_t
> +# define COPY_PIXEL(to, from) \
> + to[0] = from; to[1] = (from) >> 8; to[2] = (from) >> 16;
> SKIP_PIXEL(to)
> +# define COPY_PIXEL1(to, from) \
> + *to ++ = from; *to ++ = (from) >> 8; *to ++ = (from) >> 16
> +#elif DEPTH == 32
> +# define PIXEL_TYPE uint32_t
> +# define COPY_PIXEL(to, from) *to = from; SKIP_PIXEL(to)
> +# define COPY_PIXEL1(to, from) *to ++ = from
> +#else
> +# error unknown bit depth
> +#endif
> +
> +#ifdef WORDS_BIGENDIAN
> +# define SWAP_WORDS 1
> +#endif
> +
> +static void glue(blizzard_draw_line16_, DEPTH)(PIXEL_TYPE *dest,
> + const uint16_t *src, unsigned int width)
> +{
> +#if !defined(SWAP_WORDS) && DEPTH == 16
> + memcpy(dest, src, width << 1);
> +#else
> + uint16_t data;
> + unsigned int r, g, b;
> + const uint16_t *end = (void *) src + width;
> + while (src < end) {
> + data = lduw_raw(src ++);
> + b = (data & 0x1f) << 3;
> + data >>= 5;
> + g = (data & 0x3f) << 2;
> + data >>= 6;
> + r = (data & 0x1f) << 3;
> + data >>= 5;
> + COPY_PIXEL1(dest, glue(rgb_to_pixel, DEPTH)(r, g, b));
> + }
> +#endif
> +}
> +
> +static void glue(blizzard_draw_line24mode1_, DEPTH)(PIXEL_TYPE *dest,
> + const uint8_t *src, unsigned int width)
> +{
> + /* TODO: check if SDL 24-bit planes are not in the same format and
> + * if so, use memcpy */
> + unsigned int r[2], g[2], b[2];
> + const uint8_t *end = src + width;
> + while (src < end) {
> + g[0] = *src ++;
> + r[0] = *src ++;
> + r[1] = *src ++;
> + b[0] = *src ++;
> + COPY_PIXEL1(dest, glue(rgb_to_pixel, DEPTH)(r[0], g[0], b[0]));
> + b[1] = *src ++;
> + g[1] = *src ++;
> + COPY_PIXEL1(dest, glue(rgb_to_pixel, DEPTH)(r[1], g[1], b[1]));
> + }
> +}
> +
> +static void glue(blizzard_draw_line24mode2_, DEPTH)(PIXEL_TYPE *dest,
> + const uint8_t *src, unsigned int width)
> +{
> + unsigned int r, g, b;
> + const uint8_t *end = src + width;
> + while (src < end) {
> + r = *src ++;
> + src ++;
> + b = *src ++;
> + g = *src ++;
> + COPY_PIXEL1(dest, glue(rgb_to_pixel, DEPTH)(r, g, b));
> + }
> +}
> +
> +/* No rotation */
> +static blizzard_fn_t glue(blizzard_draw_fn_, DEPTH)[0x10] = {
> + NULL,
> + /* RGB 5:6:5*/
> + (blizzard_fn_t) glue(blizzard_draw_line16_, DEPTH),
> + /* RGB 6:6:6 mode 1 */
> + (blizzard_fn_t) glue(blizzard_draw_line24mode1_, DEPTH),
> + /* RGB 8:8:8 mode 1 */
> + (blizzard_fn_t) glue(blizzard_draw_line24mode1_, DEPTH),
> + NULL, NULL,
> + /* RGB 6:6:6 mode 2 */
> + (blizzard_fn_t) glue(blizzard_draw_line24mode2_, DEPTH),
> + /* RGB 8:8:8 mode 2 */
> + (blizzard_fn_t) glue(blizzard_draw_line24mode2_, DEPTH),
> + /* YUV 4:2:2 */
> + NULL,
> + /* YUV 4:2:0 */
> + NULL,
> + NULL, NULL, NULL, NULL, NULL, NULL,
> +};
> +
> +/* 90deg, 180deg and 270deg rotation */
> +static blizzard_fn_t glue(blizzard_draw_fn_r_, DEPTH)[0x10] = {
> + /* TODO */
> + [0 ... 0xf] = NULL,
> +};
> +
> +#undef DEPTH
> +#undef SKIP_PIXEL
> +#undef COPY_PIXEL
> +#undef COPY_PIXEL1
> +#undef PIXEL_TYPE
> +
> +#undef SWAP_WORDS
>
> Modified: trunk/hw/boards.h
> ===================================================================
> --- trunk/hw/boards.h 2008-04-14 21:28:11 UTC (rev 4214)
> +++ trunk/hw/boards.h 2008-04-14 21:57:44 UTC (rev 4215)
> @@ -81,6 +81,9 @@
> /* palm.c */
> extern QEMUMachine palmte_machine;
>
> +/* nseries.c */
> +extern QEMUMachine n800_machine;
> +
> /* gumstix.c */
> extern QEMUMachine connex_machine;
> extern QEMUMachine verdex_machine;
>
> Added: trunk/hw/cbus.c
> ===================================================================
> --- trunk/hw/cbus.c (rev 0)
> +++ trunk/hw/cbus.c 2008-04-14 21:57:44 UTC (rev 4215)
> @@ -0,0 +1,624 @@
> +/*
> + * CBUS three-pin bus and the Retu / Betty / Tahvo / Vilma / Avilma /
> + * Hinku / Vinku / Ahne / Pihi chips used in various Nokia platforms.
> + * Based on reverse-engineering of a linux driver.
> + *
> + * Copyright (C) 2008 Nokia Corporation
> + * Written by Andrzej Zaborowski <andrew@openedhand.com>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation; either version 2 or
> + * (at your option) version 3 of the License.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
> + * MA 02111-1307 USA
> + */
> +
> +#include "qemu-common.h"
> +#include "irq.h"
> +#include "devices.h"
> +#include "sysemu.h"
> +
> +//#define DEBUG
> +
> +struct cbus_slave_s;
> +struct cbus_priv_s {
> + struct cbus_s cbus;
> +
> + int sel;
> + int dat;
> + int clk;
> + int bit;
> + int dir;
> + uint16_t val;
> + qemu_irq dat_out;
> +
> + int addr;
> + int reg;
> + int rw;
> + enum {
> + cbus_address,
> + cbus_value,
> + } cycle;
> +
> + struct cbus_slave_s *slave[8];
> +};
> +
> +struct cbus_slave_s {
> + void *opaque;
> + void (*io)(void *opaque, int rw, int reg, uint16_t *val);
> + int addr;
> +};
> +
> +static void cbus_io(struct cbus_priv_s *s)
> +{
> + if (s->slave[s->addr])
> + s->slave[s->addr]->io(s->slave[s->addr]->opaque,
> + s->rw, s->reg, &s->val);
> + else
> + cpu_abort(cpu_single_env, "%s: bad slave address %i\n",
> + __FUNCTION__, s->addr);
> +}
> +
> +static void cbus_cycle(struct cbus_priv_s *s)
> +{
> + switch (s->cycle) {
> + case cbus_address:
> + s->addr = (s->val >> 6) & 7;
> + s->rw = (s->val >> 5) & 1;
> + s->reg = (s->val >> 0) & 0x1f;
> +
> + s->cycle = cbus_value;
> + s->bit = 15;
> + s->dir = !s->rw;
> + s->val = 0;
> +
> + if (s->rw)
> + cbus_io(s);
> + break;
> +
> + case cbus_value:
> + if (!s->rw)
> + cbus_io(s);
> +
> + s->cycle = cbus_address;
> + s->bit = 8;
> + s->dir = 1;
> + s->val = 0;
> + break;
> + }
> +}
> +
> +static void cbus_clk(void *opaque, int line, int level)
> +{
> + struct cbus_priv_s *s = (struct cbus_priv_s *) opaque;
> +
> + if (!s->sel && level && !s->clk) {
> + if (s->dir)
> + s->val |= s->dat << (s->bit --);
> + else
> + qemu_set_irq(s->dat_out, (s->val >> (s->bit --)) & 1);
> +
> + if (s->bit < 0)
> + cbus_cycle(s);
> + }
> +
> + s->clk = level;
> +}
> +
> +static void cbus_dat(void *opaque, int line, int level)
> +{
> + struct cbus_priv_s *s = (struct cbus_priv_s *) opaque;
> +
> + s->dat = level;
> +}
> +
> +static void cbus_sel(void *opaque, int line, int level)
> +{
> + struct cbus_priv_s *s = (struct cbus_priv_s *) opaque;
> +
> + if (!level) {
> + s->dir = 1;
> + s->bit = 8;
> + s->val = 0;
> + }
> +
> + s->sel = level;
> +}
> +
> +struct cbus_s *cbus_init(qemu_irq dat)
> +{
> + struct cbus_priv_s *s = (struct cbus_priv_s *)
> qemu_mallocz(sizeof(*s));
> +
> + s->dat_out = dat;
> + s->cbus.clk = qemu_allocate_irqs(cbus_clk, s, 1)[0];
> + s->cbus.dat = qemu_allocate_irqs(cbus_dat, s, 1)[0];
> + s->cbus.sel = qemu_allocate_irqs(cbus_sel, s, 1)[0];
> +
> + s->sel = 1;
> + s->clk = 0;
> + s->dat = 0;
> +
> + return &s->cbus;
> +}
> +
> +void cbus_attach(struct cbus_s *bus, void *slave_opaque)
> +{
> + struct cbus_slave_s *slave = (struct cbus_slave_s *) slave_opaque;
> + struct cbus_priv_s *s = (struct cbus_priv_s *) bus;
> +
> + s->slave[slave->addr] = slave;
> +}
> +
> +/* Retu/Vilma */
> +struct cbus_retu_s {
> + uint16_t irqst;
> + uint16_t irqen;
> + uint16_t cc[2];
> + int channel;
> + uint16_t result[16];
> + uint16_t sample;
> + uint16_t status;
> +
> + struct {
> + uint16_t cal;
> + } rtc;
> +
> + int is_vilma;
> + qemu_irq irq;
> + struct cbus_slave_s cbus;
> +};
> +
> +static void retu_interrupt_update(struct cbus_retu_s *s)
> +{
> + qemu_set_irq(s->irq, s->irqst & ~s->irqen);
> +}
> +
> +#define RETU_REG_ASICR 0x00 /* (RO) ASIC ID & revision */
> +#define RETU_REG_IDR 0x01 /* (T) Interrupt ID */
> +#define RETU_REG_IMR 0x02 /* (RW) Interrupt mask */
> +#define RETU_REG_RTCDSR 0x03 /* (RW) RTC seconds register */
> +#define RETU_REG_RTCHMR 0x04 /* (RO) RTC hours and minutes reg */
> +#define RETU_REG_RTCHMAR 0x05 /* (RW) RTC hours and minutes set reg */
> +#define RETU_REG_RTCCALR 0x06 /* (RW) RTC calibration register */
> +#define RETU_REG_ADCR 0x08 /* (RW) ADC result register */
> +#define RETU_REG_ADCSCR 0x09 /* (RW) ADC sample control register */
> +#define RETU_REG_AFCR 0x0a /* (RW) AFC register */
> +#define RETU_REG_ANTIFR 0x0b /* (RW) AntiF register */
> +#define RETU_REG_CALIBR 0x0c /* (RW) CalibR register*/
> +#define RETU_REG_CCR1 0x0d /* (RW) Common control register 1 */
> +#define RETU_REG_CCR2 0x0e /* (RW) Common control register 2 */
> +#define RETU_REG_RCTRL_CLR 0x0f /* (T) Regulator clear register */
> +#define RETU_REG_RCTRL_SET 0x10 /* (T) Regulator set register */
> +#define RETU_REG_TXCR 0x11 /* (RW) TxC register */
> +#define RETU_REG_STATUS 0x16 /* (RO) Status register */
> +#define RETU_REG_WATCHDOG 0x17 /* (RW) Watchdog register */
> +#define RETU_REG_AUDTXR 0x18 /* (RW) Audio Codec Tx register */
> +#define RETU_REG_AUDPAR 0x19 /* (RW) AudioPA register */
> +#define RETU_REG_AUDRXR1 0x1a /* (RW) Audio receive register 1 */
> +#define RETU_REG_AUDRXR2 0x1b /* (RW) Audio receive register 2 */
> +#define RETU_REG_SGR1 0x1c /* (RW) */
> +#define RETU_REG_SCR1 0x1d /* (RW) */
> +#define RETU_REG_SGR2 0x1e /* (RW) */
> +#define RETU_REG_SCR2 0x1f /* (RW) */
> +
> +/* Retu Interrupt sources */
> +enum {
> + retu_int_pwr = 0, /* Power button */
> + retu_int_char = 1, /* Charger */
> + retu_int_rtcs = 2, /* Seconds */
> + retu_int_rtcm = 3, /* Minutes */
> + retu_int_rtcd = 4, /* Days */
> + retu_int_rtca = 5, /* Alarm */
> + retu_int_hook = 6, /* Hook */
> + retu_int_head = 7, /* Headset */
> + retu_int_adcs = 8, /* ADC sample */
> +};
> +
> +/* Retu ADC channel wiring */
> +enum {
> + retu_adc_bsi = 1, /* BSI */
> + retu_adc_batt_temp = 2, /* Battery temperature */
> + retu_adc_chg_volt = 3, /* Charger voltage */
> + retu_adc_head_det = 4, /* Headset detection */
> + retu_adc_hook_det = 5, /* Hook detection */
> + retu_adc_rf_gp = 6, /* RF GP */
> + retu_adc_tx_det = 7, /* Wideband Tx detection */
> + retu_adc_batt_volt = 8, /* Battery voltage */
> + retu_adc_sens = 10, /* Light sensor */
> + retu_adc_sens_temp = 11, /* Light sensor temperature */
> + retu_adc_bbatt_volt = 12, /* Backup battery voltage */
> + retu_adc_self_temp = 13, /* RETU temperature */
> +};
> +
> +static inline uint16_t retu_read(struct cbus_retu_s *s, int reg)
> +{
> +#ifdef DEBUG
> + printf("RETU read at %02x\n", reg);
> +#endif
> +
> + switch (reg) {
> + case RETU_REG_ASICR:
> + return 0x0215 | (s->is_vilma << 7);
> +
> + case RETU_REG_IDR: /* TODO: Or is this ffs(s->irqst)? */
> + return s->irqst;
> +
> + case RETU_REG_IMR:
> + return s->irqen;
> +
> + case RETU_REG_RTCDSR:
> + case RETU_REG_RTCHMR:
> + case RETU_REG_RTCHMAR:
> + /* TODO */
> + return 0x0000;
> +
> + case RETU_REG_RTCCALR:
> + return s->rtc.cal;
> +
> + case RETU_REG_ADCR:
> + return (s->channel << 10) | s->result[s->channel];
> + case RETU_REG_ADCSCR:
> + return s->sample;
> +
> + case RETU_REG_AFCR:
> + case RETU_REG_ANTIFR:
> + case RETU_REG_CALIBR:
> + /* TODO */
> + return 0x0000;
> +
> + case RETU_REG_CCR1:
> + return s->cc[0];
> + case RETU_REG_CCR2:
> + return s->cc[1];
> +
> + case RETU_REG_RCTRL_CLR:
> + case RETU_REG_RCTRL_SET:
> + case RETU_REG_TXCR:
> + /* TODO */
> + return 0x0000;
> +
> + case RETU_REG_STATUS:
> + return s->status;
> +
> + case RETU_REG_WATCHDOG:
> + case RETU_REG_AUDTXR:
> + case RETU_REG_AUDPAR:
> + case RETU_REG_AUDRXR1:
> + case RETU_REG_AUDRXR2:
> + case RETU_REG_SGR1:
> + case RETU_REG_SCR1:
> + case RETU_REG_SGR2:
> + case RETU_REG_SCR2:
> + /* TODO */
> + return 0x0000;
> +
> + default:
> + cpu_abort(cpu_single_env, "%s: bad register %02x\n",
> + __FUNCTION__, reg);
> + }
> +}
> +
> +static inline void retu_write(struct cbus_retu_s *s, int reg, uint16_t
> val)
> +{
> +#ifdef DEBUG
> + printf("RETU write of %04x at %02x\n", val, reg);
> +#endif
> +
> + switch (reg) {
> + case RETU_REG_IDR:
> + s->irqst ^= val;
> + retu_interrupt_update(s);
> + break;
> +
> + case RETU_REG_IMR:
> + s->irqen = val;
> + retu_interrupt_update(s);
> + break;
> +
> + case RETU_REG_RTCDSR:
> + case RETU_REG_RTCHMAR:
> + /* TODO */
> + break;
> +
> + case RETU_REG_RTCCALR:
> + s->rtc.cal = val;
> + break;
> +
> + case RETU_REG_ADCR:
> + s->channel = (val >> 10) & 0xf;
> + s->irqst |= 1 << retu_int_adcs;
> + retu_interrupt_update(s);
> + break;
> + case RETU_REG_ADCSCR:
> + s->sample &= ~val;
> + break;
> +
> + case RETU_REG_AFCR:
> + case RETU_REG_ANTIFR:
> + case RETU_REG_CALIBR:
> +
> + case RETU_REG_CCR1:
> + s->cc[0] = val;
> + break;
> + case RETU_REG_CCR2:
> + s->cc[1] = val;
> + break;
> +
> + case RETU_REG_RCTRL_CLR:
> + case RETU_REG_RCTRL_SET:
> + /* TODO */
> + break;
> +
> + case RETU_REG_WATCHDOG:
> + if (val == 0 && (s->cc[0] & 2))
> + qemu_system_shutdown_request();
> + break;
> +
> + case RETU_REG_TXCR:
> + case RETU_REG_AUDTXR:
> + case RETU_REG_AUDPAR:
> + case RETU_REG_AUDRXR1:
> + case RETU_REG_AUDRXR2:
> + case RETU_REG_SGR1:
> + case RETU_REG_SCR1:
> + case RETU_REG_SGR2:
> + case RETU_REG_SCR2:
> + /* TODO */
> + break;
> +
> + default:
> + cpu_abort(cpu_single_env, "%s: bad register %02x\n",
> + __FUNCTION__, reg);
> + }
> +}
> +
> +static void retu_io(void *opaque, int rw, int reg, uint16_t *val)
> +{
> + struct cbus_retu_s *s = (struct cbus_retu_s *) opaque;
> +
> + if (rw)
> + *val = retu_read(s, reg);
> + else
> + retu_write(s, reg, *val);
> +}
> +
> +void *retu_init(qemu_irq irq, int vilma)
> +{
> + struct cbus_retu_s *s = (struct cbus_retu_s *)
> qemu_mallocz(sizeof(*s));
> +
> + s->irq = irq;
> + s->irqen = 0xffff;
> + s->irqst = 0x0000;
> + s->status = 0x0020;
> + s->is_vilma = !!vilma;
> + s->rtc.cal = 0x01;
> + s->result[retu_adc_bsi] = 0x3c2;
> + s->result[retu_adc_batt_temp] = 0x0fc;
> + s->result[retu_adc_chg_volt] = 0x165;
> + s->result[retu_adc_head_det] = 123;
> + s->result[retu_adc_hook_det] = 1023;
> + s->result[retu_adc_rf_gp] = 0x11;
> + s->result[retu_adc_tx_det] = 0x11;
> + s->result[retu_adc_batt_volt] = 0x250;
> + s->result[retu_adc_sens] = 2;
> + s->result[retu_adc_sens_temp] = 0x11;
> + s->result[retu_adc_bbatt_volt] = 0x3d0;
> + s->result[retu_adc_self_temp] = 0x330;
> +
> + s->cbus.opaque = s;
> + s->cbus.io = retu_io;
> + s->cbus.addr = 1;
> +
> + return &s->cbus;
> +}
> +
> +void retu_key_event(void *retu, int state)
> +{
> + struct cbus_slave_s *slave = (struct cbus_slave_s *) retu;
> + struct cbus_retu_s *s = (struct cbus_retu_s *) slave->opaque;
> +
> + s->irqst |= 1 << retu_int_pwr;
> + retu_interrupt_update(s);
> +
> + if (state)
> + s->status &= ~(1 << 5);
> + else
> + s->status |= 1 << 5;
> +}
> +
> +void retu_head_event(void *retu, int state)
> +{
> + struct cbus_slave_s *slave = (struct cbus_slave_s *) retu;
> + struct cbus_retu_s *s = (struct cbus_retu_s *) slave->opaque;
> +
> + if ((s->cc[0] & 0x500) == 0x500) { /* TODO: Which bits? */
> + /* TODO: reissue the interrupt every 100ms or so. */
> + s->irqst |= 1 << retu_int_head;
> + retu_interrupt_update(s);
> + }
> +
> + if (state)
> + s->result[retu_adc_head_det] = 50;
> + else
> + s->result[retu_adc_head_det] = 123;
> +}
> +
> +void retu_hook_event(void *retu, int state)
> +{
> + struct cbus_slave_s *slave = (struct cbus_slave_s *) retu;
> + struct cbus_retu_s *s = (struct cbus_retu_s *) slave->opaque;
> +
> + if ((s->cc[0] & 0x500) == 0x500) {
> + /* TODO: reissue the interrupt every 100ms or so. */
> + s->irqst |= 1 << retu_int_hook;
> + retu_interrupt_update(s);
> + }
> +
> + if (state)
> + s->result[retu_adc_hook_det] = 50;
> + else
> + s->result[retu_adc_hook_det] = 123;
> +}
> +
> +/* Tahvo/Betty */
> +struct cbus_tahvo_s {
> + uint16_t irqst;
> + uint16_t irqen;
> + uint8_t charger;
> + uint8_t backlight;
> + uint16_t usbr;
> + uint16_t power;
> +
> + int is_betty;
> + qemu_irq irq;
> + struct cbus_slave_s cbus;
> +};
> +
> +static void tahvo_interrupt_update(struct cbus_tahvo_s *s)
> +{
> + qemu_set_irq(s->irq, s->irqst & ~s->irqen);
> +}
> +
> +#define TAHVO_REG_ASICR 0x00 /* (RO) ASIC ID & revision */
> +#define TAHVO_REG_IDR 0x01 /* (T) Interrupt ID */
> +#define TAHVO_REG_IDSR 0x02 /* (RO) Interrupt status */
> +#define TAHVO_REG_IMR 0x03 /* (RW) Interrupt mask */
> +#define TAHVO_REG_CHAPWMR 0x04 /* (RW) Charger PWM */
> +#define TAHVO_REG_LEDPWMR 0x05 /* (RW) LED PWM */
> +#define TAHVO_REG_USBR 0x06 /* (RW) USB control */
> +#define TAHVO_REG_RCR 0x07 /* (RW) Some kind of power management */
> +#define TAHVO_REG_CCR1 0x08 /* (RW) Common control register 1 */
> +#define TAHVO_REG_CCR2 0x09 /* (RW) Common control register 2 */
> +#define TAHVO_REG_TESTR1 0x0a /* (RW) Test register 1 */
> +#define TAHVO_REG_TESTR2 0x0b /* (RW) Test register 2 */
> +#define TAHVO_REG_NOPR 0x0c /* (RW) Number of periods */
> +#define TAHVO_REG_FRR 0x0d /* (RO) FR */
> +
> +static inline uint16_t tahvo_read(struct cbus_tahvo_s *s, int reg)
> +{
> +#ifdef DEBUG
> + printf("TAHVO read at %02x\n", reg);
> +#endif
> +
> + switch (reg) {
> + case TAHVO_REG_ASICR:
> + return 0x0021 | (s->is_betty ? 0x0b00 : 0x0300); /* 22 in N810 */
> +
> + case TAHVO_REG_IDR:
> + case TAHVO_REG_IDSR: /* XXX: what does this do? */
> + return s->irqst;
> +
> + case TAHVO_REG_IMR:
> + return s->irqen;
> +
> + case TAHVO_REG_CHAPWMR:
> + return s->charger;
> +
> + case TAHVO_REG_LEDPWMR:
> + return s->backlight;
> +
> + case TAHVO_REG_USBR:
> + return s->usbr;
> +
> + case TAHVO_REG_RCR:
> + return s->power;
> +
> + case TAHVO_REG_CCR1:
> + case TAHVO_REG_CCR2:
> + case TAHVO_REG_TESTR1:
> + case TAHVO_REG_TESTR2:
> + case TAHVO_REG_NOPR:
> + case TAHVO_REG_FRR:
> + return 0x0000;
> +
> + default:
> + cpu_abort(cpu_single_env, "%s: bad register %02x\n",
> + __FUNCTION__, reg);
> + }
> +}
> +
> +static inline void tahvo_write(struct cbus_tahvo_s *s, int reg, uint16_t
> val)
> +{
> +#ifdef DEBUG
> + printf("TAHVO write of %04x at %02x\n", val, reg);
> +#endif
> +
> + switch (reg) {
> + case TAHVO_REG_IDR:
> + s->irqst ^= val;
> + tahvo_interrupt_update(s);
> + break;
> +
> + case TAHVO_REG_IMR:
> + s->irqen = val;
> + tahvo_interrupt_update(s);
> + break;
> +
> + case TAHVO_REG_CHAPWMR:
> + s->charger = val;
> + break;
> +
> + case TAHVO_REG_LEDPWMR:
> + if (s->backlight != (val & 0x7f)) {
> + s->backlight = val & 0x7f;
> + printf("%s: LCD backlight now at %i / 127\n",
> + __FUNCTION__, s->backlight);
> + }
> + break;
> +
> + case TAHVO_REG_USBR:
> + s->usbr = val;
> + break;
> +
> + case TAHVO_REG_RCR:
> + s->power = val;
> + break;
> +
> + case TAHVO_REG_CCR1:
> + case TAHVO_REG_CCR2:
> + case TAHVO_REG_TESTR1:
> + case TAHVO_REG_TESTR2:
> + case TAHVO_REG_NOPR:
> + case TAHVO_REG_FRR:
> + break;
> +
> + default:
> + cpu_abort(cpu_single_env, "%s: bad register %02x\n",
> + __FUNCTION__, reg);
> + }
> +}
> +
> +static void tahvo_io(void *opaque, int rw, int reg, uint16_t *val)
> +{
> + struct cbus_tahvo_s *s = (struct cbus_tahvo_s *) opaque;
> +
> + if (rw)
> + *val = tahvo_read(s, reg);
> + else
> + tahvo_write(s, reg, *val);
> +}
> +
> +void *tahvo_init(qemu_irq irq, int betty)
> +{
> + struct cbus_tahvo_s *s = (struct cbus_tahvo_s *)
> qemu_mallocz(sizeof(*s));
> +
> + s->irq = irq;
> + s->irqen = 0xffff;
> + s->irqst = 0x0000;
> + s->is_betty = !!betty;
> +
> + s->cbus.opaque = s;
> + s->cbus.io = tahvo_io;
> + s->cbus.addr = 2;
> +
> + return &s->cbus;
> +}
>
> Modified: trunk/hw/devices.h
> ===================================================================
> --- trunk/hw/devices.h 2008-04-14 21:28:11 UTC (rev 4214)
> +++ trunk/hw/devices.h 2008-04-14 21:57:44 UTC (rev 4215)
> @@ -31,4 +31,25 @@
> /* stellaris_input.c */
> void stellaris_gamepad_init(int n, qemu_irq *irq, const int *keycode);
>
> +/* blizzard.c */
> +void *s1d13745_init(qemu_irq gpio_int, DisplayState *ds);
> +void s1d13745_write(void *opaque, int dc, uint16_t value);
> +void s1d13745_write_block(void *opaque, int dc,
> + void *buf, size_t len, int pitch);
> +uint16_t s1d13745_read(void *opaque, int dc);
> +
> +/* cbus.c */
> +struct cbus_s {
> + qemu_irq clk;
> + qemu_irq dat;
> + qemu_irq sel;
> +};
> +struct cbus_s *cbus_init(qemu_irq dat_out);
> +void cbus_attach(struct cbus_s *bus, void *slave_opaque);
> +
> +void *retu_init(qemu_irq irq, int vilma);
> +void *tahvo_init(qemu_irq irq, int betty);
> +
> +void retu_key_event(void *retu, int state);
> +
> #endif
>
> Modified: trunk/hw/flash.h
> ===================================================================
> --- trunk/hw/flash.h 2008-04-14 21:28:11 UTC (rev 4214)
> +++ trunk/hw/flash.h 2008-04-14 21:57:44 UTC (rev 4215)
> @@ -34,6 +34,11 @@
> #define NAND_MFR_HYNIX 0xad
> #define NAND_MFR_MICRON 0x2c
>
> +/* onenand.c */
> +void onenand_base_update(void *opaque, target_phys_addr_t new);
> +void onenand_base_unmap(void *opaque);
> +void *onenand_init(uint32_t id, int regshift, qemu_irq irq);
> +
> /* ecc.c */
> struct ecc_state_s {
> uint8_t cp; /* Column parity */
>
> Modified: trunk/hw/i2c.h
> ===================================================================
> --- trunk/hw/i2c.h 2008-04-14 21:28:11 UTC (rev 4214)
> +++ trunk/hw/i2c.h 2008-04-14 21:57:44 UTC (rev 4215)
> @@ -71,4 +71,14 @@
> /* ssd0303.c */
> void ssd0303_init(DisplayState *ds, i2c_bus *bus, int address);
>
> +/* twl92230.c */
> +i2c_slave *twl92230_init(i2c_bus *bus, qemu_irq irq);
> +qemu_irq *twl92230_gpio_in_get(i2c_slave *i2c);
> +void twl92230_gpio_out_set(i2c_slave *i2c, int line, qemu_irq handler);
> +
> +/* tmp105.c */
> +struct i2c_slave *tmp105_init(i2c_bus *bus, qemu_irq alarm);
> +void tmp105_reset(i2c_slave *i2c);
> +void tmp105_set(i2c_slave *i2c, int temp);
> +
> #endif
>
> Added: trunk/hw/nseries.c
> ===================================================================
> --- trunk/hw/nseries.c (rev 0)
> +++ trunk/hw/nseries.c 2008-04-14 21:57:44 UTC (rev 4215)
> @@ -0,0 +1,918 @@
> +/*
> + * Nokia N-series internet tablets.
> + *
> + * Copyright (C) 2007 Nokia Corporation
> + * Written by Andrzej Zaborowski <andrew@openedhand.com>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation; either version 2 or
> + * (at your option) version 3 of the License.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
> + * MA 02111-1307 USA
> + */
> +
> +#include "qemu-common.h"
> +#include "sysemu.h"
> +#include "omap.h"
> +#include "arm-misc.h"
> +#include "irq.h"
> +#include "console.h"
> +#include "boards.h"
> +#include "i2c.h"
> +#include "devices.h"
> +#include "flash.h"
> +#include "hw.h"
> +
> +/* Nokia N8x0 support */
> +struct n800_s {
> + struct omap_mpu_state_s *cpu;
> +
> + struct rfbi_chip_s blizzard;
> + struct uwire_slave_s *ts;
> + i2c_bus *i2c;
> +
> + int keymap[0x80];
> +
> + void *retu;
> + void *tahvo;
> +};
> +
> +/* GPIO pins */
> +#define N800_TUSB_ENABLE_GPIO 0
> +#define N800_MMC2_WP_GPIO 8
> +#define N800_UNKNOWN_GPIO0 9 /* out */
> +#define N800_UNKNOWN_GPIO1 10 /* out */
> +#define N800_CAM_TURN_GPIO 12
> +#define N800_BLIZZARD_POWERDOWN_GPIO 15
> +#define N800_MMC1_WP_GPIO 23
> +#define N8X0_ONENAND_GPIO 26
> +#define N800_UNKNOWN_GPIO2 53 /* out */
> +#define N8X0_TUSB_INT_GPIO 58
> +#define N800_BT_WKUP_GPIO 61
> +#define N800_STI_GPIO 62
> +#define N8X0_CBUS_SEL_GPIO 64
> +#define N8X0_CBUS_CLK_GPIO 65 /* sure? */
> +#define N8X0_CBUS_DAT_GPIO 66
> +#define N800_WLAN_IRQ_GPIO 87
> +#define N800_BT_RESET_GPIO 92
> +#define N800_TEA5761_CS_GPIO 93
> +#define N800_UNKNOWN_GPIO 94
> +#define N800_CAM_ACT_GPIO 95
> +#define N800_MMC_CS_GPIO 96
> +#define N800_WLAN_PWR_GPIO 97
> +#define N8X0_BT_HOST_WKUP_GPIO 98
> +#define N800_UNKNOWN_GPIO3 101 /* out */
> +#define N810_KB_LOCK_GPIO 102
> +#define N800_TSC_TS_GPIO 103
> +#define N810_TSC2005_GPIO 106
> +#define N800_HEADPHONE_GPIO 107
> +#define N8X0_RETU_GPIO 108
> +#define N800_TSC_KP_IRQ_GPIO 109
> +#define N810_KEYBOARD_GPIO 109
> +#define N800_BAT_COVER_GPIO 110
> +#define N810_SLIDE_GPIO 110
> +#define N8X0_TAHVO_GPIO 111
> +#define N800_UNKNOWN_GPIO4 112 /* out */
> +#define N810_TSC_RESET_GPIO 118
> +#define N800_TSC_RESET_GPIO 119 /* ? */
> +#define N8X0_TMP105_GPIO 125
> +
> +/* Config */
> +#define XLDR_LL_UART 1
> +
> +/* Addresses on the I2C bus */
> +#define N8X0_TMP105_ADDR 0x48
> +#define N8X0_MENELAUS_ADDR 0x72
> +
> +/* Chipselects on GPMC NOR interface */
> +#define N8X0_ONENAND_CS 0
> +#define N8X0_USB_ASYNC_CS 1
> +#define N8X0_USB_SYNC_CS 4
> +
> +static void n800_mmc_cs_cb(void *opaque, int line, int level)
> +{
> + /* TODO: this seems to actually be connected to the menelaus, to
> + * which also both MMC slots connect. */
> + omap_mmc_enable((struct omap_mmc_s *) opaque, !level);
> +
> + printf("%s: MMC slot %i active\n", __FUNCTION__, level + 1);
> +}
> +
> +static void n800_gpio_setup(struct n800_s *s)
> +{
> + qemu_irq *mmc_cs = qemu_allocate_irqs(n800_mmc_cs_cb, s->cpu->mmc,
> 1);
> + omap2_gpio_out_set(s->cpu->gpif, N800_MMC_CS_GPIO, mmc_cs[0]);
> +
> + qemu_irq_lower(omap2_gpio_in_get(s->cpu->gpif,
> N800_BAT_COVER_GPIO)[0]);
> +}
> +
> +static void n8x0_nand_setup(struct n800_s *s)
> +{
> + /* Either ec40xx or ec48xx are OK for the ID */
> + omap_gpmc_attach(s->cpu->gpmc, N8X0_ONENAND_CS, 0,
> onenand_base_update,
> + onenand_base_unmap,
> + onenand_init(0xec4800, 1,
> + omap2_gpio_in_get(s->cpu->gpif,
> + N8X0_ONENAND_GPIO)[0]));
> +}
> +
> +static void n800_i2c_setup(struct n800_s *s)
> +{
> + qemu_irq tmp_irq = omap2_gpio_in_get(s->cpu->gpif,
> N8X0_TMP105_GPIO)[0];
> +
> + /* Attach the CPU on one end of our I2C bus. */
> + s->i2c = omap_i2c_bus(s->cpu->i2c[0]);
> +
> + /* Attach a menelaus PM chip */
> + i2c_set_slave_address(
> + twl92230_init(s->i2c,
> + s->cpu->irq[0][OMAP_INT_24XX_SYS_NIRQ]),
> + N8X0_MENELAUS_ADDR);
> +
> + /* Attach a TMP105 PM chip (A0 wired to ground) */
> + i2c_set_slave_address(tmp105_init(s->i2c, tmp_irq),
> N8X0_TMP105_ADDR);
> +}
> +
> +/* Touchscreen and keypad controller */
> +#define RETU_KEYCODE 61 /* F3 */
> +
> +static void n800_key_event(void *opaque, int keycode)
> +{
> + struct n800_s *s = (struct n800_s *) opaque;
> + int code = s->keymap[keycode & 0x7f];
> +
> + if (code == -1) {
> + if ((keycode & 0x7f) == RETU_KEYCODE)
> + retu_key_event(s->retu, !(keycode & 0x80));
> + return;
> + }
> +
> + tsc210x_key_event(s->ts, code, !(keycode & 0x80));
> +}
> +
> +static const int n800_keys[16] = {
> + -1,
> + 72, /* Up */
> + 63, /* Home (F5) */
> + -1,
> + 75, /* Left */
> + 28, /* Enter */
> + 77, /* Right */
> + -1,
> + 1, /* Cycle (ESC) */
> + 80, /* Down */
> + 62, /* Menu (F4) */
> + -1,
> + 66, /* Zoom- (F8) */
> + 64, /* FS (F6) */
> + 65, /* Zoom+ (F7) */
> + -1,
> +};
> +
> +static struct mouse_transform_info_s n800_pointercal = {
> + .x = 800,
> + .y = 480,
> + .a = { 14560, -68, -3455208, -39, -9621, 35152972, 65536 },
> +};
> +
> +static void n800_tsc_setup(struct n800_s *s)
> +{
> + int i;
> +
> + /* XXX: are the three pins inverted inside the chip between the
> + * tsc and the cpu (N4111)? */
> + qemu_irq penirq = 0; /* NC */
> + qemu_irq kbirq = omap2_gpio_in_get(s->cpu->gpif,
> N800_TSC_KP_IRQ_GPIO)[0];
> + qemu_irq dav = omap2_gpio_in_get(s->cpu->gpif, N800_TSC_TS_GPIO)[0];
> +
> + s->ts = tsc2301_init(penirq, kbirq, dav, 0);
> +
> + for (i = 0; i < 0x80; i ++)
> + s->keymap[i] = -1;
> + for (i = 0; i < 0x10; i ++)
> + if (n800_keys[i] >= 0)
> + s->keymap[n800_keys[i]] = i;
> +
> + qemu_add_kbd_event_handler(n800_key_event, s);
> +
> + tsc210x_set_transform(s->ts, &n800_pointercal);
> +}
> +
> +/* LCD MIPI DBI-C controller (URAL) */
> +struct mipid_s {
> + int resp[4];
> + int param[4];
> + int p;
> + int pm;
> + int cmd;
> +
> + int sleep;
> + int booster;
> + int te;
> + int selfcheck;
> + int partial;
> + int normal;
> + int vscr;
> + int invert;
> + int onoff;
> + int gamma;
> + uint32_t id;
> +};
> +
> +static void mipid_reset(struct mipid_s *s)
> +{
> + if (!s->sleep)
> + fprintf(stderr, "%s: Display off\n", __FUNCTION__);
> +
> + s->pm = 0;
> + s->cmd = 0;
> +
> + s->sleep = 1;
> + s->booster = 0;
> + s->selfcheck =
> + (1 << 7) | /* Register loading OK. */
> + (1 << 5) | /* The chip is attached. */
> + (1 << 4); /* Display glass still in one piece. */
> + s->te = 0;
> + s->partial = 0;
> + s->normal = 1;
> + s->vscr = 0;
> + s->invert = 0;
> + s->onoff = 1;
> + s->gamma = 0;
> +}
> +
> +static uint32_t mipid_txrx(void *opaque, uint32_t cmd)
> +{
> + struct mipid_s *s = (struct mipid_s *) opaque;
> + uint8_t ret;
> +
> + if (s->p >= sizeof(s->resp) / sizeof(*s->resp))
> + ret = 0;
> + else
> + ret = s->resp[s->p ++];
> + if (s->pm --> 0)
> + s->param[s->pm] = cmd;
> + else
> + s->cmd = cmd;
> +
> + switch (s->cmd) {
> + case 0x00: /* NOP */
> + break;
> +
> + case 0x01: /* SWRESET */
> + mipid_reset(s);
> + break;
> +
> + case 0x02: /* BSTROFF */
> + s->booster = 0;
> + break;
> + case 0x03: /* BSTRON */
> + s->booster = 1;
> + break;
> +
> + case 0x04: /* RDDID */
> + s->p = 0;
> + s->resp[0] = (s->id >> 16) & 0xff;
> + s->resp[1] = (s->id >> 8) & 0xff;
> + s->resp[2] = (s->id >> 0) & 0xff;
> + break;
> +
> + case 0x06: /* RD_RED */
> + case 0x07: /* RD_GREEN */
> + /* XXX the bootloader sometimes issues RD_BLUE meaning RDDID so
> + * for the bootloader one needs to change this. */
> + case 0x08: /* RD_BLUE */
> + s->p = 0;
> + /* TODO: return first pixel components */
> + s->resp[0] = 0x01;
> + break;
> +
> + case 0x09: /* RDDST */
> + s->p = 0;
> + s->resp[0] = s->booster << 7;
> + s->resp[1] = (5 << 4) | (s->partial << 2) |
> + (s->sleep << 1) | s->normal;
> + s->resp[2] = (s->vscr << 7) | (s->invert << 5) |
> + (s->onoff << 2) | (s->te << 1) | (s->gamma >> 2);
> + s->resp[3] = s->gamma << 6;
> + break;
> +
> + case 0x0a: /* RDDPM */
> + s->p = 0;
> + s->resp[0] = (s->onoff << 2) | (s->normal << 3) | (s->sleep << 4)
> |
> + (s->partial << 5) | (s->sleep << 6) | (s->booster << 7);
> + break;
> + case 0x0b: /* RDDMADCTR */
> + s->p = 0;
> + s->resp[0] = 0;
> + break;
> + case 0x0c: /* RDDCOLMOD */
> + s->p = 0;
> + s->resp[0] = 5; /* 65K colours */
> + break;
> + case 0x0d: /* RDDIM */
> + s->p = 0;
> + s->resp[0] = (s->invert << 5) | (s->vscr << 7) | s->gamma;
> + break;
> + case 0x0e: /* RDDSM */
> + s->p = 0;
> + s->resp[0] = s->te << 7;
> + break;
> + case 0x0f: /* RDDSDR */
> + s->p = 0;
> + s->resp[0] = s->selfcheck;
> + break;
> +
> + case 0x10: /* SLPIN */
> + s->sleep = 1;
> + break;
> + case 0x11: /* SLPOUT */
> + s->sleep = 0;
> + s->selfcheck ^= 1 << 6; /* POFF self-diagnosis Ok */
> + break;
> +
> + case 0x12: /* PTLON */
> + s->partial = 1;
> + s->normal = 0;
> + s->vscr = 0;
> + break;
> + case 0x13: /* NORON */
> + s->partial = 0;
> + s->normal = 1;
> + s->vscr = 0;
> + break;
> +
> + case 0x20: /* INVOFF */
> + s->invert = 0;
> + break;
> + case 0x21: /* INVON */
> + s->invert = 1;
> + break;
> +
> + case 0x22: /* APOFF */
> + case 0x23: /* APON */
> + goto bad_cmd;
> +
> + case 0x25: /* WRCNTR */
> + if (s->pm < 0)
> + s->pm = 1;
> + goto bad_cmd;
> +
> + case 0x26: /* GAMSET */
> + if (!s->pm)
> + s->gamma = ffs(s->param[0] & 0xf) - 1;
> + else if (s->pm < 0)
> + s->pm = 1;
> + break;
> +
> + case 0x28: /* DISPOFF */
> + s->onoff = 0;
> + fprintf(stderr, "%s: Display off\n", __FUNCTION__);
> + break;
> + case 0x29: /* DISPON */
> + s->onoff = 1;
> + fprintf(stderr, "%s: Display on\n", __FUNCTION__);
> + break;
> +
> + case 0x2a: /* CASET */
> + case 0x2b: /* RASET */
> + case 0x2c: /* RAMWR */
> + case 0x2d: /* RGBSET */
> + case 0x2e: /* RAMRD */
> + case 0x30: /* PTLAR */
> + case 0x33: /* SCRLAR */
> + goto bad_cmd;
> +
> + case 0x34: /* TEOFF */
> + s->te = 0;
> + break;
> + case 0x35: /* TEON */
> + if (!s->pm)
> + s->te = 1;
> + else if (s->pm < 0)
> + s->pm = 1;
> + break;
> +
> + case 0x36: /* MADCTR */
> + goto bad_cmd;
> +
> + case 0x37: /* VSCSAD */
> + s->partial = 0;
> + s->normal = 0;
> + s->vscr = 1;
> + break;
> +
> + case 0x38: /* IDMOFF */
> + case 0x39: /* IDMON */
> + case 0x3a: /* COLMOD */
> + goto bad_cmd;
> +
> + case 0xb0: /* CLKINT / DISCTL */
> + case 0xb1: /* CLKEXT */
> + if (s->pm < 0)
> + s->pm = 2;
> + break;
> +
> + case 0xb4: /* FRMSEL */
> + break;
> +
> + case 0xb5: /* FRM8SEL */
> + case 0xb6: /* TMPRNG / INIESC */
> + case 0xb7: /* TMPHIS / NOP2 */
> + case 0xb8: /* TMPREAD / MADCTL */
> + case 0xba: /* DISTCTR */
> + case 0xbb: /* EPVOL */
> + goto bad_cmd;
> +
> + case 0xbd: /* Unknown */
> + s->p = 0;
> + s->resp[0] = 0;
> + s->resp[1] = 1;
> + break;
> +
> + case 0xc2: /* IFMOD */
> + if (s->pm < 0)
> + s->pm = 2;
> + break;
> +
> + case 0xc6: /* PWRCTL */
> + case 0xc7: /* PPWRCTL */
> + case 0xd0: /* EPWROUT */
> + case 0xd1: /* EPWRIN */
> + case 0xd4: /* RDEV */
> + case 0xd5: /* RDRR */
> + goto bad_cmd;
> +
> + case 0xda: /* RDID1 */
> + s->p = 0;
> + s->resp[0] = (s->id >> 16) & 0xff;
> + break;
> + case 0xdb: /* RDID2 */
> + s->p = 0;
> + s->resp[0] = (s->id >> 8) & 0xff;
> + break;
> + case 0xdc: /* RDID3 */
> + s->p = 0;
> + s->resp[0] = (s->id >> 0) & 0xff;
> + break;
> +
> + default:
> + bad_cmd:
> + fprintf(stderr, "%s: unknown command %02x\n", __FUNCTION__,
> s->cmd);
> + break;
> + }
> +
> + return ret;
> +}
> +
> +static void *mipid_init(void)
> +{
> + struct mipid_s *s = (struct mipid_s *) qemu_mallocz(sizeof(*s));
> +
> + s->id = 0x838f03;
> + mipid_reset(s);
> +
> + return s;
> +}
> +
> +static void n800_spi_setup(struct n800_s *s)
> +{
> + void *tsc2301 = s->ts->opaque;
> + void *mipid = mipid_init();
> +
> + omap_mcspi_attach(s->cpu->mcspi[0], tsc210x_txrx, tsc2301, 0);
> + omap_mcspi_attach(s->cpu->mcspi[0], mipid_txrx, mipid, 1);
> +}
> +
> +/* This task is normally performed by the bootloader. If we're loading
> + * a kernel directly, we need to enable the Blizzard ourselves. */
> +static void n800_dss_init(struct rfbi_chip_s *chip)
> +{
> + uint8_t *fb_blank;
> +
> + chip->write(chip->opaque, 0, 0x2a); /* LCD Width register */
> + chip->write(chip->opaque, 1, 0x64);
> + chip->write(chip->opaque, 0, 0x2c); /* LCD HNDP register */
> + chip->write(chip->opaque, 1, 0x1e);
> + chip->write(chip->opaque, 0, 0x2e); /* LCD Height 0 register */
> + chip->write(chip->opaque, 1, 0xe0);
> + chip->write(chip->opaque, 0, 0x30); /* LCD Height 1 register */
> + chip->write(chip->opaque, 1, 0x01);
> + chip->write(chip->opaque, 0, 0x32); /* LCD VNDP register */
> + chip->write(chip->opaque, 1, 0x06);
> + chip->write(chip->opaque, 0, 0x68); /* Display Mode register */
> + chip->write(chip->opaque, 1, 1); /* Enable bit */
> +
> + chip->write(chip->opaque, 0, 0x6c);
> + chip->write(chip->opaque, 1, 0x00); /* Input X Start Position */
> + chip->write(chip->opaque, 1, 0x00); /* Input X Start Position */
> + chip->write(chip->opaque, 1, 0x00); /* Input Y Start Position */
> + chip->write(chip->opaque, 1, 0x00); /* Input Y Start Position */
> + chip->write(chip->opaque, 1, 0x1f); /* Input X End Position */
> + chip->write(chip->opaque, 1, 0x03); /* Input X End Position */
> + chip->write(chip->opaque, 1, 0xdf); /* Input Y End Position */
> + chip->write(chip->opaque, 1, 0x01); /* Input Y End Position */
> + chip->write(chip->opaque, 1, 0x00); /* Output X Start Position */
> + chip->write(chip->opaque, 1, 0x00); /* Output X Start Position */
> + chip->write(chip->opaque, 1, 0x00); /* Output Y Start Position */
> + chip->write(chip->opaque, 1, 0x00); /* Output Y Start Position */
> + chip->write(chip->opaque, 1, 0x1f); /* Output X End Position */
> + chip->write(chip->opaque, 1, 0x03); /* Output X End Position */
> + chip->write(chip->opaque, 1, 0xdf); /* Output Y End Position */
> + chip->write(chip->opaque, 1, 0x01); /* Output Y End Position */
> + chip->write(chip->opaque, 1, 0x01); /* Input Data Format */
> + chip->write(chip->opaque, 1, 0x01); /* Data Source Select */
> +
> + fb_blank = memset(qemu_malloc(800 * 480 * 2), 0xff, 800 * 480 * 2);
> + /* Display Memory Data Port */
> + chip->block(chip->opaque, 1, fb_blank, 800 * 480 * 2, 800);
> + free(fb_blank);
> +}
> +
> +static void n800_dss_setup(struct n800_s *s, DisplayState *ds)
> +{
> + s->blizzard.opaque = s1d13745_init(0, ds);
> + s->blizzard.block = s1d13745_write_block;
> + s->blizzard.write = s1d13745_write;
> + s->blizzard.read = s1d13745_read;
> +
> + omap_rfbi_attach(s->cpu->dss, 0, &s->blizzard);
> +}
> +
> +static void n800_cbus_setup(struct n800_s *s)
> +{
> + qemu_irq dat_out = omap2_gpio_in_get(s->cpu->gpif,
> N8X0_CBUS_DAT_GPIO)[0];
> + qemu_irq retu_irq = omap2_gpio_in_get(s->cpu->gpif,
> N8X0_RETU_GPIO)[0];
> + qemu_irq tahvo_irq = omap2_gpio_in_get(s->cpu->gpif,
> N8X0_TAHVO_GPIO)[0];
> +
> + struct cbus_s *cbus = cbus_init(dat_out);
> +
> + omap2_gpio_out_set(s->cpu->gpif, N8X0_CBUS_CLK_GPIO, cbus->clk);
> + omap2_gpio_out_set(s->cpu->gpif, N8X0_CBUS_DAT_GPIO, cbus->dat);
> + omap2_gpio_out_set(s->cpu->gpif, N8X0_CBUS_SEL_GPIO, cbus->sel);
> +
> + cbus_attach(cbus, s->retu = retu_init(retu_irq, 1));
> + cbus_attach(cbus, s->tahvo = tahvo_init(tahvo_irq, 1));
> +}
> +
> +/* This task is normally performed by the bootloader. If we're loading
> + * a kernel directly, we need to set up GPMC mappings ourselves. */
> +static void n800_gpmc_init(struct n800_s *s)
> +{
> + uint32_t config7 =
> + (0xf << 8) | /* MASKADDRESS */
> + (1 << 6) | /* CSVALID */
> + (4 << 0); /* BASEADDRESS */
> +
> + cpu_physical_memory_write(0x6800a078, /* GPMC_CONFIG7_0 */
> + (void *) &config7, sizeof(config7));
> +}
> +
> +/* Setup sequence done by the bootloader */
> +static void n800_boot_init(void *opaque)
> +{
> + struct n800_s *s = (struct n800_s *) opaque;
> + uint32_t buf;
> +
> + /* PRCM setup */
> +#define omap_writel(addr, val) \
> + buf = (val); \
> + cpu_physical_memory_write(addr, (void *) &buf, sizeof(buf))
> +
> + omap_writel(0x48008060, 0x41); /* PRCM_CLKSRC_CTRL */
> + omap_writel(0x48008070, 1); /* PRCM_CLKOUT_CTRL */
> + omap_writel(0x48008078, 0); /* PRCM_CLKEMUL_CTRL */
> + omap_writel(0x48008090, 0); /* PRCM_VOLTSETUP */
> + omap_writel(0x48008094, 0); /* PRCM_CLKSSETUP */
> + omap_writel(0x48008098, 0); /* PRCM_POLCTRL */
> + omap_writel(0x48008140, 2); /* CM_CLKSEL_MPU */
> + omap_writel(0x48008148, 0); /* CM_CLKSTCTRL_MPU */
> + omap_writel(0x48008158, 1); /* RM_RSTST_MPU */
> + omap_writel(0x480081c8, 0x15); /* PM_WKDEP_MPU */
> + omap_writel(0x480081d4, 0x1d4); /* PM_EVGENCTRL_MPU */
> + omap_writel(0x480081d8, 0); /* PM_EVEGENONTIM_MPU */
> + omap_writel(0x480081dc, 0); /* PM_EVEGENOFFTIM_MPU */
> + omap_writel(0x480081e0, 0xc); /* PM_PWSTCTRL_MPU */
> + omap_writel(0x48008200, 0x047e7ff7); /* CM_FCLKEN1_CORE */
> + omap_writel(0x48008204, 0x00000004); /* CM_FCLKEN2_CORE */
> + omap_writel(0x48008210, 0x047e7ff1); /* CM_ICLKEN1_CORE */
> + omap_writel(0x48008214, 0x00000004); /* CM_ICLKEN2_CORE */
> + omap_writel(0x4800821c, 0x00000000); /* CM_ICLKEN4_CORE */
> + omap_writel(0x48008230, 0); /* CM_AUTOIDLE1_CORE */
> + omap_writel(0x48008234, 0); /* CM_AUTOIDLE2_CORE */
> + omap_writel(0x48008238, 7); /* CM_AUTOIDLE3_CORE */
> + omap_writel(0x4800823c, 0); /* CM_AUTOIDLE4_CORE */
> + omap_writel(0x48008240, 0x04360626); /* CM_CLKSEL1_CORE */
> + omap_writel(0x48008244, 0x00000014); /* CM_CLKSEL2_CORE */
> + omap_writel(0x48008248, 0); /* CM_CLKSTCTRL_CORE */
> + omap_writel(0x48008300, 0x00000000); /* CM_FCLKEN_GFX */
> + omap_writel(0x48008310, 0x00000000); /* CM_ICLKEN_GFX */
> + omap_writel(0x48008340, 0x00000001); /* CM_CLKSEL_GFX */
> + omap_writel(0x48008400, 0x00000004); /* CM_FCLKEN_WKUP */
> + omap_writel(0x48008410, 0x00000004); /* CM_ICLKEN_WKUP */
> + omap_writel(0x48008440, 0x00000000); /* CM_CLKSEL_WKUP */
> + omap_writel(0x48008500, 0x000000cf); /* CM_CLKEN_PLL */
> + omap_writel(0x48008530, 0x0000000c); /* CM_AUTOIDLE_PLL */
> + omap_writel(0x48008540, /* CM_CLKSEL1_PLL */
> + (0x78 << 12) | (6 << 8));
> + omap_writel(0x48008544, 2); /* CM_CLKSEL2_PLL */
> +
> + /* GPMC setup */
> + n800_gpmc_init(s);
> +
> + /* Video setup */
> + n800_dss_init(&s->blizzard);
> +
> + /* CPU setup */
> + s->cpu->env->regs[15] = s->cpu->env->boot_info->loader_start;
> + s->cpu->env->GE = 0x5;
> +}
> +
> +#define OMAP_TAG_NOKIA_BT 0x4e01
> +#define OMAP_TAG_WLAN_CX3110X 0x4e02
> +#define OMAP_TAG_CBUS 0x4e03
> +#define OMAP_TAG_EM_ASIC_BB5 0x4e04
> +
> +static int n800_atag_setup(struct arm_boot_info *info, void *p)
> +{
> + uint8_t *b;
> + uint16_t *w;
> + uint32_t *l;
> +
> + w = p;
> +
> + stw_raw(w ++, OMAP_TAG_UART); /* u16 tag */
> + stw_raw(w ++, 4); /* u16 len */
> + stw_raw(w ++, (1 << 2) | (1 << 1) | (1 << 0)); /* uint enabled_uarts
> */
> + w ++;
> +
> + stw_raw(w ++, OMAP_TAG_EM_ASIC_BB5); /* u16 tag */
> + stw_raw(w ++, 4); /* u16 len */
> + stw_raw(w ++, N8X0_RETU_GPIO); /* s16 retu_irq_gpio */
> + stw_raw(w ++, N8X0_TAHVO_GPIO); /* s16 tahvo_irq_gpio */
> +
> + stw_raw(w ++, OMAP_TAG_CBUS); /* u16 tag */
> + stw_raw(w ++, 8); /* u16 len */
> + stw_raw(w ++, N8X0_CBUS_CLK_GPIO); /* s16 clk_gpio */
> + stw_raw(w ++, N8X0_CBUS_DAT_GPIO); /* s16 dat_gpio */
> + stw_raw(w ++, N8X0_CBUS_SEL_GPIO); /* s16 sel_gpio */
> + w ++;
> +
> + stw_raw(w ++, OMAP_TAG_GPIO_SWITCH); /* u16 tag */
> + stw_raw(w ++, 20); /* u16 len */
> + strcpy((void *) w, "bat_cover"); /* char name[12] */
> + w += 6;
> + stw_raw(w ++, N800_BAT_COVER_GPIO); /* u16 gpio */
> + stw_raw(w ++, 0x01);
> + stw_raw(w ++, 0);
> + stw_raw(w ++, 0);
> +
> + stw_raw(w ++, OMAP_TAG_GPIO_SWITCH); /* u16 tag */
> + stw_raw(w ++, 20); /* u16 len */
> + strcpy((void *) w, "cam_act"); /* char name[12] */
> + w += 6;
> + stw_raw(w ++, N800_CAM_ACT_GPIO); /* u16 gpio */
> + stw_raw(w ++, 0x20);
> + stw_raw(w ++, 0);
> + stw_raw(w ++, 0);
> +
> + stw_raw(w ++, OMAP_TAG_GPIO_SWITCH); /* u16 tag */
> + stw_raw(w ++, 20); /* u16 len */
> + strcpy((void *) w, "cam_turn"); /* char name[12] */
> + w += 6;
> + stw_raw(w ++, N800_CAM_TURN_GPIO); /* u16 gpio */
> + stw_raw(w ++, 0x21);
> + stw_raw(w ++, 0);
> + stw_raw(w ++, 0);
> +
> + stw_raw(w ++, OMAP_TAG_GPIO_SWITCH); /* u16 tag */
> + stw_raw(w ++, 20); /* u16 len */
> + strcpy((void *) w, "headphone"); /* char name[12] */
> + w += 6;
> + stw_raw(w ++, N800_HEADPHONE_GPIO); /* u16 gpio */
> + stw_raw(w ++, 0x11);
> + stw_raw(w ++, 0);
> + stw_raw(w ++, 0);
> +
> + stw_raw(w ++, OMAP_TAG_NOKIA_BT); /* u16 tag */
> + stw_raw(w ++, 12); /* u16 len */
> + b = (void *) w;
> + stb_raw(b ++, 0x01); /* u8 chip_type (CSR) */
> + stb_raw(b ++, N800_BT_WKUP_GPIO); /* u8 bt_wakeup_gpio */
> + stb_raw(b ++, N8X0_BT_HOST_WKUP_GPIO); /* u8 host_wakeup_gpio */
> + stb_raw(b ++, N800_BT_RESET_GPIO); /* u8 reset_gpio */
> + stb_raw(b ++, 1); /* u8 bt_uart */
> + memset(b, 0, 6); /* u8 bd_addr[6] */
> + b += 6;
> + stb_raw(b ++, 0x02); /* u8 bt_sysclk (38.4) */
> + w = (void *) b;
> +
> + stw_raw(w ++, OMAP_TAG_WLAN_CX3110X); /* u16 tag */
> + stw_raw(w ++, 8); /* u16 len */
> + stw_raw(w ++, 0x25); /* u8 chip_type */
> + stw_raw(w ++, N800_WLAN_PWR_GPIO); /* s16 power_gpio */
> + stw_raw(w ++, N800_WLAN_IRQ_GPIO); /* s16 irq_gpio */
> + stw_raw(w ++, -1); /* s16 spi_cs_gpio */
> +
> + stw_raw(w ++, OMAP_TAG_MMC); /* u16 tag */
> + stw_raw(w ++, 16); /* u16 len */
> + stw_raw(w ++, 0xf); /* unsigned flags */
> + stw_raw(w ++, -1); /* s16 power_pin */
> + stw_raw(w ++, -1); /* s16 switch_pin */
> + stw_raw(w ++, -1); /* s16 wp_pin */
> + stw_raw(w ++, 0); /* unsigned flags */
> + stw_raw(w ++, 0); /* s16 power_pin */
> + stw_raw(w ++, 0); /* s16 switch_pin */
> + stw_raw(w ++, 0); /* s16 wp_pin */
> +
> + stw_raw(w ++, OMAP_TAG_TEA5761); /* u16 tag */
> + stw_raw(w ++, 4); /* u16 len */
> + stw_raw(w ++, N800_TEA5761_CS_GPIO); /* u16 enable_gpio */
> + w ++;
> +
> + stw_raw(w ++, OMAP_TAG_PARTITION); /* u16 tag */
> + stw_raw(w ++, 28); /* u16 len */
> + strcpy((void *) w, "bootloader"); /* char name[16] */
> + l = (void *) (w + 8);
> + stl_raw(l ++, 0x00020000); /* unsigned int size */
> + stl_raw(l ++, 0x00000000); /* unsigned int offset */
> + stl_raw(l ++, 0x3); /* unsigned int mask_flags */
> + w = (void *) l;
> +
> + stw_raw(w ++, OMAP_TAG_PARTITION); /* u16 tag */
> + stw_raw(w ++, 28); /* u16 len */
> + strcpy((void *) w, "config"); /* char name[16] */
> + l = (void *) (w + 8);
> + stl_raw(l ++, 0x00060000); /* unsigned int size */
> + stl_raw(l ++, 0x00020000); /* unsigned int offset */
> + stl_raw(l ++, 0x0); /* unsigned int mask_flags */
> + w = (void *) l;
> +
> + stw_raw(w ++, OMAP_TAG_PARTITION); /* u16 tag */
> + stw_raw(w ++, 28); /* u16 len */
> + strcpy((void *) w, "kernel"); /* char name[16] */
> + l = (void *) (w + 8);
> + stl_raw(l ++, 0x00200000); /* unsigned int size */
> + stl_raw(l ++, 0x00080000); /* unsigned int offset */
> + stl_raw(l ++, 0x0); /* unsigned int mask_flags */
> + w = (void *) l;
> +
> + stw_raw(w ++, OMAP_TAG_PARTITION); /* u16 tag */
> + stw_raw(w ++, 28); /* u16 len */
> + strcpy((void *) w, "initfs"); /* char name[16] */
> + l = (void *) (w + 8);
> + stl_raw(l ++, 0x00200000); /* unsigned int size */
> + stl_raw(l ++, 0x00280000); /* unsigned int offset */
> + stl_raw(l ++, 0x3); /* unsigned int mask_flags */
> + w = (void *) l;
> +
> + stw_raw(w ++, OMAP_TAG_PARTITION); /* u16 tag */
> + stw_raw(w ++, 28); /* u16 len */
> + strcpy((void *) w, "rootfs"); /* char name[16] */
> + l = (void *) (w + 8);
> + stl_raw(l ++, 0x0fb80000); /* unsigned int size */
> + stl_raw(l ++, 0x00480000); /* unsigned int offset */
> + stl_raw(l ++, 0x3); /* unsigned int mask_flags */
> + w = (void *) l;
> +
> + stw_raw(w ++, OMAP_TAG_BOOT_REASON); /* u16 tag */
> + stw_raw(w ++, 12); /* u16 len */
> +#if 0
> + strcpy((void *) w, "por"); /* char reason_str[12] */
> + strcpy((void *) w, "charger"); /* char reason_str[12] */
> + strcpy((void *) w, "32wd_to"); /* char reason_str[12] */
> + strcpy((void *) w, "sw_rst"); /* char reason_str[12] */
> + strcpy((void *) w, "mbus"); /* char reason_str[12] */
> + strcpy((void *) w, "unknown"); /* char reason_str[12] */
> + strcpy((void *) w, "swdg_to"); /* char reason_str[12] */
> + strcpy((void *) w, "sec_vio"); /* char reason_str[12] */
> + strcpy((void *) w, "pwr_key"); /* char reason_str[12] */
> + strcpy((void *) w, "rtc_alarm"); /* char reason_str[12] */
> +#else
> + strcpy((void *) w, "pwr_key"); /* char reason_str[12] */
> +#endif
> + w += 6;
> +
> +#if 0 /* N810 */
> + stw_raw(w ++, OMAP_TAG_VERSION_STR); /* u16 tag */
> + stw_raw(w ++, 24); /* u16 len */
> + strcpy((void *) w, "product"); /* char component[12] */
> + w += 6;
> + strcpy((void *) w, "RX-44"); /* char version[12] */
> + w += 6;
> +
> + stw_raw(w ++, OMAP_TAG_VERSION_STR); /* u16 tag */
> + stw_raw(w ++, 24); /* u16 len */
> + strcpy((void *) w, "hw-build"); /* char component[12] */
> + w += 6;
> + strcpy((void *) w, "QEMU"); /* char version[12] */
> + w += 6;
> +
> + stw_raw(w ++, OMAP_TAG_VERSION_STR); /* u16 tag */
> + stw_raw(w ++, 24); /* u16 len */
> + strcpy((void *) w, "nolo"); /* char component[12] */
> + w += 6;
> + strcpy((void *) w, "1.1.10-qemu"); /* char version[12] */
> + w += 6;
> +#else
> + stw_raw(w ++, OMAP_TAG_VERSION_STR); /* u16 tag */
> + stw_raw(w ++, 24); /* u16 len */
> + strcpy((void *) w, "product"); /* char component[12] */
> + w += 6;
> + strcpy((void *) w, "RX-34"); /* char version[12] */
> + w += 6;
> +
> + stw_raw(w ++, OMAP_TAG_VERSION_STR); /* u16 tag */
> + stw_raw(w ++, 24); /* u16 len */
> + strcpy((void *) w, "hw-build"); /* char component[12] */
> + w += 6;
> + strcpy((void *) w, "QEMU"); /* char version[12] */
> + w += 6;
> +
> + stw_raw(w ++, OMAP_TAG_VERSION_STR); /* u16 tag */
> + stw_raw(w ++, 24); /* u16 len */
> + strcpy((void *) w, "nolo"); /* char component[12] */
> + w += 6;
> + strcpy((void *) w, "1.1.6-qemu"); /* char version[12] */
> + w += 6;
> +#endif
> +
> + stw_raw(w ++, OMAP_TAG_LCD); /* u16 tag */
> + stw_raw(w ++, 36); /* u16 len */
> + strcpy((void *) w, "QEMU LCD panel"); /* char panel_name[16] */
> + w += 8;
> + strcpy((void *) w, "blizzard"); /* char ctrl_name[16] */
> + w += 8;
> + stw_raw(w ++, 5); /* TODO s16 nreset_gpio */
> + stw_raw(w ++, 16); /* u8 data_lines */
> +
> + return (void *) w - p;
> +}
> +
> +static struct arm_boot_info n800_binfo = {
> + .loader_start = OMAP2_Q2_BASE,
> + /* Actually two chips of 0x4000000 bytes each */
> + .ram_size = 0x08000000,
> + .board_id = 0x4f7,
> + .atag_board = n800_atag_setup,
> +};
> +
> +static void n800_init(int ram_size, int vga_ram_size,
> + const char *boot_device, DisplayState *ds,
> + const char *kernel_filename, const char *kernel_cmdline,
> + const char *initrd_filename, const char *cpu_model)
> +{
> + struct n800_s *s = (struct n800_s *) qemu_mallocz(sizeof(*s));
> + int sdram_size = n800_binfo.ram_size;
> + int onenandram_size = 0x00010000;
> +
> + if (ram_size < sdram_size + onenandram_size + OMAP242X_SRAM_SIZE) {
> + fprintf(stderr, "This architecture uses %i bytes of memory\n",
> + sdram_size + onenandram_size +
> OMAP242X_SRAM_SIZE);
> + exit(1);
> + }
> +
> + s->cpu = omap2420_mpu_init(sdram_size, NULL, cpu_model);
> +
> + n800_gpio_setup(s);
> + n8x0_nand_setup(s);
> + n800_i2c_setup(s);
> + n800_tsc_setup(s);
> + n800_spi_setup(s);
> + n800_dss_setup(s, ds);
> + n800_cbus_setup(s);
> +
> + /* Setup initial (reset) machine state */
> +
> + /* Start at the OneNAND bootloader. */
> + s->cpu->env->regs[15] = 0;
> +
> + if (kernel_filename) {
> + /* Or at the linux loader. */
> + n800_binfo.kernel_filename = kernel_filename;
> + n800_binfo.kernel_cmdline = kernel_cmdline;
> + n800_binfo.initrd_filename = initrd_filename;
> + arm_load_kernel(s->cpu->env, &n800_binfo);
> +
> + qemu_register_reset(n800_boot_init, s);
> + n800_boot_init(s);
> + }
> +
> + dpy_resize(ds, 800, 480);
> +}
> +
> +QEMUMachine n800_machine = {
> + "n800",
> + "Nokia N800 aka. RX-34 tablet (OMAP2420)",
> + n800_init,
> +};
>
> Modified: trunk/hw/omap2.c
> ===================================================================
> --- trunk/hw/omap2.c 2008-04-14 21:28:11 UTC (rev 4214)
> +++ trunk/hw/omap2.c 2008-04-14 21:57:44 UTC (rev 4215)
> @@ -3496,7 +3496,7 @@
> {
> struct omap_mpu_state_s *s = (struct omap_mpu_state_s *)
> qemu_mallocz(sizeof(struct omap_mpu_state_s));
> - ram_addr_t sram_base, q3_base;
> + ram_addr_t sram_base, q2_base;
> qemu_irq *cpu_irq;
> qemu_irq dma_irqs[4];
> omap_clk gpio_clks[4];
> @@ -3520,7 +3520,7 @@
>
> /* Memory-mapped stuff */
> cpu_register_physical_memory(OMAP2_Q2_BASE, s->sdram_size,
> - (q3_base = qemu_ram_alloc(s->sdram_size)) |
> IO_MEM_RAM);
> + (q2_base = qemu_ram_alloc(s->sdram_size)) |
> IO_MEM_RAM);
> cpu_register_physical_memory(OMAP2_SRAM_BASE, s->sram_size,
> (sram_base = qemu_ram_alloc(s->sram_size)) |
> IO_MEM_RAM);
>
>
> Added: trunk/hw/onenand.c
> ===================================================================
> --- trunk/hw/onenand.c (rev 0)
> +++ trunk/hw/onenand.c 2008-04-14 21:57:44 UTC (rev 4215)
> @@ -0,0 +1,642 @@
> +/*
> + * OneNAND flash memories emulation.
> + *
> + * Copyright (C) 2008 Nokia Corporation
> + * Written by Andrzej Zaborowski <andrew@openedhand.com>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation; either version 2 or
> + * (at your option) version 3 of the License.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
> + * MA 02111-1307 USA
> + */
> +
> +#include "qemu-common.h"
> +#include "flash.h"
> +#include "irq.h"
> +#include "sysemu.h"
> +#include "block.h"
> +
> +/* 11 for 2kB-page OneNAND ("2nd generation") and 10 for 1kB-page chips
> */
> +#define PAGE_SHIFT 11
> +
> +/* Fixed */
> +#define BLOCK_SHIFT (PAGE_SHIFT + 6)
> +
> +struct onenand_s {
> + uint32_t id;
> + int shift;
> + target_phys_addr_t base;
> + qemu_irq intr;
> + qemu_irq rdy;
> + BlockDriverState *bdrv;
> + BlockDriverState *bdrv_cur;
> + uint8_t *image;
> + uint8_t *otp;
> + uint8_t *current;
> + ram_addr_t ram;
> + uint8_t *boot[2];
> + uint8_t *data[2][2];
> + int iomemtype;
> + int cycle;
> + int otpmode;
> +
> + uint16_t addr[8];
> + uint16_t unladdr[8];
> + int bufaddr;
> + int count;
> + uint16_t command;
> + uint16_t config[2];
> + uint16_t status;
> + uint16_t intstatus;
> + uint16_t wpstatus;
> +
> + struct ecc_state_s ecc;
> +
> + int density_mask;
> + int secs;
> + int secs_cur;
> + int blocks;
> + uint8_t *blockwp;
> +};
> +
> +enum {
> + ONEN_BUF_BLOCK = 0,
> + ONEN_BUF_BLOCK2 = 1,
> + ONEN_BUF_DEST_BLOCK = 2,
> + ONEN_BUF_DEST_PAGE = 3,
> + ONEN_BUF_PAGE = 7,
> +};
> +
> +enum {
> + ONEN_ERR_CMD = 1 << 10,
> + ONEN_ERR_ERASE = 1 << 11,
> + ONEN_ERR_PROG = 1 << 12,
> + ONEN_ERR_LOAD = 1 << 13,
> +};
> +
> +enum {
> + ONEN_INT_RESET = 1 << 4,
> + ONEN_INT_ERASE = 1 << 5,
> + ONEN_INT_PROG = 1 << 6,
> + ONEN_INT_LOAD = 1 << 7,
> + ONEN_INT = 1 << 15,
> +};
> +
> +enum {
> + ONEN_LOCK_LOCKTIGHTEN = 1 << 0,
> + ONEN_LOCK_LOCKED = 1 << 1,
> + ONEN_LOCK_UNLOCKED = 1 << 2,
> +};
> +
> +void onenand_base_update(void *opaque, target_phys_addr_t new)
> +{
> + struct onenand_s *s = (struct onenand_s *) opaque;
> +
> + s->base = new;
> +
> + /* XXX: We should use IO_MEM_ROMD but we broke it earlier...
> + * Both 0x0000 ... 0x01ff and 0x8000 ... 0x800f can be used to
> + * write boot commands. Also take note of the BWPS bit. */
> + cpu_register_physical_memory(s->base + (0x0000 << s->shift),
> + 0x0200 << s->shift, s->iomemtype);
> + cpu_register_physical_memory(s->base + (0x0200 << s->shift),
> + 0xbe00 << s->shift,
> + (s->ram +(0x0200 << s->shift)) | IO_MEM_RAM);
> + if (s->iomemtype)
> + cpu_register_physical_memory(s->base + (0xc000 << s->shift),
> + 0x4000 << s->shift, s->iomemtype);
> +}
> +
> +void onenand_base_unmap(void *opaque)
> +{
> + struct onenand_s *s = (struct onenand_s *) opaque;
> +
> + cpu_register_physical_memory(s->base,
> + 0x10000 << s->shift, IO_MEM_UNASSIGNED);
> +}
> +
> +static void onenand_intr_update(struct onenand_s *s)
> +{
> + qemu_set_irq(s->intr, ((s->intstatus >> 15) ^ (~s->config[0] >> 6)) &
> 1);
> +}
> +
> +/* Hot reset (Reset OneNAND command) or warm reset (RP pin low) */
> +static void onenand_reset(struct onenand_s *s, int cold)
> +{
> + memset(&s->addr, 0, sizeof(s->addr));
> + s->command = 0;
> + s->count = 1;
> + s->bufaddr = 0;
> + s->config[0] = 0x40c0;
> + s->config[1] = 0x0000;
> + onenand_intr_update(s);
> + qemu_irq_raise(s->rdy);
> + s->status = 0x0000;
> + s->intstatus = cold ? 0x8080 : 0x8010;
> + s->unladdr[0] = 0;
> + s->unladdr[1] = 0;
> + s->wpstatus = 0x0002;
> + s->cycle = 0;
> + s->otpmode = 0;
> + s->bdrv_cur = s->bdrv;
> + s->current = s->image;
> + s->secs_cur = s->secs;
> +
> + if (cold) {
> + /* Lock the whole flash */
> + memset(s->blockwp, ONEN_LOCK_LOCKED, s->blocks);
> +
> + if (s->bdrv && bdrv_read(s->bdrv, 0, s->boot[0], 8) < 0)
> + cpu_abort(cpu_single_env, "%s: Loading the BootRAM
> failed.\n",
> + __FUNCTION__);
> + }
> +}
> +
> +static inline int onenand_load_main(struct onenand_s *s, int sec, int
> secn,
> + void *dest)
> +{
> + if (s->bdrv_cur)
> + return bdrv_read(s->bdrv_cur, sec, dest, secn) < 0;
> + else if (sec + secn > s->secs_cur)
> + return 1;
> +
> + memcpy(dest, s->current + (sec << 9), secn << 9);
> +
> + return 0;
> +}
> +
> +static inline int onenand_prog_main(struct onenand_s *s, int sec, int
> secn,
> + void *src)
> +{
> + if (s->bdrv_cur)
> + return bdrv_write(s->bdrv_cur, sec, src, secn) < 0;
> + else if (sec + secn > s->secs_cur)
> + return 1;
> +
> + memcpy(s->current + (sec << 9), src, secn << 9);
> +
> + return 0;
> +}
> +
> +static inline int onenand_load_spare(struct onenand_s *s, int sec, int
> secn,
> + void *dest)
> +{
> + uint8_t buf[512];
> +
> + if (s->bdrv_cur) {
> + if (bdrv_read(s->bdrv_cur, s->secs_cur + (sec >> 5), buf, 1) < 0)
> + return 1;
> + memcpy(dest, buf + ((sec & 31) << 4), secn << 4);
> + } else if (sec + secn > s->secs_cur)
> + return 1;
> + else
> + memcpy(dest, s->current + (s->secs_cur << 9) + (sec << 4), secn
> << 4);
> +
> + return 0;
> +}
> +
> +static inline int onenand_prog_spare(struct onenand_s *s, int sec, int
> secn,
> + void *src)
> +{
> + uint8_t buf[512];
> +
> + if (s->bdrv_cur) {
> + if (bdrv_read(s->bdrv_cur, s->secs_cur + (sec >> 5), buf, 1) < 0)
> + return 1;
> + memcpy(buf + ((sec & 31) << 4), src, secn << 4);
> + return bdrv_write(s->bdrv_cur, s->secs_cur + (sec >> 5), buf, 1)
> < 0;
> + } else if (sec + secn > s->secs_cur)
> + return 1;
> +
> + memcpy(s->current + (s->secs_cur << 9) + (sec << 4), src, secn << 4);
> +
> + return 0;
> +}
> +
> +static inline int onenand_erase(struct onenand_s *s, int sec, int num)
> +{
> + /* TODO: optimise */
> + uint8_t buf[512];
> +
> + memset(buf, 0xff, sizeof(buf));
> + for (; num > 0; num --, sec ++) {
> + if (onenand_prog_main(s, sec, 1, buf))
> + return 1;
> + if (onenand_prog_spare(s, sec, 1, buf))
> + return 1;
> + }
> +
> + return 0;
> +}
> +
> +static void onenand_command(struct onenand_s *s, int cmd)
> +{
> + int b;
> + int sec;
> + void *buf;
> +#define SETADDR(block, page) \
> + sec = (s->addr[page] & 3) + \
> + ((((s->addr[page] >> 2) & 0x3f) + \
> + (((s->addr[block] & 0xfff) | \
> + (s->addr[block] >> 15 ? \
> + s->density_mask : 0)) << 6)) << (PAGE_SHIFT - 9));
> +#define SETBUF_M() \
> + buf = (s->bufaddr & 8) ? \
> + s->data[(s->bufaddr >> 2) & 1][0] : s->boot[0]; \
> + buf += (s->bufaddr & 3) << 9;
> +#define SETBUF_S() \
> + buf = (s->bufaddr & 8) ? \
> + s->data[(s->bufaddr >> 2) & 1][1] : s->boot[1]; \
> + buf += (s->bufaddr & 3) << 4;
> +
> + switch (cmd) {
> + case 0x00: /* Load single/multiple sector data unit into buffer */
> + SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE)
> +
> + SETBUF_M()
> + if (onenand_load_main(s, sec, s->count, buf))
> + s->status |= ONEN_ERR_CMD | ONEN_ERR_LOAD;
> +
> +#if 0
> + SETBUF_S()
> + if (onenand_load_spare(s, sec, s->count, buf))
> + s->status |= ONEN_ERR_CMD | ONEN_ERR_LOAD;
> +#endif
> +
> + /* TODO: if (s->bufaddr & 3) + s->count was > 4 (2k-pages)
> + * or if (s->bufaddr & 1) + s->count was > 2 (1k-pages)
> + * then we need two split the read/write into two chunks.
> + */
> + s->intstatus |= ONEN_INT | ONEN_INT_LOAD;
> + break;
> + case 0x13: /* Load single/multiple spare sector into buffer */
> + SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE)
> +
> + SETBUF_S()
> + if (onenand_load_spare(s, sec, s->count, buf))
> + s->status |= ONEN_ERR_CMD | ONEN_ERR_LOAD;
> +
> + /* TODO: if (s->bufaddr & 3) + s->count was > 4 (2k-pages)
> + * or if (s->bufaddr & 1) + s->count was > 2 (1k-pages)
> + * then we need two split the read/write into two chunks.
> + */
> + s->intstatus |= ONEN_INT | ONEN_INT_LOAD;
> + break;
> + case 0x80: /* Program single/multiple sector data unit from buffer */
> + SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE)
> +
> + SETBUF_M()
> + if (onenand_prog_main(s, sec, s->count, buf))
> + s->status |= ONEN_ERR_CMD | ONEN_ERR_PROG;
> +
> +#if 0
> + SETBUF_S()
> + if (onenand_prog_spare(s, sec, s->count, buf))
> + s->status |= ONEN_ERR_CMD | ONEN_ERR_PROG;
> +#endif
> +
> + /* TODO: if (s->bufaddr & 3) + s->count was > 4 (2k-pages)
> + * or if (s->bufaddr & 1) + s->count was > 2 (1k-pages)
> + * then we need two split the read/write into two chunks.
> + */
> + s->intstatus |= ONEN_INT | ONEN_INT_PROG;
> + break;
> + case 0x1a: /* Program single/multiple spare area sector from buffer
> */
> + SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE)
> +
> + SETBUF_S()
> + if (onenand_prog_spare(s, sec, s->count, buf))
> + s->status |= ONEN_ERR_CMD | ONEN_ERR_PROG;
> +
> + /* TODO: if (s->bufaddr & 3) + s->count was > 4 (2k-pages)
> + * or if (s->bufaddr & 1) + s->count was > 2 (1k-pages)
> + * then we need two split the read/write into two chunks.
> + */
> + s->intstatus |= ONEN_INT | ONEN_INT_PROG;
> + break;
> + case 0x1b: /* Copy-back program */
> + SETBUF_S()
> +
> + SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE)
> + if (onenand_load_main(s, sec, s->count, buf))
> + s->status |= ONEN_ERR_CMD | ONEN_ERR_PROG;
> +
> + SETADDR(ONEN_BUF_DEST_BLOCK, ONEN_BUF_DEST_PAGE)
> + if (onenand_prog_main(s, sec, s->count, buf))
> + s->status |= ONEN_ERR_CMD | ONEN_ERR_PROG;
> +
> + /* TODO: spare areas */
> +
> + s->intstatus |= ONEN_INT | ONEN_INT_PROG;
> + break;
> +
> + case 0x23: /* Unlock NAND array block(s) */
> + s->intstatus |= ONEN_INT;
> +
> + /* XXX the previous (?) area should be locked automatically */
> + for (b = s->unladdr[0]; b <= s->unladdr[1]; b ++) {
> + if (b >= s->blocks) {
> + s->status |= ONEN_ERR_CMD;
> + break;
> + }
> + if (s->blockwp[b] == ONEN_LOCK_LOCKTIGHTEN)
> + break;
> +
> + s->wpstatus = s->blockwp[b] = ONEN_LOCK_UNLOCKED;
> + }
> + break;
> + case 0x2a: /* Lock NAND array block(s) */
> + s->intstatus |= ONEN_INT;
> +
> + for (b = s->unladdr[0]; b <= s->unladdr[1]; b ++) {
> + if (b >= s->blocks) {
> + s->status |= ONEN_ERR_CMD;
> + break;
> + }
> + if (s->blockwp[b] == ONEN_LOCK_LOCKTIGHTEN)
> + break;
> +
> + s->wpstatus = s->blockwp[b] = ONEN_LOCK_LOCKED;
> + }
> + break;
> + case 0x2c: /* Lock-tight NAND array block(s) */
> + s->intstatus |= ONEN_INT;
> +
> + for (b = s->unladdr[0]; b <= s->unladdr[1]; b ++) {
> + if (b >= s->blocks) {
> + s->status |= ONEN_ERR_CMD;
> + break;
> + }
> + if (s->blockwp[b] == ONEN_LOCK_UNLOCKED)
> + continue;
> +
> + s->wpstatus = s->blockwp[b] = ONEN_LOCK_LOCKTIGHTEN;
> + }
> + break;
> +
> + case 0x71: /* Erase-Verify-Read */
> + s->intstatus |= ONEN_INT;
> + break;
> + case 0x95: /* Multi-block erase */
> + qemu_irq_pulse(s->intr);
> + /* Fall through. */
> + case 0x94: /* Block erase */
> + sec = ((s->addr[ONEN_BUF_BLOCK] & 0xfff) |
> + (s->addr[ONEN_BUF_BLOCK] >> 15 ? s->density_mask
> : 0))
> + << (BLOCK_SHIFT - 9);
> + if (onenand_erase(s, sec, 1 << (BLOCK_SHIFT - 9)))
> + s->status |= ONEN_ERR_CMD | ONEN_ERR_ERASE;
> +
> + s->intstatus |= ONEN_INT | ONEN_INT_ERASE;
> + break;
> + case 0xb0: /* Erase suspend */
> + break;
> + case 0x30: /* Erase resume */
> + s->intstatus |= ONEN_INT | ONEN_INT_ERASE;
> + break;
> +
> + case 0xf0: /* Reset NAND Flash core */
> + onenand_reset(s, 0);
> + break;
> + case 0xf3: /* Reset OneNAND */
> + onenand_reset(s, 0);
> + break;
> +
> + case 0x65: /* OTP Access */
> + s->intstatus |= ONEN_INT;
> + s->bdrv_cur = 0;
> + s->current = s->otp;
> + s->secs_cur = 1 << (BLOCK_SHIFT - 9);
> + s->addr[ONEN_BUF_BLOCK] = 0;
> + s->otpmode = 1;
> + break;
> +
> + default:
> + s->status |= ONEN_ERR_CMD;
> + s->intstatus |= ONEN_INT;
> + fprintf(stderr, "%s: unknown OneNAND command %x\n",
> + __FUNCTION__, cmd);
> + }
> +
> + onenand_intr_update(s);
> +}
> +
> +static uint32_t onenand_read(void *opaque, target_phys_addr_t addr)
> +{
> + struct onenand_s *s = (struct onenand_s *) opaque;
> + int offset = (addr - s->base) >> s->shift;
> +
> + switch (offset) {
> + case 0x0000 ... 0xc000:
> + return lduw_le_p(s->boot[0] + (addr - s->base));
> +
> + case 0xf000: /* Manufacturer ID */
> + return (s->id >> 16) & 0xff;
> + case 0xf001: /* Device ID */
> + return (s->id >> 8) & 0xff;
> + /* TODO: get the following values from a real chip! */
> + case 0xf002: /* Version ID */
> + return (s->id >> 0) & 0xff;
> + case 0xf003: /* Data Buffer size */
> + return 1 << PAGE_SHIFT;
> + case 0xf004: /* Boot Buffer size */
> + return 0x200;
> + case 0xf005: /* Amount of buffers */
> + return 1 | (2 << 8);
> + case 0xf006: /* Technology */
> + return 0;
> +
> + case 0xf100 ... 0xf107: /* Start addresses */
> + return s->addr[offset - 0xf100];
> +
> + case 0xf200: /* Start buffer */
> + return (s->bufaddr << 8) | ((s->count - 1) & (1 << (PAGE_SHIFT -
> 10)));
> +
> + case 0xf220: /* Command */
> + return s->command;
> + case 0xf221: /* System Configuration 1 */
> + return s->config[0] & 0xffe0;
> + case 0xf222: /* System Configuration 2 */
> + return s->config[1];
> +
> + case 0xf240: /* Controller Status */
> + return s->status;
> + case 0xf241: /* Interrupt */
> + return s->intstatus;
> + case 0xf24c: /* Unlock Start Block Address */
> + return s->unladdr[0];
> + case 0xf24d: /* Unlock End Block Address */
> + return s->unladdr[1];
> + case 0xf24e: /* Write Protection Status */
> + return s->wpstatus;
> +
> + case 0xff00: /* ECC Status */
> + return 0x00;
> + case 0xff01: /* ECC Result of main area data */
> + case 0xff02: /* ECC Result of spare area data */
> + case 0xff03: /* ECC Result of main area data */
> + case 0xff04: /* ECC Result of spare area data */
> + cpu_abort(cpu_single_env, "%s: imeplement ECC\n", __FUNCTION__);
> + return 0x0000;
> + }
> +
> + fprintf(stderr, "%s: unknown OneNAND register %x\n",
> + __FUNCTION__, offset);
> + return 0;
> +}
> +
> +static void onenand_write(void *opaque, target_phys_addr_t addr,
> + uint32_t value)
> +{
> + struct onenand_s *s = (struct onenand_s *) opaque;
> + int offset = (addr - s->base) >> s->shift;
> + int sec;
> +
> + switch (offset) {
> + case 0x0000 ... 0x01ff:
> + case 0x8000 ... 0x800f:
> + if (s->cycle) {
> + s->cycle = 0;
> +
> + if (value == 0x0000) {
> + SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE)
> + onenand_load_main(s, sec,
> + 1 << (PAGE_SHIFT - 9), s->data[0][0]);
> + s->addr[ONEN_BUF_PAGE] += 4;
> + s->addr[ONEN_BUF_PAGE] &= 0xff;
> + }
> + break;
> + }
> +
> + switch (value) {
> + case 0x00f0: /* Reset OneNAND */
> + onenand_reset(s, 0);
> + break;
> +
> + case 0x00e0: /* Load Data into Buffer */
> + s->cycle = 1;
> + break;
> +
> + case 0x0090: /* Read Identification Data */
> + memset(s->boot[0], 0, 3 << s->shift);
> + s->boot[0][0 << s->shift] = (s->id >> 16) & 0xff;
> + s->boot[0][1 << s->shift] = (s->id >> 8) & 0xff;
> + s->boot[0][2 << s->shift] = s->wpstatus & 0xff;
> + break;
> +
> + default:
> + fprintf(stderr, "%s: unknown OneNAND boot command %x\n",
> + __FUNCTION__, value);
> + }
> + break;
> +
> + case 0xf100 ... 0xf107: /* Start addresses */
> + s->addr[offset - 0xf100] = value;
> + break;
> +
> + case 0xf200: /* Start buffer */
> + s->bufaddr = (value >> 8) & 0xf;
> + if (PAGE_SHIFT == 11)
> + s->count = (value & 3) ?: 4;
> + else if (PAGE_SHIFT == 10)
> + s->count = (value & 1) ?: 2;
> + break;
> +
> + case 0xf220: /* Command */
> + if (s->intstatus & (1 << 15))
> + break;
> + s->command = value;
> + onenand_command(s, s->command);
> + break;
> + case 0xf221: /* System Configuration 1 */
> + s->config[0] = value;
> + onenand_intr_update(s);
> + qemu_set_irq(s->rdy, (s->config[0] >> 7) & 1);
> + break;
> + case 0xf222: /* System Configuration 2 */
> + s->config[1] = value;
> + break;
> +
> + case 0xf241: /* Interrupt */
> + s->intstatus &= value;
> + if ((1 << 15) & ~s->intstatus)
> + s->status &= ~(ONEN_ERR_CMD | ONEN_ERR_ERASE |
> + ONEN_ERR_PROG | ONEN_ERR_LOAD);
> + onenand_intr_update(s);
> + break;
> + case 0xf24c: /* Unlock Start Block Address */
> + s->unladdr[0] = value & (s->blocks - 1);
> + /* For some reason we have to set the end address to by default
> + * be same as start because the software forgets to write
> anything
> + * in there. */
> + s->unladdr[1] = value & (s->blocks - 1);
> + break;
> + case 0xf24d: /* Unlock End Block Address */
> + s->unladdr[1] = value & (s->blocks - 1);
> + break;
> +
> + default:
> + fprintf(stderr, "%s: unknown OneNAND register %x\n",
> + __FUNCTION__, offset);
> + }
> +}
> +
> +static CPUReadMemoryFunc *onenand_readfn[] = {
> + onenand_read, /* TODO */
> + onenand_read,
> + onenand_read,
> +};
> +
> +static CPUWriteMemoryFunc *onenand_writefn[] = {
> + onenand_write, /* TODO */
> + onenand_write,
> + onenand_write,
> +};
> +
> +void *onenand_init(uint32_t id, int regshift, qemu_irq irq)
> +{
> + struct onenand_s *s = (struct onenand_s *) qemu_mallocz(sizeof(*s));
> + int bdrv_index = drive_get_index(IF_MTD, 0, 0);
> + uint32_t size = 1 << (24 + ((id >> 12) & 7));
> + void *ram;
> +
> + s->shift = regshift;
> + s->intr = irq;
> + s->rdy = 0;
> + s->id = id;
> + s->blocks = size >> BLOCK_SHIFT;
> + s->secs = size >> 9;
> + s->blockwp = qemu_malloc(s->blocks);
> + s->density_mask = (id & (1 << 11)) ? (1 << (6 + ((id >> 12) & 7))) :
> 0;
> + s->iomemtype = cpu_register_io_memory(0, onenand_readfn,
> + onenand_writefn, s);
> + if (bdrv_index == -1)
> + s->image = memset(qemu_malloc(size + (size >> 5)),
> + 0xff, size + (size >> 5));
> + else
> + s->bdrv = drives_table[bdrv_index].bdrv;
> + s->otp = memset(qemu_malloc((64 + 2) << PAGE_SHIFT),
> + 0xff, (64 + 2) << PAGE_SHIFT);
> + s->ram = qemu_ram_alloc(0xc000 << s->shift);
> + ram = phys_ram_base + s->ram;
> + s->boot[0] = ram + (0x0000 << s->shift);
> + s->boot[1] = ram + (0x8000 << s->shift);
> + s->data[0][0] = ram + ((0x0200 + (0 << (PAGE_SHIFT - 1))) <<
> s->shift);
> + s->data[0][1] = ram + ((0x8010 + (0 << (PAGE_SHIFT - 6))) <<
> s->shift);
> + s->data[1][0] = ram + ((0x0200 + (1 << (PAGE_SHIFT - 1))) <<
> s->shift);
> + s->data[1][1] = ram + ((0x8010 + (1 << (PAGE_SHIFT - 6))) <<
> s->shift);
> +
> + onenand_reset(s, 1);
> +
> + return s;
> +}
>
> Added: trunk/hw/tmp105.c
> ===================================================================
> --- trunk/hw/tmp105.c (rev 0)
> +++ trunk/hw/tmp105.c 2008-04-14 21:57:44 UTC (rev 4215)
> @@ -0,0 +1,249 @@
> +/*
> + * Texas Instruments TMP105 temperature sensor.
> + *
> + * Copyright (C) 2008 Nokia Corporation
> + * Written by Andrzej Zaborowski <andrew@openedhand.com>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation; either version 2 or
> + * (at your option) version 3 of the License.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
> + * MA 02111-1307 USA
> + */
> +
> +#include "hw.h"
> +#include "i2c.h"
> +
> +struct tmp105_s {
> + i2c_slave i2c;
> + int len;
> + uint8_t buf[2];
> + qemu_irq pin;
> +
> + uint8_t pointer;
> + uint8_t config;
> + int16_t temperature;
> + int16_t limit[2];
> + int faults;
> + int alarm;
> +};
> +
> +static void tmp105_interrupt_update(struct tmp105_s *s)
> +{
> + qemu_set_irq(s->pin, s->alarm ^ ((~s->config >> 2) & 1)); /* POL */
> +}
> +
> +static void tmp105_alarm_update(struct tmp105_s *s)
> +{
> + if ((s->config >> 0) & 1) { /* SD */
> + if ((s->config >> 7) & 1) /* OS */
> + s->config &= ~(1 << 7); /* OS */
> + else
> + return;
> + }
> +
> + if ((s->config >> 1) & 1) { /* TM */
> + if (s->temperature >= s->limit[1])
> + s->alarm = 1;
> + else if (s->temperature < s->limit[0])
> + s->alarm = 1;
> + } else {
> + if (s->temperature >= s->limit[1])
> + s->alarm = 1;
> + else if (s->temperature < s->limit[0])
> + s->alarm = 0;
> + }
> +
> + tmp105_interrupt_update(s);
> +}
> +
> +/* Units are 0.001 centigrades relative to 0 C. */
> +void tmp105_set(i2c_slave *i2c, int temp)
> +{
> + struct tmp105_s *s = (struct tmp105_s *) i2c;
> +
> + if (temp >= 128000 || temp < -128000) {
> + fprintf(stderr, "%s: values is out of range (%i.%03i C)\n",
> + __FUNCTION__, temp / 1000, temp % 1000);
> + exit(-1);
> + }
> +
> + s->temperature = ((int16_t) (temp * 0x800 / 128000)) << 4;
> +
> + tmp105_alarm_update(s);
> +}
> +
> +static const int tmp105_faultq[4] = { 1, 2, 4, 6 };
> +
> +static void tmp105_read(struct tmp105_s *s)
> +{
> + s->len = 0;
> +
> + if ((s->config >> 1) & 1) { /* TM */
> + s->alarm = 0;
> + tmp105_interrupt_update(s);
> + }
> +
> + switch (s->pointer & 3) {
> + case 0: /* Temperature */
> + s->buf[s->len ++] = (((uint16_t) s->temperature) >> 8);
> + s->buf[s->len ++] = (((uint16_t) s->temperature) >> 0) &
> + (0xf0 << ((~s->config >> 5) & 3)); /* R */
> + break;
> +
> + case 1: /* Configuration */
> + s->buf[s->len ++] = s->config;
> + break;
> +
> + case 2: /* T_LOW */
> + s->buf[s->len ++] = ((uint16_t) s->limit[0]) >> 8;
> + s->buf[s->len ++] = ((uint16_t) s->limit[0]) >> 0;
> + break;
> +
> + case 3: /* T_HIGH */
> + s->buf[s->len ++] = ((uint16_t) s->limit[1]) >> 8;
> + s->buf[s->len ++] = ((uint16_t) s->limit[1]) >> 0;
> + break;
> + }
> +}
> +
> +static void tmp105_write(struct tmp105_s *s)
> +{
> + switch (s->pointer & 3) {
> + case 0: /* Temperature */
> + break;
> +
> + case 1: /* Configuration */
> + if (s->buf[0] & ~s->config & (1 << 0)) /* SD */
> + printf("%s: TMP105 shutdown\n", __FUNCTION__);
> + s->config = s->buf[0];
> + s->faults = tmp105_faultq[(s->config >> 3) & 3]; /* F */
> + tmp105_alarm_update(s);
> + break;
> +
> + case 2: /* T_LOW */
> + case 3: /* T_HIGH */
> + if (s->len >= 3)
> + s->limit[s->pointer & 1] = (int16_t)
> + ((((uint16_t) s->buf[0]) << 8) | s->buf[1]);
> + tmp105_alarm_update(s);
> + break;
> + }
> +}
> +
> +static int tmp105_rx(i2c_slave *i2c)
> +{
> + struct tmp105_s *s = (struct tmp105_s *) i2c;
> +
> + if (s->len < 2)
> + return s->buf[s->len ++];
> + else
> + return 0xff;
> +}
> +
> +static int tmp105_tx(i2c_slave *i2c, uint8_t data)
> +{
> + struct tmp105_s *s = (struct tmp105_s *) i2c;
> +
> + if (!s->len ++)
> + s->pointer = data;
> + else {
> + if (s->len <= 2)
> + s->buf[s->len - 1] = data;
> + tmp105_write(s);
> + }
> +
> + return 0;
> +}
> +
> +static void tmp105_event(i2c_slave *i2c, enum i2c_event event)
> +{
> + struct tmp105_s *s = (struct tmp105_s *) i2c;
> +
> + if (event == I2C_START_RECV)
> + tmp105_read(s);
> +
> + s->len = 0;
> +}
> +
> +static void tmp105_save(QEMUFile *f, void *opaque)
> +{
> + struct tmp105_s *s = (struct tmp105_s *) opaque;
> +
> + qemu_put_byte(f, s->len);
> + qemu_put_8s(f, &s->buf[0]);
> + qemu_put_8s(f, &s->buf[1]);
> +
> + qemu_put_8s(f, &s->pointer);
> + qemu_put_8s(f, &s->config);
> + qemu_put_be16s(f, &s->temperature);
> + qemu_put_be16s(f, &s->limit[0]);
> + qemu_put_be16s(f, &s->limit[1]);
> + qemu_put_byte(f, s->alarm);
> + s->faults = tmp105_faultq[(s->config >> 3) & 3]; /* F */
> +
> + i2c_slave_save(f, &s->i2c);
> +}
> +
> +static int tmp105_load(QEMUFile *f, void *opaque, int version_id)
> +{
> + struct tmp105_s *s = (struct tmp105_s *) opaque;
> +
> + s->len = qemu_get_byte(f);
> + qemu_get_8s(f, &s->buf[0]);
> + qemu_get_8s(f, &s->buf[1]);
> +
> + qemu_get_8s(f, &s->pointer);
> + qemu_get_8s(f, &s->config);
> + qemu_get_be16s(f, &s->temperature);
> + qemu_get_be16s(f, &s->limit[0]);
> + qemu_get_be16s(f, &s->limit[1]);
> + s->alarm = qemu_get_byte(f);
> +
> + tmp105_interrupt_update(s);
> +
> + i2c_slave_load(f, &s->i2c);
> + return 0;
> +}
> +
> +void tmp105_reset(i2c_slave *i2c)
> +{
> + struct tmp105_s *s = (struct tmp105_s *) i2c;
> +
> + s->temperature = 0;
> + s->pointer = 0;
> + s->config = 0;
> + s->faults = tmp105_faultq[(s->config >> 3) & 3];
> + s->alarm = 0;
> +
> + tmp105_interrupt_update(s);
> +}
> +
> +static int tmp105_iid = 0;
> +
> +struct i2c_slave *tmp105_init(i2c_bus *bus, qemu_irq alarm)
> +{
> + struct tmp105_s *s = (struct tmp105_s *)
> + i2c_slave_init(bus, 0, sizeof(struct tmp105_s));
> +
> + s->i2c.event = tmp105_event;
> + s->i2c.recv = tmp105_rx;
> + s->i2c.send = tmp105_tx;
> + s->pin = alarm;
> +
> + tmp105_reset(&s->i2c);
> +
> + register_savevm("TMP105", tmp105_iid ++, 0,
> + tmp105_save, tmp105_load, s);
> +
> + return &s->i2c;
> +}
>
> Added: trunk/hw/twl92230.c
> ===================================================================
> --- trunk/hw/twl92230.c (rev 0)
> +++ trunk/hw/twl92230.c 2008-04-14 21:57:44 UTC (rev 4215)
> @@ -0,0 +1,923 @@
> +/*
> + * TI TWL92230C energy-management companion device for the OMAP24xx.
> + * Aka. Menelaus (N4200 MENELAUS1_V2.2)
> + *
> + * Copyright (C) 2008 Nokia Corporation
> + * Written by Andrzej Zaborowski <andrew@openedhand.com>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation; either version 2 or
> + * (at your option) version 3 of the License.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
> + * MA 02111-1307 USA
> + */
> +
> +#include "hw.h"
> +#include "qemu-timer.h"
> +#include "i2c.h"
> +#include "sysemu.h"
> +#include "console.h"
> +
> +#define VERBOSE 1
> +
> +struct menelaus_s {
> + i2c_slave i2c;
> + qemu_irq irq;
> +
> + int firstbyte;
> + uint8_t reg;
> +
> + uint8_t vcore[5];
> + uint8_t dcdc[3];
> + uint8_t ldo[8];
> + uint8_t sleep[2];
> + uint8_t osc;
> + uint8_t detect;
> + uint16_t mask;
> + uint16_t status;
> + uint8_t dir;
> + uint8_t inputs;
> + uint8_t outputs;
> + uint8_t bbsms;
> + uint8_t pull[4];
> + uint8_t mmc_ctrl[3];
> + uint8_t mmc_debounce;
> + struct {
> + uint8_t ctrl;
> + uint16_t comp;
> + QEMUTimer *hz;
> + int64_t next;
> + struct tm tm;
> + struct tm new;
> + struct tm alm;
> + time_t sec;
> + time_t alm_sec;
> + time_t next_comp;
> + struct tm *(*gettime)(const time_t *timep, struct tm *result);
> + } rtc;
> + qemu_irq handler[3];
> + qemu_irq *in;
> + int pwrbtn_state;
> + qemu_irq pwrbtn;
> +};
> +
> +static inline void menelaus_update(struct menelaus_s *s)
> +{
> + qemu_set_irq(s->irq, s->status & ~s->mask);
> +}
> +
> +static inline void menelaus_rtc_start(struct menelaus_s *s)
> +{
> + s->rtc.next =+ qemu_get_clock(rt_clock);
> + qemu_mod_timer(s->rtc.hz, s->rtc.next);
> +}
> +
> +static inline void menelaus_rtc_stop(struct menelaus_s *s)
> +{
> + qemu_del_timer(s->rtc.hz);
> + s->rtc.next =- qemu_get_clock(rt_clock);
> + if (s->rtc.next < 1)
> + s->rtc.next = 1;
> +}
> +
> +static void menelaus_rtc_update(struct menelaus_s *s)
> +{
> + s->rtc.gettime(&s->rtc.sec, &s->rtc.tm);
> +}
> +
> +static void menelaus_alm_update(struct menelaus_s *s)
> +{
> + if ((s->rtc.ctrl & 3) == 3)
> + s->rtc.alm_sec = mktime(&s->rtc.alm);
> +}
> +
> +static void menelaus_rtc_hz(void *opaque)
> +{
> + struct menelaus_s *s = (struct menelaus_s *) opaque;
> +
> + s->rtc.sec ++;
> + s->rtc.next += 1000;
> + qemu_mod_timer(s->rtc.hz, s->rtc.next);
> + if ((s->rtc.ctrl >> 3) & 3) { /* EVERY */
> + menelaus_rtc_update(s);
> + if (((s->rtc.ctrl >> 3) & 3) == 1 && !s->rtc.tm.tm_sec)
> + s->status |= 1 << 8; /* RTCTMR */
> + else if (((s->rtc.ctrl >> 3) & 3) == 2 && !s->rtc.tm.tm_min)
> + s->status |= 1 << 8; /* RTCTMR */
> + else if (!s->rtc.tm.tm_hour)
> + s->status |= 1 << 8; /* RTCTMR */
> + } else
> + s->status |= 1 << 8; /* RTCTMR */
> + if ((s->rtc.ctrl >> 1) & 1) { /* RTC_AL_EN */
> + if (s->rtc.sec == s->rtc.alm_sec)
> + s->status |= 1 << 9; /* RTCALM */
> + /* TODO: wake-up */
> + }
> + if (s->rtc.next_comp >= s->rtc.sec) {
> + s->rtc.next -= muldiv64((int16_t) s->rtc.comp, 1000, 0x8000);
> + s->rtc.next_comp = s->rtc.sec + 3600;
> + }
> + menelaus_update(s);
> +}
> +
> +void menelaus_reset(i2c_slave *i2c)
> +{
> + struct menelaus_s *s = (struct menelaus_s *) i2c;
> + time_t ti;
> + s->reg = 0x00;
> +
> + s->vcore[0] = 0x0c; /* XXX: X-loader needs 0x8c? check! */
> + s->vcore[1] = 0x05;
> + s->vcore[2] = 0x02;
> + s->vcore[3] = 0x0c;
> + s->vcore[4] = 0x03;
> + s->dcdc[0] = 0x33; /* Depends on wiring */
> + s->dcdc[1] = 0x03;
> + s->dcdc[2] = 0x00;
> + s->ldo[0] = 0x95;
> + s->ldo[1] = 0x7e;
> + s->ldo[2] = 0x00;
> + s->ldo[3] = 0x00; /* Depends on wiring */
> + s->ldo[4] = 0x03; /* Depends on wiring */
> + s->ldo[5] = 0x00;
> + s->ldo[6] = 0x00;
> + s->ldo[7] = 0x00;
> + s->sleep[0] = 0x00;
> + s->sleep[1] = 0x00;
> + s->osc = 0x01;
> + s->detect = 0x09;
> + s->mask = 0x0fff;
> + s->status = 0;
> + s->dir = 0x07;
> + s->outputs = 0x00;
> + s->bbsms = 0x00;
> + s->pull[0] = 0x00;
> + s->pull[1] = 0x00;
> + s->pull[2] = 0x00;
> + s->pull[3] = 0x00;
> + s->mmc_ctrl[0] = 0x03;
> + s->mmc_ctrl[1] = 0xc0;
> + s->mmc_ctrl[2] = 0x00;
> + s->mmc_debounce = 0x05;
> +
> + time(&ti);
> + if (s->rtc.ctrl & 1)
> + menelaus_rtc_stop(s);
> + s->rtc.ctrl = 0x00;
> + s->rtc.comp = 0x0000;
> + s->rtc.next = 1000;
> + s->rtc.sec = ti;
> + s->rtc.next_comp = s->rtc.sec + 1800;
> + s->rtc.alm.tm_sec = 0x00;
> + s->rtc.alm.tm_min = 0x00;
> + s->rtc.alm.tm_hour = 0x00;
> + s->rtc.alm.tm_mday = 0x01;
> + s->rtc.alm.tm_mon = 0x00;
> + s->rtc.alm.tm_year = 2004;
> + menelaus_update(s);
> +}
> +
> +static inline uint8_t to_bcd(int val)
> +{
> + return ((val / 10) << 4) | (val % 10);
> +}
> +
> +static inline int from_bcd(uint8_t val)
> +{
> + return ((val >> 4) * 10) + (val & 0x0f);
> +}
> +
> +static void menelaus_gpio_set(void *opaque, int line, int level)
> +{
> + struct menelaus_s *s = (struct menelaus_s *) opaque;
> +
> + /* No interrupt generated */
> + s->inputs &= ~(1 << line);
> + s->inputs |= level << line;
> +}
> +
> +static void menelaus_pwrbtn_set(void *opaque, int line, int level)
> +{
> + struct menelaus_s *s = (struct menelaus_s *) opaque;
> +
> + if (!s->pwrbtn_state && level) {
> + s->status |= 1 << 11; /* PSHBTN */
> + menelaus_update(s);
> + }
> + s->pwrbtn_state = level;
> +}
> +
> +#define MENELAUS_REV 0x01
> +#define MENELAUS_VCORE_CTRL1 0x02
> +#define MENELAUS_VCORE_CTRL2 0x03
> +#define MENELAUS_VCORE_CTRL3 0x04
> +#define MENELAUS_VCORE_CTRL4 0x05
> +#define MENELAUS_VCORE_CTRL5 0x06
> +#define MENELAUS_DCDC_CTRL1 0x07
> +#define MENELAUS_DCDC_CTRL2 0x08
> +#define MENELAUS_DCDC_CTRL3 0x09
> +#define MENELAUS_LDO_CTRL1 0x0a
> +#define MENELAUS_LDO_CTRL2 0x0b
> +#define MENELAUS_LDO_CTRL3 0x0c
> +#define MENELAUS_LDO_CTRL4 0x0d
> +#define MENELAUS_LDO_CTRL5 0x0e
> +#define MENELAUS_LDO_CTRL6 0x0f
> +#define MENELAUS_LDO_CTRL7 0x10
> +#define MENELAUS_LDO_CTRL8 0x11
> +#define MENELAUS_SLEEP_CTRL1 0x12
> +#define MENELAUS_SLEEP_CTRL2 0x13
> +#define MENELAUS_DEVICE_OFF 0x14
> +#define MENELAUS_OSC_CTRL 0x15
> +#define MENELAUS_DETECT_CTRL 0x16
> +#define MENELAUS_INT_MASK1 0x17
> +#define MENELAUS_INT_MASK2 0x18
> +#define MENELAUS_INT_STATUS1 0x19
> +#define MENELAUS_INT_STATUS2 0x1a
> +#define MENELAUS_INT_ACK1 0x1b
> +#define MENELAUS_INT_ACK2 0x1c
> +#define MENELAUS_GPIO_CTRL 0x1d
> +#define MENELAUS_GPIO_IN 0x1e
> +#define MENELAUS_GPIO_OUT 0x1f
> +#define MENELAUS_BBSMS 0x20
> +#define MENELAUS_RTC_CTRL 0x21
> +#define MENELAUS_RTC_UPDATE 0x22
> +#define MENELAUS_RTC_SEC 0x23
> +#define MENELAUS_RTC_MIN 0x24
> +#define MENELAUS_RTC_HR 0x25
> +#define MENELAUS_RTC_DAY 0x26
> +#define MENELAUS_RTC_MON 0x27
> +#define MENELAUS_RTC_YR 0x28
> +#define MENELAUS_RTC_WKDAY 0x29
> +#define MENELAUS_RTC_AL_SEC 0x2a
> +#define MENELAUS_RTC_AL_MIN 0x2b
> +#define MENELAUS_RTC_AL_HR 0x2c
> +#define MENELAUS_RTC_AL_DAY 0x2d
> +#define MENELAUS_RTC_AL_MON 0x2e
> +#define MENELAUS_RTC_AL_YR 0x2f
> +#define MENELAUS_RTC_COMP_MSB 0x30
> +#define MENELAUS_RTC_COMP_LSB 0x31
> +#define MENELAUS_S1_PULL_EN 0x32
> +#define MENELAUS_S1_PULL_DIR 0x33
> +#define MENELAUS_S2_PULL_EN 0x34
> +#define MENELAUS_S2_PULL_DIR 0x35
> +#define MENELAUS_MCT_CTRL1 0x36
> +#define MENELAUS_MCT_CTRL2 0x37
> +#define MENELAUS_MCT_CTRL3 0x38
> +#define MENELAUS_MCT_PIN_ST 0x39
> +#define MENELAUS_DEBOUNCE1 0x3a
> +
> +static uint8_t menelaus_read(void *opaque, uint8_t addr)
> +{
> + struct menelaus_s *s = (struct menelaus_s *) opaque;
> + int reg = 0;
> +
> + switch (addr) {
> + case MENELAUS_REV:
> + return 0x22;
> +
> + case MENELAUS_VCORE_CTRL5: reg ++;
> + case MENELAUS_VCORE_CTRL4: reg ++;
> + case MENELAUS_VCORE_CTRL3: reg ++;
> + case MENELAUS_VCORE_CTRL2: reg ++;
> + case MENELAUS_VCORE_CTRL1:
> + return s->vcore[reg];
> +
> + case MENELAUS_DCDC_CTRL3: reg ++;
> + case MENELAUS_DCDC_CTRL2: reg ++;
> + case MENELAUS_DCDC_CTRL1:
> + return s->dcdc[reg];
> +
> + case MENELAUS_LDO_CTRL8: reg ++;
> + case MENELAUS_LDO_CTRL7: reg ++;
> + case MENELAUS_LDO_CTRL6: reg ++;
> + case MENELAUS_LDO_CTRL5: reg ++;
> + case MENELAUS_LDO_CTRL4: reg ++;
> + case MENELAUS_LDO_CTRL3: reg ++;
> + case MENELAUS_LDO_CTRL2: reg ++;
> + case MENELAUS_LDO_CTRL1:
> + return s->ldo[reg];
> +
> + case MENELAUS_SLEEP_CTRL2: reg ++;
> + case MENELAUS_SLEEP_CTRL1:
> + return s->sleep[reg];
> +
> + case MENELAUS_DEVICE_OFF:
> + return 0;
> +
> + case MENELAUS_OSC_CTRL:
> + return s->osc | (1 << 7); /* CLK32K_GOOD */
> +
> + case MENELAUS_DETECT_CTRL:
> + return s->detect;
> +
> + case MENELAUS_INT_MASK1:
> + return (s->mask >> 0) & 0xff;
> + case MENELAUS_INT_MASK2:
> + return (s->mask >> 8) & 0xff;
> +
> + case MENELAUS_INT_STATUS1:
> + return (s->status >> 0) & 0xff;
> + case MENELAUS_INT_STATUS2:
> + return (s->status >> 8) & 0xff;
> +
> + case MENELAUS_INT_ACK1:
> + case MENELAUS_INT_ACK2:
> + return 0;
> +
> + case MENELAUS_GPIO_CTRL:
> + return s->dir;
> + case MENELAUS_GPIO_IN:
> + return s->inputs | (~s->dir & s->outputs);
> + case MENELAUS_GPIO_OUT:
> + return s->outputs;
> +
> + case MENELAUS_BBSMS:
> + return s->bbsms;
> +
> + case MENELAUS_RTC_CTRL:
> + return s->rtc.ctrl;
> + case MENELAUS_RTC_UPDATE:
> + return 0x00;
> + case MENELAUS_RTC_SEC:
> + menelaus_rtc_update(s);
> + return to_bcd(s->rtc.tm.tm_sec);
> + case MENELAUS_RTC_MIN:
> + menelaus_rtc_update(s);
> + return to_bcd(s->rtc.tm.tm_min);
> + case MENELAUS_RTC_HR:
> + menelaus_rtc_update(s);
> + if ((s->rtc.ctrl >> 2) & 1) /* MODE12_n24 */
> + return to_bcd((s->rtc.tm.tm_hour % 12) + 1) |
> + (!!(s->rtc.tm.tm_hour >= 12) << 7); /* PM_nAM */
> + else
> + return to_bcd(s->rtc.tm.tm_hour);
> + case MENELAUS_RTC_DAY:
> + menelaus_rtc_update(s);
> + return to_bcd(s->rtc.tm.tm_mday);
> + case MENELAUS_RTC_MON:
> + menelaus_rtc_update(s);
> + return to_bcd(s->rtc.tm.tm_mon + 1);
> + case MENELAUS_RTC_YR:
> + menelaus_rtc_update(s);
> + return to_bcd(s->rtc.tm.tm_year - 2000);
> + case MENELAUS_RTC_WKDAY:
> + menelaus_rtc_update(s);
> + return to_bcd(s->rtc.tm.tm_wday);
> + case MENELAUS_RTC_AL_SEC:
> + return to_bcd(s->rtc.alm.tm_sec);
> + case MENELAUS_RTC_AL_MIN:
> + return to_bcd(s->rtc.alm.tm_min);
> + case MENELAUS_RTC_AL_HR:
> + if ((s->rtc.ctrl >> 2) & 1) /* MODE12_n24 */
> + return to_bcd((s->rtc.alm.tm_hour % 12) + 1) |
> + (!!(s->rtc.alm.tm_hour >= 12) << 7);/* AL_PM_nAM */
> + else
> + return to_bcd(s->rtc.alm.tm_hour);
> + case MENELAUS_RTC_AL_DAY:
> + return to_bcd(s->rtc.alm.tm_mday);
> + case MENELAUS_RTC_AL_MON:
> + return to_bcd(s->rtc.alm.tm_mon + 1);
> + case MENELAUS_RTC_AL_YR:
> + return to_bcd(s->rtc.alm.tm_year - 2000);
> + case MENELAUS_RTC_COMP_MSB:
> + return (s->rtc.comp >> 8) & 0xff;
> + case MENELAUS_RTC_COMP_LSB:
> + return (s->rtc.comp >> 0) & 0xff;
> +
> + case MENELAUS_S1_PULL_EN:
> + return s->pull[0];
> + case MENELAUS_S1_PULL_DIR:
> + return s->pull[1];
> + case MENELAUS_S2_PULL_EN:
> + return s->pull[2];
> + case MENELAUS_S2_PULL_DIR:
> + return s->pull[3];
> +
> + case MENELAUS_MCT_CTRL3: reg ++;
> + case MENELAUS_MCT_CTRL2: reg ++;
> + case MENELAUS_MCT_CTRL1:
> + return s->mmc_ctrl[reg];
> + case MENELAUS_MCT_PIN_ST:
> + /* TODO: return the real Card Detect */
> + return 0;
> + case MENELAUS_DEBOUNCE1:
> + return s->mmc_debounce;
> +
> + default:
> +#ifdef VERBOSE
> + printf("%s: unknown register %02x\n", __FUNCTION__, addr);
> +#endif
> + break;
> + }
> + return 0;
> +}
> +
> +static void menelaus_write(void *opaque, uint8_t addr, uint8_t value)
> +{
> + struct menelaus_s *s = (struct menelaus_s *) opaque;
> + int line;
> + int reg = 0;
> + struct tm tm;
> +
> + switch (addr) {
> + case MENELAUS_VCORE_CTRL1:
> + s->vcore[0] = (value & 0xe) | MIN(value & 0x1f, 0x12);
> + break;
> + case MENELAUS_VCORE_CTRL2:
> + s->vcore[1] = value;
> + break;
> + case MENELAUS_VCORE_CTRL3:
> + s->vcore[2] = MIN(value & 0x1f, 0x12);
> + break;
> + case MENELAUS_VCORE_CTRL4:
> + s->vcore[3] = MIN(value & 0x1f, 0x12);
> + break;
> + case MENELAUS_VCORE_CTRL5:
> + s->vcore[4] = value & 3;
> + /* XXX
> + * auto set to 3 on M_Active, nRESWARM
> + * auto set to 0 on M_WaitOn, M_Backup
> + */
> + break;
> +
> + case MENELAUS_DCDC_CTRL1:
> + s->dcdc[0] = value & 0x3f;
> + break;
> + case MENELAUS_DCDC_CTRL2:
> + s->dcdc[1] = value & 0x07;
> + /* XXX
> + * auto set to 3 on M_Active, nRESWARM
> + * auto set to 0 on M_WaitOn, M_Backup
> + */
> + break;
> + case MENELAUS_DCDC_CTRL3:
> + s->dcdc[2] = value & 0x07;
> + break;
> +
> + case MENELAUS_LDO_CTRL1:
> + s->ldo[0] = value;
> + break;
> + case MENELAUS_LDO_CTRL2:
> + s->ldo[1] = value & 0x7f;
> + /* XXX
> + * auto set to 0x7e on M_WaitOn, M_Backup
> + */
> + break;
> + case MENELAUS_LDO_CTRL3:
> + s->ldo[2] = value & 3;
> + /* XXX
> + * auto set to 3 on M_Active, nRESWARM
> + * auto set to 0 on M_WaitOn, M_Backup
> + */
> + break;
> + case MENELAUS_LDO_CTRL4:
> + s->ldo[3] = value & 3;
> + /* XXX
> + * auto set to 3 on M_Active, nRESWARM
> + * auto set to 0 on M_WaitOn, M_Backup
> + */
> + break;
> + case MENELAUS_LDO_CTRL5:
> + s->ldo[4] = value & 3;
> + /* XXX
> + * auto set to 3 on M_Active, nRESWARM
> + * auto set to 0 on M_WaitOn, M_Backup
> + */
> + break;
> + case MENELAUS_LDO_CTRL6:
> + s->ldo[5] = value & 3;
> + break;
> + case MENELAUS_LDO_CTRL7:
> + s->ldo[6] = value & 3;
> + break;
> + case MENELAUS_LDO_CTRL8:
> + s->ldo[7] = value & 3;
> + break;
> +
> + case MENELAUS_SLEEP_CTRL2: reg ++;
> + case MENELAUS_SLEEP_CTRL1:
> + s->sleep[reg] = value;
> + break;
> +
> + case MENELAUS_DEVICE_OFF:
> + if (value & 1)
> + menelaus_reset(&s->i2c);
> + break;
> +
> + case MENELAUS_OSC_CTRL:
> + s->osc = value & 7;
> + break;
> +
> + case MENELAUS_DETECT_CTRL:
> + s->detect = value & 0x7f;
> + break;
> +
> + case MENELAUS_INT_MASK1:
> + s->mask &= 0xf00;
> + s->mask |= value << 0;
> + menelaus_update(s);
> + break;
> + case MENELAUS_INT_MASK2:
> + s->mask &= 0x0ff;
> + s->mask |= value << 8;
> + menelaus_update(s);
> + break;
> +
> + case MENELAUS_INT_ACK1:
> + s->status &= ~(((uint16_t) value) << 0);
> + menelaus_update(s);
> + break;
> + case MENELAUS_INT_ACK2:
> + s->status &= ~(((uint16_t) value) << 8);
> + menelaus_update(s);
> + break;
> +
> + case MENELAUS_GPIO_CTRL:
> + for (line = 0; line < 3; line ++)
> + if (((s->dir ^ value) >> line) & 1)
> + if (s->handler[line])
> + qemu_set_irq(s->handler[line],
> + ((s->outputs & ~s->dir) >> line) &
> 1);
> + s->dir = value & 0x67;
> + break;
> + case MENELAUS_GPIO_OUT:
> + for (line = 0; line < 3; line ++)
> + if ((((s->outputs ^ value) & ~s->dir) >> line) & 1)
> + if (s->handler[line])
> + qemu_set_irq(s->handler[line], (s->outputs >> line) &
> 1);
> + s->outputs = value & 0x07;
> + break;
> +
> + case MENELAUS_BBSMS:
> + s->bbsms = 0x0d;
> + break;
> +
> + case MENELAUS_RTC_CTRL:
> + if ((s->rtc.ctrl ^ value) & 1) { /* RTC_EN */
> + if (value & 1)
> + menelaus_rtc_start(s);
> + else
> + menelaus_rtc_stop(s);
> + }
> + s->rtc.ctrl = value & 0x1f;
> + menelaus_alm_update(s);
> + break;
> + case MENELAUS_RTC_UPDATE:
> + menelaus_rtc_update(s);
> + memcpy(&tm, &s->rtc.tm, sizeof(tm));
> + switch (value & 0xf) {
> + case 0:
> + break;
> + case 1:
> + tm.tm_sec = s->rtc.new.tm_sec;
> + break;
> + case 2:
> + tm.tm_min = s->rtc.new.tm_min;
> + break;
> + case 3:
> + if (s->rtc.new.tm_hour > 23)
> + goto rtc_badness;
> + tm.tm_hour = s->rtc.new.tm_hour;
> + break;
> + case 4:
> + if (s->rtc.new.tm_mday < 1)
> + goto rtc_badness;
> + /* TODO check range */
> + tm.tm_mday = s->rtc.new.tm_mday;
> + break;
> + case 5:
> + if (s->rtc.new.tm_mon < 0 || s->rtc.new.tm_mon > 11)
> + goto rtc_badness;
> + tm.tm_mon = s->rtc.new.tm_mon;
> + break;
> + case 6:
> + tm.tm_year = s->rtc.new.tm_year;
> + break;
> + case 7:
> + /* TODO set .tm_mday instead */
> + tm.tm_wday = s->rtc.new.tm_wday;
> + break;
> + case 8:
> + if (s->rtc.new.tm_hour > 23)
> + goto rtc_badness;
> + if (s->rtc.new.tm_mday < 1)
> + goto rtc_badness;
> + if (s->rtc.new.tm_mon < 0 || s->rtc.new.tm_mon > 11)
> + goto rtc_badness;
> + tm.tm_sec = s->rtc.new.tm_sec;
> + tm.tm_min = s->rtc.new.tm_min;
> + tm.tm_hour = s->rtc.new.tm_hour;
> + tm.tm_mday = s->rtc.new.tm_mday;
> + tm.tm_mon = s->rtc.new.tm_mon;
> + tm.tm_year = s->rtc.new.tm_year;
> + break;
> + rtc_badness:
> + default:
> + fprintf(stderr, "%s: bad RTC_UPDATE value %02x\n",
> + __FUNCTION__, value);
> + s->status |= 1 << 10; /* RTCERR */
> + menelaus_update(s);
> + }
> + s->rtc.sec += difftime(mktime(&tm), mktime(&s->rtc.tm));
> + break;
> + case MENELAUS_RTC_SEC:
> + s->rtc.tm.tm_sec = from_bcd(value & 0x7f);
> + break;
> + case MENELAUS_RTC_MIN:
> + s->rtc.tm.tm_min = from_bcd(value & 0x7f);
> + break;
> + case MENELAUS_RTC_HR:
> + s->rtc.tm.tm_hour = (s->rtc.ctrl & (1 << 2)) ? /* MODE12_n24 */
> + MIN(from_bcd(value & 0x3f), 12) + ((value >> 7) ? 11
> : -1) :
> + from_bcd(value & 0x3f);
> + break;
> + case MENELAUS_RTC_DAY:
> + s->rtc.tm.tm_mday = from_bcd(value);
> + break;
> + case MENELAUS_RTC_MON:
> + s->rtc.tm.tm_mon = MAX(1, from_bcd(value)) - 1;
> + break;
> + case MENELAUS_RTC_YR:
> + s->rtc.tm.tm_year = 2000 + from_bcd(value);
> + break;
> + case MENELAUS_RTC_WKDAY:
> + s->rtc.tm.tm_mday = from_bcd(value);
> + break;
> + case MENELAUS_RTC_AL_SEC:
> + s->rtc.alm.tm_sec = from_bcd(value & 0x7f);
> + menelaus_alm_update(s);
> + break;
> + case MENELAUS_RTC_AL_MIN:
> + s->rtc.alm.tm_min = from_bcd(value & 0x7f);
> + menelaus_alm_update(s);
> + break;
> + case MENELAUS_RTC_AL_HR:
> + s->rtc.alm.tm_hour = (s->rtc.ctrl & (1 << 2)) ? /* MODE12_n24 */
> + MIN(from_bcd(value & 0x3f), 12) + ((value >> 7) ? 11
> : -1) :
> + from_bcd(value & 0x3f);
> + menelaus_alm_update(s);
> + break;
> + case MENELAUS_RTC_AL_DAY:
> + s->rtc.alm.tm_mday = from_bcd(value);
> + menelaus_alm_update(s);
> + break;
> + case MENELAUS_RTC_AL_MON:
> + s->rtc.alm.tm_mon = MAX(1, from_bcd(value)) - 1;
> + menelaus_alm_update(s);
> + break;
> + case MENELAUS_RTC_AL_YR:
> + s->rtc.alm.tm_year = 2000 + from_bcd(value);
> + menelaus_alm_update(s);
> + break;
> + case MENELAUS_RTC_COMP_MSB:
> + s->rtc.comp &= 0xff;
> + s->rtc.comp |= value << 8;
> + break;
> + case MENELAUS_RTC_COMP_LSB:
> + s->rtc.comp &= 0xff << 8;
> + s->rtc.comp |= value;
> + break;
> +
> + case MENELAUS_S1_PULL_EN:
> + s->pull[0] = value;
> + break;
> + case MENELAUS_S1_PULL_DIR:
> + s->pull[1] = value & 0x1f;
> + break;
> + case MENELAUS_S2_PULL_EN:
> + s->pull[2] = value;
> + break;
> + case MENELAUS_S2_PULL_DIR:
> + s->pull[3] = value & 0x1f;
> + break;
> +
> + case MENELAUS_MCT_CTRL1:
> + s->mmc_ctrl[0] = value & 0x7f;
> + break;
> + case MENELAUS_MCT_CTRL2:
> + s->mmc_ctrl[1] = value;
> + /* TODO update Card Detect interrupts */
> + break;
> + case MENELAUS_MCT_CTRL3:
> + s->mmc_ctrl[2] = value & 0xf;
> + break;
> + case MENELAUS_DEBOUNCE1:
> + s->mmc_debounce = value & 0x3f;
> + break;
> +
> + default:
> +#ifdef VERBOSE
> + printf("%s: unknown register %02x\n", __FUNCTION__, addr);
> +#endif
> + }
> +}
> +
> +static void menelaus_event(i2c_slave *i2c, enum i2c_event event)
> +{
> + struct menelaus_s *s = (struct menelaus_s *) i2c;
> +
> + if (event == I2C_START_SEND)
> + s->firstbyte = 1;
> +}
> +
> +static int menelaus_tx(i2c_slave *i2c, uint8_t data)
> +{
> + struct menelaus_s *s = (struct menelaus_s *) i2c;
> + /* Interpret register address byte */
> + if (s->firstbyte) {
> + s->reg = data;
> + s->firstbyte = 0;
> + } else
> + menelaus_write(s, s->reg ++, data);
> +
> + return 0;
> +}
> +
> +static int menelaus_rx(i2c_slave *i2c)
> +{
> + struct menelaus_s *s = (struct menelaus_s *) i2c;
> +
> + return menelaus_read(s, s->reg ++);
> +}
> +
> +static void tm_put(QEMUFile *f, struct tm *tm) {
> + qemu_put_be16(f, tm->tm_sec);
> + qemu_put_be16(f, tm->tm_min);
> + qemu_put_be16(f, tm->tm_hour);
> + qemu_put_be16(f, tm->tm_mday);
> + qemu_put_be16(f, tm->tm_min);
> + qemu_put_be16(f, tm->tm_year);
> +}
> +
> +static void tm_get(QEMUFile *f, struct tm *tm) {
> + tm->tm_sec = qemu_get_be16(f);
> + tm->tm_min = qemu_get_be16(f);
> + tm->tm_hour = qemu_get_be16(f);
> + tm->tm_mday = qemu_get_be16(f);
> + tm->tm_min = qemu_get_be16(f);
> + tm->tm_year = qemu_get_be16(f);
> +}
> +
> +static void menelaus_save(QEMUFile *f, void *opaque)
> +{
> + struct menelaus_s *s = (struct menelaus_s *) opaque;
> +
> + qemu_put_be32(f, s->firstbyte);
> + qemu_put_8s(f, &s->reg);
> +
> + qemu_put_8s(f, &s->vcore[0]);
> + qemu_put_8s(f, &s->vcore[1]);
> + qemu_put_8s(f, &s->vcore[2]);
> + qemu_put_8s(f, &s->vcore[3]);
> + qemu_put_8s(f, &s->vcore[4]);
> + qemu_put_8s(f, &s->dcdc[3]);
> + qemu_put_8s(f, &s->dcdc[3]);
> + qemu_put_8s(f, &s->dcdc[3]);
> + qemu_put_8s(f, &s->ldo[0]);
> + qemu_put_8s(f, &s->ldo[1]);
> + qemu_put_8s(f, &s->ldo[2]);
> + qemu_put_8s(f, &s->ldo[3]);
> + qemu_put_8s(f, &s->ldo[4]);
> + qemu_put_8s(f, &s->ldo[5]);
> + qemu_put_8s(f, &s->ldo[6]);
> + qemu_put_8s(f, &s->ldo[7]);
> + qemu_put_8s(f, &s->sleep[0]);
> + qemu_put_8s(f, &s->sleep[1]);
> + qemu_put_8s(f, &s->osc);
> + qemu_put_8s(f, &s->detect);
> + qemu_put_be16s(f, &s->mask);
> + qemu_put_be16s(f, &s->status);
> + qemu_put_8s(f, &s->dir);
> + qemu_put_8s(f, &s->inputs);
> + qemu_put_8s(f, &s->outputs);
> + qemu_put_8s(f, &s->bbsms);
> + qemu_put_8s(f, &s->pull[0]);
> + qemu_put_8s(f, &s->pull[1]);
> + qemu_put_8s(f, &s->pull[2]);
> + qemu_put_8s(f, &s->pull[3]);
> + qemu_put_8s(f, &s->mmc_ctrl[0]);
> + qemu_put_8s(f, &s->mmc_ctrl[1]);
> + qemu_put_8s(f, &s->mmc_ctrl[2]);
> + qemu_put_8s(f, &s->mmc_debounce);
> + qemu_put_8s(f, &s->rtc.ctrl);
> + qemu_put_be16s(f, &s->rtc.comp);
> + /* Should be <= 1000 */
> + qemu_put_be16(f, s->rtc.next - qemu_get_clock(rt_clock));
> + tm_put(f, &s->rtc.new);
> + tm_put(f, &s->rtc.alm);
> + qemu_put_byte(f, s->pwrbtn_state);
> +
> + i2c_slave_save(f, &s->i2c);
> +}
> +
> +static int menelaus_load(QEMUFile *f, void *opaque, int version_id)
> +{
> + struct menelaus_s *s = (struct menelaus_s *) opaque;
> +
> + s->firstbyte = qemu_get_be32(f);
> + qemu_get_8s(f, &s->reg);
> +
> + if (s->rtc.ctrl & 1) /* RTC_EN */
> + menelaus_rtc_stop(s);
> + qemu_get_8s(f, &s->vcore[0]);
> + qemu_get_8s(f, &s->vcore[1]);
> + qemu_get_8s(f, &s->vcore[2]);
> + qemu_get_8s(f, &s->vcore[3]);
> + qemu_get_8s(f, &s->vcore[4]);
> + qemu_get_8s(f, &s->dcdc[3]);
> + qemu_get_8s(f, &s->dcdc[3]);
> + qemu_get_8s(f, &s->dcdc[3]);
> + qemu_get_8s(f, &s->ldo[0]);
> + qemu_get_8s(f, &s->ldo[1]);
> + qemu_get_8s(f, &s->ldo[2]);
> + qemu_get_8s(f, &s->ldo[3]);
> + qemu_get_8s(f, &s->ldo[4]);
> + qemu_get_8s(f, &s->ldo[5]);
> + qemu_get_8s(f, &s->ldo[6]);
> + qemu_get_8s(f, &s->ldo[7]);
> + qemu_get_8s(f, &s->sleep[0]);
> + qemu_get_8s(f, &s->sleep[1]);
> + qemu_get_8s(f, &s->osc);
> + qemu_get_8s(f, &s->detect);
> + qemu_get_be16s(f, &s->mask);
> + qemu_get_be16s(f, &s->status);
> + qemu_get_8s(f, &s->dir);
> + qemu_get_8s(f, &s->inputs);
> + qemu_get_8s(f, &s->outputs);
> + qemu_get_8s(f, &s->bbsms);
> + qemu_get_8s(f, &s->pull[0]);
> + qemu_get_8s(f, &s->pull[1]);
> + qemu_get_8s(f, &s->pull[2]);
> + qemu_get_8s(f, &s->pull[3]);
> + qemu_get_8s(f, &s->mmc_ctrl[0]);
> + qemu_get_8s(f, &s->mmc_ctrl[1]);
> + qemu_get_8s(f, &s->mmc_ctrl[2]);
> + qemu_get_8s(f, &s->mmc_debounce);
> + qemu_get_8s(f, &s->rtc.ctrl);
> + qemu_get_be16s(f, &s->rtc.comp);
> + s->rtc.next = qemu_get_be16(f);
> + tm_get(f, &s->rtc.new);
> + tm_get(f, &s->rtc.alm);
> + s->pwrbtn_state = qemu_get_byte(f);
> + menelaus_alm_update(s);
> + menelaus_update(s);
> + if (s->rtc.ctrl & 1) /* RTC_EN */
> + menelaus_rtc_start(s);
> +
> + i2c_slave_load(f, &s->i2c);
> + return 0;
> +}
> +
> +static int menelaus_iid = 0;
> +
> +i2c_slave *twl92230_init(i2c_bus *bus, qemu_irq irq)
> +{
> + struct menelaus_s *s = (struct menelaus_s *)
> + i2c_slave_init(bus, 0, sizeof(struct menelaus_s));
> +
> + s->i2c.event = menelaus_event;
> + s->i2c.recv = menelaus_rx;
> + s->i2c.send = menelaus_tx;
> +
> + /* TODO: use the qemu gettime functions */
> + s->rtc.gettime = localtime_r;
> +
> + s->irq = irq;
> + s->rtc.hz = qemu_new_timer(rt_clock, menelaus_rtc_hz, s);
> + s->in = qemu_allocate_irqs(menelaus_gpio_set, s, 3);
> + s->pwrbtn = qemu_allocate_irqs(menelaus_pwrbtn_set, s, 1)[0];
> +
> + menelaus_reset(&s->i2c);
> +
> + register_savevm("menelaus", menelaus_iid ++,
> + 0, menelaus_save, menelaus_load, s);
> +
> + return &s->i2c;
> +}
> +
> +qemu_irq *twl92230_gpio_in_get(i2c_slave *i2c)
> +{
> + struct menelaus_s *s = (struct menelaus_s *) i2c;
> +
> + return s->in;
> +}
> +
> +void twl92230_gpio_out_set(i2c_slave *i2c, int line, qemu_irq handler)
> +{
> + struct menelaus_s *s = (struct menelaus_s *) i2c;
> +
> + if (line >= 3 || line < 0) {
> + fprintf(stderr, "%s: No GPO line %i\n", __FUNCTION__, line);
> + exit(-1);
> + }
> + s->handler[line] = handler;
> +}
>
> Modified: trunk/vl.c
> ===================================================================
> --- trunk/vl.c 2008-04-14 21:28:11 UTC (rev 4214)
> +++ trunk/vl.c 2008-04-14 21:57:44 UTC (rev 4215)
> @@ -8051,6 +8051,7 @@
> qemu_register_machine(&borzoipda_machine);
> qemu_register_machine(&terrierpda_machine);
> qemu_register_machine(&palmte_machine);
> + qemu_register_machine(&n800_machine);
> qemu_register_machine(&lm3s811evb_machine);
> qemu_register_machine(&lm3s6965evb_machine);
> qemu_register_machine(&connex_machine);
>
>
>
>
>
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [Qemu-devel] Re: [4215] Nokia N800 machine support (ARM).
2008-04-17 20:59 ` consul
@ 2008-04-17 23:03 ` andrzej zaborowski
2008-04-18 3:43 ` Anderson Lizardo
0 siblings, 1 reply; 7+ messages in thread
From: andrzej zaborowski @ 2008-04-17 23:03 UTC (permalink / raw)
To: qemu-devel
On 17/04/2008, consul <void@aleksoft.net> wrote:
> I'm getting an error when executing
> qemu-system-arm -M n800 -kernel c:\test\zImage -m 130 ...
> mipid_reset: Display off
> omap_l4ta_write: Bad register 0x6800a078
These messages are only diagnostic and are happily ignored by qemu
(but there should be more of them). If you don't want them, redirect
stderr as with "2> /dev/null".
As metioned in http://lists.gnu.org/archive/html/qemu-devel/2008-04/msg00282.html,
for the moment you'll need the patch to make use of the OMAP2
emulation. After you apply the patch:
The firmware kernel is very quiet, so if it's unable to mount the
initfs, it'll hang at the blank screen. A kernel built from sources
will dump logs on the third serial port. To boot Linux you'll need to
provide the kernel and a flash image with at least two of the five
partitions present on it. The flash is supplied with "-mtdblock
filename" and the file should be of 276824064 bytes (256 MB of data +
OOB data at the end). You'll need to have the initfs and the rootfs
present in this image, and for Maemo also the "config" partition (not
my fault). Poky (pokylinux.org) boots fine with just the stock initfs
+ Poky rootfs.
To get the config partition from a device you simply need to do
# cat /dev/mtdblock1 > somefile
and copy that file over. Redistribution of such image should be
legal, but I don't know for sure (since it only contains some
settings).
To write the partition images to the flash image use:
$ dd if=config.image of=mtd.image bs=2048 conv=notrunc seek=64
$ dd if=initfs.jffs2 of=mtd.image bs=2048 conv=notrunc seek=1280
$ dd if=rootfs.jffs2 of=mtd.image bs=2048 conv=notrunc seek=2304
This will obviously leave the OOB data invalid, but the stock Nokia
kernel doesn't seem to care because of hardware ECC.
>
> zImage is the firmware kernel.
> On both Windows and Linux hosts I get the same error and the VM hangs.
>
> Am I missing something? What command line do you use?
$ arm-softmmu/qemu-system-arm -M n800 -m 130 -mtdblock mtdblock -sd
card -show-cursor -serial vc -serial vc -serial stdio -kernel
nokia/linux-h/arch/arm/boot/zImage -usb -s -snapshot
Regards,
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [Qemu-devel] Re: [4215] Nokia N800 machine support (ARM).
2008-04-17 23:03 ` andrzej zaborowski
@ 2008-04-18 3:43 ` Anderson Lizardo
2008-04-18 12:13 ` andrzej zaborowski
0 siblings, 1 reply; 7+ messages in thread
From: Anderson Lizardo @ 2008-04-18 3:43 UTC (permalink / raw)
To: qemu-devel
On Thu, Apr 17, 2008 at 7:03 PM, andrzej zaborowski <balrogg@gmail.com> wrote:
> The firmware kernel is very quiet, so if it's unable to mount the
> initfs, it'll hang at the blank screen. A kernel built from sources
> will dump logs on the third serial port. To boot Linux you'll need to
> provide the kernel and a flash image with at least two of the five
> partitions present on it. The flash is supplied with "-mtdblock
> filename" and the file should be of 276824064 bytes (256 MB of data +
> OOB data at the end). You'll need to have the initfs and the rootfs
> present in this image, and for Maemo also the "config" partition (not
> my fault). Poky (pokylinux.org) boots fine with just the stock initfs
> + Poky rootfs.
A nice feature would be to allow the host to communicate with nolo
running inside qemu (e.g. through a TCP server or something), then
modify the free 0xFFFF flasher (http://nopcode.org/0xFFFF/) to be able
to communicate with this server. In practice, this would allow to
"flash" the emulated device like we do for the real hardware.
Is that feasible?
Regards,
--
Anderson Lizardo
Instituto Nokia de Tecnologia (INdT)
Manaus - Brazil
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [Qemu-devel] Re: [4215] Nokia N800 machine support (ARM).
2008-04-18 3:43 ` Anderson Lizardo
@ 2008-04-18 12:13 ` andrzej zaborowski
0 siblings, 0 replies; 7+ messages in thread
From: andrzej zaborowski @ 2008-04-18 12:13 UTC (permalink / raw)
To: qemu-devel
Hi,
On 18/04/2008, Anderson Lizardo <anderson.lizardo@gmail.com> wrote:
> On Thu, Apr 17, 2008 at 7:03 PM, andrzej zaborowski <balrogg@gmail.com> wrote:
> > The firmware kernel is very quiet, so if it's unable to mount the
> > initfs, it'll hang at the blank screen. A kernel built from sources
> > will dump logs on the third serial port. To boot Linux you'll need to
> > provide the kernel and a flash image with at least two of the five
> > partitions present on it. The flash is supplied with "-mtdblock
> > filename" and the file should be of 276824064 bytes (256 MB of data +
> > OOB data at the end). You'll need to have the initfs and the rootfs
> > present in this image, and for Maemo also the "config" partition (not
> > my fault). Poky (pokylinux.org) boots fine with just the stock initfs
> > + Poky rootfs.
>
> A nice feature would be to allow the host to communicate with nolo
> running inside qemu (e.g. through a TCP server or something), then
> modify the free 0xFFFF flasher (http://nopcode.org/0xFFFF/) to be able
> to communicate with this server. In practice, this would allow to
> "flash" the emulated device like we do for the real hardware.
That should be possible, I think the easiest communication channel
would be serial - I didn't find the "cold-flashing" over serial option
in 0xFFFF but it can be added. However several things in 0xFFFF are
outdated since 770 (I'm using version 0.3.2), so it may take a bit of
work.
You can also boot-up a small rootfs that contains the images to flash,
and use "nandwrite" from mtd-utils to write the images like on the
real heardware.
Another possibility I found is using NOLO console interface. I don't
know if there is a way to access this console on the real hardware,
but in Qemu you can type commands into NOLO on the serial port and it
even has help messages and stuff, and also allows you to flash an
image from RAM like U-boot. Judging from no google hits for any of the
NOLO commands, I think it may be only possible in qemu :)
Flashing the emulated device like the real hardware would still assume
that the config partition is already present in flash and stays there
though. In addition to the config partition Nokia stores some
information about the unit in the OneNAND's One-Time-Programmable
area, fortunately this is not critical for booting Maemo.
I uploaded at http://folks.o-hand.com/andrew/qemu-n800/ a tiny script
that creates an empty mtdblock image for qemu and can also write the
jffs2 / config partitions from images. It can use dd (very fast) or
it can use qemu to boot up the zImage which contains nandwrite in an
initramfs, and will read the images to flash from an SD generated by
the script (this is slower but independent of the file format used by
qemu).
Regards
--
Please do not print this email unless absolutely necessary. Spread
environmental awareness.
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2008-04-18 12:13 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-04-14 21:57 [Qemu-devel] [4215] Nokia N800 machine support (ARM) Andrzej Zaborowski
2008-04-15 8:00 ` [Qemu-devel] " John R. Hogerhuis
2008-04-17 21:58 ` andrzej zaborowski
2008-04-17 20:59 ` consul
2008-04-17 23:03 ` andrzej zaborowski
2008-04-18 3:43 ` Anderson Lizardo
2008-04-18 12:13 ` andrzej zaborowski
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).