[e05c265] | 1 | /* General part of a AMBA Plug & Play bus driver. |
---|
| 2 | * |
---|
| 3 | * COPYRIGHT (c) 2008. |
---|
| 4 | * Cobham Gaisler AB. |
---|
| 5 | * |
---|
| 6 | * The license and distribution terms for this file may be |
---|
| 7 | * found in the file LICENSE in this distribution or at |
---|
| 8 | * http://www.rtems.com/license/LICENSE. |
---|
| 9 | * |
---|
| 10 | * This is the general part of the different AMBA Plug & Play |
---|
| 11 | * drivers. The drivers are wrappers around this driver, making |
---|
| 12 | * the code size smaller for systems with multiple AMBA Plug & |
---|
| 13 | * Play buses. |
---|
| 14 | * |
---|
| 15 | * The BSP define APBUART_INFO_AVAIL in order to add the info routine |
---|
| 16 | * used for debugging. |
---|
| 17 | */ |
---|
| 18 | |
---|
| 19 | #include <stdio.h> |
---|
| 20 | #include <stdlib.h> |
---|
| 21 | #include <string.h> |
---|
| 22 | |
---|
| 23 | #include <drvmgr/drvmgr.h> |
---|
| 24 | #include <drvmgr/ambapp_bus.h> |
---|
| 25 | |
---|
| 26 | #include <bsp.h> |
---|
| 27 | #include <ambapp.h> |
---|
| 28 | |
---|
| 29 | /*#define DEBUG 1*/ |
---|
| 30 | #define DBG(args...) |
---|
| 31 | /*#define DBG(args...) printk(args)*/ |
---|
| 32 | |
---|
| 33 | struct grlib_gptimer_regs { |
---|
| 34 | volatile unsigned int scaler_value; /* common timer registers */ |
---|
| 35 | volatile unsigned int scaler_reload; |
---|
| 36 | volatile unsigned int status; |
---|
| 37 | volatile unsigned int notused; |
---|
| 38 | }; |
---|
| 39 | |
---|
| 40 | /* AMBA IMPLEMENTATION */ |
---|
| 41 | |
---|
| 42 | int ambapp_bus_init1(struct drvmgr_bus *bus); |
---|
| 43 | int ambapp_bus_remove(struct drvmgr_bus *bus); |
---|
| 44 | int ambapp_unite(struct drvmgr_drv *drv, struct drvmgr_dev *dev); |
---|
| 45 | int ambapp_int_register(struct drvmgr_dev *dev, int index, const char *info, drvmgr_isr isr, void *arg); |
---|
| 46 | int ambapp_int_unregister(struct drvmgr_dev *dev, int index, drvmgr_isr isr, void *arg); |
---|
| 47 | int ambapp_int_clear(struct drvmgr_dev *dev, int index); |
---|
| 48 | int ambapp_int_mask(struct drvmgr_dev *dev, int index); |
---|
| 49 | int ambapp_int_unmask(struct drvmgr_dev *dev, int index); |
---|
| 50 | int ambapp_get_params(struct drvmgr_dev *dev, struct drvmgr_bus_params *params); |
---|
| 51 | int ambapp_bus_freq_get( |
---|
| 52 | struct drvmgr_dev *dev, |
---|
| 53 | int options, |
---|
| 54 | unsigned int *freq_hz); |
---|
| 55 | void ambapp_dev_info(struct drvmgr_dev *, void (*print)(void *p, char *str), void *p); |
---|
| 56 | |
---|
| 57 | struct drvmgr_bus_ops ambapp_bus_ops = |
---|
| 58 | { |
---|
| 59 | .init = |
---|
| 60 | { |
---|
| 61 | /* init1 */ ambapp_bus_init1, |
---|
| 62 | /* init2 */ NULL, |
---|
| 63 | /* init3 */ NULL, |
---|
| 64 | /* init4 */ NULL |
---|
| 65 | }, |
---|
| 66 | .remove = ambapp_bus_remove, |
---|
| 67 | .unite = ambapp_unite, |
---|
| 68 | .int_register = ambapp_int_register, |
---|
| 69 | .int_unregister = ambapp_int_unregister, |
---|
| 70 | .int_clear = ambapp_int_clear, |
---|
| 71 | .int_mask = ambapp_int_mask, |
---|
| 72 | .int_unmask = ambapp_int_unmask, |
---|
| 73 | .get_params = ambapp_get_params, |
---|
| 74 | .freq_get = ambapp_bus_freq_get, |
---|
| 75 | #ifdef AMBAPPBUS_INFO_AVAIL |
---|
| 76 | .info_dev = ambapp_dev_info, |
---|
| 77 | #endif |
---|
| 78 | }; |
---|
| 79 | |
---|
| 80 | struct ambapp_priv { |
---|
| 81 | struct ambapp_config *config; |
---|
| 82 | }; |
---|
| 83 | |
---|
| 84 | int ambapp_unite(struct drvmgr_drv *drv, struct drvmgr_dev *dev) |
---|
| 85 | { |
---|
| 86 | struct amba_drv_info *adrv; |
---|
| 87 | struct amba_dev_id *id; |
---|
| 88 | struct amba_dev_info *amba; |
---|
| 89 | |
---|
| 90 | if ( !drv || !dev || !dev->parent ) |
---|
| 91 | return 0; |
---|
| 92 | |
---|
| 93 | if ( ! (((drv->bus_type == DRVMGR_BUS_TYPE_AMBAPP) && (dev->parent->bus_type == DRVMGR_BUS_TYPE_AMBAPP)) || |
---|
| 94 | ((drv->bus_type == DRVMGR_BUS_TYPE_AMBAPP_RMAP) && (dev->parent->bus_type == DRVMGR_BUS_TYPE_AMBAPP_RMAP)) || |
---|
| 95 | ((drv->bus_type == DRVMGR_BUS_TYPE_AMBAPP_DIST) && (dev->parent->bus_type == DRVMGR_BUS_TYPE_AMBAPP_DIST))) |
---|
| 96 | ) { |
---|
| 97 | return 0; |
---|
| 98 | } |
---|
| 99 | |
---|
| 100 | amba = (struct amba_dev_info *)dev->businfo; |
---|
| 101 | if ( !amba ) |
---|
| 102 | return 0; |
---|
| 103 | |
---|
| 104 | adrv = (struct amba_drv_info *)drv; |
---|
| 105 | id = adrv->ids; |
---|
| 106 | if ( !id ) |
---|
| 107 | return 0; |
---|
| 108 | while( id->vendor != 0 ) { |
---|
| 109 | if ( (id->vendor == amba->id.vendor) && |
---|
| 110 | (id->device == amba->id.device) ) { |
---|
| 111 | /* Unite device and driver */ |
---|
| 112 | DBG("DRV 0x%x and DEV 0x%x united\n", (unsigned int)drv, (unsigned int)dev); |
---|
| 113 | return 1; |
---|
| 114 | } |
---|
| 115 | id++; |
---|
| 116 | } |
---|
| 117 | |
---|
| 118 | return 0; |
---|
| 119 | } |
---|
| 120 | |
---|
| 121 | static int ambapp_int_get(struct drvmgr_dev *dev, int index) |
---|
| 122 | { |
---|
| 123 | int irq; |
---|
| 124 | |
---|
| 125 | /* Relative (positive) or absolute (negative) IRQ number */ |
---|
| 126 | if ( index >= 0 ) { |
---|
| 127 | /* IRQ Index relative to Cores base IRQ */ |
---|
| 128 | |
---|
| 129 | /* Get Base IRQ */ |
---|
| 130 | irq = ((struct amba_dev_info *)dev->businfo)->info.irq; |
---|
| 131 | if ( irq < 0 ) |
---|
| 132 | return -1; |
---|
| 133 | irq += index; |
---|
| 134 | } else { |
---|
| 135 | /* Absolute IRQ number */ |
---|
| 136 | irq = -index; |
---|
| 137 | } |
---|
| 138 | return irq; |
---|
| 139 | } |
---|
| 140 | |
---|
| 141 | int ambapp_int_register( |
---|
| 142 | struct drvmgr_dev *dev, |
---|
| 143 | int index, |
---|
| 144 | const char *info, |
---|
| 145 | drvmgr_isr isr, |
---|
| 146 | void *arg) |
---|
| 147 | { |
---|
| 148 | struct drvmgr_dev *busdev; |
---|
| 149 | struct ambapp_priv *priv; |
---|
| 150 | int irq; |
---|
| 151 | |
---|
| 152 | busdev = dev->parent->dev; |
---|
| 153 | priv = dev->parent->priv; |
---|
| 154 | |
---|
| 155 | /* Get IRQ number from index and device information */ |
---|
| 156 | irq = ambapp_int_get(dev, index); |
---|
| 157 | if ( irq < 0 ) |
---|
| 158 | return DRVMGR_EINVAL; |
---|
| 159 | |
---|
| 160 | DBG("Register interrupt on 0x%x for dev 0x%x (IRQ: %d)\n", (unsigned int)busdev, (unsigned int)dev, irq); |
---|
| 161 | |
---|
| 162 | if ( priv->config->ops->int_register ) { |
---|
| 163 | /* Let device override driver default */ |
---|
| 164 | return priv->config->ops->int_register(dev, irq, info, isr, arg); |
---|
| 165 | } else { |
---|
| 166 | return DRVMGR_ENOSYS; |
---|
| 167 | } |
---|
| 168 | } |
---|
| 169 | |
---|
| 170 | int ambapp_int_unregister( |
---|
| 171 | struct drvmgr_dev *dev, |
---|
| 172 | int index, |
---|
| 173 | drvmgr_isr isr, |
---|
| 174 | void *arg) |
---|
| 175 | { |
---|
| 176 | struct drvmgr_dev *busdev; |
---|
| 177 | struct ambapp_priv *priv; |
---|
| 178 | int irq; |
---|
| 179 | |
---|
| 180 | busdev = dev->parent->dev; |
---|
| 181 | priv = dev->parent->priv; |
---|
| 182 | |
---|
| 183 | /* Get IRQ number from index and device information */ |
---|
| 184 | irq = ambapp_int_get(dev, index); |
---|
| 185 | if ( irq < 0 ) |
---|
| 186 | return DRVMGR_EINVAL; |
---|
| 187 | |
---|
| 188 | DBG("Unregister interrupt on 0x%x for dev 0x%x (IRQ: %d)\n", (unsigned int)busdev, (unsigned int)dev, irq); |
---|
| 189 | |
---|
| 190 | if ( priv->config->ops->int_unregister ) { |
---|
| 191 | /* Let device override driver default */ |
---|
| 192 | return priv->config->ops->int_unregister(dev, irq, isr, arg); |
---|
| 193 | } else { |
---|
| 194 | return DRVMGR_ENOSYS; |
---|
| 195 | } |
---|
| 196 | } |
---|
| 197 | |
---|
| 198 | int ambapp_int_clear( |
---|
| 199 | struct drvmgr_dev *dev, |
---|
| 200 | int index) |
---|
| 201 | { |
---|
| 202 | struct drvmgr_dev *busdev; |
---|
| 203 | struct ambapp_priv *priv; |
---|
| 204 | int irq; |
---|
| 205 | |
---|
| 206 | busdev = dev->parent->dev; |
---|
| 207 | priv = dev->parent->priv; |
---|
| 208 | |
---|
| 209 | /* Get IRQ number from index and device information */ |
---|
| 210 | irq = ambapp_int_get(dev, index); |
---|
| 211 | if ( irq < 0 ) |
---|
| 212 | return -1; |
---|
| 213 | |
---|
| 214 | DBG("Clear interrupt on 0x%x for dev 0x%x (IRQ: %d)\n", (unsigned int)busdev, (unsigned int)dev, irq); |
---|
| 215 | |
---|
| 216 | if ( priv->config->ops->int_clear ) { |
---|
| 217 | /* Let device override driver default */ |
---|
| 218 | return priv->config->ops->int_clear(dev, irq); |
---|
| 219 | } else { |
---|
| 220 | return DRVMGR_ENOSYS; |
---|
| 221 | } |
---|
| 222 | } |
---|
| 223 | |
---|
| 224 | int ambapp_int_mask( |
---|
| 225 | struct drvmgr_dev *dev, |
---|
| 226 | int index) |
---|
| 227 | { |
---|
| 228 | struct drvmgr_dev *busdev; |
---|
| 229 | struct ambapp_priv *priv; |
---|
| 230 | int irq; |
---|
| 231 | |
---|
| 232 | busdev = dev->parent->dev; |
---|
| 233 | priv = dev->parent->priv; |
---|
| 234 | |
---|
| 235 | /* Get IRQ number from index and device information */ |
---|
| 236 | irq = ambapp_int_get(dev, index); |
---|
| 237 | if ( irq < 0 ) |
---|
| 238 | return -1; |
---|
| 239 | |
---|
| 240 | DBG("MASK interrupt on 0x%x for dev 0x%x (IRQ: %d)\n", (unsigned int)busdev, (unsigned int)dev, irq); |
---|
| 241 | |
---|
| 242 | if ( priv->config->ops->int_mask ) { |
---|
| 243 | /* Let device override driver default */ |
---|
| 244 | return priv->config->ops->int_mask(dev, irq); |
---|
| 245 | } else { |
---|
| 246 | return DRVMGR_ENOSYS; |
---|
| 247 | } |
---|
| 248 | } |
---|
| 249 | |
---|
| 250 | int ambapp_int_unmask( |
---|
| 251 | struct drvmgr_dev *dev, |
---|
| 252 | int index) |
---|
| 253 | { |
---|
| 254 | struct drvmgr_dev *busdev; |
---|
| 255 | struct ambapp_priv *priv; |
---|
| 256 | int irq; |
---|
| 257 | |
---|
| 258 | busdev = dev->parent->dev; |
---|
| 259 | priv = dev->parent->priv; |
---|
| 260 | |
---|
| 261 | /* Get IRQ number from index and device information */ |
---|
| 262 | irq = ambapp_int_get(dev, index); |
---|
| 263 | if ( irq < 0 ) |
---|
| 264 | return DRVMGR_EINVAL; |
---|
| 265 | |
---|
| 266 | DBG("UNMASK interrupt on 0x%x for dev 0x%x (IRQ: %d)\n", (unsigned int)busdev, (unsigned int)dev, irq); |
---|
| 267 | |
---|
| 268 | if ( priv->config->ops->int_unmask ) { |
---|
| 269 | /* Let device override driver default */ |
---|
| 270 | return priv->config->ops->int_unmask(dev, irq); |
---|
| 271 | } else { |
---|
| 272 | return DRVMGR_ENOSYS; |
---|
| 273 | } |
---|
| 274 | } |
---|
| 275 | |
---|
| 276 | /* Assign frequency to an AMBA Bus */ |
---|
| 277 | void ambapp_bus_freq_register( |
---|
| 278 | struct drvmgr_dev *dev, |
---|
| 279 | int amba_interface, |
---|
| 280 | unsigned int freq_hz |
---|
| 281 | ) |
---|
| 282 | { |
---|
| 283 | struct ambapp_priv *priv = (struct ambapp_priv *)dev->parent->priv; |
---|
| 284 | struct ambapp_dev *adev; |
---|
| 285 | struct amba_dev_info *pnp = dev->businfo; |
---|
| 286 | |
---|
| 287 | if ( freq_hz == 0 ) |
---|
| 288 | return; |
---|
| 289 | |
---|
| 290 | if ( amba_interface == DEV_AHB_MST ) { |
---|
| 291 | adev = (struct ambapp_dev *) |
---|
| 292 | ((unsigned int)pnp->info.ahb_mst - |
---|
| 293 | sizeof(struct ambapp_dev)); |
---|
| 294 | } else if ( amba_interface == DEV_AHB_SLV ) { |
---|
| 295 | adev = (struct ambapp_dev *) |
---|
| 296 | ((unsigned int)pnp->info.ahb_slv - |
---|
| 297 | sizeof(struct ambapp_dev)); |
---|
| 298 | } else if ( amba_interface == DEV_APB_SLV ) { |
---|
| 299 | adev = (struct ambapp_dev *) |
---|
| 300 | ((unsigned int)pnp->info.apb_slv - |
---|
| 301 | sizeof(struct ambapp_dev)); |
---|
| 302 | } else { |
---|
| 303 | return; |
---|
| 304 | } |
---|
| 305 | |
---|
| 306 | /* Calculate Top bus frequency from lower part. The frequency comes |
---|
| 307 | * from some kind of hardware able to report local bus frequency. |
---|
| 308 | */ |
---|
| 309 | ambapp_freq_init(priv->config->abus, adev, freq_hz); |
---|
| 310 | } |
---|
| 311 | |
---|
| 312 | int ambapp_bus_freq_get( |
---|
| 313 | struct drvmgr_dev *dev, |
---|
| 314 | int options, |
---|
| 315 | unsigned int *freq_hz) |
---|
| 316 | { |
---|
| 317 | struct ambapp_priv *priv = (struct ambapp_priv *)dev->parent->priv; |
---|
| 318 | struct ambapp_dev *adev; |
---|
| 319 | struct amba_dev_info *pnp = dev->businfo; |
---|
| 320 | |
---|
| 321 | if ( options == DEV_AHB_MST ) { |
---|
| 322 | adev = (struct ambapp_dev *) |
---|
| 323 | ((unsigned int)pnp->info.ahb_mst - |
---|
| 324 | sizeof(struct ambapp_dev)); |
---|
| 325 | } else if ( options == DEV_AHB_SLV ) { |
---|
| 326 | adev = (struct ambapp_dev *) |
---|
| 327 | ((unsigned int)pnp->info.ahb_slv - |
---|
| 328 | sizeof(struct ambapp_dev)); |
---|
| 329 | } else if ( options == DEV_APB_SLV ) { |
---|
| 330 | adev = (struct ambapp_dev *) |
---|
| 331 | ((unsigned int)pnp->info.apb_slv - |
---|
| 332 | sizeof(struct ambapp_dev)); |
---|
| 333 | } else { |
---|
| 334 | *freq_hz = 0; |
---|
| 335 | return -1; |
---|
| 336 | } |
---|
| 337 | |
---|
| 338 | /* Calculate core/bus frequency from top most bus frequency. */ |
---|
| 339 | *freq_hz = ambapp_freq_get(priv->config->abus, adev); |
---|
| 340 | if ( *freq_hz == 0 ) |
---|
| 341 | return -1; |
---|
| 342 | return 0; |
---|
| 343 | } |
---|
| 344 | |
---|
| 345 | int ambapp_get_params(struct drvmgr_dev *dev, struct drvmgr_bus_params *params) |
---|
| 346 | { |
---|
| 347 | struct ambapp_priv *priv = dev->parent->priv; |
---|
| 348 | |
---|
| 349 | if ( priv->config->ops->get_params ) { |
---|
| 350 | /* Let device override driver default */ |
---|
| 351 | return priv->config->ops->get_params(dev, params); |
---|
| 352 | } else { |
---|
| 353 | return -1; |
---|
| 354 | } |
---|
| 355 | } |
---|
| 356 | |
---|
| 357 | #ifdef AMBAPPBUS_INFO_AVAIL |
---|
| 358 | void ambapp_dev_info( |
---|
| 359 | struct drvmgr_dev *dev, |
---|
| 360 | void (*print_line)(void *p, char *str), |
---|
| 361 | void *p) |
---|
| 362 | { |
---|
| 363 | struct amba_dev_info *devinfo; |
---|
| 364 | struct ambapp_core *core; |
---|
| 365 | char buf[64]; |
---|
| 366 | int ver, i; |
---|
| 367 | char *str1, *str2, *str3; |
---|
| 368 | unsigned int ahbmst_freq, ahbslv_freq, apbslv_freq; |
---|
| 369 | |
---|
| 370 | if (!dev) |
---|
| 371 | return; |
---|
| 372 | |
---|
| 373 | devinfo = (struct amba_dev_info *)dev->businfo; |
---|
| 374 | if (!devinfo) |
---|
| 375 | return; |
---|
| 376 | core = &devinfo->info; |
---|
| 377 | |
---|
| 378 | print_line(p, "AMBA PnP DEVICE"); |
---|
| 379 | |
---|
| 380 | str1 = ambapp_vendor_id2str(devinfo->id.vendor); |
---|
| 381 | if (str1 == NULL) |
---|
| 382 | str1 = "unknown"; |
---|
| 383 | sprintf(buf, "VENDOR ID: 0x%04x (%s)", devinfo->id.vendor, str1); |
---|
| 384 | print_line(p, buf); |
---|
| 385 | |
---|
| 386 | str1 = ambapp_device_id2str(devinfo->id.vendor, devinfo->id.device); |
---|
| 387 | if (str1 == NULL) |
---|
| 388 | str1 = "unknown"; |
---|
| 389 | sprintf(buf, "DEVICE ID: 0x%04x (%s)", devinfo->id.device, str1); |
---|
| 390 | print_line(p, buf); |
---|
| 391 | |
---|
| 392 | ahbmst_freq = ahbslv_freq = apbslv_freq = 0; |
---|
| 393 | ver = 0; |
---|
| 394 | str1 = str2 = str3 = ""; |
---|
| 395 | if (core->ahb_mst) { |
---|
| 396 | str1 = "AHBMST "; |
---|
| 397 | ver = core->ahb_mst->ver; |
---|
| 398 | ambapp_bus_freq_get(dev, DEV_AHB_MST, &ahbmst_freq); |
---|
| 399 | } |
---|
| 400 | if (core->ahb_slv) { |
---|
| 401 | str2 = "AHBSLV "; |
---|
| 402 | ver = core->ahb_slv->ver; |
---|
| 403 | ambapp_bus_freq_get(dev, DEV_AHB_SLV, &ahbslv_freq); |
---|
| 404 | } |
---|
| 405 | if (core->apb_slv) { |
---|
| 406 | str3 = "APBSLV"; |
---|
| 407 | ver = core->apb_slv->ver; |
---|
| 408 | ambapp_bus_freq_get(dev, DEV_APB_SLV, &apbslv_freq); |
---|
| 409 | } |
---|
| 410 | |
---|
| 411 | sprintf(buf, "IRQ: %d", ambapp_int_get(dev, 0)); |
---|
| 412 | print_line(p, buf); |
---|
| 413 | |
---|
| 414 | sprintf(buf, "VERSION: 0x%x", ver); |
---|
| 415 | print_line(p, buf); |
---|
| 416 | |
---|
| 417 | sprintf(buf, "ambapp_core: %p", core); |
---|
| 418 | print_line(p, buf); |
---|
| 419 | |
---|
| 420 | sprintf(buf, "interfaces: %s%s%s", str1, str2, str3); |
---|
| 421 | print_line(p, buf); |
---|
| 422 | |
---|
| 423 | if (ahbmst_freq != 0) { |
---|
| 424 | sprintf(buf, "AHBMST FREQ: %dkHz", ahbmst_freq/1000); |
---|
| 425 | print_line(p, buf); |
---|
| 426 | } |
---|
| 427 | |
---|
| 428 | if (ahbslv_freq != 0) { |
---|
| 429 | sprintf(buf, "AHBSLV FREQ: %dkHz", ahbslv_freq/1000); |
---|
| 430 | print_line(p, buf); |
---|
| 431 | } |
---|
| 432 | |
---|
| 433 | if (apbslv_freq != 0) { |
---|
| 434 | sprintf(buf, "APBSLV FREQ: %dkHz", apbslv_freq/1000); |
---|
| 435 | print_line(p, buf); |
---|
| 436 | } |
---|
| 437 | |
---|
| 438 | if (core->ahb_slv) { |
---|
| 439 | for(i=0; i<4; i++) { |
---|
| 440 | if (core->ahb_slv->type[i] == AMBA_TYPE_AHBIO) |
---|
| 441 | str1 = " ahbio"; |
---|
| 442 | else if (core->ahb_slv->type[i] == AMBA_TYPE_MEM) |
---|
| 443 | str1 = "ahbmem"; |
---|
| 444 | else |
---|
| 445 | continue; |
---|
| 446 | sprintf(buf, " %s[%d]: 0x%08x-0x%08x", str1, i, |
---|
| 447 | core->ahb_slv->start[i], |
---|
| 448 | core->ahb_slv->start[i]+core->ahb_slv->mask[i]-1); |
---|
| 449 | print_line(p, buf); |
---|
| 450 | } |
---|
| 451 | } |
---|
| 452 | if (core->apb_slv) { |
---|
| 453 | sprintf(buf, " apb: 0x%08x-0x%08x", |
---|
| 454 | core->apb_slv->start, |
---|
| 455 | core->apb_slv->start + core->apb_slv->mask - 1); |
---|
| 456 | print_line(p, buf); |
---|
| 457 | } |
---|
| 458 | } |
---|
| 459 | #endif |
---|
| 460 | |
---|
| 461 | /* Fix device in last stage */ |
---|
| 462 | int ambapp_dev_fixup(struct drvmgr_dev *dev, struct amba_dev_info *pnp) |
---|
| 463 | { |
---|
| 464 | /* OCCAN speciality: |
---|
| 465 | * Mulitple cores are supported through the same amba AHB interface. |
---|
| 466 | * The number of "sub cores" can be detected by decoding the AMBA |
---|
| 467 | * Plug&Play version information. verion = ncores. A maximum of 8 |
---|
| 468 | * sub cores are supported, each separeated with 0x100 inbetween. |
---|
| 469 | * |
---|
| 470 | * Now, lets detect sub cores. |
---|
| 471 | */ |
---|
| 472 | if ( (pnp->info.device == GAISLER_CANAHB) && (pnp->info.vendor == VENDOR_GAISLER) ) { |
---|
| 473 | struct drvmgr_dev *newdev; |
---|
| 474 | struct amba_dev_info *pnpinfo; |
---|
| 475 | int subcores; |
---|
| 476 | int core; |
---|
| 477 | |
---|
| 478 | subcores = (pnp->info.ahb_slv->ver & 0x7) + 1; |
---|
| 479 | for(core = 1; core < subcores; core++) { |
---|
| 480 | drvmgr_alloc_dev(&newdev, sizeof(*pnpinfo)); |
---|
| 481 | memcpy(newdev, dev, sizeof(*newdev)); |
---|
| 482 | pnpinfo = (struct amba_dev_info *)(newdev+1); |
---|
| 483 | memcpy(pnpinfo, pnp, sizeof(*pnp)); |
---|
| 484 | pnpinfo->info.index = core; |
---|
| 485 | pnpinfo->info.irq += core; |
---|
| 486 | newdev->businfo = (void *)pnpinfo; |
---|
| 487 | |
---|
| 488 | /* Register device */ |
---|
| 489 | drvmgr_dev_register(newdev); |
---|
| 490 | } |
---|
| 491 | } else if ( (pnp->info.device == GAISLER_GPIO) && (pnp->info.vendor == VENDOR_GAISLER) ) { |
---|
| 492 | /* PIO[N] is connected to IRQ[N]. */ |
---|
| 493 | pnp->info.irq = 0; |
---|
| 494 | } |
---|
| 495 | return 0; |
---|
| 496 | } |
---|
| 497 | |
---|
| 498 | struct ambapp_dev_reg_struct { |
---|
| 499 | struct ambapp_bus *abus; |
---|
| 500 | struct drvmgr_bus *bus; |
---|
| 501 | struct ambapp_dev *ahb_mst; |
---|
| 502 | struct ambapp_dev *ahb_slv; |
---|
| 503 | struct ambapp_dev *apb_slv; |
---|
| 504 | }; |
---|
| 505 | |
---|
| 506 | void ambapp_core_register( |
---|
| 507 | struct ambapp_dev *ahb_mst, |
---|
| 508 | struct ambapp_dev *ahb_slv, |
---|
| 509 | struct ambapp_dev *apb_slv, |
---|
| 510 | struct ambapp_dev_reg_struct *arg |
---|
| 511 | ) |
---|
| 512 | { |
---|
| 513 | struct drvmgr_dev *newdev; |
---|
| 514 | struct amba_dev_info *pnpinfo; |
---|
| 515 | unsigned short device; |
---|
| 516 | unsigned char vendor; |
---|
| 517 | int namelen; |
---|
| 518 | char buf[64]; |
---|
| 519 | |
---|
| 520 | if ( ahb_mst ) { |
---|
| 521 | device = ahb_mst->device; |
---|
| 522 | vendor = ahb_mst->vendor; |
---|
| 523 | }else if ( ahb_slv ) { |
---|
| 524 | device = ahb_slv->device; |
---|
| 525 | vendor = ahb_slv->vendor; |
---|
| 526 | }else if( apb_slv ) { |
---|
| 527 | device = apb_slv->device; |
---|
| 528 | vendor = apb_slv->vendor; |
---|
| 529 | } else { |
---|
| 530 | DBG("NO DEV!\n"); |
---|
| 531 | return; |
---|
| 532 | } |
---|
| 533 | |
---|
| 534 | DBG("CORE REGISTER DEV [%x:%x] MST: 0x%x, SLV: 0x%x, APB: 0x%x\n", vendor, device, (unsigned int)ahb_mst, (unsigned int)ahb_slv, (unsigned int)apb_slv); |
---|
| 535 | |
---|
| 536 | /* Get unique device name from AMBA data base by combining VENDOR and |
---|
| 537 | * DEVICE short names |
---|
| 538 | */ |
---|
| 539 | namelen = ambapp_vendev_id2str(vendor, device, buf); |
---|
| 540 | |
---|
| 541 | /* Allocate a device */ |
---|
| 542 | drvmgr_alloc_dev(&newdev, sizeof(struct amba_dev_info) + namelen); |
---|
| 543 | pnpinfo = (struct amba_dev_info *)(newdev + 1); |
---|
| 544 | newdev->parent = arg->bus; /* Ourselfs */ |
---|
| 545 | newdev->minor_drv = 0; |
---|
| 546 | newdev->minor_bus = 0; |
---|
| 547 | newdev->priv = NULL; |
---|
| 548 | newdev->drv = NULL; |
---|
| 549 | if (namelen > 0) { |
---|
| 550 | newdev->name = (char *)(pnpinfo + 1); |
---|
| 551 | strcpy(newdev->name, buf); |
---|
| 552 | } else { |
---|
| 553 | newdev->name = NULL; |
---|
| 554 | } |
---|
| 555 | newdev->next_in_drv = NULL; |
---|
| 556 | newdev->bus = NULL; |
---|
| 557 | |
---|
| 558 | /* Init PnP information, Assign Core interfaces with this device */ |
---|
| 559 | pnpinfo->id.vendor = vendor; |
---|
| 560 | pnpinfo->id.device = device; |
---|
| 561 | pnpinfo->info.vendor = vendor; |
---|
| 562 | pnpinfo->info.device = device; |
---|
| 563 | pnpinfo->info.index = 0; |
---|
| 564 | if ( ahb_mst ) { |
---|
| 565 | pnpinfo->info.ahb_mst = (struct ambapp_ahb_info *) |
---|
| 566 | ahb_mst->devinfo; |
---|
| 567 | ambapp_alloc_dev(ahb_mst, (void *)newdev); |
---|
| 568 | if ( pnpinfo->info.ahb_mst->irq ) |
---|
| 569 | pnpinfo->info.irq = pnpinfo->info.ahb_mst->irq; |
---|
| 570 | } |
---|
| 571 | if ( ahb_slv ) { |
---|
| 572 | pnpinfo->info.ahb_slv = (struct ambapp_ahb_info *) |
---|
| 573 | ahb_slv->devinfo; |
---|
| 574 | ambapp_alloc_dev(ahb_slv, (void *)newdev); |
---|
| 575 | if ( pnpinfo->info.ahb_slv->irq ) |
---|
| 576 | pnpinfo->info.irq = pnpinfo->info.ahb_slv->irq; |
---|
| 577 | } |
---|
| 578 | if ( apb_slv ) { |
---|
| 579 | pnpinfo->info.apb_slv = (struct ambapp_apb_info *) |
---|
| 580 | apb_slv->devinfo; |
---|
| 581 | ambapp_alloc_dev(apb_slv, (void *)newdev); |
---|
| 582 | if ( pnpinfo->info.apb_slv->irq ) |
---|
| 583 | pnpinfo->info.irq = pnpinfo->info.apb_slv->irq; |
---|
| 584 | } |
---|
| 585 | if ( pnpinfo->info.irq == 0 ) |
---|
| 586 | pnpinfo->info.irq = -1; /* indicate no IRQ */ |
---|
| 587 | |
---|
| 588 | /* Connect device with PnP information */ |
---|
| 589 | newdev->businfo = (void *)pnpinfo; |
---|
| 590 | |
---|
| 591 | ambapp_dev_fixup(newdev, pnpinfo); |
---|
| 592 | |
---|
| 593 | /* Register New Device */ |
---|
| 594 | drvmgr_dev_register(newdev); |
---|
| 595 | } |
---|
| 596 | |
---|
| 597 | /* Register one AMBA device */ |
---|
| 598 | int ambapp_dev_register(struct ambapp_dev *dev, int index, void *arg) |
---|
| 599 | { |
---|
| 600 | struct ambapp_dev_reg_struct *p = arg; |
---|
| 601 | |
---|
| 602 | #ifdef DEBUG |
---|
| 603 | char *type; |
---|
| 604 | |
---|
| 605 | if ( dev->dev_type == DEV_AHB_MST ) |
---|
| 606 | type = "AHB MST"; |
---|
| 607 | else if ( dev->dev_type == DEV_AHB_SLV ) |
---|
| 608 | type = "AHB SLV"; |
---|
| 609 | else if ( dev->dev_type == DEV_APB_SLV ) |
---|
| 610 | type = "APB SLV"; |
---|
| 611 | |
---|
| 612 | DBG("Found [%d:%x:%x], %s\n", index, dev->vendor, dev->device, type); |
---|
| 613 | #endif |
---|
| 614 | |
---|
| 615 | if ( dev->dev_type == DEV_AHB_MST ) { |
---|
| 616 | if ( p->ahb_mst ) { |
---|
| 617 | /* This should not happen */ |
---|
| 618 | printk("ambapp_dev_register: ahb_mst not NULL!\n"); |
---|
| 619 | exit(1); |
---|
| 620 | } |
---|
| 621 | |
---|
| 622 | /* Remember AHB Master */ |
---|
| 623 | p->ahb_mst = dev; |
---|
| 624 | |
---|
| 625 | /* Find AHB Slave and APB slave for this Core */ |
---|
| 626 | ambapp_for_each(p->abus, (OPTIONS_AHB_SLVS|OPTIONS_APB_SLVS|OPTIONS_FREE), dev->vendor, dev->device, ambapp_dev_register, p); |
---|
| 627 | |
---|
| 628 | ambapp_core_register(p->ahb_mst, p->ahb_slv, p->apb_slv, p); |
---|
| 629 | p->ahb_mst = p->ahb_slv = p->apb_slv = NULL; |
---|
| 630 | return 0; |
---|
| 631 | |
---|
| 632 | } else if ( dev->dev_type == DEV_AHB_SLV ) { |
---|
| 633 | if ( p->ahb_slv ) { |
---|
| 634 | /* Already got our AHB Slave interface */ |
---|
| 635 | return 0; |
---|
| 636 | } |
---|
| 637 | |
---|
| 638 | /* Remember AHB Slave */ |
---|
| 639 | p->ahb_slv = dev; |
---|
| 640 | |
---|
| 641 | if ( p->ahb_mst ) { |
---|
| 642 | /* Continue searching for APB Slave */ |
---|
| 643 | return 0; |
---|
| 644 | } else { |
---|
| 645 | /* Find APB Slave interface for this Core */ |
---|
| 646 | ambapp_for_each(p->abus, (OPTIONS_APB_SLVS|OPTIONS_FREE), dev->vendor, dev->device, ambapp_dev_register, p); |
---|
| 647 | |
---|
| 648 | ambapp_core_register(p->ahb_mst, p->ahb_slv, p->apb_slv, p); |
---|
| 649 | p->ahb_mst = p->ahb_slv = p->apb_slv = NULL; |
---|
| 650 | return 0; |
---|
| 651 | } |
---|
| 652 | } else if ( dev->dev_type == DEV_APB_SLV ) { |
---|
| 653 | if ( p->apb_slv ) { |
---|
| 654 | /* This should not happen */ |
---|
| 655 | printk("ambapp_dev_register: apb_slv not NULL!\n"); |
---|
| 656 | exit(1); |
---|
| 657 | } |
---|
| 658 | /* Remember APB Slave */ |
---|
| 659 | p->apb_slv = dev; |
---|
| 660 | |
---|
| 661 | if ( p->ahb_mst || p->ahb_slv ) { |
---|
| 662 | /* Stop scanning */ |
---|
| 663 | return 1; |
---|
| 664 | } else { |
---|
| 665 | ambapp_core_register(p->ahb_mst, p->ahb_slv, p->apb_slv, p); |
---|
| 666 | p->ahb_mst = p->ahb_slv = p->apb_slv = NULL; |
---|
| 667 | return 0; |
---|
| 668 | } |
---|
| 669 | } |
---|
| 670 | |
---|
| 671 | return 0; |
---|
| 672 | } |
---|
| 673 | |
---|
| 674 | /* Register all AMBA devices available on the AMBAPP bus */ |
---|
| 675 | int ambapp_ids_register(struct drvmgr_bus *bus) |
---|
| 676 | { |
---|
| 677 | struct ambapp_priv *priv = bus->priv; |
---|
| 678 | struct ambapp_bus *abus; |
---|
| 679 | struct ambapp_dev_reg_struct arg; |
---|
| 680 | |
---|
| 681 | DBG("ambapp_ids_register:\n"); |
---|
| 682 | |
---|
| 683 | memset(&arg, 0, sizeof(arg)); |
---|
| 684 | |
---|
| 685 | abus = priv->config->abus; |
---|
| 686 | arg.abus = abus; |
---|
| 687 | arg.bus = bus; |
---|
| 688 | |
---|
| 689 | /* Combine the AHB MST, AHB SLV and APB SLV interfaces of a core. A core has often more than |
---|
| 690 | * one interface. A core can not have more than one interface of the same type. |
---|
| 691 | */ |
---|
| 692 | ambapp_for_each(abus, (OPTIONS_ALL_DEVS|OPTIONS_FREE), -1, -1, ambapp_dev_register, &arg); |
---|
| 693 | |
---|
| 694 | #ifdef DEBUG |
---|
| 695 | ambapp_print(abus->root, 1); |
---|
| 696 | #endif |
---|
| 697 | |
---|
| 698 | return DRVMGR_OK; |
---|
| 699 | } |
---|
| 700 | |
---|
| 701 | /*** DEVICE FUNCTIONS ***/ |
---|
| 702 | |
---|
| 703 | int ambapp_bus_register(struct drvmgr_dev *dev, struct ambapp_config *config) |
---|
| 704 | { |
---|
| 705 | struct ambapp_priv *priv; |
---|
| 706 | |
---|
| 707 | if ( !config || !config->ops ) |
---|
| 708 | return DRVMGR_OK; |
---|
| 709 | |
---|
| 710 | DBG("AMBAPP BUS: initializing\n"); |
---|
| 711 | |
---|
| 712 | /* Register BUS */ |
---|
| 713 | drvmgr_alloc_bus(&dev->bus, sizeof(struct ambapp_priv)); |
---|
| 714 | priv = (struct ambapp_priv *)(dev->bus + 1); |
---|
| 715 | priv->config = config; |
---|
| 716 | if ( priv->config->bus_type == DRVMGR_BUS_TYPE_AMBAPP_DIST ) |
---|
| 717 | dev->bus->bus_type = DRVMGR_BUS_TYPE_AMBAPP_DIST; |
---|
| 718 | else if ( priv->config->bus_type == DRVMGR_BUS_TYPE_AMBAPP_RMAP ) |
---|
| 719 | dev->bus->bus_type = DRVMGR_BUS_TYPE_AMBAPP_RMAP; |
---|
| 720 | else |
---|
| 721 | dev->bus->bus_type = DRVMGR_BUS_TYPE_AMBAPP; |
---|
| 722 | dev->bus->next = NULL; |
---|
| 723 | dev->bus->dev = dev; |
---|
| 724 | dev->bus->priv = priv; |
---|
| 725 | dev->bus->children = NULL; |
---|
| 726 | dev->bus->ops = &ambapp_bus_ops; |
---|
| 727 | dev->bus->funcs = config->funcs; |
---|
| 728 | dev->bus->dev_cnt = 0; |
---|
| 729 | dev->bus->reslist = NULL; |
---|
| 730 | dev->bus->maps_up = config->maps_up; |
---|
| 731 | dev->bus->maps_down = config->maps_down; |
---|
| 732 | |
---|
| 733 | /* Add resource configuration */ |
---|
| 734 | if ( priv->config->resources ) |
---|
| 735 | drvmgr_bus_res_add(dev->bus, priv->config->resources); |
---|
| 736 | |
---|
| 737 | drvmgr_bus_register(dev->bus); |
---|
| 738 | |
---|
| 739 | return DRVMGR_OK; |
---|
| 740 | } |
---|
| 741 | |
---|
| 742 | /*** BUS INITIALIZE FUNCTIONS ***/ |
---|
| 743 | |
---|
| 744 | /* Initialize the bus, register devices on this bus */ |
---|
| 745 | int ambapp_bus_init1(struct drvmgr_bus *bus) |
---|
| 746 | { |
---|
| 747 | /* Initialize the bus, register devices on this bus */ |
---|
| 748 | return ambapp_ids_register(bus); |
---|
| 749 | } |
---|
| 750 | |
---|
| 751 | int ambapp_bus_remove(struct drvmgr_bus *bus) |
---|
| 752 | { |
---|
| 753 | return DRVMGR_OK; |
---|
| 754 | } |
---|