In device tree, can't understand 'ranges' property of PCIe root complex node - linux-device-driver

For example in linux-5.15.68's arch/arm64/boot/dts/ti/k3-am64-main.dtsi,
&cbass_main {
... skip ...
pcie0_rc: pcie#f102000 {
compatible = "ti,am64-pcie-host", "ti,j721e-pcie-host";
reg = <0x00 0x0f102000 0x00 0x1000>,
<0x00 0x0f100000 0x00 0x400>,
<0x00 0x0d000000 0x00 0x00800000>,
<0x00 0x68000000 0x00 0x00001000>;
reg-names = "intd_cfg", "user_cfg", "reg", "cfg";
... skip ...
#address-cells = <3>;
#size-cells = <2>;
...skip ...
msi-map = <0x0 &gic_its 0x0 0x10000>;
ranges = <0x01000000 0x00 0x68001000 0x00 0x68001000 0x00 0x0010000>,
<0x02000000 0x00 0x68011000 0x00 0x68011000 0x00 0x7fef000>;
dma-ranges = <0x02000000 0x0 0x0 0x0 0x0 0x00000010 0x0>;
};
};
the 'ranges' property says
IO space starting at address 0x68001000 with size 64KiB is mapped to the parent bus address 0x68001000.
a 32 bit memory address space starting with address 0x68011000 and size 0x7fef000 is mapped to parent bus address 0x68011000.
The device tree document says these addresses are all physical.
What I can't understand is :
I know the BIOS will set the BAR addresses during the enumeration process. This is allocating the endpoint's function to a certain PCIe bus address and I guess setting BAR can be done without this 'ranges' information(it will assign free address range for that device according to the device's need). By the time linux kernel later reads this 'ranges' property, the BIOS (or bootloader) has already assigned different PCIe address to that device. Then, how is this 'ranges' property used by the kernel?

Related

Reading/Writing to LSM6DSOX via SPI from Raspberry Pi

I'm having trouble reading and writing to my Adafruit LSM6DSOX IMU from my Raspberry Pi 4 running Ubuntu 20.04. I need to do it via SPI since I require the bandwidth, but I can only seem to read the WHO_AM_I register successfully. Reading/writing to any other register only returns 0x00. I have verified that I can read data off the IMU from an Arduino via SPI, but if I try to read a register other than 0x0F (the IMU_ID) I get 0x0 as a response. Any insight/ideas what could be causing this would be greatly appreciated!
EDIT: It turns out I can read the following registers:
0x0f : 0x6c
0x13 : 0x1c
0x33 : 0x1c
0x53 : 0x1c
0x73 : 0x1c
These are all random registers however, and the value 0x1C doesn't seem to correspond with anything.
This is my main.py:
import LSM6DSOX
def main():
imu=LSM6DSOX.LSM6DSOX()
imu.initSPI()
whoamI=imu.read_reg(0x0F)
while(whoamI != imu.LSM6DSOX_ID):
imu.ms_sleep(200)
print('searching for IMU')
whoamI=imu.get_id()
print(hex(whoamI))
print('found lsm6dsox IMU')
imu.spi.close()
imu.spi = None
if __name__=="__main__":
main()
This is an excerpt from my LSM6DSOX.py:
def initSPI(self):
# Setup communication SPI
self.spi = spidev.SpiDev()
self.spi.open(0, 0)
self.spi.mode=0b11 #mode 3, (mode 0 is also fine)
self.spi.max_speed_hz = 500000
return self.spi
def read_reg(self, reg, len=1):
# Set up message
buf = bytearray(len+1)
buf[0] = 0b10000000 | reg # MSB bit must be 1 to indicate a read operation. this is OR'd with the register address you want to read
resp =self.spi.xfer2(buf) #send (and recieve) data to the imu
if len==1:
return resp[1]
else:
return resp[1:] #display recieved data
def write_reg(self, reg, data, len=1):
# Set up message
buf = bytearray(len+1)
buf[0] = 0b00000000 | reg # MSB bit must be 0 to indicate a read operation. this is OR'd with the register address you want to read
buf[1:] =bytes(data)
resp =self.spi.xfer2(buf) #send (and recieve) data to the imu
return resp[1:] #display recieved data

how is 'stream ID' or 'iommu specifier' determined in PCIe root complex mode?

