/* 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 sun4 timer control bus cycle handler: */ int _tme_sun4_timer_cycle_control(void *_sun4, struct tme_bus_cycle *cycle_init) { struct tme_sun4 *sun4; unsigned int timer_i; struct tme_sun4_timer *timer; tme_uint32_t reg; tme_uint32_t value32; struct tme_bus_cycle cycle_resp; struct timeval now; struct timeval last_reset; tme_uint32_t counter_one; tme_uint32_t usecs; tme_uint32_t ticks; /* recover our sun4: */ sun4 = (struct tme_sun4 *) _sun4; /* this must be a full 32-bit register access: */ if ((cycle_init->tme_bus_cycle_address % sizeof(tme_uint32_t)) != 0 || cycle_init->tme_bus_cycle_size != sizeof(tme_uint32_t)) { abort(); } /* get the timer and register accessed: */ if (TME_SUN4_IS_SUN44C(sun4)) { timer_i = cycle_init->tme_bus_cycle_address / TME_SUN44C_TIMER_SIZ_REG; reg = cycle_init->tme_bus_cycle_address & TME_SUN4_32_TIMER_SIZ_COUNTER; } else { abort(); } timer = &sun4->tme_sun4_timers[timer_i]; /* lock our mutex: */ tme_mutex_lock(&sun4->tme_sun4_mutex); /* if this is a read: */ if (cycle_init->tme_bus_cycle_type == TME_BUS_CYCLE_READ) { /* dispatch on the register: */ switch (reg) { default: assert(FALSE); case TME_SUN4_32_TIMER_REG_COUNTER: /* update the timers: */ _tme_sun4_timer_update(timer, &now, &last_reset); /* get the time of the last reset: */ last_reset = timer->tme_sun4_timer_limit_next; if (last_reset.tv_usec < timer->tme_sun4_timer_period.tv_usec) { last_reset.tv_sec -= 1; last_reset.tv_usec += 1000000; } last_reset.tv_sec -= timer->tme_sun4_timer_period.tv_sec; last_reset.tv_usec -= timer->tme_sun4_timer_period.tv_usec; /* get the number of microseconds since the last reset: */ usecs = now.tv_sec - last_reset.tv_sec; usecs *= 1000000; usecs += (((tme_int32_t) now.tv_usec) - ((tme_int32_t) last_reset.tv_usec)); /* to keep things simpler, we always use the sun4m 500ns tick: */ counter_one = TME_SUN4_IS_SUN44C(sun4) ? 2 : 1; /* convert the number of microseconds until this timer resets again, to the 500ns tick counter value for the timer. NB that we account for the fact that timers count from [1..limit), and not [0..limit): */ ticks = (usecs * 2) + counter_one; TME_FIELD_MASK_DEPOSITU(timer->tme_sun4_timer_counter, TME_SUN4M_TIMER_MASK, ticks); /* read this timer's counter register: */ value32 = timer->tme_sun4_timer_counter; break; case TME_SUN4_32_TIMER_REG_LIMIT: /* read this timer's limit register: */ value32 = timer->tme_sun4_timer_limit; /* a read of the limit register is used to acknowledge an interrupt, which probably means clearing the limit bit on the counter register: */ timer->tme_sun4_timer_counter = 0; timer->tme_sun4_timer_limit &= ~TME_SUN4_32_TIMER_LIMIT; break; } tme_log(TME_SUN4_LOG_HANDLE(sun4), 2000, TME_OK, (TME_SUN4_LOG_HANDLE(sun4), _("timer #%d %s -> 0x%08x"), timer_i, (reg == TME_SUN4_32_TIMER_REG_COUNTER ? "counter" : "limit"), value32)); /* byteswap the register value: */ value32 = tme_htobe_u32(value32); } /* run the bus cycle: */ cycle_resp.tme_bus_cycle_buffer = (tme_uint8_t *) &value32; cycle_resp.tme_bus_cycle_buffer_increment = 1; cycle_resp.tme_bus_cycle_lane_routing = cycle_init->tme_bus_cycle_lane_routing; cycle_resp.tme_bus_cycle_address = 0; cycle_resp.tme_bus_cycle_type = (cycle_init->tme_bus_cycle_type ^ (TME_BUS_CYCLE_WRITE | TME_BUS_CYCLE_READ)); cycle_resp.tme_bus_cycle_port = TME_BUS_CYCLE_PORT(0, TME_BUS32_LOG2); tme_bus_cycle_xfer(cycle_init, &cycle_resp); /* if this is a write: */ if (cycle_init->tme_bus_cycle_type == TME_BUS_CYCLE_WRITE) { /* byteswap the register value: */ value32 = tme_htobe_u32(value32); tme_log(TME_SUN4_LOG_HANDLE(sun4), 2000, TME_OK, (TME_SUN4_LOG_HANDLE(sun4), _("timer #%d %s <- 0x%08x"), timer_i, (reg == TME_SUN4_32_TIMER_REG_COUNTER ? "counter" : "limit"), value32)); /* dispatch on the register: */ switch (reg) { default: assert(FALSE); case TME_SUN4_32_TIMER_REG_COUNTER: abort(); case TME_SUN4_32_TIMER_REG_LIMIT: /* write the timer's limit register: */ timer->tme_sun4_timer_limit = value32; /* reset this timer: */ _tme_sun4_timer_reset(timer); /* wake up the thread for this timer: */ tme_cond_notify(&timer->tme_sun4_timer_cond, FALSE); break; } } /* make any callouts: */ _tme_sun4_timer_callout(sun4); /* unlock the mutex: */ tme_mutex_unlock(&sun4->tme_sun4_mutex); /* no faults: */ return (TME_OK); }
/* the serial callout function. it must be called with the mutex locked: */ static void _tme_posix_serial_callout(struct tme_posix_serial *serial) { struct tme_serial_connection *conn_serial; unsigned int ctrl; tme_uint8_t buffer_input[1024]; unsigned int buffer_input_size; tme_serial_data_flags_t data_flags; int rc; int again; /* if this function is already running in another thread, return now. the other thread will do our work: */ if (serial->tme_posix_serial_callouts_running) { return; } /* callouts are now running: */ serial->tme_posix_serial_callouts_running = TRUE; /* loop running callouts until there is nothing to do: */ for (again = TRUE; again;) { again = FALSE; /* if we're connected: */ conn_serial = serial->tme_posix_serial_connection; if (conn_serial != NULL) { /* if we need to notify our connection of new ctrl bits: */ ctrl = serial->tme_posix_serial_ctrl_callout; if (ctrl != serial->tme_posix_serial_ctrl_callout_last) { /* unlock the mutex: */ tme_mutex_unlock(&serial->tme_posix_serial_mutex); /* try to do the notify: */ rc = (*conn_serial->tme_serial_connection_ctrl)(conn_serial, ctrl); /* lock the mutex: */ tme_mutex_lock(&serial->tme_posix_serial_mutex); /* if the notify was successful, note what we sent, and allow the outermost loop to run again: */ if (rc == TME_OK) { serial->tme_posix_serial_ctrl_callout_last = ctrl; again = TRUE; } } /* if our connection is readable, and our output buffer isn't full, read the connection: */ if ((serial->tme_posix_serial_ctrl_callin & TME_SERIAL_CTRL_OK_READ) && !tme_serial_buffer_is_full(&serial->tme_posix_serial_buffer_out)) { /* get the minimum of the free space in the output buffer and the size of our stack buffer: */ buffer_input_size = tme_serial_buffer_space_free(&serial->tme_posix_serial_buffer_out); buffer_input_size = TME_MIN(buffer_input_size, sizeof(buffer_input)); /* unlock the mutex: */ tme_mutex_unlock(&serial->tme_posix_serial_mutex); /* do the read: */ rc = (*conn_serial->tme_serial_connection_read) (conn_serial, buffer_input, buffer_input_size, &data_flags); /* lock the mutex: */ tme_mutex_lock(&serial->tme_posix_serial_mutex); /* if the read was successful, add what we got and notify the writer so that it may try to write: */ if (rc > 0) { (void) tme_serial_buffer_copyin(&serial->tme_posix_serial_buffer_out, buffer_input, rc, data_flags, TME_SERIAL_COPY_NORMAL); tme_cond_notify(&serial->tme_posix_serial_cond_writer, TRUE); /* allow the outermost loop to run again: */ again = TRUE; } /* otherwise, the read failed, and the convention dictates that we forget this connection was readable: */ else { serial->tme_posix_serial_ctrl_callin &= ~TME_SERIAL_CTRL_OK_READ; } } } } /* there are no more callouts to make: */ serial->tme_posix_serial_callouts_running = FALSE; }