From 6a5be8627cd58dd879210811dab5212e2d35bc7d Mon Sep 17 00:00:00 2001
From: Doug Rabson <dfr@FreeBSD.org>
Date: Tue, 18 Jul 2000 09:01:09 +0000
Subject: [PATCH] Add smc37c935 chipset support and clean up the code which
 tries to allocate a short port range in some alpha configurations.

Submitted by: "Andrew M. Miklic" <miklic@udlkern.fc.hp.com>,
	      Mark Abene <phiber@radicalmedia.com>
---
 sys/dev/ppc/ppc.c    | 140 +++++++++++++++++++++++++++++++++----------
 sys/dev/ppc/ppcreg.h |  31 +++++++++-
 sys/isa/isareg.h     |   3 +-
 sys/isa/ppc.c        | 140 +++++++++++++++++++++++++++++++++----------
 sys/isa/ppcreg.h     |  31 +++++++++-
 5 files changed, 280 insertions(+), 65 deletions(-)

diff --git a/sys/dev/ppc/ppc.c b/sys/dev/ppc/ppc.c
index c6818a2712cf..ab25faf9daf1 100644
--- a/sys/dev/ppc/ppc.c
+++ b/sys/dev/ppc/ppc.c
@@ -112,7 +112,8 @@ static driver_t ppc_driver = {
   
 static char *ppc_models[] = {
 	"SMC-like", "SMC FDC37C665GT", "SMC FDC37C666GT", "PC87332", "PC87306",
-	"82091AA", "Generic", "W83877F", "W83877AF", "Winbond", "PC87334", 0
+	"82091AA", "Generic", "W83877F", "W83877AF", "Winbond", "PC87334",
+	"SMC FDC37C935", 0
 };
 
 /* list of available modes */
@@ -882,6 +883,91 @@ end_detect:
 	return (chipset_mode);
 }
 
+/*
+ * SMC FDC37C935 configuration
+ * Found on many Alpha machines
+ */
+static int
+ppc_smc37c935_detect(struct ppc_data *ppc, int chipset_mode)
+{
+	int s;
+	int type = -1;
+
+	s = splhigh();
+	outb(SMC935_CFG, 0x55); /* enter config mode */
+	outb(SMC935_CFG, 0x55);
+	splx(s);
+
+	outb(SMC935_IND, SMC935_ID); /* check device id */
+	if (inb(SMC935_DAT) == 0x2)
+		type = SMC_37C935;
+
+	if (type == -1) {
+		outb(SMC935_CFG, 0xaa); /* exit config mode */
+		return (-1);
+	}
+
+	ppc->ppc_model = type;
+
+	outb(SMC935_IND, SMC935_LOGDEV); /* select parallel port, */
+	outb(SMC935_DAT, 3);             /* which is logical device 3 */
+
+	/* set io port base */
+	outb(SMC935_IND, SMC935_PORTHI);
+	outb(SMC935_DAT, (u_char)((ppc->ppc_base & 0xff00) >> 8));
+	outb(SMC935_IND, SMC935_PORTLO);
+	outb(SMC935_DAT, (u_char)(ppc->ppc_base & 0xff));
+
+	if (!chipset_mode)
+		ppc->ppc_avm = PPB_COMPATIBLE; /* default mode */
+	else {
+		ppc->ppc_avm = chipset_mode;
+		outb(SMC935_IND, SMC935_PPMODE);
+		outb(SMC935_DAT, SMC935_CENT); /* start in compatible mode */
+
+		/* SPP + EPP or just plain SPP */
+		if (chipset_mode & (PPB_SPP)) {
+			if (chipset_mode & PPB_EPP) {
+				if (ppc->ppc_epp == EPP_1_9) {
+					outb(SMC935_IND, SMC935_PPMODE);
+					outb(SMC935_DAT, SMC935_EPP19SPP);
+				}
+				if (ppc->ppc_epp == EPP_1_7) {
+					outb(SMC935_IND, SMC935_PPMODE);
+					outb(SMC935_DAT, SMC935_EPP17SPP);
+				}
+			} else {
+				outb(SMC935_IND, SMC935_PPMODE);
+				outb(SMC935_DAT, SMC935_SPP);
+			}
+		}
+
+		/* ECP + EPP or just plain ECP */
+		if (chipset_mode & PPB_ECP) {
+			if (chipset_mode & PPB_EPP) {
+				if (ppc->ppc_epp == EPP_1_9) {
+					outb(SMC935_IND, SMC935_PPMODE);
+					outb(SMC935_DAT, SMC935_ECPEPP19);
+				}
+				if (ppc->ppc_epp == EPP_1_7) {
+					outb(SMC935_IND, SMC935_PPMODE);
+					outb(SMC935_DAT, SMC935_ECPEPP17);
+				}
+			} else {
+				outb(SMC935_IND, SMC935_PPMODE);
+				outb(SMC935_DAT, SMC935_ECP);
+			}
+		}
+	}
+
+	outb(SMC935_CFG, 0xaa); /* exit config mode */
+
+	ppc->ppc_type = PPC_TYPE_SMCLIKE;
+	ppc_smclike_setmode(ppc, chipset_mode);
+
+	return (chipset_mode);
+}
+
 /*
  * Winbond W83877F stuff
  *
@@ -1162,6 +1248,7 @@ ppc_detect(struct ppc_data *ppc, int chipset_mode) {
 		ppc_pc873xx_detect,
 		ppc_smc37c66xgt_detect,
 		ppc_w83877f_detect,
+		ppc_smc37c935_detect,
 		ppc_generic_detect,
 		NULL
 	};
@@ -1744,7 +1831,8 @@ ppc_probe(device_t dev)
 			device_printf(dev, "parallel port not found.\n");
 			return ENXIO;
 		}
-		bus_set_resource(dev, SYS_RES_IOPORT, 0, port, IO_LPTSIZE);
+		bus_set_resource(dev, SYS_RES_IOPORT, 0, port,
+				 IO_LPTSIZE_EXTENDED);
 	}
 #endif
 #ifdef __alpha__
@@ -1752,42 +1840,34 @@ ppc_probe(device_t dev)
 	 * There isn't a bios list on alpha. Put it in the usual place.
 	 */
 	if (error) {
-		bus_set_resource(dev, SYS_RES_IOPORT, 0, 0x3bc, IO_LPTSIZE);
+		bus_set_resource(dev, SYS_RES_IOPORT, 0, 0x3bc,
+				 IO_LPTSIZE_NORMAL);
 	}
 #endif
 
 	/* IO port is mandatory */