This is from Documentation/devicetree/bindings/pci/pci-iommu.txt (linux-5.10.0)
PCI root complex
================
Optional properties
-------------------
- iommu-map: Maps a Requester ID to an IOMMU and associated IOMMU specifier
data.
The property is an arbitrary number of tuples of
(rid-base,iommu,iommu-base,length).
Any RID r in the interval [rid-base, rid-base + length) is associated with
the listed IOMMU, with the IOMMU specifier (r - rid-base + iommu-base).
- iommu-map-mask: A mask to be applied to each Requester ID prior to being
mapped to an IOMMU specifier per the iommu-map property.
Example (1)
===========
/ {
#address-cells = <1>;
#size-cells = <1>;
iommu: iommu#a {
reg = <0xa 0x1>;
compatible = "vendor,some-iommu";
#iommu-cells = <1>;
};
pci: pci#f {
reg = <0xf 0x1>;
compatible = "vendor,pcie-root-complex";
device_type = "pci";
/*
* The sideband data provided to the IOMMU is the RID,
* identity-mapped.
*/
iommu-map = <0x0 &iommu 0x0 0x10000>;
};
};
I can't understand the meaning of Any RID r in the interval [rid-base, rid-base + length) is associated with the listed IOMMU, with the IOMMU specifier (r - rid-base + iommu-base). I understand this iommu-map specifies the relationship between the master device (requesting transaction from the PCIe bus to this root complex) and the assigned iommu for the device. This example says, rid 0 ~ 0x10000 is assigned to iommu specifier 0 ~ 0x10000. The document says rid is formed by {bus,device,function} (16 bits).
How is this single number iommu specifier determined by the PCIe core device? Is it device dependent(with the driver's settig)? In arm64 case, the smmu-v3(arm's type of iommu) needs stream ID (and substream ID which is mapped from PASID in PCIe TLP) and when this #iommu-cells is 1, I don't know how this stream ID is passed from the PCIe core device to smmu-v3. If anyone could clarify it, it would be much grateful.

PCI configuration space registers - write values

