1*e83ce340SAdrian Chadd /*- 2*e83ce340SAdrian Chadd * Copyright (c) 2015 Landon Fuller <landon@landonf.org> 3*e83ce340SAdrian Chadd * All rights reserved. 4*e83ce340SAdrian Chadd * 5*e83ce340SAdrian Chadd * Redistribution and use in source and binary forms, with or without 6*e83ce340SAdrian Chadd * modification, are permitted provided that the following conditions 7*e83ce340SAdrian Chadd * are met: 8*e83ce340SAdrian Chadd * 1. Redistributions of source code must retain the above copyright 9*e83ce340SAdrian Chadd * notice, this list of conditions and the following disclaimer, 10*e83ce340SAdrian Chadd * without modification. 11*e83ce340SAdrian Chadd * 2. Redistributions in binary form must reproduce at minimum a disclaimer 12*e83ce340SAdrian Chadd * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any 13*e83ce340SAdrian Chadd * redistribution must be conditioned upon including a substantially 14*e83ce340SAdrian Chadd * similar Disclaimer requirement for further binary redistribution. 15*e83ce340SAdrian Chadd * 16*e83ce340SAdrian Chadd * NO WARRANTY 17*e83ce340SAdrian Chadd * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18*e83ce340SAdrian Chadd * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19*e83ce340SAdrian Chadd * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY 20*e83ce340SAdrian Chadd * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 21*e83ce340SAdrian Chadd * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, 22*e83ce340SAdrian Chadd * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23*e83ce340SAdrian Chadd * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24*e83ce340SAdrian Chadd * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 25*e83ce340SAdrian Chadd * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26*e83ce340SAdrian Chadd * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 27*e83ce340SAdrian Chadd * THE POSSIBILITY OF SUCH DAMAGES. 28*e83ce340SAdrian Chadd */ 29*e83ce340SAdrian Chadd 30*e83ce340SAdrian Chadd #include <sys/cdefs.h> 31*e83ce340SAdrian Chadd __FBSDID("$FreeBSD$"); 32*e83ce340SAdrian Chadd 33*e83ce340SAdrian Chadd #include <sys/param.h> 34*e83ce340SAdrian Chadd #include <sys/bus.h> 35*e83ce340SAdrian Chadd #include <sys/endian.h> 36*e83ce340SAdrian Chadd #include <sys/rman.h> 37*e83ce340SAdrian Chadd #include <sys/systm.h> 38*e83ce340SAdrian Chadd 39*e83ce340SAdrian Chadd #include <machine/bus.h> 40*e83ce340SAdrian Chadd #include <machine/resource.h> 41*e83ce340SAdrian Chadd 42*e83ce340SAdrian Chadd #include <dev/bhnd/bhndvar.h> 43*e83ce340SAdrian Chadd 44*e83ce340SAdrian Chadd #include "nvramvar.h" 45*e83ce340SAdrian Chadd 46*e83ce340SAdrian Chadd #include "bhnd_spromreg.h" 47*e83ce340SAdrian Chadd #include "bhnd_spromvar.h" 48*e83ce340SAdrian Chadd 49*e83ce340SAdrian Chadd /* 50*e83ce340SAdrian Chadd * BHND SPROM Parsing 51*e83ce340SAdrian Chadd * 52*e83ce340SAdrian Chadd * Provides identification and parsing of BHND SPROM data. 53*e83ce340SAdrian Chadd */ 54*e83ce340SAdrian Chadd 55*e83ce340SAdrian Chadd static int sprom_direct_read(struct bhnd_sprom *sc, size_t offset, 56*e83ce340SAdrian Chadd void *buf, size_t nbytes, uint8_t *crc); 57*e83ce340SAdrian Chadd static int sprom_extend_shadow(struct bhnd_sprom *sc, size_t image_size, 58*e83ce340SAdrian Chadd uint8_t *crc); 59*e83ce340SAdrian Chadd static int sprom_populate_shadow(struct bhnd_sprom *sc); 60*e83ce340SAdrian Chadd 61*e83ce340SAdrian Chadd static int sprom_var_defn(struct bhnd_sprom *sc, const char *name, 62*e83ce340SAdrian Chadd const struct bhnd_nvram_var **var, 63*e83ce340SAdrian Chadd const struct bhnd_sprom_var **sprom, size_t *size); 64*e83ce340SAdrian Chadd 65*e83ce340SAdrian Chadd /* SPROM revision is always located at the second-to-last byte */ 66*e83ce340SAdrian Chadd #define SPROM_REV(_sc) SPROM_READ_1((_sc), (_sc)->sp_size - 2) 67*e83ce340SAdrian Chadd 68*e83ce340SAdrian Chadd /* SPROM CRC is always located at the last byte */ 69*e83ce340SAdrian Chadd #define SPROM_CRC_OFF(_sc) SPROM_CRC_LEN(_sc) 70*e83ce340SAdrian Chadd 71*e83ce340SAdrian Chadd /* SPROM CRC covers all but the final CRC byte */ 72*e83ce340SAdrian Chadd #define SPROM_CRC_LEN(_sc) ((_sc)->sp_size - 1) 73*e83ce340SAdrian Chadd 74*e83ce340SAdrian Chadd /* SPROM shadow I/O (with byte-order translation) */ 75*e83ce340SAdrian Chadd #define SPROM_READ_1(_sc, _off) SPROM_READ_ENC_1(_sc, _off) 76*e83ce340SAdrian Chadd #define SPROM_READ_2(_sc, _off) le16toh(SPROM_READ_ENC_2(_sc, _off)) 77*e83ce340SAdrian Chadd #define SPROM_READ_4(_sc, _off) le32toh(SPROM_READ_ENC_4(_sc, _off)) 78*e83ce340SAdrian Chadd 79*e83ce340SAdrian Chadd #define SPROM_WRITE_1(_sc, _off, _v) SPROM_WRITE_ENC_1(_sc, _off, (_v)) 80*e83ce340SAdrian Chadd #define SPROM_WRITE_2(_sc, _off, _v) SPROM_WRITE_ENC_2(_sc, _off, \ 81*e83ce340SAdrian Chadd htole16(_v)) 82*e83ce340SAdrian Chadd #define SPROM_WRITE_4(_sc, _off, _v) SPROM_WRITE_ENC_4(_sc, _off, \ 83*e83ce340SAdrian Chadd htole32(_v)) 84*e83ce340SAdrian Chadd 85*e83ce340SAdrian Chadd /* SPROM shadow I/O (without byte-order translation) */ 86*e83ce340SAdrian Chadd #define SPROM_READ_ENC_1(_sc, _off) (*(uint8_t *)((_sc)->sp_shadow + _off)) 87*e83ce340SAdrian Chadd #define SPROM_READ_ENC_2(_sc, _off) (*(uint16_t *)((_sc)->sp_shadow + _off)) 88*e83ce340SAdrian Chadd #define SPROM_READ_ENC_4(_sc, _off) (*(uint32_t *)((_sc)->sp_shadow + _off)) 89*e83ce340SAdrian Chadd 90*e83ce340SAdrian Chadd #define SPROM_WRITE_ENC_1(_sc, _off, _v) \ 91*e83ce340SAdrian Chadd *((uint8_t *)((_sc)->sp_shadow + _off)) = (_v) 92*e83ce340SAdrian Chadd #define SPROM_WRITE_ENC_2(_sc, _off, _v) \ 93*e83ce340SAdrian Chadd *((uint16_t *)((_sc)->sp_shadow + _off)) = (_v) 94*e83ce340SAdrian Chadd #define SPROM_WRITE_ENC_4(_sc, _off, _v) \ 95*e83ce340SAdrian Chadd *((uint32_t *)((_sc)->sp_shadow + _off)) = (_v) 96*e83ce340SAdrian Chadd 97*e83ce340SAdrian Chadd /* Call @p _next macro with the C type, widened (signed or unsigned) C 98*e83ce340SAdrian Chadd * type, and width associated with @p _dtype */ 99*e83ce340SAdrian Chadd #define SPROM_SWITCH_TYPE(_dtype, _next, ...) \ 100*e83ce340SAdrian Chadd do { \ 101*e83ce340SAdrian Chadd switch (_dtype) { \ 102*e83ce340SAdrian Chadd case BHND_NVRAM_DT_UINT8: \ 103*e83ce340SAdrian Chadd _next (uint8_t, uint32_t, 1, \ 104*e83ce340SAdrian Chadd ## __VA_ARGS__); \ 105*e83ce340SAdrian Chadd break; \ 106*e83ce340SAdrian Chadd case BHND_NVRAM_DT_UINT16: \ 107*e83ce340SAdrian Chadd _next (uint16_t, uint32_t, 2, \ 108*e83ce340SAdrian Chadd ## __VA_ARGS__); \ 109*e83ce340SAdrian Chadd break; \ 110*e83ce340SAdrian Chadd case BHND_NVRAM_DT_UINT32: \ 111*e83ce340SAdrian Chadd _next (uint32_t, uint32_t, 4, \ 112*e83ce340SAdrian Chadd ## __VA_ARGS__); \ 113*e83ce340SAdrian Chadd break; \ 114*e83ce340SAdrian Chadd case BHND_NVRAM_DT_INT8: \ 115*e83ce340SAdrian Chadd _next (int8_t, int32_t, 1, \ 116*e83ce340SAdrian Chadd ## __VA_ARGS__); \ 117*e83ce340SAdrian Chadd break; \ 118*e83ce340SAdrian Chadd case BHND_NVRAM_DT_INT16: \ 119*e83ce340SAdrian Chadd _next (int16_t, int32_t, 2, \ 120*e83ce340SAdrian Chadd ## __VA_ARGS__); \ 121*e83ce340SAdrian Chadd break; \ 122*e83ce340SAdrian Chadd case BHND_NVRAM_DT_INT32: \ 123*e83ce340SAdrian Chadd _next (int32_t, int32_t, 4, \ 124*e83ce340SAdrian Chadd ## __VA_ARGS__); \ 125*e83ce340SAdrian Chadd break; \ 126*e83ce340SAdrian Chadd case BHND_NVRAM_DT_CHAR: \ 127*e83ce340SAdrian Chadd _next (uint8_t, uint32_t, 1, \ 128*e83ce340SAdrian Chadd ## __VA_ARGS__); \ 129*e83ce340SAdrian Chadd break; \ 130*e83ce340SAdrian Chadd } \ 131*e83ce340SAdrian Chadd } while (0) 132*e83ce340SAdrian Chadd 133*e83ce340SAdrian Chadd /* 134*e83ce340SAdrian Chadd * Table of supported SPROM image formats, sorted by image size, ascending. 135*e83ce340SAdrian Chadd */ 136*e83ce340SAdrian Chadd #define SPROM_FMT(_sz, _revmin, _revmax, _sig) \ 137*e83ce340SAdrian Chadd { SPROM_SZ_ ## _sz, _revmin, _revmax, \ 138*e83ce340SAdrian Chadd SPROM_SIG_ ## _sig ## _OFF, \ 139*e83ce340SAdrian Chadd SPROM_SIG_ ## _sig } 140*e83ce340SAdrian Chadd 141*e83ce340SAdrian Chadd static const struct sprom_fmt { 142*e83ce340SAdrian Chadd size_t size; 143*e83ce340SAdrian Chadd uint8_t rev_min; 144*e83ce340SAdrian Chadd uint8_t rev_max; 145*e83ce340SAdrian Chadd size_t sig_offset; 146*e83ce340SAdrian Chadd uint16_t sig_req; 147*e83ce340SAdrian Chadd } sprom_fmts[] = { 148*e83ce340SAdrian Chadd SPROM_FMT(R1_3, 1, 3, NONE), 149*e83ce340SAdrian Chadd SPROM_FMT(R4_8_9, 4, 4, R4), 150*e83ce340SAdrian Chadd SPROM_FMT(R4_8_9, 8, 9, R8_9), 151*e83ce340SAdrian Chadd SPROM_FMT(R10, 10, 10, R10), 152*e83ce340SAdrian Chadd SPROM_FMT(R11, 11, 11, R11) 153*e83ce340SAdrian Chadd }; 154*e83ce340SAdrian Chadd 155*e83ce340SAdrian Chadd /** 156*e83ce340SAdrian Chadd * Identify the SPROM format at @p offset within @p r, verify the CRC, 157*e83ce340SAdrian Chadd * and allocate a local shadow copy of the SPROM data. 158*e83ce340SAdrian Chadd * 159*e83ce340SAdrian Chadd * After successful initialization, @p r will not be accessed; any pin 160*e83ce340SAdrian Chadd * configuration required for SPROM access may be reset. 161*e83ce340SAdrian Chadd * 162*e83ce340SAdrian Chadd * @param[out] sprom On success, will be initialized with shadow of the SPROM 163*e83ce340SAdrian Chadd * data. 164*e83ce340SAdrian Chadd * @param r An active resource mapping the SPROM data. 165*e83ce340SAdrian Chadd * @param offset Offset of the SPROM data within @p resource. 166*e83ce340SAdrian Chadd */ 167*e83ce340SAdrian Chadd int 168*e83ce340SAdrian Chadd bhnd_sprom_init(struct bhnd_sprom *sprom, struct bhnd_resource *r, 169*e83ce340SAdrian Chadd bus_size_t offset) 170*e83ce340SAdrian Chadd { 171*e83ce340SAdrian Chadd bus_size_t res_size; 172*e83ce340SAdrian Chadd int error; 173*e83ce340SAdrian Chadd 174*e83ce340SAdrian Chadd sprom->dev = rman_get_device(r->res); 175*e83ce340SAdrian Chadd sprom->sp_res = r; 176*e83ce340SAdrian Chadd sprom->sp_res_off = offset; 177*e83ce340SAdrian Chadd 178*e83ce340SAdrian Chadd /* Determine maximum possible SPROM image size */ 179*e83ce340SAdrian Chadd res_size = rman_get_size(r->res); 180*e83ce340SAdrian Chadd if (offset >= res_size) 181*e83ce340SAdrian Chadd return (EINVAL); 182*e83ce340SAdrian Chadd 183*e83ce340SAdrian Chadd sprom->sp_size_max = MIN(res_size - offset, SPROM_SZ_MAX); 184*e83ce340SAdrian Chadd 185*e83ce340SAdrian Chadd /* Allocate and populate SPROM shadow */ 186*e83ce340SAdrian Chadd sprom->sp_size = 0; 187*e83ce340SAdrian Chadd sprom->sp_capacity = sprom->sp_size_max; 188*e83ce340SAdrian Chadd sprom->sp_shadow = malloc(sprom->sp_capacity, M_BHND, M_NOWAIT); 189*e83ce340SAdrian Chadd if (sprom->sp_shadow == NULL) 190*e83ce340SAdrian Chadd return (ENOMEM); 191*e83ce340SAdrian Chadd 192*e83ce340SAdrian Chadd /* Read and identify SPROM image */ 193*e83ce340SAdrian Chadd if ((error = sprom_populate_shadow(sprom))) 194*e83ce340SAdrian Chadd return (error); 195*e83ce340SAdrian Chadd 196*e83ce340SAdrian Chadd return (0); 197*e83ce340SAdrian Chadd } 198*e83ce340SAdrian Chadd 199*e83ce340SAdrian Chadd /** 200*e83ce340SAdrian Chadd * Release all resources held by @p sprom. 201*e83ce340SAdrian Chadd * 202*e83ce340SAdrian Chadd * @param sprom A SPROM instance previously initialized via bhnd_sprom_init(). 203*e83ce340SAdrian Chadd */ 204*e83ce340SAdrian Chadd void 205*e83ce340SAdrian Chadd bhnd_sprom_fini(struct bhnd_sprom *sprom) 206*e83ce340SAdrian Chadd { 207*e83ce340SAdrian Chadd free(sprom->sp_shadow, M_BHND); 208*e83ce340SAdrian Chadd } 209*e83ce340SAdrian Chadd 210*e83ce340SAdrian Chadd /* Perform a read using a SPROM offset descriptor, safely widening the 211*e83ce340SAdrian Chadd * result to its 32-bit representation before assigning it to @p _dest. */ 212*e83ce340SAdrian Chadd #define SPROM_GETVAR_READ(_type, _widen, _width, _sc, _off, _dest) \ 213*e83ce340SAdrian Chadd do { \ 214*e83ce340SAdrian Chadd _type _v = (_type)SPROM_READ_ ## _width(_sc, _off->offset); \ 215*e83ce340SAdrian Chadd if (_off->shift > 0) { \ 216*e83ce340SAdrian Chadd _v >>= _off->shift; \ 217*e83ce340SAdrian Chadd } else if (off->shift < 0) { \ 218*e83ce340SAdrian Chadd _v <<= -_off->shift; \ 219*e83ce340SAdrian Chadd } \ 220*e83ce340SAdrian Chadd _dest = ((uint32_t) (_widen) _v) & _off->mask; \ 221*e83ce340SAdrian Chadd } while(0) 222*e83ce340SAdrian Chadd 223*e83ce340SAdrian Chadd /* Emit a value read using a SPROM offset descriptor, narrowing the 224*e83ce340SAdrian Chadd * result output representation and, if necessary, OR'ing it with the 225*e83ce340SAdrian Chadd * previously read value from @p _buf. */ 226*e83ce340SAdrian Chadd #define SPROM_GETVAR_WRITE(_type, _widen, _width, _off, _src, _buf) \ 227*e83ce340SAdrian Chadd do { \ 228*e83ce340SAdrian Chadd _type _v = (_type) (_widen) _src; \ 229*e83ce340SAdrian Chadd if (_off->cont) \ 230*e83ce340SAdrian Chadd _v |= *((_type *)_buf); \ 231*e83ce340SAdrian Chadd *((_type *)_buf) = _v; \ 232*e83ce340SAdrian Chadd } while(0) 233*e83ce340SAdrian Chadd 234*e83ce340SAdrian Chadd /** 235*e83ce340SAdrian Chadd * Read a SPROM variable, performing conversion to host byte order. 236*e83ce340SAdrian Chadd * 237*e83ce340SAdrian Chadd * @param sc The SPROM parser state. 238*e83ce340SAdrian Chadd * @param name The SPROM variable name. 239*e83ce340SAdrian Chadd * @param[out] buf On success, the requested value will be written 240*e83ce340SAdrian Chadd * to this buffer. This argment may be NULL if 241*e83ce340SAdrian Chadd * the value is not desired. 242*e83ce340SAdrian Chadd * @param[in,out] len The capacity of @p buf. On success, will be set 243*e83ce340SAdrian Chadd * to the actual size of the requested value. 244*e83ce340SAdrian Chadd * 245*e83ce340SAdrian Chadd * @retval 0 success 246*e83ce340SAdrian Chadd * @retval ENOENT The requested variable was not found. 247*e83ce340SAdrian Chadd * @retval ENOMEM If @p buf is non-NULL and a buffer of @p len is too 248*e83ce340SAdrian Chadd * small to hold the requested value. 249*e83ce340SAdrian Chadd * @retval non-zero If reading @p name otherwise fails, a regular unix 250*e83ce340SAdrian Chadd * error code will be returned. 251*e83ce340SAdrian Chadd */ 252*e83ce340SAdrian Chadd int 253*e83ce340SAdrian Chadd bhnd_sprom_getvar(struct bhnd_sprom *sc, const char *name, void *buf, 254*e83ce340SAdrian Chadd size_t *len) 255*e83ce340SAdrian Chadd { 256*e83ce340SAdrian Chadd const struct bhnd_nvram_var *nv; 257*e83ce340SAdrian Chadd const struct bhnd_sprom_var *sv; 258*e83ce340SAdrian Chadd size_t all1_offs; 259*e83ce340SAdrian Chadd size_t req_size; 260*e83ce340SAdrian Chadd int error; 261*e83ce340SAdrian Chadd 262*e83ce340SAdrian Chadd if ((error = sprom_var_defn(sc, name, &nv, &sv, &req_size))) 263*e83ce340SAdrian Chadd return (error); 264*e83ce340SAdrian Chadd 265*e83ce340SAdrian Chadd /* Provide required size */ 266*e83ce340SAdrian Chadd if (buf == NULL) { 267*e83ce340SAdrian Chadd *len = req_size; 268*e83ce340SAdrian Chadd return (0); 269*e83ce340SAdrian Chadd } 270*e83ce340SAdrian Chadd 271*e83ce340SAdrian Chadd /* Check (and update) target buffer len */ 272*e83ce340SAdrian Chadd if (*len < req_size) 273*e83ce340SAdrian Chadd return (ENOMEM); 274*e83ce340SAdrian Chadd else 275*e83ce340SAdrian Chadd *len = req_size; 276*e83ce340SAdrian Chadd 277*e83ce340SAdrian Chadd /* Read data */ 278*e83ce340SAdrian Chadd all1_offs = 0; 279*e83ce340SAdrian Chadd for (size_t i = 0; i < sv->num_offsets; i++) { 280*e83ce340SAdrian Chadd const struct bhnd_sprom_offset *off; 281*e83ce340SAdrian Chadd uint32_t val; 282*e83ce340SAdrian Chadd 283*e83ce340SAdrian Chadd off = &sv->offsets[i]; 284*e83ce340SAdrian Chadd KASSERT(!off->cont || i > 0, ("cont marked on first offset")); 285*e83ce340SAdrian Chadd 286*e83ce340SAdrian Chadd /* If not a continuation, advance the output buffer */ 287*e83ce340SAdrian Chadd if (i > 0 && !off->cont) { 288*e83ce340SAdrian Chadd buf = ((uint8_t *)buf) + 289*e83ce340SAdrian Chadd bhnd_nvram_type_width(sv->offsets[i-1].type); 290*e83ce340SAdrian Chadd } 291*e83ce340SAdrian Chadd 292*e83ce340SAdrian Chadd /* Read the value, widening to a common uint32 293*e83ce340SAdrian Chadd * representation */ 294*e83ce340SAdrian Chadd SPROM_SWITCH_TYPE(off->type, SPROM_GETVAR_READ, sc, off, val); 295*e83ce340SAdrian Chadd 296*e83ce340SAdrian Chadd /* If IGNALL1, record whether value has all bits set. */ 297*e83ce340SAdrian Chadd if (nv->flags & BHND_NVRAM_VF_IGNALL1) { 298*e83ce340SAdrian Chadd uint32_t all1; 299*e83ce340SAdrian Chadd 300*e83ce340SAdrian Chadd all1 = off->mask; 301*e83ce340SAdrian Chadd if (off->shift > 0) 302*e83ce340SAdrian Chadd all1 >>= off->shift; 303*e83ce340SAdrian Chadd else if (off->shift < 0) 304*e83ce340SAdrian Chadd all1 <<= -off->shift; 305*e83ce340SAdrian Chadd 306*e83ce340SAdrian Chadd if ((val & all1) == all1) 307*e83ce340SAdrian Chadd all1_offs++; 308*e83ce340SAdrian Chadd } 309*e83ce340SAdrian Chadd 310*e83ce340SAdrian Chadd /* Write the value, narrowing to the appropriate output 311*e83ce340SAdrian Chadd * width. */ 312*e83ce340SAdrian Chadd SPROM_SWITCH_TYPE(nv->type, SPROM_GETVAR_WRITE, off, val, buf); 313*e83ce340SAdrian Chadd } 314*e83ce340SAdrian Chadd 315*e83ce340SAdrian Chadd /* Should value should be treated as uninitialized? */ 316*e83ce340SAdrian Chadd if (nv->flags & BHND_NVRAM_VF_IGNALL1 && all1_offs == sv->num_offsets) 317*e83ce340SAdrian Chadd return (ENOENT); 318*e83ce340SAdrian Chadd 319*e83ce340SAdrian Chadd return (0); 320*e83ce340SAdrian Chadd } 321*e83ce340SAdrian Chadd 322*e83ce340SAdrian Chadd /* Perform a read of a variable offset from _src, safely widening the result 323*e83ce340SAdrian Chadd * to its 32-bit representation before assigning it to @p 324*e83ce340SAdrian Chadd * _dest. */ 325*e83ce340SAdrian Chadd #define SPROM_SETVAR_READ(_type, _widen, _width, _off, _src, _dest) \ 326*e83ce340SAdrian Chadd do { \ 327*e83ce340SAdrian Chadd _type _v = *(const _type *)_src; \ 328*e83ce340SAdrian Chadd if (_off->shift > 0) { \ 329*e83ce340SAdrian Chadd _v <<= _off->shift; \ 330*e83ce340SAdrian Chadd } else if (off->shift < 0) { \ 331*e83ce340SAdrian Chadd _v >>= -_off->shift; \ 332*e83ce340SAdrian Chadd } \ 333*e83ce340SAdrian Chadd _dest = ((uint32_t) (_widen) _v) & _off->mask; \ 334*e83ce340SAdrian Chadd } while(0) 335*e83ce340SAdrian Chadd 336*e83ce340SAdrian Chadd 337*e83ce340SAdrian Chadd /* Emit a value read using a SPROM offset descriptor, narrowing the 338*e83ce340SAdrian Chadd * result output representation and, if necessary, OR'ing it with the 339*e83ce340SAdrian Chadd * previously read value from @p _buf. */ 340*e83ce340SAdrian Chadd #define SPROM_SETVAR_WRITE(_type, _widen, _width, _sc, _off, _src) \ 341*e83ce340SAdrian Chadd do { \ 342*e83ce340SAdrian Chadd _type _v = (_type) (_widen) _src; \ 343*e83ce340SAdrian Chadd if (_off->cont) \ 344*e83ce340SAdrian Chadd _v |= SPROM_READ_ ## _width(_sc, _off->offset); \ 345*e83ce340SAdrian Chadd SPROM_WRITE_ ## _width(_sc, _off->offset, _v); \ 346*e83ce340SAdrian Chadd } while(0) 347*e83ce340SAdrian Chadd 348*e83ce340SAdrian Chadd /** 349*e83ce340SAdrian Chadd * Set a local value for a SPROM variable, performing conversion to SPROM byte 350*e83ce340SAdrian Chadd * order. 351*e83ce340SAdrian Chadd * 352*e83ce340SAdrian Chadd * The new value will be written to the backing SPROM shadow. 353*e83ce340SAdrian Chadd * 354*e83ce340SAdrian Chadd * @param sc The SPROM parser state. 355*e83ce340SAdrian Chadd * @param name The SPROM variable name. 356*e83ce340SAdrian Chadd * @param[out] buf The new value. 357*e83ce340SAdrian Chadd * @param[in,out] len The size of @p buf. 358*e83ce340SAdrian Chadd * 359*e83ce340SAdrian Chadd * @retval 0 success 360*e83ce340SAdrian Chadd * @retval ENOENT The requested variable was not found. 361*e83ce340SAdrian Chadd * @retval EINVAL If @p len does not match the expected variable size. 362*e83ce340SAdrian Chadd */ 363*e83ce340SAdrian Chadd int 364*e83ce340SAdrian Chadd bhnd_sprom_setvar(struct bhnd_sprom *sc, const char *name, const void *buf, 365*e83ce340SAdrian Chadd size_t len) 366*e83ce340SAdrian Chadd { 367*e83ce340SAdrian Chadd const struct bhnd_nvram_var *nv; 368*e83ce340SAdrian Chadd const struct bhnd_sprom_var *sv; 369*e83ce340SAdrian Chadd size_t req_size; 370*e83ce340SAdrian Chadd int error; 371*e83ce340SAdrian Chadd uint8_t crc; 372*e83ce340SAdrian Chadd 373*e83ce340SAdrian Chadd if ((error = sprom_var_defn(sc, name, &nv, &sv, &req_size))) 374*e83ce340SAdrian Chadd return (error); 375*e83ce340SAdrian Chadd 376*e83ce340SAdrian Chadd /* Provide required size */ 377*e83ce340SAdrian Chadd if (len != req_size) 378*e83ce340SAdrian Chadd return (EINVAL); 379*e83ce340SAdrian Chadd 380*e83ce340SAdrian Chadd /* Write data */ 381*e83ce340SAdrian Chadd for (size_t i = 0; i < sv->num_offsets; i++) { 382*e83ce340SAdrian Chadd const struct bhnd_sprom_offset *off; 383*e83ce340SAdrian Chadd uint32_t val; 384*e83ce340SAdrian Chadd 385*e83ce340SAdrian Chadd off = &sv->offsets[i]; 386*e83ce340SAdrian Chadd KASSERT(!off->cont || i > 0, ("cont marked on first offset")); 387*e83ce340SAdrian Chadd 388*e83ce340SAdrian Chadd /* If not a continuation, advance the input pointer */ 389*e83ce340SAdrian Chadd if (i > 0 && !off->cont) { 390*e83ce340SAdrian Chadd buf = ((const uint8_t *)buf) + 391*e83ce340SAdrian Chadd bhnd_nvram_type_width(sv->offsets[i-1].type); 392*e83ce340SAdrian Chadd } 393*e83ce340SAdrian Chadd 394*e83ce340SAdrian Chadd /* Read the value, widening to a common uint32 395*e83ce340SAdrian Chadd * representation */ 396*e83ce340SAdrian Chadd SPROM_SWITCH_TYPE(nv->type, SPROM_SETVAR_READ, off, buf, val); 397*e83ce340SAdrian Chadd 398*e83ce340SAdrian Chadd /* Write the value, narrowing to the appropriate output 399*e83ce340SAdrian Chadd * width. */ 400*e83ce340SAdrian Chadd SPROM_SWITCH_TYPE(off->type, SPROM_SETVAR_WRITE, sc, off, val); 401*e83ce340SAdrian Chadd } 402*e83ce340SAdrian Chadd 403*e83ce340SAdrian Chadd /* Update CRC */ 404*e83ce340SAdrian Chadd crc = ~bhnd_nvram_crc8(sc->sp_shadow, SPROM_CRC_LEN(sc), 405*e83ce340SAdrian Chadd BHND_NVRAM_CRC8_INITIAL); 406*e83ce340SAdrian Chadd SPROM_WRITE_1(sc, SPROM_CRC_OFF(sc), crc); 407*e83ce340SAdrian Chadd 408*e83ce340SAdrian Chadd return (0); 409*e83ce340SAdrian Chadd } 410*e83ce340SAdrian Chadd 411*e83ce340SAdrian Chadd /* Read and identify the SPROM image by incrementally performing 412*e83ce340SAdrian Chadd * read + CRC of all supported image formats */ 413*e83ce340SAdrian Chadd static int 414*e83ce340SAdrian Chadd sprom_populate_shadow(struct bhnd_sprom *sc) 415*e83ce340SAdrian Chadd { 416*e83ce340SAdrian Chadd const struct sprom_fmt *fmt; 417*e83ce340SAdrian Chadd int error; 418*e83ce340SAdrian Chadd uint16_t sig; 419*e83ce340SAdrian Chadd uint8_t srom_rev; 420*e83ce340SAdrian Chadd uint8_t crc; 421*e83ce340SAdrian Chadd 422*e83ce340SAdrian Chadd crc = BHND_NVRAM_CRC8_INITIAL; 423*e83ce340SAdrian Chadd 424*e83ce340SAdrian Chadd /* Identify the SPROM revision (and populate the SPROM shadow) */ 425*e83ce340SAdrian Chadd for (size_t i = 0; i < nitems(sprom_fmts); i++) { 426*e83ce340SAdrian Chadd fmt = &sprom_fmts[i]; 427*e83ce340SAdrian Chadd 428*e83ce340SAdrian Chadd /* Read image data and check CRC */ 429*e83ce340SAdrian Chadd if ((error = sprom_extend_shadow(sc, fmt->size, &crc))) 430*e83ce340SAdrian Chadd return (error); 431*e83ce340SAdrian Chadd 432*e83ce340SAdrian Chadd /* Skip on invalid CRC */ 433*e83ce340SAdrian Chadd if (crc != BHND_NVRAM_CRC8_VALID) 434*e83ce340SAdrian Chadd continue; 435*e83ce340SAdrian Chadd 436*e83ce340SAdrian Chadd /* Fetch SROM revision */ 437*e83ce340SAdrian Chadd srom_rev = SPROM_REV(sc); 438*e83ce340SAdrian Chadd 439*e83ce340SAdrian Chadd /* Early sromrev 1 devices (specifically some BCM440x enet 440*e83ce340SAdrian Chadd * cards) are reported to have been incorrectly programmed 441*e83ce340SAdrian Chadd * with a revision of 0x10. */ 442*e83ce340SAdrian Chadd if (fmt->size == SPROM_SZ_R1_3 && srom_rev == 0x10) 443*e83ce340SAdrian Chadd srom_rev = 0x1; 444*e83ce340SAdrian Chadd 445*e83ce340SAdrian Chadd /* Verify revision range */ 446*e83ce340SAdrian Chadd if (srom_rev < fmt->rev_min || srom_rev > fmt->rev_max) 447*e83ce340SAdrian Chadd continue; 448*e83ce340SAdrian Chadd 449*e83ce340SAdrian Chadd /* Verify signature (if any) */ 450*e83ce340SAdrian Chadd sig = SPROM_SIG_NONE; 451*e83ce340SAdrian Chadd if (fmt->sig_offset != SPROM_SIG_NONE_OFF) 452*e83ce340SAdrian Chadd sig = SPROM_READ_2(sc, fmt->sig_offset); 453*e83ce340SAdrian Chadd 454*e83ce340SAdrian Chadd if (sig != fmt->sig_req) { 455*e83ce340SAdrian Chadd device_printf(sc->dev, 456*e83ce340SAdrian Chadd "invalid sprom %hhu signature: 0x%hx " 457*e83ce340SAdrian Chadd "(expected 0x%hx)\n", 458*e83ce340SAdrian Chadd srom_rev, sig, fmt->sig_req); 459*e83ce340SAdrian Chadd return (EINVAL); 460*e83ce340SAdrian Chadd } 461*e83ce340SAdrian Chadd 462*e83ce340SAdrian Chadd /* Identified */ 463*e83ce340SAdrian Chadd sc->sp_rev = srom_rev; 464*e83ce340SAdrian Chadd return (0); 465*e83ce340SAdrian Chadd } 466*e83ce340SAdrian Chadd 467*e83ce340SAdrian Chadd /* identification failed */ 468*e83ce340SAdrian Chadd device_printf(sc->dev, "unrecognized sprom format\n"); 469*e83ce340SAdrian Chadd return (EINVAL); 470*e83ce340SAdrian Chadd } 471*e83ce340SAdrian Chadd 472*e83ce340SAdrian Chadd /* 473*e83ce340SAdrian Chadd * Extend the shadowed SPROM buffer to image_size, reading any required 474*e83ce340SAdrian Chadd * data from the backing SPROM resource and updating the CRC. 475*e83ce340SAdrian Chadd */ 476*e83ce340SAdrian Chadd static int 477*e83ce340SAdrian Chadd sprom_extend_shadow(struct bhnd_sprom *sc, size_t image_size, 478*e83ce340SAdrian Chadd uint8_t *crc) 479*e83ce340SAdrian Chadd { 480*e83ce340SAdrian Chadd int error; 481*e83ce340SAdrian Chadd 482*e83ce340SAdrian Chadd KASSERT(image_size >= sc->sp_size, (("shadow truncation unsupported"))); 483*e83ce340SAdrian Chadd 484*e83ce340SAdrian Chadd /* Verify the request fits within our shadow buffer */ 485*e83ce340SAdrian Chadd if (image_size > sc->sp_capacity) 486*e83ce340SAdrian Chadd return (ENOSPC); 487*e83ce340SAdrian Chadd 488*e83ce340SAdrian Chadd /* Skip no-op requests */ 489*e83ce340SAdrian Chadd if (sc->sp_size == image_size) 490*e83ce340SAdrian Chadd return (0); 491*e83ce340SAdrian Chadd 492*e83ce340SAdrian Chadd /* Populate the extended range */ 493*e83ce340SAdrian Chadd error = sprom_direct_read(sc, sc->sp_size, sc->sp_shadow + sc->sp_size, 494*e83ce340SAdrian Chadd image_size - sc->sp_size, crc); 495*e83ce340SAdrian Chadd if (error) 496*e83ce340SAdrian Chadd return (error); 497*e83ce340SAdrian Chadd 498*e83ce340SAdrian Chadd sc->sp_size = image_size; 499*e83ce340SAdrian Chadd return (0); 500*e83ce340SAdrian Chadd } 501*e83ce340SAdrian Chadd 502*e83ce340SAdrian Chadd /** 503*e83ce340SAdrian Chadd * Read nbytes at the given offset from the backing SPROM resource, and 504*e83ce340SAdrian Chadd * update the CRC. 505*e83ce340SAdrian Chadd */ 506*e83ce340SAdrian Chadd static int 507*e83ce340SAdrian Chadd sprom_direct_read(struct bhnd_sprom *sc, size_t offset, void *buf, 508*e83ce340SAdrian Chadd size_t nbytes, uint8_t *crc) 509*e83ce340SAdrian Chadd { 510*e83ce340SAdrian Chadd bus_size_t res_offset; 511*e83ce340SAdrian Chadd size_t nread; 512*e83ce340SAdrian Chadd uint16_t *p; 513*e83ce340SAdrian Chadd 514*e83ce340SAdrian Chadd KASSERT(nbytes % sizeof(uint16_t) == 0, ("unaligned sprom size")); 515*e83ce340SAdrian Chadd KASSERT(offset % sizeof(uint16_t) == 0, ("unaligned sprom offset")); 516*e83ce340SAdrian Chadd 517*e83ce340SAdrian Chadd /* Check for read overrun */ 518*e83ce340SAdrian Chadd if (offset >= sc->sp_size_max || sc->sp_size_max - offset < nbytes) { 519*e83ce340SAdrian Chadd device_printf(sc->dev, "requested SPROM read would overrun\n"); 520*e83ce340SAdrian Chadd return (EINVAL); 521*e83ce340SAdrian Chadd } 522*e83ce340SAdrian Chadd 523*e83ce340SAdrian Chadd p = (uint16_t *)buf; 524*e83ce340SAdrian Chadd res_offset = sc->sp_res_off + offset; 525*e83ce340SAdrian Chadd 526*e83ce340SAdrian Chadd /* Perform read */ 527*e83ce340SAdrian Chadd for (nread = 0; nread < nbytes; nread += 2) { 528*e83ce340SAdrian Chadd *p = bhnd_bus_read_stream_2(sc->sp_res, res_offset+nread); 529*e83ce340SAdrian Chadd *crc = bhnd_nvram_crc8(p, sizeof(*p), *crc); 530*e83ce340SAdrian Chadd p++; 531*e83ce340SAdrian Chadd }; 532*e83ce340SAdrian Chadd 533*e83ce340SAdrian Chadd return (0); 534*e83ce340SAdrian Chadd } 535*e83ce340SAdrian Chadd 536*e83ce340SAdrian Chadd 537*e83ce340SAdrian Chadd /** 538*e83ce340SAdrian Chadd * Locate the variable and SPROM revision-specific definitions 539*e83ce340SAdrian Chadd * for variable with @p name. 540*e83ce340SAdrian Chadd */ 541*e83ce340SAdrian Chadd static int 542*e83ce340SAdrian Chadd sprom_var_defn(struct bhnd_sprom *sc, const char *name, 543*e83ce340SAdrian Chadd const struct bhnd_nvram_var **var, 544*e83ce340SAdrian Chadd const struct bhnd_sprom_var **sprom, 545*e83ce340SAdrian Chadd size_t *size) 546*e83ce340SAdrian Chadd { 547*e83ce340SAdrian Chadd /* Find variable definition */ 548*e83ce340SAdrian Chadd *var = bhnd_nvram_var_defn(name); 549*e83ce340SAdrian Chadd if (*var == NULL) 550*e83ce340SAdrian Chadd return (ENOENT); 551*e83ce340SAdrian Chadd 552*e83ce340SAdrian Chadd /* Find revision-specific SPROM definition */ 553*e83ce340SAdrian Chadd for (size_t i = 0; i < (*var)->num_sp_descs; i++) { 554*e83ce340SAdrian Chadd const struct bhnd_sprom_var *sp = &(*var)->sprom_descs[i]; 555*e83ce340SAdrian Chadd 556*e83ce340SAdrian Chadd if (sc->sp_rev < sp->compat.first) 557*e83ce340SAdrian Chadd continue; 558*e83ce340SAdrian Chadd 559*e83ce340SAdrian Chadd if (sc->sp_rev > sp->compat.last) 560*e83ce340SAdrian Chadd continue; 561*e83ce340SAdrian Chadd 562*e83ce340SAdrian Chadd /* Found */ 563*e83ce340SAdrian Chadd *sprom = sp; 564*e83ce340SAdrian Chadd 565*e83ce340SAdrian Chadd /* Calculate size in bytes */ 566*e83ce340SAdrian Chadd *size = bhnd_nvram_type_width((*var)->type) * sp->num_offsets; 567*e83ce340SAdrian Chadd return (0); 568*e83ce340SAdrian Chadd } 569*e83ce340SAdrian Chadd 570*e83ce340SAdrian Chadd /* Not supported by this SPROM revision */ 571*e83ce340SAdrian Chadd return (ENOENT); 572*e83ce340SAdrian Chadd }