+
+	/* Try "extended" IO port range...*/
 	ppc->res_ioport = bus_alloc_resource(dev, SYS_RES_IOPORT,
 					     &ppc->rid_ioport, 0, ~0,
-					     IO_LPTSIZE, RF_ACTIVE);
-	if (ppc->res_ioport == 0) {
-		device_printf(dev, "cannot reserve I/O port range\n");
-		goto error;
-	}
-
-	/* Assume we support the extended IO range of some ppc chipsets...*/
-
-	ppc->rid_extraio = 1;
-	ppc->res_extraio =
-		bus_alloc_resource(dev,
-				   SYS_RES_IOPORT,
-				   &ppc->rid_extraio,
-				   0,
-				   ~0,
-				   IO_LPTSIZE,
-				   RF_ACTIVE);
-
-	/* If we cannot reserve the extra ports for the extended IO range,
-	indicate this with a non-threatening message (this is not an error,
-	so don't treat it as such)... */
-
-	if (ppc->res_extraio == 0) {
-
-		ppc->rid_extraio = 0;
+					     IO_LPTSIZE_EXTENDED, RF_ACTIVE);
 
+	if (ppc->res_ioport != 0) {
 		if (bootverbose)
-			device_printf(dev,
-"This ppc chipset does not support the extended I/O port range...no problem\n");
+			device_printf(dev, "using extended I/O port range\n");
+	} else {
+		/* Failed? If so, then try the "normal" IO port range... */
+		 ppc->res_ioport = bus_alloc_resource(dev, SYS_RES_IOPORT,
+						      &ppc->rid_ioport, 0, ~0,
+						      IO_LPTSIZE_NORMAL,
+						      RF_ACTIVE);
+		if (ppc->res_ioport != 0) {
+			if (bootverbose)
+				device_printf(dev, "using normal I/O port range\n");
+		} else {
+			device_printf(dev, "cannot reserve I/O port range\n");
+			goto error;
+		}
 	}
 
  	ppc->ppc_base = rman_get_start(ppc->res_ioport);
