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