1 /* $NetBSD: eeprom.c,v 1.18 1998/02/05 04:56:36 gwr Exp $ */ 2 3 /*- 4 * Copyright (c) 1996 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Gordon W. Ross. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39 /* 40 * Access functions for the EEPROM (Electrically Eraseable PROM) 41 * The main reason for the existence of this module is to 42 * handle the painful task of updating the EEPROM contents. 43 * After a write, it must not be touched for 10 milliseconds. 44 * (See the Sun-3 Architecture Manual sec. 5.9) 45 */ 46 47 #include <sys/param.h> 48 #include <sys/systm.h> 49 #include <sys/device.h> 50 #include <sys/conf.h> 51 #include <sys/buf.h> 52 #include <sys/malloc.h> 53 #include <sys/proc.h> 54 55 #include <machine/autoconf.h> 56 #include <machine/idprom.h> 57 #include <machine/eeprom.h> 58 59 #define HZ 100 /* XXX */ 60 61 #ifndef EEPROM_SIZE 62 #define EEPROM_SIZE 0x800 63 #endif 64 65 struct eeprom *eeprom_copy; /* soft copy. */ 66 67 static int ee_update(int off, int cnt); 68 69 static char *eeprom_va; /* mapping to actual device */ 70 static int ee_size; /* size of usable part. */ 71 72 static int ee_busy, ee_want; /* serialization */ 73 74 static int eeprom_match __P((struct device *, struct cfdata *, void *)); 75 static void eeprom_attach __P((struct device *, struct device *, void *)); 76 77 struct cfattach eeprom_ca = { 78 sizeof(struct device), eeprom_match, eeprom_attach 79 }; 80 81 static int 82 eeprom_match(parent, cf, args) 83 struct device *parent; 84 struct cfdata *cf; 85 void *args; 86 { 87 struct confargs *ca = args; 88 89 /* This driver only supports one unit. */ 90 if (cf->cf_unit != 0) 91 return (0); 92 93 if (bus_peek(ca->ca_bustype, ca->ca_paddr, 1) == -1) 94 return (0); 95 96 return (1); 97 } 98 99 static void 100 eeprom_attach(parent, self, args) 101 struct device *parent; 102 struct device *self; 103 void *args; 104 { 105 struct confargs *ca = args; 106 char *src, *dst, *lim; 107 108 printf("\n"); 109 #ifdef DIAGNOSTIC 110 if (sizeof(struct eeprom) != EEPROM_SIZE) 111 panic("eeprom struct wrong"); 112 #endif 113 114 ee_size = EEPROM_SIZE; 115 eeprom_va = bus_mapin(ca->ca_bustype, ca->ca_paddr, ee_size); 116 if (!eeprom_va) 117 panic("eeprom_attach"); 118 119 /* Keep a "soft" copy of the EEPROM to make access simpler. */ 120 eeprom_copy = malloc(ee_size, M_DEVBUF, M_NOWAIT); 121 if (eeprom_copy == 0) 122 panic("eeprom_attach: malloc eeprom_copy"); 123 124 /* 125 * On the 3/80, do not touch the last 40 bytes! 126 * Writes there are ignored, reads show zero. 127 * Reduce ee_size and clear the last part of the 128 * soft copy. Note: ee_update obeys ee_size. 129 */ 130 if (cpu_machine_id == SUN3X_MACH_80) 131 ee_size -= 40; 132 133 /* Do only byte access in the EEPROM. */ 134 src = eeprom_va; 135 dst = (char*) eeprom_copy; 136 lim = dst + ee_size; 137 138 do *dst++ = *src++; 139 while (dst < lim); 140 141 if (ee_size < EEPROM_SIZE) { 142 /* Clear out the last part. */ 143 bzero(dst, (EEPROM_SIZE - ee_size)); 144 } 145 } 146 147 148 /* Take the lock. */ 149 static int 150 ee_take __P((void)) 151 { 152 int error = 0; 153 while (ee_busy) { 154 ee_want = 1; 155 error = tsleep(&ee_busy, PZERO | PCATCH, "eeprom", 0); 156 ee_want = 0; 157 if (error) /* interrupted */ 158 return error; 159 } 160 ee_busy = 1; 161 return error; 162 } 163 164 /* Give the lock. */ 165 static void 166 ee_give __P((void)) 167 { 168 ee_busy = 0; 169 if (ee_want) { 170 ee_want = 0; 171 wakeup(&ee_busy); 172 } 173 } 174 175 /* 176 * This is called by mem.c to handle /dev/eeprom 177 */ 178 int 179 eeprom_uio(struct uio *uio) 180 { 181 int cnt, error; 182 int off; /* NOT off_t */ 183 caddr_t va; 184 185 if (eeprom_copy == NULL) 186 return (ENXIO); 187 188 off = uio->uio_offset; 189 if ((off < 0) || (off > EEPROM_SIZE)) 190 return (EFAULT); 191 192 cnt = min(uio->uio_resid, (EEPROM_SIZE - off)); 193 if (cnt == 0) 194 return (0); /* EOF */ 195 196 va = ((char*)eeprom_copy) + off; 197 error = uiomove(va, (int)cnt, uio); 198 199 /* If we wrote the rambuf, update the H/W. */ 200 if (!error && (uio->uio_rw != UIO_READ)) { 201 error = ee_take(); 202 if (!error) 203 error = ee_update(off, cnt); 204 ee_give(); 205 } 206 207 return (error); 208 } 209 210 /* 211 * Update the EEPROM from the soft copy. 212 * Other than the attach function, this is 213 * the ONLY place we touch the EEPROM H/W. 214 */ 215 static int 216 ee_update(int off, int cnt) 217 { 218 volatile char *ep; 219 char *bp; 220 int errcnt; 221 222 if (eeprom_va == NULL) 223 return (ENXIO); 224 225 /* 226 * This check is NOT redundant with the one in 227 * eeprom_uio above if we are on a 3/80 where 228 * ee_size < EEPROM_SIZE 229 */ 230 if (cnt > (ee_size - off)) 231 cnt = (ee_size - off); 232 233 bp = ((char*)eeprom_copy) + off; 234 ep = eeprom_va + off; 235 errcnt = 0; 236 237 while (cnt > 0) { 238 /* 239 * DO NOT WRITE IT UNLESS WE HAVE TO because the 240 * EEPROM has a limited number of write cycles. 241 * After some number of writes it just fails! 242 */ 243 if (*ep != *bp) { 244 *ep = *bp; 245 /* 246 * We have written the EEPROM, so now we must 247 * sleep for at least 10 milliseconds while 248 * holding the lock to prevent all access to 249 * the EEPROM while it recovers. 250 */ 251 (void)tsleep(eeprom_va, PZERO-1, "eeprom", HZ/50); 252 } 253 /* Make sure the write worked. */ 254 if (*ep != *bp) 255 errcnt++; 256 ep++; 257 bp++; 258 cnt--; 259 } 260 return (errcnt ? EIO : 0); 261 } 262