Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1032bce3 authored by Wu Zhangjin's avatar Wu Zhangjin Committed by Ralf Baechle
Browse files

MIPS: Lemote 2F: Add PCI support



PCI support for the Fuloong 2E and Lemote Loongson 2F family machines is
mostly identical with the exception of CS5536 support.

Rename ops-fuloong2e.c to ops-loongson2.c then add the CS5536 support to
share most of the source code among Loongson machines.

Signed-off-by: default avatarWu Zhangjin <wuzhangjin@gmail.com>
Cc: zhangfx@lemote.com
Cc: yanh@lemote.com 
Cc: huhb@lemote.com
Cc: Nicholas Mc Guire <hofrat@hofr.at>
Cc: Arnaud Patard <apatard@mandriva.com>
Cc: loongson-dev@googlegroups.com
Cc: linux-mips@linux-mips.org
Signed-off-by: default avatarRalf Baechle <ralf@linux-mips.org>
parent 22c21003
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -28,7 +28,8 @@ obj-$(CONFIG_MIPS_COBALT) += fixup-cobalt.o
obj-$(CONFIG_SOC_AU1500)	+= fixup-au1000.o ops-au1000.o
obj-$(CONFIG_SOC_AU1550)	+= fixup-au1000.o ops-au1000.o
obj-$(CONFIG_SOC_PNX8550)	+= fixup-pnx8550.o ops-pnx8550.o
obj-$(CONFIG_LEMOTE_FULOONG2E)	+= fixup-fuloong2e.o ops-fuloong2e.o
obj-$(CONFIG_LEMOTE_FULOONG2E)	+= fixup-fuloong2e.o ops-loongson2.o
obj-$(CONFIG_LEMOTE_MACH2F)	+= fixup-lemote2f.o ops-loongson2.o
obj-$(CONFIG_MIPS_MALTA)	+= fixup-malta.o
obj-$(CONFIG_PMC_MSP7120_GW)	+= fixup-pmcmsp.o ops-pmcmsp.o
obj-$(CONFIG_PMC_MSP7120_EVAL)	+= fixup-pmcmsp.o ops-pmcmsp.o
+160 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2008 Lemote Technology
 * Copyright (C) 2004 ICT CAS
 * Author: Li xiaoyu, lixy@ict.ac.cn
 *
 * Copyright (C) 2007 Lemote, Inc.
 * Author: Fuxin Zhang, zhangfx@lemote.com
 *
 *  This program is free software; you can redistribute  it and/or modify it
 *  under  the terms of  the GNU General  Public License as published by the
 *  Free Software Foundation;  either version 2 of the  License, or (at your
 *  option) any later version.
 */
#include <linux/init.h>
#include <linux/pci.h>

#include <loongson.h>
#include <cs5536/cs5536.h>
#include <cs5536/cs5536_pci.h>

/* PCI interrupt pins
 *
 * These should not be changed, or you should consider loongson2f interrupt
 * register and your pci card dispatch
 */

#define PCIA		4
#define PCIB		5
#define PCIC		6
#define PCID		7

/* all the pci device has the PCIA pin, check the datasheet. */
static char irq_tab[][5] __initdata = {
	/*      INTA    INTB    INTC    INTD */
	{0, 0, 0, 0, 0},	/*  11: Unused */
	{0, 0, 0, 0, 0},	/*  12: Unused */
	{0, 0, 0, 0, 0},	/*  13: Unused */
	{0, 0, 0, 0, 0},	/*  14: Unused */
	{0, 0, 0, 0, 0},	/*  15: Unused */
	{0, 0, 0, 0, 0},	/*  16: Unused */
	{0, PCIA, 0, 0, 0},	/*  17: RTL8110-0 */
	{0, PCIB, 0, 0, 0},	/*  18: RTL8110-1 */
	{0, PCIC, 0, 0, 0},	/*  19: SiI3114 */
	{0, PCID, 0, 0, 0},	/*  20: 3-ports nec usb */
	{0, PCIA, PCIB, PCIC, PCID},	/*  21: PCI-SLOT */
	{0, 0, 0, 0, 0},	/*  22: Unused */
	{0, 0, 0, 0, 0},	/*  23: Unused */
	{0, 0, 0, 0, 0},	/*  24: Unused */
	{0, 0, 0, 0, 0},	/*  25: Unused */
	{0, 0, 0, 0, 0},	/*  26: Unused */
	{0, 0, 0, 0, 0},	/*  27: Unused */
};

