1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright (C) 2008 Lemote Technology |
4 | * Copyright (C) 2004 ICT CAS |
5 | * Author: Li xiaoyu, lixy@ict.ac.cn |
6 | * |
7 | * Copyright (C) 2007 Lemote, Inc. |
8 | * Author: Fuxin Zhang, zhangfx@lemote.com |
9 | */ |
10 | #include <linux/init.h> |
11 | #include <linux/pci.h> |
12 | |
13 | #include <loongson.h> |
14 | #include <cs5536/cs5536.h> |
15 | #include <cs5536/cs5536_pci.h> |
16 | |
17 | /* PCI interrupt pins |
18 | * |
19 | * These should not be changed, or you should consider loongson2f interrupt |
20 | * register and your pci card dispatch |
21 | */ |
22 | |
23 | #define PCIA 4 |
24 | #define PCIB 5 |
25 | #define PCIC 6 |
26 | #define PCID 7 |
27 | |
28 | /* all the pci device has the PCIA pin, check the datasheet. */ |
29 | static char irq_tab[][5] = { |
30 | /* INTA INTB INTC INTD */ |
31 | {0, 0, 0, 0, 0}, /* 11: Unused */ |
32 | {0, 0, 0, 0, 0}, /* 12: Unused */ |
33 | {0, 0, 0, 0, 0}, /* 13: Unused */ |
34 | {0, 0, 0, 0, 0}, /* 14: Unused */ |
35 | {0, 0, 0, 0, 0}, /* 15: Unused */ |
36 | {0, 0, 0, 0, 0}, /* 16: Unused */ |
37 | {0, PCIA, 0, 0, 0}, /* 17: RTL8110-0 */ |
38 | {0, PCIB, 0, 0, 0}, /* 18: RTL8110-1 */ |
39 | {0, PCIC, 0, 0, 0}, /* 19: SiI3114 */ |
40 | {0, PCID, 0, 0, 0}, /* 20: 3-ports nec usb */ |
41 | {0, PCIA, PCIB, PCIC, PCID}, /* 21: PCI-SLOT */ |
42 | {0, 0, 0, 0, 0}, /* 22: Unused */ |
43 | {0, 0, 0, 0, 0}, /* 23: Unused */ |
44 | {0, 0, 0, 0, 0}, /* 24: Unused */ |
45 | {0, 0, 0, 0, 0}, /* 25: Unused */ |
46 | {0, 0, 0, 0, 0}, /* 26: Unused */ |
47 | {0, 0, 0, 0, 0}, /* 27: Unused */ |
48 | }; |
49 | |
50 | int pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) |
51 | { |
52 | int virq; |
53 | |
54 | if ((PCI_SLOT(dev->devfn) != PCI_IDSEL_CS5536) |
55 | && (PCI_SLOT(dev->devfn) < 32)) { |
56 | virq = irq_tab[slot][pin]; |
57 | printk(KERN_INFO "slot: %d, pin: %d, irq: %d\n" , slot, pin, |
58 | virq + LOONGSON_IRQ_BASE); |
59 | if (virq != 0) |
60 | return LOONGSON_IRQ_BASE + virq; |
61 | else |
62 | return 0; |
63 | } else if (PCI_SLOT(dev->devfn) == PCI_IDSEL_CS5536) { /* cs5536 */ |
64 | switch (PCI_FUNC(dev->devfn)) { |
65 | case 2: |
66 | pci_write_config_byte(dev, PCI_INTERRUPT_LINE, |
67 | val: CS5536_IDE_INTR); |
68 | return CS5536_IDE_INTR; /* for IDE */ |
69 | case 3: |
70 | pci_write_config_byte(dev, PCI_INTERRUPT_LINE, |
71 | val: CS5536_ACC_INTR); |
72 | return CS5536_ACC_INTR; /* for AUDIO */ |
73 | case 4: /* for OHCI */ |
74 | case 5: /* for EHCI */ |
75 | case 6: /* for UDC */ |
76 | case 7: /* for OTG */ |
77 | pci_write_config_byte(dev, PCI_INTERRUPT_LINE, |
78 | val: CS5536_USB_INTR); |
79 | return CS5536_USB_INTR; |
80 | } |
81 | return dev->irq; |
82 | } else { |
83 | printk(KERN_INFO "strange PCI slot number.\n" ); |
84 | return 0; |
85 | } |
86 | } |
87 | |
88 | /* Do platform specific device initialization at pci_enable_device() time */ |
89 | int pcibios_plat_dev_init(struct pci_dev *dev) |
90 | { |
91 | return 0; |
92 | } |
93 | |
94 | /* CS5536 SPEC. fixup */ |
95 | static void loongson_cs5536_isa_fixup(struct pci_dev *pdev) |
96 | { |
97 | /* the uart1 and uart2 interrupt in PIC is enabled as default */ |
98 | pci_write_config_dword(dev: pdev, where: PCI_UART1_INT_REG, val: 1); |
99 | pci_write_config_dword(dev: pdev, where: PCI_UART2_INT_REG, val: 1); |
100 | } |
101 | |
102 | static void loongson_cs5536_ide_fixup(struct pci_dev *pdev) |
103 | { |
104 | /* setting the mutex pin as IDE function */ |
105 | pci_write_config_dword(dev: pdev, where: PCI_IDE_CFG_REG, |
106 | val: CS5536_IDE_FLASH_SIGNATURE); |
107 | } |
108 | |
109 | static void loongson_cs5536_acc_fixup(struct pci_dev *pdev) |
110 | { |
111 | /* enable the AUDIO interrupt in PIC */ |
112 | pci_write_config_dword(dev: pdev, where: PCI_ACC_INT_REG, val: 1); |
113 | |
114 | pci_write_config_byte(dev: pdev, PCI_LATENCY_TIMER, val: 0xc0); |
115 | } |
116 | |
117 | static void loongson_cs5536_ohci_fixup(struct pci_dev *pdev) |
118 | { |
119 | /* enable the OHCI interrupt in PIC */ |
120 | /* THE OHCI, EHCI, UDC, OTG are shared with interrupt in PIC */ |
121 | pci_write_config_dword(dev: pdev, where: PCI_OHCI_INT_REG, val: 1); |
122 | } |
123 | |
124 | static void loongson_cs5536_ehci_fixup(struct pci_dev *pdev) |
125 | { |
126 | u32 hi, lo; |
127 | |
128 | /* Serial short detect enable */ |
129 | _rdmsr(USB_MSR_REG(USB_CONFIG), &hi, &lo); |
130 | _wrmsr(USB_MSR_REG(USB_CONFIG), (1 << 1) | (1 << 3), lo); |
131 | |
132 | /* setting the USB2.0 micro frame length */ |
133 | pci_write_config_dword(dev: pdev, where: PCI_EHCI_FLADJ_REG, val: 0x2000); |
134 | } |
135 | |
136 | static void loongson_nec_fixup(struct pci_dev *pdev) |
137 | { |
138 | unsigned int val; |
139 | |
140 | pci_read_config_dword(dev: pdev, where: 0xe0, val: &val); |
141 | /* Only 2 port be used */ |
142 | pci_write_config_dword(dev: pdev, where: 0xe0, val: (val & ~3) | 0x2); |
143 | } |
144 | |
145 | DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA, |
146 | loongson_cs5536_isa_fixup); |
147 | DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_OHC, |
148 | loongson_cs5536_ohci_fixup); |
149 | DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_EHC, |
150 | loongson_cs5536_ehci_fixup); |
151 | DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_AUDIO, |
152 | loongson_cs5536_acc_fixup); |
153 | DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_IDE, |
154 | loongson_cs5536_ide_fixup); |
155 | DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_NEC_USB, |
156 | loongson_nec_fixup); |
157 | |