Annotation of tme/ic/mk48txx.c, revision 1.1

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: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.