提交 7e7c5e4c 编写于 作者: B balrog

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
上级 a5d7eb65
......@@ -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
......
此差异已折叠。
/*
* 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;
......
/*
* 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;
}
......@@ -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 */
......
......@@ -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
此差异已折叠。
......@@ -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);
......
/*
* 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;
}
/*
* 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;
}
此差异已折叠。
......@@ -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);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册