8 | | systems traditionally use a hardcoded register location, whereas in LEON3 systems Plug & Play is used to find |
9 | | all cores such a Memory controller, Timer, IRQ Controller, Ethernet MAC. |
| 8 | systems traditionally use a hardcoded register location and IRQ assignment, whereas in LEON3 systems |
| 9 | Plug & Play is used to find all cores such a Memory controller, Timer, IRQ Controller, Ethernet MAC, etc. The |
| 10 | Plug & Play information in a LEON3 system contain IRQ number, register base addresses, memory space regions |
| 11 | etc. |
17 | | * Bus information (Bus frequency, Snooping/Cache options) |
18 | | * Unify the way drivers are configured. The driver registration string is replaced. |
19 | | * build images suitable for multiple targets with different hardware present. |
20 | | * Hardware topology awareness. |
21 | | * Easier to access which drivers are loaded and what hardware is present. |
22 | | * An initial step towards an architecture independent PCI layer. |
| 19 | * Bus information (Bus/Core frequency, Snooping/Cache options, Location in system) |
| 20 | * Unify the way drivers are configured. The driver registration string is replaced with "driver resources" managed per bus. |
| 21 | * build images suitable for multiple targets with different hardware present, for example different PCI Host controllers. |
| 22 | * Hardware bus topology awareness in software, this makes it easier to access which drivers are loaded and what hardware is present. |
33 | | the information about the hardware (provided by the bus driver) to the driver hardware support information |
34 | | (provided by the driver). When a driver is found that can handle the device the device and driver are |
35 | | "united". The device is put into a list later processed by the driver manager. |
36 | | |
37 | | All drivers are registered before starting scanning for devices. |
| 37 | the information about the hardware (provided by the bus driver from Plug & Play) to the driver hardware |
| 38 | support information (provided by the driver). When a driver is found that can handle the hardware device |
| 39 | the device and driver are "united". The device is put into a list later processed by the driver manager. |
| 40 | Each device in the list is picked out and initialized in a two step process (init1 and init2) by calling |
| 41 | the device driver's functions, init1() and init2(). If the device driver happens to be a bus driver more |
| 42 | devices will be registered and put at the end of the device list. When there is no more devices left in the |
| 43 | list the manager will complete. |
| 44 | |
| 45 | All device drivers and bus drivers are registered before starting scanning for devices. |
40 | | a device with child devices. A device represents a certain hardware. The topmost device is a bus |
41 | | device, the root bus device. All devices is connected to the root bus device. |
| 48 | a device with child devices. A device represents a certain hardware situated on a bus. A driver |
| 49 | handles one or more devices of the same hardware type. Driver resources are configuration parameters |
| 50 | targeted one specific driver and one specific device, the resouces consists of an array with name, type |
| 51 | and value. The resources are hardcoded at compile time by the user. |
| 52 | |
| 53 | The topmost device is a bus device, the root bus device. The driver for that device is called the root |
| 54 | bus driver. All devices are connected to the root bus device. |
| 55 | = Initialization steps = |
| 56 | |
45 | | BSP prior to driver manager initialization. The root bus device is initialized and reports the |
46 | | available hardware on the bus., once the root bus device is done initializing the driver manager |
47 | | proceeds to initialize the found devices in a two step process. |
| 60 | BSP prior to driver manager initialization. |
| 61 | |
| 62 | After all drivers have been registered to the driver manager the device initialization begins. The root |
| 63 | bus device is initialized and reports the available hardware devices on the bus. Once the root bus device |
| 64 | is done initializing, the driver manager proceeds to initialize the found devices in a two step process. |
| 89 | = Driver Interface = |
| 90 | |
| 91 | The driver manager provides the drivers with a common interface independent of bus type. In order for the |
| 92 | driver manager to implement one interface it relies on the bus driver to make bus dependent operations. The |
| 93 | bus drivers are the core of the functionality provided by the driver manager. |
| 94 | |
| 95 | It may be bus dependent how to find the bus frequency, manage interrupts (level sensitive?), Plug & Play |
| 96 | information, translate addresses, find a driver for a device etc. |
| 97 | |
| 98 | When the driver manager interface is not enough for a bus type, the bus driver may implement functions of |
| 99 | its own extending the driver manager interface but still taking advantage of the device and driver structure |
| 100 | introduced by the manager. Perhaps a stupid example but anyway, the example below shows how a PCI bus |
| 101 | driver could make PCI Read Configuration Space function simpler by removing the knowledge of Bus,Slot,Func |
| 102 | in a PCI device driver, instead of calling pci_cfg_read_word directly: |
| 103 | |
| 104 | {{{ |
| 105 | int pci_cfg_read_word(int bus, int slot, int func, int offset, unsigned int *buf); |
| 106 | |
| 107 | int pcimgr_cfg_read_word(rtems_drvmgr_dev_info *dev, int offset, unsigned int *buf) |
| 108 | { |
| 109 | /* Get Bus specific information previously prepared by bus driver */ |
| 110 | struct pci_dev_info *pciinfo = (struct pci_dev_info *)dev->businfo; |
| 111 | |
| 112 | return pci_cfg_read_word(pciinfo->bus, pciinfo->slot, pciinfo->func, offset, buf); |
| 113 | } |
| 114 | }}} |
76 | | the manager. The bus driver must provide some services in order for the drivers to the found devices to |
77 | | function properly. The services are implemented by functions that are pointed to by a operations struct |
78 | | making it possible for the manager driver interface to call the functions. |
79 | | |
80 | | The bus driver must be able to unite a hardware device with a driver. |
| 119 | the manager. The bus driver must also provide function pointers to a couple of services in order for |
| 120 | the driver manager to present a common driver interface to device drivers. |
| 121 | |
| 122 | One of the most important services is the ability to unite a hardware device with a driver, for this to happen |
| 123 | a driver must provide information about what hardware is supported so that the bus driver can compare that with |
| 124 | the found hardware. |
132 | | }; |
| 177 | int error; /*!< Error state returned by driver */ |
| 178 | }; |
| 179 | }}} |
| 180 | == Device State == |
| 181 | |
| 182 | During the initialization of a device the initialization level (init1 or init2) |
| 183 | changes, to hold that information a device state integer is maintained for each |
| 184 | device. The device state is updated when, |
| 185 | ¤ Assigning a driver |
| 186 | ¤ Init1 is completed or erroneous |
| 187 | ¤ Init2 is completed or erroneous |
| 188 | ¤ A device is deleted by the user |
| 189 | ¤ A device must be ignored by user's request |
| 190 | |
| 191 | The manager uses the state internaly but it may also be of improtance to the |
| 192 | user when a device failed. |
| 193 | == Device Error Handling == |
| 194 | |
| 195 | During the initialization steps of a device the driver manager may not complete |
| 196 | the initialization because of, |
| 197 | ¤ No suitable driver found |
| 198 | ¤ Device was requested to be ignored by user |
| 199 | ¤ Driver failed to take device into init1 or init2 dur to an error |
| 200 | |
| 201 | The driver manager removes the device from further initialization and instead of |
| 202 | freeing the device structure the device is put into a separate list (the inactive |
| 203 | list) and the device state is updated accordingly. When initialization fails the |
| 204 | error field in the device structure is updated indicating the error more in |
| 205 | detail. |
| 206 | |
| 207 | A user may process all failed devices on the inactive list to determined if the |
| 208 | error was acceptable or not. |
| 209 | == Unregistering a device == |
| 210 | |
| 211 | A device may also be unregistered due to device error (parity error on the PCI |
| 212 | bus for example) or hardware was removed (hot-plug system). A device is |
| 213 | unregistered by calling rtems_drvmgr_dev_unregister. The device is moved from |
| 214 | the current list to the inactive list (delete=0) or it may be removed completely |
| 215 | to avoid memory exhaustion (delete=1). |
| 216 | |
| 217 | {{{ |
| 218 | /*! Remove a device, and all its children devices if device is a bus device. The device |
| 219 | * driver will be requested to remove the device and once gone from bus, device and |
| 220 | * driver list the device is put into a inactive list for debugging (this is optional |
| 221 | * by using delete argument). |
| 222 | * |
| 223 | * Removing the Root Bus Device is not supported. |
| 224 | * |
| 225 | * \param delete If non-zero the device will be deallocated, and not put into the |
| 226 | * inacitve list. |
| 227 | */ |
| 228 | extern int rtems_drvmgr_dev_unregister(struct rtems_drvmgr_dev_info *dev, int delete); |
| 263 | == Driver ID == |
| 264 | |
| 265 | All drivers must have a unique ID, it may be created by using one of the |
| 266 | supported devices's ID. Often a device have a VENDOR:DEVICE unique for the bus. |
| 267 | A driver ID is created by combining a bus dependent ID and the bus type number: |
| 268 | |
| 269 | From drvmgr.h: |
| 270 | {{{ |
| 271 | /*** Bus indentification ***/ |
| 272 | #define DRVMGR_BUS_TYPE_NONE 0 /* Not a valid bus */ |
| 273 | #define DRVMGR_BUS_TYPE_ROOT 1 /* Hard coded bus */ |
| 274 | #define DRVMGR_BUS_TYPE_PCI 2 /* PCI bus */ |
| 275 | #define DRVMGR_BUS_TYPE_AMBAPP 3 /* AMBA Plug & Play bus */ |
| 276 | |
| 277 | /* 64-bit identification integer definition |
| 278 | * ¤ Bus ID 8-bit [7..0] |
| 279 | * ¤ Reserved 8-bit field [63..56] |
| 280 | * ¤ Device ID specific for bus type 48-bit [55..8] (Different buses have different unique identifications for hardware/driver.) |
| 281 | * |
| 282 | * ID Rules |
| 283 | * ¤ A root bus driver must always have device ID set to 0. There can only by one root bus driver |
| 284 | * for a certain bus type. |
| 285 | * ¤ A Driver ID must identify a unique hardware core |
| 286 | * |
| 287 | */ |
| 288 | |
| 289 | /* Bus ID Mask */ |
| 290 | #define DRIVER_ID_BUS_MASK 0x00000000000000FFULL |
| 291 | |
| 292 | /* Reserved Mask for future use */ |
| 293 | #define DRIVER_ID_RSV_MASK 0xFF00000000000000ULL |
| 294 | |
| 295 | /* Reserved Mask for future use */ |
| 296 | #define DRIVER_ID_DEV_MASK 0x00FFFFFFFFFFFF00ULL |
| 297 | |
| 298 | /* Set Bus ID Mask. */ |
| 299 | #define DRIVER_ID(busid, devid) \ |
| 300 | (unsigned long long)( (((unsigned long long)(devid) << 8) & DRIVER_ID_DEV_MASK) | ((unsigned long long)(busid) & DRIVER_ID_BUS_MASK) ) |
| 301 | }}} |
| 302 | |
| 303 | From AMBA Plug & Play bus, ambapp_bus.h: |
| 304 | {{{ |
| 305 | /* GRLIB AMBA Plug&Play Driver ID generation */ |
| 306 | #define DRIVER_AMBAPP_ID(vendor, device) \ |
| 307 | DRIVER_ID(DRVMGR_BUS_TYPE_AMBAPP, ((((vendor) & 0xff) << 16) | ((device) & 0xfff))) |
| 308 | |
| 309 | /*** Gaisler Hardware Device Driver IDs ***/ |
| 310 | #define DRIVER_AMBAPP_GAISLER_GRETH_ID DRIVER_AMBAPP_ID(VENDOR_GAISLER, GAISLER_ETHMAC) |
| 311 | }}} |
| 312 | |
| 313 | In the above example VENDOR_GAISLER and GAISLER_GRETH are IDs assigned by the |
| 314 | GRLIB Bus domain. |
| 315 | |
| 316 | The Driver ID and a device unit number is used when assigning driver resources |
| 317 | to a certain device. The driver resources must be in a format the driver can |
| 318 | understand, which the driver ID makes sure of, and the driver must not use |
| 319 | another device's resources, which the unit/core number makes sure of. |
167 | | be integer with value 65 called "numberTxDescriptors". The resources are grouped together in |
168 | | arrays targeting one device instance. The resources are assigned to one or multiple buses making |
169 | | it possible for the drivers to find whose hardware device is situated on that very bus. Multiple |
170 | | hardware devices of the same type is separated by their bus unit number which is always the |
171 | | same between starts. The number usually comes from the order the device if found in the plug |
172 | | and play information. Below is a typical bus resource definition grlib_drv_resource configuring |
173 | | two GRSPW device drivers. The second GRSPW core will have double the amount of descriptors |
174 | | than the first. |
| 323 | be an integer with value 65 called "numberTxDescriptors". The resources are grouped together in |
| 324 | arrays targeting one device instance. The resources are assigned to a bus making it possible for |
| 325 | a driver to find the resources for a specific device, since the Driver Manager knows which bus |
| 326 | the device is situated on. A resource is identified by BUS, DRIVER ID and Unit number. Multiple |
| 327 | hardware devices of the same type are separated by their bus unit number which is always the |
| 328 | same between runs. The number usually comes from the order the device if found in the plug |
| 329 | and play information. |
| 330 | |
| 331 | Below is a typical bus resource definition in a LEON3-GRLIB system. The grlib_drv_resource |
| 332 | configures two GRSPW device drivers. The second GRSPW core will have double the amount of |
| 333 | descriptors than the first. |
180 | | * All bus resources entries (bus_res_node) are linked together for a bus (bus_info->reslist). |
181 | | * One bus resource entry has a pointer to an array of driver resources (drv_res). One driver |
182 | | * resouces is made out of an array of keys (drv_res_key). All keys belongs to the same driver |
183 | | * and harwdare device. Each key has a Name, Type ID and Data interpreted differently |
184 | | * depending on the Type ID (union key_value). |
| 339 | * All bus resources entries (_bus_res) are linked together per bus (bus_info->reslist). |
| 340 | * One bus resource entry has a pointer to an array of driver resources (_drv_res). One driver |
| 341 | * resouces is made out of an array of keys (rtems_drvmgr_key). All keys belongs to the |
| 342 | * same driver and harwdare device. Each key has a Name, Type ID and Data interpreted |
| 343 | * differently depending on the Type ID (union rtems_drvmgr_key_value). |
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 |
| 603 | /* GRLIB AMBA Plug&Play Driver ID generation */ |
| 604 | #define DRIVER_AMBAPP_ID(vendor, device) \ |
| 605 | DRIVER_ID(DRVMGR_BUS_TYPE_AMBAPP, ((((vendor) & 0xff) << 16) | ((device) & 0xfff))) |
| 606 | |
| 607 | /*** Gaisler Hardware Device Driver IDs ***/ |
| 608 | #define DRIVER_AMBAPP_GAISLER_GRETH_ID DRIVER_AMBAPP_ID(VENDOR_GAISLER, GAISLER_ETHMAC) |
| 609 | #define DRIVER_AMBAPP_GAISLER_GRSPW_ID DRIVER_AMBAPP_ID(VENDOR_GAISLER, GAISLER_SPW) |
| 610 | |
| 611 | /*** ESA Hardware Device Driver IDs ***/ |
| 612 | #define DRIVER_AMBAPP_ESA_MCTRL_ID DRIVER_AMBAPP_ID(VENDOR_ESA, ESA_MCTRL) |
| 984 | = Multi-processor system notes = |
| 985 | |
| 986 | Ina Multi-processor system sharing the RTEMS instances often share the resources, in a non |
| 987 | Plug & Play system it might be easy to avoid conflicts however in a Plug & Play system it |
| 988 | may be tougher. Say that four UARTs are found, usually the first one is used a console and the |
| 989 | other ones are initialized by the driver into some kind of reset state and registered in the |
| 990 | file system to /dev/ttyS[1..3]. This may not work in a MP system, to solve this problem the |
| 991 | driver manager provides means to stop a device from being given to the it's device driver. |
| 992 | This, of course, must be configured by the user. A device may be ignored by the driver manager |
| 993 | by defining an entry in the driver resource array targeting a specific device, but setting the |
| 994 | pointer to resource array to NULL. |
| 995 | |
| 996 | After a new device has been registered by the bus driver the device is united with a driver, then |
| 997 | a NULL pointer resource array is searched for, if found the device is put on an inactive list rather |
| 998 | than entering stage1 later on. The device's state mask is set to DEV_STATE_IGNORED. |