mirror of https://gitee.com/openkylin/qemu.git
Nokia N800 machine support (ARM).
Also add various peripherals: two miscellaneous Nokia CBUS chips, EPSON S1D13745 LCD/TV remote-framebuffer controller, TWL92230 - standard OMAP2 power management companion chip on i2c. Generic OneNAND flash memory, TMP105 temperature sensor on i2c. git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@4215 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
parent
a5d7eb6534
commit
7e7c5e4c1b
3
Makefile
3
Makefile
|
@ -51,7 +51,8 @@ OBJS+=block.o
|
|||
|
||||
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
|
||||
|
|
|
@ -612,6 +612,7 @@ OBJS+= spitz.o ide.o serial.o nand.o ecc.o
|
|||
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
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
|
@ -81,6 +81,9 @@ extern QEMUMachine terrierpda_machine;
|
|||
/* palm.c */
|
||||
extern QEMUMachine palmte_machine;
|
||||
|
||||
/* nseries.c */
|
||||
extern QEMUMachine n800_machine;
|
||||
|
||||
/* gumstix.c */
|
||||
extern QEMUMachine connex_machine;
|
||||
extern QEMUMachine verdex_machine;
|
||||
|
|
|
@ -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;
|
||||
}
|
21
hw/devices.h
21
hw/devices.h
|
@ -31,4 +31,25 @@ void tsc210x_key_event(struct uwire_slave_s *chip, int key, int down);
|
|||
/* 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
|
||||
|
|
|
@ -34,6 +34,11 @@ uint8_t nand_getio(struct nand_flash_s *s);
|
|||
#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 */
|
||||
|
|
10
hw/i2c.h
10
hw/i2c.h
|
@ -71,4 +71,14 @@ uint32_t wm8750_adc_dat(void *opaque);
|
|||
/* 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
|
||||
|
|
|
@ -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,
|
||||
};
|
|
@ -3496,7 +3496,7 @@ struct omap_mpu_state_s *omap2420_mpu_init(unsigned long sdram_size,
|
|||
{
|
||||
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 @@ struct omap_mpu_state_s *omap2420_mpu_init(unsigned long sdram_size,
|
|||
|
||||
/* 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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
1
vl.c
1
vl.c
|
@ -8051,6 +8051,7 @@ static void register_machines(void)
|
|||
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);
|
||||
|
|
Loading…
Reference in New Issue