Split spdmem driver into a bus attachment and a common back-end

probe and ROM decode, to follow similar changes made in OpenBSD:

        http://article.gmane.org/gmane.os.openbsd.cvs/94948
        http://article.gmane.org/gmane.os.openbsd.cvs/94956

There exists at least one alternative attachment (for SGI IP35
systems; see http://article.gmane.org/gmane.os.openbsd.cvs/94947),
and there could be more in the future.

Thanks to Christoph Egger for pointing out the OpenBSD activity.
This commit is contained in:
pgoyette 2010-03-24 00:31:41 +00:00
parent 8183d39c9c
commit d41a3cef46
6 changed files with 160 additions and 80 deletions

View File

@ -1,4 +1,4 @@
# $NetBSD: files,v 1.983 2010/03/15 20:35:19 christos Exp $
# $NetBSD: files,v 1.984 2010/03/24 00:31:41 pgoyette Exp $
# @(#)files.newconf 7.5 (Berkeley) 5/10/93
version 20090313
@ -328,6 +328,13 @@ attach video at videobus
device lm: sysmon_envsys
file dev/ic/nslm7x.c lm needs-flag
# JEDEC standard SPD EPROM
#
# (included here so files.i2c can define an attachment)
device spdmem
file dev/ic/spdmem.c spdmem
# I2C device support
include "dev/i2c/files.i2c"

View File

@ -1,4 +1,4 @@
# $NetBSD: files.i2c,v 1.26 2010/02/22 03:50:56 pgoyette Exp $
# $NetBSD: files.i2c,v 1.27 2010/03/24 00:31:41 pgoyette Exp $
defflag opt_i2cbus.h I2C_SCAN
define i2cbus { }
@ -102,9 +102,8 @@ attach xbseeprom at iic
file dev/i2c/xbseeprom.c xbseeprom
# Memory Serial Presence Detect
device spdmem
attach spdmem at iic
file dev/i2c/spdmem.c spdmem
attach spdmem at iic with spdmem_iic
file dev/i2c/spdmem_i2c.c spdmem_iic
# Memory Temp Sensor
device sdtemp: sysmon_envsys

122
sys/dev/i2c/spdmem_i2c.c Normal file
View File

@ -0,0 +1,122 @@
/* $NetBSD: spdmem_i2c.c,v 1.1 2010/03/24 00:31:41 pgoyette Exp $ */
/*
* Copyright (c) 2007 Nicolas Joly
* Copyright (c) 2007 Paul Goyette
* Copyright (c) 2007 Tobias Nygren
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Serial Presence Detect (SPD) memory identification
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: spdmem_i2c.c,v 1.1 2010/03/24 00:31:41 pgoyette Exp $");
#include <sys/param.h>
#include <sys/device.h>
#include <sys/endian.h>
#include <sys/sysctl.h>
#include <machine/bswap.h>
#include <dev/i2c/i2cvar.h>
#include <dev/ic/spdmemreg.h>
#include <dev/ic/spdmemvar.h>
/* Constants for matching i2c bus address */
#define SPDMEM_I2C_ADDRMASK 0x78
#define SPDMEM_I2C_ADDR 0x50
struct spdmem_i2c_softc {
struct spdmem_softc sc_base;
i2c_tag_t sc_tag;
i2c_addr_t sc_addr;
};
static int spdmem_i2c_match(device_t, cfdata_t, void *);
static void spdmem_i2c_attach(device_t, device_t, void *);
SYSCTL_SETUP_PROTO(sysctl_spdmem_setup);
static uint8_t spdmem_i2c_read(struct spdmem_softc *, uint8_t);
CFATTACH_DECL_NEW(spdmem_iic, sizeof(struct spdmem_i2c_softc),
spdmem_i2c_match, spdmem_i2c_attach, NULL, NULL);
static int
spdmem_i2c_match(device_t parent, cfdata_t match, void *aux)
{
struct i2c_attach_args *ia = aux;
struct spdmem_i2c_softc sc;
if (ia->ia_name) {
/* add other names as we find more firmware variations */
if (strcmp(ia->ia_name, "dimm-spd"))
return 0;
}
/* only do this lame test when not using direct config */
if (ia->ia_name == NULL) {
if ((ia->ia_addr & SPDMEM_I2C_ADDRMASK) != SPDMEM_I2C_ADDR)
return 0;
}
sc.sc_tag = ia->ia_tag;
sc.sc_addr = ia->ia_addr;
sc.sc_base.sc_read = spdmem_i2c_read;
return spdmem_common_probe(&sc.sc_base);
}
static void
spdmem_i2c_attach(device_t parent, device_t self, void *aux)
{
struct spdmem_i2c_softc *sc = device_private(self);
struct i2c_attach_args *ia = aux;
sc->sc_tag = ia->ia_tag;
sc->sc_addr = ia->ia_addr;
sc->sc_base.sc_read = spdmem_i2c_read;
if (!pmf_device_register(self, NULL, NULL))
aprint_error_dev(self, "couldn't establish power handler\n");
spdmem_common_attach(&sc->sc_base, self);
}
static uint8_t
spdmem_i2c_read(struct spdmem_softc *softc, uint8_t reg)
{
uint8_t val;
struct spdmem_i2c_softc *sc = (struct spdmem_i2c_softc *)softc;
iic_acquire_bus(sc->sc_tag, 0);
iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, &reg, 1,
&val, 1, 0);
iic_release_bus(sc->sc_tag, 0);
return val;
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: spdmem.c,v 1.18 2010/03/23 12:13:28 njoly Exp $ */
/* $NetBSD: spdmem.c,v 1.1 2010/03/24 00:31:41 pgoyette Exp $ */
/*
* Copyright (c) 2007 Nicolas Joly
@ -35,7 +35,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: spdmem.c,v 1.18 2010/03/23 12:13:28 njoly Exp $");
__KERNEL_RCSID(0, "$NetBSD: spdmem.c,v 1.1 2010/03/24 00:31:41 pgoyette Exp $");
#include <sys/param.h>
#include <sys/device.h>
@ -44,15 +44,11 @@ __KERNEL_RCSID(0, "$NetBSD: spdmem.c,v 1.18 2010/03/23 12:13:28 njoly Exp $");
#include <machine/bswap.h>
#include <dev/i2c/i2cvar.h>
#include <dev/i2c/spdmemreg.h>
#include <dev/i2c/spdmemvar.h>
#include <dev/ic/spdmemreg.h>
#include <dev/ic/spdmemvar.h>
static int spdmem_match(device_t, cfdata_t, void *);
static void spdmem_attach(device_t, device_t, void *);
SYSCTL_SETUP_PROTO(sysctl_spdmem_setup);
static uint8_t spdmem_read(struct spdmem_softc *, uint8_t);
/* Routines for decoding spd data */
static void decode_edofpm(const struct sysctlnode *, device_t, struct spdmem *);
static void decode_rom(const struct sysctlnode *, device_t, struct spdmem *);
@ -67,9 +63,6 @@ static void decode_size_speed(const struct sysctlnode *, int, int, int, int,
bool, const char *, int);
static void decode_voltage_refresh(device_t, struct spdmem *);
CFATTACH_DECL_NEW(spdmem, sizeof(struct spdmem_softc),
spdmem_match, spdmem_attach, NULL, NULL);
#define IS_RAMBUS_TYPE (s->sm_len < 4)
static const char* spdmem_basic_types[] = {
@ -145,7 +138,7 @@ static uint16_t spdcrc16 (struct spdmem_softc *sc, int count)
uint8_t val;
crc = 0;
for (j = 0; j <= count; j++) {
val = spdmem_read(sc, j);
val = (sc->sc_read)(sc, j);
crc = crc ^ val << 8;
for (i = 0; i < 8; ++i)
if (crc & 0x8000)
@ -156,42 +149,24 @@ static uint16_t spdcrc16 (struct spdmem_softc *sc, int count)
return (crc & 0xFFFF);
}
static int
spdmem_match(device_t parent, cfdata_t match, void *aux)
int
spdmem_common_probe(struct spdmem_softc *sc)
{
struct i2c_attach_args *ia = aux;
struct spdmem_softc sc;
int cksum = 0;
uint8_t i, val, spd_type;
int spd_len, spd_crc_cover;
uint16_t crc_calc, crc_spd;
if (ia->ia_name) {
/* add other names as we find more firmware variations */
if (strcmp(ia->ia_name, "dimm-spd"))
return 0;
}
/* only do this lame test when not using direct config */
if (ia->ia_name == NULL) {
if ((ia->ia_addr & SPDMEM_ADDRMASK) != SPDMEM_ADDR)
return 0;
}
sc.sc_tag = ia->ia_tag;
sc.sc_addr = ia->ia_addr;
spd_type = spdmem_read(&sc, 2);
spd_type = (sc->sc_read)(sc, 2);
/* For older memory types, validate the checksum over 1st 63 bytes */
if (spd_type <= SPDMEM_MEMTYPE_DDR2SDRAM) {
for (i = 0; i < 63; i++)
cksum += spdmem_read(&sc, i);
cksum += (sc->sc_read)(sc, i);
val = spdmem_read(&sc, 63);
val = (sc->sc_read)(sc, 63);
if (cksum == 0 || (cksum & 0xff) != val) {
aprint_debug("spd addr 0x%2x: ", sc.sc_addr);
aprint_debug("spd checksum failed, calc = 0x%02x, "
"spd = 0x%02x\n", cksum, val);
return 0;
@ -201,7 +176,7 @@ spdmem_match(device_t parent, cfdata_t match, void *aux)
/* For DDR3 and FBDIMM, verify the CRC */
else if (spd_type <= SPDMEM_MEMTYPE_DDR3SDRAM) {
spd_len = spdmem_read(&sc, 0);
spd_len = (sc->sc_read)(sc, 0);
if (spd_len && SPDMEM_SPDCRC_116)
spd_crc_cover = 116;
else
@ -221,11 +196,10 @@ spdmem_match(device_t parent, cfdata_t match, void *aux)
}
if (spd_crc_cover > spd_len)
return 0;
crc_calc = spdcrc16(&sc, spd_crc_cover);
crc_spd = spdmem_read(&sc, 127) << 8;
crc_spd |= spdmem_read(&sc, 126);
crc_calc = spdcrc16(sc, spd_crc_cover);
crc_spd = (sc->sc_read)(sc, 127) << 8;
crc_spd |= (sc->sc_read)(sc, 126);
if (crc_calc != crc_spd) {
aprint_debug("spd addr 0x%2x: ", sc.sc_addr);
aprint_debug("crc16 failed, covers %d bytes, "
"calc = 0x%04x, spd = 0x%04x\n",
spd_crc_cover, crc_calc, crc_spd);
@ -238,11 +212,9 @@ spdmem_match(device_t parent, cfdata_t match, void *aux)
return 0;
}
static void
spdmem_attach(device_t parent, device_t self, void *aux)
void
spdmem_common_attach(struct spdmem_softc *sc, device_t self)
{
struct spdmem_softc *sc = device_private(self);
struct i2c_attach_args *ia = aux;
struct spdmem *s = &(sc->sc_spd_data);
const char *type;
const char *rambus_rev = "Reserved";
@ -251,19 +223,13 @@ spdmem_attach(device_t parent, device_t self, void *aux)
unsigned int spd_len, spd_size;
const struct sysctlnode *node = NULL;
sc->sc_tag = ia->ia_tag;
sc->sc_addr = ia->ia_addr;
if (!pmf_device_register(self, NULL, NULL))
aprint_error_dev(self, "couldn't establish power handler\n");
/*
* FBDIMM and DDR3 (and probably all newer) have a different
* encoding of the SPD EEPROM used/total sizes
*/
s->sm_len = spdmem_read(sc, 0);
s->sm_size = spdmem_read(sc, 1);
s->sm_type = spdmem_read(sc, 2);
s->sm_len = (sc->sc_read)(sc, 0);
s->sm_size = (sc->sc_read)(sc, 1);
s->sm_type = (sc->sc_read)(sc, 2);
if (s->sm_type >= SPDMEM_MEMTYPE_FBDIMM) {
spd_size = 64 << (s->sm_len & SPDMEM_SPDSIZE_MASK);
@ -292,7 +258,7 @@ spdmem_attach(device_t parent, device_t self, void *aux)
if (spd_len > sizeof(struct spdmem))
spd_len = sizeof(struct spdmem);
for (i = 3; i < spd_len; i++)
((uint8_t *)s)[i] = spdmem_read(sc, i);
((uint8_t *)s)[i] = (sc->sc_read)(sc, i);
#ifdef DEBUG
for (i = 0; i < spd_len; i += 16) {
@ -420,19 +386,6 @@ spdmem_attach(device_t parent, device_t self, void *aux)
}
}
static uint8_t
spdmem_read(struct spdmem_softc *sc, uint8_t reg)
{
uint8_t val;
iic_acquire_bus(sc->sc_tag, 0);
iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, &reg, 1,
&val, 1, 0);
iic_release_bus(sc->sc_tag, 0);
return val;
}
SYSCTL_SETUP(sysctl_spdmem_setup, "sysctl hw.spdmem subtree setup")
{
const struct sysctlnode *node;

View File

@ -1,4 +1,4 @@
/* $NetBSD: spdmemreg.h,v 1.3 2009/02/22 17:28:50 pgoyette Exp $ */
/* $NetBSD: spdmemreg.h,v 1.1 2010/03/24 00:31:41 pgoyette Exp $ */
/*
* Copyright (c) 2007 Paul Goyette
@ -28,10 +28,6 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
/* Constants for matching i2c bus address */
#define SPDMEM_ADDRMASK 0x78
#define SPDMEM_ADDR 0x50
/* possible values for the memory type */
#define SPDMEM_MEMTYPE_FPM 0x01
#define SPDMEM_MEMTYPE_EDO 0x02

View File

@ -1,4 +1,4 @@
/* $NetBSD: spdmemvar.h,v 1.7 2008/11/22 19:25:38 pgoyette Exp $ */
/* $NetBSD: spdmemvar.h,v 1.1 2010/03/24 00:31:41 pgoyette Exp $ */
/*
* Copyright (c) 2007 Paul Goyette
@ -514,9 +514,12 @@ struct spdmem {
#define sm_selfrefresh sm_fpm.fpm_selfrefresh
#define SPDMEM_TYPE_MAXLEN 16
struct spdmem_softc {
i2c_tag_t sc_tag;
i2c_addr_t sc_addr;
uint8_t (*sc_read)(struct spdmem_softc *, uint8_t);
struct spdmem sc_spd_data;
char sc_type[SPDMEM_TYPE_MAXLEN];
};
int spdmem_common_probe(struct spdmem_softc *);
void spdmem_common_attach(struct spdmem_softc *, device_t);