----- Forwarded message from stan <[email protected]> -----
From: stan <[email protected]>
To: Theo de Raadt <[email protected]>
Subject: Re: watchdog suport for new hardware
Date: Tue, 26 Apr 2016 09:19:20 -0400
User-Agent: Mutt/1.5.4i
X-Operating-System: Debian GNU/Linux
X-Kernel-Version: 2.4.23
X-Uptime: 09:17:17 up 91 days, 8:18, 1 user, load average: 0.00, 0.02, 0.04
X-Editor: gVim
Hee is the core of the functionality:
/* $OpenBSD: selwd.c,v 1.0 2016/04/01 05:00:00 jsg Exp $ */
/*
* Copyright (c) 2016 PREMIER System Integrators
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Schweitzer Engineering Laboratories: SEL-3355 Embedded controller
*/
#include <sys/types.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <machine/bus.h>
#include <dev/isa/isavar.h>
#include <dev/isa/selwdreg.h>
struct selwd_softc {
/* sc_dev must be the first item in the struct */
struct device sc_dev;
/* device access through bus space */
bus_space_tag_t sc_iot;
bus_space_handle_t sc_ioh;
};
int selwd_wait(bus_space_tag_t, bus_space_handle_t, bool);
/* Autoconfiguration glue */
int selwd_probe(struct device *, void *, void *);
void selwd_attach(struct device *, struct device *, void *);
int selwd_print(void *, const char *);
int selwd_wd_cb(void *, int);
/* functions to interact with the controller */
void selwd_write_wdog(bus_space_tag_t, bus_space_handle_t, int);
u_int8_t selwd_read_wdog(bus_space_tag_t, bus_space_handle_t);
u_int8_t selwd_read_status(bus_space_tag_t, bus_space_handle_t);
void selwd_abort(bus_space_tag_t, bus_space_handle_t);
u_int32_t selwd_read_boardid(bus_space_tag_t, bus_space_handle_t);
u_int32_t selwd_read_mcversion(bus_space_tag_t, bus_space_handle_t);
u_int16_t selwd_read_pciboardid(bus_space_tag_t, bus_space_handle_t);
u_int8_t selwd_read_ledctl0(bus_space_tag_t, bus_space_handle_t);
void selwd_write_ledctl0(bus_space_tag_t, bus_space_handle_t, u_int8_t);
u_int8_t selwd_read_ledctl1(bus_space_tag_t, bus_space_handle_t);
void selwd_write_ledctl1(bus_space_tag_t, bus_space_handle_t, u_int8_t);
u_int8_t selwd_read_miscctl0(bus_space_tag_t, bus_space_handle_t);
u_int8_t selwd_read_miscctl1(bus_space_tag_t, bus_space_handle_t);
void selwd_read_modelno(bus_space_tag_t, bus_space_handle_t, char*);
void selwd_read_serialno(bus_space_tag_t, bus_space_handle_t, char*);
void selwd_read_configid(bus_space_tag_t, bus_space_handle_t, char*);
/* macros to extract bits from the status register */
#define EC_STATUS_IBF(status) (((status) >> 0x1) & 0x1)
#define EC_STATUS_OBF(status) (((status) & 0x1))
#define EC_STATUS_BUSY(status) (((status) >> 0x4) & 0x1)
#define EC_STATUS_STATE(status) (((status) >> 0x6) & 0x3)
struct cfattach selwd_ca = {
sizeof(struct selwd_softc), selwd_probe, selwd_attach
};
struct cfdriver selwd_cd = {
NULL, "selwd", DV_DULL
};
const char* selwd_models[] = { SELWD_DEV_3355, SELWD_DEV_3360 };
int selwd_wait(bus_space_tag_t iot, bus_space_handle_t ioh, bool useobf)
{
uint32_t timeout = 0;
uint8_t status = 0;
while (timeout < 50) {
status = bus_space_read_1(iot, ioh, SELWD_CMD);
if ((EC_STATUS_IBF(status) == 0x0) &&
(EC_STATUS_BUSY(status) == 0x0) &&
(EC_STATUS_STATE(status) != 0x3) &&
((EC_STATUS_OBF(status) == 0x0) || !useobf)) {
return 1;
}
delay(10);
++timeout;
}
return 0;
}
static __inline void
selwd_conf_enable(bus_space_tag_t iot, bus_space_handle_t ioh, u_int8_t cmd)
{
/* write the desired command to the command register */
bus_space_write_1(iot, ioh, SELWD_CMD, cmd);
/* wait for controller to be ready */
selwd_wait(iot,ioh,0);
}
static __inline u_int8_t
selwd_conf_read(bus_space_tag_t iot, bus_space_handle_t ioh, u_int8_t target)
{
/* write the target address to the data register */
bus_space_write_1(iot, ioh, SELWD_DATA, target);
/* wait for controller to be ready */
selwd_wait(iot, ioh,1);
/* return the value from the data register */
return (bus_space_read_1(iot, ioh, SELWD_DATA));
}
static __inline void
selwd_conf_write(bus_space_tag_t iot, bus_space_handle_t ioh, u_int8_t target,
u_int8_t data)
{
/* write the target address to the data register */
bus_space_write_1(iot, ioh, SELWD_DATA, target);
/* wait for controller to be ready */
selwd_wait(iot, ioh,0);
/* write the desired data to the data register */
bus_space_write_1(iot, ioh, SELWD_DATA, data);
/* wait for controller to be ready */
selwd_wait(iot, ioh,0);
}
int
selwd_probe(struct device *parent, void *match, void *aux)
{
struct isa_attach_args *ia = aux;
bus_space_tag_t iot;
bus_space_handle_t ioh;
/* Match by device ID */
iot = ia->ia_iot;
if (bus_space_map(iot, ia->ipa_io[0].base, SELWD_IOSIZE, 0, &ioh))
return 0;
/* print some diagnostic stuff */
struct device *g1parent = parent->dv_parent;
struct device *g2parent = g1parent->dv_parent;
struct device *g3parent = g2parent->dv_parent;
struct device *g4parent = g3parent->dv_parent;
if (g4parent == NULL)
printf("SEL device: parent=%s, g1parent=%s, g2parent=%s,
g3parent=%s,
g4parent=NULL\n",parent->dv_xname,g1parent->dv_xname,g2parent->dv_xname,g3parent->dv_xname);
else
printf("SEL device: parent=%s, g1parent=%s, g2parent=%s,
g3parent=%s,
g4parent=%s\n",parent->dv_xname,g1parent->dv_xname,g2parent->dv_xname,g3parent->dv_xname,g4parent->dv_xname);
/* read model number */
char *model = malloc(sizeof(char)*16, M_DEVBUF, M_WAITOK | M_ZERO);
selwd_read_modelno(iot, ioh, model);
bus_space_unmap(iot, ioh, SELWD_IOSIZE);
/* model number must match pre-defined values */
int i;
for (i=0; selwd_models[i]; i++) {
if (!strcmp(model,selwd_models[i])) {
ia->ipa_nio = 1;
ia->ipa_io[0].length = SELWD_IOSIZE;
ia->ipa_nmem = 0;
ia->ipa_nirq = 0;
ia->ipa_ndrq = 0;
return 1;
}
i++;
}
free(model, M_DEVBUF, 0);
return 0;
}
void
selwd_attach(struct device *parent, struct device *self, void *aux)
{
struct selwd_softc *sc = (struct selwd_softc *)self;
struct isa_attach_args *ia = aux;
struct isa_attach_args nia;
u_int32_t devid;
/* Map ISA I/O space */
sc->sc_iot = ia->ia_iot;
if (bus_space_map(sc->sc_iot, ia->ipa_io[0].base,
SELWD_IOSIZE, 0, &sc->sc_ioh)) {
printf("SEL Watchdog cannot map i/o space\n");
return;
}
/* read board ID */
devid = selwd_read_boardid(sc->sc_iot, sc->sc_ioh);
printf(": SEL Board ID (%x)",devid);
/* read model number */
char *model = malloc(sizeof(char)*16, M_DEVBUF, M_WAITOK | M_ZERO);
selwd_read_modelno(sc->sc_iot, sc->sc_ioh, model);
printf(", Model No (%s)",model);
free(model, M_DEVBUF, 0);
/* read serial number */
char *serial = malloc(sizeof(char)*16, M_DEVBUF, M_WAITOK | M_ZERO);
selwd_read_serialno(sc->sc_iot, sc->sc_ioh, serial);
printf(", Serial No (%s)\n",serial);
free(serial, M_DEVBUF, 0);
/* write to Enabled and Alarm LEDs */
/* turns Enabled green and Alarm off */
u_int8_t led0 = 0x02;
selwd_write_ledctl0(sc->sc_iot, sc->sc_ioh, led0);
/* write to Aux LEDs */
/* turns off the LEDs */
u_int8_t led1 = 0x00;
selwd_write_ledctl1(sc->sc_iot, sc->sc_ioh, led1);
nia = *ia;
nia.ia_iobase = 0x192;
nia.ia_aux = (void *)(u_long)devid; /* pass devid down to wb_match */
config_found(self, &nia, selwd_print);
wdog_register(selwd_wd_cb,sc);
}
int
selwd_wd_cb(void *arg, int period)
{
/* This function is called by the watchdog daemon */
/* Get a reference to the software context */
struct selwd_softc *sc = arg;
if (period < 0)
period = 0;
/* The period value is in seconds, the watchdog value is in
twos-of-secs */
int wd = period / 2;
/* write watchdog value */
selwd_write_wdog(sc->sc_iot, sc->sc_ioh, wd);
/* return the period back to the daemon */
return period;
}
int
selwd_print(void *aux, const char *pnp)
{
struct isa_attach_args *ia = aux;
if (pnp)
printf("%s", pnp);
if (ia->ia_iosize)
printf(" port 0x%x", ia->ia_iobase);
if (ia->ia_iosize > 1)
printf("/%d", ia->ia_iosize);
printf("\n");
return 0;
}
void
selwd_write_wdog(bus_space_tag_t iot, bus_space_handle_t ioh, int value)
{
/* Check to make sure that the state is idle */
selwd_abort(iot, ioh);
/* Write watchdog timer value */
/* write the WRITECFG command to the command register */
selwd_conf_enable(iot, ioh, SELWD_CMD_WRITECFG);
/* write the watchdog value to the watchdog address */
selwd_conf_write(iot, ioh, SELWD_CFG_WATCHDOG, value);
}
u_int8_t
selwd_read_wdog(bus_space_tag_t iot, bus_space_handle_t ioh)
{
/* read the watchdog value from the controller */
/* make sure status is 0 before proceeding */
selwd_abort(iot, ioh);
u_int8_t wdog;
/* write the READCFG command to the command register */
selwd_conf_enable(iot, ioh, SELWD_CMD_READCFG);
/* read the WATCHDOG data value from the data register */
wdog = selwd_conf_read(iot, ioh, SELWD_CFG_WATCHDOG);
return wdog;
}
u_int8_t
selwd_read_status(bus_space_tag_t iot, bus_space_handle_t ioh)
{
/* read the command/status register */
u_int8_t status = 0;
status = bus_space_read_1(iot, ioh, SELWD_CMD);
return status;
}
void
selwd_abort(bus_space_tag_t iot, bus_space_handle_t ioh)
{
/* write the abort value to the command/status register */
u_int8_t status = 0;
/* first read the status register */
status = bus_space_read_1(iot, ioh, SELWD_CMD);
/* if the state is IDLE return */
if (EC_STATUS_STATE(status) == 0)
return;
uint32_t timeout = 0;
/* make sure status is 0 before proceeding */
while (EC_STATUS_STATE(status) != 0 && timeout < 50) {
/* write the abort command */
bus_space_write_1(iot, ioh, SELWD_CMD, SELWD_CMD_ABORT);
/* wait until controller isn't busy */
selwd_wait(iot, ioh, 0);
/* read the status register */
status = bus_space_read_1(iot, ioh, SELWD_CMD);
delay(10);
/* increment timeout */
++timeout;
}
}
u_int32_t
selwd_read_boardid(bus_space_tag_t iot, bus_space_handle_t ioh)
{
/* read the board ID from the controller */
/* make sure status is 0 before proceeding */
selwd_abort(iot, ioh);
u_int8_t reg0, reg1, reg2, reg3;
/* write the READCFG command to the command register */
selwd_conf_enable(iot, ioh, SELWD_CMD_READCFG);
/* read the BOARID0 data value from the data register */
reg0 = selwd_conf_read(iot, ioh, SELWD_CFG_BOARDID0);
/* write the READCFG command to the command register */
selwd_conf_enable(iot, ioh, SELWD_CMD_READCFG);
/* read the BOARID1 data value from the data register */
reg1 = selwd_conf_read(iot, ioh, SELWD_CFG_BOARDID1);
/* write the READCFG command to the command register */
selwd_conf_enable(iot, ioh, SELWD_CMD_READCFG);
/* read the BOARID2 data value from the data register */
reg2 = selwd_conf_read(iot, ioh, SELWD_CFG_BOARDID2);
/* write the READCFG command to the command register */
selwd_conf_enable(iot, ioh, SELWD_CMD_READCFG);
/* read the BOARID3 data value from the data register */
reg3 = selwd_conf_read(iot, ioh, SELWD_CFG_BOARDID3);
/* convert the 4 bytes into a 32 bit number */
u_int32_t boardid = (reg3 << 24) | (reg2 << 16) | (reg1 << 8) | reg0;
return boardid;
}
u_int32_t
selwd_read_mcversion(bus_space_tag_t iot, bus_space_handle_t ioh)
{
/* read the MC version from the controller */
/* make sure status is 0 before proceeding */
selwd_abort(iot, ioh);
u_int8_t reg0, reg1, reg2, reg3;
/* write the READCFG command to the command register */
selwd_conf_enable(iot, ioh, SELWD_CMD_READCFG);
/* read the VERSION0 data value from the data register */
reg0 = selwd_conf_read(iot, ioh, SELWD_CFG_VERSION0);
/* write the READCFG command to the command register */
selwd_conf_enable(iot, ioh, SELWD_CMD_READCFG);
/* read the VERSION1 data value from the data register */
reg1 = selwd_conf_read(iot, ioh, SELWD_CFG_VERSION1);
/* write the READCFG command to the command register */
selwd_conf_enable(iot, ioh, SELWD_CMD_READCFG);
/* read the VERSION2 data value from the data register */
reg2 = selwd_conf_read(iot, ioh, SELWD_CFG_VERSION2);
/* write the READCFG command to the command register */
selwd_conf_enable(iot, ioh, SELWD_CMD_READCFG);
/* read the VERSION3 data value from the data register */
reg3 = selwd_conf_read(iot, ioh, SELWD_CFG_VERSION3);
/* convert the 4 bytes into a 32 bit number */
u_int32_t version = (reg3 << 24) | (reg2 << 16) | (reg1 << 8) | reg0;
return version;
}
u_int16_t
selwd_read_pciboardid(bus_space_tag_t iot, bus_space_handle_t ioh)
{
/* read the PCI board ID from the controller */
/* make sure status is 0 before proceeding */
selwd_abort(iot, ioh);
u_int8_t reg0, reg1;
/* write the READCFG command to the command register */
selwd_conf_enable(iot, ioh, SELWD_CMD_READCFG);
/* read the PCIBRDID0 data value from the data register */
reg0 = selwd_conf_read(iot, ioh, SELWD_CFG_PCIBRDID0);
/* write the READCFG command to the command register */
selwd_conf_enable(iot, ioh, SELWD_CMD_READCFG);
/* read the PCIBRDID1 data value from the data register */
reg1 = selwd_conf_read(iot, ioh, SELWD_CFG_PCIBRDID1);
/* convert the 2 bytes into a 16 bit number */
u_int16_t boardid = (reg1 << 8) | reg0;
return boardid;
}
u_int8_t
selwd_read_miscctl0(bus_space_tag_t iot, bus_space_handle_t ioh)
{
/* read the misc control 0 value from the controller */
/* make sure status is 0 before proceeding */
selwd_abort(iot, ioh);
u_int8_t reg;
/* write the READCFG command to the command register */
selwd_conf_enable(iot, ioh, SELWD_CMD_READCFG);
/* read the MISCCTL0 data value from the data register */
reg = selwd_conf_read(iot, ioh, SELWD_CFG_MISCCTL0);
return reg;
}
u_int8_t
selwd_read_miscctl1(bus_space_tag_t iot, bus_space_handle_t ioh)
{
/* read the misc control 1 value from the controller */
/* make sure status is 0 before proceeding */
selwd_abort(iot, ioh);
u_int8_t reg;
/* write the READCFG command to the command register */
selwd_conf_enable(iot, ioh, SELWD_CMD_READCFG);
/* read the MISCCTL1 data value from the data register */
reg = selwd_conf_read(iot, ioh, SELWD_CFG_MISCCTL1);
return reg;
}
u_int8_t
selwd_read_ledctl0(bus_space_tag_t iot, bus_space_handle_t ioh)
{
/* read the LED control 0 value from the controller */
/* make sure status is 0 before proceeding */
selwd_abort(iot, ioh);
u_int8_t reg;
/* write the READCFG command to the command register */
selwd_conf_enable(iot, ioh, SELWD_CMD_READCFG);
/* read the LEDCTRL0 data value from the data register */
reg = selwd_conf_read(iot, ioh, SELWD_CFG_LEDCTRL0);
return reg;
}
void
selwd_write_ledctl0(bus_space_tag_t iot, bus_space_handle_t ioh, u_int8_t data)
{
/* write the LED control 0 value to the controller */
/* make sure status is 0 before proceeding */
selwd_abort(iot, ioh);
/* write the WRITECFG command to the command register */
selwd_conf_enable(iot, ioh, SELWD_CMD_WRITECFG);
/* write the LEDCTRL0 data value to the data register */
selwd_conf_write(iot, ioh, SELWD_CFG_LEDCTRL0, data);
}
u_int8_t
selwd_read_ledctl1(bus_space_tag_t iot, bus_space_handle_t ioh)
{
/* read the LED control 1 value from the controller */
/* make sure status is 0 before proceeding */
selwd_abort(iot, ioh);
u_int8_t reg;
/* write the READCFG command to the command register */
selwd_conf_enable(iot, ioh, SELWD_CMD_READCFG);
/* read the LEDCTRL1 data value from the data register */
reg = selwd_conf_read(iot, ioh, SELWD_CFG_LEDCTRL1);
return reg;
}
void
selwd_write_ledctl1(bus_space_tag_t iot, bus_space_handle_t ioh, u_int8_t data)
{
/* write the LED control 1 value to the controller */
/* make sure status is 0 before proceeding */
selwd_abort(iot, ioh);
/* write the WRITECFG command to the command register */
selwd_conf_enable(iot, ioh, SELWD_CMD_WRITECFG);
/* write the LEDCTRL1 data value to the data register */
selwd_conf_write(iot, ioh, SELWD_CFG_LEDCTRL1, data);
}
void
selwd_read_modelno(bus_space_tag_t iot, bus_space_handle_t ioh, char* model)
{
/* read the MODEL NUMBER value from the controller */
/* make sure status is 0 before proceeding */
selwd_abort(iot, ioh);
u_int8_t reg;
int i = 0;
/* write the READCFG command to the command register */
selwd_conf_enable(iot, ioh, SELWD_CMD_READCFG);
/* read the MODELNO data value from the data register */
reg = selwd_conf_read(iot, ioh, SELWD_CFG_MODELNO);
while (reg != 0 && i < 15) {
model[i] = reg;
/* make sure status is 0 before proceeding */
selwd_abort(iot, ioh);
/* write the READCFG command to the command register */
selwd_conf_enable(iot, ioh, SELWD_CMD_READCFG);
/* read the MODELNO data value from the data register */
reg = selwd_conf_read(iot, ioh, SELWD_CFG_MODELNO);
i++;
}
}
void
selwd_read_serialno(bus_space_tag_t iot, bus_space_handle_t ioh, char* serial)
{
/* read the SERIAL NUMBER value from the controller */
/* make sure status is 0 before proceeding */
selwd_abort(iot, ioh);
u_int8_t reg;
int i = 0;
/* write the READCFG command to the command register */
selwd_conf_enable(iot, ioh, SELWD_CMD_READCFG);
/* read the SERIALNO data value from the data register */
reg = selwd_conf_read(iot, ioh, SELWD_CFG_SERIALNO);
while (reg != 0 && i < 15) {
serial[i] = reg;
/* make sure status is 0 before proceeding */
selwd_abort(iot, ioh);
/* write the READCFG command to the command register */
selwd_conf_enable(iot, ioh, SELWD_CMD_READCFG);
/* read the SERIALNO data value from the data register */
reg = selwd_conf_read(iot, ioh, SELWD_CFG_SERIALNO);
i++;
}
}
void
selwd_read_configid(bus_space_tag_t iot, bus_space_handle_t ioh, char* configid)
{
/* read the CONFIGURATION ID value from the controller */
/* make sure status is 0 before proceeding */
selwd_abort(iot, ioh);
u_int8_t reg;
int i = 0;
/* write the READCFG command to the command register */
selwd_conf_enable(iot, ioh, SELWD_CMD_READCFG);
/* read the CONFIGID data value from the data register */
reg = selwd_conf_read(iot, ioh, SELWD_CFG_CONFIGID);
while (reg != 0 && i < 15) {
configid[i] = reg;
/* make sure status is 0 before proceeding */
selwd_abort(iot, ioh);
/* write the READCFG command to the command register */
selwd_conf_enable(iot, ioh, SELWD_CMD_READCFG);
/* read the CONFIGID data value from the data register */
reg = selwd_conf_read(iot, ioh, SELWD_CFG_CONFIGID);
i++;
}
}
On Tue, Apr 26, 2016 at 06:44:34AM -0600, Theo de Raadt wrote:
> obviously you show the code, and then when the complexity/simplicity of it
> is seen, some people can jump in and help.
>
> that is the traditional way: show it
>
> > We are embarking on a project where we will be using a number of
> > industrially hardened computers manufactured by Schweitzer Engineering
> > Laboratories, Inc. (SEL). SEL provides a very well whiten document
> > describing certain special features of these computers. One of these is a
> > hardware watchdog.
> >
> > We have contracted a systems integrator to write a device driver for this
> > watchdog, with the stipulation that the code will be relapsed under the BSD
> > licence. This work is essiantly complete, and I was wondering if someone
> > could
> > suggest to me the best way to submit this for (potential) inclusion in the
> > OpenBSD base system?
> >
> > Thanks for all the good work on a really solid OS!
> >
> >
> > --
> > A: Because it messes up the order in which people normally read text.
> > Q: Why is top-posting such a bad thing?
> > A: Top-posting.
> > Q: What is the most annoying thing in e-mail?
> >
>
--
A: Because it messes up the order in which people normally read text.
Q: Why is top-posting such a bad thing?
A: Top-posting.
Q: What is the most annoying thing in e-mail?
----- End forwarded message -----
--
A: Because it messes up the order in which people normally read text.
Q: Why is top-posting such a bad thing?
A: Top-posting.
Q: What is the most annoying thing in e-mail?