|
|
1.1 ! root 1: /* $Id: mk48txx.c,v 1.2 2006/11/26 16:01:49 fredette Exp $ */ ! 2: ! 3: /* ic/mk48txx.c - implementation of Mostek 48Txx emulation: */ ! 4: ! 5: /* ! 6: * Copyright (c) 2006 Matt Fredette ! 7: * All rights reserved. ! 8: * ! 9: * Redistribution and use in source and binary forms, with or without ! 10: * modification, are permitted provided that the following conditions ! 11: * are met: ! 12: * 1. Redistributions of source code must retain the above copyright ! 13: * notice, this list of conditions and the following disclaimer. ! 14: * 2. Redistributions in binary form must reproduce the above copyright ! 15: * notice, this list of conditions and the following disclaimer in the ! 16: * documentation and/or other materials provided with the distribution. ! 17: * 3. All advertising materials mentioning features or use of this software ! 18: * must display the following acknowledgement: ! 19: * This product includes software developed by Matt Fredette. ! 20: * 4. The name of the author may not be used to endorse or promote products ! 21: * derived from this software without specific prior written permission. ! 22: * ! 23: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR ! 24: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED ! 25: * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE ! 26: * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, ! 27: * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ! 28: * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ! 29: * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ! 30: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, ! 31: * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ! 32: * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ! 33: * POSSIBILITY OF SUCH DAMAGE. ! 34: */ ! 35: ! 36: #include <tme/common.h> ! 37: _TME_RCSID("$Id: mk48txx.c,v 1.2 2006/11/26 16:01:49 fredette Exp $"); ! 38: ! 39: /* includes: */ ! 40: #include <tme/generic/bus-device.h> ! 41: #include <tme/ic/mk48txx.h> ! 42: #include <tme/misc.h> ! 43: #include <time.h> ! 44: #include <sys/time.h> ! 45: ! 46: /* macros: */ ! 47: ! 48: /* the different parts: */ ! 49: #define TME_MK48TXX_PART_02 (02) ! 50: ! 51: /* register addresses: */ ! 52: #define TME_MK48TXX_REGS_COUNT (8) /* maximum number of registers */ ! 53: #define TME_MK48TXX_REG_YEAR (TME_MK48TXX_REGS_COUNT - 1) /* BCD year */ ! 54: #define TME_MK48TXX_REG_MON (TME_MK48TXX_REGS_COUNT - 2) /* BCD month */ ! 55: #define TME_MK48TXX_REG_DAY (TME_MK48TXX_REGS_COUNT - 3) /* BCD day in month */ ! 56: #define TME_MK48TXX_REG_WDAY (TME_MK48TXX_REGS_COUNT - 4) /* weekday */ ! 57: #define TME_MK48TXX_REG_HOUR (TME_MK48TXX_REGS_COUNT - 5) /* BCD hour */ ! 58: #define TME_MK48TXX_REG_MIN (TME_MK48TXX_REGS_COUNT - 6) /* BCD minutes */ ! 59: #define TME_MK48TXX_REG_SEC (TME_MK48TXX_REGS_COUNT - 7) /* BCD seconds */ ! 60: #define TME_MK48TXX_REG_CSR (TME_MK48TXX_REGS_COUNT - 8) /* control register */ ! 61: ! 62: /* the first register implemented by a part: */ ! 63: #define TME_MK48TXX_REG_FIRST(part) (TME_MK48TXX_REG_CSR) ! 64: ! 65: /* bits in the CSR: */ ! 66: #define TME_MK48TXX_CSR_WRITE TME_BIT(7) /* start writing */ ! 67: #define TME_MK48TXX_CSR_READ TME_BIT(6) /* start reading */ ! 68: ! 69: /* bits in the weekday register: */ ! 70: #define TME_MK48TXX_WDAY_FTEST TME_BIT(6) /* 512Hz frequency test */ ! 71: ! 72: /* bits in the seconds register: */ ! 73: #define TME_MK48TXX_SEC_STOP TME_BIT(7) /* all stop */ ! 74: ! 75: #define TME_MK48TXX_LOG_HANDLE(am) (&(am)->tme_mk48txx_element->tme_element_log_handle) ! 76: ! 77: /* structures: */ ! 78: struct tme_mk48txx { ! 79: ! 80: /* our simple bus device header: */ ! 81: struct tme_bus_device tme_mk48txx_device; ! 82: #define tme_mk48txx_element tme_mk48txx_device.tme_bus_device_element ! 83: ! 84: /* our socket: */ ! 85: struct tme_mk48txx_socket tme_mk48txx_socket; ! 86: #define tme_mk48txx_addr_shift tme_mk48txx_socket.tme_mk48txx_socket_addr_shift ! 87: #define tme_mk48txx_port_least_lane tme_mk48txx_socket.tme_mk48txx_socket_port_least_lane ! 88: #define tme_mk48txx_year_zero tme_mk48txx_socket.tme_mk48txx_socket_year_zero ! 89: ! 90: /* our mutex: */ ! 91: tme_mutex_t tme_mk48txx_mutex; ! 92: ! 93: /* the part emulated: */ ! 94: unsigned int tme_mk48txx_part; ! 95: ! 96: /* our timer condition: */ ! 97: tme_cond_t tme_mk48txx_cond_timer; ! 98: ! 99: /* it's easiest to just model the chip as a chunk of memory: */ ! 100: tme_uint8_t tme_mk48txx_regs[TME_MK48TXX_REGS_COUNT]; ! 101: ! 102: /* if nonzero, the internal time-of-day needs to be updated: */ ! 103: tme_uint8_t tme_mk48txx_tod_update; ! 104: }; ! 105: ! 106: /* the mk48txx bus router: */ ! 107: static const tme_bus_lane_t tme_mk48txx_router[TME_BUS_ROUTER_SIZE(TME_BUS8_LOG2)] = { ! 108: ! 109: /* [gen] initiator port size: 8 bits ! 110: [gen] initiator port least lane: 0: */ ! 111: /* D7-D0 */ TME_BUS_LANE_ROUTE(0), ! 112: }; ! 113: ! 114: /* these convert values to and from BCD: */ ! 115: static inline tme_uint8_t ! 116: _tme_mk48txx_bcd_out(unsigned int value) ! 117: { ! 118: return ((value % 10) ! 119: + ((value / 10) * 16)); ! 120: } ! 121: static inline unsigned int ! 122: _tme_mk48txx_bcd_in(tme_uint8_t value) ! 123: { ! 124: return ((value % 16) ! 125: + ((value / 16) * 10)); ! 126: } ! 127: ! 128: /* this resets an mk48txx: */ ! 129: static void ! 130: _tme_mk48txx_reset(struct tme_mk48txx *mk48txx) ! 131: { ! 132: ! 133: /* clear the CSR: */ ! 134: mk48txx->tme_mk48txx_regs[TME_MK48TXX_REG_CSR] = 0; ! 135: ! 136: /* start the clock running normally: */ ! 137: mk48txx->tme_mk48txx_regs[TME_MK48TXX_REG_WDAY] = 0; ! 138: mk48txx->tme_mk48txx_regs[TME_MK48TXX_SEC_STOP] = 0; ! 139: } ! 140: ! 141: /* the mk48txx bus cycle handler: */ ! 142: static int ! 143: _tme_mk48txx_bus_cycle(void *_mk48txx, struct tme_bus_cycle *cycle_init) ! 144: { ! 145: struct tme_mk48txx *mk48txx; ! 146: tme_bus_addr_t address, mk48txx_address_last; ! 147: tme_uint8_t buffer, value; ! 148: struct tme_bus_cycle cycle_resp; ! 149: unsigned int reg; ! 150: struct timeval now; ! 151: time_t _now; ! 152: struct tm *now_tm, now_tm_buffer; ! 153: ! 154: /* recover our data structure: */ ! 155: mk48txx = (struct tme_mk48txx *) _mk48txx; ! 156: ! 157: /* the requested cycle must be within range: */ ! 158: mk48txx_address_last = mk48txx->tme_mk48txx_device.tme_bus_device_address_last; ! 159: assert(cycle_init->tme_bus_cycle_address <= mk48txx_address_last); ! 160: assert(cycle_init->tme_bus_cycle_size <= (mk48txx_address_last - cycle_init->tme_bus_cycle_address) + 1); ! 161: ! 162: /* get the register being accessed: */ ! 163: address = cycle_init->tme_bus_cycle_address; ! 164: reg = (address >> mk48txx->tme_mk48txx_addr_shift) + TME_MK48TXX_REG_FIRST(mk48txx->tme_mk48txx_part); ! 165: ! 166: /* lock the mutex: */ ! 167: tme_mutex_lock(&mk48txx->tme_mk48txx_mutex); ! 168: ! 169: /* if the clock is not being read or written: */ ! 170: if ((mk48txx->tme_mk48txx_regs[TME_MK48TXX_REG_CSR] ! 171: & (TME_MK48TXX_CSR_READ ! 172: | TME_MK48TXX_CSR_WRITE)) == 0) { ! 173: ! 174: /* sample the time of day: */ ! 175: gettimeofday(&now, NULL); ! 176: _now = now.tv_sec; ! 177: now_tm = gmtime_r(&_now, &now_tm_buffer); ! 178: ! 179: /* put the time-of-day into the registers: */ ! 180: mk48txx->tme_mk48txx_regs[TME_MK48TXX_REG_HOUR] = _tme_mk48txx_bcd_out(now_tm->tm_hour); ! 181: mk48txx->tme_mk48txx_regs[TME_MK48TXX_REG_MIN] = _tme_mk48txx_bcd_out(now_tm->tm_min); ! 182: mk48txx->tme_mk48txx_regs[TME_MK48TXX_REG_SEC] = _tme_mk48txx_bcd_out(now_tm->tm_sec); ! 183: mk48txx->tme_mk48txx_regs[TME_MK48TXX_REG_MON] = _tme_mk48txx_bcd_out(now_tm->tm_mon + 1); ! 184: mk48txx->tme_mk48txx_regs[TME_MK48TXX_REG_DAY] = _tme_mk48txx_bcd_out(now_tm->tm_mday); ! 185: mk48txx->tme_mk48txx_regs[TME_MK48TXX_REG_YEAR] ! 186: = _tme_mk48txx_bcd_out((1900 + now_tm->tm_year) - mk48txx->tme_mk48txx_year_zero); ! 187: mk48txx->tme_mk48txx_regs[TME_MK48TXX_REG_WDAY] = now_tm->tm_wday; ! 188: } ! 189: ! 190: /* if this is a write: */ ! 191: if (cycle_init->tme_bus_cycle_type == TME_BUS_CYCLE_WRITE) { ! 192: ! 193: /* run the bus cycle: */ ! 194: cycle_resp.tme_bus_cycle_buffer = &buffer; ! 195: cycle_resp.tme_bus_cycle_lane_routing = tme_mk48txx_router; ! 196: cycle_resp.tme_bus_cycle_address = 0; ! 197: cycle_resp.tme_bus_cycle_buffer_increment = 1; ! 198: cycle_resp.tme_bus_cycle_type = TME_BUS_CYCLE_READ; ! 199: cycle_resp.tme_bus_cycle_size = sizeof(buffer); ! 200: cycle_resp.tme_bus_cycle_port = ! 201: TME_BUS_CYCLE_PORT(mk48txx->tme_mk48txx_port_least_lane, ! 202: TME_BUS8_LOG2); ! 203: tme_bus_cycle_xfer(cycle_init, &cycle_resp); ! 204: value = buffer; ! 205: ! 206: /* log this write: */ ! 207: tme_log(TME_MK48TXX_LOG_HANDLE(mk48txx), 100000, TME_OK, ! 208: (TME_MK48TXX_LOG_HANDLE(mk48txx), ! 209: "reg %d write %02x", ! 210: reg, value)); ! 211: ! 212: /* dispatch on the register: */ ! 213: switch (reg) { ! 214: ! 215: case TME_MK48TXX_REG_WDAY: ! 216: ! 217: /* flag that the time-of-day needs to be updated: */ ! 218: mk48txx->tme_mk48txx_tod_update = TRUE; ! 219: ! 220: /* update the register: */ ! 221: mk48txx->tme_mk48txx_regs[reg] = value; ! 222: break; ! 223: ! 224: case TME_MK48TXX_REG_HOUR: ! 225: case TME_MK48TXX_REG_MIN: ! 226: case TME_MK48TXX_REG_SEC: ! 227: case TME_MK48TXX_REG_MON: ! 228: case TME_MK48TXX_REG_DAY: ! 229: case TME_MK48TXX_REG_YEAR: ! 230: ! 231: /* flag that the time-of-day needs to be updated: */ ! 232: mk48txx->tme_mk48txx_tod_update = TRUE; ! 233: ! 234: /* update the register: */ ! 235: mk48txx->tme_mk48txx_regs[reg] = value; ! 236: break; ! 237: ! 238: case TME_MK48TXX_REG_CSR: ! 239: ! 240: /* update the CSR: */ ! 241: mk48txx->tme_mk48txx_regs[TME_MK48TXX_REG_CSR] = value; ! 242: ! 243: break; ! 244: ! 245: default: ! 246: /* ignore */ ! 247: break; ! 248: } ! 249: } ! 250: ! 251: /* otherwise, this is a read: */ ! 252: else { ! 253: assert(cycle_init->tme_bus_cycle_type == TME_BUS_CYCLE_READ); ! 254: ! 255: /* dispatch on the register: */ ! 256: switch (reg) { ! 257: ! 258: case TME_MK48TXX_REG_HOUR: ! 259: case TME_MK48TXX_REG_MIN: ! 260: case TME_MK48TXX_REG_SEC: ! 261: case TME_MK48TXX_REG_MON: ! 262: case TME_MK48TXX_REG_DAY: ! 263: case TME_MK48TXX_REG_YEAR: ! 264: case TME_MK48TXX_REG_WDAY: ! 265: ! 266: /* read the register: */ ! 267: value = mk48txx->tme_mk48txx_regs[reg]; ! 268: break; ! 269: ! 270: case TME_MK48TXX_REG_CSR: ! 271: ! 272: /* read the register: */ ! 273: value = mk48txx->tme_mk48txx_regs[reg]; ! 274: break; ! 275: ! 276: /* undefined registers return garbage when read: */ ! 277: default: ! 278: value = 0xff; ! 279: break; ! 280: } ! 281: ! 282: /* log this read: */ ! 283: tme_log(TME_MK48TXX_LOG_HANDLE(mk48txx), 100000, TME_OK, ! 284: (TME_MK48TXX_LOG_HANDLE(mk48txx), ! 285: "reg %d read %02x", ! 286: reg, value)); ! 287: ! 288: /* run the bus cycle: */ ! 289: buffer = value; ! 290: cycle_resp.tme_bus_cycle_buffer = &buffer; ! 291: cycle_resp.tme_bus_cycle_lane_routing = tme_mk48txx_router; ! 292: cycle_resp.tme_bus_cycle_address = 0; ! 293: cycle_resp.tme_bus_cycle_buffer_increment = 1; ! 294: cycle_resp.tme_bus_cycle_type = TME_BUS_CYCLE_WRITE; ! 295: cycle_resp.tme_bus_cycle_size = sizeof(buffer); ! 296: cycle_resp.tme_bus_cycle_port = ! 297: TME_BUS_CYCLE_PORT(mk48txx->tme_mk48txx_port_least_lane, ! 298: TME_BUS8_LOG2); ! 299: tme_bus_cycle_xfer(cycle_init, &cycle_resp); ! 300: } ! 301: ! 302: /* if the time-of-day registers have been updated, and the clock ! 303: is running: */ ! 304: if (mk48txx->tme_mk48txx_tod_update ! 305: && ((mk48txx->tme_mk48txx_regs[TME_MK48TXX_REG_CSR] ! 306: & (TME_MK48TXX_CSR_READ ! 307: | TME_MK48TXX_CSR_WRITE)) == 0)) { ! 308: ! 309: /* XXX update the host's time-of-day clock? */ ! 310: mk48txx->tme_mk48txx_tod_update = FALSE; ! 311: } ! 312: ! 313: /* unlock the mutex: */ ! 314: tme_mutex_unlock(&mk48txx->tme_mk48txx_mutex); ! 315: ! 316: /* no faults: */ ! 317: return (TME_OK); ! 318: } ! 319: ! 320: /* the mk48txx TLB filler: */ ! 321: static int ! 322: _tme_mk48txx_tlb_fill(void *_mk48txx, struct tme_bus_tlb *tlb, ! 323: tme_bus_addr_t address, unsigned int cycles) ! 324: { ! 325: struct tme_mk48txx *mk48txx; ! 326: tme_bus_addr_t mk48txx_address_last; ! 327: ! 328: /* recover our data structure: */ ! 329: mk48txx = (struct tme_mk48txx *) _mk48txx; ! 330: ! 331: /* the address must be within range: */ ! 332: mk48txx_address_last = mk48txx->tme_mk48txx_device.tme_bus_device_address_last; ! 333: assert(address <= mk48txx_address_last); ! 334: ! 335: /* initialize the TLB entry: */ ! 336: tme_bus_tlb_initialize(tlb); ! 337: ! 338: /* this TLB entry can cover the whole device: */ ! 339: tlb->tme_bus_tlb_addr_first = 0; ! 340: tlb->tme_bus_tlb_addr_last = mk48txx_address_last; ! 341: ! 342: /* allow reading and writing: */ ! 343: tlb->tme_bus_tlb_cycles_ok = TME_BUS_CYCLE_READ | TME_BUS_CYCLE_WRITE; ! 344: ! 345: /* our bus cycle handler: */ ! 346: tlb->tme_bus_tlb_cycle_private = mk48txx; ! 347: tlb->tme_bus_tlb_cycle = _tme_mk48txx_bus_cycle; ! 348: ! 349: return (TME_OK); ! 350: } ! 351: ! 352: /* the new mk48txx element function: */ ! 353: static int ! 354: _tme_mk48txx_new(struct tme_element *element, ! 355: const char * const *args, ! 356: const void *extra, ! 357: char **_output, ! 358: unsigned int part) ! 359: { ! 360: const struct tme_mk48txx_socket *socket; ! 361: struct tme_mk48txx *mk48txx; ! 362: struct tme_mk48txx_socket socket_real; ! 363: tme_bus_addr_t address_mask; ! 364: int arg_i; ! 365: int usage; ! 366: ! 367: /* dispatch on our socket version: */ ! 368: socket = (const struct tme_mk48txx_socket *) extra; ! 369: if (socket == NULL) { ! 370: tme_output_append_error(_output, _("need an ic socket")); ! 371: return (ENXIO); ! 372: } ! 373: switch (socket->tme_mk48txx_socket_version) { ! 374: case TME_MK48TXX_SOCKET_0: ! 375: socket_real = *socket; ! 376: break; ! 377: default: ! 378: tme_output_append_error(_output, _("socket type")); ! 379: return (EOPNOTSUPP); ! 380: } ! 381: ! 382: /* check our arguments: */ ! 383: usage = 0; ! 384: arg_i = 1; ! 385: for (;;) { ! 386: ! 387: if (0) { ! 388: } ! 389: ! 390: /* if we ran out of arguments: */ ! 391: else if (args[arg_i] == NULL) { ! 392: ! 393: break; ! 394: } ! 395: ! 396: /* otherwise this is a bad argument: */ ! 397: else { ! 398: tme_output_append_error(_output, ! 399: "%s %s, ", ! 400: args[arg_i], ! 401: _("unexpected")); ! 402: usage = TRUE; ! 403: break; ! 404: } ! 405: } ! 406: ! 407: if (usage) { ! 408: tme_output_append_error(_output, ! 409: "%s %s", ! 410: _("usage:"), ! 411: args[0]); ! 412: return (EINVAL); ! 413: } ! 414: ! 415: /* start the mk48txx structure: */ ! 416: mk48txx = tme_new0(struct tme_mk48txx, 1); ! 417: tme_mutex_init(&mk48txx->tme_mk48txx_mutex); ! 418: mk48txx->tme_mk48txx_part = part; ! 419: mk48txx->tme_mk48txx_socket = socket_real; ! 420: mk48txx->tme_mk48txx_element = element; ! 421: _tme_mk48txx_reset(mk48txx); ! 422: ! 423: /* figure our address mask, up to the nearest power of two: */ ! 424: address_mask = (TME_MK48TXX_REGS_COUNT - TME_MK48TXX_REG_FIRST(part)) << mk48txx->tme_mk48txx_addr_shift; ! 425: if (address_mask & (address_mask - 1)) { ! 426: for (; address_mask & (address_mask - 1); address_mask &= (address_mask - 1)); ! 427: address_mask <<= 1; ! 428: } ! 429: address_mask -= 1; ! 430: ! 431: /* initialize our simple bus device descriptor: */ ! 432: mk48txx->tme_mk48txx_device.tme_bus_device_tlb_fill = _tme_mk48txx_tlb_fill; ! 433: mk48txx->tme_mk48txx_device.tme_bus_device_address_last = address_mask; ! 434: ! 435: /* fill the element: */ ! 436: element->tme_element_private = mk48txx; ! 437: element->tme_element_connections_new = tme_bus_device_connections_new; ! 438: ! 439: return (TME_OK); ! 440: } ! 441: ! 442: TME_ELEMENT_X_NEW_DECL(tme_ic_,mk48txx,mk48t02) { ! 443: return (_tme_mk48txx_new(element, args, extra, _output, TME_MK48TXX_PART_02)); ! 444: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.