Merge tag '9p-for-5.13-rc1' of git://github.com/martinetd/linux
[linux-2.6-microblaze.git] / arch / microblaze / pci / indirect_pci.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Support for indirect PCI bridges.
4  *
5  * Copyright (C) 1998 Gabriel Paubert.
6  */
7
8 #include <linux/kernel.h>
9 #include <linux/pci.h>
10 #include <linux/delay.h>
11 #include <linux/string.h>
12 #include <linux/init.h>
13
14 #include <linux/io.h>
15 #include <asm/pci-bridge.h>
16
17 static int
18 indirect_read_config(struct pci_bus *bus, unsigned int devfn, int offset,
19                      int len, u32 *val)
20 {
21         struct pci_controller *hose = pci_bus_to_host(bus);
22         volatile void __iomem *cfg_data;
23         u8 cfg_type = 0;
24         u32 bus_no, reg;
25
26         if (hose->indirect_type & INDIRECT_TYPE_NO_PCIE_LINK) {
27                 if (bus->number != hose->first_busno)
28                         return PCIBIOS_DEVICE_NOT_FOUND;
29                 if (devfn != 0)
30                         return PCIBIOS_DEVICE_NOT_FOUND;
31         }
32
33         if (hose->indirect_type & INDIRECT_TYPE_SET_CFG_TYPE)
34                 if (bus->number != hose->first_busno)
35                         cfg_type = 1;
36
37         bus_no = (bus->number == hose->first_busno) ?
38                         hose->self_busno : bus->number;
39
40         if (hose->indirect_type & INDIRECT_TYPE_EXT_REG)
41                 reg = ((offset & 0xf00) << 16) | (offset & 0xfc);
42         else
43                 reg = offset & 0xfc; /* Only 3 bits for function */
44
45         if (hose->indirect_type & INDIRECT_TYPE_BIG_ENDIAN)
46                 out_be32(hose->cfg_addr, (0x80000000 | (bus_no << 16) |
47                          (devfn << 8) | reg | cfg_type));
48         else
49                 out_le32(hose->cfg_addr, (0x80000000 | (bus_no << 16) |
50                          (devfn << 8) | reg | cfg_type));
51
52         /*
53          * Note: the caller has already checked that offset is
54          * suitably aligned and that len is 1, 2 or 4.
55          */
56         cfg_data = hose->cfg_data + (offset & 3); /* Only 3 bits for function */
57         switch (len) {
58         case 1:
59                 *val = in_8(cfg_data);
60                 break;
61         case 2:
62                 *val = in_le16(cfg_data);
63                 break;
64         default:
65                 *val = in_le32(cfg_data);
66                 break;
67         }
68         return PCIBIOS_SUCCESSFUL;
69 }
70
71 static int
72 indirect_write_config(struct pci_bus *bus, unsigned int devfn, int offset,
73                       int len, u32 val)
74 {
75         struct pci_controller *hose = pci_bus_to_host(bus);
76         volatile void __iomem *cfg_data;
77         u8 cfg_type = 0;
78         u32 bus_no, reg;
79
80         if (hose->indirect_type & INDIRECT_TYPE_NO_PCIE_LINK) {
81                 if (bus->number != hose->first_busno)
82                         return PCIBIOS_DEVICE_NOT_FOUND;
83                 if (devfn != 0)
84                         return PCIBIOS_DEVICE_NOT_FOUND;
85         }
86
87         if (hose->indirect_type & INDIRECT_TYPE_SET_CFG_TYPE)
88                 if (bus->number != hose->first_busno)
89                         cfg_type = 1;
90
91         bus_no = (bus->number == hose->first_busno) ?
92                         hose->self_busno : bus->number;
93
94         if (hose->indirect_type & INDIRECT_TYPE_EXT_REG)
95                 reg = ((offset & 0xf00) << 16) | (offset & 0xfc);
96         else
97                 reg = offset & 0xfc;
98
99         if (hose->indirect_type & INDIRECT_TYPE_BIG_ENDIAN)
100                 out_be32(hose->cfg_addr, (0x80000000 | (bus_no << 16) |
101                          (devfn << 8) | reg | cfg_type));
102         else
103                 out_le32(hose->cfg_addr, (0x80000000 | (bus_no << 16) |
104                          (devfn << 8) | reg | cfg_type));
105
106         /* suppress setting of PCI_PRIMARY_BUS */
107         if (hose->indirect_type & INDIRECT_TYPE_SURPRESS_PRIMARY_BUS)
108                 if ((offset == PCI_PRIMARY_BUS) &&
109                         (bus->number == hose->first_busno))
110                         val &= 0xffffff00;
111
112         /* Workaround for PCI_28 Errata in 440EPx/GRx */
113         if ((hose->indirect_type & INDIRECT_TYPE_BROKEN_MRM) &&
114                         offset == PCI_CACHE_LINE_SIZE) {
115                 val = 0;
116         }
117
118         /*
119          * Note: the caller has already checked that offset is
120          * suitably aligned and that len is 1, 2 or 4.
121          */
122         cfg_data = hose->cfg_data + (offset & 3);
123         switch (len) {
124         case 1:
125                 out_8(cfg_data, val);
126                 break;
127         case 2:
128                 out_le16(cfg_data, val);
129                 break;
130         default:
131                 out_le32(cfg_data, val);
132                 break;
133         }
134
135         return PCIBIOS_SUCCESSFUL;
136 }
137
138 static struct pci_ops indirect_pci_ops = {
139         .read = indirect_read_config,
140         .write = indirect_write_config,
141 };
142
143 void __init
144 setup_indirect_pci(struct pci_controller *hose,
145                    resource_size_t cfg_addr,
146                    resource_size_t cfg_data, u32 flags)
147 {
148         resource_size_t base = cfg_addr & PAGE_MASK;
149         void __iomem *mbase;
150
151         mbase = ioremap(base, PAGE_SIZE);
152         hose->cfg_addr = mbase + (cfg_addr & ~PAGE_MASK);
153         if ((cfg_data & PAGE_MASK) != base)
154                 mbase = ioremap(cfg_data & PAGE_MASK, PAGE_SIZE);
155         hose->cfg_data = mbase + (cfg_data & ~PAGE_MASK);
156         hose->ops = &indirect_pci_ops;
157         hose->indirect_type = flags;
158 }