1 /* $NetBSD: eeprom.c,v 1.24 2002/10/02 16:02:25 thorpej 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 #include <sys/kernel.h> 55 56 #include <machine/autoconf.h> 57 #include <machine/idprom.h> 58 #include <machine/eeprom.h> 59 60 #ifndef EEPROM_SIZE 61 #define EEPROM_SIZE 0x800 62 #endif 63 64 struct eeprom *eeprom_copy; /* soft copy. */ 65 66 static int ee_update(int off, int cnt); 67 68 static char *eeprom_va; /* mapping to actual device */ 69 static int ee_size; /* size of usable part. */ 70 71 static int ee_busy, ee_want; /* serialization */ 72 73 static int eeprom_match __P((struct device *, struct cfdata *, void *)); 74 static void eeprom_attach __P((struct device *, struct device *, void *)); 75 76 CFATTACH_DECL(eeprom, sizeof(struct device), 77 eeprom_match, eeprom_attach, NULL, NULL); 78 79 static int 80 eeprom_match(parent, cf, args) 81 struct device *parent; 82 struct cfdata *cf; 83 void *args; 84 { 85 struct confargs *ca = args; 86 87 /* This driver only supports one instance. */ 88 if (eeprom_va != NULL) 89 return (0); 90 91 if (bus_peek(ca->ca_bustype, ca->ca_paddr, 1) == -1) 92 return (0); 93 94 return (1); 95 } 96 97 static void 98 eeprom_attach(parent, self, args) 99 struct device *parent; 100 struct device *self; 101 void *args; 102 { 103 struct confargs *ca = args; 104 char *src, *dst, *lim; 105 106 printf("\n"); 107 #ifdef DIAGNOSTIC 108 if (sizeof(struct eeprom) != EEPROM_SIZE) 109 panic("eeprom struct wrong"); 110 #endif 111 112 ee_size = EEPROM_SIZE; 113 eeprom_va = bus_mapin(ca->ca_bustype, ca->ca_paddr, ee_size); 114 if (!eeprom_va) 115 panic("eeprom_attach"); 116 117 /* Keep a "soft" copy of the EEPROM to make access simpler. */ 118 eeprom_copy = malloc(ee_size, M_DEVBUF, M_NOWAIT); 119 if (eeprom_copy == 0) 120 panic("eeprom_attach: malloc eeprom_copy"); 121 122 /* 123 * On the 3/80, do not touch the last 40 bytes! 124 * Writes there are ignored, reads show zero. 125 * Reduce ee_size and clear the last part of the 126 * soft copy. Note: ee_update obeys ee_size. 127 */ 128 if (cpu_machine_id == SUN3X_MACH_80) 129 ee_size -= 40; 130 131 /* Do only byte access in the EEPROM. */ 132 src = eeprom_va; 133 dst = (char*) eeprom_copy; 134 lim = dst + ee_size; 135 136 do *dst++ = *src++; 137 while (dst < lim); 138 139 if (ee_size < EEPROM_SIZE) { 140 /* Clear out the last part. */ 141 memset(dst, 0, (EEPROM_SIZE - ee_size)); 142 } 143 } 144 145 146 /* Take the lock. */ 147 static int 148 ee_take __P((void)) 149 { 150 int error = 0; 151 while (ee_busy) { 152 ee_want = 1; 153 error = tsleep(&ee_busy, PZERO | PCATCH, "eeprom", 0); 154 ee_want = 0; 155 if (error) /* interrupted */ 156 return error; 157 } 158 ee_busy = 1; 159 return error; 160 } 161 162 /* Give the lock. */ 163 static void 164 ee_give __P((void)) 165 { 166 ee_busy = 0; 167 if (ee_want) { 168 ee_want = 0; 169 wakeup(&ee_busy); 170 } 171 } 172 173 /* 174 * This is called by mem.c to handle /dev/eeprom 175 */ 176 int 177 eeprom_uio(struct uio *uio) 178 { 179 int cnt, error; 180 int off; /* NOT off_t */ 181 caddr_t va; 182 183 if (eeprom_copy == NULL) 184 return (ENXIO); 185 186 off = uio->uio_offset; 187 if ((off < 0) || (off > EEPROM_SIZE)) 188 return (EFAULT); 189 190 cnt = min(uio->uio_resid, (EEPROM_SIZE - off)); 191 if (cnt == 0) 192 return (0); /* EOF */ 193 194 va = ((char*)eeprom_copy) + off; 195 error = uiomove(va, (int)cnt, uio); 196 197 /* If we wrote the rambuf, update the H/W. */ 198 if (!error && (uio->uio_rw != UIO_READ)) { 199 error = ee_take(); 200 if (!error) 201 error = ee_update(off, cnt); 202 ee_give(); 203 } 204 205 return (error); 206 } 207 208 /* 209 * Update the EEPROM from the soft copy. 210 * Other than the attach function, this is 211 * the ONLY place we touch the EEPROM H/W. 212 */ 213 static int 214 ee_update(int off, int cnt) 215 { 216 volatile char *ep; 217 char *bp; 218 int errcnt; 219 220 if (eeprom_va == NULL) 221 return (ENXIO); 222 223 /* 224 * This check is NOT redundant with the one in 225 * eeprom_uio above if we are on a 3/80 where 226 * ee_size < EEPROM_SIZE 227 */ 228 if (cnt > (ee_size - off)) 229 cnt = (ee_size - off); 230 231 bp = ((char*)eeprom_copy) + off; 232 ep = eeprom_va + off; 233 errcnt = 0; 234 235 while (cnt > 0) { 236 /* 237 * DO NOT WRITE IT UNLESS WE HAVE TO because the 238 * EEPROM has a limited number of write cycles. 239 * After some number of writes it just fails! 240 */ 241 if (*ep != *bp) { 242 *ep = *bp; 243 /* 244 * We have written the EEPROM, so now we must 245 * sleep for at least 10 milliseconds while 246 * holding the lock to prevent all access to 247 * the EEPROM while it recovers. 248 */ 249 (void)tsleep(eeprom_va, PZERO-1, "eeprom", hz/50); 250 } 251 /* Make sure the write worked. */ 252 if (*ep != *bp) 253 errcnt++; 254 ep++; 255 bp++; 256 cnt--; 257 } 258 return (errcnt ? EIO : 0); 259 } 260