int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
{
	int virq;

	if ((PCI_SLOT(dev->devfn) != PCI_IDSEL_CS5536)
	    && (PCI_SLOT(dev->devfn) < 32)) {
		virq = irq_tab[slot][pin];
		printk(KERN_INFO "slot: %d, pin: %d, irq: %d\n", slot, pin,
		       virq + LOONGSON_IRQ_BASE);
		if (virq != 0)
			return LOONGSON_IRQ_BASE + virq;
		else
			return 0;
	} else if (PCI_SLOT(dev->devfn) == PCI_IDSEL_CS5536) {	/*  cs5536 */
		switch (PCI_FUNC(dev->devfn)) {
		case 2:
			pci_write_config_byte(dev, PCI_INTERRUPT_LINE,
					      CS5536_IDE_INTR);
			return CS5536_IDE_INTR;	/*  for IDE */
		case 3:
			pci_write_config_byte(dev, PCI_INTERRUPT_LINE,
					      CS5536_ACC_INTR);
			return CS5536_ACC_INTR;	/*  for AUDIO */
		case 4:	/*  for OHCI */
		case 5:	/*  for EHCI */
		case 6:	/*  for UDC */
		case 7:	/*  for OTG */
			pci_write_config_byte(dev, PCI_INTERRUPT_LINE,
					      CS5536_USB_INTR);
			return CS5536_USB_INTR;
		}
		return dev->irq;
	} else {
		printk(KERN_INFO " strange pci slot number.\n");
		return 0;
	}
}

/* Do platform specific device initialization at pci_enable_device() time */
int pcibios_plat_dev_init(struct pci_dev *dev)
{
	return 0;
}

/* CS5536 SPEC. fixup */
static void __init loongson_cs5536_isa_fixup(struct pci_dev *pdev)
{
	/* the uart1 and uart2 interrupt in PIC is enabled as default */
	pci_write_config_dword(pdev, PCI_UART1_INT_REG, 1);
	pci_write_config_dword(pdev, PCI_UART2_INT_REG, 1);
}

static void __init loongson_cs5536_ide_fixup(struct pci_dev *pdev)
{
	/* setting the mutex pin as IDE function */
	pci_write_config_dword(pdev, PCI_IDE_CFG_REG,
			       CS5536_IDE_FLASH_SIGNATURE);
}

static void __init loongson_cs5536_acc_fixup(struct pci_dev *pdev)
{
	/* enable the AUDIO interrupt in PIC  */
	pci_write_config_dword(pdev, PCI_ACC_INT_REG, 1);

	pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0xc0);
}

static void __init loongson_cs5536_ohci_fixup(struct pci_dev *pdev)
{
	/* enable the OHCI interrupt in PIC */
	/* THE OHCI, EHCI, UDC, OTG are shared with interrupt in PIC */
	pci_write_config_dword(pdev, PCI_OHCI_INT_REG, 1);
}

static void __init loongson_cs5536_ehci_fixup(struct pci_dev *pdev)
{
	u32 hi, lo;

	/* Serial short detect enable */
	_rdmsr(USB_MSR_REG(USB_CONFIG), &hi, &lo);
	_wrmsr(USB_MSR_REG(USB_CONFIG), (1 << 1) | (1 << 2) | (1 << 3), lo);

	/* setting the USB2.0 micro frame length */
	pci_write_config_dword(pdev, PCI_EHCI_FLADJ_REG, 0x2000);
}

