1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) 1999, 2000, 2004, 2005 MIPS Technologies, Inc. |
4 | * All rights reserved. |
5 | * Authors: Carsten Langgaard <carstenl@mips.com> |
6 | * Maciej W. Rozycki <macro@mips.com> |
7 | * Copyright (C) 2005 Ralf Baechle (ralf@linux-mips.org) |
8 | * |
9 | * MIPS boards specific PCI support. |
10 | */ |
11 | #include <linux/types.h> |
12 | #include <linux/pci.h> |
13 | #include <linux/kernel.h> |
14 | |
15 | #include <asm/mips-boards/msc01_pci.h> |
16 | |
17 | #define PCI_ACCESS_READ 0 |
18 | #define PCI_ACCESS_WRITE 1 |
19 | |
20 | /* |
21 | * PCI configuration cycle AD bus definition |
22 | */ |
23 | /* Type 0 */ |
24 | #define PCI_CFG_TYPE0_REG_SHF 0 |
25 | #define PCI_CFG_TYPE0_FUNC_SHF 8 |
26 | |
27 | /* Type 1 */ |
28 | #define PCI_CFG_TYPE1_REG_SHF 0 |
29 | #define PCI_CFG_TYPE1_FUNC_SHF 8 |
30 | #define PCI_CFG_TYPE1_DEV_SHF 11 |
31 | #define PCI_CFG_TYPE1_BUS_SHF 16 |
32 | |
33 | static int msc_pcibios_config_access(unsigned char access_type, |
34 | struct pci_bus *bus, unsigned int devfn, int where, u32 * data) |
35 | { |
36 | unsigned char busnum = bus->number; |
37 | u32 intr; |
38 | |
39 | /* Clear status register bits. */ |
40 | MSC_WRITE(MSC01_PCI_INTSTAT, |
41 | (MSC01_PCI_INTCFG_MA_BIT | MSC01_PCI_INTCFG_TA_BIT)); |
42 | |
43 | MSC_WRITE(MSC01_PCI_CFGADDR, |
44 | ((busnum << MSC01_PCI_CFGADDR_BNUM_SHF) | |
45 | (PCI_SLOT(devfn) << MSC01_PCI_CFGADDR_DNUM_SHF) | |
46 | (PCI_FUNC(devfn) << MSC01_PCI_CFGADDR_FNUM_SHF) | |
47 | ((where / 4) << MSC01_PCI_CFGADDR_RNUM_SHF))); |
48 | |
49 | /* Perform access */ |
50 | if (access_type == PCI_ACCESS_WRITE) |
51 | MSC_WRITE(MSC01_PCI_CFGDATA, *data); |
52 | else |
53 | MSC_READ(MSC01_PCI_CFGDATA, *data); |
54 | |
55 | /* Detect Master/Target abort */ |
56 | MSC_READ(MSC01_PCI_INTSTAT, intr); |
57 | if (intr & (MSC01_PCI_INTCFG_MA_BIT | MSC01_PCI_INTCFG_TA_BIT)) { |
58 | /* Error occurred */ |
59 | |
60 | /* Clear bits */ |
61 | MSC_WRITE(MSC01_PCI_INTSTAT, |
62 | (MSC01_PCI_INTCFG_MA_BIT | MSC01_PCI_INTCFG_TA_BIT)); |
63 | |
64 | return -1; |
65 | } |
66 | |
67 | return 0; |
68 | } |
69 | |
70 | |
71 | /* |
72 | * We can't address 8 and 16 bit words directly. Instead we have to |
73 | * read/write a 32bit word and mask/modify the data we actually want. |
74 | */ |
75 | static int msc_pcibios_read(struct pci_bus *bus, unsigned int devfn, |
76 | int where, int size, u32 * val) |
77 | { |
78 | u32 data = 0; |
79 | |
80 | if ((size == 2) && (where & 1)) |
81 | return PCIBIOS_BAD_REGISTER_NUMBER; |
82 | else if ((size == 4) && (where & 3)) |
83 | return PCIBIOS_BAD_REGISTER_NUMBER; |
84 | |
85 | if (msc_pcibios_config_access(PCI_ACCESS_READ, bus, devfn, where, |
86 | data: &data)) |
87 | return -1; |
88 | |
89 | if (size == 1) |
90 | *val = (data >> ((where & 3) << 3)) & 0xff; |
91 | else if (size == 2) |
92 | *val = (data >> ((where & 3) << 3)) & 0xffff; |
93 | else |
94 | *val = data; |
95 | |
96 | return PCIBIOS_SUCCESSFUL; |
97 | } |
98 | |
99 | static int msc_pcibios_write(struct pci_bus *bus, unsigned int devfn, |
100 | int where, int size, u32 val) |
101 | { |
102 | u32 data = 0; |
103 | |
104 | if ((size == 2) && (where & 1)) |
105 | return PCIBIOS_BAD_REGISTER_NUMBER; |
106 | else if ((size == 4) && (where & 3)) |
107 | return PCIBIOS_BAD_REGISTER_NUMBER; |
108 | |
109 | if (size == 4) |
110 | data = val; |
111 | else { |
112 | if (msc_pcibios_config_access(PCI_ACCESS_READ, bus, devfn, |
113 | where, data: &data)) |
114 | return -1; |
115 | |
116 | if (size == 1) |
117 | data = (data & ~(0xff << ((where & 3) << 3))) | |
118 | (val << ((where & 3) << 3)); |
119 | else if (size == 2) |
120 | data = (data & ~(0xffff << ((where & 3) << 3))) | |
121 | (val << ((where & 3) << 3)); |
122 | } |
123 | |
124 | if (msc_pcibios_config_access(PCI_ACCESS_WRITE, bus, devfn, where, |
125 | data: &data)) |
126 | return -1; |
127 | |
128 | return PCIBIOS_SUCCESSFUL; |
129 | } |
130 | |
131 | struct pci_ops msc_pci_ops = { |
132 | .read = msc_pcibios_read, |
133 | .write = msc_pcibios_write |
134 | }; |
135 | |