diff --git a/sys/dev/ppc/ppcreg.h b/sys/dev/ppc/ppcreg.h
index ab0bf6f9bdf0..2d2266a868ab 100644
--- a/sys/dev/ppc/ppcreg.h
+++ b/sys/dev/ppc/ppcreg.h
@@ -43,6 +43,7 @@
 #define WINB_W83877AF	8
 #define WINB_UNKNOWN	9
 #define NS_PC87334	10
+#define SMC_37C935	11
 
 /*
  * Parallel Port Chipset Type. SMC versus GENERIC (others)
@@ -100,8 +101,8 @@ struct ppc_data {
 
 	device_t ppbus;		/* parallel port chipset corresponding ppbus */
 
-  	int rid_irq, rid_drq, rid_ioport, rid_extraio;
-	struct resource *res_irq, *res_drq, *res_ioport, *res_extraio;
+  	int rid_irq, rid_drq, rid_ioport;
+	struct resource *res_irq, *res_drq, *res_ioport;
 
 	void *intr_cookie;
 
@@ -205,6 +206,32 @@ struct ppc_data {
 #define SMC_ECP		0x2 		/* ECP */
 #define SMC_ECPEPP	0x3		/* ECP and EPP */
 
+/*
+ * Register defines for the SMC FDC37C935 parts
+ */
+
+/* Configuration ports */
+#define SMC935_CFG	0x370
+#define SMC935_IND	0x370
+#define SMC935_DAT	0x371
+
+/* Registers */
+#define SMC935_LOGDEV	0x7
+#define SMC935_ID	0x20
+#define SMC935_PORTHI	0x60
+#define SMC935_PORTLO	0x61
+#define SMC935_PPMODE	0xf0
+
+/* Parallel port modes */
+#define SMC935_SPP	0x38 + 0
+#define SMC935_EPP19SPP	0x38 + 1
+#define SMC935_ECP	0x38 + 2
+#define SMC935_ECPEPP19	0x38 + 3
+#define SMC935_CENT	0x38 + 4
+#define SMC935_EPP17SPP	0x38 + 5
+#define SMC935_UNUSED	0x38 + 6
+#define SMC935_ECPEPP17	0x38 + 7
+
 /*
  * Register defines for the Winbond W83877F parts
  */
diff --git a/sys/isa/isareg.h b/sys/isa/isareg.h
index 532da43a4d12..e14e71643baf 100644
--- a/sys/isa/isareg.h
+++ b/sys/isa/isareg.h
@@ -164,7 +164,8 @@
    the additional 4 can be used by the specific chipset is now done in the ppc
    code by ppc_probe()... */
 
-#define IO_LPTSIZE	4		/* LPT controllers, Alpha only uses 4 */
+#define IO_LPTSIZE_EXTENDED	8	/* "Extended" LPT controllers */
+#define IO_LPTSIZE_NORMAL	4	/* "Normal" LPT controllers */
 
 #define	IO_MDASIZE	12		/* Monochrome display controllers */
 #define	IO_NPXSIZE	16		/* 80387/80487 NPX registers */
diff --git a/sys/isa/ppc.c b/sys/isa/ppc.c
index c6818a2712cf..ab25faf9daf1 100644
--- a/sys/isa/ppc.c
+++ b/sys/isa/ppc.c
@@ -112,7 +112,8 @@ static driver_t ppc_driver = {
   
 static char *ppc_models[] = {
 	"SMC-like", "SMC FDC37C665GT", "SMC FDC37C666GT", "PC87332", "PC87306",
-	"82091AA", "Generic", "W83877F", "W83877AF", "Winbond", "PC87334", 0
+	"82091AA", "Generic", "W83877F", "W83877AF", "Winbond", "PC87334",
+	"SMC FDC37C935", 0
 };
 
 /* list of available modes */
@@ -882,6 +883,91 @@ end_detect:
 	return (chipset_mode);
 }
 
+/*
+ * SMC FDC37C935 configuration
+ * Found on many Alpha machines
+ */
+static int
+ppc_smc37c935_detect(struct ppc_data *ppc, int chipset_mode)
+{
+	int s;
+	int type = -1;
+
+	s = splhigh();
+	outb(SMC935_CFG, 0x55); /* enter config mode */
+	outb(SMC935_CFG, 0x55);
+	splx(s);
+
+	outb(SMC935_IND, SMC935_ID); /* check device id */
+	if (inb(SMC935_DAT) == 0x2)
+		type = SMC_37C935;
+
+	if (type == -1) {
+		outb(SMC935_CFG, 0xaa); /* exit config mode */
+		return (-1);
+	}
+
+	ppc->ppc_model = type;
+
+	outb(SMC935_IND, SMC935_LOGDEV); /* select parallel port, */
+	outb(SMC935_DAT, 3);             /* which is logical device 3 */
+
+	/* set io port base */
+	outb(SMC935_IND, SMC935_PORTHI);
+	outb(SMC935_DAT, (u_char)((ppc->ppc_base & 0xff00) >> 8));
+	outb(SMC935_IND, SMC935_PORTLO);
+	outb(SMC935_DAT, (u_char)(ppc->ppc_base & 0xff));
+
+	if (!chipset_mode)
+		ppc->ppc_avm = PPB_COMPATIBLE; /* default mode */
+	else {
+		ppc->ppc_avm = chipset_mode;
+		outb(SMC935_IND, SMC935_PPMODE);
+		outb(SMC935_DAT, SMC935_CENT); /* start in compatible mode */
+
+		/* SPP + EPP or just plain SPP */
+		if (chipset_mode & (PPB_SPP)) {
+			if (chipset_mode & PPB_EPP) {
+				if (ppc->ppc_epp == EPP_1_9) {
+					outb(SMC935_IND, SMC935_PPMODE);
+					outb(SMC935_DAT, SMC935_EPP19SPP);
+				}
+				if (ppc->ppc_epp == EPP_1_7) {
+					outb(SMC935_IND, SMC935_PPMODE);
+					outb(SMC935_DAT, SMC935_EPP17SPP);
+				}
+			} else {
+				outb(SMC935_IND, SMC935_PPMODE);
+				outb(SMC935_DAT, SMC935_SPP);
+			}
+		}
+
+		/* ECP + EPP or just plain ECP */
+		if (chipset_mode & PPB_ECP) {
+			if (chipset_mode & PPB_EPP) {
+				if (ppc->ppc_epp == EPP_1_9) {
+					outb(SMC935_IND, SMC935_PPMODE);
+					outb(SMC935_DAT, SMC935_ECPEPP19);
+				}
+				if (ppc->ppc_epp == EPP_1_7) {
+					outb(SMC935_IND, SMC935_PPMODE);
+					outb(SMC935_DAT, SMC935_ECPEPP17);
+				}
+			} else {
+				outb(SMC935_IND, SMC935_PPMODE);
+				outb(SMC935_DAT, SMC935_ECP);
+			}
+		}
+	}
+
+	outb(SMC935_CFG, 0xaa); /* exit config mode */
+
+	ppc->ppc_type = PPC_TYPE_SMCLIKE;
+	ppc_smclike_setmode(ppc, chipset_mode);
+
+	return (chipset_mode);
+}
+
 /*
  * Winbond W83877F stuff
  *
@@ -1162,6 +1248,7 @@ ppc_detect(struct ppc_data *ppc, int chipset_mode) {
 		ppc_pc873xx_detect,
 		ppc_smc37c66xgt_detect,
 		ppc_w83877f_detect,
+		ppc_smc37c935_detect,
 		ppc_generic_detect,
 		NULL
 	};
@@ -1744,7 +1831,8 @@ ppc_probe(device_t dev)
 			device_printf(dev, "parallel port not found.\n");
 			return ENXIO;
 		}
-		bus_set_resource(dev, SYS_RES_IOPORT, 0, port, IO_LPTSIZE);
+		bus_set_resource(dev, SYS_RES_IOPORT, 0, port,
+				 IO_LPTSIZE_EXTENDED);
 	}
 #endif
 #ifdef __alpha__
@@ -1752,42 +1840,34 @@ ppc_probe(device_t dev)
 	 * There isn't a bios list on alpha. Put it in the usual place.
 	 */
 	if (error) {
-		bus_set_resource(dev, SYS_RES_IOPORT, 0, 0x3bc, IO_LPTSIZE);
+		bus_set_resource(dev, SYS_RES_IOPORT, 0, 0x3bc,
+				 IO_LPTSIZE_NORMAL);
 	}
 #endif
 
 	/* IO port is mandatory */
+
+	/* Try "extended" IO port range...*/
 	ppc->res_ioport = bus_alloc_resource(dev, SYS_RES_IOPORT,
 					     &ppc->rid_ioport, 0, ~0,
-					     IO_LPTSIZE, RF_ACTIVE);
-	if (ppc->res_ioport == 0) {
-		device_printf(dev, "cannot reserve I/O port range\n");
-		goto error;
-	}
-
-	/* Assume we support the extended IO range of some ppc chipsets...*/
-
-	ppc->rid_extraio = 1;
-	ppc->res_extraio =
-		bus_alloc_resource(dev,
-				   SYS_RES_IOPORT,
-				   &ppc->rid_extraio,
-				   0,
-				   ~0,
-				   IO_LPTSIZE,
-				   RF_ACTIVE);
-
-	/* If we cannot reserve the extra ports for the extended IO range,
-	indicate this with a non-threatening message (this is not an error,
-	so don't treat it as such)... */
-
-	if (ppc->res_extraio == 0) {
-
-		ppc->rid_extraio = 0;
+					     IO_LPTSIZE_EXTENDED, RF_ACTIVE);
 
+	if (ppc->res_ioport != 0) {
 		if (bootverbose)
-			device_printf(dev,
-"This ppc chipset does not support the extended I/O port range...no problem\n");
+			device_printf(dev, "using extended I/O port range\n");
+	} else {
+		/* Failed? If so, then try the "normal" IO port range... */
+		 ppc->res_ioport = bus_alloc_resource(dev, SYS_RES_IOPORT,
+						      &ppc->rid_ioport, 0, ~0,
+						      IO_LPTSIZE_NORMAL,
+						      RF_ACTIVE);
+		if (ppc->res_ioport != 0) {
+			if (bootverbose)
+				device_printf(dev, "using normal I/O port range\n");
+		} else {
+			device_printf(dev, "cannot reserve I/O port range\n");
+			goto error;
+		}
 	}
 
  	ppc->ppc_base = rman_get_start(ppc->res_ioport);
diff --git a/sys/isa/ppcreg.h b/sys/isa/ppcreg.h
index ab0bf6f9bdf0..2d2266a868ab 100644
--- a/sys/isa/ppcreg.h
+++ b/sys/isa/ppcreg.h
@@ -43,6 +43,7 @@
 #define WINB_W83877AF	8
 #define WINB_UNKNOWN	9
 #define NS_PC87334	10
+#define SMC_37C935	11
 
 /*
  * Parallel Port Chipset Type. SMC versus GENERIC (others)
@@ -100,8 +101,8 @@ struct ppc_data {
 
 	device_t ppbus;		/* parallel port chipset corresponding ppbus */
 
-  	int rid_irq, rid_drq, rid_ioport, rid_extraio;
-	struct resource *res_irq, *res_drq, *res_ioport, *res_extraio;
+  	int rid_irq, rid_drq, rid_ioport;
+	struct resource *res_irq, *res_drq, *res_ioport;
 
 	void *intr_cookie;
 
@@ -205,6 +206,32 @@ struct ppc_data {
 #define SMC_ECP		0x2 		/* ECP */
 #define SMC_ECPEPP	0x3		/* ECP and EPP */
 
+/*
+ * Register defines for the SMC FDC37C935 parts
+ */
+
+/* Configuration ports */
+#define SMC935_CFG	0x370
+#define SMC935_IND	0x370
+#define SMC935_DAT	0x371
+
+/* Registers */
+#define SMC935_LOGDEV	0x7
+#define SMC935_ID	0x20
+#define SMC935_PORTHI	0x60
+#define SMC935_PORTLO	0x61
+#define SMC935_PPMODE	0xf0
+
+/* Parallel port modes */
+#define SMC935_SPP	0x38 + 0
+#define SMC935_EPP19SPP	0x38 + 1
+#define SMC935_ECP	0x38 + 2
+#define SMC935_ECPEPP19	0x38 + 3
+#define SMC935_CENT	0x38 + 4
+#define SMC935_EPP17SPP	0x38 + 5
+#define SMC935_UNUSED	0x38 + 6
+#define SMC935_ECPEPP17	0x38 + 7
+
 /*
  * Register defines for the Winbond W83877F parts
  */