Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+97
View File
@@ -0,0 +1,97 @@
# SPDX-License-Identifier: GPL-2.0
if MACH_LOONGSON2EF
choice
prompt "Machine Type"
config LEMOTE_FULOONG2E
bool "Lemote Fuloong(2e) mini-PC"
select ARCH_SPARSEMEM_ENABLE
select ARCH_HAS_PHYS_TO_DMA
select ARCH_MIGHT_HAVE_PC_PARPORT
select ARCH_MIGHT_HAVE_PC_SERIO
select CEVT_R4K
select CSRC_R4K
select SYS_HAS_CPU_LOONGSON2E
select DMA_NONCOHERENT
select BOOT_ELF32
select BOARD_SCACHE
select FORCE_PCI
select I8259
select ISA
select IRQ_MIPS_CPU
select SYS_SUPPORTS_64BIT_KERNEL
select SYS_SUPPORTS_LITTLE_ENDIAN
select SYS_SUPPORTS_HIGHMEM
select SYS_HAS_EARLY_PRINTK
select USE_GENERIC_EARLY_PRINTK_8250
select GENERIC_ISA_DMA_SUPPORT_BROKEN
select CPU_HAS_WB
select LOONGSON_MC146818
help
Lemote Fuloong(2e) mini-PC board based on the Chinese Loongson-2E CPU and
an FPGA northbridge
Lemote Fuloong(2e) mini PC have a VIA686B south bridge.
config LEMOTE_MACH2F
bool "Lemote Loongson 2F family machines"
select ARCH_SPARSEMEM_ENABLE
select ARCH_HAS_PHYS_TO_DMA
select ARCH_MIGHT_HAVE_PC_PARPORT
select ARCH_MIGHT_HAVE_PC_SERIO
select BOARD_SCACHE
select BOOT_ELF32
select CEVT_R4K if ! MIPS_EXTERNAL_TIMER
select CPU_HAS_WB
select CS5536
select CSRC_R4K if ! MIPS_EXTERNAL_TIMER
select DMA_NONCOHERENT
select GENERIC_ISA_DMA_SUPPORT_BROKEN
select GPIOLIB
select FORCE_PCI
select I8259
select IRQ_MIPS_CPU
select ISA
select SYS_HAS_CPU_LOONGSON2F
select SYS_HAS_EARLY_PRINTK
select USE_GENERIC_EARLY_PRINTK_8250
select SYS_SUPPORTS_64BIT_KERNEL
select SYS_SUPPORTS_HIGHMEM
select SYS_SUPPORTS_LITTLE_ENDIAN
select LOONGSON_MC146818
help
Lemote Loongson 2F family machines utilize the 2F revision of
Loongson processor and the AMD CS5536 south bridge.
These family machines include fuloong2f mini PC, yeeloong2f notebook,
LingLoong allinone PC and so forth.
endchoice
config CS5536
bool
config CS5536_MFGPT
bool "CS5536 MFGPT Timer"
depends on CS5536 && !HIGH_RES_TIMERS
select MIPS_EXTERNAL_TIMER
help
This option enables the mfgpt0 timer of AMD CS5536. With this timer
switched on you can not use high resolution timers.
If you want to enable the Loongson2 CPUFreq Driver, Please enable
this option at first, otherwise, You will get wrong system time.
If unsure, say Yes.
config LOONGSON_UART_BASE
bool
default y
depends on EARLY_PRINTK || SERIAL_8250
config LOONGSON_MC146818
bool
default n
endif # MACH_LOONGSON2EF
@@ -0,0 +1,18 @@
# SPDX-License-Identifier: GPL-2.0-only
#
# Common code for all Loongson based systems
#
obj-$(CONFIG_MACH_LOONGSON2EF) += common/
#
# Lemote Fuloong mini-PC (Loongson 2E-based)
#
obj-$(CONFIG_LEMOTE_FULOONG2E) += fuloong-2e/
#
# Lemote loongson2f family machines
#
obj-$(CONFIG_LEMOTE_MACH2F) += lemote-2f/
@@ -0,0 +1,12 @@
#
# Loongson Processors' Support
#
#
# Loongson Machines' Support
#
cflags-$(CONFIG_MACH_LOONGSON2EF) += -I$(srctree)/arch/mips/include/asm/mach-loongson2ef
cflags-$(CONFIG_CC_HAS_MNO_BRANCH_LIKELY) += -mno-branch-likely
load-$(CONFIG_LEMOTE_FULOONG2E) += 0xffffffff80100000
load-$(CONFIG_LEMOTE_MACH2F) += 0xffffffff80200000
@@ -0,0 +1,28 @@
# SPDX-License-Identifier: GPL-2.0
#
# Makefile for loongson based machines.
#
obj-y += setup.o init.o env.o time.o reset.o irq.o \
bonito-irq.o mem.o machtype.o platform.o
obj-$(CONFIG_PCI) += pci.o
#
# Serial port support
#
obj-$(CONFIG_LOONGSON_UART_BASE) += serial.o
obj-$(CONFIG_EARLY_PRINTK) += serial.o
obj-$(CONFIG_LOONGSON_UART_BASE) += uart_base.o
obj-$(CONFIG_LOONGSON_MC146818) += rtc.o
#
# Enable CS5536 Virtual Support Module(VSM) to virtulize the PCI configure
# space
#
obj-$(CONFIG_CS5536) += cs5536/
#
# Suspend Support
#
obj-$(CONFIG_SUSPEND) += pm.o
@@ -0,0 +1,46 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright 2001 MontaVista Software Inc.
* Author: Jun Sun, jsun@mvista.com or jsun@junsun.net
* Copyright (C) 2000, 2001 Ralf Baechle (ralf@gnu.org)
*
* Copyright (C) 2007 Lemote Inc. & Institute of Computing Technology
* Author: Fuxin Zhang, zhangfx@lemote.com
*/
#include <linux/interrupt.h>
#include <linux/compiler.h>
#include <loongson.h>
static inline void bonito_irq_enable(struct irq_data *d)
{
LOONGSON_INTENSET = (1 << (d->irq - LOONGSON_IRQ_BASE));
mmiowb();
}
static inline void bonito_irq_disable(struct irq_data *d)
{
LOONGSON_INTENCLR = (1 << (d->irq - LOONGSON_IRQ_BASE));
mmiowb();
}
static struct irq_chip bonito_irq_type = {
.name = "bonito_irq",
.irq_mask = bonito_irq_disable,
.irq_unmask = bonito_irq_enable,
};
void bonito_irq_init(void)
{
u32 i;
for (i = LOONGSON_IRQ_BASE; i < LOONGSON_IRQ_BASE + 32; i++)
irq_set_chip_and_handler(i, &bonito_irq_type,
handle_level_irq);
#ifdef CONFIG_CPU_LOONGSON2E
i = LOONGSON_IRQ_BASE + 10;
if (request_irq(i, no_action, 0, "dma_timeout", NULL))
pr_err("Failed to request irq %d (dma_timeout)\n", i);
#endif
}
@@ -0,0 +1,12 @@
# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for CS5536 support.
#
obj-$(CONFIG_CS5536) += cs5536_pci.o cs5536_ide.o cs5536_acc.o cs5536_ohci.o \
cs5536_isa.o cs5536_ehci.o
#
# Enable cs5536 mfgpt Timer
#
obj-$(CONFIG_CS5536_MFGPT) += cs5536_mfgpt.o
@@ -0,0 +1,136 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* the ACC Virtual Support Module of AMD CS5536
*
* Copyright (C) 2007 Lemote, Inc.
* Author : jlliu, liujl@lemote.com
*
* Copyright (C) 2009 Lemote, Inc.
* Author: Wu Zhangjin, wuzhangjin@gmail.com
*/
#include <cs5536/cs5536.h>
#include <cs5536/cs5536_pci.h>
void pci_acc_write_reg(int reg, u32 value)
{
u32 hi = 0, lo = value;
switch (reg) {
case PCI_COMMAND:
_rdmsr(GLIU_MSR_REG(GLIU_PAE), &hi, &lo);
if (value & PCI_COMMAND_MASTER)
lo |= (0x03 << 8);
else
lo &= ~(0x03 << 8);
_wrmsr(GLIU_MSR_REG(GLIU_PAE), hi, lo);
break;
case PCI_STATUS:
if (value & PCI_STATUS_PARITY) {
_rdmsr(SB_MSR_REG(SB_ERROR), &hi, &lo);
if (lo & SB_PARE_ERR_FLAG) {
lo = (lo & 0x0000ffff) | SB_PARE_ERR_FLAG;
_wrmsr(SB_MSR_REG(SB_ERROR), hi, lo);
}
}
break;
case PCI_BAR0_REG:
if (value == PCI_BAR_RANGE_MASK) {
_rdmsr(GLCP_MSR_REG(GLCP_SOFT_COM), &hi, &lo);
lo |= SOFT_BAR_ACC_FLAG;
_wrmsr(GLCP_MSR_REG(GLCP_SOFT_COM), hi, lo);
} else if (value & 0x01) {
value &= 0xfffffffc;
hi = 0xA0000000 | ((value & 0x000ff000) >> 12);
lo = 0x000fff80 | ((value & 0x00000fff) << 20);
_wrmsr(GLIU_MSR_REG(GLIU_IOD_BM1), hi, lo);
}
break;
case PCI_ACC_INT_REG:
_rdmsr(DIVIL_MSR_REG(PIC_YSEL_LOW), &hi, &lo);
/* disable all the usb interrupt in PIC */
lo &= ~(0xf << PIC_YSEL_LOW_ACC_SHIFT);
if (value) /* enable all the acc interrupt in PIC */
lo |= (CS5536_ACC_INTR << PIC_YSEL_LOW_ACC_SHIFT);
_wrmsr(DIVIL_MSR_REG(PIC_YSEL_LOW), hi, lo);
break;
default:
break;
}
}
u32 pci_acc_read_reg(int reg)
{
u32 hi, lo;
u32 conf_data = 0;
switch (reg) {
case PCI_VENDOR_ID:
conf_data =
CFG_PCI_VENDOR_ID(CS5536_ACC_DEVICE_ID, CS5536_VENDOR_ID);
break;
case PCI_COMMAND:
_rdmsr(GLIU_MSR_REG(GLIU_IOD_BM1), &hi, &lo);
if (((lo & 0xfff00000) || (hi & 0x000000ff))
&& ((hi & 0xf0000000) == 0xa0000000))
conf_data |= PCI_COMMAND_IO;
_rdmsr(GLIU_MSR_REG(GLIU_PAE), &hi, &lo);
if ((lo & 0x300) == 0x300)
conf_data |= PCI_COMMAND_MASTER;
break;
case PCI_STATUS:
conf_data |= PCI_STATUS_66MHZ;
conf_data |= PCI_STATUS_FAST_BACK;
_rdmsr(SB_MSR_REG(SB_ERROR), &hi, &lo);
if (lo & SB_PARE_ERR_FLAG)
conf_data |= PCI_STATUS_PARITY;
conf_data |= PCI_STATUS_DEVSEL_MEDIUM;
break;
case PCI_CLASS_REVISION:
_rdmsr(ACC_MSR_REG(ACC_CAP), &hi, &lo);
conf_data = lo & 0x000000ff;
conf_data |= (CS5536_ACC_CLASS_CODE << 8);
break;
case PCI_CACHE_LINE_SIZE:
conf_data =
CFG_PCI_CACHE_LINE_SIZE(PCI_NORMAL_HEADER_TYPE,
PCI_NORMAL_LATENCY_TIMER);
break;
case PCI_BAR0_REG:
_rdmsr(GLCP_MSR_REG(GLCP_SOFT_COM), &hi, &lo);
if (lo & SOFT_BAR_ACC_FLAG) {
conf_data = CS5536_ACC_RANGE |
PCI_BASE_ADDRESS_SPACE_IO;
lo &= ~SOFT_BAR_ACC_FLAG;
_wrmsr(GLCP_MSR_REG(GLCP_SOFT_COM), hi, lo);
} else {
_rdmsr(GLIU_MSR_REG(GLIU_IOD_BM1), &hi, &lo);
conf_data = (hi & 0x000000ff) << 12;
conf_data |= (lo & 0xfff00000) >> 20;
conf_data |= 0x01;
conf_data &= ~0x02;
}
break;
case PCI_CARDBUS_CIS:
conf_data = PCI_CARDBUS_CIS_POINTER;
break;
case PCI_SUBSYSTEM_VENDOR_ID:
conf_data =
CFG_PCI_VENDOR_ID(CS5536_ACC_SUB_ID, CS5536_SUB_VENDOR_ID);
break;
case PCI_ROM_ADDRESS:
conf_data = PCI_EXPANSION_ROM_BAR;
break;
case PCI_CAPABILITY_LIST:
conf_data = PCI_CAPLIST_USB_POINTER;
break;
case PCI_INTERRUPT_LINE:
conf_data =
CFG_PCI_INTERRUPT_LINE(PCI_DEFAULT_PIN, CS5536_ACC_INTR);
break;
default:
break;
}
return conf_data;
}
@@ -0,0 +1,156 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* the EHCI Virtual Support Module of AMD CS5536
*
* Copyright (C) 2007 Lemote, Inc.
* Author : jlliu, liujl@lemote.com
*
* Copyright (C) 2009 Lemote, Inc.
* Author: Wu Zhangjin, wuzhangjin@gmail.com
*/
#include <cs5536/cs5536.h>
#include <cs5536/cs5536_pci.h>
void pci_ehci_write_reg(int reg, u32 value)
{
u32 hi = 0, lo = value;
switch (reg) {
case PCI_COMMAND:
_rdmsr(USB_MSR_REG(USB_EHCI), &hi, &lo);
if (value & PCI_COMMAND_MASTER)
hi |= PCI_COMMAND_MASTER;
else
hi &= ~PCI_COMMAND_MASTER;
if (value & PCI_COMMAND_MEMORY)
hi |= PCI_COMMAND_MEMORY;
else
hi &= ~PCI_COMMAND_MEMORY;
_wrmsr(USB_MSR_REG(USB_EHCI), hi, lo);
break;
case PCI_STATUS:
if (value & PCI_STATUS_PARITY) {
_rdmsr(SB_MSR_REG(SB_ERROR), &hi, &lo);
if (lo & SB_PARE_ERR_FLAG) {
lo = (lo & 0x0000ffff) | SB_PARE_ERR_FLAG;
_wrmsr(SB_MSR_REG(SB_ERROR), hi, lo);
}
}
break;
case PCI_BAR0_REG:
if (value == PCI_BAR_RANGE_MASK) {
_rdmsr(GLCP_MSR_REG(GLCP_SOFT_COM), &hi, &lo);
lo |= SOFT_BAR_EHCI_FLAG;
_wrmsr(GLCP_MSR_REG(GLCP_SOFT_COM), hi, lo);
} else if ((value & 0x01) == 0x00) {
_rdmsr(USB_MSR_REG(USB_EHCI), &hi, &lo);
lo = value;
_wrmsr(USB_MSR_REG(USB_EHCI), hi, lo);
value &= 0xfffffff0;
hi = 0x40000000 | ((value & 0xff000000) >> 24);
lo = 0x000fffff | ((value & 0x00fff000) << 8);
_wrmsr(GLIU_MSR_REG(GLIU_P2D_BM4), hi, lo);
}
break;
case PCI_EHCI_LEGSMIEN_REG:
_rdmsr(USB_MSR_REG(USB_EHCI), &hi, &lo);
hi &= 0x003f0000;
hi |= (value & 0x3f) << 16;
_wrmsr(USB_MSR_REG(USB_EHCI), hi, lo);
break;
case PCI_EHCI_FLADJ_REG:
_rdmsr(USB_MSR_REG(USB_EHCI), &hi, &lo);
hi &= ~0x00003f00;
hi |= value & 0x00003f00;
_wrmsr(USB_MSR_REG(USB_EHCI), hi, lo);
break;
default:
break;
}
}
u32 pci_ehci_read_reg(int reg)
{
u32 conf_data = 0;
u32 hi, lo;
switch (reg) {
case PCI_VENDOR_ID:
conf_data =
CFG_PCI_VENDOR_ID(CS5536_EHCI_DEVICE_ID, CS5536_VENDOR_ID);
break;
case PCI_COMMAND:
_rdmsr(USB_MSR_REG(USB_EHCI), &hi, &lo);
if (hi & PCI_COMMAND_MASTER)
conf_data |= PCI_COMMAND_MASTER;
if (hi & PCI_COMMAND_MEMORY)
conf_data |= PCI_COMMAND_MEMORY;
break;
case PCI_STATUS:
conf_data |= PCI_STATUS_66MHZ;
conf_data |= PCI_STATUS_FAST_BACK;
_rdmsr(SB_MSR_REG(SB_ERROR), &hi, &lo);
if (lo & SB_PARE_ERR_FLAG)
conf_data |= PCI_STATUS_PARITY;
conf_data |= PCI_STATUS_DEVSEL_MEDIUM;
break;
case PCI_CLASS_REVISION:
_rdmsr(USB_MSR_REG(USB_CAP), &hi, &lo);
conf_data = lo & 0x000000ff;
conf_data |= (CS5536_EHCI_CLASS_CODE << 8);
break;
case PCI_CACHE_LINE_SIZE:
conf_data =
CFG_PCI_CACHE_LINE_SIZE(PCI_NORMAL_HEADER_TYPE,
PCI_NORMAL_LATENCY_TIMER);
break;
case PCI_BAR0_REG:
_rdmsr(GLCP_MSR_REG(GLCP_SOFT_COM), &hi, &lo);
if (lo & SOFT_BAR_EHCI_FLAG) {
conf_data = CS5536_EHCI_RANGE |
PCI_BASE_ADDRESS_SPACE_MEMORY;
lo &= ~SOFT_BAR_EHCI_FLAG;
_wrmsr(GLCP_MSR_REG(GLCP_SOFT_COM), hi, lo);
} else {
_rdmsr(USB_MSR_REG(USB_EHCI), &hi, &lo);
conf_data = lo & 0xfffff000;
}
break;
case PCI_CARDBUS_CIS:
conf_data = PCI_CARDBUS_CIS_POINTER;
break;
case PCI_SUBSYSTEM_VENDOR_ID:
conf_data =
CFG_PCI_VENDOR_ID(CS5536_EHCI_SUB_ID, CS5536_SUB_VENDOR_ID);
break;
case PCI_ROM_ADDRESS:
conf_data = PCI_EXPANSION_ROM_BAR;
break;
case PCI_CAPABILITY_LIST:
conf_data = PCI_CAPLIST_USB_POINTER;
break;
case PCI_INTERRUPT_LINE:
conf_data =
CFG_PCI_INTERRUPT_LINE(PCI_DEFAULT_PIN, CS5536_USB_INTR);
break;
case PCI_EHCI_LEGSMIEN_REG:
_rdmsr(USB_MSR_REG(USB_EHCI), &hi, &lo);
conf_data = (hi & 0x003f0000) >> 16;
break;
case PCI_EHCI_LEGSMISTS_REG:
_rdmsr(USB_MSR_REG(USB_EHCI), &hi, &lo);
conf_data = (hi & 0x3f000000) >> 24;
break;
case PCI_EHCI_FLADJ_REG:
_rdmsr(USB_MSR_REG(USB_EHCI), &hi, &lo);
conf_data = hi & 0x00003f00;
break;
default:
break;
}
return conf_data;
}
@@ -0,0 +1,188 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* the IDE Virtual Support Module of AMD CS5536
*
* Copyright (C) 2007 Lemote, Inc.
* Author : jlliu, liujl@lemote.com
*
* Copyright (C) 2009 Lemote, Inc.
* Author: Wu Zhangjin, wuzhangjin@gmail.com
*/
#include <cs5536/cs5536.h>
#include <cs5536/cs5536_pci.h>
void pci_ide_write_reg(int reg, u32 value)
{
u32 hi = 0, lo = value;
switch (reg) {
case PCI_COMMAND:
_rdmsr(GLIU_MSR_REG(GLIU_PAE), &hi, &lo);
if (value & PCI_COMMAND_MASTER)
lo |= (0x03 << 4);
else
lo &= ~(0x03 << 4);
_wrmsr(GLIU_MSR_REG(GLIU_PAE), hi, lo);
break;
case PCI_STATUS:
if (value & PCI_STATUS_PARITY) {
_rdmsr(SB_MSR_REG(SB_ERROR), &hi, &lo);
if (lo & SB_PARE_ERR_FLAG) {
lo = (lo & 0x0000ffff) | SB_PARE_ERR_FLAG;
_wrmsr(SB_MSR_REG(SB_ERROR), hi, lo);
}
}
break;
case PCI_CACHE_LINE_SIZE:
value &= 0x0000ff00;
_rdmsr(SB_MSR_REG(SB_CTRL), &hi, &lo);
hi &= 0xffffff00;
hi |= (value >> 8);
_wrmsr(SB_MSR_REG(SB_CTRL), hi, lo);
break;
case PCI_BAR4_REG:
if (value == PCI_BAR_RANGE_MASK) {
_rdmsr(GLCP_MSR_REG(GLCP_SOFT_COM), &hi, &lo);
lo |= SOFT_BAR_IDE_FLAG;
_wrmsr(GLCP_MSR_REG(GLCP_SOFT_COM), hi, lo);
} else if (value & 0x01) {
_rdmsr(IDE_MSR_REG(IDE_IO_BAR), &hi, &lo);
lo = (value & 0xfffffff0) | 0x1;
_wrmsr(IDE_MSR_REG(IDE_IO_BAR), hi, lo);
value &= 0xfffffffc;
hi = 0x60000000 | ((value & 0x000ff000) >> 12);
lo = 0x000ffff0 | ((value & 0x00000fff) << 20);
_wrmsr(GLIU_MSR_REG(GLIU_IOD_BM2), hi, lo);
}
break;
case PCI_IDE_CFG_REG:
if (value == CS5536_IDE_FLASH_SIGNATURE) {
_rdmsr(DIVIL_MSR_REG(DIVIL_BALL_OPTS), &hi, &lo);
lo |= 0x01;
_wrmsr(DIVIL_MSR_REG(DIVIL_BALL_OPTS), hi, lo);
} else {
_rdmsr(IDE_MSR_REG(IDE_CFG), &hi, &lo);
lo = value;
_wrmsr(IDE_MSR_REG(IDE_CFG), hi, lo);
}
break;
case PCI_IDE_DTC_REG:
_rdmsr(IDE_MSR_REG(IDE_DTC), &hi, &lo);
lo = value;
_wrmsr(IDE_MSR_REG(IDE_DTC), hi, lo);
break;
case PCI_IDE_CAST_REG:
_rdmsr(IDE_MSR_REG(IDE_CAST), &hi, &lo);
lo = value;
_wrmsr(IDE_MSR_REG(IDE_CAST), hi, lo);
break;
case PCI_IDE_ETC_REG:
_rdmsr(IDE_MSR_REG(IDE_ETC), &hi, &lo);
lo = value;
_wrmsr(IDE_MSR_REG(IDE_ETC), hi, lo);
break;
case PCI_IDE_PM_REG:
_rdmsr(IDE_MSR_REG(IDE_INTERNAL_PM), &hi, &lo);
lo = value;
_wrmsr(IDE_MSR_REG(IDE_INTERNAL_PM), hi, lo);
break;
default:
break;
}
}
u32 pci_ide_read_reg(int reg)
{
u32 conf_data = 0;
u32 hi, lo;
switch (reg) {
case PCI_VENDOR_ID:
conf_data =
CFG_PCI_VENDOR_ID(CS5536_IDE_DEVICE_ID, CS5536_VENDOR_ID);
break;
case PCI_COMMAND:
_rdmsr(IDE_MSR_REG(IDE_IO_BAR), &hi, &lo);
if (lo & 0xfffffff0)
conf_data |= PCI_COMMAND_IO;
_rdmsr(GLIU_MSR_REG(GLIU_PAE), &hi, &lo);
if ((lo & 0x30) == 0x30)
conf_data |= PCI_COMMAND_MASTER;
break;
case PCI_STATUS:
conf_data |= PCI_STATUS_66MHZ;
conf_data |= PCI_STATUS_FAST_BACK;
_rdmsr(SB_MSR_REG(SB_ERROR), &hi, &lo);
if (lo & SB_PARE_ERR_FLAG)
conf_data |= PCI_STATUS_PARITY;
conf_data |= PCI_STATUS_DEVSEL_MEDIUM;
break;
case PCI_CLASS_REVISION:
_rdmsr(IDE_MSR_REG(IDE_CAP), &hi, &lo);
conf_data = lo & 0x000000ff;
conf_data |= (CS5536_IDE_CLASS_CODE << 8);
break;
case PCI_CACHE_LINE_SIZE:
_rdmsr(SB_MSR_REG(SB_CTRL), &hi, &lo);
hi &= 0x000000f8;
conf_data = CFG_PCI_CACHE_LINE_SIZE(PCI_NORMAL_HEADER_TYPE, hi);
break;
case PCI_BAR4_REG:
_rdmsr(GLCP_MSR_REG(GLCP_SOFT_COM), &hi, &lo);
if (lo & SOFT_BAR_IDE_FLAG) {
conf_data = CS5536_IDE_RANGE |
PCI_BASE_ADDRESS_SPACE_IO;
lo &= ~SOFT_BAR_IDE_FLAG;
_wrmsr(GLCP_MSR_REG(GLCP_SOFT_COM), hi, lo);
} else {
_rdmsr(IDE_MSR_REG(IDE_IO_BAR), &hi, &lo);
conf_data = lo & 0xfffffff0;
conf_data |= 0x01;
conf_data &= ~0x02;
}
break;
case PCI_CARDBUS_CIS:
conf_data = PCI_CARDBUS_CIS_POINTER;
break;
case PCI_SUBSYSTEM_VENDOR_ID:
conf_data =
CFG_PCI_VENDOR_ID(CS5536_IDE_SUB_ID, CS5536_SUB_VENDOR_ID);
break;
case PCI_ROM_ADDRESS:
conf_data = PCI_EXPANSION_ROM_BAR;
break;
case PCI_CAPABILITY_LIST:
conf_data = PCI_CAPLIST_POINTER;
break;
case PCI_INTERRUPT_LINE:
conf_data =
CFG_PCI_INTERRUPT_LINE(PCI_DEFAULT_PIN, CS5536_IDE_INTR);
break;
case PCI_IDE_CFG_REG:
_rdmsr(IDE_MSR_REG(IDE_CFG), &hi, &lo);
conf_data = lo;
break;
case PCI_IDE_DTC_REG:
_rdmsr(IDE_MSR_REG(IDE_DTC), &hi, &lo);
conf_data = lo;
break;
case PCI_IDE_CAST_REG:
_rdmsr(IDE_MSR_REG(IDE_CAST), &hi, &lo);
conf_data = lo;
break;
case PCI_IDE_ETC_REG:
_rdmsr(IDE_MSR_REG(IDE_ETC), &hi, &lo);
conf_data = lo;
break;
case PCI_IDE_PM_REG:
_rdmsr(IDE_MSR_REG(IDE_INTERNAL_PM), &hi, &lo);
conf_data = lo;
break;
default:
break;
}
return conf_data;
}
@@ -0,0 +1,326 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* the ISA Virtual Support Module of AMD CS5536
*
* Copyright (C) 2007 Lemote, Inc.
* Author : jlliu, liujl@lemote.com
*
* Copyright (C) 2009 Lemote, Inc.
* Author: Wu Zhangjin, wuzhangjin@gmail.com
*/
#include <linux/pci.h>
#include <cs5536/cs5536.h>
#include <cs5536/cs5536_pci.h>
/* common variables for PCI_ISA_READ/WRITE_BAR */
static const u32 divil_msr_reg[6] = {
DIVIL_MSR_REG(DIVIL_LBAR_SMB), DIVIL_MSR_REG(DIVIL_LBAR_GPIO),
DIVIL_MSR_REG(DIVIL_LBAR_MFGPT), DIVIL_MSR_REG(DIVIL_LBAR_IRQ),
DIVIL_MSR_REG(DIVIL_LBAR_PMS), DIVIL_MSR_REG(DIVIL_LBAR_ACPI),
};
static const u32 soft_bar_flag[6] = {
SOFT_BAR_SMB_FLAG, SOFT_BAR_GPIO_FLAG, SOFT_BAR_MFGPT_FLAG,
SOFT_BAR_IRQ_FLAG, SOFT_BAR_PMS_FLAG, SOFT_BAR_ACPI_FLAG,
};
static const u32 sb_msr_reg[6] = {
SB_MSR_REG(SB_R0), SB_MSR_REG(SB_R1), SB_MSR_REG(SB_R2),
SB_MSR_REG(SB_R3), SB_MSR_REG(SB_R4), SB_MSR_REG(SB_R5),
};
static const u32 bar_space_range[6] = {
CS5536_SMB_RANGE, CS5536_GPIO_RANGE, CS5536_MFGPT_RANGE,
CS5536_IRQ_RANGE, CS5536_PMS_RANGE, CS5536_ACPI_RANGE,
};
static const int bar_space_len[6] = {
CS5536_SMB_LENGTH, CS5536_GPIO_LENGTH, CS5536_MFGPT_LENGTH,
CS5536_IRQ_LENGTH, CS5536_PMS_LENGTH, CS5536_ACPI_LENGTH,
};
/*
* enable the divil module bar space.
*
* For all the DIVIL module LBAR, you should control the DIVIL LBAR reg
* and the RCONFx(0~5) reg to use the modules.
*/
static void divil_lbar_enable(void)
{
u32 hi, lo;
int offset;
/*
* The DIVIL IRQ is not used yet. and make the RCONF0 reserved.
*/
for (offset = DIVIL_LBAR_SMB; offset <= DIVIL_LBAR_PMS; offset++) {
_rdmsr(DIVIL_MSR_REG(offset), &hi, &lo);
hi |= 0x01;
_wrmsr(DIVIL_MSR_REG(offset), hi, lo);
}
}
/*
* disable the divil module bar space.
*/
static void divil_lbar_disable(void)
{
u32 hi, lo;
int offset;
for (offset = DIVIL_LBAR_SMB; offset <= DIVIL_LBAR_PMS; offset++) {
_rdmsr(DIVIL_MSR_REG(offset), &hi, &lo);
hi &= ~0x01;
_wrmsr(DIVIL_MSR_REG(offset), hi, lo);
}
}
/*
* BAR write: write value to the n BAR
*/
void pci_isa_write_bar(int n, u32 value)
{
u32 hi = 0, lo = value;
if (value == PCI_BAR_RANGE_MASK) {
_rdmsr(GLCP_MSR_REG(GLCP_SOFT_COM), &hi, &lo);
lo |= soft_bar_flag[n];
_wrmsr(GLCP_MSR_REG(GLCP_SOFT_COM), hi, lo);
} else if (value & 0x01) {
/* NATIVE reg */
hi = 0x0000f001;
lo &= bar_space_range[n];
_wrmsr(divil_msr_reg[n], hi, lo);
/* RCONFx is 4bytes in units for I/O space */
hi = ((value & 0x000ffffc) << 12) |
((bar_space_len[n] - 4) << 12) | 0x01;
lo = ((value & 0x000ffffc) << 12) | 0x01;
_wrmsr(sb_msr_reg[n], hi, lo);
}
}
/*
* BAR read: read the n BAR
*/
u32 pci_isa_read_bar(int n)
{
u32 conf_data = 0;
u32 hi, lo;
_rdmsr(GLCP_MSR_REG(GLCP_SOFT_COM), &hi, &lo);
if (lo & soft_bar_flag[n]) {
conf_data = bar_space_range[n] | PCI_BASE_ADDRESS_SPACE_IO;
lo &= ~soft_bar_flag[n];
_wrmsr(GLCP_MSR_REG(GLCP_SOFT_COM), hi, lo);
} else {
_rdmsr(divil_msr_reg[n], &hi, &lo);
conf_data = lo & bar_space_range[n];
conf_data |= 0x01;
conf_data &= ~0x02;
}
return conf_data;
}
/*
* isa_write: ISA write transfer
*
* We assume that this is not a bus master transfer.
*/
void pci_isa_write_reg(int reg, u32 value)
{
u32 hi = 0, lo = value;
u32 temp;
switch (reg) {
case PCI_COMMAND:
if (value & PCI_COMMAND_IO)
divil_lbar_enable();
else
divil_lbar_disable();
break;
case PCI_STATUS:
_rdmsr(SB_MSR_REG(SB_ERROR), &hi, &lo);
temp = lo & 0x0000ffff;
if ((value & PCI_STATUS_SIG_TARGET_ABORT) &&
(lo & SB_TAS_ERR_EN))
temp |= SB_TAS_ERR_FLAG;
if ((value & PCI_STATUS_REC_TARGET_ABORT) &&
(lo & SB_TAR_ERR_EN))
temp |= SB_TAR_ERR_FLAG;
if ((value & PCI_STATUS_REC_MASTER_ABORT)
&& (lo & SB_MAR_ERR_EN))
temp |= SB_MAR_ERR_FLAG;
if ((value & PCI_STATUS_DETECTED_PARITY)
&& (lo & SB_PARE_ERR_EN))
temp |= SB_PARE_ERR_FLAG;
lo = temp;
_wrmsr(SB_MSR_REG(SB_ERROR), hi, lo);
break;
case PCI_CACHE_LINE_SIZE:
value &= 0x0000ff00;
_rdmsr(SB_MSR_REG(SB_CTRL), &hi, &lo);
hi &= 0xffffff00;
hi |= (value >> 8);
_wrmsr(SB_MSR_REG(SB_CTRL), hi, lo);
break;
case PCI_BAR0_REG:
pci_isa_write_bar(0, value);
break;
case PCI_BAR1_REG:
pci_isa_write_bar(1, value);
break;
case PCI_BAR2_REG:
pci_isa_write_bar(2, value);
break;
case PCI_BAR3_REG:
pci_isa_write_bar(3, value);
break;
case PCI_BAR4_REG:
pci_isa_write_bar(4, value);
break;
case PCI_BAR5_REG:
pci_isa_write_bar(5, value);
break;
case PCI_UART1_INT_REG:
_rdmsr(DIVIL_MSR_REG(PIC_YSEL_HIGH), &hi, &lo);
/* disable uart1 interrupt in PIC */
lo &= ~(0xf << 24);
if (value) /* enable uart1 interrupt in PIC */
lo |= (CS5536_UART1_INTR << 24);
_wrmsr(DIVIL_MSR_REG(PIC_YSEL_HIGH), hi, lo);
break;
case PCI_UART2_INT_REG:
_rdmsr(DIVIL_MSR_REG(PIC_YSEL_HIGH), &hi, &lo);
/* disable uart2 interrupt in PIC */
lo &= ~(0xf << 28);
if (value) /* enable uart2 interrupt in PIC */
lo |= (CS5536_UART2_INTR << 28);
_wrmsr(DIVIL_MSR_REG(PIC_YSEL_HIGH), hi, lo);
break;
case PCI_ISA_FIXUP_REG:
if (value) {
/* enable the TARGET ABORT/MASTER ABORT etc. */
_rdmsr(SB_MSR_REG(SB_ERROR), &hi, &lo);
lo |= 0x00000063;
_wrmsr(SB_MSR_REG(SB_ERROR), hi, lo);
}
break;
default:
/* ALL OTHER PCI CONFIG SPACE HEADER IS NOT IMPLEMENTED. */
break;
}
}
/*
* isa_read: ISA read transfers
*
* We assume that this is not a bus master transfer.
*/
u32 pci_isa_read_reg(int reg)
{
u32 conf_data = 0;
u32 hi, lo;
switch (reg) {
case PCI_VENDOR_ID:
conf_data =
CFG_PCI_VENDOR_ID(CS5536_ISA_DEVICE_ID, CS5536_VENDOR_ID);
break;
case PCI_COMMAND:
/* we just check the first LBAR for the IO enable bit, */
/* maybe we should changed later. */
_rdmsr(DIVIL_MSR_REG(DIVIL_LBAR_SMB), &hi, &lo);
if (hi & 0x01)
conf_data |= PCI_COMMAND_IO;
break;
case PCI_STATUS:
conf_data |= PCI_STATUS_66MHZ;
conf_data |= PCI_STATUS_DEVSEL_MEDIUM;
conf_data |= PCI_STATUS_FAST_BACK;
_rdmsr(SB_MSR_REG(SB_ERROR), &hi, &lo);
if (lo & SB_TAS_ERR_FLAG)
conf_data |= PCI_STATUS_SIG_TARGET_ABORT;
if (lo & SB_TAR_ERR_FLAG)
conf_data |= PCI_STATUS_REC_TARGET_ABORT;
if (lo & SB_MAR_ERR_FLAG)
conf_data |= PCI_STATUS_REC_MASTER_ABORT;
if (lo & SB_PARE_ERR_FLAG)
conf_data |= PCI_STATUS_DETECTED_PARITY;
break;
case PCI_CLASS_REVISION:
_rdmsr(GLCP_MSR_REG(GLCP_CHIP_REV_ID), &hi, &lo);
conf_data = lo & 0x000000ff;
conf_data |= (CS5536_ISA_CLASS_CODE << 8);
break;
case PCI_CACHE_LINE_SIZE:
_rdmsr(SB_MSR_REG(SB_CTRL), &hi, &lo);
hi &= 0x000000f8;
conf_data = CFG_PCI_CACHE_LINE_SIZE(PCI_BRIDGE_HEADER_TYPE, hi);
break;
/*
* we only use the LBAR of DIVIL, no RCONF used.
* all of them are IO space.
*/
case PCI_BAR0_REG:
return pci_isa_read_bar(0);
break;
case PCI_BAR1_REG:
return pci_isa_read_bar(1);
break;
case PCI_BAR2_REG:
return pci_isa_read_bar(2);
break;
case PCI_BAR3_REG:
break;
case PCI_BAR4_REG:
return pci_isa_read_bar(4);
break;
case PCI_BAR5_REG:
return pci_isa_read_bar(5);
break;
case PCI_CARDBUS_CIS:
conf_data = PCI_CARDBUS_CIS_POINTER;
break;
case PCI_SUBSYSTEM_VENDOR_ID:
conf_data =
CFG_PCI_VENDOR_ID(CS5536_ISA_SUB_ID, CS5536_SUB_VENDOR_ID);
break;
case PCI_ROM_ADDRESS:
conf_data = PCI_EXPANSION_ROM_BAR;
break;
case PCI_CAPABILITY_LIST:
conf_data = PCI_CAPLIST_POINTER;
break;
case PCI_INTERRUPT_LINE:
/* no interrupt used here */
conf_data = CFG_PCI_INTERRUPT_LINE(0x00, 0x00);
break;
default:
break;
}
return conf_data;
}
/*
* The mfgpt timer interrupt is running early, so we must keep the south bridge
* mmio always enabled. Otherwise we may race with the PCI configuration which
* may temporarily disable it. When that happens and the timer interrupt fires,
* we are not able to clear it and the system will hang.
*/
static void cs5536_isa_mmio_always_on(struct pci_dev *dev)
{
dev->mmio_always_on = 1;
}
DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA,
PCI_CLASS_BRIDGE_ISA, 8, cs5536_isa_mmio_always_on);
@@ -0,0 +1,203 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* CS5536 General timer functions
*
* Copyright (C) 2007 Lemote Inc. & Institute of Computing Technology
* Author: Yanhua, yanh@lemote.com
*
* Copyright (C) 2009 Lemote Inc.
* Author: Wu zhangjin, wuzhangjin@gmail.com
*
* Reference: AMD Geode(TM) CS5536 Companion Device Data Book
*/
#include <linux/io.h>
#include <linux/init.h>
#include <linux/export.h>
#include <linux/jiffies.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/clockchips.h>
#include <asm/time.h>
#include <cs5536/cs5536_mfgpt.h>
static DEFINE_RAW_SPINLOCK(mfgpt_lock);
static u32 mfgpt_base;
/*
* Initialize the MFGPT timer.
*
* This is also called after resume to bring the MFGPT into operation again.
*/
/* disable counter */
void disable_mfgpt0_counter(void)
{
outw(inw(MFGPT0_SETUP) & 0x7fff, MFGPT0_SETUP);
}
EXPORT_SYMBOL(disable_mfgpt0_counter);
/* enable counter, comparator2 to event mode, 14.318MHz clock */
void enable_mfgpt0_counter(void)
{
outw(0xe310, MFGPT0_SETUP);
}
EXPORT_SYMBOL(enable_mfgpt0_counter);
static int mfgpt_timer_set_periodic(struct clock_event_device *evt)
{
raw_spin_lock(&mfgpt_lock);
outw(COMPARE, MFGPT0_CMP2); /* set comparator2 */
outw(0, MFGPT0_CNT); /* set counter to 0 */
enable_mfgpt0_counter();
raw_spin_unlock(&mfgpt_lock);
return 0;
}
static int mfgpt_timer_shutdown(struct clock_event_device *evt)
{
if (clockevent_state_periodic(evt) || clockevent_state_oneshot(evt)) {
raw_spin_lock(&mfgpt_lock);
disable_mfgpt0_counter();
raw_spin_unlock(&mfgpt_lock);
}
return 0;
}
static struct clock_event_device mfgpt_clockevent = {
.name = "mfgpt",
.features = CLOCK_EVT_FEAT_PERIODIC,
/* The oneshot mode have very high deviation, don't use it! */
.set_state_shutdown = mfgpt_timer_shutdown,
.set_state_periodic = mfgpt_timer_set_periodic,
.irq = CS5536_MFGPT_INTR,
};
static irqreturn_t timer_interrupt(int irq, void *dev_id)
{
u32 basehi;
/*
* get MFGPT base address
*
* NOTE: do not remove me, it's need for the value of mfgpt_base is
* variable
*/
_rdmsr(DIVIL_MSR_REG(DIVIL_LBAR_MFGPT), &basehi, &mfgpt_base);
/* ack */
outw(inw(MFGPT0_SETUP) | 0x4000, MFGPT0_SETUP);
mfgpt_clockevent.event_handler(&mfgpt_clockevent);
return IRQ_HANDLED;
}
/*
* Initialize the conversion factor and the min/max deltas of the clock event
* structure and register the clock event source with the framework.
*/
void __init setup_mfgpt0_timer(void)
{
u32 basehi;
struct clock_event_device *cd = &mfgpt_clockevent;
unsigned int cpu = smp_processor_id();
cd->cpumask = cpumask_of(cpu);
clockevent_set_clock(cd, MFGPT_TICK_RATE);
cd->max_delta_ns = clockevent_delta2ns(0xffff, cd);
cd->max_delta_ticks = 0xffff;
cd->min_delta_ns = clockevent_delta2ns(0xf, cd);
cd->min_delta_ticks = 0xf;
/* Enable MFGPT0 Comparator 2 Output to the Interrupt Mapper */
_wrmsr(DIVIL_MSR_REG(MFGPT_IRQ), 0, 0x100);
/* Enable Interrupt Gate 5 */
_wrmsr(DIVIL_MSR_REG(PIC_ZSEL_LOW), 0, 0x50000);
/* get MFGPT base address */
_rdmsr(DIVIL_MSR_REG(DIVIL_LBAR_MFGPT), &basehi, &mfgpt_base);
clockevents_register_device(cd);
if (request_irq(CS5536_MFGPT_INTR, timer_interrupt,
IRQF_NOBALANCING | IRQF_TIMER, "timer", NULL))
pr_err("Failed to register timer interrupt\n");
}
/*
* Since the MFGPT overflows every tick, its not very useful
* to just read by itself. So use jiffies to emulate a free
* running counter:
*/
static u64 mfgpt_read(struct clocksource *cs)
{
unsigned long flags;
int count;
u32 jifs;
static int old_count;
static u32 old_jifs;
raw_spin_lock_irqsave(&mfgpt_lock, flags);
/*
* Although our caller may have the read side of xtime_lock,
* this is now a seqlock, and we are cheating in this routine
* by having side effects on state that we cannot undo if
* there is a collision on the seqlock and our caller has to
* retry. (Namely, old_jifs and old_count.) So we must treat
* jiffies as volatile despite the lock. We read jiffies
* before latching the timer count to guarantee that although
* the jiffies value might be older than the count (that is,
* the counter may underflow between the last point where
* jiffies was incremented and the point where we latch the
* count), it cannot be newer.
*/
jifs = jiffies;
/* read the count */
count = inw(MFGPT0_CNT);
/*
* It's possible for count to appear to go the wrong way for this
* reason:
*
* The timer counter underflows, but we haven't handled the resulting
* interrupt and incremented jiffies yet.
*
* Previous attempts to handle these cases intelligently were buggy, so
* we just do the simple thing now.
*/
if (count < old_count && jifs == old_jifs)
count = old_count;
old_count = count;
old_jifs = jifs;
raw_spin_unlock_irqrestore(&mfgpt_lock, flags);
return (u64) (jifs * COMPARE) + count;
}
static struct clocksource clocksource_mfgpt = {
.name = "mfgpt",
.rating = 120, /* Functional for real use, but not desired */
.read = mfgpt_read,
.mask = CLOCKSOURCE_MASK(32),
};
int __init init_mfgpt_clocksource(void)
{
if (num_possible_cpus() > 1) /* MFGPT does not scale! */
return 0;
return clocksource_register_hz(&clocksource_mfgpt, MFGPT_TICK_RATE);
}
arch_initcall(init_mfgpt_clocksource);
@@ -0,0 +1,145 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* the OHCI Virtual Support Module of AMD CS5536
*
* Copyright (C) 2007 Lemote, Inc.
* Author : jlliu, liujl@lemote.com
*
* Copyright (C) 2009 Lemote, Inc.
* Author: Wu Zhangjin, wuzhangjin@gmail.com
*/
#include <cs5536/cs5536.h>
#include <cs5536/cs5536_pci.h>
void pci_ohci_write_reg(int reg, u32 value)
{
u32 hi = 0, lo = value;
switch (reg) {
case PCI_COMMAND:
_rdmsr(USB_MSR_REG(USB_OHCI), &hi, &lo);
if (value & PCI_COMMAND_MASTER)
hi |= PCI_COMMAND_MASTER;
else
hi &= ~PCI_COMMAND_MASTER;
if (value & PCI_COMMAND_MEMORY)
hi |= PCI_COMMAND_MEMORY;
else
hi &= ~PCI_COMMAND_MEMORY;
_wrmsr(USB_MSR_REG(USB_OHCI), hi, lo);
break;
case PCI_STATUS:
if (value & PCI_STATUS_PARITY) {
_rdmsr(SB_MSR_REG(SB_ERROR), &hi, &lo);
if (lo & SB_PARE_ERR_FLAG) {
lo = (lo & 0x0000ffff) | SB_PARE_ERR_FLAG;
_wrmsr(SB_MSR_REG(SB_ERROR), hi, lo);
}
}
break;
case PCI_BAR0_REG:
if (value == PCI_BAR_RANGE_MASK) {
_rdmsr(GLCP_MSR_REG(GLCP_SOFT_COM), &hi, &lo);
lo |= SOFT_BAR_OHCI_FLAG;
_wrmsr(GLCP_MSR_REG(GLCP_SOFT_COM), hi, lo);
} else if ((value & 0x01) == 0x00) {
_rdmsr(USB_MSR_REG(USB_OHCI), &hi, &lo);
lo = value;
_wrmsr(USB_MSR_REG(USB_OHCI), hi, lo);
value &= 0xfffffff0;
hi = 0x40000000 | ((value & 0xff000000) >> 24);
lo = 0x000fffff | ((value & 0x00fff000) << 8);
_wrmsr(GLIU_MSR_REG(GLIU_P2D_BM3), hi, lo);
}
break;
case PCI_OHCI_INT_REG:
_rdmsr(DIVIL_MSR_REG(PIC_YSEL_LOW), &hi, &lo);
lo &= ~(0xf << PIC_YSEL_LOW_USB_SHIFT);
if (value) /* enable all the usb interrupt in PIC */
lo |= (CS5536_USB_INTR << PIC_YSEL_LOW_USB_SHIFT);
_wrmsr(DIVIL_MSR_REG(PIC_YSEL_LOW), hi, lo);
break;
default:
break;
}
}
u32 pci_ohci_read_reg(int reg)
{
u32 conf_data = 0;
u32 hi, lo;
switch (reg) {
case PCI_VENDOR_ID:
conf_data =
CFG_PCI_VENDOR_ID(CS5536_OHCI_DEVICE_ID, CS5536_VENDOR_ID);
break;
case PCI_COMMAND:
_rdmsr(USB_MSR_REG(USB_OHCI), &hi, &lo);
if (hi & PCI_COMMAND_MASTER)
conf_data |= PCI_COMMAND_MASTER;
if (hi & PCI_COMMAND_MEMORY)
conf_data |= PCI_COMMAND_MEMORY;
break;
case PCI_STATUS:
conf_data |= PCI_STATUS_66MHZ;
conf_data |= PCI_STATUS_FAST_BACK;
_rdmsr(SB_MSR_REG(SB_ERROR), &hi, &lo);
if (lo & SB_PARE_ERR_FLAG)
conf_data |= PCI_STATUS_PARITY;
conf_data |= PCI_STATUS_DEVSEL_MEDIUM;
break;
case PCI_CLASS_REVISION:
_rdmsr(USB_MSR_REG(USB_CAP), &hi, &lo);
conf_data = lo & 0x000000ff;
conf_data |= (CS5536_OHCI_CLASS_CODE << 8);
break;
case PCI_CACHE_LINE_SIZE:
conf_data =
CFG_PCI_CACHE_LINE_SIZE(PCI_NORMAL_HEADER_TYPE,
PCI_NORMAL_LATENCY_TIMER);
break;
case PCI_BAR0_REG:
_rdmsr(GLCP_MSR_REG(GLCP_SOFT_COM), &hi, &lo);
if (lo & SOFT_BAR_OHCI_FLAG) {
conf_data = CS5536_OHCI_RANGE |
PCI_BASE_ADDRESS_SPACE_MEMORY;
lo &= ~SOFT_BAR_OHCI_FLAG;
_wrmsr(GLCP_MSR_REG(GLCP_SOFT_COM), hi, lo);
} else {
_rdmsr(USB_MSR_REG(USB_OHCI), &hi, &lo);
conf_data = lo & 0xffffff00;
conf_data &= ~0x0000000f; /* 32bit mem */
}
break;
case PCI_CARDBUS_CIS:
conf_data = PCI_CARDBUS_CIS_POINTER;
break;
case PCI_SUBSYSTEM_VENDOR_ID:
conf_data =
CFG_PCI_VENDOR_ID(CS5536_OHCI_SUB_ID, CS5536_SUB_VENDOR_ID);
break;
case PCI_ROM_ADDRESS:
conf_data = PCI_EXPANSION_ROM_BAR;
break;
case PCI_CAPABILITY_LIST:
conf_data = PCI_CAPLIST_USB_POINTER;
break;
case PCI_INTERRUPT_LINE:
conf_data =
CFG_PCI_INTERRUPT_LINE(PCI_DEFAULT_PIN, CS5536_USB_INTR);
break;
case PCI_OHCI_INT_REG:
_rdmsr(DIVIL_MSR_REG(PIC_YSEL_LOW), &hi, &lo);
if (((lo >> PIC_YSEL_LOW_USB_SHIFT) & 0xf) == CS5536_USB_INTR)
conf_data = 1;
break;
default:
break;
}
return conf_data;
}
@@ -0,0 +1,84 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* read/write operation to the PCI config space of CS5536
*
* Copyright (C) 2007 Lemote, Inc.
* Author : jlliu, liujl@lemote.com
*
* Copyright (C) 2009 Lemote, Inc.
* Author: Wu Zhangjin, wuzhangjin@gmail.com
*
* the Virtual Support Module(VSM) for virtulizing the PCI
* configure space are defined in cs5536_modulename.c respectively,
*
* after this virtulizing, user can access the PCI configure space
* directly as a normal multi-function PCI device which follows
* the PCI-2.2 spec.
*/
#include <linux/types.h>
#include <cs5536/cs5536_pci.h>
#include <cs5536/cs5536_vsm.h>
enum {
CS5536_FUNC_START = -1,
CS5536_ISA_FUNC,
reserved_func,
CS5536_IDE_FUNC,
CS5536_ACC_FUNC,
CS5536_OHCI_FUNC,
CS5536_EHCI_FUNC,
CS5536_FUNC_END,
};
static const cs5536_pci_vsm_write vsm_conf_write[] = {
[CS5536_ISA_FUNC] = pci_isa_write_reg,
[reserved_func] = NULL,
[CS5536_IDE_FUNC] = pci_ide_write_reg,
[CS5536_ACC_FUNC] = pci_acc_write_reg,
[CS5536_OHCI_FUNC] = pci_ohci_write_reg,
[CS5536_EHCI_FUNC] = pci_ehci_write_reg,
};
static const cs5536_pci_vsm_read vsm_conf_read[] = {
[CS5536_ISA_FUNC] = pci_isa_read_reg,
[reserved_func] = NULL,
[CS5536_IDE_FUNC] = pci_ide_read_reg,
[CS5536_ACC_FUNC] = pci_acc_read_reg,
[CS5536_OHCI_FUNC] = pci_ohci_read_reg,
[CS5536_EHCI_FUNC] = pci_ehci_read_reg,
};
/*
* write to PCI config space and transfer it to MSR write.
*/
void cs5536_pci_conf_write4(int function, int reg, u32 value)
{
if ((function <= CS5536_FUNC_START) || (function >= CS5536_FUNC_END))
return;
if ((reg < 0) || (reg > 0x100) || ((reg & 0x03) != 0))
return;
if (vsm_conf_write[function] != NULL)
vsm_conf_write[function](reg, value);
}
/*
* read PCI config space and transfer it to MSR access.
*/
u32 cs5536_pci_conf_read4(int function, int reg)
{
u32 data = 0;
if ((function <= CS5536_FUNC_START) || (function >= CS5536_FUNC_END))
return 0;
if ((reg < 0) || ((reg & 0x03) != 0))
return 0;
if (reg > 0x100)
return 0xffffffff;
if (vsm_conf_read[function] != NULL)
data = vsm_conf_read[function](reg);
return data;
}
@@ -0,0 +1,53 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Based on Ocelot Linux port, which is
* Copyright 2001 MontaVista Software Inc.
* Author: jsun@mvista.com or jsun@junsun.net
*
* Copyright 2003 ICT CAS
* Author: Michael Guo <guoyi@ict.ac.cn>
*
* Copyright (C) 2007 Lemote Inc. & Institute of Computing Technology
* Author: Fuxin Zhang, zhangfx@lemote.com
*
* Copyright (C) 2009 Lemote Inc.
* Author: Wu Zhangjin, wuzhangjin@gmail.com
*/
#include <linux/export.h>
#include <asm/bootinfo.h>
#include <asm/fw/fw.h>
#include <loongson.h>
u32 cpu_clock_freq;
EXPORT_SYMBOL(cpu_clock_freq);
void __init prom_init_env(void)
{
/* pmon passes arguments in 32bit pointers */
unsigned int processor_id;
cpu_clock_freq = fw_getenvl("cpuclock");
memsize = fw_getenvl("memsize");
highmemsize = fw_getenvl("highmemsize");
if (memsize == 0)
memsize = 256;
pr_info("memsize=%u, highmemsize=%u\n", memsize, highmemsize);
if (cpu_clock_freq == 0) {
processor_id = (&current_cpu_data)->processor_id;
switch (processor_id & PRID_REV_MASK) {
case PRID_REV_LOONGSON2E:
cpu_clock_freq = 533080000;
break;
case PRID_REV_LOONGSON2F:
cpu_clock_freq = 797000000;
break;
default:
cpu_clock_freq = 100000000;
break;
}
}
pr_info("CpuClock = %u\n", cpu_clock_freq);
}
@@ -0,0 +1,47 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2009 Lemote Inc.
* Author: Wu Zhangjin, wuzhangjin@gmail.com
*/
#include <linux/memblock.h>
#include <asm/bootinfo.h>
#include <asm/traps.h>
#include <asm/smp-ops.h>
#include <asm/cacheflush.h>
#include <asm/fw/fw.h>
#include <loongson.h>
/* Loongson CPU address windows config space base address */
unsigned long __maybe_unused _loongson_addrwincfg_base;
static void __init mips_nmi_setup(void)
{
void *base;
base = (void *)(CAC_BASE + 0x380);
memcpy(base, except_vec_nmi, 0x80);
flush_icache_range((unsigned long)base, (unsigned long)base + 0x80);
}
void __init prom_init(void)
{
#ifdef CONFIG_CPU_SUPPORTS_ADDRWINCFG
_loongson_addrwincfg_base = (unsigned long)
ioremap(LOONGSON_ADDRWINCFG_BASE, LOONGSON_ADDRWINCFG_SIZE);
#endif
fw_init_cmdline();
prom_init_machtype();
prom_init_env();
/* init base address of io space */
set_io_port_base((unsigned long)
ioremap(LOONGSON_PCIIO_BASE, LOONGSON_PCIIO_SIZE));
prom_init_memory();
/*init the uart base address */
prom_init_uart_base();
board_nmi_handler_setup = mips_nmi_setup;
}
@@ -0,0 +1,63 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2007 Lemote Inc. & Institute of Computing Technology
* Author: Fuxin Zhang, zhangfx@lemote.com
*/
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <loongson.h>
/*
* the first level int-handler will jump here if it is a bonito irq
*/
void bonito_irqdispatch(void)
{
u32 int_status;
int i;
/* workaround the IO dma problem: let cpu looping to allow DMA finish */
int_status = LOONGSON_INTISR;
while (int_status & (1 << 10)) {
udelay(1);
int_status = LOONGSON_INTISR;
}
/* Get pending sources, masked by current enables */
int_status = LOONGSON_INTISR & LOONGSON_INTEN;
if (int_status) {
i = __ffs(int_status);
do_IRQ(LOONGSON_IRQ_BASE + i);
}
}
asmlinkage void plat_irq_dispatch(void)
{
unsigned int pending;
pending = read_c0_cause() & read_c0_status() & ST0_IM;
/* machine-specific plat_irq_dispatch */
mach_irq_dispatch(pending);
}
void __init arch_init_irq(void)
{
/*
* Clear all of the interrupts while we change the able around a bit.
* int-handler is not on bootstrap
*/
clear_c0_status(ST0_IM | ST0_BEV);
/* no steer */
LOONGSON_INTSTEER = 0;
/*
* Mask out all interrupt by writing "1" to all bit position in
* the interrupt reset reg.
*/
LOONGSON_INTENCLR = ~0;
/* machine specific irq init */
mach_init_irq();
}
@@ -0,0 +1,61 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2009 Lemote Inc.
* Author: Wu Zhangjin, wuzhangjin@gmail.com
*
* Copyright (c) 2009 Zhang Le <r0bertz@gentoo.org>
*/
#include <linux/errno.h>
#include <asm/bootinfo.h>
#include <loongson.h>
#include <machine.h>
/* please ensure the length of the machtype string is less than 50 */
#define MACHTYPE_LEN 50
static const char *system_types[] = {
[MACH_LOONGSON_UNKNOWN] = "unknown loongson machine",
[MACH_LEMOTE_FL2E] = "lemote-fuloong-2e-box",
[MACH_LEMOTE_FL2F] = "lemote-fuloong-2f-box",
[MACH_LEMOTE_ML2F7] = "lemote-mengloong-2f-7inches",
[MACH_LEMOTE_YL2F89] = "lemote-yeeloong-2f-8.9inches",
[MACH_DEXXON_GDIUM2F10] = "dexxon-gdium-2f",
[MACH_LEMOTE_NAS] = "lemote-nas-2f",
[MACH_LEMOTE_LL2F] = "lemote-lynloong-2f",
[MACH_LOONGSON_END] = NULL,
};
const char *get_system_type(void)
{
return system_types[mips_machtype];
}
void __weak __init mach_prom_init_machtype(void)
{
}
void __init prom_init_machtype(void)
{
char *p, str[MACHTYPE_LEN + 1];
int machtype = MACH_LEMOTE_FL2E;
mips_machtype = LOONGSON_MACHTYPE;
p = strstr(arcs_cmdline, "machtype=");
if (!p) {
mach_prom_init_machtype();
return;
}
p += strlen("machtype=");
strscpy(str, p);
p = strstr(str, " ");
if (p)
*p = '\0';
for (; system_types[machtype]; machtype++)
if (strstr(system_types[machtype], str)) {
mips_machtype = machtype;
break;
}
}
@@ -0,0 +1,43 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
*/
#include <linux/fs.h>
#include <linux/fcntl.h>
#include <linux/memblock.h>
#include <linux/mm.h>
#include <asm/bootinfo.h>
#include <loongson.h>
#include <mem.h>
#include <pci.h>
u32 memsize, highmemsize;
void __init prom_init_memory(void)
{
memblock_add(0x0, (memsize << 20));
#ifdef CONFIG_CPU_SUPPORTS_ADDRWINCFG
{
int bit;
bit = fls(memsize + highmemsize);
if (bit != ffs(memsize + highmemsize))
bit += 20;
else
bit = bit + 20 - 1;
/* set cpu window3 to map CPU to DDR: 2G -> 2G */
LOONGSON_ADDRWIN_CPUTODDR(ADDRWIN_WIN3, 0x80000000ul,
0x80000000ul, (1 << bit));
mmiowb();
}
#endif /* !CONFIG_CPU_SUPPORTS_ADDRWINCFG */
#ifdef CONFIG_64BIT
if (highmemsize > 0)
memblock_add(LOONGSON_HIGHMEM_START, highmemsize << 20);
#endif /* !CONFIG_64BIT */
}
@@ -0,0 +1,91 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2007 Lemote, Inc. & Institute of Computing Technology
* Author: Fuxin Zhang, zhangfx@lemote.com
*/
#include <linux/pci.h>
#include <pci.h>
#include <loongson.h>
static struct resource loongson_pci_mem_resource = {
.name = "pci memory space",
.start = LOONGSON_PCI_MEM_START,
.end = LOONGSON_PCI_MEM_END,
.flags = IORESOURCE_MEM,
};
static struct resource loongson_pci_io_resource = {
.name = "pci io space",
.start = 0x00000000UL, /* See loongson2ef_pcibios_init(). */
.end = IO_SPACE_LIMIT,
.flags = IORESOURCE_IO,
};
static struct pci_controller loongson_pci_controller = {
.pci_ops = &loongson_pci_ops,
.io_resource = &loongson_pci_io_resource,
.mem_resource = &loongson_pci_mem_resource,
.mem_offset = 0x00000000UL,
.io_offset = 0x00000000UL,
};
static void __init setup_pcimap(void)
{
/*
* local to PCI mapping for CPU accessing PCI space
* CPU address space [256M,448M] is window for accessing pci space
* we set pcimap_lo[0,1,2] to map it to pci space[0M,64M], [320M,448M]
*
* pcimap: PCI_MAP2 PCI_Mem_Lo2 PCI_Mem_Lo1 PCI_Mem_Lo0
* [<2G] [384M,448M] [320M,384M] [0M,64M]
*/
LOONGSON_PCIMAP = LOONGSON_PCIMAP_PCIMAP_2 |
LOONGSON_PCIMAP_WIN(2, LOONGSON_PCILO2_BASE) |
LOONGSON_PCIMAP_WIN(1, LOONGSON_PCILO1_BASE) |
LOONGSON_PCIMAP_WIN(0, 0);
/*
* PCI-DMA to local mapping: [2G,2G+256M] -> [0M,256M]
*/
LOONGSON_PCIBASE0 = 0x80000000ul; /* base: 2G -> mmap: 0M */
/* size: 256M, burst transmission, pre-fetch enable, 64bit */
LOONGSON_PCI_HIT0_SEL_L = 0xc000000cul;
LOONGSON_PCI_HIT0_SEL_H = 0xfffffffful;
LOONGSON_PCI_HIT1_SEL_L = 0x00000006ul; /* set this BAR as invalid */
LOONGSON_PCI_HIT1_SEL_H = 0x00000000ul;
LOONGSON_PCI_HIT2_SEL_L = 0x00000006ul; /* set this BAR as invalid */
LOONGSON_PCI_HIT2_SEL_H = 0x00000000ul;
/* avoid deadlock of PCI reading/writing lock operation */
LOONGSON_PCI_ISR4C = 0xd2000001ul;
/* can not change gnt to break pci transfer when device's gnt not
deassert for some broken device */
LOONGSON_PXARB_CFG = 0x00fe0105ul;
#ifdef CONFIG_CPU_SUPPORTS_ADDRWINCFG
/*
* set cpu addr window2 to map CPU address space to PCI address space
*/
LOONGSON_ADDRWIN_CPUTOPCI(ADDRWIN_WIN2, LOONGSON_CPU_MEM_SRC,
LOONGSON_PCI_MEM_DST, MMAP_CPUTOPCI_SIZE);
#endif
}
void __init loongson2ef_pcibios_init(void)
{
setup_pcimap();
/*
* ISA-mode only IDE controllers have a hard dependency on ISA IO ports.
*
* Claim them by setting PCI IO space to start at 0x00000000, and set
* PCIBIOS_MIN_IO to prevent non-legacy PCI devices from touching
* reserved regions.
*/
PCIBIOS_MIN_IO = LOONGSON_PCI_IO_START;
loongson_pci_controller.io_map_base = mips_io_port_base;
register_pci_controller(&loongson_pci_controller);
}
@@ -0,0 +1,27 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2009 Lemote Inc.
* Author: Wu Zhangjin, wuzhangjin@gmail.com
*/
#include <linux/err.h>
#include <linux/smp.h>
#include <linux/platform_device.h>
static struct platform_device loongson2_cpufreq_device = {
.name = "loongson2_cpufreq",
.id = -1,
};
static int __init loongson2_cpufreq_init(void)
{
struct cpuinfo_mips *c = &current_cpu_data;
/* Only 2F revision and its successors support CPUFreq */
if ((c->processor_id & PRID_REV_MASK) >= PRID_REV_LOONGSON2F)
return platform_device_register(&loongson2_cpufreq_device);
return -ENODEV;
}
arch_initcall(loongson2_cpufreq_init);
@@ -0,0 +1,158 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* loongson-specific suspend support
*
* Copyright (C) 2009 Lemote Inc.
* Author: Wu Zhangjin <wuzhangjin@gmail.com>
*/
#include <linux/suspend.h>
#include <linux/interrupt.h>
#include <linux/pm.h>
#include <asm/i8259.h>
#include <asm/mipsregs.h>
#include <loongson.h>
static unsigned int __maybe_unused cached_master_mask; /* i8259A */
static unsigned int __maybe_unused cached_slave_mask;
static unsigned int __maybe_unused cached_bonito_irq_mask; /* bonito */
void arch_suspend_disable_irqs(void)
{
/* disable all mips events */
local_irq_disable();
#ifdef CONFIG_I8259
/* disable all events of i8259A */
cached_slave_mask = inb(PIC_SLAVE_IMR);
cached_master_mask = inb(PIC_MASTER_IMR);
outb(0xff, PIC_SLAVE_IMR);
inb(PIC_SLAVE_IMR);
outb(0xff, PIC_MASTER_IMR);
inb(PIC_MASTER_IMR);
#endif
/* disable all events of bonito */
cached_bonito_irq_mask = LOONGSON_INTEN;
LOONGSON_INTENCLR = 0xffff;
(void)LOONGSON_INTENCLR;
}
void arch_suspend_enable_irqs(void)
{
/* enable all mips events */
local_irq_enable();
#ifdef CONFIG_I8259
/* only enable the cached events of i8259A */
outb(cached_slave_mask, PIC_SLAVE_IMR);
outb(cached_master_mask, PIC_MASTER_IMR);
#endif
/* enable all cached events of bonito */
LOONGSON_INTENSET = cached_bonito_irq_mask;
(void)LOONGSON_INTENSET;
}
/*
* Setup the board-specific events for waking up loongson from wait mode
*/
void __weak setup_wakeup_events(void)
{
}
/*
* Check wakeup events
*/
int __weak wakeup_loongson(void)
{
return 1;
}
/*
* If the events are really what we want to wakeup the CPU, wake it up
* otherwise put the CPU asleep again.
*/
static void wait_for_wakeup_events(void)
{
while (!wakeup_loongson())
writel(readl(LOONGSON_CHIPCFG) & ~0x7, LOONGSON_CHIPCFG);
}
/*
* Stop all perf counters
*
* $24 is the control register of Loongson perf counter
*/
static inline void stop_perf_counters(void)
{
__write_64bit_c0_register($24, 0, 0);
}
static void loongson_suspend_enter(void)
{
unsigned int cached_cpu_freq;
/* setup wakeup events via enabling the IRQs */
setup_wakeup_events();
stop_perf_counters();
cached_cpu_freq = readl(LOONGSON_CHIPCFG);
/* Put CPU into wait mode */
writel(readl(LOONGSON_CHIPCFG) & ~0x7, LOONGSON_CHIPCFG);
/* wait for the given events to wakeup cpu from wait mode */
wait_for_wakeup_events();
writel(cached_cpu_freq, LOONGSON_CHIPCFG);
mmiowb();
}
void __weak mach_suspend(void)
{
}
void __weak mach_resume(void)
{
}
static int loongson_pm_enter(suspend_state_t state)
{
mach_suspend();
/* processor specific suspend */
loongson_suspend_enter();
mach_resume();
return 0;
}
static int loongson_pm_valid_state(suspend_state_t state)
{
switch (state) {
case PM_SUSPEND_ON:
case PM_SUSPEND_STANDBY:
case PM_SUSPEND_MEM:
return 1;
default:
return 0;
}
}
static const struct platform_suspend_ops loongson_pm_ops = {
.valid = loongson_pm_valid_state,
.enter = loongson_pm_enter,
};
static int __init loongson_pm_init(void)
{
suspend_set_ops(&loongson_pm_ops);
return 0;
}
arch_initcall(loongson_pm_init);
@@ -0,0 +1,73 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
*
* Copyright (C) 2007 Lemote, Inc. & Institute of Computing Technology
* Author: Fuxin Zhang, zhangfx@lemote.com
* Copyright (C) 2009 Lemote, Inc.
* Author: Zhangjin Wu, wuzhangjin@gmail.com
*/
#include <linux/init.h>
#include <linux/pm.h>
#include <asm/idle.h>
#include <asm/reboot.h>
#include <loongson.h>
static inline void loongson_reboot(void)
{
#ifndef CONFIG_CPU_JUMP_WORKAROUNDS
((void (*)(void))ioremap(LOONGSON_BOOT_BASE, 4)) ();
#else
void (*func)(void);
func = (void *)ioremap(LOONGSON_BOOT_BASE, 4);
__asm__ __volatile__(
" .set noat \n"
" jr %[func] \n"
" .set at \n"
: /* No outputs */
: [func] "r" (func));
#endif
}
static void loongson_restart(char *command)
{
/* do preparation for reboot */
mach_prepare_reboot();
/* reboot via jumping to boot base address */
loongson_reboot();
}
static void loongson_poweroff(void)
{
mach_prepare_shutdown();
/*
* It needs a wait loop here, but mips/kernel/reset.c already calls
* a generic delay loop, machine_hang(), so simply return.
*/
return;
}
static void loongson_halt(void)
{
pr_notice("\n\n** You can safely turn off the power now **\n\n");
while (1) {
if (cpu_wait)
cpu_wait();
}
}
static int __init mips_reboot_setup(void)
{
_machine_restart = loongson_restart;
_machine_halt = loongson_halt;
pm_power_off = loongson_poweroff;
return 0;
}
arch_initcall(mips_reboot_setup);
@@ -0,0 +1,39 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Lemote Fuloong platform support
*
* Copyright(c) 2010 Arnaud Patard <apatard@mandriva.com>
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/mc146818rtc.h>
static struct resource loongson_rtc_resources[] = {
{
.start = RTC_PORT(0),
.end = RTC_PORT(1),
.flags = IORESOURCE_IO,
}, {
.start = RTC_IRQ,
.end = RTC_IRQ,
.flags = IORESOURCE_IRQ,
}
};
static struct platform_device loongson_rtc_device = {
.name = "rtc_cmos",
.id = -1,
.resource = loongson_rtc_resources,
.num_resources = ARRAY_SIZE(loongson_rtc_resources),
};
static int __init loongson_rtc_platform_init(void)
{
platform_device_register(&loongson_rtc_device);
return 0;
}
device_initcall(loongson_rtc_platform_init);
@@ -0,0 +1,86 @@
/*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 2007 Ralf Baechle (ralf@linux-mips.org)
*
* Copyright (C) 2009 Lemote, Inc.
* Author: Yan hua (yanhua@lemote.com)
* Author: Wu Zhangjin (wuzhangjin@gmail.com)
*/
#include <linux/io.h>
#include <linux/module.h>
#include <linux/serial_8250.h>
#include <asm/bootinfo.h>
#include <loongson.h>
#include <machine.h>
#define PORT(int, clk) \
{ \
.irq = int, \
.uartclk = clk, \
.iotype = UPIO_PORT, \
.flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST, \
.regshift = 0, \
}
#define PORT_M(int, clk) \
{ \
.irq = MIPS_CPU_IRQ_BASE + (int), \
.uartclk = clk, \
.iotype = UPIO_MEM, \
.membase = (void __iomem *)NULL, \
.flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST, \
.regshift = 0, \
}
static struct plat_serial8250_port uart8250_data[MACH_LOONGSON_END + 1] = {
[MACH_LOONGSON_UNKNOWN] = {},
[MACH_LEMOTE_FL2E] = PORT(4, 1843200),
[MACH_LEMOTE_FL2F] = PORT(3, 1843200),
[MACH_LEMOTE_ML2F7] = PORT_M(3, 3686400),
[MACH_LEMOTE_YL2F89] = PORT_M(3, 3686400),
[MACH_DEXXON_GDIUM2F10] = PORT_M(3, 3686400),
[MACH_LEMOTE_NAS] = PORT_M(3, 3686400),
[MACH_LEMOTE_LL2F] = PORT(3, 1843200),
[MACH_LOONGSON_END] = {},
};
static struct platform_device uart8250_device = {
.name = "serial8250",
.id = PLAT8250_DEV_PLATFORM,
};
static int __init serial_init(void)
{
unsigned char iotype;
iotype = uart8250_data[mips_machtype].iotype;
if (UPIO_MEM == iotype) {
uart8250_data[mips_machtype].mapbase =
loongson_uart_base;
uart8250_data[mips_machtype].membase =
(void __iomem *)_loongson_uart_base;
}
else if (UPIO_PORT == iotype)
uart8250_data[mips_machtype].iobase =
loongson_uart_base - LOONGSON_PCIIO_BASE;
memset(&uart8250_data[mips_machtype + 1], 0,
sizeof(struct plat_serial8250_port));
uart8250_device.dev.platform_data = &uart8250_data[mips_machtype];
return platform_device_register(&uart8250_device);
}
module_init(serial_init);
static void __exit serial_exit(void)
{
platform_device_unregister(&uart8250_device);
}
module_exit(serial_exit);
@@ -0,0 +1,31 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2007 Lemote Inc. & Institute of Computing Technology
* Author: Fuxin Zhang, zhangfx@lemote.com
*/
#include <linux/export.h>
#include <linux/init.h>
#include <asm/wbflush.h>
#include <asm/bootinfo.h>
#include <loongson.h>
static void wbflush_loongson(void)
{
asm(".set\tpush\n\t"
".set\tnoreorder\n\t"
".set mips3\n\t"
"sync\n\t"
"nop\n\t"
".set\tpop\n\t"
".set mips0\n\t");
}
void (*__wbflush)(void) = wbflush_loongson;
EXPORT_SYMBOL(__wbflush);
void __init plat_mem_setup(void)
{
loongson2ef_pcibios_init();
}
@@ -0,0 +1,28 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2007 Lemote, Inc. & Institute of Computing Technology
* Author: Fuxin Zhang, zhangfx@lemote.com
*
* Copyright (C) 2009 Lemote Inc.
* Author: Wu Zhangjin, wuzhangjin@gmail.com
*/
#include <asm/mc146818-time.h>
#include <asm/time.h>
#include <asm/hpet.h>
#include <loongson.h>
#include <cs5536/cs5536_mfgpt.h>
void __init plat_time_init(void)
{
/* setup mips r4k timer */
mips_hpt_frequency = cpu_clock_freq / 2;
setup_mfgpt0_timer();
}
void read_persistent_clock64(struct timespec64 *ts)
{
ts->tv_sec = mc146818_get_cmos_time();
ts->tv_nsec = 0;
}
@@ -0,0 +1,43 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2009 Lemote Inc.
* Author: Wu Zhangjin, wuzhangjin@gmail.com
*/
#include <linux/export.h>
#include <asm/bootinfo.h>
#include <asm/setup.h>
#include <loongson.h>
/* raw */
unsigned long loongson_uart_base;
/* ioremapped */
unsigned long _loongson_uart_base;
EXPORT_SYMBOL(loongson_uart_base);
EXPORT_SYMBOL(_loongson_uart_base);
void prom_init_loongson_uart_base(void)
{
switch (mips_machtype) {
case MACH_LEMOTE_FL2E:
loongson_uart_base = LOONGSON_PCIIO_BASE + 0x3f8;
break;
case MACH_LEMOTE_FL2F:
case MACH_LEMOTE_LL2F:
loongson_uart_base = LOONGSON_PCIIO_BASE + 0x2f8;
break;
case MACH_LEMOTE_ML2F7:
case MACH_LEMOTE_YL2F89:
case MACH_DEXXON_GDIUM2F10:
case MACH_LEMOTE_NAS:
default:
/* The CPU provided serial port (LPC) */
loongson_uart_base = LOONGSON_LIO1_BASE + 0x3f8;
break;
}
_loongson_uart_base = TO_UNCAC(loongson_uart_base);
setup_8250_early_printk_port(_loongson_uart_base, 0, 1024);
}
@@ -0,0 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for Lemote Fuloong2e mini-PC board.
#
obj-y += irq.o reset.o dma.o
@@ -0,0 +1,12 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/dma-direct.h>
dma_addr_t phys_to_dma(struct device *dev, phys_addr_t paddr)
{
return paddr | 0x80000000;
}
phys_addr_t dma_to_phys(struct device *dev, dma_addr_t dma_addr)
{
return dma_addr & 0x7fffffff;
}
@@ -0,0 +1,65 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2007 Lemote Inc. & Institute of Computing Technology
* Author: Fuxin Zhang, zhangfx@lemote.com
*/
#include <linux/interrupt.h>
#include <asm/irq_cpu.h>
#include <asm/i8259.h>
#include <loongson.h>
static void i8259_irqdispatch(void)
{
int irq;
irq = i8259_irq();
if (irq >= 0)
do_IRQ(irq);
else
spurious_interrupt();
}
asmlinkage void mach_irq_dispatch(unsigned int pending)
{
if (pending & CAUSEF_IP7)
do_IRQ(MIPS_CPU_IRQ_BASE + 7);
else if (pending & CAUSEF_IP6) /* perf counter loverflow */
return;
else if (pending & CAUSEF_IP5)
i8259_irqdispatch();
else if (pending & CAUSEF_IP2)
bonito_irqdispatch();
else
spurious_interrupt();
}
void __init mach_init_irq(void)
{
int irq;
/* init all controller
* 0-15 ------> i8259 interrupt
* 16-23 ------> mips cpu interrupt
* 32-63 ------> bonito irq
*/
/* most bonito irq should be level triggered */
LOONGSON_INTEDGE = LOONGSON_ICU_SYSTEMERR | LOONGSON_ICU_MASTERERR |
LOONGSON_ICU_RETRYERR | LOONGSON_ICU_MBOXES;
/* Sets the first-level interrupt dispatcher. */
mips_cpu_irq_init();
init_i8259_irqs();
bonito_irq_init();
/* bonito irq at IP2 */
irq = MIPS_CPU_IRQ_BASE + 2;
if (request_irq(irq, no_action, IRQF_NO_THREAD, "cascade", NULL))
pr_err("Failed to request irq %d (cascade)\n", irq);
/* 8259 irq at IP5 */
irq = MIPS_CPU_IRQ_BASE + 5;
if (request_irq(irq, no_action, IRQF_NO_THREAD, "cascade", NULL))
pr_err("Failed to request irq %d (cascade)\n", irq);
}
@@ -0,0 +1,19 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/* Board-specific reboot/shutdown routines
* Copyright (c) 2009 Philippe Vachon <philippe@cowpig.ca>
*
* Copyright (C) 2009 Lemote Inc.
* Author: Wu Zhangjin, wuzhangjin@gmail.com
*/
#include <loongson.h>
void mach_prepare_reboot(void)
{
LOONGSON_GENCFG &= ~(1 << 2);
LOONGSON_GENCFG |= (1 << 2);
}
void mach_prepare_shutdown(void)
{
}
@@ -0,0 +1,12 @@
# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for lemote loongson2f family machines
#
obj-y += clock.o machtype.o irq.o reset.o dma.o ec_kb3310b.o
#
# Suspend Support
#
obj-$(CONFIG_SUSPEND) += pm.o
@@ -0,0 +1,51 @@
/*
* Copyright (C) 2006 - 2008 Lemote Inc. & Institute of Computing Technology
* Author: Yanhua, yanh@lemote.com
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*/
#include <linux/cpufreq.h>
#include <linux/errno.h>
#include <linux/export.h>
#include <asm/mach-loongson2ef/loongson.h>
enum {
DC_ZERO, DC_25PT = 2, DC_37PT, DC_50PT, DC_62PT, DC_75PT,
DC_87PT, DC_DISABLE, DC_RESV
};
struct cpufreq_frequency_table loongson2_clockmod_table[] = {
{0, DC_RESV, CPUFREQ_ENTRY_INVALID},
{0, DC_ZERO, CPUFREQ_ENTRY_INVALID},
{0, DC_25PT, 0},
{0, DC_37PT, 0},
{0, DC_50PT, 0},
{0, DC_62PT, 0},
{0, DC_75PT, 0},
{0, DC_87PT, 0},
{0, DC_DISABLE, 0},
{0, DC_RESV, CPUFREQ_TABLE_END},
};
EXPORT_SYMBOL_GPL(loongson2_clockmod_table);
int loongson2_cpu_set_rate(unsigned long rate_khz)
{
struct cpufreq_frequency_table *pos;
int regval;
cpufreq_for_each_valid_entry(pos, loongson2_clockmod_table)
if (rate_khz == pos->frequency)
break;
if (rate_khz != pos->frequency)
return -ENOTSUPP;
regval = readl(LOONGSON_CHIPCFG);
regval = (regval & ~0x7) | (pos->driver_data - 1);
writel(regval, LOONGSON_CHIPCFG);
return 0;
}
EXPORT_SYMBOL_GPL(loongson2_cpu_set_rate);
@@ -0,0 +1,14 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/dma-direct.h>
dma_addr_t phys_to_dma(struct device *dev, phys_addr_t paddr)
{
return paddr | 0x80000000;
}
phys_addr_t dma_to_phys(struct device *dev, dma_addr_t dma_addr)
{
if (dma_addr > 0x8fffffff)
return dma_addr;
return dma_addr & 0x0fffffff;
}
@@ -0,0 +1,125 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Basic KB3310B Embedded Controller support for the YeeLoong 2F netbook
*
* Copyright (C) 2008 Lemote Inc.
* Author: liujl <liujl@lemote.com>, 2008-04-20
*/
#include <linux/io.h>
#include <linux/export.h>
#include <linux/spinlock.h>
#include <linux/delay.h>
#include "ec_kb3310b.h"
static DEFINE_SPINLOCK(index_access_lock);
static DEFINE_SPINLOCK(port_access_lock);
unsigned char ec_read(unsigned short addr)
{
unsigned char value;
unsigned long flags;
spin_lock_irqsave(&index_access_lock, flags);
outb((addr & 0xff00) >> 8, EC_IO_PORT_HIGH);
outb((addr & 0x00ff), EC_IO_PORT_LOW);
value = inb(EC_IO_PORT_DATA);
spin_unlock_irqrestore(&index_access_lock, flags);
return value;
}
EXPORT_SYMBOL_GPL(ec_read);
void ec_write(unsigned short addr, unsigned char val)
{
unsigned long flags;
spin_lock_irqsave(&index_access_lock, flags);
outb((addr & 0xff00) >> 8, EC_IO_PORT_HIGH);
outb((addr & 0x00ff), EC_IO_PORT_LOW);
outb(val, EC_IO_PORT_DATA);
/* flush the write action */
inb(EC_IO_PORT_DATA);
spin_unlock_irqrestore(&index_access_lock, flags);
}
EXPORT_SYMBOL_GPL(ec_write);
/*
* This function is used for EC command writes and corresponding status queries.
*/
int ec_query_seq(unsigned char cmd)
{
int timeout;
unsigned char status;
unsigned long flags;
int ret = 0;
spin_lock_irqsave(&port_access_lock, flags);
/* make chip goto reset mode */
udelay(EC_REG_DELAY);
outb(cmd, EC_CMD_PORT);
udelay(EC_REG_DELAY);
/* check if the command is received by ec */
timeout = EC_CMD_TIMEOUT;
status = inb(EC_STS_PORT);
while (timeout-- && (status & (1 << 1))) {
status = inb(EC_STS_PORT);
udelay(EC_REG_DELAY);
}
spin_unlock_irqrestore(&port_access_lock, flags);
if (timeout <= 0) {
printk(KERN_ERR "%s: deadable error : timeout...\n", __func__);
ret = -EINVAL;
} else
printk(KERN_INFO
"(%x/%d)ec issued command %d status : 0x%x\n",
timeout, EC_CMD_TIMEOUT - timeout, cmd, status);
return ret;
}
EXPORT_SYMBOL_GPL(ec_query_seq);
/*
* Send query command to EC to get the proper event number
*/
int ec_query_event_num(void)
{
return ec_query_seq(CMD_GET_EVENT_NUM);
}
EXPORT_SYMBOL(ec_query_event_num);
/*
* Get event number from EC
*
* NOTE: This routine must follow the query_event_num function in the
* interrupt.
*/
int ec_get_event_num(void)
{
int timeout = 100;
unsigned char value;
unsigned char status;
udelay(EC_REG_DELAY);
status = inb(EC_STS_PORT);
udelay(EC_REG_DELAY);
while (timeout-- && !(status & (1 << 0))) {
status = inb(EC_STS_PORT);
udelay(EC_REG_DELAY);
}
if (timeout <= 0) {
pr_info("%s: get event number timeout.\n", __func__);
return -EINVAL;
}
value = inb(EC_DAT_PORT);
udelay(EC_REG_DELAY);
return value;
}
EXPORT_SYMBOL(ec_get_event_num);
@@ -0,0 +1,184 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* KB3310B Embedded Controller
*
* Copyright (C) 2008 Lemote Inc.
* Author: liujl <liujl@lemote.com>, 2008-03-14
*/
#ifndef _EC_KB3310B_H
#define _EC_KB3310B_H
extern unsigned char ec_read(unsigned short addr);
extern void ec_write(unsigned short addr, unsigned char val);
extern int ec_query_seq(unsigned char cmd);
extern int ec_query_event_num(void);
extern int ec_get_event_num(void);
typedef int (*sci_handler) (int status);
extern sci_handler yeeloong_report_lid_status;
#define SCI_IRQ_NUM 0x0A
/*
* The following registers are determined by the EC index configuration.
* 1, fill the PORT_HIGH as EC register high part.
* 2, fill the PORT_LOW as EC register low part.
* 3, fill the PORT_DATA as EC register write data or get the data from it.
*/
#define EC_IO_PORT_HIGH 0x0381
#define EC_IO_PORT_LOW 0x0382
#define EC_IO_PORT_DATA 0x0383
/*
* EC delay time is 500us for register and status access
*/
#define EC_REG_DELAY 500 /* unit : us */
#define EC_CMD_TIMEOUT 0x1000
/*
* EC access port for SCI communication
*/
#define EC_CMD_PORT 0x66
#define EC_STS_PORT 0x66
#define EC_DAT_PORT 0x62
#define CMD_INIT_IDLE_MODE 0xdd
#define CMD_EXIT_IDLE_MODE 0xdf
#define CMD_INIT_RESET_MODE 0xd8
#define CMD_REBOOT_SYSTEM 0x8c
#define CMD_GET_EVENT_NUM 0x84
#define CMD_PROGRAM_PIECE 0xda
/* temperature & fan registers */
#define REG_TEMPERATURE_VALUE 0xF458
#define REG_FAN_AUTO_MAN_SWITCH 0xF459
#define BIT_FAN_AUTO 0
#define BIT_FAN_MANUAL 1
#define REG_FAN_CONTROL 0xF4D2
#define BIT_FAN_CONTROL_ON (1 << 0)
#define BIT_FAN_CONTROL_OFF (0 << 0)
#define REG_FAN_STATUS 0xF4DA
#define BIT_FAN_STATUS_ON (1 << 0)
#define BIT_FAN_STATUS_OFF (0 << 0)
#define REG_FAN_SPEED_HIGH 0xFE22
#define REG_FAN_SPEED_LOW 0xFE23
#define REG_FAN_SPEED_LEVEL 0xF4CC
/* fan speed divider */
#define FAN_SPEED_DIVIDER 480000 /* (60*1000*1000/62.5/2)*/
/* battery registers */
#define REG_BAT_DESIGN_CAP_HIGH 0xF77D
#define REG_BAT_DESIGN_CAP_LOW 0xF77E
#define REG_BAT_FULLCHG_CAP_HIGH 0xF780
#define REG_BAT_FULLCHG_CAP_LOW 0xF781
#define REG_BAT_DESIGN_VOL_HIGH 0xF782
#define REG_BAT_DESIGN_VOL_LOW 0xF783
#define REG_BAT_CURRENT_HIGH 0xF784
#define REG_BAT_CURRENT_LOW 0xF785
#define REG_BAT_VOLTAGE_HIGH 0xF786
#define REG_BAT_VOLTAGE_LOW 0xF787
#define REG_BAT_TEMPERATURE_HIGH 0xF788
#define REG_BAT_TEMPERATURE_LOW 0xF789
#define REG_BAT_RELATIVE_CAP_HIGH 0xF492
#define REG_BAT_RELATIVE_CAP_LOW 0xF493
#define REG_BAT_VENDOR 0xF4C4
#define FLAG_BAT_VENDOR_SANYO 0x01
#define FLAG_BAT_VENDOR_SIMPLO 0x02
#define REG_BAT_CELL_COUNT 0xF4C6
#define FLAG_BAT_CELL_3S1P 0x03
#define FLAG_BAT_CELL_3S2P 0x06
#define REG_BAT_CHARGE 0xF4A2
#define FLAG_BAT_CHARGE_DISCHARGE 0x01
#define FLAG_BAT_CHARGE_CHARGE 0x02
#define FLAG_BAT_CHARGE_ACPOWER 0x00
#define REG_BAT_STATUS 0xF4B0
#define BIT_BAT_STATUS_LOW (1 << 5)
#define BIT_BAT_STATUS_DESTROY (1 << 2)
#define BIT_BAT_STATUS_FULL (1 << 1)
#define BIT_BAT_STATUS_IN (1 << 0)
#define REG_BAT_CHARGE_STATUS 0xF4B1
#define BIT_BAT_CHARGE_STATUS_OVERTEMP (1 << 2)
#define BIT_BAT_CHARGE_STATUS_PRECHG (1 << 1)
#define REG_BAT_STATE 0xF482
#define BIT_BAT_STATE_CHARGING (1 << 1)
#define BIT_BAT_STATE_DISCHARGING (1 << 0)
#define REG_BAT_POWER 0xF440
#define BIT_BAT_POWER_S3 (1 << 2)
#define BIT_BAT_POWER_ON (1 << 1)
#define BIT_BAT_POWER_ACIN (1 << 0)
/* other registers */
/* Audio: rd/wr */
#define REG_AUDIO_VOLUME 0xF46C
#define REG_AUDIO_MUTE 0xF4E7
#define REG_AUDIO_BEEP 0xF4D0
/* USB port power or not: rd/wr */
#define REG_USB0_FLAG 0xF461
#define REG_USB1_FLAG 0xF462
#define REG_USB2_FLAG 0xF463
#define BIT_USB_FLAG_ON 1
#define BIT_USB_FLAG_OFF 0
/* LID */
#define REG_LID_DETECT 0xF4BD
#define BIT_LID_DETECT_ON 1
#define BIT_LID_DETECT_OFF 0
/* CRT */
#define REG_CRT_DETECT 0xF4AD
#define BIT_CRT_DETECT_PLUG 1
#define BIT_CRT_DETECT_UNPLUG 0
/* LCD backlight brightness adjust: 9 levels */
#define REG_DISPLAY_BRIGHTNESS 0xF4F5
/* Black screen Status */
#define BIT_DISPLAY_LCD_ON 1
#define BIT_DISPLAY_LCD_OFF 0
/* LCD backlight control: off/restore */
#define REG_BACKLIGHT_CTRL 0xF7BD
#define BIT_BACKLIGHT_ON 1
#define BIT_BACKLIGHT_OFF 0
/* Reset the machine auto-clear: rd/wr */
#define REG_RESET 0xF4EC
#define BIT_RESET_ON 1
/* Light the led: rd/wr */
#define REG_LED 0xF4C8
#define BIT_LED_RED_POWER (1 << 0)
#define BIT_LED_ORANGE_POWER (1 << 1)
#define BIT_LED_GREEN_CHARGE (1 << 2)
#define BIT_LED_RED_CHARGE (1 << 3)
#define BIT_LED_NUMLOCK (1 << 4)
/* Test led mode, all led on/off */
#define REG_LED_TEST 0xF4C2
#define BIT_LED_TEST_IN 1
#define BIT_LED_TEST_OUT 0
/* Camera on/off */
#define REG_CAMERA_STATUS 0xF46A
#define BIT_CAMERA_STATUS_ON 1
#define BIT_CAMERA_STATUS_OFF 0
#define REG_CAMERA_CONTROL 0xF7B7
#define BIT_CAMERA_CONTROL_OFF 0
#define BIT_CAMERA_CONTROL_ON 1
/* Wlan Status */
#define REG_WLAN 0xF4FA
#define BIT_WLAN_ON 1
#define BIT_WLAN_OFF 0
#define REG_DISPLAY_LCD 0xF79F
/* SCI Event Number from EC */
enum {
EVENT_LID = 0x23, /* LID open/close */
EVENT_DISPLAY_TOGGLE, /* Fn+F3 for display switch */
EVENT_SLEEP, /* Fn+F1 for entering sleep mode */
EVENT_OVERTEMP, /* Over-temperature happened */
EVENT_CRT_DETECT, /* CRT is connected */
EVENT_CAMERA, /* Camera on/off */
EVENT_USB_OC2, /* USB2 Over Current occurred */
EVENT_USB_OC0, /* USB0 Over Current occurred */
EVENT_BLACK_SCREEN, /* Turn on/off backlight */
EVENT_AUDIO_MUTE, /* Mute on/off */
EVENT_DISPLAY_BRIGHTNESS,/* LCD backlight brightness adjust */
EVENT_AC_BAT, /* AC & Battery relative issue */
EVENT_AUDIO_VOLUME, /* Volume adjust */
EVENT_WLAN, /* Wlan on/off */
EVENT_END
};
#endif /* !_EC_KB3310B_H */
@@ -0,0 +1,117 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2007 Lemote Inc.
* Author: Fuxin Zhang, zhangfx@lemote.com
*/
#include <linux/export.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <asm/irq_cpu.h>
#include <asm/i8259.h>
#include <asm/mipsregs.h>
#include <loongson.h>
#include <machine.h>
#define LOONGSON_TIMER_IRQ (MIPS_CPU_IRQ_BASE + 7) /* cpu timer */
#define LOONGSON_NORTH_BRIDGE_IRQ (MIPS_CPU_IRQ_BASE + 6) /* bonito */
#define LOONGSON_UART_IRQ (MIPS_CPU_IRQ_BASE + 3) /* cpu serial port */
#define LOONGSON_SOUTH_BRIDGE_IRQ (MIPS_CPU_IRQ_BASE + 2) /* i8259 */
#define LOONGSON_INT_BIT_INT0 (1 << 11)
#define LOONGSON_INT_BIT_INT1 (1 << 12)
/*
* The generic i8259_irq() make the kernel hang on booting. Since we cannot
* get the irq via the IRR directly, we access the ISR instead.
*/
int mach_i8259_irq(void)
{
int irq, isr;
irq = -1;
if ((LOONGSON_INTISR & LOONGSON_INTEN) & LOONGSON_INT_BIT_INT0) {
raw_spin_lock(&i8259A_lock);
isr = inb(PIC_MASTER_CMD) &
~inb(PIC_MASTER_IMR) & ~(1 << PIC_CASCADE_IR);
if (!isr)
isr = (inb(PIC_SLAVE_CMD) & ~inb(PIC_SLAVE_IMR)) << 8;
irq = ffs(isr) - 1;
if (unlikely(irq == 7)) {
/*
* This may be a spurious interrupt.
*
* Read the interrupt status register (ISR). If the most
* significant bit is not set then there is no valid
* interrupt.
*/
outb(0x0B, PIC_MASTER_ISR); /* ISR register */
if (~inb(PIC_MASTER_ISR) & 0x80)
irq = -1;
}
raw_spin_unlock(&i8259A_lock);
}
return irq;
}
EXPORT_SYMBOL(mach_i8259_irq);
static void i8259_irqdispatch(void)
{
int irq;
irq = mach_i8259_irq();
if (irq >= 0)
do_IRQ(irq);
else
spurious_interrupt();
}
void mach_irq_dispatch(unsigned int pending)
{
if (pending & CAUSEF_IP7)
do_IRQ(LOONGSON_TIMER_IRQ);
else if (pending & CAUSEF_IP6) { /* North Bridge, Perf counter */
bonito_irqdispatch();
} else if (pending & CAUSEF_IP3) /* CPU UART */
do_IRQ(LOONGSON_UART_IRQ);
else if (pending & CAUSEF_IP2) /* South Bridge */
i8259_irqdispatch();
else
spurious_interrupt();
}
static irqreturn_t ip6_action(int cpl, void *dev_id)
{
return IRQ_HANDLED;
}
void __init mach_init_irq(void)
{
/* init all controller
* 0-15 ------> i8259 interrupt
* 16-23 ------> mips cpu interrupt
* 32-63 ------> bonito irq
*/
/* setup cs5536 as high level trigger */
LOONGSON_INTPOL = LOONGSON_INT_BIT_INT0 | LOONGSON_INT_BIT_INT1;
LOONGSON_INTEDGE &= ~(LOONGSON_INT_BIT_INT0 | LOONGSON_INT_BIT_INT1);
/* Sets the first-level interrupt dispatcher. */
mips_cpu_irq_init();
init_i8259_irqs();
bonito_irq_init();
/* setup north bridge irq (bonito) */
if (request_irq(LOONGSON_NORTH_BRIDGE_IRQ, ip6_action,
IRQF_SHARED | IRQF_NO_THREAD, "cascade", ip6_action))
pr_err("Failed to register north bridge cascade interrupt\n");
/* setup source bridge irq (i8259) */
if (request_irq(LOONGSON_SOUTH_BRIDGE_IRQ, no_action,
IRQF_NO_THREAD | IRQF_NO_SUSPEND, "cascade", NULL))
pr_err("Failed to register south bridge cascade interrupt\n");
}
@@ -0,0 +1,41 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2009 Lemote Inc.
* Author: Wu Zhangjin, wuzhangjin@gmail.com
*/
#include <asm/bootinfo.h>
#include <loongson.h>
void __init mach_prom_init_machtype(void)
{
/* We share the same kernel image file among Lemote 2F family
* of machines, and provide the machtype= kernel command line
* to users to indicate their machine, this command line will
* be passed by the latest PMON automatically. and fortunately,
* up to now, we can get the machine type from the PMON_VER=
* commandline directly except the NAS machine, In the old
* machines, this will help the users a lot.
*
* If no "machtype=" passed, get machine type from "PMON_VER=".
* PMON_VER=LM8089 Lemote 8.9'' netbook
* LM8101 Lemote 10.1'' netbook
* (The above two netbooks have the same kernel support)
* LM6XXX Lemote FuLoong(2F) box series
* LM9XXX Lemote LynLoong PC series
*/
if (strstr(arcs_cmdline, "PMON_VER=LM")) {
if (strstr(arcs_cmdline, "PMON_VER=LM8"))
mips_machtype = MACH_LEMOTE_YL2F89;
else if (strstr(arcs_cmdline, "PMON_VER=LM6"))
mips_machtype = MACH_LEMOTE_FL2F;
else if (strstr(arcs_cmdline, "PMON_VER=LM9"))
mips_machtype = MACH_LEMOTE_LL2F;
else
mips_machtype = MACH_LEMOTE_NAS;
strcat(arcs_cmdline, " machtype=");
strcat(arcs_cmdline, get_system_type());
strcat(arcs_cmdline, " ");
}
}
@@ -0,0 +1,145 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Lemote loongson2f family machines' specific suspend support
*
* Copyright (C) 2009 Lemote Inc.
* Author: Wu Zhangjin <wuzhangjin@gmail.com>
*/
#include <linux/suspend.h>
#include <linux/interrupt.h>
#include <linux/pm.h>
#include <linux/i8042.h>
#include <linux/export.h>
#include <asm/i8259.h>
#include <asm/mipsregs.h>
#include <asm/bootinfo.h>
#include <loongson.h>
#include <cs5536/cs5536_mfgpt.h>
#include "ec_kb3310b.h"
#define I8042_KBD_IRQ 1
#define I8042_CTR_KBDINT 0x01
#define I8042_CTR_KBDDIS 0x10
static unsigned char i8042_ctr;
static int i8042_enable_kbd_port(void)
{
if (i8042_command(&i8042_ctr, I8042_CMD_CTL_RCTR)) {
pr_err("i8042.c: Can't read CTR while enabling i8042 kbd port."
"\n");
return -EIO;
}
i8042_ctr &= ~I8042_CTR_KBDDIS;
i8042_ctr |= I8042_CTR_KBDINT;
if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
i8042_ctr &= ~I8042_CTR_KBDINT;
i8042_ctr |= I8042_CTR_KBDDIS;
pr_err("i8042.c: Failed to enable KBD port.\n");
return -EIO;
}
return 0;
}
void setup_wakeup_events(void)
{
int irq_mask;
switch (mips_machtype) {
case MACH_LEMOTE_ML2F7:
case MACH_LEMOTE_YL2F89:
/* open the keyboard irq in i8259A */
outb((0xff & ~(1 << I8042_KBD_IRQ)), PIC_MASTER_IMR);
irq_mask = inb(PIC_MASTER_IMR);
/* enable keyboard port */
i8042_enable_kbd_port();
/* Wakeup CPU via SCI lid open event */
outb(irq_mask & ~(1 << PIC_CASCADE_IR), PIC_MASTER_IMR);
inb(PIC_MASTER_IMR);
outb(0xff & ~(1 << (SCI_IRQ_NUM - 8)), PIC_SLAVE_IMR);
inb(PIC_SLAVE_IMR);
break;
default:
break;
}
}
static struct delayed_work lid_task;
static int initialized;
/* yeeloong_report_lid_status will be implemented in yeeloong_laptop.c */
sci_handler yeeloong_report_lid_status;
EXPORT_SYMBOL(yeeloong_report_lid_status);
static void yeeloong_lid_update_task(struct work_struct *work)
{
if (yeeloong_report_lid_status)
yeeloong_report_lid_status(BIT_LID_DETECT_ON);
}
int wakeup_loongson(void)
{
int irq;
/* query the interrupt number */
irq = mach_i8259_irq();
if (irq < 0)
return 0;
printk(KERN_INFO "%s: irq = %d\n", __func__, irq);
if (irq == I8042_KBD_IRQ)
return 1;
else if (irq == SCI_IRQ_NUM) {
int ret, sci_event;
/* query the event number */
ret = ec_query_seq(CMD_GET_EVENT_NUM);
if (ret < 0)
return 0;
sci_event = ec_get_event_num();
if (sci_event < 0)
return 0;
if (sci_event == EVENT_LID) {
int lid_status;
/* check the LID status */
lid_status = ec_read(REG_LID_DETECT);
/* wakeup cpu when people open the LID */
if (lid_status == BIT_LID_DETECT_ON) {
/* If we call it directly here, the WARNING
* will be sent out by getnstimeofday
* via "WARN_ON(timekeeping_suspended);"
* because we can not schedule in suspend mode.
*/
if (initialized == 0) {
INIT_DELAYED_WORK(&lid_task,
yeeloong_lid_update_task);
initialized = 1;
}
schedule_delayed_work(&lid_task, 1);
return 1;
}
}
}
return 0;
}
void __weak mach_suspend(void)
{
disable_mfgpt0_counter();
}
void __weak mach_resume(void)
{
enable_mfgpt0_counter();
}
@@ -0,0 +1,155 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/* Board-specific reboot/shutdown routines
*
* Copyright (c) 2009 Philippe Vachon <philippe@cowpig.ca>
*
* Copyright (C) 2009 Lemote Inc.
* Author: Wu Zhangjin, wuzhangjin@gmail.com
*/
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/types.h>
#include <asm/bootinfo.h>
#include <loongson.h>
#include <cs5536/cs5536.h>
#include "ec_kb3310b.h"
static void reset_cpu(void)
{
/*
* reset cpu to full speed, this is needed when enabling cpu frequency
* scalling
*/
writel(readl(LOONGSON_CHIPCFG) | 0x7, LOONGSON_CHIPCFG);
}
/* reset support for fuloong2f */
static void fl2f_reboot(void)
{
reset_cpu();
/* send a reset signal to south bridge.
*
* NOTE: if enable "Power Management" in kernel, rtl8169 will not reset
* normally with this reset operation and it will not work in PMON, but
* you can type halt command and then reboot, seems the hardware reset
* logic not work normally.
*/
{
u32 hi, lo;
_rdmsr(DIVIL_MSR_REG(DIVIL_SOFT_RESET), &hi, &lo);
lo |= 0x00000001;
_wrmsr(DIVIL_MSR_REG(DIVIL_SOFT_RESET), hi, lo);
}
}
static void fl2f_shutdown(void)
{
u32 hi, lo, val;
int gpio_base;
/* get gpio base */
_rdmsr(DIVIL_MSR_REG(DIVIL_LBAR_GPIO), &hi, &lo);
gpio_base = lo & 0xff00;
/* make cs5536 gpio13 output enable */
val = inl(gpio_base + GPIOL_OUT_EN);
val &= ~(1 << (16 + 13));
val |= (1 << 13);
outl(val, gpio_base + GPIOL_OUT_EN);
mmiowb();
/* make cs5536 gpio13 output low level voltage. */
val = inl(gpio_base + GPIOL_OUT_VAL) & ~(1 << (13));
val |= (1 << (16 + 13));
outl(val, gpio_base + GPIOL_OUT_VAL);
mmiowb();
}
/* reset support for yeeloong2f and mengloong2f notebook */
static void ml2f_reboot(void)
{
reset_cpu();
/* sending an reset signal to EC(embedded controller) */
ec_write(REG_RESET, BIT_RESET_ON);
}
#define yl2f89_reboot ml2f_reboot
/* menglong(7inches) laptop has different shutdown logic from 8.9inches */
#define EC_SHUTDOWN_IO_PORT_HIGH 0xff2d
#define EC_SHUTDOWN_IO_PORT_LOW 0xff2e
#define EC_SHUTDOWN_IO_PORT_DATA 0xff2f
#define REG_SHUTDOWN_HIGH 0xFC
#define REG_SHUTDOWN_LOW 0x29
#define BIT_SHUTDOWN_ON (1 << 1)
static void ml2f_shutdown(void)
{
u8 val;
u64 i;
outb(REG_SHUTDOWN_HIGH, EC_SHUTDOWN_IO_PORT_HIGH);
outb(REG_SHUTDOWN_LOW, EC_SHUTDOWN_IO_PORT_LOW);
mmiowb();
val = inb(EC_SHUTDOWN_IO_PORT_DATA);
outb(val & (~BIT_SHUTDOWN_ON), EC_SHUTDOWN_IO_PORT_DATA);
mmiowb();
/* need enough wait here... how many microseconds needs? */
for (i = 0; i < 0x10000; i++)
delay();
outb(val | BIT_SHUTDOWN_ON, EC_SHUTDOWN_IO_PORT_DATA);
mmiowb();
}
static void yl2f89_shutdown(void)
{
/* cpu-gpio0 output low */
LOONGSON_GPIODATA &= ~0x00000001;
/* cpu-gpio0 as output */
LOONGSON_GPIOIE &= ~0x00000001;
}
void mach_prepare_reboot(void)
{
switch (mips_machtype) {
case MACH_LEMOTE_FL2F:
case MACH_LEMOTE_NAS:
case MACH_LEMOTE_LL2F:
fl2f_reboot();
break;
case MACH_LEMOTE_ML2F7:
ml2f_reboot();
break;
case MACH_LEMOTE_YL2F89:
yl2f89_reboot();
break;
default:
break;
}
}
void mach_prepare_shutdown(void)
{
switch (mips_machtype) {
case MACH_LEMOTE_FL2F:
case MACH_LEMOTE_NAS:
case MACH_LEMOTE_LL2F:
fl2f_shutdown();
break;
case MACH_LEMOTE_ML2F7:
ml2f_shutdown();
break;
case MACH_LEMOTE_YL2F89:
yl2f89_shutdown();
break;
default:
break;
}
}