Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
superio/smsc: Add support for the SCH555x series
Used by the OptiPlex 3020/7020/9020: - EMI and Runtime registers work - UART1 works (including IRQs) - PS/2 keyboard and mouse untested Signed-off-by: Mate Kukri <kukri.mate@gmail.com> Change-Id: I9323198f1139cd0c3dd37f977ae7693b721654f4 Reviewed-on: https://review.coreboot.org/c/coreboot/+/64359 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Martin L Roth <gaumless@gmail.com>
- Loading branch information
Showing
7 changed files
with
329 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,3 +13,4 @@ subdirs-y += mec1308 | |
subdirs-y += smscsuperio | ||
subdirs-y += sio1036 | ||
subdirs-y += sch5545 | ||
subdirs-y += sch555x |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
## SPDX-License-Identifier: GPL-2.0-only | ||
|
||
config SUPERIO_SMSC_SCH555x | ||
bool |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
# SPDX-License-Identifier: GPL-2.0-only | ||
|
||
bootblock-$(CONFIG_SUPERIO_SMSC_SCH555x) += emi.c bootblock.c | ||
ramstage-$(CONFIG_SUPERIO_SMSC_SCH555x) += ramstage.c |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
/* SPDX-License-Identifier: GPL-2.0-only */ | ||
|
||
#include <arch/io.h> | ||
#include <device/pnp_ops.h> | ||
#include "sch555x.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); | ||
} | ||
|
||
static void pnp_write_config32(pnp_devfn_t dev, uint8_t offset, uint32_t value) | ||
{ | ||
pnp_write_config(dev, offset, value & 0xff); | ||
pnp_write_config(dev, offset + 1, (value >> 8) & 0xff); | ||
pnp_write_config(dev, offset + 2, (value >> 16) & 0xff); | ||
pnp_write_config(dev, offset + 3, (value >> 24) & 0xff); | ||
} | ||
|
||
/* | ||
* Do just enough init so that the motherboard specific magic EMI | ||
* sequences can be sent before sch555x_enable_serial is called | ||
*/ | ||
void sch555x_early_init(pnp_devfn_t global_dev) | ||
{ | ||
pnp_enter_conf_state(global_dev); | ||
|
||
// Enable IRQs | ||
pnp_set_logical_device(global_dev); | ||
pnp_write_config(global_dev, SCH555x_DEVICE_MODE, 0x04); | ||
|
||
// Map EMI and runtime registers | ||
pnp_devfn_t lpci_dev = PNP_DEV(global_dev >> 8, SCH555x_LDN_LPCI); | ||
|
||
pnp_set_logical_device(lpci_dev); | ||
pnp_write_config32(lpci_dev, SCH555x_LPCI_EMI_BAR, | ||
(SCH555x_EMI_IOBASE << 16) | 0x800f); | ||
pnp_write_config32(lpci_dev, SCH555x_LPCI_RUNTIME_BAR, | ||
(SCH555x_RUNTIME_IOBASE << 16) | 0x8a3f); | ||
|
||
pnp_exit_conf_state(global_dev); | ||
} | ||
|
||
void sch555x_enable_serial(pnp_devfn_t uart_dev, uint16_t serial_iobase) | ||
{ | ||
pnp_enter_conf_state(uart_dev); | ||
|
||
// Set LPCI BAR register to map UART into I/O space | ||
pnp_devfn_t lpci_dev = PNP_DEV(uart_dev >> 8, SCH555x_LDN_LPCI); | ||
|
||
pnp_set_logical_device(lpci_dev); | ||
u8 uart_bar = (uart_dev & 0xff) == SCH555x_LDN_UART1 | ||
? SCH555x_LPCI_UART1_BAR | ||
: SCH555x_LPCI_UART2_BAR; | ||
pnp_write_config32(lpci_dev, uart_bar, serial_iobase << 16 | 0x8707); | ||
|
||
// Set up the UART's configuration registers | ||
pnp_set_logical_device(uart_dev); | ||
pnp_set_enable(uart_dev, 1); // Activate | ||
pnp_write_config(uart_dev, 0x0f, 0x02); // Config select | ||
|
||
pnp_exit_conf_state(uart_dev); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
/* SPDX-License-Identifier: GPL-2.0-only */ | ||
|
||
#include <arch/io.h> | ||
#include <device/pnp_ops.h> | ||
#include "sch555x.h" | ||
|
||
uint8_t sch555x_emi_read8(uint16_t addr) | ||
{ | ||
outw(addr | 0x8000, SCH555x_EMI_IOBASE + 2); | ||
return inb(SCH555x_EMI_IOBASE + 4); | ||
} | ||
|
||
uint16_t sch555x_emi_read16(uint16_t addr) | ||
{ | ||
outw(addr | 0x8001, SCH555x_EMI_IOBASE + 2); | ||
return inw(SCH555x_EMI_IOBASE + 4); | ||
} | ||
|
||
uint32_t sch555x_emi_read32(uint16_t addr) | ||
{ | ||
outw(addr | 0x8002, SCH555x_EMI_IOBASE + 2); | ||
return inl(SCH555x_EMI_IOBASE + 4); | ||
} | ||
|
||
void sch555x_emi_write8(uint16_t addr, uint8_t val) | ||
{ | ||
outw(addr | 0x8000, SCH555x_EMI_IOBASE + 2); | ||
outb(val, SCH555x_EMI_IOBASE + 4); | ||
} | ||
|
||
void sch555x_emi_write16(uint16_t addr, uint16_t val) | ||
{ | ||
outw(addr | 0x8001, SCH555x_EMI_IOBASE + 2); | ||
outw(val, SCH555x_EMI_IOBASE + 4); | ||
} | ||
|
||
void sch555x_emi_write32(uint16_t addr, uint32_t val) | ||
{ | ||
outw(addr | 0x8002, SCH555x_EMI_IOBASE + 2); | ||
outl(val, SCH555x_EMI_IOBASE + 4); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
;;/* SPDX-License-Identifier: GPL-2.0-only */ | ||
|
||
#include <arch/io.h> | ||
#include <console/console.h> | ||
#include <device/pnp.h> | ||
#include <pc80/keyboard.h> | ||
#include <superio/conf_mode.h> | ||
#include "sch555x.h" | ||
|
||
static void sch555x_init(struct device *dev) | ||
{ | ||
if (dev->enabled && dev->path.pnp.device == SCH555x_LDN_8042) | ||
pc_keyboard_init(NO_AUX_DEVICE); | ||
} | ||
|
||
static uint8_t sch555x_ldn_to_bar(uint8_t ldn) | ||
{ | ||
switch (ldn) { | ||
case SCH555x_LDN_LPCI: | ||
return SCH555x_LPCI_LPCI_BAR; | ||
case SCH555x_LDN_EMI: | ||
return SCH555x_LPCI_EMI_BAR; | ||
case SCH555x_LDN_UART1: | ||
return SCH555x_LPCI_UART1_BAR; | ||
case SCH555x_LDN_UART2: | ||
return SCH555x_LPCI_UART2_BAR; | ||
case SCH555x_LDN_RUNTIME: | ||
return SCH555x_LPCI_RUNTIME_BAR; | ||
case SCH555x_LDN_8042: | ||
return SCH555x_LPCI_8042_BAR; | ||
case SCH555x_LDN_FDC: | ||
return SCH555x_LPCI_FDC_BAR; | ||
case SCH555x_LDN_PP: | ||
return SCH555x_LPCI_PP_BAR; | ||
default: | ||
return 0; | ||
} | ||
} | ||
|
||
/* | ||
* IO BARs don't live in normal LDN configuration space but in the LPC interface. | ||
* Thus we ignore the index and choose what BAR to set just based on the LDN. | ||
*/ | ||
static void sch555x_set_iobase(struct device *lpci, struct device *dev, | ||
uint8_t index, uint16_t iobase) | ||
{ | ||
const uint8_t bar = sch555x_ldn_to_bar(dev->path.pnp.device); | ||
if (bar) { | ||
pnp_set_logical_device(lpci); | ||
pnp_unset_and_set_config(lpci, bar + 1, 0, 1 << 7); | ||
pnp_write_config(lpci, bar + 2, iobase & 0xff); | ||
pnp_write_config(lpci, bar + 3, (iobase >> 8) & 0xff); | ||
} | ||
} | ||
|
||
/* | ||
* IRQs don't live in normal LDN configuration space but in the LPC interface. | ||
* | ||
* The following fake offsets are used: | ||
* 0x70 => First IRQ | ||
* 0x72 => Second IRQ | ||
*/ | ||
static void sch555x_set_irq(struct device *lpci, struct device *dev, | ||
uint8_t index, uint8_t irq) | ||
{ | ||
if (index >= PNP_IDX_MSC0) { | ||
pnp_set_logical_device(dev); | ||
pnp_write_config(dev, index, irq); | ||
return; | ||
} | ||
|
||
pnp_set_logical_device(lpci); | ||
switch (index) { | ||
case 0x70: | ||
pnp_write_config(lpci, SCH555x_LPCI_IRQ(irq), dev->path.pnp.device); | ||
break; | ||
case 0x72: | ||
pnp_write_config(lpci, SCH555x_LPCI_IRQ(irq), dev->path.pnp.device | 0x80); | ||
break; | ||
} | ||
} | ||
|
||
/* | ||
* DMA channels don't live in normal LDN configuration space but in the LPC interface. | ||
*/ | ||
static void sch555x_set_drq(struct device *lpci, struct device *dev, | ||
uint8_t index, uint8_t drq) | ||
{ | ||
pnp_set_logical_device(lpci); | ||
pnp_write_config(lpci, SCH555x_LPCI_DMA(drq), dev->path.pnp.device | 0x80); | ||
} | ||
|
||
static void sch555x_set_resources(struct device *dev) | ||
{ | ||
struct device *lpci = dev_find_slot_pnp(dev->path.pnp.port, SCH555x_LDN_LPCI); | ||
if (!lpci) { | ||
printk(BIOS_ERR, "SCH555x LPC interface not present in device tree!\n"); | ||
return; | ||
} | ||
|
||
pnp_enter_conf_mode(dev); | ||
for (struct resource *res = dev->resource_list; res; res = res->next) { | ||
if (res->flags & IORESOURCE_IO) | ||
sch555x_set_iobase(lpci, dev, res->index, res->base); | ||
else if (res->flags & IORESOURCE_DRQ) | ||
sch555x_set_drq(lpci, dev, res->index, res->base); | ||
else if (res->flags & IORESOURCE_IRQ) | ||
sch555x_set_irq(lpci, dev, res->index, res->base); | ||
} | ||
pnp_exit_conf_mode(dev); | ||
} | ||
|
||
static void sch555x_enable_dev(struct device *dev) | ||
{ | ||
static struct device_operations ops = { | ||
.read_resources = pnp_read_resources, | ||
.set_resources = sch555x_set_resources, | ||
.enable_resources = pnp_enable_resources, | ||
.enable = pnp_alt_enable, | ||
.init = sch555x_init, | ||
.ops_pnp_mode = &pnp_conf_mode_55_aa, | ||
}; | ||
|
||
static struct pnp_info pnp_dev_info[] = { | ||
{ NULL, SCH555x_LDN_EMI, PNP_IO0 | PNP_IRQ0 | PNP_IRQ1, 0x0ff0 }, | ||
{ NULL, SCH555x_LDN_8042, PNP_IO0 | PNP_IRQ0 | PNP_IRQ1, 0x0fff }, | ||
{ NULL, SCH555x_LDN_UART1, PNP_IO0 | PNP_IRQ0 | PNP_MSC0, 0x0ff8 }, | ||
{ NULL, SCH555x_LDN_UART2, PNP_IO0 | PNP_IRQ0 | PNP_MSC0, 0x0ff8 }, | ||
{ NULL, SCH555x_LDN_LPCI, PNP_IO0, 0x0ffe }, | ||
{ NULL, SCH555x_LDN_RUNTIME, PNP_IO0 | PNP_IRQ0 | PNP_IRQ1, 0x0fc0 }, | ||
{ NULL, SCH555x_LDN_FDC, PNP_IO0 | PNP_IRQ0 | PNP_DRQ0, 0x0ff8, }, | ||
{ NULL, SCH555x_LDN_PP, PNP_IO0 | PNP_IRQ0 | PNP_DRQ0, 0x0ff8 }, | ||
}; | ||
|
||
pnp_enable_devices(dev, &ops, ARRAY_SIZE(pnp_dev_info), pnp_dev_info); | ||
} | ||
|
||
struct chip_operations superio_smsc_sch555x_ops = { | ||
CHIP_NAME("SMSC SCH555x Super I/O") | ||
.enable_dev = sch555x_enable_dev, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
/* SPDX-License-Identifier: GPL-2.0-only */ | ||
|
||
#ifndef SUPERIO_SCH555x_H | ||
#define SUPERIO_SCH555x_H | ||
|
||
#include <types.h> | ||
|
||
// Global registers | ||
#define SCH555x_DEVICE_ID 0x20 | ||
#define SCH555x_DEVICE_REV 0x21 | ||
#define SCH555x_DEVICE_MODE 0x24 | ||
|
||
// Logical device numbers | ||
#define SCH555x_LDN_EMI 0x00 | ||
#define SCH555x_LDN_8042 0x01 | ||
#define SCH555x_LDN_UART1 0x07 | ||
#define SCH555x_LDN_UART2 0x08 | ||
#define SCH555x_LDN_RUNTIME 0x0a | ||
#define SCH555x_LDN_FDC 0x0b | ||
#define SCH555x_LDN_LPCI 0x0c | ||
#define SCH555x_LDN_PP 0x11 | ||
#define SCH555x_LDN_GLOBAL 0x3f | ||
|
||
// LPC interface registers | ||
#define SCH555x_LPCI_IRQ(i) (0x40 + (i)) | ||
// DMA channel register is 2 bytes, we care about the second byte | ||
#define SCH555x_LPCI_DMA(i) (0x50 + (i) * 2 + 1) | ||
// BAR offset (inside LPCI) for each LDN | ||
#define SCH555x_LPCI_LPCI_BAR 0x60 | ||
#define SCH555x_LPCI_EMI_BAR 0x64 | ||
#define SCH555x_LPCI_UART1_BAR 0x68 | ||
#define SCH555x_LPCI_UART2_BAR 0x6c | ||
#define SCH555x_LPCI_RUNTIME_BAR 0x70 | ||
#define SCH555x_LPCI_8042_BAR 0x78 | ||
#define SCH555x_LPCI_FDC_BAR 0x7c | ||
#define SCH555x_LPCI_PP_BAR 0x80 | ||
|
||
// Runtime registers (in I/O space) | ||
#define SCH555x_RUNTIME_PME_STS 0x00 | ||
#define SCH555x_RUNTIME_PME_EN 0x01 | ||
#define SCH555x_RUNTIME_PME_EN1 0x05 | ||
#define SCH555x_RUNTIME_LED 0x25 | ||
// NOTE: not in the SCH5627P datasheet but Dell's firmware writes to it | ||
#define SCH555x_RUNTIME_UNK1 0x35 | ||
|
||
// Needed in the bootblock, thus we map them at a fixed address | ||
#define SCH555x_EMI_IOBASE 0xa00 | ||
#define SCH555x_RUNTIME_IOBASE 0xa40 | ||
|
||
/* | ||
* EMI access | ||
*/ | ||
|
||
uint8_t sch555x_emi_read8(uint16_t addr); | ||
uint16_t sch555x_emi_read16(uint16_t addr); | ||
uint32_t sch555x_emi_read32(uint16_t addr); | ||
void sch555x_emi_write8(uint16_t addr, uint8_t val); | ||
void sch555x_emi_write16(uint16_t addr, uint16_t val); | ||
void sch555x_emi_write32(uint16_t addr, uint32_t val); | ||
|
||
/* | ||
* Bootblock entry points | ||
*/ | ||
|
||
void sch555x_early_init(pnp_devfn_t global_dev); | ||
void sch555x_enable_serial(pnp_devfn_t uart_dev, uint16_t serial_iobase); | ||
|
||
#endif |