From fba08308f086d7b77f95554df094288fd55903d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20=C5=BBygowski?= Date: Mon, 13 Apr 2020 20:51:32 +0200 Subject: [PATCH] superio/smsc/sch5545: add support for SMSC SCH5545 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The SMSC SCH5545 is very similar to the publicly available datasheet for the SCH5627. TEST=use PS2 keyboard and mouse, serial port, runtime registers and Embedded Memory Interface on Dell Optiplex 9010 Signed-off-by: Michał Żygowski Change-Id: If8a60d5802675f09b08014ed583d2d8afa29fc04 Reviewed-on: https://review.coreboot.org/c/coreboot/+/40350 Reviewed-by: Felix Held Tested-by: build bot (Jenkins) --- src/superio/smsc/Makefile.inc | 1 + src/superio/smsc/sch5545/Kconfig | 4 + src/superio/smsc/sch5545/Makefile.inc | 10 + .../smsc/sch5545/acpi/resource_helpers.asl | 412 ++++++++++++ src/superio/smsc/sch5545/acpi/superio.asl | 589 ++++++++++++++++++ src/superio/smsc/sch5545/sch5545.h | 299 +++++++++ src/superio/smsc/sch5545/sch5545_early_init.c | 179 ++++++ src/superio/smsc/sch5545/sch5545_emi.c | 214 +++++++ src/superio/smsc/sch5545/sch5545_emi.h | 167 +++++ src/superio/smsc/sch5545/superio.c | 301 +++++++++ 10 files changed, 2176 insertions(+) create mode 100644 src/superio/smsc/sch5545/Kconfig create mode 100644 src/superio/smsc/sch5545/Makefile.inc create mode 100644 src/superio/smsc/sch5545/acpi/resource_helpers.asl create mode 100644 src/superio/smsc/sch5545/acpi/superio.asl create mode 100644 src/superio/smsc/sch5545/sch5545.h create mode 100644 src/superio/smsc/sch5545/sch5545_early_init.c create mode 100644 src/superio/smsc/sch5545/sch5545_emi.c create mode 100644 src/superio/smsc/sch5545/sch5545_emi.h create mode 100644 src/superio/smsc/sch5545/superio.c diff --git a/src/superio/smsc/Makefile.inc b/src/superio/smsc/Makefile.inc index 17bdaea6af..9442a9efde 100644 --- a/src/superio/smsc/Makefile.inc +++ b/src/superio/smsc/Makefile.inc @@ -16,3 +16,4 @@ subdirs-y += mec1308 subdirs-y += smscsuperio subdirs-y += sio1036 subdirs-y += sch4037 +subdirs-y += sch5545 diff --git a/src/superio/smsc/sch5545/Kconfig b/src/superio/smsc/sch5545/Kconfig new file mode 100644 index 0000000000..c6b9bae14a --- /dev/null +++ b/src/superio/smsc/sch5545/Kconfig @@ -0,0 +1,4 @@ +## SPDX-License-Identifier: GPL-2.0-only + +config SUPERIO_SMSC_SCH5545 + bool diff --git a/src/superio/smsc/sch5545/Makefile.inc b/src/superio/smsc/sch5545/Makefile.inc new file mode 100644 index 0000000000..2a59f8f529 --- /dev/null +++ b/src/superio/smsc/sch5545/Makefile.inc @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0-only + +bootblock-$(CONFIG_SUPERIO_SMSC_SCH5545) += sch5545_early_init.c +romstage-$(CONFIG_SUPERIO_SMSC_SCH5545) += sch5545_early_init.c + +bootblock-$(CONFIG_SUPERIO_SMSC_SCH5545) += sch5545_emi.c +romstage-$(CONFIG_SUPERIO_SMSC_SCH5545) += sch5545_emi.c +ramstage-$(CONFIG_SUPERIO_SMSC_SCH5545) += sch5545_emi.c + +ramstage-$(CONFIG_SUPERIO_SMSC_SCH5545) += superio.c diff --git a/src/superio/smsc/sch5545/acpi/resource_helpers.asl b/src/superio/smsc/sch5545/acpi/resource_helpers.asl new file mode 100644 index 0000000000..818f470534 --- /dev/null +++ b/src/superio/smsc/sch5545/acpi/resource_helpers.asl @@ -0,0 +1,412 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +/* Helper package for determining IO, DMA, IRQ location according to LDN */ +Name (DCAT, Package (0x10) { + 0x07, /* UARTA */ + 0x08, /* UARTB */ + 0x11, /* LPT */ + 0x0B, /* Floppy */ + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + One, /* KBC */ + 0xFF, + 0xFF, + 0xFF, + One, /* KBC */ + 0xFF +}) + +Method (CGLD, 1, NotSerialized) +{ + Return (DerefOf (DCAT [Arg0])) +} + +/* Return Parallel port mode*/ +Method (LPTM, 1, NotSerialized) +{ + ENTER_CONFIG_MODE (CGLD (Arg0)) + Local0 = (OPT0 & 0x02) + EXIT_CONFIG_MODE () + Return (Local0) +} + +/* Device Status */ +Method (DSTA, 1, NotSerialized) +{ + ENTER_CONFIG_MODE (CGLD (Arg0)) + Local0 = PNP_DEVICE_ACTIVE + If (Local0 == 0xFF) + { + Return (Zero) + } + + Local0 &= One + If (Arg0 < 0x10) + { + IOST |= (Local0 << Arg0) + } + + If (Local0) + { + Return (DEVICE_PRESENT_ACTIVE) + } + ElseIf ((One << Arg0) & IOST) + { + Return (DEVICE_PRESENT_INACTIVE) + } + Else + { + Return (Zero) + } + + EXIT_CONFIG_MODE () +} + +Method (DCNT, 2, NotSerialized) +{ + ENTER_CONFIG_MODE (CGLD (Arg0)) + PNP_DEVICE_ACTIVE = Arg1 + EXIT_CONFIG_MODE () +} + +/* Resource templates for SIO LDNs */ +Name (CRS1, ResourceTemplate () +{ + IO (Decode16, + 0x0000, + 0x0000, + 0x01, + 0x00, + _Y16) + IRQ (Edge, ActiveHigh, Exclusive, _Y14) {} + DMA (Compatibility, NotBusMaster, Transfer8, _Y15) {} +}) + +CreateWordField (CRS1, \_SB.PCI0.LPCB.SIO1._Y14._INT, IRQM) +CreateByteField (CRS1, \_SB.PCI0.LPCB.SIO1._Y15._DMA, DMAM) +CreateWordField (CRS1, \_SB.PCI0.LPCB.SIO1._Y16._MIN, IO11) +CreateWordField (CRS1, \_SB.PCI0.LPCB.SIO1._Y16._MAX, IO12) +CreateByteField (CRS1, \_SB.PCI0.LPCB.SIO1._Y16._LEN, LEN1) + +Name (CRS2, ResourceTemplate () +{ + IO (Decode16, + 0x0000, + 0x0000, + 0x01, + 0x00, + _Y19) + IO (Decode16, + 0x0000, + 0x0000, + 0x01, + 0x00, + _Y1A) + IRQ (Edge, ActiveHigh, Exclusive, _Y17) {} + DMA (Compatibility, NotBusMaster, Transfer8, _Y18) {} +}) + +CreateWordField (CRS2, \_SB.PCI0.LPCB.SIO1._Y17._INT, IRQE) +CreateByteField (CRS2, \_SB.PCI0.LPCB.SIO1._Y18._DMA, DMAE) +CreateWordField (CRS2, \_SB.PCI0.LPCB.SIO1._Y19._MIN, IO21) +CreateWordField (CRS2, \_SB.PCI0.LPCB.SIO1._Y19._MAX, IO22) +CreateByteField (CRS2, \_SB.PCI0.LPCB.SIO1._Y19._LEN, LEN2) +CreateWordField (CRS2, \_SB.PCI0.LPCB.SIO1._Y1A._MIN, IO31) +CreateWordField (CRS2, \_SB.PCI0.LPCB.SIO1._Y1A._MAX, IO32) +CreateByteField (CRS2, \_SB.PCI0.LPCB.SIO1._Y1A._LEN, LEN3) + +/* Read IO resource */ +Method (GIOB, 1, NotSerialized) +{ + If (CGLD (Arg0) == 0x07) /* UARTA */ + { + SWITCH_LDN (SUPERIO_LPC_LDN) + Local0 = (CR6B << 0x08) + Local0 |= CR6A + Return (Local0) + } + + If (CGLD (Arg0) == 0x08) /* UARTB */ + { + SWITCH_LDN (SUPERIO_LPC_LDN) + Local0 = (CR6F << 0x08) + Local0 |= CR6E + Return (Local0) + } + + If (CGLD (Arg0) == 0x11) /* LPT */ + { + SWITCH_LDN (SUPERIO_LPC_LDN) + Local0 = (CR83 << 0x08) + Local0 |= CR82 + Return (Local0) + } + + If (CGLD (Arg0) == 0x0B) /* Floppy */ + { + SWITCH_LDN (SUPERIO_LPC_LDN) + Local0 = (CR7F << 0x08) + Local0 |= CR7E + Return (Local0) + } + + Return (Zero) +} + +/* Read IRQ resource */ +Method (GIRQ, 1, NotSerialized) +{ + SWITCH_LDN (SUPERIO_LPC_LDN) + Local0 = 0x0F /* 15 IRQ regs, 1 for each IRQ number */ + While (Local0) + { + Local1 = (0x40 + Local0) /* IRQ regs begin at offset 0x40 */ + PNP_ADDR_REG = Local1 + Local1 = PNP_DATA_REG + If (CGLD (Arg0) == Local1) + { + Local1 = One + Local0 = (Local1 << Local0) + Return (Local0) + } + + Local0-- + } + + Return (0xFF) +} + +/* Read DMA resource */ +Method (GDMA, 1, NotSerialized) +{ + SWITCH_LDN (SUPERIO_LPC_LDN) + Local0 = 0x03 /* Only DMA Channels 0-3 */ + While (Local0) + { + Local1 = (Local0 << One) + Local1 += 0x51 /* DMA regs begin at offset 0x50 */ + PNP_ADDR_REG = Local1 + Local1 = PNP_DATA_REG + If ((0x80 | CGLD (Arg0)) == Local1) + { + Local1 = One + Local0 = (Local1 << Local0) + Return (Local0) + } + + Local0-- + } + + Return (0xFF) +} + +/* Set IO resource */ +Method (STIO, 2, NotSerialized) +{ + SWITCH_LDN (SUPERIO_LPC_LDN) + Local0 = (Arg1 & 0xFF) + PNP_ADDR_REG = Arg0 + PNP_DATA_REG = Local0 + Local0 = (Arg1 >> 0x08) + Local1 = (Arg0 + One) + PNP_ADDR_REG = Local1 + PNP_DATA_REG = Local0 +} + +/* Set IRQ resource */ +Method (SIRQ, 2, NotSerialized) +{ + SWITCH_LDN (SUPERIO_LPC_LDN) + FindSetRightBit (Arg1, Local0) + Local0 -= One + Local1 = 0x0F + While (Local1) + { + Local2 = (0x40 + Local1) + PNP_ADDR_REG = Local2 + Local3 = PNP_DATA_REG + If (CGLD (Arg0) == Local3) + { + If (Local0 != Local1) + { + PNP_ADDR_REG = Local2 + PNP_DATA_REG = 0xFF + Break + } + Else + { + Return (Zero) + } + } + + Local1-- + } + + Local0 += 0x40 + PNP_ADDR_REG = Local0 + PNP_DATA_REG = CGLD (Arg0) + Return (0xFF) +} + +/* Set DMA resource */ +Method (SDMA, 2, NotSerialized) +{ + SWITCH_LDN (SUPERIO_LPC_LDN) + FindSetRightBit (Arg1, Local0) + Local0 -= One + Local1 = 0x03 + While (Local1) + { + Local2 = (Local1 << One) + Local3 = (0x51 + Local2) + PNP_ADDR_REG = Local3 + Local4 = PNP_DATA_REG + If ((0x80 | CGLD (Arg0)) == Local4) + { + If (Local0 != Local1) + { + PNP_ADDR_REG = Local3 + PNP_DATA_REG = Zero + Break + } + Else + { + Return (Zero) + } + } + + Local1-- + } + + Local0 <<= One + Local0 += 0x51 + PNP_ADDR_REG = Local0 + PNP_DATA_REG = (0x80 | CGLD (Arg0)) + Return (Zero) +} + +/* Device Current Resource Settings */ +Method (DCRS, 2, NotSerialized) +{ + If (CGLD (Arg0) == 0x07) /* UARTA resources */ + { + ENTER_CONFIG_MODE (SUPERIO_LPC_LDN) + IO11 = GIOB (Arg0) + IO12 = IO11 + LEN1 = 0x08 + IRQM = GIRQ (Arg0) + If ((GDMA (Arg0) > 0x03) || (Arg1 == Zero)) + { + DMAM = Zero + } + Else + { + DMAM = GDMA (Arg0) + } + + EXIT_CONFIG_MODE () + Return (CRS1) + } + + If (CGLD (Arg0) == 0x08) /* UARTB resources */ + { + ENTER_CONFIG_MODE (SUPERIO_LPC_LDN) + IO11 = GIOB (Arg0) + IO12 = IO11 + LEN1 = 0x08 + IRQM = GIRQ (Arg0) + If ((GDMA (Arg0) > 0x03) || (Arg1 == Zero)) + { + DMAM = Zero + } + Else + { + DMAM = GDMA (Arg0) + } + + EXIT_CONFIG_MODE () + Return (CRS1) + } + + If (CGLD (Arg0) == 0x11) /* LPT resources */ + { + If (LPTM (Arg0)) + { + ENTER_CONFIG_MODE (SUPERIO_LPC_LDN) + IO21 = GIOB (Arg0) + IO22 = IO21 + IO31 = (IO21 + 0x0400) + IO32 = IO31 + If ((IO21 & 0xFF) == 0xBC) + { + LEN2 = 0x04 + LEN3 = 0x04 + } + Else + { + LEN2 = 0x08 + LEN3 = 0x04 + } + + IRQE = GIRQ (Arg0) + If ((GDMA (Arg0) > 0x03) || (Arg1 == Zero)) + { + DMAM = Zero + } + Else + { + DMAE = GDMA (Arg0) + } + + EXIT_CONFIG_MODE () + Return (CRS2) /* \_SB_.PCI0.LPCB.SIO1.CRS2 */ + } + Else + { + ENTER_CONFIG_MODE (SUPERIO_LPC_LDN) + IO11 = GIOB (Arg0) + IO12 = IO11 /* \_SB_.PCI0.LPCB.SIO1.IO11 */ + If ((IO11 & 0xFF) == 0xBC) + { + LEN1 = 0x04 + } + Else + { + LEN1 = 0x08 + } + + IRQM = GIRQ (Arg0) + EXIT_CONFIG_MODE () + Return (CRS1) /* \_SB_.PCI0.LPCB.SIO1.CRS1 */ + } + } + + If (CGLD (Arg0) == 0x0B) /* Floppy resources */ + { + ENTER_CONFIG_MODE (SUPERIO_LPC_LDN) + IO21 = GIOB (Arg0) + IO22 = IO21 /* \_SB_.PCI0.LPCB.SIO1.IO21 */ + LEN2 = 0x06 + IO31 = (IO21 + 0x07) + IO32 = IO31 /* \_SB_.PCI0.LPCB.SIO1.IO31 */ + LEN3 = One + IRQE = GIRQ (Arg0) + If ((GDMA (Arg0) > 0x03) || (Arg1 == Zero)) + { + DMAM = Zero + } + Else + { + DMAE = GDMA (Arg0) + } + + EXIT_CONFIG_MODE () + Return (CRS2) /* \_SB_.PCI0.LPCB.SIO1.CRS2 */ + } + + Return (CRS1) /* \_SB_.PCI0.LPCB.SIO1.CRS1 */ +} diff --git a/src/superio/smsc/sch5545/acpi/superio.asl b/src/superio/smsc/sch5545/acpi/superio.asl new file mode 100644 index 0000000000..97303c29fa --- /dev/null +++ b/src/superio/smsc/sch5545/acpi/superio.asl @@ -0,0 +1,589 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +/* + * Include this file into a mainboard's DSDT _SB device tree and it will + * expose the SCH5545 SuperIO and some of its functionality. + * + * It allows the change of IO ports, IRQs and DMA settings on logical + * devices, disabling and reenabling logical devices and controlling power + * saving mode on logical devices or the whole chip. + * + * LDN State + * 0x0 FDC Not implemented + * 0x3 PP Not implemented + * 0x4 UARTA Implemented and tested + * 0x5 UARTB Implemented + * 0x7 KBC Implemented and tested + * + * Controllable through preprocessor defines: + * SUPERIO_PNP_BASE I/O address of the first PnP configuration register + * SCH5545_SHOW_UARTA If defined, UARTA will be exposed. + * SCH5545_SHOW_UARTB If defined, UARTB will be exposed. + * SCH5545_SHOW_KBC If defined, the KBC will be exposed. + * SCH5545_EMI_BASE If defined, the Embedded Memory Interface resource will be exposed. + * SCH5545_RUNTIME_BASE If defined, The Runtime Registers resource will be exposed. + */ + +#undef SUPERIO_CHIP_NAME +#define SUPERIO_CHIP_NAME SCH5545 +#include + +#undef PNP_DEFAULT_PSC +#define PNP_DEFAULT_PSC Return (0) /* no power management */ + +/* +* Common helpers will not work on this chip. IO, DMA and IRQ resources. +* These are accessed via LPC interface LDN 0xC. +*/ +#undef PNP_READ_IO +#undef PNP_READ_IRQ +#undef PNP_READ_DMA +#undef PNP_WRITE_IO +#undef PNP_WRITE_IRQ +#undef PNP_WRITE_DMA + +Device(SIO1) { + Name (_HID, EisaId("PNP0A05")) + Name (_STR, Unicode("SMSC SCH5545 Super I/O")) + Name (_UID, SUPERIO_UID(SIO1,)) + +#ifdef SCH5545_EMI_BASE + Name (IO1B, SCH5545_EMI_BASE) +#endif +#ifdef SCH5545_RUNTIME_BASE + Name (IO2B, SCH5545_RUNTIME_BASE) +#endif + Name (IOST, 0x0001) /* IO decoding status */ + Name (MSFG, One) /* Mouse wake config */ + Name (KBFG, One) /* Keyboard wake config */ + Name (PMFG, Zero) /* Wake config */ + + /* SuperIO configuration ports */ + OperationRegion (CREG, SystemIO, SUPERIO_PNP_BASE, 0x02) + Field (CREG, ByteAcc, NoLock, Preserve) + { + PNP_ADDR_REG, 8, + PNP_DATA_REG, 8 + } + IndexField (PNP_ADDR_REG, PNP_DATA_REG, ByteAcc, NoLock, Preserve) + { + Offset (0x07), + PNP_LOGICAL_DEVICE, 8, /* Logical device selector */ + + Offset (0x30), + PNP_DEVICE_ACTIVE, 1, /* Logical device activation */ + + Offset (0x69), + CR69, 8, /* UART1 Base address Registers */ + CR6A, 8, + CR6B, 8, + Offset (0x6D), + CR6D, 8, /* UART2 Base address Registers */ + CR6E, 8, + CR6F, 8, + Offset (0x7D), + CR7D, 8, /* FD Base address Registers */ + CR7E, 8, + CR7F, 8, + Offset (0x81), + CR81, 8, /* LPT Base address Registers */ + CR82, 8, + CR83, 8, + Offset (0xF0), + OPT0, 8, /* MISC registers */ + OPT1, 8, + OPT2, 8, + OPT3, 8, + OPT4, 8, + OPT5, 8 + } + +#ifdef SCH5545_RUNTIME_BASE + /* Runtime registers */ + OperationRegion (RNTR, SystemIO, SCH5545_RUNTIME_BASE, 0x40) + Field (RNTR, ByteAcc, NoLock, Preserve) + { + PMES, 1, /* PME Global Status */ + Offset (0x01), + PMEN, 1, /* PME Global Enable */ + Offset (0x02), + PMS1, 8, /* PME Status 1 for KBD and PS2M */ + PMS2, 8, /* PME Status 2 for EC, WDT, Bat, Intruder */ + PMS3, 8, /* PME Status 3 for GPIOs */ + PME1, 8, /* PME Enable 1 for KBD and PS2M */ + PME2, 8, /* PME Enable 2 for EC, WDT, Bat, Intruder */ + PME3, 8, /* PME Enable 3 for GPIOs */ + Offset (0x10), + SOIS, 1, /* SMI Global Status*/ + Offset (0x11), + SOIE, 1, /* SMI Global Enable */ + Offset (0x12), + SST1, 8, /* SMI Status 1 for UARTs, LPT, FD, EC, Bat */ + SST2, 8, /* SMI Status 2 for KBD, PS2M, WDT, Intruder */ + SST3, 8, /* SMI Status 3 for GPIOs */ + Offset (0x16), + SEN1, 8, /* SMI Enable 1 for UARTs, LPT, FD, EC, Bat */ + SEN2, 8, /* SMI Enable 2 for KBD, PS2M, WDT, Intruder */ + SEN3, 8, /* SMI Enable 3 for GPIOs */ + Offset (0x25), + LED1, 8, /* LED control register */ + Offset (0x28), + GPSR, 8, /* GPIO Select Register */ + GPRR, 8 /* GPIO Read Register */ + } +#endif + Name (CRS, ResourceTemplate () + { + IO (Decode16, + 0x0000, + 0x0000, + 0x00, + 0x00, + _Y11) +#ifdef SCH5545_EMI_BASE + IO (Decode16, + 0x0000, + 0x0000, + 0x00, + 0x00, + _Y12) +#endif +#ifdef SCH5545_RUNTIME_BASE + IO (Decode16, + 0x0000, + 0x0000, + 0x00, + 0x00, + _Y13) +#endif + }) + Method (_CRS, 0, NotSerialized) + { + If (SUPERIO_PNP_BASE) + { + CreateWordField (CRS, \_SB.PCI0.LPCB.SIO1._Y11._MIN, GPI0) + CreateWordField (CRS, \_SB.PCI0.LPCB.SIO1._Y11._MAX, GPI1) + CreateByteField (CRS, \_SB.PCI0.LPCB.SIO1._Y11._LEN, GPIL) + GPI0 = SUPERIO_PNP_BASE + GPI1 = SUPERIO_PNP_BASE + GPIL = 0x02 + } +#ifdef SCH5545_EMI_BASE + If (IO1B) + { + CreateWordField (CRS, \_SB.PCI0.LPCB.SIO1._Y12._MIN, GP10) + CreateWordField (CRS, \_SB.PCI0.LPCB.SIO1._Y12._MAX, GP11) + CreateByteField (CRS, \_SB.PCI0.LPCB.SIO1._Y12._LEN, GPL1) + GP10 = SCH5545_EMI_BASE + GP11 = SCH5545_EMI_BASE + GPL1 = 0x10 + } +#endif +#ifdef SCH5545_RUNTIME_BASE + If (IO2B) + { + CreateWordField (CRS, \_SB.PCI0.LPCB.SIO1._Y13._MIN, GP20) + CreateWordField (CRS, \_SB.PCI0.LPCB.SIO1._Y13._MAX, GP21) + CreateByteField (CRS, \_SB.PCI0.LPCB.SIO1._Y13._LEN, GPL2) + GP20 = SCH5545_RUNTIME_BASE + GP21 = SCH5545_RUNTIME_BASE + GPL2 = 0x40 + } +#endif + Return (CRS) + } + + #undef PNP_ENTER_MAGIC_1ST + #undef PNP_ENTER_MAGIC_2ND + #undef PNP_ENTER_MAGIC_3RD + #undef PNP_ENTER_MAGIC_4TH + #undef PNP_EXIT_MAGIC_1ST + #undef PNP_EXIT_SPECIAL_REG + #undef PNP_EXIT_SPECIAL_VAL + #define PNP_ENTER_MAGIC_1ST 0x55 + #define PNP_EXIT_MAGIC_1ST 0xaa + #include + #define SUPERIO_LPC_LDN 0x0C + #include "resource_helpers.asl" + +#ifdef SCH5545_SHOW_KBC + Device (PS2K) + { + Name (_HID, EisaId ("PNP0303")) + Name (_CID, EisaId ("PNP030B")) + Method (_STA, 0, NotSerialized) + { + Return (DSTA (0xa)) + } + + Name (_CRS, ResourceTemplate () + { + IO (Decode16, + 0x0060, + 0x0060, + 0x00, + 0x01, + ) + IO (Decode16, + 0x0064, + 0x0064, + 0x00, + 0x01, + ) + IRQ (Edge, ActiveHigh, Exclusive) {1} + }) + Name (_PRS, ResourceTemplate () + { + StartDependentFn (0x00, 0x00) + { + FixedIO ( + 0x0060, // Address + 0x01, + ) + FixedIO ( + 0x0064, // Address + 0x01, + ) + IRQ (Edge, ActiveHigh, Exclusive) {1} + } + EndDependentFn () + }) + Method (_PSW, 1, NotSerialized) // _PSW: Power State Wake + { + KBFG = Arg0 + } + Name (_PRW, Package() { 8, 3 }) + } + + Device (PS2M) + { + Name (_HID, EisaId ("PNP0F13")) + Method (_STA, 0, NotSerialized) + { + Return (DSTA (0xe)) + } + + Name (_CRS, ResourceTemplate () + { + IRQ (Edge, ActiveHigh, Exclusive) {12} + }) + + Name (_PRS, ResourceTemplate () + { + StartDependentFn (0x00, 0x00) + { + IRQ (Edge, ActiveHigh, Exclusive) {12} + } + EndDependentFn () + }) + + Method (_PSW, 1, NotSerialized) + { + MSFG = Arg0 + } + + Name (_PRW, Package() { 8, 3 }) + } + + OperationRegion (IOKP, SystemIO, 0x60, 0x05) + Field (IOKP, ByteAcc, NoLock, Preserve) + { + KP60, 8, + Offset (0x04), + KP64, 8 + } + + OperationRegion (KB64, SystemIO, 0x64, One) + Field (KB64, ByteAcc, NoLock, Preserve) + { + , 1, + KRDY, 1, + Offset (0x01) + } +#ifdef SCH5545_RUNTIME_BASE + /* SIO prepare to sleep */ + Method (SIOS, 1, NotSerialized) + { + If ((Arg0 == 0x03) | (Arg0 == One)) + { + ENTER_CONFIG_MODE (One) + Local0 = OPT0 + OPT0 = (Local0 | 0x60) + EXIT_CONFIG_MODE () + Local0 = PMS1 + PMS1 = (Local0 | 0x18) + Local0 = PMES + PMES = (Local0 | One) + + Local0 = PME1 + If (KBFG) + { + PME1 = (Local0 | 0x08) + } + Else + { + PME1 = (Local0 & 0xF7) + } + + Local0 = PME1 + If (MSFG) + { + PME1 = (Local0 | 0x10) + } + Else + { + PME1 = (Local0 & 0xEF) + } + + Local0 = PMEN + PMEN = (Local0 | One) + + While (KRDY) {} + KP60 = 0xED + While (KRDY) {} + KP60 = Zero + While (KRDY) {} + KP60 = 0xF4 + Sleep (One) + } + } + + Method (GPKM, 0, NotSerialized) + { + Local0 = PME1 + PME1 = (Local0 & 0xE7) + Local0 = PMEN + PMEN = (Local0 & 0xFE) + Local0 = PMS1 + PMS1 = (Local0 & 0x18) + Local0 = PMES + PMES = (Local0 & One) + } + + /* SIO wake method */ + Method (SIOW, 1, NotSerialized) + { + PMFG = PMS1 + If (Arg0 == One) + { + GPKM () + ENTER_CONFIG_MODE (One) + Local0 = OPT0 + OPT0 = (Local0 & 0x9F) + EXIT_CONFIG_MODE () + } + + If (Arg0 == 0x03) + { + GPKM () + ENTER_CONFIG_MODE (One) + Local0 = OPT0 + OPT0 = (Local0 & 0x9F) + OPT2 |= One + OPT2 &= 0xFE + EXIT_CONFIG_MODE () + } + } + + Method (SIOH, 0, NotSerialized) + { + If (PMFG & 0x08) + { + Notify (PS2K, 0x02) // Device Wake + } + + If (PMFG & 0x10) + { + Notify (PS2M, 0x02) // Device Wake + } + } +#endif // SCH5545_RUNTIME_BASE +#endif // SCH5545_SHOW_KBC + +#ifdef SCH5545_SHOW_UARTA +#define SUPERIO_UARTA_LDN 7 + Device (UAR1) + { + Name (_HID, EisaId ("PNP0501")) + Name (_UID, SUPERIO_UID(SER, SUPERIO_UARTA_LDN)) + Method (_STA, 0, NotSerialized) + { + Return (DSTA (Zero)) + } + + Method (_DIS, 0, NotSerialized) + { + DCNT (Zero, Zero) + } + + Method (_CRS, 0, NotSerialized) + { + Return (DCRS (Zero, Zero)) + } + + Method (_SRS, 1, NotSerialized) + { + CreateWordField (Arg0, 0x02, IO11) + CreateWordField (Arg0, 0x09, IRQM) + ENTER_CONFIG_MODE (SUPERIO_LPC_LDN) + STIO (0x6A, IO11) + SIRQ (Zero, IRQM) + EXIT_CONFIG_MODE () + DCNT (Zero, One) + } + + Name (_PRS, ResourceTemplate () + { + StartDependentFn (0x00, 0x00) + { + IO (Decode16, + 0x03F8, + 0x03F8, + 0x01, + 0x08, + ) + IRQNoFlags () + {4} + DMA (Compatibility, NotBusMaster, Transfer8, ) + {} + } + StartDependentFn (0x00, 0x00) + { + IO (Decode16, + 0x02F8, + 0x02F8, + 0x01, + 0x08, + ) + IRQNoFlags () + {3} + DMA (Compatibility, NotBusMaster, Transfer8, ) + {} + } + StartDependentFn (0x00, 0x00) + { + IO (Decode16, + 0x03E8, + 0x03E8, + 0x01, + 0x08, + ) + IRQNoFlags () + {4} + DMA (Compatibility, NotBusMaster, Transfer8, ) + {} + } + StartDependentFn (0x00, 0x00) + { + IO (Decode16, + 0x02E8, + 0x02E8, + 0x01, + 0x08, + ) + IRQNoFlags () + {3} + DMA (Compatibility, NotBusMaster, Transfer8, ) + {} + } + EndDependentFn () + }) + + Name (_PRW, Package() { 8, 3 }) + } +#endif + +#ifdef SCH5545_SHOW_UARTB +#define SUPERIO_UARTB_LDN 8 + Device (UAR1) + { + Name (_HID, EisaId ("PNP0501")) + Name (_UID, SUPERIO_UID(SER, SUPERIO_UARTB_LDN)) + Method (_STA, 0, NotSerialized) + { + Return (DSTA (One)) + } + + Method (_DIS, 0, NotSerialized) + { + DCNT (One, Zero) + } + + Method (_CRS, 0, NotSerialized) + { + Return (DCRS (One, Zero)) + } + + Method (_SRS, 1, NotSerialized) + { + CreateWordField (Arg0, 0x02, IO11) + CreateWordField (Arg0, 0x09, IRQM) + ENTER_CONFIG_MODE (SUPERIO_LPC_LDN) + STIO (0x6E, IO11) + SIRQ (One, IRQM) + EXIT_CONFIG_MODE () + DCNT (One, One) + } + + Name (_PRS, ResourceTemplate () + { + StartDependentFn (0x00, 0x00) + { + IO (Decode16, + 0x03F8, + 0x03F8, + 0x01, + 0x08, + ) + IRQNoFlags () + {4} + DMA (Compatibility, NotBusMaster, Transfer8, ) + {} + } + StartDependentFn (0x00, 0x00) + { + IO (Decode16, + 0x02F8, + 0x02F8, + 0x01, + 0x08, + ) + IRQNoFlags () + {3} + DMA (Compatibility, NotBusMaster, Transfer8, ) + {} + } + StartDependentFn (0x00, 0x00) + { + IO (Decode16, + 0x03E8, + 0x03E8, + 0x01, + 0x08, + ) + IRQNoFlags () + {4} + DMA (Compatibility, NotBusMaster, Transfer8, ) + {} + } + StartDependentFn (0x00, 0x00) + { + IO (Decode16, + 0x02E8, + 0x02E8, + 0x01, + 0x08, + ) + IRQNoFlags () + {3} + DMA (Compatibility, NotBusMaster, Transfer8, ) + {} + } + EndDependentFn () + }) + + Name (_PRW, Package() { 8, 3 }) + } + +#endif +} diff --git a/src/superio/smsc/sch5545/sch5545.h b/src/superio/smsc/sch5545/sch5545.h new file mode 100644 index 0000000000..464be1a106 --- /dev/null +++ b/src/superio/smsc/sch5545/sch5545.h @@ -0,0 +1,299 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +/* + * Based on SMSC SCH5627 datahseet: + * http://pdf.datasheetcatalog.com/datasheets/microchip/00001996A.pdf + */ + +#ifndef SUPERIO_SCH_5545_H +#define SUPERIO_SCH_5545_H + +/* LPC I/O space */ +#define SCH5545_RUNTIME_REG_BASE 0x0a00 +#define SCH5545_EMI_BASE 0x0a40 + +/* logical devices */ +#define SCH5545_LDN_EMI 0x00 +#define SCH5545_LDN_KBC 0x01 +#define SCH5545_LDN_UART1 0x07 +#define SCH5545_LDN_UART2 0x08 +#define SCH5545_LDN_RR 0x0a /* Runtime Registers */ +#define SCH5545_LDN_FDC 0x0b +#define SCH5545_LDN_LPC 0x0c /* LPC Interface */ +#define SCH5545_LDN_PP 0x11 +#define SCH5545_LDN_GCONF 0x3f /* Global Config */ + +/* KBC config registers */ +#define SCH5545_KRST_GA20 0xf0 +#define SCH5545_PORT92_EN (1 << 2) +#define SCH5545_KBD_INT_LATCH (1 << 3) +#define SCH5545_MOUSE_INT_LATCH (1 << 4) +#define SCH5545_KBD_ISOLATION (1 << 5) +#define SCH5545_MOUSE_ISOLATION (1 << 6) +#define SCH5545_KEYBOARD_SELECT 0xf1 +#define SCH5545_KBD_MOUSE_SWAP (1 << 0) +#define SCH5545_8042_RESET 0xf2 +#define SCH5545_8042_IN_RESET (1 << 0) +#define SCH5545_8042_OUT_RESET (0 << 0) + +/* UART config registers */ +#define SCH5545_UART_CONFIG_SELECT 0xf0 +#define SCH5545_UART_CLK_64MHZ_RO (0 << 0) +#define SCH5545_UART_CLK_96MHZ_PLL (1 << 0) +#define SCH5545_UART_POWER_VTR (0 << 1) +#define SCH5545_UART_POWER_VCC (1 << 1) +#define SCH5545_UART_NO_POLARITY_INVERT (0 << 2) +#define SCH5545_UART_INVERT_POLARITY (1 << 2) + +/* RR config registers */ +#define SCH5545_SPEKEY 0xf0 +#define SCH5545_SPEKEY_WAKE_EN (0 << 1) +#define SCH5545_SPEKEY_WAKE_DIS (1 << 1) + +/* Floppy config registers */ +#define SCH5545_FDD_MODE 0xf0 +#define SCH5545_FDD_MODE_NORMAL (0 << 0) +#define SCH5545_FDD_MODE_ENHANCED (1 << 0) +#define SCH5545_FDC_DMA_MODE_BURST (0 << 1) +#define SCH5545_FDC_DMA_MODE_NON_BURST (1 << 1) +#define SCH5545_FDD_IF_MODE_AT (3 << 2) +#define SCH5545_FDD_IF_MODE_PS2 (1 << 2) +#define SCH5545_FDD_IF_MODE_MODEL30 (0 << 2) +#define SCH5545_FDC_OUTPUT_TYPE_OPEN_DRAIN (0 << 6) +#define SCH5545_FDC_OUTPUT_TYPE_PUSH_PULL (1 << 6) +#define SCH5545_FDC_OUTPUT_CTRL_ACTIVE (0 << 7) +#define SCH5545_FDC_OUTPUT_CTRL_TRISTATE (1 << 7) +#define SCH5545_FDD_OPTION 0xf1 +#define SCH5545_FDD_FORCED_WP_INACTIVE (0 << 0) +#define SCH5545_FDD_FORCED_WP_ACTIVE (1 << 0) +#define SCH5545_FDD_DENSITY_NORMAL (0 << 2) +#define SCH5545_FDD_DENSITY_NORMAL_USER (1 << 2) +#define SCH5545_FDD_DENSITY_LOGIC_ONE (2 << 2) +#define SCH5545_FDD_DENSITY_LOGIC_ZERO (3 << 2) +#define SCH5545_FDD_TYPE 0xf2 +#define SCH5545_FDD0 0xf4 +#define SCH5545_FDD0_TYPE_SEL_DT0 (1 << 0) +#define SCH5545_FDD0_TYPE_SEL_DT1 (1 << 1) +#define SCH5545_FDD0_DRT_SEL_DRT0 (1 << 3) +#define SCH5545_FDD0_USE_PRECOMPENSATION (0 << 6) +#define SCH5545_FDD0_NO_PRECOMPENSATION (1 << 6) + +/* Parallel Port config registers */ +#define SCH5545_PP_INT_SELECT 0x70 +#define SCH5545_PP_SERIRQ_CHANNEL_MASK 0xf +#define SCH5545_PP_DMA_SELECT 0x74 +#define SCH5545_PP_DMA_CHANNEL_MASK 0x7 +#define SCH5545_PP_MODE 0xf0 +#define SCH5545_PP_MODE_PRINTER (4 << 0) +#define SCH5545_PP_MODE_SPP (0 << 0) +#define SCH5545_PP_MODE_EPP19_SPP (1 << 0) +#define SCH5545_PP_MODE_EPP17_SPP (5 << 0) +#define SCH5545_PP_MODE_ECP (2 << 0) +#define SCH5545_PP_MODE_EPP17_ECP (3 << 0) +#define SCH5545_PP_MODE_EPP19_ECP (7 << 0) +#define SCH5545_PP_ECP_FIFO_TRESH_MASK (0xf << 3) +#define SCH5545_PP_INT_PULSED_LOW (1 << 7) +#define SCH5545_PP_INT_FOLLOWS_ACK (0 << 7) +#define SCH5545_PP_MODE2 0xf1 +#define SCH5545_PP_TMOUT_CLEARED_ON_WRITE (0 << 4) +#define SCH5545_PP_TMOUT_CLEARED_ON_READ (1 << 4) + +/* LPC IF config registers */ +#define SCH5545_IRQ_BASE 0x40 +#define SCH5545_DRQ_BASE 0x50 +/* + * BAR registers are 4 byte + * byte 0 0-6 mask, 7 reserved + * byte 1 0-5 frame, 6 device, 7 valid + * byte 2 LPC address least sig. + * byte 3 LPC address most sig. + */ +#define SCH5545_BAR_LPC_IF 0x60 +#define SCH5545_BAR_EM_IF 0x64 +#define SCH5545_BAR_UART1 0x68 +#define SCH5545_BAR_UART2 0x6c +#define SCH5545_BAR_RUNTIME_REG 0x70 +/* Certain SMSC parts have SPI controller LDN 0xf with BAR rgeister at 0x74 */ +#define SCH5545_BAR_KBC 0x78 +#define SCH5545_BAR_FLOPPY 0x7c +#define SCH5545_BAR_PARPORT 0x80 + +/* IRQ <> device mappings */ +#define SCH5545_IRQ_KBD 0x01 +#define SCH5545_IRQ_MOUSE 0x81 +#define SCH5545_IRQ_UART1 0x07 +#define SCH5545_IRQ_UART2 0x08 +#define SCH5545_IRQ_EMI_MAILBOX 0x00 +#define SCH5545_IRQ_EMI_IRQ_SOURCE 0x80 +#define SCH5545_IRQ_RUNTIME_REG 0x0a +#define SCH5545_IRQ_RUNTIME_REG_SMI 0x8a +#define SCH5545_IRQ_FLOPPY 0x0b +#define SCH5545_IRQ_PARPORT 0x11 +#define SCH5545_IRQ_DISABLED 0xff + + +/* runtime registers */ +#define SCH5545_RR_PME_STS 0x00 +#define SCH5545_GLOBAL_PME_STS 0x01 +#define SCH5545_RR_PME_EN 0x01 +#define SCH5545_GLOBAL_PME_EN 0x01 +#define SCH5545_RR_PME_STS1 0x02 +#define SCH5545_UART2_RI_PME_STS 0x2 +#define SCH5545_UART1_RI_PME_STS 0x4 +#define SCH5545_KBD_PME_STS 0x8 +#define SCH5545_MOUSE_PME_STS 0x10 +#define SCH5545_SPECIFIC_KEY_PME_STS 0x20 +#define SCH5545_RR_PME_STS2 0x03 +#define SCH5545_IO_SMI_EVT_STS 0x1 +#define SCH5545_WDT_TIMEOUT_EVT_STS 0x2 +#define SCH5545_EM_EVT1_STS 0x4 +#define SCH5545_EM_EVT2_STS 0x8 +#define SCH5545_FW_EVT_STS 0x10 +#define SCH5545_BAT_LOW_STS 0x20 +#define SCH5545_INTRUDER_STS 0x40 +#define SCH5545_RR_PME_STS3 0x04 +#define SCH5545_GPIO62_PME_STS 0x1 +#define SCH5545_GPIO54_PME_STS 0x2 +#define SCH5545_GPIO53_PME_STS 0x4 +#define SCH5545_GPIO35_PME_STS 0x8 +#define SCH5545_GPIO31_PME_STS 0x10 +#define SCH5545_GPIO25_PME_STS 0x20 +#define SCH5545_GPIO24_PME_STS 0x40 +#define SCH5545_GPIO21_PME_STS 0x80 +#define SCH5545_RR_PME_EN1 0x05 +#define SCH5545_UART2_RI_PME_EN 0x2 +#define SCH5545_UART1_RI_PME_EN 0x4 +#define SCH5545_KBD_PME_EN 0x8 +#define SCH5545_MOUSE_PME_EN 0x10 +#define SCH5545_SPECIFIC_KEY_PME_EN 0x20 +#define SCH5545_RR_PME_EN2 0x06 +#define SCH5545_IO_SMI_EVT_PME_EN 0x1 +#define SCH5545_WDT_EVT_PME_EN 0x2 +#define SCH5545_EM_EVT1_PME_EN 0x4 +#define SCH5545_EM_EVT2_PME_EN 0x8 +#define SCH5545_FW_EVT_PME_EN 0x10 +#define SCH5545_BAT_LOW_PME_EN 0x20 +#define SCH5545_INTRUDER_PME_EN 0x40 +#define SCH5545_RR_PME_EN3 0x07 +#define SCH5545_GPIO62_PME_EN 0x1 +#define SCH5545_GPIO54_PME_EN 0x2 +#define SCH5545_GPIO53_PME_EN 0x4 +#define SCH5545_GPIO35_PME_EN 0x8 +#define SCH5545_GPIO31_PME_EN 0x10 +#define SCH5545_GPIO25_PME_EN 0x20 +#define SCH5545_GPIO24_PME_EN 0x40 +#define SCH5545_GPIO21_PME_EN 0x80 +#define SCH5545_RR_SMI_STS 0x10 +#define SCH5545_SMI_GLOBAL_STS 0x1 +#define SCH5545_RR_SMI_EN 0x11 +#define SCH5545_SMI_GLOBAL_EN 0x1 +#define SCH5545_RR_SMI_STS1 0x12 +#define SCH5545_LOW_BAT_SMI_STS 0x1 +#define SCH5545_PAR_PORT_SMI_STS 0x2 +#define SCH5545_UART2_SMI_STS 0x4 +#define SCH5545_UART1_SMI_STS 0x8 +#define SCH5545_FLOPPY_SMI_STS 0x10 +#define SCH5545_EM_EVT1_SMI_STS 0x20 +#define SCH5545_EM_EVT2_SMI_STS 0x40 +#define SCH5545_FW_EVT_SMI_STS 0x80 +#define SCH5545_RR_SMI_STS2 0x13 +#define SCH5545_MOUSE_SMI_STS 0x1 +#define SCH5545_KBD_SMI_STS 0x2 +#define SCH5545_WATCHDOG_EVT_SMI_STS 0x8 +#define SCH5545_INTRUSION_SMI_STS 0x10 +#define SCH5545_RR_SMI_STS3 0x14 +#define SCH5545_GPIO62_SMI_STS 0x1 +#define SCH5545_GPIO54_SMI_STS 0x2 +#define SCH5545_GPIO53_SMI_STS 0x4 +#define SCH5545_GPIO35_SMI_STS 0x8 +#define SCH5545_GPIO31_SMI_STS 0x10 +#define SCH5545_GPIO25_SMI_STS 0x20 +#define SCH5545_GPIO24_SMI_STS 0x40 +#define SCH5545_GPIO21_SMI_STS 0x80 +#define SCH5545_RR_SMI_EN1 0x15 +#define SCH5545_LOW_BAT_SMI_EN 0x1 +#define SCH5545_PAR_PORT_SMI_EN 0x2 +#define SCH5545_UART2_SMI_EN 0x4 +#define SCH5545_UART1_SMI_EN 0x8 +#define SCH5545_FLOPPY_SMI_EN 0x10 +#define SCH5545_EM_EVT1_SMI_EN 0x20 +#define SCH5545_EM_EVT2_SMI_EN 0x40 +#define SCH5545_FW_EVT_SMI_EN 0x1 +#define SCH5545_RR_SMI_EN2 0x16 +#define SCH5545_MOUSE_SMI_EN 0x1 +#define SCH5545_KBD_SMI_EN 0x2 +#define SCH5545_WATCHDOG_EVT_SMI_EN 0x8 +#define SCH5545_INTRUSION_SMI_EN 0x10 +#define SCH5545_RR_SMI_EN3 0x17 +#define SCH5545_GPIO62_SMI_EN 0x1 +#define SCH5545_GPIO54_SMI_EN 0x2 +#define SCH5545_GPIO53_SMI_EN 0x4 +#define SCH5545_GPIO35_SMI_EN 0x8 +#define SCH5545_GPIO31_SMI_EN 0x10 +#define SCH5545_GPIO25_SMI_EN 0x20 +#define SCH5545_GPIO24_SMI_EN 0x40 +#define SCH5545_GPIO21_SMI_EN 0x80 +#define SCH5545_RR_FORCE_DISK_CH 0x20 +#define SCH5545_FLOPPY_DISK_CHANGE 0x1 +#define SCH5545_RR_FLOPPY_DR_SEL 0x21 +#define SCH5545_DR_SELECT0 0x1 +#define SCH5545_DR_SELECT1 0x2 +#define SCH5545_FLOPPY_PRECOMP0 0x4 +#define SCH5545_FLOPPY_PRECOMP1 0x8 +#define SCH5545_FLOPPY_PRECOMP2 0x10 +#define SCH5545_FLOPPY_PWR_DOWN 0x40 +#define SCH5545_FLOPPY_SOFT_RESET 0x80 +#define SCH5545_RR_UART1_FIFO_CTRL 0x22 +#define SCH5545_UART1_FIFO_FE 0x1 +#define SCH5545_UART1_FIFO_RFR 0x2 +#define SCH5545_UART1_FIFO_XFR 0x4 +#define SCH5545_UART1_FIFO_DMS 0x8 +#define SCH5545_UART1_FIFO_RTL 0x40 +#define SCH5545_UART1_FIFO_RTM 0x80 +#define SCH5545_RR_UART2_FIFO_CTRL 0x23 +#define SCH5545_UART2_FIFO_FE 0x1 +#define SCH5545_UART2_FIFO_RFR 0x2 +#define SCH5545_UART2_FIFO_XFR 0x4 +#define SCH5545_UART2_FIFO_DMS 0x8 +#define SCH5545_UART2_FIFO_RTL 0x40 +#define SCH5545_UART2_FIFO_RTM 0x80 +#define SCH5545_RR_DEV_DISABLE 0x24 +#define SCH5545_FLOPPY_WP 0x1 +#define SCH5545_FLOPPY_DIS 0x8 +#define SCH5545_UART2_DIS 0x20 +#define SCH5545_UART1_DIS 0x40 +#define SCH5545_PAR_PORT_DIS 0x80 +#define SCH5545_RR_LED 0x25 +#define SCH5545_LED_BLINK_OFF 0x0 +#define SCH5545_LED_BLINK_1HZ 0x1 +#define SCH5545_LED_BLINK_ON 0x3 +#define SCH5545_LED_BLINK_MASK 0x3 +#define SCH5545_LED_COLOR_YELLOW 0x0 +#define SCH5545_LED_COLOR_GREEN 0x4 +#define SCH5545_LED_CODE_FETCH 0x8 +#define SCH5545_RR_KB_SCAN 0x26 +#define SCH5545_RR_PWRGOOD 0x27 +#define SCH5545_PWRGOOD_DELAY 0x1 +#define SCH5545_PWRGOOD_LOCK 0x2 +#define SCH5545_PCIRST_OUT4_EN 0x10 +#define SCH5545_PCIRST_OUT3_EN 0x20 +#define SCH5545_PCIRST_OUT1_EN 0x40 +#define SCH5545_PCIRST_OUT2_EN 0x80 +#define SCH5545_RR_GPIO_SEL 0x28 +#define SCH5545_RR_GPIO_READ 0x29 +#define SCH5545_RR_GPIO_WRITE 0x2A +#define SCH5545_RR_FW_EVT_STS 0x30 +#define SCH5545_RR_FW_EVT_EN 0x31 +#define SCH5545_RR_PWR_REC_MODES 0x32 +#define SCH5545_PWR_SUPPLY_OFF 0x00 +#define SCH5545_PWR_SUPPLY_ON 0x80 +#define SCH5545_RR_INTRUDER 0x34 +#define SCH5545_INTRUSION_EDGE_STS 0x1 +#define SCH5545_INTRUDER_PIN_STS 0x2 + +void sch5545_early_init(unsigned int port); +void sch5545_enable_uart(unsigned int port, unsigned int uart_no); +void sch5545_set_led(unsigned int runtime_reg_base, unsigned int color, uint16_t blink); +int sch5545_get_gpio(uint8_t sio_port, uint8_t gpio); + +#endif /* SUPERIO_SCH_5545_H */ diff --git a/src/superio/smsc/sch5545/sch5545_early_init.c b/src/superio/smsc/sch5545/sch5545_early_init.c new file mode 100644 index 0000000000..4841571113 --- /dev/null +++ b/src/superio/smsc/sch5545/sch5545_early_init.c @@ -0,0 +1,179 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#include +#include +#include +#include +#include "sch5545.h" + +static void pnp_enter_conf_state(pnp_devfn_t dev) +{ + unsigned int port = dev >> 8; + outb(0x55, port); +} + +static void pnp_exit_conf_state(pnp_devfn_t dev) +{ + unsigned int port = dev >> 8; + outb(0xaa, port); +} + +/* + * Set the BAR / iobase for a specific device. + * pnp_devfn_t dev must be in conf state. + * LDN LPC IF must be active. + */ +static void set_iobase(pnp_devfn_t dev, uint16_t device_addr, uint16_t bar_addr) +{ + uint16_t bar; + + /* + * Set the BAR. We have to flip the BAR due to different register layout: + * - LPC addr LSB on device_addr + 2 + * - LPC addr MSB on device_addr + 3 + */ + bar = ((bar_addr >> 8) & 0xff) | ((bar_addr & 0xff) << 8); + pnp_set_iobase(dev, device_addr + 2, bar); +} + +/* + * Set the IRQ for the specific device. + * pnp_devfn_t dev must be in conf state. + * LDN LPC IF must be active. + */ +static void set_irq(pnp_devfn_t dev, uint8_t irq_device, unsigned int irq) +{ + if (irq > 15) + return; + + pnp_write_config(dev, SCH5545_IRQ_BASE + irq, irq_device); +} + +/* + * sch5545 has 2 LEDs which are accessed via color (1 bit), 2 bits for a + * pattern blink and 1 bit for "code fetch" which means the cpu/mainboard is + * working (always set). + */ +void sch5545_set_led(unsigned int runtime_reg_base, unsigned int color, uint16_t blink) +{ + uint8_t val = blink & SCH5545_LED_BLINK_MASK; + val |= SCH5545_LED_CODE_FETCH; + if (color) + val |= SCH5545_LED_COLOR_GREEN; + outb(val, runtime_reg_base + SCH5545_RR_LED); +} + +void sch5545_early_init(unsigned int port) +{ + pnp_devfn_t dev; + + /* Enable SERIRQ */ + dev = PNP_DEV(port, SCH5545_LDN_GCONF); + pnp_enter_conf_state(dev); + pnp_set_logical_device(dev); + pnp_write_config(dev, 0x24, pnp_read_config(dev, 0x24) | 0x04); + + /* Enable LPC interface */ + dev = PNP_DEV(port, SCH5545_LDN_LPC); + pnp_set_logical_device(dev); + pnp_set_enable(dev, 1); + /* Set LPC BAR mask */ + pnp_write_config(dev, SCH5545_BAR_LPC_IF, 0x01); + /* BAR valid, Frame/LDN = 0xc */ + pnp_write_config(dev, SCH5545_BAR_LPC_IF + 1, SCH5545_LDN_LPC | 0x80); + set_iobase(dev, SCH5545_BAR_LPC_IF, port); + + /* Enable Runtime Registers */ + + /* The Runtime Registers BAR is 0x40 long */ + pnp_write_config(dev, SCH5545_BAR_RUNTIME_REG, 0x3f); + /* BAR valid, Frame/LDN = 0xa */ + pnp_write_config(dev, SCH5545_BAR_RUNTIME_REG + 1, SCH5545_LDN_RR | 0x80); + + /* Map Runtime Registers */ + set_iobase(dev, SCH5545_BAR_RUNTIME_REG, SCH5545_RUNTIME_REG_BASE); + dev = PNP_DEV(port, SCH5545_LDN_RR); + pnp_set_logical_device(dev); + pnp_set_enable(dev, 1); + + /* Set LED color and indicate BIOS has reached code fetch phase */ + sch5545_set_led(SCH5545_RUNTIME_REG_BASE, SCH5545_LED_COLOR_GREEN, + SCH5545_LED_BLINK_ON); + + /* Configure EMI */ + dev = PNP_DEV(port, SCH5545_LDN_LPC); + pnp_set_logical_device(dev); + /* EMI BAR has 11 registers, but vendor sets the mask to 0xf */ + pnp_write_config(dev, SCH5545_BAR_EM_IF, 0x0f); + /* BAR valid, Frame/LDN = 0x00 */ + pnp_write_config(dev, SCH5545_BAR_EM_IF + 1, SCH5545_LDN_EMI | 0x80); + set_iobase(dev, SCH5545_BAR_EM_IF, SCH5545_EMI_BASE); + + pnp_exit_conf_state(dev); +} + +void sch5545_enable_uart(unsigned int port, unsigned int uart_no) +{ + pnp_devfn_t dev; + + if (uart_no > 1) + return; + + /* Configure serial port */ + dev = PNP_DEV(port, SCH5545_LDN_LPC); + pnp_enter_conf_state(dev); + pnp_set_logical_device(dev); + /* Set UART BAR mask to 0x07 (8 registers) */ + pnp_write_config(dev, SCH5545_BAR_UART1 + (4 * uart_no), 0x07); + /* Set BAR valid, Frame/LDN = UART1/2 LDN 0x07/0x08 */ + pnp_write_config(dev, SCH5545_BAR_UART1 + (4 * uart_no) + 1, + (SCH5545_LDN_UART1 + uart_no) | 0x80); + set_iobase(dev, SCH5545_BAR_UART1 + (4 * uart_no), (uart_no == 1) ? 0x2f8 : 0x3f8); + /* IRQ 3 for UART2, IRQ4 for UART1 */ + set_irq(dev, SCH5545_LDN_UART1 + uart_no, 4 - uart_no); + + dev = PNP_DEV(port, SCH5545_LDN_UART1 + uart_no); + pnp_set_logical_device(dev); + pnp_set_enable(dev, 1); + pnp_write_config(dev, SCH5545_UART_CONFIG_SELECT, SCH5545_UART_POWER_VCC); + + pnp_exit_conf_state(dev); +} + +int sch5545_get_gpio(uint8_t sio_port, uint8_t gpio) +{ + pnp_devfn_t dev; + uint16_t runtime_reg_base; + uint8_t gpio_bank, gpio_num; + + gpio_bank = gpio / 10; + gpio_num = gpio % 10; + /* + * GPIOs are divided into banks of 8 GPIOs (kind of). Each group starts at decimal + * base, i.e. 8 GPIOs from GPIO000, 8 GPIOs from GPIO010, etc., up to GPIO071 and + * GPIO072 which are an exception (only two GPIOs in the bank 7). + */ + if (gpio_num > 7) + return -1; + else if (gpio_bank == 7 && gpio_num > 1) + return -1; + else if (gpio_bank > 7) + return -1; + + dev = PNP_DEV(sio_port, SCH5545_LDN_LPC); + pnp_enter_conf_state(dev); + pnp_set_logical_device(dev); + + runtime_reg_base = pnp_read_config(dev, SCH5545_BAR_RUNTIME_REG + 2); + runtime_reg_base |= pnp_read_config(dev, SCH5545_BAR_RUNTIME_REG + 3) << 8; + + pnp_exit_conf_state(dev); + + if (runtime_reg_base == 0) + return -1; + + outb(gpio_bank * 8 + gpio_num, runtime_reg_base + SCH5545_RR_GPIO_SEL); + + return inb(runtime_reg_base + SCH5545_RR_GPIO_READ) & 1; +} diff --git a/src/superio/smsc/sch5545/sch5545_emi.c b/src/superio/smsc/sch5545/sch5545_emi.c new file mode 100644 index 0000000000..2bb150c69b --- /dev/null +++ b/src/superio/smsc/sch5545/sch5545_emi.c @@ -0,0 +1,214 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#include +#include +#include +#include +#include + +#include "sch5545.h" +#include "sch5545_emi.h" + +static uint16_t emi_bar; + +#ifdef __SIMPLE_DEVICE__ +static void sch5545_enter_conf_state(pnp_devfn_t dev) +{ + unsigned int port = dev >> 8; + outb(0x55, port); +} + +static void sch5545_exit_conf_state(pnp_devfn_t dev) +{ + unsigned int port = dev >> 8; + outb(0xaa, port); +} +#endif + +uint16_t sch5545_read_emi_bar(uint8_t sio_port) +{ + uint16_t bar; + +#ifdef __SIMPLE_DEVICE__ + pnp_devfn_t lpcif = PNP_DEV(sio_port, SCH5545_LDN_LPC); + sch5545_enter_conf_state(lpcif); +#else + struct device *lpcif = dev_find_slot_pnp(sio_port, SCH5545_LDN_LPC); + if (!lpcif) + return 0; + pnp_enter_conf_mode_55(lpcif); +#endif + pnp_set_logical_device(lpcif); + + bar = pnp_read_config(lpcif, SCH5545_BAR_EM_IF + 2); + bar |= pnp_read_config(lpcif, SCH5545_BAR_EM_IF + 3) << 8; + +#ifdef __SIMPLE_DEVICE__ + sch5545_exit_conf_state(lpcif); +#else + pnp_exit_conf_mode_aa(lpcif); +#endif + return bar; +} + +void sch5545_emi_init(uint8_t sio_port) +{ + emi_bar = sch5545_read_emi_bar(sio_port); + assert(emi_bar != 0); +} + +void sch5545_emi_ec2h_mailbox_clear(void) +{ + sch5545_emi_ec2h_mbox_write(sch5545_emi_ec2h_mbox_read()); +} + +void sch5545_emi_disable_interrupts(void) +{ + sch5545_emi_set_int_mask(0); +} + +void sch5545_emi_h2ec_mbox_write(uint8_t mbox_message) +{ + outb(mbox_message, emi_bar + SCH5545_EMI_HOST_TO_EC_MAILBOX); +} + +uint8_t sch5545_emi_h2ec_mbox_read(void) +{ + return inb(emi_bar + SCH5545_EMI_HOST_TO_EC_MAILBOX); +} + +void sch5545_emi_ec2h_mbox_write(uint8_t mbox_message) +{ + outb(mbox_message, emi_bar + SCH5545_EMI_EC_TO_HOST_MAILBOX); +} + +uint8_t sch5545_emi_ec2h_mbox_read(void) +{ + return inb(emi_bar + SCH5545_EMI_EC_TO_HOST_MAILBOX); +} + +void sch5545_emi_set_int_mask(uint16_t mask) +{ + outw(mask, emi_bar + SCH5545_EMI_INT_MASK); +} + +void sch5545_emi_set_int_mask_low(uint8_t mask) +{ + outb(mask, emi_bar + SCH5545_EMI_INT_MASK); +} + +void sch5545_emi_set_int_mask_high(uint8_t mask) +{ + outb(mask, emi_bar + SCH5545_EMI_INT_MASK + 1); +} + +uint8_t sch5545_emi_get_int_mask_low(void) +{ + return inb(emi_bar + SCH5545_EMI_INT_MASK); +} + +uint8_t sch5545_emi_get_int_mask_high(void) +{ + return inb(emi_bar + SCH5545_EMI_INT_MASK + 1); +} + +uint16_t sch5545_emi_get_int_mask(void) +{ + return inw(emi_bar + SCH5545_EMI_INT_MASK); +} + +void sch5545_emi_set_int_src_low(uint8_t int_src) +{ + outb(int_src, emi_bar + SCH5545_EMI_INT_SOURCE); +} + +void sch5545_emi_set_int_src_high(uint8_t int_src) +{ + outb(int_src, emi_bar + SCH5545_EMI_INT_SOURCE + 1); +} + +uint8_t sch5545_emi_get_int_src_low(void) +{ + return inb(emi_bar + SCH5545_EMI_INT_SOURCE); +} + +uint8_t sch5545_emi_get_int_src_high(void) +{ + return inb(emi_bar + SCH5545_EMI_INT_SOURCE + 1); +} +uint16_t sch5545_emi_get_int_src(void) +{ + return inw(emi_bar + SCH5545_EMI_INT_SOURCE); +} + +void sch5545_emi_set_int_src(uint16_t int_src) +{ + outw(int_src, emi_bar + SCH5545_EMI_INT_SOURCE); +} + +void sch5545_emi_set_ec_addr(uint16_t addr) +{ + outw(addr, emi_bar + SCH5545_EMI_EC_ADDR); +} + +uint16_t sch5545_emi_read_ec_addr(void) +{ + return inw(emi_bar + SCH5545_EMI_EC_ADDR); +} + +void sch5545_emi_ec_write8(uint16_t addr, uint8_t data) +{ + sch5545_emi_set_ec_addr((addr & 0xfffc) | EMI_EC_8BIT_ACCESS); + outb(data, emi_bar + SCH5545_EMI_EC_DATA + (addr & 3)); +} + +void sch5545_emi_ec_write16(uint16_t addr, uint16_t data) +{ + sch5545_emi_set_ec_addr((addr & 0xfffc) | EMI_EC_16BIT_ACCESS); + outw(data, emi_bar + SCH5545_EMI_EC_DATA + (addr & 2)); +} + +void sch5545_emi_ec_write32(uint16_t addr, uint32_t data) +{ + sch5545_emi_set_ec_addr((addr & 0xfffc) | EMI_EC_32BIT_ACCESS); + outl(data, emi_bar + SCH5545_EMI_EC_DATA); +} + +void sch5545_emi_ec_write32_bulk(uint16_t addr, const uint32_t *buffer, size_t len) +{ + sch5545_emi_set_ec_addr((addr & 0xfffc) | EMI_EC_32BIT_AUTO_ACCESS); + + while (len > 0) { + outl(*(buffer++), emi_bar + SCH5545_EMI_EC_DATA); + len--; + } +} + +uint8_t sch5545_emi_ec_read8(uint16_t addr) +{ + sch5545_emi_set_ec_addr((addr & 0xfffc) | EMI_EC_8BIT_ACCESS); + return inb(emi_bar + SCH5545_EMI_EC_DATA + (addr & 3)); +} + +uint16_t sch5545_emi_ec_read16(uint16_t addr) +{ + sch5545_emi_set_ec_addr((addr & 0xfffc) | EMI_EC_16BIT_ACCESS); + return inw(emi_bar + SCH5545_EMI_EC_DATA + (addr & 2)); +} + +uint32_t sch5545_emi_ec_read32(uint16_t addr) +{ + sch5545_emi_set_ec_addr((addr & 0xfffc) | EMI_EC_32BIT_ACCESS); + return inb(emi_bar + SCH5545_EMI_EC_DATA); +} + +void sch5545_emi_ec_read32_bulk(uint16_t addr, uint32_t *buffer, size_t len) +{ + sch5545_emi_set_ec_addr((addr & 0xfffc) | EMI_EC_32BIT_AUTO_ACCESS); + + while (len > 0) { + *(buffer++) = inl(emi_bar + SCH5545_EMI_EC_DATA); + len--; + } +} diff --git a/src/superio/smsc/sch5545/sch5545_emi.h b/src/superio/smsc/sch5545/sch5545_emi.h new file mode 100644 index 0000000000..2bf3a769a5 --- /dev/null +++ b/src/superio/smsc/sch5545/sch5545_emi.h @@ -0,0 +1,167 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef SUPERIO_SCH_5545_EMI_H +#define SUPERIO_SCH_5545_EMI_H + +#include +#include + +/* Embedded Memory Interface registers */ +#define SCH5545_EMI_HOST_TO_EC_MAILBOX 0x0 +#define SCH5545_EMI_EC_TO_HOST_MAILBOX 0x1 +#define SCH5545_EMI_EC_ADDR 0x2 +#define SCH5545_EMI_EC_DATA 0x4 +#define SCH5545_EMI_INT_SOURCE 0x8 +#define SCH5545_EMI_INT_MASK 0xa + +#define EMI_EC_8BIT_ACCESS 0 +#define EMI_EC_16BIT_ACCESS 1 +#define EMI_EC_32BIT_ACCESS 2 +#define EMI_EC_32BIT_AUTO_ACCESS 3 + +/** + * Reads and returns the base address of EMI from the SuperIO. + */ +uint16_t sch5545_read_emi_bar(uint8_t sio_port); +/** + * One must call this function at every stage before using any of the EMI + * functions. The base address of EMI interface must not be zero. + */ +void sch5545_emi_init(uint8_t sio_port); +/** + * Reads the EC to Host mailbox register and then writes the same content to + * clear it. + */ +void sch5545_emi_ec2h_mailbox_clear(void); +/** + * Writes the interrupt mask register with 0. + */ +void sch5545_emi_disable_interrupts(void); +/** + * Writes the Host to EC mailbox 8bit register with mbox_message. + */ +void sch5545_emi_h2ec_mbox_write(uint8_t mbox_message); +/** + * Reads and returns the Host to EC mailbox 8bit register. + */ +uint8_t sch5545_emi_h2ec_mbox_read(void); +/** + * Writes the EC to Host mailbox 8bit register with mbox_message. + */ +void sch5545_emi_ec2h_mbox_write(uint8_t mbox_message); +/** + * Reads and returns the EC to Host mailbox 8bit register. + */ +uint8_t sch5545_emi_ec2h_mbox_read(void); +/** + * Sets the mask for all EC interrupts. + */ +void sch5545_emi_set_int_mask(uint16_t mask); +/** + * Sets the EC interrupt mask for LSB in the Interrupt Mask register. + */ +void sch5545_emi_set_int_mask_low(uint8_t mask); +/** + * Sets the EC interrupt mask for MSB in the Interrupt Mask register. + */ +void sch5545_emi_set_int_mask_high(uint8_t mask); +/** + * Returns LSB of Interrupt mask register. + */ +uint8_t sch5545_emi_get_int_mask_low(void); +/** + * Returns MSB of Interrupt mask register. + */ +uint8_t sch5545_emi_get_int_mask_high(void); +/** + * Returns the content of interrupt mask register. + */ +uint16_t sch5545_emi_get_int_mask(void); +/** + * Clears the interrupt status bits. + */ +void sch5545_emi_clear_int_src(void); +/** + * Writes int_src bits to clear the desired interrupt source LSB. + */ +void sch5545_emi_set_int_src_low(uint8_t int_src); +/** + * Writes int_src bits to clear the desired interrupt source MSB. + */ +void sch5545_emi_set_int_src_high(uint8_t int_src); +/** + * Writes int_src bits to clear the desired interrupt source bits. + */ +void sch5545_emi_set_int_src(uint16_t int_src); +/** + * Returns LSB of interrupt source register. + */ +uint8_t sch5545_emi_get_int_src_low(void); +/** + * Returns MSB of interrupt source register. + */ +uint8_t sch5545_emi_get_int_src_high(void); +/** + * Returns the content of interrupt source register. + */ +uint16_t sch5545_emi_get_int_src(void); +/** + * Sets the EC address registers with given addr for indirect access to + * Embedded Memory. + */ +void sch5545_emi_set_ec_addr(uint16_t addr); +/** + * Return the current EC address used for indirect access to Embedded Memory. + */ +uint16_t sch5545_emi_read_ec_addr(void); +/** + * Writes any byte of 4 bytes from the 32bit dword indicated by addr. The + * function will automatically align to the matching 32bit dword. + */ +void sch5545_emi_ec_write8(uint16_t addr, uint8_t data); +/** + * Writes any word of 2 words from the 32bit dword indicated by addr. The addr + * must be aligned to 16bit access, because function programs the right access + * mode rounding the address to be written to 16 bit boundary. + */ +void sch5545_emi_ec_write16(uint16_t addr, uint16_t data); +/** + * Writes dword of data at the desired address indicated by addr. The addr must + * be aligned to 32bit access, because function programs the right access mode + * rounding the address to be written to 32 bit boundary. + */ +void sch5545_emi_ec_write32(uint16_t addr, uint32_t data); +/** + * Writes an array of dwords at the desired address indicated by addr. The addr + * must be aligned to 32bit access, because function programs the right access + * mode rounding the address to be written to 32 bit boundary. The address is + * autoincremented by each IO write operation automatically. + */ +void sch5545_emi_ec_write32_bulk(uint16_t addr, const uint32_t *buffer, size_t len); +/** + * Reads any byte of 4 bytes from the 32bit dword indicated by addr. The + * function will automatically align to the matching 32bit dword. + */ +uint8_t sch5545_emi_ec_read8(uint16_t addr); +/** + * Reads any word of 2 words from the 32bit dword indicated by addr. The addr + * must be aligned to 16bit access, because function programs the right access + * mode rounding the address to be read to 16 bit boundary. + */ +uint16_t sch5545_emi_ec_read16(uint16_t addr); +/** + * Reads dword of data at the desired address indicated by addr. The addr must + * be aligned to 32bit access, because function programs the right access mode + * rounding the address to be read to 32 bit boundary. + */ +uint32_t sch5545_emi_ec_read32(uint16_t addr); +/** + * Reads a stream of dwords of size len to an array of dwords from the desired + * address indicated by addr. The addr must be aligned to 32bit access, because + * function programs the right access mode rounding the start address to be + * read to 32 bit boundary. The address is autoincremented by each IO read + * operation automatically. + */ +void sch5545_emi_ec_read32_bulk(uint16_t addr, uint32_t *buffer, size_t len); + +#endif /* SUPERIO_SCH_5545_EMI_H */ diff --git a/src/superio/smsc/sch5545/superio.c b/src/superio/smsc/sch5545/superio.c new file mode 100644 index 0000000000..b6e5308f3c --- /dev/null +++ b/src/superio/smsc/sch5545/superio.c @@ -0,0 +1,301 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#include +#include +#include +#include +#include +#include + +#include "sch5545.h" + +int sch5545_get_gpio(uint8_t sio_port, uint8_t gpio) +{ + struct device *dev; + uint16_t runtime_reg_base; + uint8_t gpio_bank, gpio_num; + + gpio_bank = gpio / 10; + gpio_num = gpio % 10; + /* + * GPIOs are divided into banks of 8 GPIOs (kind of). Each group starts + * at decimal base, i.e. 8 GPIOs from GPIO000, 8 GPIOs from GPIO010, + * etc., up to GPIO071 and GPIO072 which are an exception (only two + * gpios in the bank 7). + */ + if (gpio_num > 7) + return -1; + else if (gpio_bank == 7 && gpio_num > 1) + return -1; + else if (gpio_bank > 7) + return -1; + + dev = dev_find_slot_pnp(sio_port, SCH5545_LDN_LPC); + + if (!dev) { + printk(BIOS_ERR, "%s: ERROR: LPC interface LDN not present." + "Check the devicetree!\n", __func__); + return -1; + } + + pnp_enter_conf_mode(dev); + pnp_set_logical_device(dev); + + runtime_reg_base = pnp_read_config(dev, SCH5545_BAR_RUNTIME_REG + 2); + runtime_reg_base |= pnp_read_config(dev, SCH5545_BAR_RUNTIME_REG + 3) << 8; + + pnp_exit_conf_mode(dev); + + if (runtime_reg_base == 0) + return -1; + + outb(gpio_bank * 8 + gpio_num, runtime_reg_base + SCH5545_RR_GPIO_SEL); + + return inb(runtime_reg_base + SCH5545_RR_GPIO_READ) & 1; +} + +static void sch5545_init(struct device *dev) +{ + if (!dev->enabled) + return; + + switch (dev->path.pnp.device) { + case SCH5545_LDN_KBC: + pc_keyboard_init(NO_AUX_DEVICE); + break; + case SCH5545_LDN_LPC: + pnp_enter_conf_mode(dev); + pnp_set_logical_device(dev); + /* Enable SERIRQ */ + pnp_write_config(dev, 0x24, pnp_read_config(dev, 0x24) | 0x04); + pnp_exit_conf_mode(dev); + break; + } +} + +static void sch5545_set_iobase(struct device *dev, u8 index, u16 iobase) +{ + u8 val; + struct device *lpc_if; + u8 iobase_reg = 0; + + lpc_if = dev_find_slot_pnp(dev->path.pnp.port, SCH5545_LDN_LPC); + + if (!lpc_if) { + printk(BIOS_ERR, "ERROR: %s LPC interface LDN not present." + "Check the devicetree!\n", dev_path(dev)); + return; + } + + switch (dev->path.pnp.device) { + case SCH5545_LDN_EMI: + iobase_reg = SCH5545_BAR_EM_IF; + break; + case SCH5545_LDN_KBC: + iobase_reg = SCH5545_BAR_KBC; + break; + case SCH5545_LDN_UART1: + iobase_reg = SCH5545_BAR_UART1; + break; + case SCH5545_LDN_UART2: + iobase_reg = SCH5545_BAR_UART2; + break; + case SCH5545_LDN_RR: + iobase_reg = SCH5545_BAR_RUNTIME_REG; + break; + case SCH5545_LDN_FDC: + iobase_reg = SCH5545_BAR_FLOPPY; + break; + case SCH5545_LDN_LPC: + iobase_reg = SCH5545_BAR_LPC_IF; + break; + case SCH5545_LDN_PP: + iobase_reg = SCH5545_BAR_PARPORT; + break; + default: + return; + } + + pnp_set_logical_device(lpc_if); + + /* Flip the bytes in IO base, LSB goes first */ + pnp_write_config(lpc_if, iobase_reg + 2, iobase & 0xff); + pnp_write_config(lpc_if, iobase_reg + 3, (iobase >> 8) & 0xff); + + /* Set valid bit */ + val = pnp_read_config(lpc_if, iobase_reg + 1); + val |= 0x80; + pnp_write_config(lpc_if, iobase_reg + 1, val); + + pnp_set_logical_device(dev); +} + +static void sch5545_set_irq(struct device *dev, u8 index, u8 irq) +{ + u8 select_bit = 0; + struct device *lpc_if; + + /* In case it is not the IRQ, write misc register directly */ + if (index >= PNP_IDX_MSC0) { + pnp_write_config(dev, index, irq); + return; + } + + lpc_if = dev_find_slot_pnp(dev->path.pnp.port, SCH5545_LDN_LPC); + + if (!lpc_if) { + printk(BIOS_ERR, "ERROR: %s LPC interface LDN not present." + "Check the devicetree!\n", dev_path(dev)); + return; + } + + pnp_set_logical_device(lpc_if); + + /* + * Some LDNs can generate IRQs from two sources, i.e. + * - EMI may generate interrupts for Mailbox and INT source register + * - KBC may generate separate IRQ for mouse and keyboard, + * - RR LDN may generate IRQ for PME and SMI etc. + * SELECT bit allows to distinguish IRQ source for single LDN. + * Use the standard IRQs for devices. + */ + switch (dev->path.pnp.device) { + case SCH5545_LDN_EMI: + case SCH5545_LDN_KBC: + case SCH5545_LDN_RR: + if (index == 0x72) + select_bit = 0x80; + break; + default: + break; + } + + /* + * IRQs are set in a little different manner. Each IRQ number has its + * own register which is programmed with LDN number which should use + * the IRQ. Ignore the index offset and choose register based on IRQ + * number counting from IRQ base. + */ + pnp_write_config(lpc_if, SCH5545_IRQ_BASE + irq, dev->path.pnp.device | select_bit); + pnp_set_logical_device(dev); +} + +static void sch5545_set_drq(struct device *dev, u8 index, u8 drq) +{ + struct device *lpc_if; + + if (drq == 4) { + printk(BIOS_ERR, "ERROR: %s %02x: Trying to set reserved DMA channel 4!\n", + dev_path(dev), index); + printk(BIOS_ERR, "This configuration is untested. Trying to continue.\n"); + } + + /* DMA channel is programmed via LPC LDN */ + lpc_if = dev_find_slot_pnp(dev->path.pnp.port, SCH5545_LDN_LPC); + + if (!lpc_if) { + printk(BIOS_ERR, "ERROR: %s LPC interface LDN not present." + "Check the devicetree!\n", dev_path(dev)); + return; + } + + pnp_set_logical_device(lpc_if); + + /* + * There are 8 configurable DMA channels. DRQs are set in a little + * different manner. Each DMA channel has its own 16-bit register which + * is programmed with LDN number (in higher byte) which should use the + * IRQ. Ignore the index offset and choose register based on IRQ number + * counting from IRQ base. Set valid bit (bit 7) additionally. + */ + pnp_write_config(dev, SCH5545_DRQ_BASE + (drq * 2) + 1, dev->path.pnp.device | 0x80); + + pnp_set_logical_device(dev); +} + +static void sch5545_set_resource(struct device *dev, struct resource *resource) +{ + if (!(resource->flags & IORESOURCE_ASSIGNED)) { + /* + * The PNP_MSC super IO registers have the IRQ flag set. If no + * value is assigned in the devicetree, the corresponding + * PNP_MSC register doesn't get written, which should be printed + * as warning and not as error. + */ + if (resource->flags & IORESOURCE_IRQ && + (resource->index != PNP_IDX_IRQ0) && + (resource->index != PNP_IDX_IRQ1)) + printk(BIOS_WARNING, "WARNING: %s %02lx %s size: " + "0x%010llx not assigned\n", dev_path(dev), + resource->index, resource_type(resource), + resource->size); + else + printk(BIOS_ERR, "ERROR: %s %02lx %s size: 0x%010llx " + "not assigned\n", dev_path(dev), resource->index, + resource_type(resource), resource->size); + return; + } + + /* Now store the resource. */ + if (resource->flags & IORESOURCE_IO) { + sch5545_set_iobase(dev, resource->index, resource->base); + } else if (resource->flags & IORESOURCE_DRQ) { + sch5545_set_drq(dev, resource->index, resource->base); + } else if (resource->flags & IORESOURCE_IRQ) { + sch5545_set_irq(dev, resource->index, resource->base); + } else { + printk(BIOS_ERR, "ERROR: %s %02lx unknown resource type\n", + dev_path(dev), resource->index); + return; + } + resource->flags |= IORESOURCE_STORED; + + report_resource_stored(dev, resource, ""); +} + +static void sch5545_set_resources(struct device *dev) +{ + struct resource *res; + + pnp_enter_conf_mode(dev); + + /* Select the logical device (LDN). */ + pnp_set_logical_device(dev); + + for (res = dev->resource_list; res; res = res->next) + sch5545_set_resource(dev, res); + + pnp_exit_conf_mode(dev); +} + +static struct device_operations ops = { + .read_resources = pnp_read_resources, + .set_resources = sch5545_set_resources, + .enable_resources = pnp_enable_resources, + .enable = pnp_alt_enable, + .init = sch5545_init, + .ops_pnp_mode = &pnp_conf_mode_55_aa, +}; + +static struct pnp_info pnp_dev_info[] = { + { NULL, SCH5545_LDN_EMI, PNP_IO0 | PNP_IRQ0 | PNP_IRQ1, 0x0ff0 }, + { NULL, SCH5545_LDN_KBC, PNP_IO0 | PNP_IRQ0 | PNP_IRQ1 | PNP_MSC0 | PNP_MSC1, 0x0fff }, + { NULL, SCH5545_LDN_UART1, PNP_IO0 | PNP_IRQ0 | PNP_MSC0, 0x0ff8 }, + { NULL, SCH5545_LDN_UART2, PNP_IO0 | PNP_IRQ0 | PNP_MSC0, 0x0ff8 }, + { NULL, SCH5545_LDN_RR, PNP_IO0 | PNP_IRQ0 | PNP_IRQ1 | PNP_MSC0, 0x0fc0 }, + { NULL, SCH5545_LDN_FDC, PNP_IO0 | PNP_IRQ0 | PNP_DRQ0 | PNP_MSC0 | PNP_MSC1 | + PNP_MSC2 | PNP_MSC3 | PNP_MSC4 | PNP_MSC5, 0x0ff8, }, + { NULL, SCH5545_LDN_LPC, PNP_IO0, 0x0ffe }, + { NULL, SCH5545_LDN_PP, PNP_IO0 | PNP_IRQ0 | PNP_DRQ0 | PNP_MSC0 | PNP_MSC1, 0x0ff8 }, +}; + +static void enable_dev(struct device *dev) +{ + pnp_enable_devices(dev, &ops, ARRAY_SIZE(pnp_dev_info), pnp_dev_info); +} + +struct chip_operations superio_smsc_sch5545_ops = { + CHIP_NAME("SMSC SCH5545 Super I/O") + .enable_dev = enable_dev, +};