in hw/rtc/twl92230.c [401:702]
static void menelaus_write(void *opaque, uint8_t addr, uint8_t value)
{
MenelausState *s = (MenelausState *) opaque;
int line;
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_CTRL1:
case MENELAUS_SLEEP_CTRL2:
s->sleep[addr - MENELAUS_SLEEP_CTRL1] = value;
break;
case MENELAUS_DEVICE_OFF:
if (value & 1) {
menelaus_reset(I2C_SLAVE(s));
}
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) {
qemu_set_irq(s->out[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) {
qemu_set_irq(s->out[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",
__func__, value);
s->status |= 1 << 10; /* RTCERR */
menelaus_update(s);
}
s->rtc.sec_offset = qemu_timedate_diff(&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", __func__, addr);
#endif
break;
}
}