Home | History | Annotate | Line # | Download | only in ppc
      1 /*  This file is part of the program psim.
      2 
      3     Copyright (C) 1994-1996, Andrew Cagney <cagney (at) highland.com.au>
      4 
      5     This program is free software; you can redistribute it and/or modify
      6     it under the terms of the GNU General Public License as published by
      7     the Free Software Foundation; either version 3 of the License, or
      8     (at your option) any later version.
      9 
     10     This program is distributed in the hope that it will be useful,
     11     but WITHOUT ANY WARRANTY; without even the implied warranty of
     12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13     GNU General Public License for more details.
     14 
     15     You should have received a copy of the GNU General Public License
     16     along with this program; if not, see <http://www.gnu.org/licenses/>.
     17 
     18     */
     19 
     20 
     21 #ifndef _HW_NVRAM_C_
     22 #define _HW_NVRAM_C_
     23 
     24 #ifndef STATIC_INLINE_HW_NVRAM
     25 #define STATIC_INLINE_HW_NVRAM STATIC_INLINE
     26 #endif
     27 
     28 #include "device_table.h"
     29 
     30 #include <time.h>
     31 #include <string.h>
     32 
     33 /* DEVICE
     34 
     35 
     36    nvram - non-volatile memory with clock
     37 
     38 
     39    DESCRIPTION
     40 
     41 
     42    This device implements a small byte addressable non-volatile
     43    memory.  The top 8 bytes of this memory include a real-time clock.
     44 
     45 
     46    PROPERTIES
     47 
     48 
     49    reg = <address> <size> (required)
     50 
     51    Specify the address/size of this device within its parents address
     52    space.
     53 
     54 
     55    timezone = <integer> (optional)
     56 
     57    Adjustment to the hosts current GMT (in seconds) that should be
     58    applied when updating the NVRAM's clock.  If no timezone is
     59    specified, zero (GMT or UCT) is assumed.
     60 
     61 
     62    */
     63 
     64 typedef struct _hw_nvram_device {
     65   uint8_t *memory;
     66   unsigned sizeof_memory;
     67   time_t host_time;
     68   unsigned timezone;
     69   /* useful */
     70   unsigned addr_year;
     71   unsigned addr_month;
     72   unsigned addr_date;
     73   unsigned addr_day;
     74   unsigned addr_hour;
     75   unsigned addr_minutes;
     76   unsigned addr_seconds;
     77   unsigned addr_control;
     78 } hw_nvram_device;
     79 
     80 static void *
     81 hw_nvram_create(const char *name,
     82 		const device_unit *unit_address,
     83 		const char *args)
     84 {
     85   hw_nvram_device *nvram = ZALLOC(hw_nvram_device);
     86   return nvram;
     87 }
     88 
     89 typedef struct _hw_nvram_reg_spec {
     90   uint32_t base;
     91   uint32_t size;
     92 } hw_nvram_reg_spec;
     93 
     94 static void
     95 hw_nvram_init_address(device *me)
     96 {
     97   hw_nvram_device *nvram = (hw_nvram_device*)device_data(me);
     98 
     99   /* use the generic init code to attach this device to its parent bus */
    100   generic_device_init_address(me);
    101 
    102   /* find the first non zero reg property and use that as the device
    103      size */
    104   if (nvram->sizeof_memory == 0) {
    105     reg_property_spec reg;
    106     int reg_nr;
    107     for (reg_nr = 0;
    108 	 device_find_reg_array_property(me, "reg", reg_nr, &reg);
    109 	 reg_nr++) {
    110       unsigned attach_size;
    111       if (device_size_to_attach_size(device_parent(me),
    112 				     &reg.size, &attach_size,
    113 				     me)) {
    114 	nvram->sizeof_memory = attach_size;
    115 	break;
    116       }
    117     }
    118     if (nvram->sizeof_memory == 0)
    119       device_error(me, "reg property must contain a non-zero phys-addr:size tupple");
    120     if (nvram->sizeof_memory < 8)
    121       device_error(me, "NVRAM must be at least 8 bytes in size");
    122   }
    123 
    124   /* initialize the hw_nvram */
    125   if (nvram->memory == NULL) {
    126     nvram->memory = zalloc(nvram->sizeof_memory);
    127   }
    128   else
    129     memset(nvram->memory, 0, nvram->sizeof_memory);
    130 
    131   if (device_find_property(me, "timezone") == NULL)
    132     nvram->timezone = 0;
    133   else
    134     nvram->timezone = device_find_integer_property(me, "timezone");
    135 
    136   nvram->addr_year = nvram->sizeof_memory - 1;
    137   nvram->addr_month = nvram->sizeof_memory - 2;
    138   nvram->addr_date = nvram->sizeof_memory - 3;
    139   nvram->addr_day = nvram->sizeof_memory - 4;
    140   nvram->addr_hour = nvram->sizeof_memory - 5;
    141   nvram->addr_minutes = nvram->sizeof_memory - 6;
    142   nvram->addr_seconds = nvram->sizeof_memory - 7;
    143   nvram->addr_control = nvram->sizeof_memory - 8;
    144 
    145 }
    146 
    147 static int
    148 hw_nvram_bcd(int val)
    149 {
    150   val = val % 100;
    151   if (val < 0)
    152     val += 100;
    153   return ((val / 10) << 4) + (val % 10);
    154 }
    155 
    156 
    157 /* If reached an update interval and allowed, update the clock within
    158    the hw_nvram.  While this function could be implemented using events
    159    it isn't on the assumption that the HW_NVRAM will hardly ever be
    160    referenced and hence there is little need in keeping the clock
    161    continually up-to-date */
    162 
    163 static void
    164 hw_nvram_update_clock(hw_nvram_device *nvram,
    165 		      cpu *processor)
    166 {
    167   if (!(nvram->memory[nvram->addr_control] & 0xc0)) {
    168     time_t host_time = time(NULL);
    169     if (nvram->host_time != host_time) {
    170       time_t nvtime = host_time + nvram->timezone;
    171       struct tm *clock = gmtime(&nvtime);
    172       nvram->host_time = host_time;
    173       nvram->memory[nvram->addr_year] = hw_nvram_bcd(clock->tm_year);
    174       nvram->memory[nvram->addr_month] = hw_nvram_bcd(clock->tm_mon + 1);
    175       nvram->memory[nvram->addr_date] = hw_nvram_bcd(clock->tm_mday);
    176       nvram->memory[nvram->addr_day] = hw_nvram_bcd(clock->tm_wday + 1);
    177       nvram->memory[nvram->addr_hour] = hw_nvram_bcd(clock->tm_hour);
    178       nvram->memory[nvram->addr_minutes] = hw_nvram_bcd(clock->tm_min);
    179       nvram->memory[nvram->addr_seconds] = hw_nvram_bcd(clock->tm_sec);
    180     }
    181   }
    182 }
    183 
    184 static void
    185 hw_nvram_set_clock(hw_nvram_device *nvram, cpu *processor)
    186 {
    187   error ("fixme - how do I set the localtime\n");
    188 }
    189 
    190 static unsigned
    191 hw_nvram_io_read_buffer(device *me,
    192 			void *dest,
    193 			int space,
    194 			unsigned_word addr,
    195 			unsigned nr_bytes,
    196 			cpu *processor,
    197 			unsigned_word cia)
    198 {
    199   int i;
    200   hw_nvram_device *nvram = (hw_nvram_device*)device_data(me);
    201   for (i = 0; i < nr_bytes; i++) {
    202     unsigned address = (addr + i) % nvram->sizeof_memory;
    203     uint8_t data = nvram->memory[address];
    204     hw_nvram_update_clock(nvram, processor);
    205     ((uint8_t*)dest)[i] = data;
    206   }
    207   return nr_bytes;
    208 }
    209 
    210 static unsigned
    211 hw_nvram_io_write_buffer(device *me,
    212 			 const void *source,
    213 			 int space,
    214 			 unsigned_word addr,
    215 			 unsigned nr_bytes,
    216 			 cpu *processor,
    217 			 unsigned_word cia)
    218 {
    219   int i;
    220   hw_nvram_device *nvram = (hw_nvram_device*)device_data(me);
    221   for (i = 0; i < nr_bytes; i++) {
    222     unsigned address = (addr + i) % nvram->sizeof_memory;
    223     uint8_t data = ((uint8_t*)source)[i];
    224     if (address == nvram->addr_control
    225 	&& (data & 0x80) == 0
    226 	&& (nvram->memory[address] & 0x80) == 0x80)
    227       hw_nvram_set_clock(nvram, processor);
    228     else
    229       hw_nvram_update_clock(nvram, processor);
    230     nvram->memory[address] = data;
    231   }
    232   return nr_bytes;
    233 }
    234 
    235 static device_callbacks const hw_nvram_callbacks = {
    236   { hw_nvram_init_address, },
    237   { NULL, }, /* address */
    238   { hw_nvram_io_read_buffer, hw_nvram_io_write_buffer }, /* IO */
    239 };
    240 
    241 const device_descriptor hw_nvram_device_descriptor[] = {
    242   { "nvram", hw_nvram_create, &hw_nvram_callbacks },
    243   { NULL },
    244 };
    245 
    246 #endif /* _HW_NVRAM_C_ */
    247