summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/dev/ips/ips.c66
-rw-r--r--sys/dev/ips/ips.h59
-rw-r--r--sys/dev/ips/ips_commands.c90
3 files changed, 211 insertions, 4 deletions
diff --git a/sys/dev/ips/ips.c b/sys/dev/ips/ips.c
index 0cbcf6b..165ebb0 100644
--- a/sys/dev/ips/ips.c
+++ b/sys/dev/ips/ips.c
@@ -46,6 +46,25 @@ static struct cdevsw ips_cdevsw = {
.d_maj = IPS_CDEV_MAJOR,
};
+static const char* ips_adapter_name[] = {
+ "N/A",
+ "ServeRAID (copperhead)",
+ "ServeRAID II (copperhead refresh)",
+ "ServeRAID onboard (copperhead)",
+ "ServeRAID onboard (copperhead)",
+ "ServeRAID 3H (clarinet)",
+ "ServeRAID 3L (clarinet lite)",
+ "ServeRAID 4H (trombone)",
+ "ServeRAID 4M (morpheus)",
+ "ServeRAID 4L (morpheus lite)",
+ "ServeRAID 4Mx (neo)",
+ "ServeRAID 4Lx (neo lite)",
+ "ServeRAID 5i II (sarasota)",
+ "ServeRAID 5i (sarasota)",
+ "ServeRAID 6M (marco)",
+ "ServeRAID 6i (sebring)"
+};
+
static int ips_open(dev_t dev, int flags, int fmt, struct thread *td)
{
@@ -240,12 +259,44 @@ void ips_insert_free_cmd(ips_softc_t *sc, ips_command_t *command)
if(!(sc->state & IPS_TIMEOUT))
ips_run_waiting_command(sc);
}
+static const char* ips_diskdev_statename(u_int8_t state)
+{
+ static char statebuf[20];
+ switch(state){
+ case IPS_LD_OFFLINE:
+ return("OFFLINE");
+ break;
+ case IPS_LD_OKAY:
+ return("OK");
+ break;
+ case IPS_LD_DEGRADED:
+ return("DEGRADED");
+ break;
+ case IPS_LD_FREE:
+ return("FREE");
+ break;
+ case IPS_LD_SYS:
+ return("SYS");
+ break;
+ case IPS_LD_CRS:
+ return("CRS");
+ break;
+ }
+ sprintf(statebuf,"UNKNOWN(0x%02x)", state);
+ return(statebuf);
+}
static int ips_diskdev_init(ips_softc_t *sc)
{
int i;
for(i=0; i < IPS_MAX_NUM_DRIVES; i++){
- if(sc->drives[i].state & IPS_LD_OKAY){
+ if(sc->drives[i].state == IPS_LD_FREE) continue;
+ device_printf(sc->dev, "Logical Drive %d: RAID%d sectors: %u, state %s\n",
+ i, sc->drives[i].raid_lvl,
+ sc->drives[i].sector_count,
+ ips_diskdev_statename(sc->drives[i].state));
+ if(sc->drives[i].state == IPS_LD_OKAY ||
+ sc->drives[i].state == IPS_LD_DEGRADED){
sc->diskdev[i] = device_add_child(sc->dev, NULL, -1);
device_set_ivars(sc->diskdev[i],(void *)(uintptr_t) i);
}
@@ -369,15 +420,26 @@ int ips_adapter_init(ips_softc_t *sc)
goto error;
mtx_init(&sc->cmd_mtx, "ips command mutex", NULL, MTX_DEF);
+
+ /* initialize ffdc values */
+ microtime(&sc->ffdc_resettime);
+ sc->ffdc_resetcount = 1;
+ if ((i = ips_ffdc_reset(sc)) != 0) {
+ device_printf(sc->dev, "failed to send ffdc reset to device (%d)\n", i);
+ goto error;
+ }
if ((i = ips_get_adapter_info(sc)) != 0) {
device_printf(sc->dev, "failed to get adapter configuration data from device (%d)\n", i);
goto error;
}
+ ips_update_nvram(sc); /* no error check as failure doesn't matter */
+ if(sc->adapter_type > 0 && sc->adapter_type <= IPS_ADAPTER_MAX_T){
+ device_printf(sc->dev, "adapter type: %s\n", ips_adapter_name[sc->adapter_type]);
+ }
if ((i = ips_get_drive_info(sc)) != 0) {
device_printf(sc->dev, "failed to get drive configuration data from device (%d)\n", i);
goto error;
}
- ips_update_nvram(sc); /* no error check as failure doesn't matter */
ips_cmdqueue_free(sc);
if(sc->adapter_info.max_concurrent_cmds)
diff --git a/sys/dev/ips/ips.h b/sys/dev/ips/ips.h
index 3d0f19a..f644df6 100644
--- a/sys/dev/ips/ips.h
+++ b/sys/dev/ips/ips.h
@@ -39,6 +39,7 @@
#include <sys/bio.h>
#include <sys/malloc.h>
#include <sys/mutex.h>
+#include <sys/time.h>
#include <machine/bus_memio.h>
#include <machine/bus.h>
@@ -82,6 +83,7 @@
#define IPS_LD_OFFLINE 0x02
#define IPS_LD_OKAY 0x03
+#define IPS_LD_DEGRADED 0x04
#define IPS_LD_FREE 0x00
#define IPS_LD_SYS 0x06
#define IPS_LD_CRS 0x24
@@ -140,14 +142,47 @@
#define IPS_SG_READ_CMD 0x82
#define IPS_SG_WRITE_CMD 0x83
#define IPS_RW_NVRAM_CMD 0xBC
+#define IPS_FFDC_CMD 0xD7
/* error information returned by the adapter */
#define IPS_MIN_ERROR 0x02
#define IPS_ERROR_STATUS 0x13000200 /* ahh, magic numbers */
-#define IPS_OS_FREEBSD 10
+#define IPS_OS_FREEBSD 8
#define IPS_VERSION_MAJOR "0.90"
-#define IPS_VERSION_MINOR ".00"
+#define IPS_VERSION_MINOR ".10"
+
+/* Adapter Types */
+#define IPS_ADAPTER_COPPERHEAD 0x01
+#define IPS_ADAPTER_COPPERHEAD2 0x02
+#define IPS_ADAPTER_COPPERHEADOB1 0x03
+#define IPS_ADAPTER_COPPERHEADOB2 0x04
+#define IPS_ADAPTER_CLARINET 0x05
+#define IPS_ADAPTER_CLARINETLITE 0x06
+#define IPS_ADAPTER_TROMBONE 0x07
+#define IPS_ADAPTER_MORPHEUS 0x08
+#define IPS_ADAPTER_MORPHEUSLITE 0x09
+#define IPS_ADAPTER_NEO 0x0A
+#define IPS_ADAPTER_NEOLITE 0x0B
+#define IPS_ADAPTER_SARASOTA2 0x0C
+#define IPS_ADAPTER_SARASOTA1 0x0D
+#define IPS_ADAPTER_MARCO 0x0E
+#define IPS_ADAPTER_SEBRING 0x0F
+#define IPS_ADAPTER_MAX_T IPS_ADAPTER_SEBRING
+
+/* values for ffdc_settime (from gmtime) */
+#define IPS_SECSPERMIN 60
+#define IPS_MINSPERHOUR 60
+#define IPS_HOURSPERDAY 24
+#define IPS_DAYSPERWEEK 7
+#define IPS_DAYSPERNYEAR 365
+#define IPS_DAYSPERLYEAR 366
+#define IPS_SECSPERHOUR (IPS_SECSPERMIN * IPS_MINSPERHOUR)
+#define IPS_SECSPERDAY ((long) IPS_SECSPERHOUR * IPS_HOURSPERDAY)
+#define IPS_MONSPERYEAR 12
+#define IPS_EPOCH_YEAR 1970
+#define IPS_LEAPS_THRU_END_OF(y) ((y) / 4 - (y) / 100 + (y) / 400)
+#define ips_isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
/*
* IPS MACROS
@@ -232,6 +267,22 @@ typedef struct{
u_int32_t reserve3;
} __attribute__((packed)) ips_adapter_info_cmd;
+typedef struct{
+ u_int8_t command;
+ u_int8_t id;
+ u_int8_t reset_count;
+ u_int8_t reset_type;
+ u_int8_t second;
+ u_int8_t minute;
+ u_int8_t hour;
+ u_int8_t day;
+ u_int8_t reserve1[4];
+ u_int8_t month;
+ u_int8_t yearH;
+ u_int8_t yearL;
+ u_int8_t reserve2;
+} __attribute__((packed)) ips_adapter_ffdc_cmd;
+
typedef union{
ips_generic_cmd generic_cmd;
ips_drive_cmd drive_cmd;
@@ -358,10 +409,13 @@ typedef struct ips_softc{
device_t dev;
dev_t device_file;
struct callout_handle timer;
+ u_int16_t adapter_type;
ips_adapter_info_t adapter_info;
device_t diskdev[IPS_MAX_NUM_DRIVES];
ips_drive_t drives[IPS_MAX_NUM_DRIVES];
u_int8_t drivecount;
+ u_int16_t ffdc_resetcount;
+ struct timeval ffdc_resettime;
u_int8_t next_drive;
u_int8_t max_cmds;
volatile u_int8_t used_commands;
@@ -387,6 +441,7 @@ extern int ips_flush_cache(ips_softc_t *sc);
extern void ips_start_io_request(ips_softc_t *sc, struct bio *iobuf);
extern int ips_get_drive_info(ips_softc_t *sc);
extern int ips_get_adapter_info(ips_softc_t *sc);
+extern int ips_ffdc_reset(ips_softc_t *sc);
extern int ips_update_nvram(ips_softc_t *sc);
extern int ips_clear_adapter(ips_softc_t *sc);
diff --git a/sys/dev/ips/ips_commands.c b/sys/dev/ips/ips_commands.c
index 67f1655..cb0dd76 100644
--- a/sys/dev/ips/ips_commands.c
+++ b/sys/dev/ips/ips_commands.c
@@ -434,6 +434,93 @@ int ips_flush_cache(ips_softc_t *sc)
return 0;
}
+/* Simplified localtime to provide timevalues for ffdc.
+ * Taken from libc/stdtime/localtime.c
+ */
+void static ips_ffdc_settime(ips_adapter_ffdc_cmd *command, time_t sctime)
+{
+ long days, rem, y;
+ int yleap, *ip, month;
+ int year_lengths[2] = { IPS_DAYSPERNYEAR, IPS_DAYSPERLYEAR };
+ int mon_lengths[2][IPS_MONSPERYEAR] = {
+ { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
+ { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
+ };
+
+ days = sctime / IPS_SECSPERDAY;
+ rem = sctime % IPS_SECSPERDAY;
+
+ command->hour = rem / IPS_SECSPERHOUR;
+ rem = rem % IPS_SECSPERHOUR;
+
+ command->minute = rem / IPS_SECSPERMIN;
+ command->second = rem % IPS_SECSPERMIN;
+
+ y = IPS_EPOCH_YEAR;
+ while (days < 0 || days >= (long) year_lengths[yleap = ips_isleap(y)]) {
+ long newy;
+
+ newy = y + days / IPS_DAYSPERNYEAR;
+ if (days < 0)
+ --newy;
+ days -= (newy - y) * IPS_DAYSPERNYEAR +
+ IPS_LEAPS_THRU_END_OF(newy - 1) -
+ IPS_LEAPS_THRU_END_OF(y - 1);
+ y = newy;
+ }
+ command->yearH = y / 100;
+ command->yearL = y % 100;
+ ip = mon_lengths[yleap];
+ for(month = 0; days >= (long) ip[month]; ++month)
+ days = days - (long) ip[month];
+ command->month = month + 1;
+ command->day = days + 1;
+}
+
+static int ips_send_ffdc_reset_cmd(ips_command_t *command)
+{
+ ips_softc_t *sc = command->sc;
+ ips_cmd_status_t *status = command->arg;
+ ips_adapter_ffdc_cmd *command_struct;
+
+ PRINTF(10,"ips test: got a command, building ffdc reset command\n");
+ command->callback = ips_wakeup_callback;
+ command_struct = (ips_adapter_ffdc_cmd *)command->command_buffer;
+ command_struct->command = IPS_FFDC_CMD;
+ command_struct->id = command->id;
+ command_struct->reset_count = sc->ffdc_resetcount;
+ command_struct->reset_type = 0x80;
+ ips_ffdc_settime(command_struct, sc->ffdc_resettime.tv_sec);
+
+ bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
+ BUS_DMASYNC_PREWRITE);
+ mtx_lock(&sc->cmd_mtx);
+ sc->ips_issue_cmd(command);
+ if (status->value != IPS_ERROR_STATUS)
+ msleep(status, &sc->cmd_mtx, 0, "ffdc", 0);
+ mtx_unlock(&sc->cmd_mtx);
+ ips_insert_free_cmd(sc, command);
+ return 0;
+}
+
+int ips_ffdc_reset(ips_softc_t *sc)
+{
+ ips_cmd_status_t *status;
+ status = malloc(sizeof(ips_cmd_status_t), M_DEVBUF, M_NOWAIT|M_ZERO);
+ if(!status)
+ return ENOMEM;
+ if(ips_get_free_cmd(sc, ips_send_ffdc_reset_cmd, status,
+ IPS_NOWAIT_FLAG)){
+ free(status, M_DEVBUF);
+ device_printf(sc->dev, "ERROR: unable to get a command! can't send ffdc reset!\n");
+ }
+ if(COMMAND_ERROR(status)){
+ device_printf(sc->dev, "ERROR: ffdc reset command failed!\n");
+ }
+ free(status, M_DEVBUF);
+ return 0;
+}
+
static void ips_write_nvram(ips_command_t *command){
ips_softc_t *sc = command->sc;
ips_rw_nvram_cmd *command_struct;
@@ -449,6 +536,9 @@ static void ips_write_nvram(ips_command_t *command){
bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
BUS_DMASYNC_POSTREAD);
nvram = command->data_buffer;
+ /* retrieve adapter info and save in sc */
+ sc->adapter_type = nvram->adapter_type;
+
strncpy(nvram->driver_high, IPS_VERSION_MAJOR, 4);
strncpy(nvram->driver_low, IPS_VERSION_MINOR, 4);
nvram->operating_system = IPS_OS_FREEBSD;
OpenPOWER on IntegriCloud