source: mainline/uspace/drv/bus/amba/amba.c@ 6de4b4a1

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 6de4b4a1 was 208b5f5, checked in by Martin Decky <martin@…>, 12 years ago

cherrypick important fixes and updates from lp:~jceel/helenos/leon3

  • Property mode set to 100644
File size: 10.2 KB
Line 
1/*
2 * Copyright (c) 2013 Jakub Klama
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/**
30 * @defgroup amba AMBA bus driver.
31 * @brief HelenOS AMBA bus driver.
32 * @{
33 */
34/** @file
35 */
36
37#include <assert.h>
38#include <byteorder.h>
39#include <stdio.h>
40#include <errno.h>
41#include <stdbool.h>
42#include <fibril_synch.h>
43#include <str.h>
44#include <ctype.h>
45#include <macros.h>
46#include <str_error.h>
47#include <ddf/driver.h>
48#include <ddf/log.h>
49#include <ipc/dev_iface.h>
50#include <ipc/irc.h>
51#include <ns.h>
52#include <ipc/services.h>
53#include <sysinfo.h>
54#include <ops/hw_res.h>
55#include <device/hw_res.h>
56#include <ddi.h>
57#include "ambapp.h"
58
59#define NAME "amba"
60#define ID_MAX_STR_LEN 32
61
62#define LVL_DEBUG LVL_ERROR
63
64typedef struct leon_amba_bus {
65 /** DDF device node */
66 ddf_dev_t *dnode;
67 uintptr_t master_area_addr;
68 uintptr_t slave_area_addr;
69 size_t master_area_size;
70 size_t slave_area_size;
71 void *master_area;
72 void *slave_area;
73 fibril_mutex_t area_mutex;
74} amba_bus_t;
75
76typedef struct amba_fun_data {
77 amba_bus_t *busptr;
78 ddf_fun_t *fnode;
79 int bus;
80 int index;
81 uint8_t vendor_id;
82 uint32_t device_id;
83 int version;
84 hw_resource_list_t hw_resources;
85 hw_resource_t resources[AMBA_MAX_HW_RES];
86} amba_fun_t;
87
88static amba_fun_t *amba_fun_new(amba_bus_t *);
89static void amba_fun_set_name(amba_fun_t *);
90static void amba_fun_create_match_ids(amba_fun_t *);
91static int amba_fun_online(ddf_fun_t *);
92static int amba_fun_offline(ddf_fun_t *);
93static hw_resource_list_t *amba_get_resources(ddf_fun_t *);
94static bool amba_enable_interrupt(ddf_fun_t *);
95static void amba_add_bar(amba_fun_t *, uintptr_t, size_t);
96static void amba_add_interrupt(amba_fun_t *, int);
97static int amba_bus_scan(amba_bus_t *, void *, unsigned int);
98static void amba_fake_scan(amba_bus_t *);
99static int amba_dev_add(ddf_dev_t *);
100
101static hw_res_ops_t amba_fun_hw_res_ops = {
102 .get_resource_list = &amba_get_resources,
103 .enable_interrupt = &amba_enable_interrupt
104};
105
106static ddf_dev_ops_t amba_fun_ops = {
107 .interfaces[HW_RES_DEV_IFACE] = &amba_fun_hw_res_ops
108};
109
110static driver_ops_t amba_ops = {
111 .dev_add = &amba_dev_add,
112 .fun_online = &amba_fun_online,
113 .fun_offline = &amba_fun_offline
114};
115
116static driver_t amba_driver = {
117 .name = NAME,
118 .driver_ops = &amba_ops
119};
120
121static amba_fun_t *amba_fun_new(amba_bus_t *bus)
122{
123 ddf_msg(LVL_DEBUG, "amba_fun_new(): bus=%p, bus->dnode=%p", bus,
124 bus->dnode);
125
126 ddf_fun_t *fnode = ddf_fun_create(bus->dnode, fun_inner, NULL);
127 if (fnode == NULL)
128 return NULL;
129
130 ddf_msg(LVL_DEBUG, "amba_fun_new(): created");
131
132 amba_fun_t *fun = ddf_fun_data_alloc(fnode, sizeof(amba_fun_t));
133 if (fun == NULL)
134 return NULL;
135
136 ddf_msg(LVL_DEBUG, "amba_fun_new(): allocated data");
137
138 fun->busptr = bus;
139 fun->fnode = fnode;
140 return fun;
141}
142
143static void amba_fun_set_name(amba_fun_t *fun)
144{
145 char *name = NULL;
146
147 asprintf(&name, "%02x:%02x", fun->bus, fun->index);
148 ddf_fun_set_name(fun->fnode, name);
149}
150
151static void amba_fun_create_match_ids(amba_fun_t *fun)
152{
153 /* Vendor ID & Device ID */
154 char match_id_str[ID_MAX_STR_LEN];
155 int rc = snprintf(match_id_str, ID_MAX_STR_LEN, "amba/ven=%02x&dev=%08x",
156 fun->vendor_id, fun->device_id);
157 if (rc < 0) {
158 ddf_msg(LVL_ERROR, "Failed creating match ID str: %s",
159 str_error(rc));
160 }
161
162 rc = ddf_fun_add_match_id(fun->fnode, match_id_str, 90);
163}
164
165static int amba_fun_online(ddf_fun_t *fun)
166{
167 ddf_msg(LVL_DEBUG, "amba_fun_online()");
168 return ddf_fun_online(fun);
169
170}
171
172static int amba_fun_offline(ddf_fun_t *fun)
173{
174 ddf_msg(LVL_DEBUG, "amba_fun_offline()");
175 return ddf_fun_offline(fun);
176}
177
178static hw_resource_list_t *amba_get_resources(ddf_fun_t *fnode)
179{
180 amba_fun_t *fun = ddf_fun_data_get(fnode);
181
182 if (fun == NULL)
183 return NULL;
184
185 return &fun->hw_resources;
186}
187
188static bool amba_enable_interrupt(ddf_fun_t *fnode)
189{
190 return true;
191}
192
193static void amba_alloc_resource_list(amba_fun_t *fun)
194{
195 fun->hw_resources.resources = fun->resources;
196}
197
198static void amba_add_bar(amba_fun_t *fun, uintptr_t addr, size_t size)
199{
200 hw_resource_list_t *hw_res_list = &fun->hw_resources;
201 hw_resource_t *hw_resources = hw_res_list->resources;
202 size_t count = hw_res_list->count;
203
204 assert(hw_resources != NULL);
205 assert(count < AMBA_MAX_HW_RES);
206
207 hw_resources[count].type = MEM_RANGE;
208 hw_resources[count].res.mem_range.address = addr;
209 hw_resources[count].res.mem_range.size = size;
210 hw_resources[count].res.mem_range.endianness = BIG_ENDIAN;
211
212 hw_res_list->count++;
213}
214
215static void amba_add_interrupt(amba_fun_t *fun, int irq)
216{
217 hw_resource_list_t *hw_res_list = &fun->hw_resources;
218 hw_resource_t *hw_resources = hw_res_list->resources;
219 size_t count = hw_res_list->count;
220
221 assert(NULL != hw_resources);
222 assert(count < AMBA_MAX_HW_RES);
223
224 hw_resources[count].type = INTERRUPT;
225 hw_resources[count].res.interrupt.irq = irq;
226
227 hw_res_list->count++;
228
229 ddf_msg(LVL_NOTE, "Function %s uses irq %x.", ddf_fun_get_name(fun->fnode), irq);
230}
231
232static int amba_bus_scan(amba_bus_t *bus, void *area, unsigned int max_entries)
233{
234 ddf_msg(LVL_DEBUG, "amba_bus_scan(): area=%p, max_entries=%u", area, max_entries);
235
236 ambapp_entry_t *devices = (ambapp_entry_t *) area;
237 int found = 0;
238
239 for (unsigned int i = 0; i < max_entries; i++) {
240 ambapp_entry_t *entry = &devices[i];
241 if (entry->vendor_id == 0xff)
242 continue;
243
244 amba_fun_t *fun = amba_fun_new(bus);
245 fun->bus = 0;
246 fun->index = i;
247 fun->vendor_id = entry->vendor_id;
248 fun->device_id = entry->device_id;
249 fun->version = entry->version;
250
251 for (unsigned int bnum = 0; bnum < 4; bnum++) {
252 ambapp_bar_t *bar = &entry->bar[bnum];
253 amba_add_bar(fun, bar->addr << 20, bar->mask);
254 }
255
256 if (entry->irq != -1)
257 amba_add_interrupt(fun, entry->irq);
258
259 ddf_fun_set_ops(fun->fnode, &amba_fun_ops);
260 ddf_fun_bind(fun->fnode);
261 }
262
263 return found;
264}
265
266static void amba_fake_scan(amba_bus_t *bus)
267{
268 ddf_msg(LVL_DEBUG, "amba_fake_scan()");
269
270 /* UART */
271 amba_fun_t *fun = amba_fun_new(bus);
272 fun->bus = 0;
273 fun->index = 0;
274 fun->vendor_id = GAISLER;
275 fun->device_id = GAISLER_APBUART;
276 fun->version = 1;
277 amba_alloc_resource_list(fun);
278 amba_add_bar(fun, 0x80000100, 0x100);
279 amba_add_interrupt(fun, 3);
280 amba_fun_set_name(fun);
281 amba_fun_create_match_ids(fun);
282 ddf_fun_set_ops(fun->fnode, &amba_fun_ops);
283 ddf_fun_bind(fun->fnode);
284
285 ddf_msg(LVL_DEBUG, "added uart");
286
287 /* IRQMP */
288 fun = amba_fun_new(bus);
289 fun->bus = 0;
290 fun->index = 1;
291 fun->vendor_id = GAISLER;
292 fun->device_id = GAISLER_IRQMP;
293 fun->version = 1;
294 amba_alloc_resource_list(fun);
295 amba_add_bar(fun, 0x80000200, 0x100);
296 amba_fun_set_name(fun);
297 amba_fun_create_match_ids(fun);
298 ddf_fun_set_ops(fun->fnode, &amba_fun_ops);
299 ddf_fun_bind(fun->fnode);
300
301 ddf_msg(LVL_DEBUG, "added irqmp");
302
303 /* GPTIMER */
304 fun = amba_fun_new(bus);
305 fun->bus = 0;
306 fun->index = 2;
307 fun->vendor_id = GAISLER;
308 fun->device_id = GAISLER_GPTIMER;
309 fun->version = 1;
310 amba_alloc_resource_list(fun);
311 amba_add_bar(fun, 0x80000300, 0x100);
312 amba_add_interrupt(fun, 8);
313 amba_fun_set_name(fun);
314 amba_fun_create_match_ids(fun);
315 ddf_fun_set_ops(fun->fnode, &amba_fun_ops);
316 ddf_fun_bind(fun->fnode);
317
318 ddf_msg(LVL_DEBUG, "added timer");
319}
320
321static int amba_dev_add(ddf_dev_t *dnode)
322{
323 int rc = 0;
324 int found = 0;
325 bool got_res = false;
326
327 amba_bus_t *bus = ddf_dev_data_alloc(dnode, sizeof(amba_bus_t));
328 if (bus == NULL) {
329 ddf_msg(LVL_ERROR, "amba_dev_add: allocation failed.");
330 rc = ENOMEM;
331 goto fail;
332 }
333
334 bus->dnode = dnode;
335 async_sess_t *sess = ddf_dev_parent_sess_create(dnode, EXCHANGE_SERIALIZE);
336 if (sess == NULL) {
337 ddf_msg(LVL_ERROR, "amba_dev_add failed to connect to the "
338 "parent driver.");
339 rc = ENOENT;
340 goto fail;
341 }
342
343 hw_resource_list_t hw_resources;
344 rc = hw_res_get_resource_list(sess, &hw_resources);
345 if (rc != EOK) {
346 ddf_msg(LVL_ERROR, "amba_dev_add failed to get hw resources "
347 "for the device.");
348 goto fail;
349 }
350
351 got_res = true;
352 assert(hw_resources.count > 1);
353
354 bus->master_area_addr = hw_resources.resources[0].res.mem_range.address;
355 bus->master_area_size = hw_resources.resources[0].res.mem_range.size;
356 bus->slave_area_addr = hw_resources.resources[1].res.mem_range.address;
357 bus->slave_area_size = hw_resources.resources[1].res.mem_range.size;
358
359 ddf_msg(LVL_DEBUG, "AMBA master area: 0x%08x", bus->master_area_addr);
360 ddf_msg(LVL_DEBUG, "AMBA slave area: 0x%08x", bus->slave_area_addr);
361
362 if (pio_enable((void *)bus->master_area_addr, bus->master_area_size, &bus->master_area)) {
363 ddf_msg(LVL_ERROR, "Failed to enable master area.");
364 rc = EADDRNOTAVAIL;
365 goto fail;
366 }
367
368 if (pio_enable((void *)bus->slave_area_addr, bus->slave_area_size, &bus->slave_area)) {
369 ddf_msg(LVL_ERROR, "Failed to enable slave area.");
370 rc = EADDRNOTAVAIL;
371 goto fail;
372 }
373
374 /*
375 * If nothing is found, we are probably running inside QEMU
376 * and need to fake AMBA P&P entries.
377 */
378 if (found == 0)
379 amba_fake_scan(bus);
380
381 ddf_msg(LVL_DEBUG, "done");
382
383 return EOK;
384
385fail:
386 if (got_res)
387 hw_res_clean_resource_list(&hw_resources);
388
389 return rc;
390}
391
392int main(int argc, char *argv[])
393{
394 printf("%s: HelenOS LEON3 AMBA bus driver\n", NAME);
395 ddf_log_init(NAME);
396 return ddf_driver_main(&amba_driver);
397}
Note: See TracBrowser for help on using the repository browser.