Relationship between kernel and board support package - linux-kernel

In general, if I wanted to access(read or write) to some hardware device how do the kernel and bsp (board support package) interact to make this possible assuming the device drivers live inside the bsp?

BSP comprises support for CPU/peripheral initialization and on-board peripheral specific functions, drivers. BSP shall have routines specific to entities like interrupt controller, rtc, timers, dma, uart/usb/ethernet/other-interfaces, spi/i2c, pci/isa bus, flash/ ROM/ EEPROM/ NVRAM, memory map, bootloader, filesystem etc. The routines shall be related to device selection/identification, registration, initialization/opening, reading/receiving, writing/sending or closing/freeing the device.
Normally, these routines shall be placed in respective processor directory/sub-directories. These functions shall be used/invoked by kernel in the form of device driver or independently as required by kernel. Since BSP is collection of hardware specific routines(or set of library), the BSP is also termed as a kind of interface between kernel/OS and on-board peripherals/hardware.

Related

What happens when we press a key on Windows?

First of all, I would say to you that I write this question from nothing because I have attempt to find good documentation but nothing stand out...
What happens when we squeeze a key?
I think this is complex but I hope you can help me.
What I search to know : all (but especially the program start on the host machine and how the key electric signal is encoded and send...)
The eXtensible Host Controller (xHC) has a Periodic Transfer Ring. Windows programs this ring to trigger a transfer every time an interval in milliseconds has passed. The right interval is specified in the USB descriptor returned by the USB device. When the transfer occurs, the xHC puts a Transfer Event TRB on the event ring and triggers an MSI-X interrupt which bypasses the IOAPIC as some kind of inter-processor interrupt. If Windows detects some change in the keys pressed, it will send a message to the application which currently has focus (calling the window's procedure) with the key pressed in one of the argument.
I don't know about electrical signals but I know the eXtensible Host Controller is the USB controller responsible to interact with USB on modern Windows systems. Since Windows nowadays requires an x64 processor, the xHC must be present on your motherboard. The xHC is a PCI-Express device which is compliant with the PCI-Express specification.
To find an xHC, you:
Find the RSDP ACPI table in RAM;
This table will be found by the UEFI firmware which acts as some kind of small operating-system (OS) during boot of the computer. Then, the OS developers will write a small UEFI application named bootx64.efi that they will place on a FAT32 partition on the hard-disk. They will place this app in the /boot/efi directory. The UEFI firmware will directly launch that application on boot of the computer which allows to have an OS which doesn't require user input to be launched (similarly to how it used to work with the legacy BIOS fetching the first sector of the hard-disk and executing the instructions found there).
The UEFI application is compiled in practice with either EDK2 or gnu-efi. These compilers are aware of the UEFI environment and specification. They thus compile the code to system calls that are present during boot and available for the UEFI application written by the OS developers. The System Tables (often the ACPI tables) are given as an argument to the "main" function (often called UefiMain) called by the UEFI firmware in the UEFI application. The code of the application can thus simply use these arguments to find the RSDP table and pass it to the OS.
Find the MCFG ACPI table using the RSDP;
The chain of table is RSDP -> XSDT -> MCFG. Once the OS found the MCFG, this table specifies the base address of the PCI configuration space. To interact with PCI devices you use memory mapped IO (MMIO). You write to some position in RAM and it will instead write to the registers of the PCI devices. The MCFG thus specifies the base address at which you will start finding MMIO registers for the different PCI devices that are plugged into the computer.
Iterate on the PCI devices and look at their IDs until you find an xHC.
To iterate on the PCI devices, the PCI convention specifies a formula which is the following:
UINT64 physical_address = base_address + ((bus - first_bus) << 20 | device << 15 | function << 12);
The base_address is for a specific segment group. Each segment group can have 256 buses (suitable for large servers or large computers with lots of components). There can be up to 65536 segment groups and each can have up to 256 PCI buses. Each PCI bus can have up to 32 devices plugged onto it and each device can have up to 8 functions. Each function can also be a PCI bridge. This is quite straightforward to understand because the terminology is clear. The bus here is an actual serial bus that the PCI devices (like a network card, a graphics card, an xHC, an AHCI, etc.) use to communicate with RAM. The function is a functionality of the PCI device like controlling USB devices, hard-disks, HDMI screens (for graphics cards), etc. The PCI bridge bridges a PCI bus to another PCI bus. It means you can have almost an infinite amount of devices with the PCI specification because the bridges allow to extend the tree of devices by adding other PCI host controllers.
Meanwhile, the bus is simply a number between 0 and 255. The first bus is specified in the MCFG ACPI table for a specific segment group. The device is a number between 0 and 31 and the function is a number between 0 and 7. This formula returns a physical address which points to a conventional configuration space (it is the same for all functions) which has specific registers. These registers are used to determine what is the type of device and to load a proper driver for it. Each function of each device thus gets a configuration space.
For the xHC, there will be only one function and the IDs returned by its configuration space will be 0x0C for the class ID and 0x03 for the subclass ID (https://wiki.osdev.org/EXtensible_Host_Controller_Interface).
Once you found an xHC, it gets rather complex. You need to initialize it and get the USB devices which are plugged in the computer at the current moment. You need to take several steps to get the xHC operational. For this part, I'll leave you to read the xHCI specification which (on chapter 4) specifies exactly the steps which need to be taken (https://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/extensible-host-controler-interface-usb-xhci.pdf).
For the keyboard portion I'll leave you to read one of my answer on the stackexchange for computer science: https://cs.stackexchange.com/questions/141870/when-are-a-controllers-registers-loaded-and-ready-to-inform-an-i-o-operation/141918#141918.
Some good links:
https://wiki.osdev.org/Universal_Serial_Bus
https://wiki.osdev.org/PCI

Mapping external memory device

I am using the GCC toolchain and the ARM Cortex-M0 uC. I would like to ask if it is possible to define a space in the linker so that the reading and writing operations would call the external device driver functions for reading and writing it's space (eg. SPI memory). Can anyone give some hints how to do it?
Regards, Rafal
EDIT:
Thank you for your comments and replies. My setup is:
The random access SPI memory is connected via SPI controller and I use a "standard" driver to access the memory space and store/read data from it.
What I wanted to do is to avoid calling the driver's functions explicitly, but to hide them behind some fixed RAM address, so that any read of that address would call the spi read memory driver function and write would call the spi write memory function (the offset of the initial address would be the address of the data in the external memory). I doubt that it is at all possible in the uC without the MMU, but I think it is always worth to ask someone else who might have had similar idea.
No, this is not how it works. Cortex-M0 has no memory management Unit, and is therefore unable to intercept accesses to specific memory regions.
It's not really clear what you are trying to achieve. If you have connected SPI memory external to the chip, you have to perform all the accesses using a driver, it is not possible to memory map the SPI port abstraction.
If this is an on-device SPI memory controller, it will have two regions in the memory map. One will be the 'memory'region, and will probably behave read-only, one with be the control registers for the memory controller hardware, and it is these registers which the device driver talks to. Specifically, to write to the SPI, you need to perform driver accesses to perform the write.
In the extreme case, (for example Cortex-M1 for Xilinx), there will be an eXecute In Place (XIP) peripheral for the memory map behaviour, and a SPI Master device for the read/write functionality. A GPIO pin is used to multiplex the SPI EEPROM pins between 'memory mode' and çonfiguration mode'.

what is the use of Flattened device tree - Linux Kernel

I am going through the Uboot & kernel startup process. What exactly is the use of the FDT (Flat device tree) ?
Many link i have read they state that uboot pass the board & SOC configuration information to Kernel in the form of FDT
https://wiki.freebsd.org/FlattenedDeviceTree
Why kernel need the board configuration information ?
I am asking this question because when ever we make device driver in linux we use to initialize the device at probe() or module_init() call & use request_mem_region() & ioremap() function to get the range of address
& then set the clock & other register of the driver.
What does request_mem_region() actually do and when it is needed?
Now if my device drivers for onchip & offchip devices are doing the full board initialisation.
Then what is the use of flattened device tree for the kernel ?
You are right in assuming that the board files and device-trees are required for initialisation of on-chip blocks and off-chip peripherals.
While booting-up, the respective drivers for all on-chip blocks of an SoC and off-chip peripherals interfaced to it need to be "probed" i.e. loaded and called. On bus-es like USB and PCI, the peripherals can be detected physically and enumerated and their corresponding driver probed. However in general such a facility is NOT available is case of the rest of the peripherals on the rest of the buses like I2C, SPI etc.
In addition to above, when the device-driver is probed, one also needs to provide some information to it about the way in which we intend to configure and utilise the hardware. This varies depending upon the use case. For example the baud-rate at which we would like to operate an UART port.
Both the above classes of information i.e.
Physical Topology of the hardware.
Configuration options of the hardware.
were usually defined as structs within the "board" file.
However using the board-file approach required one to re-build the kernel even to simply modify a configurable option to a different value during initialisation. Also when several physical boards differing slightly in their topology/configuration exist, the "board" file approach becomes too cumbersome to maintain.
Hence the interest in maintaining this information separately in a device-tree. Any device-driver can parse the relevant branches and leaves of the device-tree to obtain the information it requires.
When developing your own device-driver, if your platform supports the device-tree, then you are encouraged to utilise the device tree to store the "platform data" required by your device-driver. This should help you clearly separate :
the generic driver code for your device in the <driver.c> file and
the device's config options specific to this platform into the device-tree.
A step-by-step approach to porting the Linux kernel to a board/SoC should help you appreciate the nuances involved and the advantages of using a device-tree.

Inter processor Interrrupts in ARM cortex A9 ( How To write an handler for Software generated Interrupt ( ARM) in Linux? )

I read that the Software generated interrupts in ARM are used as Inter-processor interrupts. I can also see that 5 of those interrupts are already in use. I also know that ARM provides 16 Software generated interrupts.
In my application i am running a bare metal application on of the ARM-cortex cores and Linux on the other. I want to communicate some data from the core running bare metal application to the core which is running Linux. I plan to copy the data to the on chip memory ( which is shared) and I will trigger a SGI on the Core ( running linux) to indicate some data is available for it to process. Now I am able to generate the SGI from the core ( running bare-metal application ). But for handling the interrupt in the linux side, I am not sure of the SGI IRQ numbers which are free and I am also not sure whether i can use the IRQ number directly ( in general SGI are from 0-15). Does any one have an idea how to write a handler for SGI in Linux?
Edit: This is a re-wording of the above text, because the question was closed for SSCE reasons. The Cortex-A CPUs are used in multi-CPU systems. An ARM generic interrupt controller (GIC) monitors all global interrupts and dispatches them to a particular CPU. In order for individual CPUs to signal each other, a software generated interrupt (SGI) is sent from one core to the other; this uses peripheral private interrupts (PPI). This question is,
How to implement a Linux kernel driver that can receive an SGI as a PPI?
Does any one have an idea how to write a handler for SGI in Linux?
As you didn't give the Linux version, I will assume you work with the latest (or at least recent). The ARM GIC has device tree bindings. Typically, you need to specify the SGI interrupt number in a device tree node,
ipc: ipc#address {
compatible = "company,board-ipc"; /* Your driver */
reg = <address range>;
interrupts = <1 SGI 0x02>; /* SGI is your CPU interrupt. */
status = "enabled";
};
The first number in the interrupt stanza denotes a PPI. The SGI will probably be between 0-15 as this is where the SGI interrupts are routed (at least on a Cortex-A5).
Then you can just use the platform_get_irq() in your driver to get the PPI (peripheral private interrupt). I guess that address is the shared memory (physical) where you wish to do the communications; maybe reg is not appropriate, but I think it will work. This area will be remapped by the Linux MMU and you can use it with,
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
mem = devm_ioremap_resource(dev, res);
The address in the device tree above is a hex value of the physical address. The platform_get_irq() should return an irq number which you can use with the request_irq() family of functions. Just connect this to your routine.
Edit: Unfortunately, interrupts below 16 are forbidden by the Linux irq-gic.c. For example, gic_handle_irq(), limits handler to interrupts between 16 and 1020. If SMP is enabled, then handle_IPI() is called for the interrupts of interest. gic_raise_softirq() can be used to signal an interrupt. To handle the SGI with the current Linux, smp.c needs additional enum ipi_msg_type values and code to handle these in handle_IPI(). It looks like newer kernels (3.14+ perhaps?) may add a set_ipi_handler() to smp.c to make such a modification unneeded.
I would like to add that an example of such inter-core communication can be found in TI multicore SoC's (i.e. OMAP3530). Some time ago when I was using such a mechanism, means were provided by TI. Specifically, it was the DSPLink Linux device driver which was providing such a functionality. At that time, unfortunately, it wasn't an open source solution, but maybe there is some technical paper from TI describing how it works ... Just a direction what you could investigate further :)
EDIT: In the meantime, it seems that they've made it open source. So, if that's what you are looking for, you can have a look: DSPLink and SysLink (successor of DSPLink)

