1 // SPDX-License-Identifier: GPL-2.0-only
2 #include <linux/types.h>
3 #include <linux/ioport.h>
4 #include <linux/slab.h>
5 #include <linux/export.h>
9 #include "mcb-internal.h"
11 struct mcb_parse_priv
{
16 #define for_each_chameleon_cell(dtype, p) \
17 for ((dtype) = get_next_dtype((p)); \
18 (dtype) != CHAMELEON_DTYPE_END; \
19 (dtype) = get_next_dtype((p)))
21 static inline uint32_t get_next_dtype(void __iomem
*p
)
29 static int chameleon_parse_bdd(struct mcb_bus
*bus
,
30 struct chameleon_bar
*cb
,
36 static int chameleon_parse_gdd(struct mcb_bus
*bus
,
37 struct chameleon_bar
*cb
,
38 void __iomem
*base
, int bar_count
)
40 struct chameleon_gdd __iomem
*gdd
=
41 (struct chameleon_gdd __iomem
*) base
;
42 struct mcb_device
*mdev
;
50 mdev
= mcb_alloc_dev(bus
);
54 reg1
= readl(&gdd
->reg1
);
55 reg2
= readl(&gdd
->reg2
);
56 offset
= readl(&gdd
->offset
);
57 size
= readl(&gdd
->size
);
59 mdev
->id
= GDD_DEV(reg1
);
60 mdev
->rev
= GDD_REV(reg1
);
61 mdev
->var
= GDD_VAR(reg1
);
62 mdev
->bar
= GDD_BAR(reg2
);
63 mdev
->group
= GDD_GRP(reg2
);
64 mdev
->inst
= GDD_INS(reg2
);
67 * If the BAR is missing, dev_mapbase is zero, or if the
68 * device is IO mapped we just print a warning and go on with the
69 * next device, instead of completely stop the gdd parser
71 if (mdev
->bar
> bar_count
- 1) {
72 pr_info("No BAR for 16z%03d\n", mdev
->id
);
77 dev_mapbase
= cb
[mdev
->bar
].addr
;
79 pr_info("BAR not assigned for 16z%03d\n", mdev
->id
);
84 if (dev_mapbase
& 0x01) {
85 pr_info("IO mapped Device (16z%03d) not yet supported\n",
91 pr_debug("Found a 16z%03d\n", mdev
->id
);
93 mdev
->irq
.start
= GDD_IRQ(reg1
);
94 mdev
->irq
.end
= GDD_IRQ(reg1
);
95 mdev
->irq
.flags
= IORESOURCE_IRQ
;
97 mdev
->mem
.start
= dev_mapbase
+ offset
;
99 mdev
->mem
.end
= mdev
->mem
.start
+ size
- 1;
100 mdev
->mem
.flags
= IORESOURCE_MEM
;
102 mdev
->is_added
= false;
104 ret
= mcb_device_register(bus
, mdev
);
116 static void chameleon_parse_bar(void __iomem
*base
,
117 struct chameleon_bar
*cb
, int bar_count
)
119 char __iomem
*p
= base
;
125 for (i
= 0; i
< bar_count
; i
++) {
126 cb
[i
].addr
= readl(p
);
127 cb
[i
].size
= readl(p
+ 4);
129 p
+= sizeof(struct chameleon_bar
);
133 static int chameleon_get_bar(char __iomem
**base
, phys_addr_t mapbase
,
134 struct chameleon_bar
**cb
)
136 struct chameleon_bar
*c
;
142 * For those devices which are not connected
143 * to the PCI Bus (e.g. LPC) there is a bar
144 * descriptor located directly after the
145 * chameleon header. This header is comparable
148 dtype
= get_next_dtype(*base
);
149 if (dtype
== CHAMELEON_DTYPE_BAR
) {
152 bar_count
= BAR_CNT(reg
);
153 if (bar_count
<= 0 || bar_count
> CHAMELEON_BAR_MAX
)
156 c
= kcalloc(bar_count
, sizeof(struct chameleon_bar
),
161 chameleon_parse_bar(*base
, c
, bar_count
);
162 *base
+= BAR_DESC_SIZE(bar_count
);
164 c
= kzalloc(sizeof(struct chameleon_bar
), GFP_KERNEL
);
177 int chameleon_parse_cells(struct mcb_bus
*bus
, phys_addr_t mapbase
,
180 struct chameleon_fpga_header
*header
;
181 struct chameleon_bar
*cb
;
182 char __iomem
*p
= base
;
189 hsize
= sizeof(struct chameleon_fpga_header
);
191 header
= kzalloc(hsize
, GFP_KERNEL
);
195 /* Extract header information */
196 memcpy_fromio(header
, p
, hsize
);
197 /* We only support chameleon v2 at the moment */
198 header
->magic
= le16_to_cpu(header
->magic
);
199 if (header
->magic
!= CHAMELEONV2_MAGIC
) {
200 pr_err("Unsupported chameleon version 0x%x\n",
207 bus
->revision
= header
->revision
;
208 bus
->model
= header
->model
;
209 bus
->minor
= header
->minor
;
210 snprintf(bus
->name
, CHAMELEON_FILENAME_LEN
+ 1, "%s",
213 bar_count
= chameleon_get_bar(&p
, mapbase
, &cb
);
219 for_each_chameleon_cell(dtype
, p
) {
221 case CHAMELEON_DTYPE_GENERAL
:
222 ret
= chameleon_parse_gdd(bus
, cb
, p
, bar_count
);
225 p
+= sizeof(struct chameleon_gdd
);
227 case CHAMELEON_DTYPE_BRIDGE
:
228 chameleon_parse_bdd(bus
, cb
, p
);
229 p
+= sizeof(struct chameleon_bdd
);
231 case CHAMELEON_DTYPE_END
:
234 pr_err("Invalid chameleon descriptor type 0x%x\n",
256 EXPORT_SYMBOL_GPL(chameleon_parse_cells
);