blob: 62cc005320959417dbb1bfa1890e452ae6e6cbe5 [file] [log] [blame]
/* $Id: isil7170.c,v 1.6 2010/06/05 14:37:27 fredette Exp $ */
/* ic/isil7170.c - implementation of Intersil 7170 emulation: */
/*
* Copyright (c) 2004 Matt Fredette
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Matt Fredette.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <tme/common.h>
_TME_RCSID("$Id: isil7170.c,v 1.6 2010/06/05 14:37:27 fredette Exp $");
/* includes: */
#include <tme/generic/bus-device.h>
#include <tme/ic/isil7170.h>
#include <tme/misc.h>
#include <time.h>
#include <sys/time.h>
/* macros: */
/* register addresses: */
#define TME_ISIL7170_REG_CSEC (0)
#define TME_ISIL7170_REG_HOUR (1)
#define TME_ISIL7170_REG_MIN (2)
#define TME_ISIL7170_REG_SEC (3)
#define TME_ISIL7170_REG_MON (4)
#define TME_ISIL7170_REG_DAY (5)
#define TME_ISIL7170_REG_YEAR (6)
#define TME_ISIL7170_REG_DOW (7)
#define TME_ISIL7170_REG_CMP_CSEC (8)
#define TME_ISIL7170_REG_CMP_HOUR (9)
#define TME_ISIL7170_REG_CMP_MIN (10)
#define TME_ISIL7170_REG_CMP_SEC (11)
#define TME_ISIL7170_REG_CMP_MON (12)
#define TME_ISIL7170_REG_CMP_DAY (13)
#define TME_ISIL7170_REG_CMP_YEAR (14)
#define TME_ISIL7170_REG_CMP_DOW (15)
#define TME_ISIL7170_REG_INT (16)
#define TME_ISIL7170_REG_CMD (17)
#define TME_ISIL7170_REGS_COUNT (32)
/* bits in the Interrupt register: */
#define TME_ISIL7170_INT_PENDING TME_BIT(7) /* interrupt pending */
#define TME_ISIL7170_INT_DAY TME_BIT(6) /* day periodic interrupt */
#define TME_ISIL7170_INT_HOUR TME_BIT(5) /* hour periodic interrupt */
#define TME_ISIL7170_INT_MIN TME_BIT(4) /* minute periodic interrupt */
#define TME_ISIL7170_INT_SEC TME_BIT(3) /* second periodic interrupt */
#define TME_ISIL7170_INT_TSEC TME_BIT(2) /* 1/10 second periodic interrupt */
#define TME_ISIL7170_INT_HSEC TME_BIT(1) /* 1/100 periodic second interrupt */
#define TME_ISIL7170_INT_ALARM TME_BIT(0) /* time match interrupt */
/* bits in the Command register: */
#define TME_ISIL7170_CMD_TEST TME_BIT(5) /* test mode */
#define TME_ISIL7170_CMD_INTENA TME_BIT(4) /* interrupt enable */
#define TME_ISIL7170_CMD_RUN TME_BIT(3) /* running */
#define TME_ISIL7170_CMD_FMT24 TME_BIT(2) /* 24-hour format (instead of 12-hour) */
#define TME_ISIL7170_CMD_FREQ_MASK (0x3) /* frequency mask */
#define TME_ISIL7170_CMD_FREQ_4M (0x3) /* frequency 4.194304MHz */
#define TME_ISIL7170_CMD_FREQ_2M (0x2) /* frequency 2.097152MHz */
#define TME_ISIL7170_CMD_FREQ_1M (0x1) /* frequency 1.048576MHz */
#define TME_ISIL7170_CMD_FREQ_32K (0x0) /* frequency 32.768KHz */
/* year zero in the chip corresponds to 1968: */
#define TME_ISIL7170_REG_YEAR_0 (1968)
/* define this to track interrupt rates, reporting once every N
seconds: */
#if 1
#define TME_ISIL7170_TRACK_INT_RATE (10)
#endif
#define TME_ISIL7170_LOG_HANDLE(am) (&(am)->tme_isil7170_element->tme_element_log_handle)
/* structures: */
struct tme_isil7170 {
/* our simple bus device header: */
struct tme_bus_device tme_isil7170_device;
#define tme_isil7170_element tme_isil7170_device.tme_bus_device_element
/* our socket: */
struct tme_isil7170_socket tme_isil7170_socket;
#define tme_isil7170_addr_shift tme_isil7170_socket.tme_isil7170_socket_addr_shift
#define tme_isil7170_port_least_lane tme_isil7170_socket.tme_isil7170_socket_port_least_lane
#define tme_isil7170_clock_basic tme_isil7170_socket.tme_isil7170_socket_clock_basic
#define tme_isil7170_int_signal tme_isil7170_socket.tme_isil7170_socket_int_signal
/* our mutex: */
tme_mutex_t tme_isil7170_mutex;
/* our timer condition: */
tme_cond_t tme_isil7170_cond_timer;
/* this is nonzero iff callouts are running: */
int tme_isil7170_callouts_running;
/* it's easiest to just model the chip as a chunk of memory: */
tme_uint8_t tme_isil7170_regs[TME_ISIL7170_REGS_COUNT];
/* the real-time durations, in microseconds, of a hundredth of a
second and a tenth of a second: */
unsigned long tme_isil7170_clock_hsec_usec;
unsigned long tme_isil7170_clock_tsec_usec;
/* if nonzero, the internal time-of-day needs to be updated: */
tme_uint8_t tme_isil7170_tod_update;
/* if nonzero, the interrupt the timer thread is sleeping for: */
tme_uint8_t tme_isil7170_timer_sleeping;
/* the interrupt mask: */
tme_uint8_t tme_isil7170_int_mask;
/* nonzero if the interrupt signal is asserted: */
int tme_isil7170_int_asserted;
/* any scaling factor: */
unsigned long tme_isil7170_clock_scale;
#ifdef TME_ISIL7170_TRACK_INT_RATE
/* the end time of this sample: */
struct timeval tme_isil7170_int_sample_time;
/* the number of distinct interrupts that have been delivered during
this sample: */
unsigned long tme_isil7170_int_sample;
#endif /* TME_ISIL7170_TRACK_INT_RATE */
};
/* the isil7170 bus router: */
static const tme_bus_lane_t tme_isil7170_router[TME_BUS_ROUTER_SIZE(TME_BUS8_LOG2)] = {
/* [gen] initiator port size: 8 bits
[gen] initiator port least lane: 0: */
/* D7-D0 */ TME_BUS_LANE_ROUTE(0),
};
/* this sets the frequency on an isil7170: */
static void
_tme_isil7170_freq(struct tme_isil7170 *isil7170)
{
tme_uint32_t clock_user, clock_basic;
unsigned long hsec_usec, tsec_usec;
/* get the user clock frequency: */
switch (isil7170->tme_isil7170_regs[TME_ISIL7170_REG_CMD] & TME_ISIL7170_CMD_FREQ_MASK) {
case TME_ISIL7170_CMD_FREQ_4M: clock_user = TME_ISIL7170_FREQ_4M; break;
case TME_ISIL7170_CMD_FREQ_2M: clock_user = TME_ISIL7170_FREQ_2M; break;
case TME_ISIL7170_CMD_FREQ_1M: clock_user = TME_ISIL7170_FREQ_1M; break;
default:
case TME_ISIL7170_CMD_FREQ_32K: clock_user = TME_ISIL7170_FREQ_32K; break;
}
/* get the hardware basic clock frequency: */
clock_basic = isil7170->tme_isil7170_clock_basic;
/* calculate the real-time durations, in microseconds, of a tenth
and hundredth of a second, given the actual basic clock into the
chip, and what the user claims is the basic clock into the chip.
we have to be careful to avoid overflow here: */
if (clock_user == clock_basic) {
hsec_usec = 10000;
tsec_usec = 100000;
}
else {
hsec_usec = ((1000 * clock_user) / (clock_basic / 10));
tsec_usec = ((1000 * clock_user) / (clock_basic / 100));
}
/* scale the result: */
hsec_usec *= isil7170->tme_isil7170_clock_scale;
tsec_usec *= isil7170->tme_isil7170_clock_scale;
isil7170->tme_isil7170_clock_hsec_usec = hsec_usec;
isil7170->tme_isil7170_clock_tsec_usec = tsec_usec;
}
/* this makes isil7170 callouts. it must be called with the mutex held: */
static void
_tme_isil7170_callout(struct tme_isil7170 *isil7170)
{
struct tme_bus_connection *conn_bus;
int again;
int int_asserted;
int rc;
/* if this function is already running in another thread, return
now. the other thread will do our work: */
if (isil7170->tme_isil7170_callouts_running) {
return;
}
/* callouts are now running: */
isil7170->tme_isil7170_callouts_running = TRUE;
/* get our bus connection: */
conn_bus = tme_memory_atomic_pointer_read(struct tme_bus_connection *,
isil7170->tme_isil7170_device.tme_bus_device_connection,
&isil7170->tme_isil7170_device.tme_bus_device_connection_rwlock);
/* loop forever: */
for (again = TRUE; again;) {
again = FALSE;
/* see if there are any pending, unmasked interrupts: */
int_asserted = (isil7170->tme_isil7170_regs[TME_ISIL7170_REG_INT]
& isil7170->tme_isil7170_int_mask);
/* update our interrupt-pending flag: */
if (int_asserted) {
isil7170->tme_isil7170_regs[TME_ISIL7170_REG_INT]
= ((isil7170->tme_isil7170_regs[TME_ISIL7170_REG_INT]
& ~TME_ISIL7170_INT_PENDING)
| (int_asserted
? TME_ISIL7170_INT_PENDING
: 0));
}
/* see if our interrupt signal should be asserted: */
int_asserted
= (int_asserted
&& (isil7170->tme_isil7170_regs[TME_ISIL7170_REG_CMD]
& TME_ISIL7170_CMD_INTENA));
/* if our interrupt signal has changed: */
if (!int_asserted != !isil7170->tme_isil7170_int_asserted) {
/* unlock our mutex: */
tme_mutex_unlock(&isil7170->tme_isil7170_mutex);
rc = (*conn_bus->tme_bus_signal)
(conn_bus,
isil7170->tme_isil7170_int_signal
| (int_asserted
? TME_BUS_SIGNAL_LEVEL_ASSERTED
: TME_BUS_SIGNAL_LEVEL_NEGATED));
/* lock our mutex: */
tme_mutex_lock(&isil7170->tme_isil7170_mutex);
/* if this call out succeeded, update the interrupt-asserted flag: */
if (rc == TME_OK) {
isil7170->tme_isil7170_int_asserted = int_asserted;
again = TRUE;
}
}
}
/* there are no more callouts to make: */
isil7170->tme_isil7170_callouts_running = FALSE;
}
/* this resets an isil7170: */
static void
_tme_isil7170_reset(struct tme_isil7170 *isil7170)
{
/* clear the interrupt mask: */
isil7170->tme_isil7170_int_mask = 0;
/* clear the command register: */
isil7170->tme_isil7170_regs[TME_ISIL7170_REG_CMD] = 0;
/* update the frequency: */
_tme_isil7170_freq(isil7170);
/* callout to update our interrupt signal: */
_tme_isil7170_callout(isil7170);
}
/* the isil7170 timer thread: */
static void
_tme_isil7170_th_timer(struct tme_isil7170 *isil7170)
{
tme_uint8_t int_mask;
tme_uint32_t sleep_usec;
#ifdef TME_ISIL7170_TRACK_INT_RATE
struct timeval now;
#endif /* TME_ISIL7170_TRACK_INT_RATE */
/* lock the mutex: */
tme_mutex_lock(&isil7170->tme_isil7170_mutex);
/* loop forever: */
for (;;) {
/* if we were sleeping: */
int_mask = isil7170->tme_isil7170_timer_sleeping;
isil7170->tme_isil7170_timer_sleeping = 0;
if (int_mask) {
#ifdef TME_ISIL7170_TRACK_INT_RATE
/* if no interrupt is pending, and this interrupt is not masked,
we will deliver another interrupt: */
if (!(isil7170->tme_isil7170_regs[TME_ISIL7170_REG_INT]
& TME_ISIL7170_INT_PENDING)
&& (int_mask
& isil7170->tme_isil7170_int_mask)) {
isil7170->tme_isil7170_int_sample++;
}
/* if the sample time has finished, report on the interrupt rate: */
gettimeofday(&now, NULL);
if (now.tv_sec > isil7170->tme_isil7170_int_sample_time.tv_sec
|| (now.tv_sec == isil7170->tme_isil7170_int_sample_time.tv_sec
&& now.tv_usec > isil7170->tme_isil7170_int_sample_time.tv_usec)) {
if (isil7170->tme_isil7170_int_sample > 0) {
tme_log(TME_ISIL7170_LOG_HANDLE(isil7170),
0, TME_OK,
(TME_ISIL7170_LOG_HANDLE(isil7170),
"timer interrupt rate: %ld/sec",
(isil7170->tme_isil7170_int_sample
/ (TME_ISIL7170_TRACK_INT_RATE
+ (unsigned long) (now.tv_sec
- isil7170->tme_isil7170_int_sample_time.tv_sec)))));
}
/* reset the sample: */
isil7170->tme_isil7170_int_sample_time.tv_sec = now.tv_sec + TME_ISIL7170_TRACK_INT_RATE;
isil7170->tme_isil7170_int_sample_time.tv_usec = now.tv_usec;
isil7170->tme_isil7170_int_sample = 0;
}
#endif /* TME_ISIL7170_TRACK_INT_RATE */
/* update the interrupt register: */
isil7170->tme_isil7170_regs[TME_ISIL7170_REG_INT] |= int_mask;
/* callout to update our interrupt signal: */
_tme_isil7170_callout(isil7170);
}
/* get the interrupt mask: */
int_mask = isil7170->tme_isil7170_int_mask;
/* if the 1/100 second periodic interrupt is unmasked: */
if (int_mask & TME_ISIL7170_INT_HSEC) {
int_mask = TME_ISIL7170_INT_HSEC;
sleep_usec = isil7170->tme_isil7170_clock_hsec_usec;
}
/* if the 1/10 second periodic interrupt is unmasked: */
else if (int_mask & TME_ISIL7170_INT_TSEC) {
int_mask = TME_ISIL7170_INT_TSEC;
sleep_usec = isil7170->tme_isil7170_clock_tsec_usec;
}
/* otherwise, all periodic interrupts are masked. wait until one
of them is not: */
else {
tme_cond_wait_yield(&isil7170->tme_isil7170_cond_timer,
&isil7170->tme_isil7170_mutex);
continue;
}
/* we are sleeping: */
isil7170->tme_isil7170_timer_sleeping = int_mask;
/* unlock our mutex: */
tme_mutex_unlock(&isil7170->tme_isil7170_mutex);
/* sleep: */
tme_thread_sleep_yield(0, sleep_usec);
/* lock our mutex: */
tme_mutex_unlock(&isil7170->tme_isil7170_mutex);
}
/* NOTREACHED */
}
/* the isil7170 bus cycle handler: */
static int
_tme_isil7170_bus_cycle(void *_isil7170, struct tme_bus_cycle *cycle_init)
{
struct tme_isil7170 *isil7170;
tme_bus_addr32_t address, isil7170_address_last;
tme_uint8_t buffer, value, value_old;
struct tme_bus_cycle cycle_resp;
unsigned int reg;
struct timeval now;
time_t _now;
struct tm *now_tm, now_tm_buffer;
/* recover our data structure: */
isil7170 = (struct tme_isil7170 *) _isil7170;
/* the requested cycle must be within range: */
isil7170_address_last = isil7170->tme_isil7170_device.tme_bus_device_address_last;
assert(cycle_init->tme_bus_cycle_address <= isil7170_address_last);
assert(cycle_init->tme_bus_cycle_size <= (isil7170_address_last - cycle_init->tme_bus_cycle_address) + 1);
/* get the register being accessed: */
address = cycle_init->tme_bus_cycle_address;
reg = address >> isil7170->tme_isil7170_addr_shift;
/* lock the mutex: */
tme_mutex_lock(&isil7170->tme_isil7170_mutex);
/* if the clock is running and this address is in the time-of-day
registers, or if this is a write to the command register: */
if (((isil7170->tme_isil7170_regs[TME_ISIL7170_REG_CMD] & TME_ISIL7170_CMD_RUN)
&& reg <= TME_ISIL7170_REG_DOW)
|| (cycle_init->tme_bus_cycle_type == TME_BUS_CYCLE_WRITE
&& reg == TME_ISIL7170_REG_CMD)) {
/* sample the time of day: */
gettimeofday(&now, NULL);
_now = now.tv_sec;
now_tm = gmtime_r(&_now, &now_tm_buffer);
/* put the time-of-day into the registers: */
isil7170->tme_isil7170_regs[TME_ISIL7170_REG_CSEC] = now.tv_usec / 10000;
isil7170->tme_isil7170_regs[TME_ISIL7170_REG_HOUR] = now_tm->tm_hour;
isil7170->tme_isil7170_regs[TME_ISIL7170_REG_MIN] = now_tm->tm_min;
isil7170->tme_isil7170_regs[TME_ISIL7170_REG_SEC] = now_tm->tm_sec;
isil7170->tme_isil7170_regs[TME_ISIL7170_REG_MON] = now_tm->tm_mon + 1;
isil7170->tme_isil7170_regs[TME_ISIL7170_REG_DAY] = now_tm->tm_mday;
isil7170->tme_isil7170_regs[TME_ISIL7170_REG_YEAR] = (1900 + now_tm->tm_year) - TME_ISIL7170_REG_YEAR_0;
isil7170->tme_isil7170_regs[TME_ISIL7170_REG_DOW] = now_tm->tm_wday;
}
/* if this is a write: */
if (cycle_init->tme_bus_cycle_type == TME_BUS_CYCLE_WRITE) {
/* run the bus cycle: */
cycle_resp.tme_bus_cycle_buffer = &buffer;
cycle_resp.tme_bus_cycle_lane_routing = tme_isil7170_router;
cycle_resp.tme_bus_cycle_address = 0;
cycle_resp.tme_bus_cycle_buffer_increment = 1;
cycle_resp.tme_bus_cycle_type = TME_BUS_CYCLE_READ;
cycle_resp.tme_bus_cycle_size = sizeof(buffer);
cycle_resp.tme_bus_cycle_port =
TME_BUS_CYCLE_PORT(isil7170->tme_isil7170_port_least_lane,
TME_BUS8_LOG2);
tme_bus_cycle_xfer(cycle_init, &cycle_resp);
value = buffer;
/* log this write: */
tme_log(TME_ISIL7170_LOG_HANDLE(isil7170), 100000, TME_OK,
(TME_ISIL7170_LOG_HANDLE(isil7170),
"reg %d write %02x",
reg, value));
/* dispatch on the register: */
switch (reg) {
case TME_ISIL7170_REG_CSEC:
case TME_ISIL7170_REG_HOUR:
case TME_ISIL7170_REG_MIN:
case TME_ISIL7170_REG_SEC:
case TME_ISIL7170_REG_MON:
case TME_ISIL7170_REG_DAY:
case TME_ISIL7170_REG_YEAR:
case TME_ISIL7170_REG_DOW:
/* flag that the time-of-day needs to be updated: */
isil7170->tme_isil7170_tod_update = TRUE;
/* FALLTHROUGH */
case TME_ISIL7170_REG_CMP_CSEC:
case TME_ISIL7170_REG_CMP_HOUR:
case TME_ISIL7170_REG_CMP_MIN:
case TME_ISIL7170_REG_CMP_SEC:
case TME_ISIL7170_REG_CMP_MON:
case TME_ISIL7170_REG_CMP_DAY:
case TME_ISIL7170_REG_CMP_YEAR:
case TME_ISIL7170_REG_CMP_DOW:
/* update the register: */
isil7170->tme_isil7170_regs[reg] = value;
break;
case TME_ISIL7170_REG_INT:
/* we don't support the alarm interrupt, or any of the daily,
hourly, etc., interrupts: */
if (value & (TME_ISIL7170_INT_DAY
| TME_ISIL7170_INT_HOUR
| TME_ISIL7170_INT_MIN
| TME_ISIL7170_INT_SEC
| TME_ISIL7170_INT_ALARM)) {
abort();
}
/* update the interrupt mask: */
isil7170->tme_isil7170_int_mask = (value & ~TME_ISIL7170_INT_PENDING);
/* callout to update our interrupt signal: */
_tme_isil7170_callout(isil7170);
/* notify the timer thread: */
tme_cond_notify(&isil7170->tme_isil7170_cond_timer, FALSE);
break;
case TME_ISIL7170_REG_CMD:
/* we don't support the test mode: */
if (value & TME_ISIL7170_CMD_TEST) {
abort();
}
/* update the command register: */
value_old = isil7170->tme_isil7170_regs[TME_ISIL7170_REG_CMD];
isil7170->tme_isil7170_regs[TME_ISIL7170_REG_CMD] = value;
/* if the frequency changed, update our periodic intervals: */
if ((value_old ^ value) & TME_ISIL7170_CMD_FREQ_MASK) {
_tme_isil7170_freq(isil7170);
}
/* if the interrupt enable changed, callout to update our
interrupt signal: */
if ((value_old ^ value) & TME_ISIL7170_CMD_INTENA) {
_tme_isil7170_callout(isil7170);
}
break;
default:
/* ignore */
break;
}
}
/* otherwise, this is a read: */
else {
assert(cycle_init->tme_bus_cycle_type == TME_BUS_CYCLE_READ);
/* dispatch on the register: */
switch (reg) {
case TME_ISIL7170_REG_CSEC:
case TME_ISIL7170_REG_HOUR:
case TME_ISIL7170_REG_MIN:
case TME_ISIL7170_REG_SEC:
case TME_ISIL7170_REG_MON:
case TME_ISIL7170_REG_DAY:
case TME_ISIL7170_REG_YEAR:
case TME_ISIL7170_REG_DOW:
case TME_ISIL7170_REG_CMP_CSEC:
case TME_ISIL7170_REG_CMP_HOUR:
case TME_ISIL7170_REG_CMP_MIN:
case TME_ISIL7170_REG_CMP_SEC:
case TME_ISIL7170_REG_CMP_MON:
case TME_ISIL7170_REG_CMP_DAY:
case TME_ISIL7170_REG_CMP_YEAR:
case TME_ISIL7170_REG_CMP_DOW:
/* read the register: */
value = isil7170->tme_isil7170_regs[reg];
break;
case TME_ISIL7170_REG_INT:
/* reading the Interrupt register clears the interrupt: */
value = isil7170->tme_isil7170_regs[TME_ISIL7170_REG_INT];
isil7170->tme_isil7170_regs[TME_ISIL7170_REG_INT] = 0;
/* callout to update our interrupt signal: */
_tme_isil7170_callout(isil7170);
break;
/* the Command register, and all undefined registers, return
garbage when read: */
case TME_ISIL7170_REG_CMD:
default:
value = 0xff;
break;
}
/* log this read: */
tme_log(TME_ISIL7170_LOG_HANDLE(isil7170), 100000, TME_OK,
(TME_ISIL7170_LOG_HANDLE(isil7170),
"reg %d read %02x",
reg, value));
/* run the bus cycle: */
buffer = value;
cycle_resp.tme_bus_cycle_buffer = &buffer;
cycle_resp.tme_bus_cycle_lane_routing = tme_isil7170_router;
cycle_resp.tme_bus_cycle_address = 0;
cycle_resp.tme_bus_cycle_buffer_increment = 1;
cycle_resp.tme_bus_cycle_type = TME_BUS_CYCLE_WRITE;
cycle_resp.tme_bus_cycle_size = sizeof(buffer);
cycle_resp.tme_bus_cycle_port =
TME_BUS_CYCLE_PORT(isil7170->tme_isil7170_port_least_lane,
TME_BUS8_LOG2);
tme_bus_cycle_xfer(cycle_init, &cycle_resp);
}
/* if the time-of-day registers have been updated, and the clock
is running: */
if (isil7170->tme_isil7170_tod_update
&& (isil7170->tme_isil7170_regs[TME_ISIL7170_REG_CMD]
& TME_ISIL7170_CMD_RUN)) {
/* XXX update the host's time-of-day clock? */
isil7170->tme_isil7170_tod_update = FALSE;
}
/* unlock the mutex: */
tme_mutex_unlock(&isil7170->tme_isil7170_mutex);
/* no faults: */
return (TME_OK);
}
/* the isil7170 TLB filler: */
static int
_tme_isil7170_tlb_fill(void *_isil7170, struct tme_bus_tlb *tlb,
tme_bus_addr_t address, unsigned int cycles)
{
struct tme_isil7170 *isil7170;
tme_bus_addr32_t isil7170_address_last;
/* recover our data structure: */
isil7170 = (struct tme_isil7170 *) _isil7170;
/* the address must be within range: */
isil7170_address_last = isil7170->tme_isil7170_device.tme_bus_device_address_last;
assert(address <= isil7170_address_last);
/* initialize the TLB entry: */
tme_bus_tlb_initialize(tlb);
/* this TLB entry can cover the whole device: */
tlb->tme_bus_tlb_addr_first = 0;
tlb->tme_bus_tlb_addr_last = isil7170_address_last;
/* allow reading and writing: */
tlb->tme_bus_tlb_cycles_ok = TME_BUS_CYCLE_READ | TME_BUS_CYCLE_WRITE;
/* our bus cycle handler: */
tlb->tme_bus_tlb_cycle_private = isil7170;
tlb->tme_bus_tlb_cycle = _tme_isil7170_bus_cycle;
return (TME_OK);
}
/* the new isil7170 element function: */
TME_ELEMENT_NEW_DECL(tme_ic_isil7170) {
const struct tme_isil7170_socket *socket;
struct tme_isil7170 *isil7170;
struct tme_isil7170_socket socket_real;
tme_bus_addr_t address_mask;
unsigned long scale;
int arg_i;
int usage;
/* dispatch on our socket version: */
socket = (const struct tme_isil7170_socket *) extra;
if (socket == NULL) {
tme_output_append_error(_output, _("need an ic socket"));
return (ENXIO);
}
switch (socket->tme_isil7170_socket_version) {
case TME_ISIL7170_SOCKET_0:
socket_real = *socket;
break;
default:
tme_output_append_error(_output, _("socket type"));
return (EOPNOTSUPP);
}
/* check our arguments: */
usage = 0;
scale = 1;
arg_i = 1;
for (;;) {
/* a scale factor: */
if (TME_ARG_IS(args[arg_i + 0], "scale")) {
scale = tme_misc_unumber_parse(args[arg_i + 1], 0);
if (scale == 0) {
usage = TRUE;
break;
}
arg_i += 2;
}
/* if we ran out of arguments: */
else if (args[arg_i] == NULL) {
break;
}
/* otherwise this is a bad argument: */
else {
tme_output_append_error(_output,
"%s %s, ",
args[arg_i],
_("unexpected"));
usage = TRUE;
break;
}
}
if (usage) {
tme_output_append_error(_output,
"%s %s [ scale %s ]",
_("usage:"),
args[0],
_("SCALE"));
return (EINVAL);
}
/* start the isil7170 structure: */
isil7170 = tme_new0(struct tme_isil7170, 1);
isil7170->tme_isil7170_socket = socket_real;
isil7170->tme_isil7170_element = element;
isil7170->tme_isil7170_clock_scale = scale;
_tme_isil7170_reset(isil7170);
/* figure our address mask, up to the nearest power of two: */
address_mask = TME_ISIL7170_REGS_COUNT << isil7170->tme_isil7170_addr_shift;
if (address_mask & (address_mask - 1)) {
for (; address_mask & (address_mask - 1); address_mask &= (address_mask - 1));
address_mask <<= 1;
}
address_mask -= 1;
/* initialize our simple bus device descriptor: */
isil7170->tme_isil7170_device.tme_bus_device_tlb_fill = _tme_isil7170_tlb_fill;
isil7170->tme_isil7170_device.tme_bus_device_address_last = address_mask;
/* start the timer thread: */
tme_mutex_init(&isil7170->tme_isil7170_mutex);
tme_cond_init(&isil7170->tme_isil7170_cond_reader);
tme_thread_create((tme_thread_t) _tme_isil7170_th_timer, isil7170);
/* fill the element: */
element->tme_element_private = isil7170;
element->tme_element_connections_new = tme_bus_device_connections_new;
return (TME_OK);
}