How linux drive many network cards with the same driver?

I am learning linux network driver recently, and I wonder that if I have many network cards in same type on my board, how does the kernel drive them? Does the kernel need to load the same driver many times? I think it's not possible, insmod won't do that, so how can I make all same kind cards work at same time?
regards
The state of every card (I/O addresses, IRQs, ...) is stored into a driver-specific structure that is passed (directly or indirectly) to every entry point of the driver which can this way differenciate the cards. That way the very same code can control different cards (which means that yes, the kernel only keeps one instance of a driver's module no matter the number of devices it controls).
For instance, have a look at drivers/video/backlight/platform_lcd.c, which is a very simple LCD power driver. It contains a structure called platform_lcd that is private to this file and stores the state of the LCD (whether it is powered, and whether it is suspended). One instance of this structure is allocated in the probe function of the driver through kzalloc - that is, one per LCD device - and stored into the platform device representing the LCD using platform_set_drvdata. The instance that has been allocated for this device is then fetched back at the beginning of all other driver functions so that it knows which instance it is working on:
struct platform_lcd *plcd = to_our_lcd(lcd);
to_our_lcd expands to lcd_get_data which itself expands to dev_get_drvdata (a counterpart of platform_set_drvdata) if you look at include/linux/lcd.h. The function can then know the state of the device is has been invoked for.
This is a very simple example, and the platform_lcd driver does not directly control any device (this is deferred to a function pointer in the platform data), but add hardware-specific parameters (IRQ, I/O base, etc.) and you get how 99% of the drivers in Linux work.
The driver code is only loaded once, but it allocates a separate context structure for each card. Typically you will see a struct pci_driver with a .probe function pointer. The probe function is called once for each card by the PCI support code, and it calls alloc_etherdev to allocate a network interface with space for whatever private context it needs.

Resources