I am developing a network driver (RTL8139) for a selfmade operating system and have problems in writing values to the PCI configuration space registers.
I want to change the value of the interrupt line (offset 0x3c) to get another IRQ number and enable bus master (set bit 2) of the command register (offset 0x04).
When I read back the values I see that my values are correctly written. But instead of using the new IRQ number (in my case 6) it uses the old value (11).
Also bus master for DMA is not working (my packets I would like to send have the correct size (set by IO-Port) but they have no content (all values just 0, transceive buffer is on physical memory and has non zero values). This always worked with my code as expected after I double checked the physical address. I need this to let the network controller access to my physical memory where my buffers for receive/transceive are. (Bus mastering needed for RTL8139)
Do I have to do something else to confirm my changes to the PCI-device?
As emulator I use qemu.
For reading/writing I wrote the following functions:
uint16_t pci_read_word(uint16_t bus, uint16_t slot, uint16_t func, uint16_t offset)
{
uint64_t address;
uint64_t lbus = (uint64_t)bus;
uint64_t lslot = (uint64_t)slot;
uint64_t lfunc = (uint64_t)func;
uint16_t tmp = 0;
address = (uint64_t)((lbus << 16) | (lslot << 11) |
(lfunc << 8) | (offset & 0xfc) | ((uint32_t)0x80000000));
outportl (0xCF8, address);
tmp = (uint16_t)((inportl (0xCFC) >> ((offset & 2) * 8)) & 0xffff);
return (tmp);
}
uint16_t pci_write_word(uint16_t bus, uint16_t slot, uint16_t func, uint16_t offset, uint16_t data)
{
uint64_t address;
uint64_t lbus = (uint64_t)bus;
uint64_t lslot = (uint64_t)slot;
uint64_t lfunc = (uint64_t)func;
uint32_t tmp = 0;
address = (uint64_t)((lbus << 16) | (lslot << 11) |
(lfunc << 8) | (offset & 0xfc) | ((uint32_t)0x80000000));
outportl (0xCF8, address);
tmp = (inportl (0xCFC));
tmp &= ~(0xFFFF << ((offset & 0x2)*8)); // reset the word at the offset
tmp |= data << ((offset & 0x2)*8); // write the data at the offset
outportl (0xCF8, address); // set address again just to be sure
outportl(0xCFC,tmp); // write data
return pci_read_word(bus,slot,func,offset); // read back data;
}
I hope somebody can help me.
The "interrupt line" field (at offset 0x03C in PCI configuration space) literally does nothing.
The Full Story
PCI cards could use up to 4 "PCI IRQs" at the PCI slot; and use them in order (so if you have ten PCI cards that all have one IRQ, then they'll all use the first PCI IRQ at the slot).
There's a tricky "barber pole" arrangement to connect "PCI IRQ at the slot" to "PCI IRQ at the host controller" that is designed to reduce IRQ sharing. If you have ten PCI cards that all have one IRQ, then they'll all use the first PCI IRQ at the slot, but the first IRQ at each PCI slot will be connected to a different PCI IRQ at the host controller. All of this is hard-wired and can not be changed by software.
To complicate things more; to connect PCI IRQs (from the PCI host controller) to the legacy PIC chips, a special "PCI IRQ router" was added. In theory the configuration of the "PCI IRQ router" can be changed by software (if you can find the documentation that describes a table that describes the location, capabilities and restrictions of the "PCI IRQ router").
Without the firmware's help it'd be impossible for an OS to figure out which PIC chip input a PCI device actually uses. For that reason, the firmware figures it out during boot and then stores "PIC chip input number" somewhere for the OS to find. That is what the "interrupt line" register is - it's just an 8-bit register that can store anything you like (that the BIOS/firmware uses to store "PIC chip input number").

Deciphering HCI event from BLUEnrg-ms bluetooth device

I am successfully able to communicate with my IDB05A1 from my nucleo-64 board.
I make it discoverable and pair my phone to it. However immediately after pairing, the device disconnects from my phone.
Before it disconnects I receive a HCI event i cannot decipher:
0x04 0xff 0x0b 0x01 0x0c 0x01 0x08 0x04 0x00 0x02 0x00 0x00 0x02 0x00
please help me decipher this. datasheet with commands and events
0x04 //HCI event
0xff //Vendor specific
0x0b //Contains 0b(12) bytes
0x01, 0x0c //BLUEnrg event code
0x......
What event is this?
This event looks not related to the problem.
It indicates the modified value of some attribute.
HCI event packet:
0x04 //HCI event
0xff //Vendor specific
0x0b //Contains 0b(11) bytes
0x01 0x0c 0x01 0x08 0x04 0x00 0x02 0x00 0x00 0x02 0x00 //Event data
Event data:
0x0c01 //ACI_GATT_ATTRIBUTE_MODIFIED_EVENT
0x0801 //The connection handle which modified the attribute
0x0004 //Handle of the attribute that was modified
0x0002 //Length of the attribute data
0x00 //Offset
0x0002 //The modified value

SPI fails to read first 6 bytes

I'm having a lot of issues with SPI module on my STM32F051 MCU. I've got it configured as a master to drive a slave flash memory module (that doesn't really matter).
I'm trying to read 8 bytes from memory, this is how the 'read data' message is structured:
First 4 bytes of the message are transmitted, next 8 are received.
First byte is 'read data' opcode, three following are data address and equal 0 in this case.
Code:
memset(out, 0x00, 256);
memset(in, 0x00, 256);
out[0] = OPCODE_READ;
out[1] = 0x00;
out[2] = 0x00;
out[3] = 0x00;
uint32_t len = 4 + size; // size == 8
spi_select(M25P80);
HAL_SPI_TransmitReceive(&hspi1, out, in, len, TIMEOUT);
delay_ms(BYTE_SPEED_MS * 5); // Needed because ^ finishes before physically
// transmitting the data. Nevermind the 5, it
// was picked experimentally
spi_deselect(M25P80);
Signal (yellow - clock, red - miso):
At 488 bits/s transmitting 4 bytes takes 4 * 1E3 / (488 / 8) = 65.5 ms. Then the reception starts. Memory starts transmitting [0xFF...0xFF] right away, but contents of the 'in' buffer are:
[0x00 0x00 0x00 0x00] [0x00 0x00 0x00 0x00 0x00] 0xFF 0xFF 0x00...0x00
^ zero because this ^ should be 0xFF ^ correct data
is the part where
data was being sent
to the memory
So first six bytes of data are just lost. Am I the only one who's having such a hard time with STM's SPI module?
EDIT:
I've gotten myself a different eval board with a slightly different MCU (STM32F030) and it gets even weirder:
[0x02 0x02 0x02 0x02]
0x00 0x02 0x00 0x00 0xFF 0x00 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0x00...0x00
Although I must mention that I'm using a different compiler with this MCU.
EDIT 2:
The way I partially got it to work is using 16-bit mode with SPI. This fixed this particular bug, but there are more similar oddities with STM32's SPI.
EDIT 3:
SPI initialisation code:
void MX_SPI1_Init(void)
{
hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER;
hspi1.Init.Direction = SPI_DIRECTION_2LINES;
hspi1.Init.DataSize = SPI_DATASIZE_16BIT;
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
hspi1.Init.NSS = SPI_NSS_SOFT;
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi1.Init.TIMode = SPI_TIMODE_DISABLED;
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLED;
hspi1.Init.NSSPMode = SPI_NSS_PULSE_DISABLED;
HAL_SPI_Init(&hspi1);
}
Are you sure that the initialization of SPI is right?
Maybe your Clock polarity or phase settings does not match between Master and Slave?
Take a watch to ClockSettings.
Please show your SPI-Initialization-Code!