#include <asm/chsc.h>
 #include <asm/crw.h>
 #include <asm/isc.h>
+#include <asm/ebcdic.h>
 
 #include "css.h"
 #include "cio.h"
        css_schedule_reprobe();
 }
 
-static int
-__get_chpid_from_lir(void *data)
-{
-       struct lir {
-               u8  iq;
-               u8  ic;
-               u16 sci;
-               /* incident-node descriptor */
-               u32 indesc[28];
-               /* attached-node descriptor */
-               u32 andesc[28];
-               /* incident-specific information */
-               u32 isinfo[28];
-       } __attribute__ ((packed)) *lir;
-
-       lir = data;
-       if (!(lir->iq&0x80))
-               /* NULL link incident record */
-               return -EINVAL;
-       if (!(lir->indesc[0]&0xc0000000))
-               /* node descriptor not valid */
-               return -EINVAL;
-       if (!(lir->indesc[0]&0x10000000))
-               /* don't handle device-type nodes - FIXME */
-               return -EINVAL;
-       /* Byte 3 contains the chpid. Could also be CTCA, but we don't care */
-
-       return (u16) (lir->indesc[0]&0x000000ff);
-}
-
 struct chsc_sei_nt0_area {
        u8  flags;
        u8  vf;                         /* validity flags */
        } u;
 } __packed;
 
+/*
+ * Node Descriptor as defined in SA22-7204, "Common I/O-Device Commands"
+ */
+
+#define ND_VALIDITY_VALID      0
+#define ND_VALIDITY_OUTDATED   1
+#define ND_VALIDITY_INVALID    2
+
+struct node_descriptor {
+       /* Flags. */
+       union {
+               struct {
+                       u32 validity:3;
+                       u32 reserved:5;
+               } __packed;
+               u8 byte0;
+       } __packed;
+
+       /* Node parameters. */
+       u32 params:24;
+
+       /* Node ID. */
+       char type[6];
+       char model[3];
+       char manufacturer[3];
+       char plant[2];
+       char seq[12];
+       u16 tag;
+} __packed;
+
+/*
+ * Link Incident Record as defined in SA22-7202, "ESCON I/O Interface"
+ */
+
+#define LIR_IQ_CLASS_INFO              0
+#define LIR_IQ_CLASS_DEGRADED          1
+#define LIR_IQ_CLASS_NOT_OPERATIONAL   2
+
+struct lir {
+       struct {
+               u32 null:1;
+               u32 reserved:3;
+               u32 class:2;
+               u32 reserved2:2;
+       } __packed iq;
+       u32 ic:8;
+       u32 reserved:16;
+       struct node_descriptor incident_node;
+       struct node_descriptor attached_node;
+       u8 reserved2[32];
+} __packed;
+
+#define PARAMS_LEN     10      /* PARAMS=xx,xxxxxx */
+#define NODEID_LEN     35      /* NODEID=tttttt/mdl,mmm.ppssssssssssss,xxxx */
+
+/* Copy EBCIDC text, convert to ASCII and optionally add delimiter. */
+static char *store_ebcdic(char *dest, const char *src, unsigned long len,
+                         char delim)
+{
+       memcpy(dest, src, len);
+       EBCASC(dest, len);
+
+       if (delim)
+               dest[len++] = delim;
+
+       return dest + len;
+}
+
+/* Format node ID and parameters for output in LIR log message. */
+static void format_node_data(char *params, char *id, struct node_descriptor *nd)
+{
+       memset(params, 0, PARAMS_LEN);
+       memset(id, 0, NODEID_LEN);
+
+       if (nd->validity != ND_VALIDITY_VALID) {
+               strncpy(params, "n/a", PARAMS_LEN - 1);
+               strncpy(id, "n/a", NODEID_LEN - 1);
+               return;
+       }
+
+       /* PARAMS=xx,xxxxxx */
+       snprintf(params, PARAMS_LEN, "%02x,%06x", nd->byte0, nd->params);
+       /* NODEID=tttttt/mdl,mmm.ppssssssssssss,xxxx */
+       id = store_ebcdic(id, nd->type, sizeof(nd->type), '/');
+       id = store_ebcdic(id, nd->model, sizeof(nd->model), ',');
+       id = store_ebcdic(id, nd->manufacturer, sizeof(nd->manufacturer), '.');
+       id = store_ebcdic(id, nd->plant, sizeof(nd->plant), 0);
+       id = store_ebcdic(id, nd->seq, sizeof(nd->seq), ',');
+       sprintf(id, "%04X", nd->tag);
+}
+
 static void chsc_process_sei_link_incident(struct chsc_sei_nt0_area *sei_area)
 {
-       struct chp_id chpid;
-       int id;
+       struct lir *lir = (struct lir *) &sei_area->ccdf;
+       char iuparams[PARAMS_LEN], iunodeid[NODEID_LEN], auparams[PARAMS_LEN],
+            aunodeid[NODEID_LEN];
 
-       CIO_CRW_EVENT(4, "chsc: link incident (rs=%02x, rs_id=%04x)\n",
-                     sei_area->rs, sei_area->rsid);
-       if (sei_area->rs != 4)
+       CIO_CRW_EVENT(4, "chsc: link incident (rs=%02x, rs_id=%04x, iq=%02x)\n",
+                     sei_area->rs, sei_area->rsid, sei_area->ccdf[0]);
+
+       /* Ignore NULL Link Incident Records. */
+       if (lir->iq.null)
                return;
-       id = __get_chpid_from_lir(sei_area->ccdf);
-       if (id < 0)
-               CIO_CRW_EVENT(4, "chsc: link incident - invalid LIR\n");
-       else {
-               chp_id_init(&chpid);
-               chpid.id = id;
-               chsc_chp_offline(chpid);
+
+       /* Inform user that a link requires maintenance actions because it has
+        * become degraded or not operational. Note that this log message is
+        * the primary intention behind a Link Incident Record. */
+
+       format_node_data(iuparams, iunodeid, &lir->incident_node);
+       format_node_data(auparams, aunodeid, &lir->attached_node);
+
+       switch (lir->iq.class) {
+       case LIR_IQ_CLASS_DEGRADED:
+               pr_warn("Link degraded: RS=%02x RSID=%04x IC=%02x "
+                       "IUPARAMS=%s IUNODEID=%s AUPARAMS=%s AUNODEID=%s\n",
+                       sei_area->rs, sei_area->rsid, lir->ic, iuparams,
+                       iunodeid, auparams, aunodeid);
+               break;
+       case LIR_IQ_CLASS_NOT_OPERATIONAL:
+               pr_err("Link stopped: RS=%02x RSID=%04x IC=%02x "
+                      "IUPARAMS=%s IUNODEID=%s AUPARAMS=%s AUNODEID=%s\n",
+                      sei_area->rs, sei_area->rsid, lir->ic, iuparams,
+                      iunodeid, auparams, aunodeid);
+               break;
+       default:
+               break;
        }
 }