]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0 |
feaa6066 GJ |
2 | /* |
3 | * Copyright (C) 2013 Gabor Juhos <[email protected]> | |
4 | * | |
5 | * Based on the Linux implementation. | |
6 | * Copyright (C) 1999, 2000, 2004 MIPS Technologies, Inc. | |
7 | * Authors: Carsten Langgaard <[email protected]> | |
8 | * Maciej W. Rozycki <[email protected]> | |
feaa6066 GJ |
9 | */ |
10 | ||
201d49d9 | 11 | #include <dm.h> |
feaa6066 | 12 | #include <gt64120.h> |
691d719d | 13 | #include <init.h> |
f7ae49fc | 14 | #include <log.h> |
feaa6066 GJ |
15 | #include <pci.h> |
16 | #include <pci_gt64120.h> | |
17 | ||
18 | #include <asm/io.h> | |
19 | ||
20 | #define PCI_ACCESS_READ 0 | |
21 | #define PCI_ACCESS_WRITE 1 | |
22 | ||
23 | struct gt64120_regs { | |
24 | u8 unused_000[0xc18]; | |
25 | u32 intrcause; | |
26 | u8 unused_c1c[0x0dc]; | |
27 | u32 pci0_cfgaddr; | |
28 | u32 pci0_cfgdata; | |
29 | }; | |
30 | ||
31 | struct gt64120_pci_controller { | |
32 | struct pci_controller hose; | |
33 | struct gt64120_regs *regs; | |
34 | }; | |
35 | ||
36 | static inline struct gt64120_pci_controller * | |
37 | hose_to_gt64120(struct pci_controller *hose) | |
38 | { | |
39 | return container_of(hose, struct gt64120_pci_controller, hose); | |
40 | } | |
41 | ||
42 | #define GT_INTRCAUSE_ABORT_BITS \ | |
43 | (GT_INTRCAUSE_MASABORT0_BIT | GT_INTRCAUSE_TARABORT0_BIT) | |
44 | ||
45 | static int gt_config_access(struct gt64120_pci_controller *gt, | |
46 | unsigned char access_type, pci_dev_t bdf, | |
47 | int where, u32 *data) | |
48 | { | |
49 | unsigned int bus = PCI_BUS(bdf); | |
50 | unsigned int dev = PCI_DEV(bdf); | |
2b29d79b | 51 | unsigned int func = PCI_FUNC(bdf); |
feaa6066 GJ |
52 | u32 intr; |
53 | u32 addr; | |
54 | u32 val; | |
55 | ||
56 | if (bus == 0 && dev >= 31) { | |
57 | /* Because of a bug in the galileo (for slot 31). */ | |
58 | return -1; | |
59 | } | |
60 | ||
61 | if (access_type == PCI_ACCESS_WRITE) | |
62 | debug("PCI WR %02x:%02x.%x reg:%02d data:%08x\n", | |
63 | PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf), where, *data); | |
64 | ||
65 | /* Clear cause register bits */ | |
66 | writel(~GT_INTRCAUSE_ABORT_BITS, >->regs->intrcause); | |
67 | ||
2b29d79b | 68 | addr = PCI_CONF1_ADDRESS(bus, dev, func, where); |
feaa6066 GJ |
69 | |
70 | /* Setup address */ | |
71 | writel(addr, >->regs->pci0_cfgaddr); | |
72 | ||
73 | if (access_type == PCI_ACCESS_WRITE) { | |
74 | if (bus == 0 && dev == 0) { | |
75 | /* | |
76 | * The Galileo system controller is acting | |
77 | * differently than other devices. | |
78 | */ | |
79 | val = *data; | |
80 | } else { | |
81 | val = cpu_to_le32(*data); | |
82 | } | |
83 | ||
84 | writel(val, >->regs->pci0_cfgdata); | |
85 | } else { | |
86 | val = readl(>->regs->pci0_cfgdata); | |
87 | ||
88 | if (bus == 0 && dev == 0) { | |
89 | /* | |
90 | * The Galileo system controller is acting | |
91 | * differently than other devices. | |
92 | */ | |
93 | *data = val; | |
94 | } else { | |
95 | *data = le32_to_cpu(val); | |
96 | } | |
97 | } | |
98 | ||
99 | /* Check for master or target abort */ | |
100 | intr = readl(>->regs->intrcause); | |
101 | if (intr & GT_INTRCAUSE_ABORT_BITS) { | |
102 | /* Error occurred, clear abort bits */ | |
103 | writel(~GT_INTRCAUSE_ABORT_BITS, >->regs->intrcause); | |
104 | return -1; | |
105 | } | |
106 | ||
107 | if (access_type == PCI_ACCESS_READ) | |
108 | debug("PCI RD %02x:%02x.%x reg:%02d data:%08x\n", | |
109 | PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf), where, *data); | |
110 | ||
111 | return 0; | |
112 | } | |
113 | ||
201d49d9 DS |
114 | static int gt64120_pci_read_config(const struct udevice *dev, pci_dev_t bdf, |
115 | uint where, ulong *val, | |
116 | enum pci_size_t size) | |
117 | { | |
118 | struct gt64120_pci_controller *gt = dev_get_priv(dev); | |
119 | u32 data = 0; | |
120 | ||
121 | if (gt_config_access(gt, PCI_ACCESS_READ, bdf, where, &data)) { | |
122 | *val = pci_get_ff(size); | |
123 | return 0; | |
124 | } | |
125 | ||
126 | *val = pci_conv_32_to_size(data, where, size); | |
127 | ||
128 | return 0; | |
129 | } | |
130 | ||
131 | static int gt64120_pci_write_config(struct udevice *dev, pci_dev_t bdf, | |
132 | uint where, ulong val, | |
133 | enum pci_size_t size) | |
134 | { | |
135 | struct gt64120_pci_controller *gt = dev_get_priv(dev); | |
136 | u32 data = 0; | |
137 | ||
138 | if (size == PCI_SIZE_32) { | |
139 | data = val; | |
140 | } else { | |
141 | u32 old; | |
142 | ||
143 | if (gt_config_access(gt, PCI_ACCESS_READ, bdf, where, &old)) | |
144 | return 0; | |
145 | ||
146 | data = pci_conv_size_to_32(old, val, where, size); | |
147 | } | |
148 | ||
149 | gt_config_access(gt, PCI_ACCESS_WRITE, bdf, where, &data); | |
150 | ||
151 | return 0; | |
152 | } | |
153 | ||
154 | static int gt64120_pci_probe(struct udevice *dev) | |
155 | { | |
156 | struct gt64120_pci_controller *gt = dev_get_priv(dev); | |
157 | ||
158 | gt->regs = dev_remap_addr(dev); | |
159 | if (!gt->regs) | |
160 | return -EINVAL; | |
161 | ||
162 | return 0; | |
163 | } | |
164 | ||
165 | static const struct dm_pci_ops gt64120_pci_ops = { | |
166 | .read_config = gt64120_pci_read_config, | |
167 | .write_config = gt64120_pci_write_config, | |
168 | }; | |
169 | ||
170 | static const struct udevice_id gt64120_pci_ids[] = { | |
171 | { .compatible = "marvell,pci-gt64120" }, | |
172 | { } | |
173 | }; | |
174 | ||
175 | U_BOOT_DRIVER(gt64120_pci) = { | |
176 | .name = "gt64120_pci", | |
177 | .id = UCLASS_PCI, | |
178 | .of_match = gt64120_pci_ids, | |
179 | .ops = >64120_pci_ops, | |
180 | .probe = gt64120_pci_probe, | |
181 | .priv_auto = sizeof(struct gt64120_pci_controller), | |
182 | }; |