| 418 | |
| 419 | /*! Get key value from the bus resources matching [device[drv_id, minor_bus], key name, key type] |
| 420 | * if no matching key NULL is returned. |
| 421 | * |
| 422 | * This is typically used by device drivers to find a particular device resource. |
| 423 | * |
| 424 | * \param dev The device to search resource for. |
| 425 | * \param key_name The key name to search for |
| 426 | * \param key_type The key type expected. |
| 427 | * \return Returns NULL if no value found matching Key Name and Key Type was found for device. |
| 428 | */ |
| 429 | extern union rtems_drvmgr_key_value *rtems_drvmgr_dev_key_get( |
| 430 | struct rtems_drvmgr_dev_info *dev, |
| 431 | char *key_name, |
| 432 | int key_type); |
| 433 | |
| 434 | }}} |
| 435 | = Driver skeleton = |
| 436 | |
| 437 | |
| 438 | Below is a selection from the GRSPW driver on how to create a driver using the Driver Manager structure. Below |
| 439 | is also a sniplet from AMBA PnP the bus driver that the GRSPW driver is intended for. |
| 440 | |
| 441 | {{{ |
| 442 | /* General part of a AMBA Plug & Play bus driver. */ |
| 443 | |
| 444 | #define DRIVER_AMBA_ID(vendor, device, subid) \ |
| 445 | DRIVER_SUBID_ADD((((unsigned long long)(vendor)<<32) | ((unsigned long long)(device))), subid) |
| 446 | |
| 447 | /*** Gaisler Hardware Device Driver definitions ***/ |
| 448 | #define DRIVER_AMBAPP_GAISLER_GRETH_ID DRIVER_AMBA_ID(VENDOR_GAISLER, GAISLER_ETHMAC, 0) |
| 449 | #define DRIVER_AMBAPP_GAISLER_GRETH_ID_ALL DRIVER_AMBA_ID(VENDOR_GAISLER, GAISLER_ETHMAC, -1) |
| 450 | |
| 451 | #define DRIVER_AMBAPP_GAISLER_GRSPW_ID DRIVER_AMBA_ID(VENDOR_GAISLER, GAISLER_SPW, 0) |
| 452 | #define DRIVER_AMBAPP_GAISLER_GRSPW_ID_ALL DRIVER_AMBA_ID(VENDOR_GAISLER, GAISLER_SPW, -1) |
| 453 | |
| 454 | #define DRIVER_AMBAPP_GAISLER_GRCAN_ID DRIVER_AMBA_ID(VENDOR_GAISLER, GAISLER_GRCAN, 0) |
| 455 | #define DRIVER_AMBAPP_GAISLER_GRCAN_ID_ALL DRIVER_AMBA_ID(VENDOR_GAISLER, GAISLER_GRCAN, -1) |
| 456 | |
| 457 | /*** ESA Hardware Device Driver definitions ***/ |
| 458 | #define DRIVER_AMBAPP_ESA_MCTRL_ID DRIVER_AMBA_ID(VENDOR_ESA, ESA_MCTRL, 0) |
| 459 | #define DRIVER_AMBAPP_ESA_MCTRL_ID_ALL DRIVER_AMBA_ID(VENDOR_ESA, ESA_MCTRL, -1) |
| 460 | #define DRIVER_AMBAPP_MCTRL_ID DRIVER_AMBAPP_ESA_MCTRL_ID |
| 461 | #define DRIVER_AMBAPP_MCTRL_ID_ALL DRIVER_AMBAPP_ESA_MCTRL_ID_ALL |
| 462 | |
| 463 | struct amba_dev_id { |
| 464 | unsigned short vendor; |
| 465 | unsigned short device; |
| 466 | /* Version ? */ |
| 467 | }; |
| 468 | |
| 469 | struct amba_drv_info { |
| 470 | struct rtems_drvmgr_drv_info general; /* General bus info */ |
| 471 | /* AMBA specific bus information */ |
| 472 | struct amba_dev_id *ids; /* Supported hardware */ |
| 473 | }; |
| 474 | |
| 475 | struct amba_dev_info { |
| 476 | struct amba_dev_id id; |
| 477 | struct ambapp_dev_info info; |
| 478 | }; |
| 479 | }}} |
| 480 | |
| 481 | {{{ |
| 482 | #define GRSPW_DRIVER_TABLE_ENTRY \ |
| 483 | { grspw_initialize, \ |
| 484 | grspw_open, \ |
| 485 | grspw_close, \ |
| 486 | grspw_read, \ |
| 487 | grspw_write, \ |
| 488 | grspw_control } |
| 489 | |
| 490 | static rtems_driver_address_table grspw_driver = GRSPW_DRIVER_TABLE_ENTRY; |
| 491 | static int grspw_driver_io_registered = 0; |
| 492 | static rtems_device_major_number grspw_driver_io_major = 0; |
| 493 | |
| 494 | /******************* Driver manager interface ***********************/ |
| 495 | |
| 496 | /* Driver prototypes */ |
| 497 | int grspw_register_io(rtems_device_major_number *m); |
| 498 | int grspw_device_init(GRSPW_DEV *pDev); |
| 499 | |
| 500 | rtems_status_code grspw_init1(struct rtems_drvmgr_dev_info *dev); |
| 501 | rtems_status_code grspw_init2(struct rtems_drvmgr_dev_info *dev); |
| 502 | |
| 503 | /* Driver operations */ |
| 504 | struct rtems_drvmgr_drv_ops grspw_ops = |
| 505 | { |
| 506 | grspw_init1, |
| 507 | grspw_init2, |
| 508 | NULL /* Driver does not support device unregister (delete) */ |
| 509 | }; |
| 510 | |
| 511 | /* Supported Hardware, Two different cores, however GRSPW2 is backwards compatible |
| 512 | * except for some small details. |
| 513 | */ |
| 514 | struct amba_dev_id grspw_ids[] = |
| 515 | { |
| 516 | {VENDOR_GAISLER, GAISLER_SPW}, |
| 517 | {VENDOR_GAISLER, GAISLER_SPW2}, |
| 518 | {0, 0} /* Mark end of table */ |
| 519 | }; |
| 520 | |
| 521 | struct amba_drv_info grspw_drv_info = |
| 522 | |
| 523 | { |
| 524 | /* General Driver Part that all drivers must define */ |
| 525 | { |
| 526 | NULL, /* Next driver */ |
| 527 | NULL, /* Device list */ |
| 528 | DRIVER_AMBAPP_GAISLER_GRSPW_ID, /* Driver ID */ |
| 529 | "GRSPW_DRV", /* Driver Name */ |
| 530 | DRVMGR_BUS_TYPE_AMBAPP, /* Bus Type */ |
| 531 | &grspw_ops, /* Driver oprtations supported */ |
| 532 | 0, /* No devices yet */ |
| 533 | }, |
| 534 | /* AMBA PnP Specific Driver Part that all AMBA PnP drivers must define */ |
| 535 | &grspw_ids[0] |
| 536 | }; |
| 537 | |
| 538 | void grspw_register_drv (void) |
| 539 | { |
| 540 | SPACEWIRE_DBG("Registering GRSPW driver\n"); |
| 541 | rtems_drvmgr_drv_register(&grspw_drv_info.general); |
| 542 | } |
| 543 | |
| 544 | rtems_status_code grspw_init1(struct rtems_drvmgr_dev_info *dev) |
| 545 | { |
| 546 | GRSPW_DEV *priv; |
| 547 | |
| 548 | SPACEWIRE_DBG("GRSPW[%d] on bus %s\n", dev->minor_drv, dev->parent->dev->name); |
| 549 | priv = dev->priv = malloc(sizeof(GRSPW_DEV)); |
| 550 | if ( !priv ) |
| 551 | return RTEMS_NO_MEMORY; |
| 552 | memset(priv, 0, sizeof(*priv)); |
| 553 | priv->dev = dev; |
| 554 | |
| 555 | /* This core will not find other cores, so we wait for init2() */ |
| 556 | |
| 557 | return RTEMS_SUCCESSFUL; |
| 558 | } |
| 559 | |
| 560 | rtems_status_code grspw_init2(struct rtems_drvmgr_dev_info *dev) |
| 561 | { |
| 562 | GRSPW_DEV *priv; |
| 563 | char prefix[16]; |
| 564 | rtems_status_code status; |
| 565 | |
| 566 | priv = dev->priv; |
| 567 | |
| 568 | /* Do initialization */ |
| 569 | |
| 570 | if ( grspw_driver_io_registered == 0) { |
| 571 | /* Register the I/O driver only once for all cores */ |
| 572 | if ( grspw_register_io(&grspw_driver_io_major) ) { |
| 573 | /* Failed to register I/O driver */ |
| 574 | free(dev->priv); |
| 575 | dev->priv = NULL; |
| 576 | return RTEMS_UNSATISFIED; |
| 577 | } |
| 578 | |
| 579 | grspw_driver_io_registered = 1; |
| 580 | } |
| 581 | |
| 582 | /* I/O system registered and initialized |
| 583 | * Now we take care of device initialization. |
| 584 | */ |
| 585 | |
| 586 | /* Get Bus frequency in Hz. The SpaceWire core is clocked by the bus */ |
| 587 | if ( rtems_drvmgr_get_freq(dev, &priv->core_freq_khz) ) { |
| 588 | return RTEMS_UNSATISFIED; |
| 589 | } |
| 590 | /* Convert from Hz -> kHz */ |
| 591 | priv->core_freq_khz = priv->core_freq_khz / 1000; |
| 592 | |
| 593 | /* Initialize Device */ |
| 594 | if ( grspw_device_init(priv) ) { |
| 595 | return RTEMS_UNSATISFIED; |
| 596 | } |
| 597 | |
| 598 | /* Get Filesystem name prefix, this is to separate GRSPW cores by bus, |
| 599 | * This might not always be wanted. |
| 600 | */ |
| 601 | prefix[0] = '\0'; |
| 602 | if ( rtems_drvmgr_get_dev_prefix(dev, prefix) ) { |
| 603 | /* Failed to get prefix, make sure of a unique FS name |
| 604 | * by using the driver minor. |
| 605 | */ |
| 606 | sprintf(priv->devName, "/dev/grspw%d", dev->minor_drv); |
| 607 | } else { |
| 608 | /* Got special prefix, this means we have a bus prefix |
| 609 | * And we should use our "bus minor" |
| 610 | */ |
| 611 | sprintf(priv->devName, "/dev/%sgrspw%d", prefix, dev->minor_bus); |
| 612 | } |
| 613 | |
| 614 | /* Register Device */ |
| 615 | status = rtems_io_register_name(priv->devName, grspw_driver_io_major, dev->minor_drv); |
| 616 | if (status != RTEMS_SUCCESSFUL) { |
| 617 | return status; |
| 618 | } |
| 619 | |
| 620 | return RTEMS_SUCCESSFUL; |
| 621 | } |
| 622 | |
| 623 | /******************* Driver Implementation ***********************/ |
| 624 | |
| 625 | int grspw_register_io(rtems_device_major_number *m) |
| 626 | { |
| 627 | rtems_status_code r; |
| 628 | |
| 629 | if ((r = rtems_io_register_driver(0, &grspw_driver, m)) == RTEMS_SUCCESSFUL) { |
| 630 | SPACEWIRE_DBG("GRSPW driver successfully registered, major: %d\n", *m); |
| 631 | } else { |
| 632 | switch(r) { |
| 633 | case RTEMS_TOO_MANY: |
| 634 | printk("GRSPW rtems_io_register_driver failed: RTEMS_TOO_MANY\n"); |
| 635 | return -1; |
| 636 | case RTEMS_INVALID_NUMBER: |
| 637 | printk("GRSPW rtems_io_register_driver failed: RTEMS_INVALID_NUMBER\n"); |
| 638 | return -1; |
| 639 | case RTEMS_RESOURCE_IN_USE: |
| 640 | printk("GRSPW rtems_io_register_driver failed: RTEMS_RESOURCE_IN_USE\n"); |
| 641 | return -1; |
| 642 | default: |
| 643 | printk("GRSPW rtems_io_register_driver failed\n"); |
| 644 | return -1; |
| 645 | } |
| 646 | } |
| 647 | return 0; |
| 648 | } |
| 649 | |
| 650 | int grspw_device_init(GRSPW_DEV *pDev) |
| 651 | { |
| 652 | struct amba_dev_info *ambadev; |
| 653 | struct ambapp_dev_info *pnpinfo; |
| 654 | union rtems_drvmgr_key_value *value; |
| 655 | |
| 656 | /* Get device information from AMBA PnP information */ |
| 657 | ambadev = (struct amba_dev_info *)pDev->dev->businfo; |
| 658 | if ( ambadev == NULL ) { |
| 659 | return -1; |
| 660 | } |
| 661 | pnpinfo = &ambadev->info; |
| 662 | pDev->irq = pnpinfo->irq; |
| 663 | pDev->regs = (LEON3_SPACEWIRE_Regs_Map *)pnpinfo->apb_slv->start; |
| 664 | pDev->minor = pDev->dev->minor_drv; |
| 665 | |
| 666 | /* Get SpaceWire core version */ |
| 667 | switch( pnpinfo->device ) { |
| 668 | case GAISLER_SPW: |
| 669 | pDev->core_ver = 1; |
| 670 | break; |
| 671 | case GAISLER_SPW2: |
| 672 | pDev->core_ver = 2; |
| 673 | break; |
| 674 | default: |
| 675 | return -1; |
| 676 | } |
| 677 | |
| 678 | /* initialize the code with some resonable values, |
| 679 | * actual initialization is done later using ioctl(fd) |
| 680 | * on the opened device */ |
| 681 | pDev->config.rxmaxlen = SPACEWIRE_RXPCK_SIZE; |
| 682 | pDev->txdbufsize = SPACEWIRE_TXD_SIZE; |
| 683 | pDev->txhbufsize = SPACEWIRE_TXH_SIZE; |
| 684 | pDev->rxbufsize = SPACEWIRE_RXPCK_SIZE; |
| 685 | pDev->txbufcnt = SPACEWIRE_TXBUFS_NR; |
| 686 | pDev->rxbufcnt = SPACEWIRE_RXBUFS_NR; |
| 687 | |
| 688 | pDev->ptr_rxbuf0 = 0; |
| 689 | pDev->ptr_txdbuf0 = 0; |
| 690 | pDev->ptr_txhbuf0 = 0; |
| 691 | pDev->rx_dma_area = 0; |
| 692 | pDev->tx_data_dma_area = 0; |
| 693 | pDev->tx_hdr_dma_area = 0; |
| 694 | |
| 695 | /* Get Configuration from Bus resources (Let user override defaults) */ |
| 696 | |
| 697 | value = rtems_drvmgr_dev_key_get(pDev->dev, "txBdCnt", KEY_TYPE_INT); |
| 698 | if ( value ) |
| 699 | pDev->txbufcnt = value->i; |
| 700 | |
| 701 | value = rtems_drvmgr_dev_key_get(pDev->dev, "rxBdCnt", KEY_TYPE_INT); |
| 702 | if ( value ) |
| 703 | pDev->rxbufcnt = value->i; |
| 704 | |
| 705 | value = rtems_drvmgr_dev_key_get(pDev->dev, "txDataSize", KEY_TYPE_INT); |
| 706 | if ( value ) |
| 707 | pDev->txdbufsize = value->i; |
| 708 | |
| 709 | value = rtems_drvmgr_dev_key_get(pDev->dev, "txHdrSize", KEY_TYPE_INT); |
| 710 | if ( value ) |
| 711 | pDev->txhbufsize = value->i; |
| 712 | |
| 713 | value = rtems_drvmgr_dev_key_get(pDev->dev, "rxPktSize", KEY_TYPE_INT); |
| 714 | if ( value ) |
| 715 | pDev->rxbufsize = value->i; |
| 716 | |
| 717 | value = rtems_drvmgr_dev_key_get(pDev->dev, "rxDmaArea", KEY_TYPE_INT); |
| 718 | if ( value ) |
| 719 | pDev->rx_dma_area = value->i; |
| 720 | |
| 721 | value = rtems_drvmgr_dev_key_get(pDev->dev, "txDataDmaArea", KEY_TYPE_INT); |
| 722 | if ( value ) |
| 723 | pDev->tx_data_dma_area = value->i; |
| 724 | |
| 725 | value = rtems_drvmgr_dev_key_get(pDev->dev, "txHdrDmaArea", KEY_TYPE_INT); |
| 726 | if ( value ) |
| 727 | pDev->tx_hdr_dma_area = value->i; |
| 728 | |
| 729 | if (grspw_buffer_alloc(pDev)) |
| 730 | return RTEMS_NO_MEMORY; |
| 731 | |
| 732 | /* Create semaphores */ |
| 733 | rtems_semaphore_create( |
| 734 | rtems_build_name('T', 'x', 'S', '0' + pDev->minor), |
| 735 | 0, |
| 736 | RTEMS_FIFO | RTEMS_SIMPLE_BINARY_SEMAPHORE | RTEMS_NO_INHERIT_PRIORITY | \ |
| 737 | RTEMS_NO_PRIORITY_CEILING, |
| 738 | 0, |
| 739 | &(pDev->txsp)); |
| 740 | |
| 741 | rtems_semaphore_create( |
| 742 | rtems_build_name('R', 'x', 'S', '0' + pDev->minor), |
| 743 | 0, |
| 744 | RTEMS_FIFO | RTEMS_SIMPLE_BINARY_SEMAPHORE | RTEMS_NO_INHERIT_PRIORITY | \ |
| 745 | RTEMS_NO_PRIORITY_CEILING, |
| 746 | 0, |
| 747 | &(pDev->rxsp)); |
| 748 | |
| 749 | grspw_hw_init(pDev); |
| 750 | |
| 751 | /* Register interrupt routine */ |
| 752 | if ( rtems_drvmgr_interrupt_register(pDev->dev, 0, grspw_interrupt, pDev) ) { |
| 753 | return -1; |
| 754 | } |
| 755 | |
| 756 | return 0; |
| 757 | } |
| 758 | |
| 759 | /* IOCTL */ |
| 760 | static rtems_device_driver grspw_control( |
| 761 | rtems_device_major_number major, |
| 762 | rtems_device_minor_number minor, |
| 763 | void * arg |
| 764 | ) |
| 765 | { |
| 766 | int timeout; |
| 767 | rtems_device_driver ret; |
| 768 | rtems_libio_ioctl_args_t *ioarg = (rtems_libio_ioctl_args_t *) arg; |
| 769 | GRSPW_DEV *pDev; |
| 770 | struct rtems_drvmgr_dev_info *dev; |
| 771 | |
| 772 | SPACEWIRE_DBGC(DBGSPW_IOCALLS, "ctrl [%i,%i]\n", major, minor); |
| 773 | |
| 774 | /* Get device pointer by searching in driver list for matching driver minor, |
| 775 | * it would be better if RTEMS delivered the device pointer when calling |
| 776 | * grspw_control... |
| 777 | */ |
| 778 | if ( rtems_drvmgr_get_dev(&grspw_drv_info.general, minor, &dev) ) { |
| 779 | return RTEMS_INVALID_NAME; |
| 780 | } |
| 781 | pDev = (GRSPW_DEV *)dev->priv; |
| 782 | |
| 783 | if (!ioarg) |
| 784 | return RTEMS_INVALID_NAME; |
| 785 | |
| 786 | ioarg->ioctl_return = 0; |
| 787 | switch(ioarg->command) { |
| 788 | case SPACEWIRE_IOCTRL_START: |
| 789 | if ( pDev->running ){ |
| 790 | return RTEMS_INVALID_NAME; |
| 791 | } |
| 792 | |
| 793 | /* Get timeout from userspace |
| 794 | * timeout: |
| 795 | * ¤ -1 = Default timeout |
| 796 | * ¤ less than -1 = forever |
| 797 | * ¤ 0 = no wait, proceed if link is up |
| 798 | * ¤ positive = specifies number of system clock ticks that |
| 799 | * startup will wait for link to enter ready mode. |
| 800 | */ |
| 801 | timeout = (int)ioarg->buffer; |
| 802 | |
| 803 | if ( (ret=grspw_hw_startup(pDev,timeout)) != RTEMS_SUCCESSFUL ) { |
| 804 | return ret; |
| 805 | } |
| 806 | pDev->running = 1; |
| 807 | /* Enable interrupt */ |
| 808 | rtems_drvmgr_interrupt_enable(dev, 0); |
| 809 | break; |
| 810 | |
| 811 | case SPACEWIRE_IOCTRL_STOP: |
| 812 | if ( !pDev->running ){ |
| 813 | return RTEMS_INVALID_NAME; |
| 814 | } |
| 815 | /* Disable interrupts */ |
| 816 | rtems_drvmgr_interrupt_disable(dev, 0); |
| 817 | |
| 818 | pDev->running = 0; |
| 819 | |
| 820 | /* Stop Receiver and transmitter */ |
| 821 | grspw_hw_stop(pDev,1,1); |
| 822 | break; |
| 823 | |
| 824 | default: |
| 825 | return RTEMS_NOT_IMPLEMENTED; |
| 826 | } |
| 827 | |
| 828 | SPACEWIRE_DBGC(DBGSPW_IOCALLS, "SPW_IOCTRL Return\n"); |
| 829 | return RTEMS_SUCCESSFUL; |
| 830 | } |