162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * the ISA Virtual Support Module of AMD CS5536
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2007 Lemote, Inc.
662306a36Sopenharmony_ci * Author : jlliu, liujl@lemote.com
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Copyright (C) 2009 Lemote, Inc.
962306a36Sopenharmony_ci * Author: Wu Zhangjin, wuzhangjin@gmail.com
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/pci.h>
1362306a36Sopenharmony_ci#include <cs5536/cs5536.h>
1462306a36Sopenharmony_ci#include <cs5536/cs5536_pci.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci/* common variables for PCI_ISA_READ/WRITE_BAR */
1762306a36Sopenharmony_cistatic const u32 divil_msr_reg[6] = {
1862306a36Sopenharmony_ci	DIVIL_MSR_REG(DIVIL_LBAR_SMB), DIVIL_MSR_REG(DIVIL_LBAR_GPIO),
1962306a36Sopenharmony_ci	DIVIL_MSR_REG(DIVIL_LBAR_MFGPT), DIVIL_MSR_REG(DIVIL_LBAR_IRQ),
2062306a36Sopenharmony_ci	DIVIL_MSR_REG(DIVIL_LBAR_PMS), DIVIL_MSR_REG(DIVIL_LBAR_ACPI),
2162306a36Sopenharmony_ci};
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistatic const u32 soft_bar_flag[6] = {
2462306a36Sopenharmony_ci	SOFT_BAR_SMB_FLAG, SOFT_BAR_GPIO_FLAG, SOFT_BAR_MFGPT_FLAG,
2562306a36Sopenharmony_ci	SOFT_BAR_IRQ_FLAG, SOFT_BAR_PMS_FLAG, SOFT_BAR_ACPI_FLAG,
2662306a36Sopenharmony_ci};
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistatic const u32 sb_msr_reg[6] = {
2962306a36Sopenharmony_ci	SB_MSR_REG(SB_R0), SB_MSR_REG(SB_R1), SB_MSR_REG(SB_R2),
3062306a36Sopenharmony_ci	SB_MSR_REG(SB_R3), SB_MSR_REG(SB_R4), SB_MSR_REG(SB_R5),
3162306a36Sopenharmony_ci};
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic const u32 bar_space_range[6] = {
3462306a36Sopenharmony_ci	CS5536_SMB_RANGE, CS5536_GPIO_RANGE, CS5536_MFGPT_RANGE,
3562306a36Sopenharmony_ci	CS5536_IRQ_RANGE, CS5536_PMS_RANGE, CS5536_ACPI_RANGE,
3662306a36Sopenharmony_ci};
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic const int bar_space_len[6] = {
3962306a36Sopenharmony_ci	CS5536_SMB_LENGTH, CS5536_GPIO_LENGTH, CS5536_MFGPT_LENGTH,
4062306a36Sopenharmony_ci	CS5536_IRQ_LENGTH, CS5536_PMS_LENGTH, CS5536_ACPI_LENGTH,
4162306a36Sopenharmony_ci};
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci/*
4462306a36Sopenharmony_ci * enable the divil module bar space.
4562306a36Sopenharmony_ci *
4662306a36Sopenharmony_ci * For all the DIVIL module LBAR, you should control the DIVIL LBAR reg
4762306a36Sopenharmony_ci * and the RCONFx(0~5) reg to use the modules.
4862306a36Sopenharmony_ci */
4962306a36Sopenharmony_cistatic void divil_lbar_enable(void)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	u32 hi, lo;
5262306a36Sopenharmony_ci	int offset;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	/*
5562306a36Sopenharmony_ci	 * The DIVIL IRQ is not used yet. and make the RCONF0 reserved.
5662306a36Sopenharmony_ci	 */
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	for (offset = DIVIL_LBAR_SMB; offset <= DIVIL_LBAR_PMS; offset++) {
5962306a36Sopenharmony_ci		_rdmsr(DIVIL_MSR_REG(offset), &hi, &lo);
6062306a36Sopenharmony_ci		hi |= 0x01;
6162306a36Sopenharmony_ci		_wrmsr(DIVIL_MSR_REG(offset), hi, lo);
6262306a36Sopenharmony_ci	}
6362306a36Sopenharmony_ci}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci/*
6662306a36Sopenharmony_ci * disable the divil module bar space.
6762306a36Sopenharmony_ci */
6862306a36Sopenharmony_cistatic void divil_lbar_disable(void)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	u32 hi, lo;
7162306a36Sopenharmony_ci	int offset;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	for (offset = DIVIL_LBAR_SMB; offset <= DIVIL_LBAR_PMS; offset++) {
7462306a36Sopenharmony_ci		_rdmsr(DIVIL_MSR_REG(offset), &hi, &lo);
7562306a36Sopenharmony_ci		hi &= ~0x01;
7662306a36Sopenharmony_ci		_wrmsr(DIVIL_MSR_REG(offset), hi, lo);
7762306a36Sopenharmony_ci	}
7862306a36Sopenharmony_ci}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci/*
8162306a36Sopenharmony_ci * BAR write: write value to the n BAR
8262306a36Sopenharmony_ci */
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_civoid pci_isa_write_bar(int n, u32 value)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	u32 hi = 0, lo = value;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	if (value == PCI_BAR_RANGE_MASK) {
8962306a36Sopenharmony_ci		_rdmsr(GLCP_MSR_REG(GLCP_SOFT_COM), &hi, &lo);
9062306a36Sopenharmony_ci		lo |= soft_bar_flag[n];
9162306a36Sopenharmony_ci		_wrmsr(GLCP_MSR_REG(GLCP_SOFT_COM), hi, lo);
9262306a36Sopenharmony_ci	} else if (value & 0x01) {
9362306a36Sopenharmony_ci		/* NATIVE reg */
9462306a36Sopenharmony_ci		hi = 0x0000f001;
9562306a36Sopenharmony_ci		lo &= bar_space_range[n];
9662306a36Sopenharmony_ci		_wrmsr(divil_msr_reg[n], hi, lo);
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci		/* RCONFx is 4bytes in units for I/O space */
9962306a36Sopenharmony_ci		hi = ((value & 0x000ffffc) << 12) |
10062306a36Sopenharmony_ci		    ((bar_space_len[n] - 4) << 12) | 0x01;
10162306a36Sopenharmony_ci		lo = ((value & 0x000ffffc) << 12) | 0x01;
10262306a36Sopenharmony_ci		_wrmsr(sb_msr_reg[n], hi, lo);
10362306a36Sopenharmony_ci	}
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci/*
10762306a36Sopenharmony_ci * BAR read: read the n BAR
10862306a36Sopenharmony_ci */
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ciu32 pci_isa_read_bar(int n)
11162306a36Sopenharmony_ci{
11262306a36Sopenharmony_ci	u32 conf_data = 0;
11362306a36Sopenharmony_ci	u32 hi, lo;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	_rdmsr(GLCP_MSR_REG(GLCP_SOFT_COM), &hi, &lo);
11662306a36Sopenharmony_ci	if (lo & soft_bar_flag[n]) {
11762306a36Sopenharmony_ci		conf_data = bar_space_range[n] | PCI_BASE_ADDRESS_SPACE_IO;
11862306a36Sopenharmony_ci		lo &= ~soft_bar_flag[n];
11962306a36Sopenharmony_ci		_wrmsr(GLCP_MSR_REG(GLCP_SOFT_COM), hi, lo);
12062306a36Sopenharmony_ci	} else {
12162306a36Sopenharmony_ci		_rdmsr(divil_msr_reg[n], &hi, &lo);
12262306a36Sopenharmony_ci		conf_data = lo & bar_space_range[n];
12362306a36Sopenharmony_ci		conf_data |= 0x01;
12462306a36Sopenharmony_ci		conf_data &= ~0x02;
12562306a36Sopenharmony_ci	}
12662306a36Sopenharmony_ci	return conf_data;
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci/*
13062306a36Sopenharmony_ci * isa_write: ISA write transfer
13162306a36Sopenharmony_ci *
13262306a36Sopenharmony_ci * We assume that this is not a bus master transfer.
13362306a36Sopenharmony_ci */
13462306a36Sopenharmony_civoid pci_isa_write_reg(int reg, u32 value)
13562306a36Sopenharmony_ci{
13662306a36Sopenharmony_ci	u32 hi = 0, lo = value;
13762306a36Sopenharmony_ci	u32 temp;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	switch (reg) {
14062306a36Sopenharmony_ci	case PCI_COMMAND:
14162306a36Sopenharmony_ci		if (value & PCI_COMMAND_IO)
14262306a36Sopenharmony_ci			divil_lbar_enable();
14362306a36Sopenharmony_ci		else
14462306a36Sopenharmony_ci			divil_lbar_disable();
14562306a36Sopenharmony_ci		break;
14662306a36Sopenharmony_ci	case PCI_STATUS:
14762306a36Sopenharmony_ci		_rdmsr(SB_MSR_REG(SB_ERROR), &hi, &lo);
14862306a36Sopenharmony_ci		temp = lo & 0x0000ffff;
14962306a36Sopenharmony_ci		if ((value & PCI_STATUS_SIG_TARGET_ABORT) &&
15062306a36Sopenharmony_ci		    (lo & SB_TAS_ERR_EN))
15162306a36Sopenharmony_ci			temp |= SB_TAS_ERR_FLAG;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci		if ((value & PCI_STATUS_REC_TARGET_ABORT) &&
15462306a36Sopenharmony_ci		    (lo & SB_TAR_ERR_EN))
15562306a36Sopenharmony_ci			temp |= SB_TAR_ERR_FLAG;
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci		if ((value & PCI_STATUS_REC_MASTER_ABORT)
15862306a36Sopenharmony_ci		    && (lo & SB_MAR_ERR_EN))
15962306a36Sopenharmony_ci			temp |= SB_MAR_ERR_FLAG;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci		if ((value & PCI_STATUS_DETECTED_PARITY)
16262306a36Sopenharmony_ci		    && (lo & SB_PARE_ERR_EN))
16362306a36Sopenharmony_ci			temp |= SB_PARE_ERR_FLAG;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci		lo = temp;
16662306a36Sopenharmony_ci		_wrmsr(SB_MSR_REG(SB_ERROR), hi, lo);
16762306a36Sopenharmony_ci		break;
16862306a36Sopenharmony_ci	case PCI_CACHE_LINE_SIZE:
16962306a36Sopenharmony_ci		value &= 0x0000ff00;
17062306a36Sopenharmony_ci		_rdmsr(SB_MSR_REG(SB_CTRL), &hi, &lo);
17162306a36Sopenharmony_ci		hi &= 0xffffff00;
17262306a36Sopenharmony_ci		hi |= (value >> 8);
17362306a36Sopenharmony_ci		_wrmsr(SB_MSR_REG(SB_CTRL), hi, lo);
17462306a36Sopenharmony_ci		break;
17562306a36Sopenharmony_ci	case PCI_BAR0_REG:
17662306a36Sopenharmony_ci		pci_isa_write_bar(0, value);
17762306a36Sopenharmony_ci		break;
17862306a36Sopenharmony_ci	case PCI_BAR1_REG:
17962306a36Sopenharmony_ci		pci_isa_write_bar(1, value);
18062306a36Sopenharmony_ci		break;
18162306a36Sopenharmony_ci	case PCI_BAR2_REG:
18262306a36Sopenharmony_ci		pci_isa_write_bar(2, value);
18362306a36Sopenharmony_ci		break;
18462306a36Sopenharmony_ci	case PCI_BAR3_REG:
18562306a36Sopenharmony_ci		pci_isa_write_bar(3, value);
18662306a36Sopenharmony_ci		break;
18762306a36Sopenharmony_ci	case PCI_BAR4_REG:
18862306a36Sopenharmony_ci		pci_isa_write_bar(4, value);
18962306a36Sopenharmony_ci		break;
19062306a36Sopenharmony_ci	case PCI_BAR5_REG:
19162306a36Sopenharmony_ci		pci_isa_write_bar(5, value);
19262306a36Sopenharmony_ci		break;
19362306a36Sopenharmony_ci	case PCI_UART1_INT_REG:
19462306a36Sopenharmony_ci		_rdmsr(DIVIL_MSR_REG(PIC_YSEL_HIGH), &hi, &lo);
19562306a36Sopenharmony_ci		/* disable uart1 interrupt in PIC */
19662306a36Sopenharmony_ci		lo &= ~(0xf << 24);
19762306a36Sopenharmony_ci		if (value)	/* enable uart1 interrupt in PIC */
19862306a36Sopenharmony_ci			lo |= (CS5536_UART1_INTR << 24);
19962306a36Sopenharmony_ci		_wrmsr(DIVIL_MSR_REG(PIC_YSEL_HIGH), hi, lo);
20062306a36Sopenharmony_ci		break;
20162306a36Sopenharmony_ci	case PCI_UART2_INT_REG:
20262306a36Sopenharmony_ci		_rdmsr(DIVIL_MSR_REG(PIC_YSEL_HIGH), &hi, &lo);
20362306a36Sopenharmony_ci		/* disable uart2 interrupt in PIC */
20462306a36Sopenharmony_ci		lo &= ~(0xf << 28);
20562306a36Sopenharmony_ci		if (value)	/* enable uart2 interrupt in PIC */
20662306a36Sopenharmony_ci			lo |= (CS5536_UART2_INTR << 28);
20762306a36Sopenharmony_ci		_wrmsr(DIVIL_MSR_REG(PIC_YSEL_HIGH), hi, lo);
20862306a36Sopenharmony_ci		break;
20962306a36Sopenharmony_ci	case PCI_ISA_FIXUP_REG:
21062306a36Sopenharmony_ci		if (value) {
21162306a36Sopenharmony_ci			/* enable the TARGET ABORT/MASTER ABORT etc. */
21262306a36Sopenharmony_ci			_rdmsr(SB_MSR_REG(SB_ERROR), &hi, &lo);
21362306a36Sopenharmony_ci			lo |= 0x00000063;
21462306a36Sopenharmony_ci			_wrmsr(SB_MSR_REG(SB_ERROR), hi, lo);
21562306a36Sopenharmony_ci		}
21662306a36Sopenharmony_ci		break;
21762306a36Sopenharmony_ci	default:
21862306a36Sopenharmony_ci		/* ALL OTHER PCI CONFIG SPACE HEADER IS NOT IMPLEMENTED. */
21962306a36Sopenharmony_ci		break;
22062306a36Sopenharmony_ci	}
22162306a36Sopenharmony_ci}
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci/*
22462306a36Sopenharmony_ci * isa_read: ISA read transfers
22562306a36Sopenharmony_ci *
22662306a36Sopenharmony_ci * We assume that this is not a bus master transfer.
22762306a36Sopenharmony_ci */
22862306a36Sopenharmony_ciu32 pci_isa_read_reg(int reg)
22962306a36Sopenharmony_ci{
23062306a36Sopenharmony_ci	u32 conf_data = 0;
23162306a36Sopenharmony_ci	u32 hi, lo;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	switch (reg) {
23462306a36Sopenharmony_ci	case PCI_VENDOR_ID:
23562306a36Sopenharmony_ci		conf_data =
23662306a36Sopenharmony_ci		    CFG_PCI_VENDOR_ID(CS5536_ISA_DEVICE_ID, CS5536_VENDOR_ID);
23762306a36Sopenharmony_ci		break;
23862306a36Sopenharmony_ci	case PCI_COMMAND:
23962306a36Sopenharmony_ci		/* we just check the first LBAR for the IO enable bit, */
24062306a36Sopenharmony_ci		/* maybe we should changed later. */
24162306a36Sopenharmony_ci		_rdmsr(DIVIL_MSR_REG(DIVIL_LBAR_SMB), &hi, &lo);
24262306a36Sopenharmony_ci		if (hi & 0x01)
24362306a36Sopenharmony_ci			conf_data |= PCI_COMMAND_IO;
24462306a36Sopenharmony_ci		break;
24562306a36Sopenharmony_ci	case PCI_STATUS:
24662306a36Sopenharmony_ci		conf_data |= PCI_STATUS_66MHZ;
24762306a36Sopenharmony_ci		conf_data |= PCI_STATUS_DEVSEL_MEDIUM;
24862306a36Sopenharmony_ci		conf_data |= PCI_STATUS_FAST_BACK;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci		_rdmsr(SB_MSR_REG(SB_ERROR), &hi, &lo);
25162306a36Sopenharmony_ci		if (lo & SB_TAS_ERR_FLAG)
25262306a36Sopenharmony_ci			conf_data |= PCI_STATUS_SIG_TARGET_ABORT;
25362306a36Sopenharmony_ci		if (lo & SB_TAR_ERR_FLAG)
25462306a36Sopenharmony_ci			conf_data |= PCI_STATUS_REC_TARGET_ABORT;
25562306a36Sopenharmony_ci		if (lo & SB_MAR_ERR_FLAG)
25662306a36Sopenharmony_ci			conf_data |= PCI_STATUS_REC_MASTER_ABORT;
25762306a36Sopenharmony_ci		if (lo & SB_PARE_ERR_FLAG)
25862306a36Sopenharmony_ci			conf_data |= PCI_STATUS_DETECTED_PARITY;
25962306a36Sopenharmony_ci		break;
26062306a36Sopenharmony_ci	case PCI_CLASS_REVISION:
26162306a36Sopenharmony_ci		_rdmsr(GLCP_MSR_REG(GLCP_CHIP_REV_ID), &hi, &lo);
26262306a36Sopenharmony_ci		conf_data = lo & 0x000000ff;
26362306a36Sopenharmony_ci		conf_data |= (CS5536_ISA_CLASS_CODE << 8);
26462306a36Sopenharmony_ci		break;
26562306a36Sopenharmony_ci	case PCI_CACHE_LINE_SIZE:
26662306a36Sopenharmony_ci		_rdmsr(SB_MSR_REG(SB_CTRL), &hi, &lo);
26762306a36Sopenharmony_ci		hi &= 0x000000f8;
26862306a36Sopenharmony_ci		conf_data = CFG_PCI_CACHE_LINE_SIZE(PCI_BRIDGE_HEADER_TYPE, hi);
26962306a36Sopenharmony_ci		break;
27062306a36Sopenharmony_ci		/*
27162306a36Sopenharmony_ci		 * we only use the LBAR of DIVIL, no RCONF used.
27262306a36Sopenharmony_ci		 * all of them are IO space.
27362306a36Sopenharmony_ci		 */
27462306a36Sopenharmony_ci	case PCI_BAR0_REG:
27562306a36Sopenharmony_ci		return pci_isa_read_bar(0);
27662306a36Sopenharmony_ci		break;
27762306a36Sopenharmony_ci	case PCI_BAR1_REG:
27862306a36Sopenharmony_ci		return pci_isa_read_bar(1);
27962306a36Sopenharmony_ci		break;
28062306a36Sopenharmony_ci	case PCI_BAR2_REG:
28162306a36Sopenharmony_ci		return pci_isa_read_bar(2);
28262306a36Sopenharmony_ci		break;
28362306a36Sopenharmony_ci	case PCI_BAR3_REG:
28462306a36Sopenharmony_ci		break;
28562306a36Sopenharmony_ci	case PCI_BAR4_REG:
28662306a36Sopenharmony_ci		return pci_isa_read_bar(4);
28762306a36Sopenharmony_ci		break;
28862306a36Sopenharmony_ci	case PCI_BAR5_REG:
28962306a36Sopenharmony_ci		return pci_isa_read_bar(5);
29062306a36Sopenharmony_ci		break;
29162306a36Sopenharmony_ci	case PCI_CARDBUS_CIS:
29262306a36Sopenharmony_ci		conf_data = PCI_CARDBUS_CIS_POINTER;
29362306a36Sopenharmony_ci		break;
29462306a36Sopenharmony_ci	case PCI_SUBSYSTEM_VENDOR_ID:
29562306a36Sopenharmony_ci		conf_data =
29662306a36Sopenharmony_ci		    CFG_PCI_VENDOR_ID(CS5536_ISA_SUB_ID, CS5536_SUB_VENDOR_ID);
29762306a36Sopenharmony_ci		break;
29862306a36Sopenharmony_ci	case PCI_ROM_ADDRESS:
29962306a36Sopenharmony_ci		conf_data = PCI_EXPANSION_ROM_BAR;
30062306a36Sopenharmony_ci		break;
30162306a36Sopenharmony_ci	case PCI_CAPABILITY_LIST:
30262306a36Sopenharmony_ci		conf_data = PCI_CAPLIST_POINTER;
30362306a36Sopenharmony_ci		break;
30462306a36Sopenharmony_ci	case PCI_INTERRUPT_LINE:
30562306a36Sopenharmony_ci		/* no interrupt used here */
30662306a36Sopenharmony_ci		conf_data = CFG_PCI_INTERRUPT_LINE(0x00, 0x00);
30762306a36Sopenharmony_ci		break;
30862306a36Sopenharmony_ci	default:
30962306a36Sopenharmony_ci		break;
31062306a36Sopenharmony_ci	}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	return conf_data;
31362306a36Sopenharmony_ci}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci/*
31662306a36Sopenharmony_ci * The mfgpt timer interrupt is running early, so we must keep the south bridge
31762306a36Sopenharmony_ci * mmio always enabled. Otherwise we may race with the PCI configuration which
31862306a36Sopenharmony_ci * may temporarily disable it. When that happens and the timer interrupt fires,
31962306a36Sopenharmony_ci * we are not able to clear it and the system will hang.
32062306a36Sopenharmony_ci */
32162306a36Sopenharmony_cistatic void cs5536_isa_mmio_always_on(struct pci_dev *dev)
32262306a36Sopenharmony_ci{
32362306a36Sopenharmony_ci	dev->mmio_always_on = 1;
32462306a36Sopenharmony_ci}
32562306a36Sopenharmony_ciDECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA,
32662306a36Sopenharmony_ci	PCI_CLASS_BRIDGE_ISA, 8, cs5536_isa_mmio_always_on);
327