static void __init loongson_nec_fixup(struct pci_dev *pdev)
{
	unsigned int val;

	pci_read_config_dword(pdev, 0xe0, &val);
	/* Only 2 port be used */
	pci_write_config_dword(pdev, 0xe0, (val & ~3) | 0x2);
}

DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA,
			 loongson_cs5536_isa_fixup);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_OHC,
			 loongson_cs5536_ohci_fixup);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_EHC,
			 loongson_cs5536_ehci_fixup);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_AUDIO,
			 loongson_cs5536_acc_fixup);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_IDE,
			 loongson_cs5536_ide_fixup);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_NEC_USB,
			 loongson_nec_fixup);
+54 −0
Original line number Diff line number Diff line
@@ -20,6 +20,11 @@

#include <loongson.h>

#ifdef CONFIG_CS5536
#include <cs5536/cs5536_pci.h>
#include <cs5536/cs5536.h>
#endif

#define PCI_ACCESS_READ  0
#define PCI_ACCESS_WRITE 1

@@ -43,6 +48,29 @@ static int loongson_pcibios_config_access(unsigned char access_type,
	int reg = where & ~3;

	if (busnum == 0) {
		/* board-specific part,currently,only fuloong2f,yeeloong2f
		 * use CS5536, fuloong2e use via686b, gdium has no
		 * south bridge
		 */
#ifdef CONFIG_CS5536
		/* cs5536_pci_conf_read4/write4() will call _rdmsr/_wrmsr() to
		 * access the regsters PCI_MSR_ADDR, PCI_MSR_DATA_LO,
		 * PCI_MSR_DATA_HI, which is bigger than PCI_MSR_CTRL, so, it
		 * will not go this branch, but the others. so, no calling dead
		 * loop here.
		 */
		if ((PCI_IDSEL_CS5536 == device) && (reg < PCI_MSR_CTRL)) {
			switch (access_type) {
			case PCI_ACCESS_READ:
				*data = cs5536_pci_conf_read4(function, reg);
				break;
			case PCI_ACCESS_WRITE:
				cs5536_pci_conf_write4(function, reg, *data);
				break;
			}
			return 0;
		}
#endif
		/* Type 0 configuration for onboard PCI bus */
		if (device > MAX_DEV_NUM)
			return -1;
@@ -152,3 +180,29 @@ struct pci_ops loongson_pci_ops = {
	.read = loongson_pcibios_read,
	.write = loongson_pcibios_write
};

#ifdef CONFIG_CS5536
void _rdmsr(u32 msr, u32 *hi, u32 *lo)
{
	struct pci_bus bus = {
		.number = PCI_BUS_CS5536
	};
	u32 devfn = PCI_DEVFN(PCI_IDSEL_CS5536, 0);
	loongson_pcibios_write(&bus, devfn, PCI_MSR_ADDR, 4, msr);
	loongson_pcibios_read(&bus, devfn, PCI_MSR_DATA_LO, 4, lo);
	loongson_pcibios_read(&bus, devfn, PCI_MSR_DATA_HI, 4, hi);
}
EXPORT_SYMBOL(_rdmsr);

void _wrmsr(u32 msr, u32 hi, u32 lo)
{
	struct pci_bus bus = {
		.number = PCI_BUS_CS5536
	};
	u32 devfn = PCI_DEVFN(PCI_IDSEL_CS5536, 0);
	loongson_pcibios_write(&bus, devfn, PCI_MSR_ADDR, 4, msr);
	loongson_pcibios_write(&bus, devfn, PCI_MSR_DATA_LO, 4, lo);
	loongson_pcibios_write(&bus, devfn, PCI_MSR_DATA_HI, 4, hi);
}
EXPORT_SYMBOL(_wrmsr);
#endif