blob: 5555cf5a8c9e20cdd4c555ead0bb5d1e1f549123 [file] [log] [blame]
Ramon Fried663686d2019-04-27 11:15:21 +03001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * PCI Endpoint uclass
4 *
5 * Based on Linux PCI-EP driver written by
6 * Kishon Vijay Abraham I <kishon@ti.com>
7 *
8 * Copyright (c) 2019
9 * Written by Ramon Fried <ramon.fried@gmail.com>
10 */
11
12#include <common.h>
13#include <dm.h>
14#include <errno.h>
Simon Glass3ba929a2020-10-30 21:38:53 -060015#include <asm/global_data.h>
Ramon Fried663686d2019-04-27 11:15:21 +030016#include <linux/log2.h>
17#include <pci_ep.h>
18
19DECLARE_GLOBAL_DATA_PTR;
20
21int pci_ep_write_header(struct udevice *dev, uint fn, struct pci_ep_header *hdr)
22{
23 struct pci_ep_ops *ops = pci_ep_get_ops(dev);
24
25 if (!ops->write_header)
26 return -ENOSYS;
27
28 return ops->write_header(dev, fn, hdr);
29}
30
31int pci_ep_read_header(struct udevice *dev, uint fn, struct pci_ep_header *hdr)
32{
33 struct pci_ep_ops *ops = pci_ep_get_ops(dev);
34
35 if (!ops->read_header)
36 return -ENOSYS;
37
38 return ops->read_header(dev, fn, hdr);
39}
40
41int pci_ep_set_bar(struct udevice *dev, uint func_no, struct pci_bar *ep_bar)
42{
43 struct pci_ep_ops *ops = pci_ep_get_ops(dev);
44 int flags = ep_bar->flags;
45
46 /* Some basic bar validity checks */
Ramon Fried77990812019-07-31 11:04:26 +030047 if (ep_bar->barno > BAR_5 || ep_bar->barno < BAR_0)
Ramon Fried663686d2019-04-27 11:15:21 +030048 return -EINVAL;
49
50 if ((ep_bar->barno == BAR_5 &&
51 (flags & PCI_BASE_ADDRESS_MEM_TYPE_64)) ||
52 ((flags & PCI_BASE_ADDRESS_SPACE_IO) &&
53 (flags & PCI_BASE_ADDRESS_IO_MASK)) ||
54 (upper_32_bits(ep_bar->size) &&
55 !(flags & PCI_BASE_ADDRESS_MEM_TYPE_64)))
56 return -EINVAL;
57
58 if (!ops->set_bar)
59 return -ENOSYS;
60
61 return ops->set_bar(dev, func_no, ep_bar);
62}
63
64int pci_ep_read_bar(struct udevice *dev, uint func_no, struct pci_bar *ep_bar,
65 enum pci_barno barno)
66{
67 struct pci_ep_ops *ops = pci_ep_get_ops(dev);
68
69 /* Some basic bar validity checks */
70 if (barno > BAR_5 || barno < BAR_0)
71 return -EINVAL;
72
73 if (!ops->read_bar)
74 return -ENOSYS;
75
76 return ops->read_bar(dev, func_no, ep_bar, barno);
77}
78
79int pci_ep_clear_bar(struct udevice *dev, uint func_num, enum pci_barno bar)
80{
81 struct pci_ep_ops *ops = pci_ep_get_ops(dev);
82
83 if (!ops->clear_bar)
84 return -ENOSYS;
85
86 return ops->clear_bar(dev, func_num, bar);
87}
88
89int pci_ep_map_addr(struct udevice *dev, uint func_no, phys_addr_t addr,
90 u64 pci_addr, size_t size)
91{
92 struct pci_ep_ops *ops = pci_ep_get_ops(dev);
93
94 if (!ops->map_addr)
95 return -ENOSYS;
96
97 return ops->map_addr(dev, func_no, addr, pci_addr, size);
98}
99
100int pci_ep_unmap_addr(struct udevice *dev, uint func_no, phys_addr_t addr)
101{
102 struct pci_ep_ops *ops = pci_ep_get_ops(dev);
103
104 if (!ops->unmap_addr)
105 return -ENOSYS;
106
107 return ops->unmap_addr(dev, func_no, addr);
108}
109
110int pci_ep_set_msi(struct udevice *dev, uint func_no, uint interrupts)
111{
112 struct pci_ep_ops *ops = pci_ep_get_ops(dev);
113 uint encode_int;
114
115 if (interrupts > 32)
116 return -EINVAL;
117
118 if (!ops->set_msi)
119 return -ENOSYS;
120
121 /* MSI spec permits allocation of
122 * only 1, 2, 4, 8, 16, 32 interrupts
123 */
124 encode_int = order_base_2(interrupts);
125
126 return ops->set_msi(dev, func_no, encode_int);
127}
128
129int pci_ep_get_msi(struct udevice *dev, uint func_no)
130{
131 struct pci_ep_ops *ops = pci_ep_get_ops(dev);
132 int interrupt;
133
134 if (!ops->get_msi)
135 return -ENOSYS;
136
137 interrupt = ops->get_msi(dev, func_no);
138
139 if (interrupt < 0)
140 return 0;
141
142 /* Translate back from order base 2*/
143 interrupt = 1 << interrupt;
144
145 return interrupt;
146}
147
148int pci_ep_set_msix(struct udevice *dev, uint func_no, uint interrupts)
149{
150 struct pci_ep_ops *ops = pci_ep_get_ops(dev);
151
152 if (interrupts < 1 || interrupts > 2048)
153 return -EINVAL;
154
155 if (!ops->set_msix)
156 return -ENOSYS;
157
158 return ops->set_msix(dev, func_no, interrupts - 1);
159}
160
161int pci_ep_get_msix(struct udevice *dev, uint func_no)
162{
163 struct pci_ep_ops *ops = pci_ep_get_ops(dev);
164 int interrupt;
165
166 if (!ops->get_msix)
167 return -ENOSYS;
168
169 interrupt = ops->get_msix(dev, func_no);
170
171 if (interrupt < 0)
172 return 0;
173
174 return interrupt + 1;
175}
176
177int pci_ep_raise_irq(struct udevice *dev, uint func_no,
178 enum pci_ep_irq_type type, uint interrupt_num)
179{
180 struct pci_ep_ops *ops = pci_ep_get_ops(dev);
181
182 if (!ops->raise_irq)
183 return -ENOSYS;
184
185 return ops->raise_irq(dev, func_no, type, interrupt_num);
186}
187
188int pci_ep_start(struct udevice *dev)
189{
190 struct pci_ep_ops *ops = pci_ep_get_ops(dev);
191
192 if (!ops->start)
193 return -ENOSYS;
194
195 return ops->start(dev);
196}
197
198int pci_ep_stop(struct udevice *dev)
199{
200 struct pci_ep_ops *ops = pci_ep_get_ops(dev);
201
202 if (!ops->stop)
203 return -ENOSYS;
204
205 return ops->stop(dev);
206}
207
208UCLASS_DRIVER(pci_ep) = {
209 .id = UCLASS_PCI_EP,
210 .name = "pci_ep",
211 .flags = DM_UC_FLAG_SEQ_ALIAS,
212};
Xiaowei Bao97434b42020-07-09 23:31:34 +0800213
Ovidiu Panaita45c6e12020-11-28 10:43:11 +0200214int pci_ep_init(void)
Xiaowei Bao97434b42020-07-09 23:31:34 +0800215{
216 struct udevice *dev;
217
218 for (uclass_first_device_check(UCLASS_PCI_EP, &dev);
219 dev;
220 uclass_next_device_check(&dev)) {
221 ;
222 }
Ovidiu Panaita45c6e12020-11-28 10:43:11 +0200223
224 return 0;
Xiaowei Bao97434b42020-07-09 23:31:34 +0800225}