]>
Commit | Line | Data |
---|---|---|
3764e82e JT |
1 | #include <linux/types.h> |
2 | #include <linux/ioport.h> | |
3 | #include <linux/slab.h> | |
4 | #include <linux/export.h> | |
5 | #include <linux/io.h> | |
6 | #include <linux/mcb.h> | |
7 | ||
8 | #include "mcb-internal.h" | |
9 | ||
10 | struct mcb_parse_priv { | |
11 | phys_addr_t mapbase; | |
12 | void __iomem *base; | |
13 | }; | |
14 | ||
15 | #define for_each_chameleon_cell(dtype, p) \ | |
16 | for ((dtype) = get_next_dtype((p)); \ | |
17 | (dtype) != CHAMELEON_DTYPE_END; \ | |
18 | (dtype) = get_next_dtype((p))) | |
19 | ||
20 | static inline uint32_t get_next_dtype(void __iomem *p) | |
21 | { | |
22 | uint32_t dtype; | |
23 | ||
24 | dtype = readl(p); | |
25 | return dtype >> 28; | |
26 | } | |
27 | ||
28 | static int chameleon_parse_bdd(struct mcb_bus *bus, | |
29 | phys_addr_t mapbase, | |
30 | void __iomem *base) | |
31 | { | |
32 | return 0; | |
33 | } | |
34 | ||
35 | static int chameleon_parse_gdd(struct mcb_bus *bus, | |
36 | phys_addr_t mapbase, | |
37 | void __iomem *base) | |
38 | { | |
39 | struct chameleon_gdd __iomem *gdd = | |
40 | (struct chameleon_gdd __iomem *) base; | |
41 | struct mcb_device *mdev; | |
42 | u32 offset; | |
43 | u32 size; | |
44 | int ret; | |
45 | __le32 reg1; | |
46 | __le32 reg2; | |
47 | ||
48 | mdev = mcb_alloc_dev(bus); | |
49 | if (!mdev) | |
50 | return -ENOMEM; | |
51 | ||
52 | reg1 = readl(&gdd->reg1); | |
53 | reg2 = readl(&gdd->reg2); | |
54 | offset = readl(&gdd->offset); | |
55 | size = readl(&gdd->size); | |
56 | ||
57 | mdev->id = GDD_DEV(reg1); | |
58 | mdev->rev = GDD_REV(reg1); | |
59 | mdev->var = GDD_VAR(reg1); | |
60 | mdev->bar = GDD_BAR(reg1); | |
61 | mdev->group = GDD_GRP(reg2); | |
62 | mdev->inst = GDD_INS(reg2); | |
63 | ||
64 | pr_debug("Found a 16z%03d\n", mdev->id); | |
65 | ||
66 | mdev->irq.start = GDD_IRQ(reg1); | |
67 | mdev->irq.end = GDD_IRQ(reg1); | |
68 | mdev->irq.flags = IORESOURCE_IRQ; | |
69 | ||
70 | mdev->mem.start = mapbase + offset; | |
71 | mdev->mem.end = mdev->mem.start + size - 1; | |
72 | mdev->mem.flags = IORESOURCE_MEM; | |
73 | ||
74 | mdev->is_added = false; | |
75 | ||
76 | ret = mcb_device_register(bus, mdev); | |
77 | if (ret < 0) | |
78 | goto err; | |
79 | ||
80 | return 0; | |
81 | ||
82 | err: | |
83 | mcb_free_dev(mdev); | |
84 | ||
85 | return ret; | |
86 | } | |
87 | ||
88 | int chameleon_parse_cells(struct mcb_bus *bus, phys_addr_t mapbase, | |
89 | void __iomem *base) | |
90 | { | |
91 | char __iomem *p = base; | |
92 | struct chameleon_fpga_header *header; | |
93 | uint32_t dtype; | |
94 | int num_cells = 0; | |
95 | int ret = 0; | |
96 | u32 hsize; | |
97 | ||
98 | hsize = sizeof(struct chameleon_fpga_header); | |
99 | ||
100 | header = kzalloc(hsize, GFP_KERNEL); | |
101 | if (!header) | |
102 | return -ENOMEM; | |
103 | ||
104 | /* Extract header information */ | |
105 | memcpy_fromio(header, p, hsize); | |
106 | /* We only support chameleon v2 at the moment */ | |
107 | header->magic = le16_to_cpu(header->magic); | |
108 | if (header->magic != CHAMELEONV2_MAGIC) { | |
109 | pr_err("Unsupported chameleon version 0x%x\n", | |
110 | header->magic); | |
111 | kfree(header); | |
112 | return -ENODEV; | |
113 | } | |
114 | p += hsize; | |
115 | ||
116 | pr_debug("header->revision = %d\n", header->revision); | |
117 | pr_debug("header->model = 0x%x ('%c')\n", header->model, | |
118 | header->model); | |
119 | pr_debug("header->minor = %d\n", header->minor); | |
120 | pr_debug("header->bus_type = 0x%x\n", header->bus_type); | |
121 | ||
122 | ||
123 | pr_debug("header->magic = 0x%x\n", header->magic); | |
124 | pr_debug("header->filename = \"%.*s\"\n", CHAMELEON_FILENAME_LEN, | |
125 | header->filename); | |
126 | ||
127 | for_each_chameleon_cell(dtype, p) { | |
128 | switch (dtype) { | |
129 | case CHAMELEON_DTYPE_GENERAL: | |
130 | ret = chameleon_parse_gdd(bus, mapbase, p); | |
131 | if (ret < 0) | |
132 | goto out; | |
133 | p += sizeof(struct chameleon_gdd); | |
134 | break; | |
135 | case CHAMELEON_DTYPE_BRIDGE: | |
136 | chameleon_parse_bdd(bus, mapbase, p); | |
137 | p += sizeof(struct chameleon_bdd); | |
138 | break; | |
139 | case CHAMELEON_DTYPE_END: | |
140 | break; | |
141 | default: | |
142 | pr_err("Invalid chameleon descriptor type 0x%x\n", | |
143 | dtype); | |
7c735282 | 144 | kfree(header); |
3764e82e JT |
145 | return -EINVAL; |
146 | } | |
147 | num_cells++; | |
148 | } | |
149 | ||
150 | if (num_cells == 0) | |
151 | num_cells = -EINVAL; | |
152 | ||
153 | kfree(header); | |
154 | return num_cells; | |
155 | ||
156 | out: | |
157 | kfree(header); | |
158 | return ret; | |
159 | } | |
160 | EXPORT_SYMBOL_GPL(chameleon_parse_cells); |