1 /* $NetBSD: eeprom.c,v 1.16 1997/04/28 21:51:49 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/obio.h> 57 #include <machine/eeprom.h> 58 #include <machine/machdep.h> 59 60 #define HZ 100 /* XXX */ 61 62 #ifndef OBIO_EEPROM_SIZE 63 #define OBIO_EEPROM_SIZE sizeof(struct eeprom) 64 #endif 65 66 static int ee_update(int off, int cnt); 67 68 static char *eeprom_va; 69 static char *ee_rambuf; 70 static int ee_busy, ee_want; 71 72 static int eeprom_match __P((struct device *, struct cfdata *, void *)); 73 static void eeprom_attach __P((struct device *, struct device *, void *)); 74 75 struct cfattach eeprom_ca = { 76 sizeof(struct device), eeprom_match, eeprom_attach 77 }; 78 79 struct cfdriver eeprom_cd = { 80 NULL, "eeprom", DV_DULL 81 }; 82 83 static int 84 eeprom_match(parent, cf, args) 85 struct device *parent; 86 struct cfdata *cf; 87 void *args; 88 { 89 struct confargs *ca = args; 90 91 /* This driver only supports one unit. */ 92 if (cf->cf_unit != 0) 93 return (0); 94 95 /* We use obio_mapin(), so require OBIO. */ 96 if (ca->ca_bustype != BUS_OBIO) 97 return (0); 98 99 if (bus_peek(ca->ca_bustype, ca->ca_paddr, 1) == -1) 100 return (0); 101 102 return (1); 103 } 104 105 static void 106 eeprom_attach(parent, self, args) 107 struct device *parent; 108 struct device *self; 109 void *args; 110 { 111 struct confargs *ca = args; 112 char *src, *dst, *lim; 113 114 printf("\n"); 115 116 eeprom_va = obio_mapin(ca->ca_paddr, OBIO_EEPROM_SIZE); 117 if (!eeprom_va) 118 panic("eeprom_attach"); 119 120 /* Keep a "soft" copy of the EEPROM to make access simpler. */ 121 ee_rambuf = malloc(sizeof(struct eeprom), M_DEVBUF, M_NOWAIT); 122 if (ee_rambuf == 0) 123 panic("eeprom_attach: malloc ee_rambuf"); 124 125 /* Do only byte access in the EEPROM. */ 126 src = eeprom_va; 127 dst = ee_rambuf; 128 lim = ee_rambuf + sizeof(struct eeprom); 129 do *dst++ = *src++; 130 while (dst < lim); 131 } 132 133 134 /* Take the lock. */ 135 static int 136 ee_take __P((void)) 137 { 138 int error = 0; 139 while (ee_busy) { 140 ee_want = 1; 141 error = tsleep(&ee_busy, PZERO | PCATCH, "eeprom", 0); 142 ee_want = 0; 143 if (error) /* interrupted */ 144 return error; 145 } 146 ee_busy = 1; 147 return error; 148 } 149 150 /* Give the lock. */ 151 static void 152 ee_give __P((void)) 153 { 154 ee_busy = 0; 155 if (ee_want) { 156 ee_want = 0; 157 wakeup(&ee_busy); 158 } 159 } 160 161 /* 162 * This is called by mem.c to handle /dev/eeprom 163 */ 164 int 165 eeprom_uio(struct uio *uio) 166 { 167 int cnt, error; 168 int off; /* NOT off_t */ 169 caddr_t va; 170 171 if (ee_rambuf == NULL) 172 return (ENXIO); 173 174 off = uio->uio_offset; 175 if ((off < 0) || (off > OBIO_EEPROM_SIZE)) 176 return (EFAULT); 177 178 cnt = min(uio->uio_resid, (OBIO_EEPROM_SIZE - off)); 179 if (cnt == 0) 180 return (0); /* EOF */ 181 182 va = ee_rambuf + off; 183 error = uiomove(va, (int)cnt, uio); 184 185 /* If we wrote the rambuf, update the H/W. */ 186 if (!error && (uio->uio_rw != UIO_READ)) { 187 error = ee_take(); 188 if (!error) 189 error = ee_update(off, cnt); 190 ee_give(); 191 } 192 193 return (error); 194 } 195 196 /* 197 * Update the EEPROM from the soft copy. 198 * Other than the attach function, this is 199 * the ONLY place we touch the EEPROM H/W. 200 */ 201 static int 202 ee_update(int off, int cnt) 203 { 204 volatile char *ep; 205 char *bp; 206 int errcnt; 207 208 if (eeprom_va == NULL) 209 return (ENXIO); 210 211 bp = ee_rambuf + off; 212 ep = eeprom_va + off; 213 errcnt = 0; 214 215 while (cnt > 0) { 216 /* 217 * DO NOT WRITE IT UNLESS WE HAVE TO because the 218 * EEPROM has a limited number of write cycles. 219 * After some number of writes it just fails! 220 */ 221 if (*ep != *bp) { 222 *ep = *bp; 223 /* 224 * We have written the EEPROM, so now we must 225 * sleep for at least 10 milliseconds while 226 * holding the lock to prevent all access to 227 * the EEPROM while it recovers. 228 */ 229 (void)tsleep(eeprom_va, PZERO-1, "eeprom", HZ/50); 230 } 231 /* Make sure the write worked. */ 232 if (*ep != *bp) 233 errcnt++; 234 ep++; 235 bp++; 236 cnt--; 237 } 238 return (errcnt ? EIO : 0); 239 } 240