NetBSD-Bugs archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
kern/38726: Update spdmem driver for DDR3 memory
>Number: 38726
>Category: kern
>Synopsis: Update spdmem driver for DDR3
>Confidential: no
>Severity: non-critical
>Priority: medium
>Responsible: kern-bug-people
>State: open
>Class: change-request
>Submitter-Id: net
>Arrival-Date: Thu May 22 11:40:00 +0000 2008
>Originator: Paul Goyette
>Release: NetBSD 4.99.62
>Organization:
----------------------------------------------------------------------
| Paul Goyette | PGP DSS Key fingerprint: | E-mail addresses: |
| Customer Service | FA29 0E3B 35AF E8AE 6651 | paul%whooppee.com@localhost |
| Network Engineer | 0786 F758 55DE 53BA 7731 | pgoyette%juniper.net@localhost |
----------------------------------------------------------------------
>Environment:
System: NetBSD quicky.whooppee.com 4.99.62 NetBSD 4.99.62 (QUICKY (ASUS M2N32
WS) 2008-05-14 13:20:42) #1: Wed May 14 06:25:08 PDT 2008
paul%speedy.whooppee.com@localhost:/build/obj/amd64/sys/arch/amd64/compile/QUICKY
amd64
Architecture: x86_64
Machine: amd64
>Description:
The spdmem driver currently does not recognize DDR3 SDRAM memory
modules.
>How-To-Repeat:
>Fix:
The following patch provides initial support of DDR3 memory.
Note: It has been compile-tested on both i386 and amd64, but
since I don't have any DDR3 memory yet I cannot be certain that
it works perfectly. It has NOT had any negative regressions on
recognizing my existing DDR2 memory.
Index: src/sys/dev/i2c/spdmem.c
===================================================================
RCS file: /cvsroot/src/sys/dev/i2c/spdmem.c,v
retrieving revision 1.8
diff -u -p -r1.8 spdmem.c
--- src/sys/dev/i2c/spdmem.c 4 May 2008 15:26:29 -0000 1.8
+++ src/sys/dev/i2c/spdmem.c 22 May 2008 11:32:17 -0000
@@ -68,7 +68,8 @@ static const char* spdmem_basic_types[]
"DDR SDRAM",
"DDR2 SDRAM",
"DDR2 SDRAM FB",
- "DDR2 SDRAM FB Probe"
+ "DDR2 SDRAM FB Probe",
+ "DDR3 SDRAM"
};
static const char* spdmem_superset_types[] = {
@@ -116,13 +117,34 @@ static const uint8_t spdmem_cycle_frac[]
/* sysctl stuff */
static int hw_node = CTL_EOL;
+/* CRC functions used for certain memory types */
+
+static uint8_t spdcrc16 (struct spdmem_softc *sc, int count)
+{
+ uint16_t crc;
+ int i, j;
+ uint8_t val;
+ crc = 0;
+ for (j = 0; j <= count; j++) {
+ val = spdmem_read(sc, j);
+ crc = crc ^ val << 8;
+ for (i = 0; i < 8; ++i)
+ if (crc & 0x8000)
+ crc = crc << 1 ^ 0x1021;
+ else
+ crc = crc << 1;
+ }
+ return (crc & 0xFFFF);
+}
static int
spdmem_match(device_t parent, cfdata_t match, void *aux)
{
struct i2c_attach_args *ia = aux;
struct spdmem_softc sc;
int cksum = 0;
- uint8_t i, val;
+ uint8_t i, val, spd_type;
+ int spd_len, spd_crc_cover;
+ int crc_calc, crc_spd;
if (ia->ia_addr < 0x50 || ia->ia_addr > 0x57)
return 0;
@@ -130,15 +152,63 @@ spdmem_match(device_t parent, cfdata_t m
sc.sc_tag = ia->ia_tag;
sc.sc_addr = ia->ia_addr;
- for (i = 0; i < 63; i++)
- cksum += spdmem_read(&sc, i);
+ spd_type = spdmem_read(&sc, 2);
- val = spdmem_read(&sc, 63);
+ /* For older memory types, validate the checksum over 1st 63 bytes */
+ if (spd_type < SPDMEM_MEMTYPE_DDR3SDRAM &&
+ spd_type != SPDMEM_MEMTYPE_FBDIMM) {
+ for (i = 0; i < 63; i++)
+ cksum += spdmem_read(&sc, i);
+
+ val = spdmem_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;
+ } else
+ return 1;
+ }
- if (cksum == 0 || (cksum & 0xff) != val)
- return 0;
+ /* For DDR3, verify the CRC */
+ if (spd_type == SPDMEM_MEMTYPE_DDR3SDRAM ||
+ spd_type == SPDMEM_MEMTYPE_FBDIMM) {
+ spd_len = spdmem_read(&sc, 0);
+ if (spd_len && SPDMEM_SPDCRC_116)
+ spd_crc_cover = 116;
+ else
+ spd_crc_cover = 125;
+ switch (spd_len & SPDMEM_SPDLEN_MASK) {
+ case SPDMEM_SPDLEN_128:
+ spd_len = 128;
+ break;
+ case SPDMEM_SPDLEN_176:
+ spd_len = 176;
+ break;
+ case SPDMEM_SPDLEN_256:
+ spd_len = 256;
+ break;
+ default:
+ return 0;
+ }
+ 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);
+ 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);
+ return 0;
+ }
+ return 1;
+ }
- return 1;
+ /* For unrecognized memory types, don't match at all */
+ return 0;
}
static void
@@ -155,22 +225,60 @@ spdmem_attach(device_t parent, device_t
int per_chip = 0;
int dimm_size, cycle_time, d_clk, p_clk, bits;
int i;
+ unsigned int spd_len, spd_size;
const struct sysctlnode *node = NULL;
sc->sc_tag = ia->ia_tag;
sc->sc_addr = ia->ia_addr;
- /* All SPD have at least 64 bytes of data including checksum */
- for (i = 0; i < 64; i++) {
+ /*
+ * FBDIMM and DDR3 (and probably all newer) have a different
+ * encoding of the SPD EEPROM used/total sizes
+ */
+ if (sc->sc_spd_data.sm_type == SPDMEM_MEMTYPE_FBDIMM ||
+ sc->sc_spd_data.sm_type == SPDMEM_MEMTYPE_DDR3SDRAM) {
+ switch (sc->sc_spd_data.sm_len & SPDMEM_SPDSIZE_MASK) {
+ case SPDMEM_SPDSIZE_256:
+ spd_size = 256;
+ break;
+ default:
+ spd_size = 0;
+ break;
+ }
+ switch (sc->sc_spd_data.sm_len & SPDMEM_SPDLEN_MASK) {
+ case SPDMEM_SPDLEN_128:
+ spd_len = 128;
+ break;
+ case SPDMEM_SPDLEN_176:
+ spd_len = 176;
+ break;
+ case SPDMEM_SPDLEN_256:
+ spd_len = 256;
+ break;
+ default:
+ spd_len = 64;
+ break;
+ }
+ } else if (sc->sc_spd_data.sm_type < SPDMEM_MEMTYPE_DDR3SDRAM &&
+ sc->sc_spd_data.sm_type != SPDMEM_MEMTYPE_FBDIMM) {
+ spd_len = sc->sc_spd_data.sm_len;
+ spd_size = 1 << sc->sc_spd_data.sm_size;
+ } else
+ spd_size = 0;
+ if (spd_len > spd_size)
+ spd_len = spd_size;
+ if (spd_len > sizeof(struct spdmem))
+ spd_len = sizeof(struct spdmem);
+ for (i = 0; i < spd_len; i++)
((uint8_t *)s)[i] = spdmem_read(sc, i);
- }
#ifdef DEBUG
- for (i = 0; i < 64; i += 16) {
- int j;
+ for (i = 0; i < spd_len; i += 16) {
+ int j, k;
aprint_debug("\n");
aprint_debug_dev(self, "0x%02x:", i);
- for(j = 0; j < 16; j++)
+ k = (spd_len - i < 16) ? spd_len - i : 16;
+ for(j = 0; j < k; j++)
aprint_debug(" %02x", ((uint8_t *)s)[i + j]);
}
aprint_debug("\n");
@@ -185,21 +293,21 @@ spdmem_attach(device_t parent, device_t
0, CTLTYPE_NODE,
device_xname(self), NULL, NULL, 0, NULL, 0,
CTL_HW, CTL_CREATE, CTL_EOL);
- if (node != NULL)
+ if (node != NULL && spd_len != 0)
sysctl_createv(NULL, 0, NULL, NULL,
0,
CTLTYPE_STRUCT, "spd_data",
- SYSCTL_DESCR("raw spd data (first 64 bytes)"), NULL,
- 0, s, sizeof(*s),
+ SYSCTL_DESCR("raw spd data"), NULL,
+ 0, s, spd_len,
CTL_HW, node->sysctl_num, CTL_CREATE, CTL_EOL);
/*
- * Decode and print SPD contents
+ * Decode and print key SPD contents
*/
if (IS_RAMBUS_TYPE)
type = "Rambus";
else {
- if (s->sm_type <= 10)
+ if (s->sm_type <= __arraycount(spdmem_basic_types))
type = spdmem_basic_types[s->sm_type];
else
type = "unknown memory type";
@@ -234,8 +342,10 @@ spdmem_attach(device_t parent, device_t
if ((s->sm_type == SPDMEM_MEMTYPE_SDRAM ||
s->sm_type == SPDMEM_MEMTYPE_DDRSDRAM ||
s->sm_type == SPDMEM_MEMTYPE_DDR2SDRAM ) &&
- s->sm_config < 8)
+ s->sm_config < __arraycount(spdmem_parity_types))
aprint_normal(", %s", spdmem_parity_types[s->sm_config]);
+ else if (s->sm_type == SPDMEM_MEMTYPE_DDR3SDRAM)
+ aprint_normal(", %sECC", s->sm_ddr3.ddr3_hasECC?"":"no ");
dimm_size = 0;
if (IS_RAMBUS_TYPE) {
@@ -253,6 +363,21 @@ spdmem_attach(device_t parent, device_t
dimm_size = s->sm_ddr2.ddr2_rows + s->sm_ddr2.ddr2_cols - 17;
num_banks = s->sm_ddr2.ddr2_ranks + 1;
per_chip = s->sm_ddr2.ddr2_banks_per_chip;
+ } else if (s->sm_type == SPDMEM_MEMTYPE_DDR3SDRAM) {
+ /*
+ * DDR3 size specification is quite different from DDR2
+ *
+ * Module capacity is defined as
+ * Chip_Capacity_in_bits / 8bits-per-byte
+ * * external_bus_width
+ * / internal_bus_width
+ * We further divide by 2**20 to get our answer in MB
+ */
+ dimm_size = s->sm_ddr3.ddr3_chipsize + 28 - 20 - 3 +
+ s->sm_ddr3.ddr3_datawidth + 1 -
+ s->sm_ddr3.ddr3_chipwidth -2;
+ num_banks = s->sm_ddr3.ddr3_physbanks;
+ per_chip = 1;
}
if (!(IS_RAMBUS_TYPE) && num_banks <= 8 && per_chip <= 8 &&
dimm_size > 0 && dimm_size <= 12) {
@@ -278,6 +403,11 @@ spdmem_attach(device_t parent, device_t
cycle_time = s->sm_ddr2.ddr2_cycle_whole * 100 +
spdmem_cycle_frac[s->sm_ddr2.ddr2_cycle_frac];
}
+ else if (s->sm_type == SPDMEM_MEMTYPE_DDR3SDRAM) {
+ cycle_time = ((1000 * s->sm_ddr3.ddr3_mtb_dividend + 5) /
+ s->sm_ddr3.ddr3_mtb_divisor) / 10;
+ cycle_time *= s->sm_ddr3.ddr3_tCK_min;
+ }
if (cycle_time != 0) {
/*
@@ -286,7 +416,12 @@ spdmem_attach(device_t parent, device_t
* of cycles per microsecond.
*/
d_clk = 100 * 1000;
- if (s->sm_type == SPDMEM_MEMTYPE_DDR2SDRAM) {
+ if (s->sm_type == SPDMEM_MEMTYPE_DDR3SDRAM) {
+ /* DDR3 uses an 8X-pumped clock */
+ d_clk *= 8;
+ bits = 1 << (s->sm_ddr3.ddr3_datawidth + 3);
+ ddr_type_string = "PC3";
+ } else if (s->sm_type == SPDMEM_MEMTYPE_DDR2SDRAM) {
/* DDR2 uses quad-pumped clock */
d_clk *= 4;
bits = s->sm_ddr2.ddr2_datawidth;
@@ -317,6 +452,8 @@ spdmem_attach(device_t parent, device_t
d_clk /= cycle_time;
if (s->sm_type == SPDMEM_MEMTYPE_DDR2SDRAM)
d_clk = (d_clk + 1) / 2;
+ if (s->sm_type == SPDMEM_MEMTYPE_DDR3SDRAM)
+ d_clk = (d_clk + 2) / 4;
p_clk = d_clk * bits / 8;
if ((p_clk % 100) >= 50)
p_clk += 50;
@@ -373,25 +510,35 @@ spdmem_attach(device_t parent, device_t
s->sm_ddr2.ddr2_cycle_whole,
spdmem_cycle_frac[s->sm_ddr2.ddr2_cycle_frac]);
break;
+ case SPDMEM_MEMTYPE_DDR3SDRAM:
+ aprint_verbose(
+ "%d rows, %d cols, %d log banks, %d phys banks, "
+ "%d.%02dns cycle time\n",
+ s->sm_ddr3.ddr3_rows, s->sm_ddr3.ddr3_cols,
+ s->sm_ddr3.ddr3_logbanks, s->sm_ddr3.ddr3_physbanks,
+ cycle_time/100, cycle_time % 100);
+ break;
default:
break;
}
- if (s->sm_voltage <= 0x5)
- voltage = spdmem_voltage_types[s->sm_voltage];
- else
- voltage = "unknown";
-
- if (s->sm_refresh <= 0x05)
- refresh = spdmem_refresh_types[s->sm_refresh];
- else
- refresh = "unknown";
-
- aprint_verbose_dev(self, "voltage %s, refresh time %s",
- voltage, refresh);
- if (s->sm_selfrefresh)
- aprint_verbose(" (self-refreshing)");
- aprint_verbose("\n");
+ if (s->sm_type < SPDMEM_MEMTYPE_DDR3SDRAM) {
+ if (s->sm_voltage <= __arraycount(spdmem_voltage_types))
+ voltage = spdmem_voltage_types[s->sm_voltage];
+ else
+ voltage = "unknown";
+
+ if (s->sm_refresh <= __arraycount(spdmem_refresh_types))
+ refresh = spdmem_refresh_types[s->sm_refresh];
+ else
+ refresh = "unknown";
+
+ aprint_verbose_dev(self, "voltage %s, refresh time %s",
+ voltage, refresh);
+ if (s->sm_selfrefresh)
+ aprint_verbose(" (self-refreshing)");
+ aprint_verbose("\n");
+ }
if (!pmf_device_register(self, NULL, NULL))
aprint_error_dev(self, "couldn't establish power handler\n");
Index: src/sys/dev/i2c/spdmemreg.h
===================================================================
RCS file: /cvsroot/src/sys/dev/i2c/spdmemreg.h,v
retrieving revision 1.1
diff -u -p -r1.1 spdmemreg.h
--- src/sys/dev/i2c/spdmemreg.h 18 Aug 2007 11:26:37 -0000 1.1
+++ src/sys/dev/i2c/spdmemreg.h 22 May 2008 11:32:17 -0000
@@ -37,6 +37,21 @@
#define SPDMEM_MEMTYPE_DDRSGRAM 0x06
#define SPDMEM_MEMTYPE_DDRSDRAM 0x07
#define SPDMEM_MEMTYPE_DDR2SDRAM 0x08
+#define SPDMEM_MEMTYPE_FBDIMM 0x09
+#define SPDMEM_MEMTYPE_FBDIMM_PROBE 0x0A
+#define SPDMEM_MEMTYPE_DDR3SDRAM 0x0B
+
+/* Encodings of the size used/total byte for certain memory types */
+#define SPDMEM_SPDSIZE_256 0x01 /* SPD Bytes written */
+#define SPDMEM_SPDSIZE_MASK 0x0F /* Bits 0 - 3 */
+
+#define SPDMEM_SPDLEN_128 0x10 /* SPD EEPROM Sizes */
+#define SPDMEM_SPDLEN_176 0x20
+#define SPDMEM_SPDLEN_256 0x30
+#define SPDMEM_SPDLEN_MASK 0x70 /* Bits 4 - 6 */
+
+#define SPDMEM_SPDCRC_116 0x80 /* CRC Bytes covered */
+#define SPDMEM_SPDCRC_125 0x00
/* possible values for the supply voltage */
#define SPDMEM_VOLTAGE_TTL_5V 0x00
Index: src/sys/dev/i2c/spdmemvar.h
===================================================================
RCS file: /cvsroot/src/sys/dev/i2c/spdmemvar.h,v
retrieving revision 1.3
diff -u -p -r1.3 spdmemvar.h
--- src/sys/dev/i2c/spdmemvar.h 4 May 2008 15:26:29 -0000 1.3
+++ src/sys/dev/i2c/spdmemvar.h 22 May 2008 11:32:17 -0000
@@ -53,6 +53,7 @@ struct spdmem_fpm { /* FPM and EDO
DI
uint8_t fpm_unused2[17];
uint8_t fpm_superset;
uint8_t fpm_unused3[30];
+ uint8_t fpm_cksum;
} __packed;
struct spdmem_sdram { /* PC66/PC100/PC133 SDRAM */
@@ -115,10 +116,11 @@ struct spdmem_sdram { /*
PC66/PC100/P
uint8_t sdr_tDH;
uint8_t sdr_unused2[5];
uint8_t sdr_tRC;
- uint8_t sdr_unused3[20];
+ uint8_t sdr_unused3[18];
uint8_t sdr_esdram;
uint8_t sdr_super_tech;
uint8_t sdr_spdrev;
+ uint8_t sdr_cksum;
} __packed;
struct spdmem_rom {
@@ -136,6 +138,7 @@ struct spdmem_rom {
uint8_t rom_burstlength;
uint8_t rom_unused2[14];
uint8_t rom_superset[31];
+ uint8_t rom_cksum;
} __packed;
@@ -206,6 +209,7 @@ struct spdmem_ddr { /* Dual Data
Rate
uint8_t ddr_unused3;
uint8_t ddr_height;
uint8_t ddr_unused4[15];
+ uint8_t ddr_cksum;
} __packed;
struct spdmem_ddr2 { /* Dual Data Rate 2 SDRAM */
@@ -292,6 +296,7 @@ struct spdmem_ddr2 { /* Dual
Data Rat
uint8_t ddr2_dt_PLL_Active;
uint8_t ddr2_dt_Reg_Active;
uint8_t ddr2_spdrev;
+ uint8_t ddr2_cksum;
} __packed;
struct spdmem_fbdimm { /* Fully-buffered DIMM */
@@ -368,6 +373,7 @@ struct spdmem_fbdimm { /*
Fully-buffe
uint8_t fbdimm_DT5B;
uint8_t fbdimm_DT7;
uint8_t fbdimm_unused4[21];
+ uint8_t fbdimm_cksum;
} __packed;
struct spdmem_rambus { /* Direct Rambus DRAM */
@@ -377,8 +383,78 @@ struct spdmem_rambus { /*
Direct Ramb
);
} __packed;
+struct spdmem_ddr3 { /* Dual Data Rate 3 SDRAM */
+ uint8_t ddr3_mod_type;
+ SPD_BITFIELD( \
+ uint8_t ddr3_chipsize:5, \
+ uint8_t ddr3_logbanks:3, , \
+ );
+ /* cols is offset by 9, rows offset by 12 */
+ SPD_BITFIELD( \
+ uint8_t ddr3_cols:3, \
+ uint8_t ddr3_rows:5, , \
+ );
+ uint8_t ddr3_unused2;
+ /* chipwidth in bits offset by 2 - 0 = X4, 1 = X8, 2 = X16 */
+ /* physbanks is offset by 1 */
+ SPD_BITFIELD( \
+ uint8_t ddr3_chipwidth:3, \
+ uint8_t ddr3_physbanks:5, , \
+ );
+ /* datawidth in bits offset by 3 - 1 = 16b, 2 = 32b, 3 = 64b */
+ SPD_BITFIELD( \
+ uint8_t ddr3_datawidth:7, \
+ uint8_t ddr3_hasECC:1, , \
+ );
+ uint8_t ddr3_ftb; /* 0x52 = 2.5ps, 0x55 = 5.0ps */
+ uint8_t ddr3_mtb_dividend; /* 0x0108 = 0.1250ns */
+ uint8_t ddr3_mtb_divisor; /* 0x010f = 0.0625ns */
+ uint8_t ddr3_tCK_min; /* in terms of mtb */
+ uint8_t ddr3_unused3;
+ uint16_t ddr3_CAS_sup;
+ uint8_t ddr3_tAAmin; /* in terms of mtb */
+ uint8_t ddr3_tWRmin;
+ uint8_t ddr3_tRCDmin;
+ uint8_t ddr3_tRRDmin;
+ uint8_t ddr3_tRPmin;
+ SPD_BITFIELD( \
+ uint8_t ddr3_tRAS_msb:4, \
+ uint8_t ddr3_tRC_msb:4, , \
+ );
+ uint8_t ddr3_tRAS_lsb;
+ uint8_t ddr3_tRC_lsb;
+ uint8_t ddr3_tRFCmin_lsb;
+ uint8_t ddr3_tRFCmin_msb;
+ uint8_t ddr3_tWTRmin;
+ uint8_t ddr3_tRTPmin;
+ SPD_BITFIELD( \
+ uint8_t ddr3_tFAW_msb:4, , , \
+ );
+ uint8_t ddr3_tFAW_lsb;
+ uint8_t ddr3_output_drvrs;
+ SPD_BITFIELD( \
+ uint8_t ddr3_ext_temp_range:1, \
+ uint8_t ddr3_unused6:1, \
+ uint8_t ddr3_asr_refresh:1, \
+ uint8_t ddr3_unused7:5 \
+ );
+ uint8_t ddr3_unused4[28];
+ uint8_t ddr3_mod_height;
+ uint8_t ddr3_mod_thickness;
+ uint8_t ddr3_ref_card;
+ uint8_t ddr3_mapping;
+ uint8_t ddr3_unused5[53];
+ uint8_t ddr3_mfgID_lsb;
+ uint8_t ddr3_mfgID_msb;
+ uint8_t ddr3_mfgloc;
+ uint8_t ddr3_mfg_year;
+ uint8_t ddr3_mfg_week;
+ uint8_t ddr3_serial[4];
+ uint16_t ddr3_crc;
+} __packed;
+
struct spdmem {
- uint8_t sm_len;
+ uint8_t sm_len;
uint8_t sm_size;
uint8_t sm_type;
union {
@@ -389,19 +465,23 @@ struct spdmem {
struct spdmem_sdram u1_sdr;
struct spdmem_rambus u1_rdr;
struct spdmem_rom u1_rom;
+ struct spdmem_ddr3 u1_ddr3;
} sm_u1;
+ uint8_t sm_extension[128];
+} __packed;
#define sm_fbd sm_u1.u1_fbd
#define sm_fpm sm_u1.u1_fpm
#define sm_ddr sm_u1.u1_ddr
#define sm_ddr2 sm_u1.u1_ddr2
#define sm_rdr sm_u1.u1_rdr
#define sm_rom sm_u1.u1_rom
+#define sm_ddr3 sm_u1.u1_ddr3
#define sm_sdr sm_u1.u1_sdr
- uint8_t sm_cksum;
-} __packed;
+
/* some fields are in the same place for all memory types */
+#define sm_cksum sm_fpm.fpm_cksum
#define sm_config sm_fpm.fpm_config
#define sm_voltage sm_fpm.fpm_voltage
#define sm_refresh sm_fpm.fpm_refresh
>Unformatted:
Home |
Main Index |
Thread Index |
Old Index