Writing PCI driver for DMA transfer on Qemu - linux-device-driver

I am writing a PCI device on Qemu and driver(LKM) in the guest OS. While Qemu provides an example PCI device, edu(edu.txt and edu.c) with it's distribution, I am having trouble writing the kernel module to do DMA transfer. A basic driver has been covered here but it does not support DMA.
I am following the implementation of the link and this. I tried to transmit buffer to the PCI device from the IRQ handler. The device can read the data (pci_dma_read) but the I am not getting the correct data that I am supposed to receive. Here is the code segment that is doing DMA transfer:
static int write_to_HyPerf(void *dev, void* addr, uint32_t size)
{
/* ----------------------------------------------------------------------------
* PCI address 'addr':
* addr -> DMA source address
* 0x40000 -> DMA destination address
* 100 -> DMA transfer count
* 1 -> DMA command register
* while (DMA command register & 1)
*--------------------------------------------------------------------------------
*/
iowrite32((u32 *)dma_handle_to_device, mmio + IO_DMA_SRC);
iowrite32(DMA_START, mmio + IO_DMA_DST);
iowrite32((u32 *)size, mmio + IO_DMA_XCNT);
iowrite32(DMA_CMD | DMA_IRQ, mmio + IO_DMA_CMD);
}
I also have setup coherent mapping using dma_alloc_coherent.
vaddr_to_device = dma_alloc_coherent(&(dev->dev), DMA_SIZE, &dma_handle_to_device, GFP_ATOMIC);
The complete code is available here. What am I doing wrong?

Could be that you having problem in your driver.
In this case you can use this:
https://github.com/cirosantilli/linux-kernel-module-cheat/blob/master/kernel_modules/qemu_edu.c
and you can use dd command as in this script to write and read from your device:
https://github.com/cirosantilli/linux-kernel-module-cheat/blob/master/rootfs_overlay/lkmc/qemu_edu.sh
Then all you need is to write the wanted dma values to the correct address
like in the edu.c code:
case 0x80:
dma_rw(edu, false, &val, &edu->dma.src, false);
break;
case 0x88:
dma_rw(edu, false, &val, &edu->dma.dst, false);
break;
case 0x90:
dma_rw(edu, false, &val, &edu->dma.cnt, false);
break;
case 0x98:
dma_rw(edu, false, &val, &edu->dma.cmd, false);
break;

Related

How to pass user space data to dmaengine client usage call?

[EDITED]
I have a board on arm64 with fpga (SoC).
The task is simple:
make possible to transfer data from/to "User Space" area (app) to/from "Kernel Space" phys mem (device mem = fpga regs), with and without dma support usage (streaming type). That dma is in the board (ZynqMP / GDMA).
I will have several devices - on fpga and outside, which should use this communication, but now I'm working only with fpga-ddr4 mem area.
Now I see the next logic flow:
some initialization (dma parameters and so on);
ioremap() a fpga device area;
make a buffer (by kzalloc() or another) - this buffer I should give to the US by mmap fops;
make a scatterlist from the buffer (pseudo-code below);
use the scatterlist with dmaengine to transfer data;
// scatterlist init pseudo-code
struct scatterlist sgl[2];
struct scatterlist *sge;
int i, buf_n, err_code;
__u8 *buffer; // allocated earlier
sg_init_table(sgl, ARRAY_SIZE(sgl));
for_each_sg(sgl, sge, ARRAY_SIZE(sgl), i) {
struct page *pg = virt_to_page(buffer + i * PAGE_SIZE);
dma_addr_t dma_handle = dma_map_page(&pdev->dev, pg, 0, PAGE_SIZE, direction /* DMA_TO_DEVICE */);
if ((err_code = dma_mapping_error(&pdev->dev, dma_handle))) {
dev_err(&pdev->dev, "dma page mapping failed! (code: %i)\n", err_code);
break;
}
sg_set_page(sge, pg, PAGE_SIZE, 0);
}
dma_map_sg(&pdev->dev, sgl, ARRAY_SIZE(sgl), direction) // with appropriate check
Now I misunderstanding next - how or where the destination controled? I mean, I had allocated the buffer in RAM, make scatterlist from it and give this list by argument of dmaengine funtions for transferring. But I dont set/use ioremapped device mem area to save this buffer data! Is this dma works only with appropriate RAM memory area and I should copy buffer to the device area? Or, should I use the ioremapped area as my buffer?
Is it right flow? Explain me my mistakes pleaes?

i2c instead of mdio, marvell phy driver

I am trying to run marvell phy linux driver on my custom board.
The driver uses mdio interface, but my board has i2c.
I replaced phy_read()/phy_write() in marvell.c file by i2c read/write functions. It doesn't work. probe function doesn't called, phy subsystem uses mdio for detecting marvell, and cannot detect it.
How can I use i2c in phy linux sysbsystem?
I decided it with mdio-i2c.c module and wrote my own platform driver.
In my driver probe:
new_bus = mdio_i2c_alloc(&pdev->dev, i2c); /* create bridge */
if (!new_bus){
return -ENOMEM;
}
new_bus->name = "marvell mdio i2c bus";
new_bus->parent = &pdev->dev;
err = of_mdiobus_register(new_bus, pdev->dev.of_node);
dts:
mdio_i2c{
compatible = "marvell,i2c-mdio";
i2c-bus = <&i2c_0>;
ethphy1: ethernet-phy#1f {
reg = <0x1f>;
};
};
It's work.
There is one caveat. A marvell88e1111 has 0x5f i2c address. This address is unacceptable for mdio. I set address 0x1f. The mdio-i2c.c module corrects it
static unsigned int i2c_mii_phy_addr(int phy_id)
{
return phy_id + 0x40;
}

stm32f4xx HAL lib & PCF8457AT - no response to write

I have stm32f4-discovery kit and I want to try i/o expander for hd44870 LCD . I have PCF8574AT link to io example like mine 8-bit expander where i2c address is 0x3f (checked by i2c scanner) on hi2c3 hardware. For c/c++ use HAL libraries on Eclipse environment. Ok take look at code.
First I initialize i2c3 - like Datasheet 100kHz on SCL:
static void MX_I2C3_Init(void)
{
hi2c3.Instance = I2C3;
hi2c3.Init.ClockSpeed = 100000;
hi2c3.Init.DutyCycle = I2C_DUTYCYCLE_2;
hi2c3.Init.OwnAddress1 = 0;
hi2c3.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c3.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c3.Init.OwnAddress2 = 0;
hi2c3.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c3.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
if (HAL_I2C_Init(&hi2c3) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
}
Then try to send data to I/O expander. But before that I check that i/o is ready to use:
result = HAL_I2C_IsDeviceReady(&hi2c3,0x3f , 2, 2);
if (result == HAL_BUSY)
{
HD44780_Puts(6, 1, "busy");
}else{
HD44780_Puts(6, 1, "ready");
uint8_t data_io = 0xff;
HAL_I2C_Master_Transmit(&hi2c3, 0x3f, data_io, 1, 100);
}
On a same expander nothing changes. Any ideas what is wrong or maybe i/0 expander is broken ?
Im not sure about HAL driver, really never used HAL. But I have touched pcf8574 IO expander. As you said, you have checked it with scanner and if you get address, line and device is OK. As I am not expert on I2C and HAL libs,I'll show my I2C driver it relies on STM32 standard periphery drivers and it worked for PCF8574 and various I2C devices. There is an example,snippet(blocking mode, not irq based):
Checking if IO is not busy.
while(I2C_GetFlagStatus(&I2Cx, I2C_FLAG_BUSY) == SET){
if((timeout--) == 0) return -ETIMEDOUT;
}
Generate start condition and set write condition(with address for write mode).
I2C_TransferHandling(&I2Cx, dev_addr, 1, I2C_SoftEnd_Mode, I2C_Generate_Start_Write);
while(I2C_GetFlagStatus(&I2Cx, I2C_ISR_TXIS) == RESET){
if((timeout--) == 0) return -ENODEV;
}
Now you can send data byte( it is your IO states), This function writes directly to I2C TX(transceiver) register :
I2C_SendData(&I2Cx, reg_addr);
while(I2C_GetFlagStatus(&I2Cx, I2C_ISR_TC) == RESET){
if((timeout--) == 0) return -EIO;
}
Generate reading condition and than read from PCF8574, data should be same as it was just written(if nothing toggles IO expander). Basically you can read byte or more bytes (depends on device). In your case PCF8574(8bit) gives only 1 byte.
I2C_TransferHandling(dev->channel,dev_addr, len, I2C_AutoEnd_Mode,I2C_Generate_Start_Read);
size_t i;
for(i=0;i<len;i++){
timeout = I2C_TIMEOUT;
while(I2C_GetFlagStatus(dev->channel, I2C_ISR_RXNE) == RESET){
if((timeout--) == 0) return -EIO;
}
data[i] = I2C_ReceiveData(dev->channel);
}
You can continue RW operations, or just simply wait till device automatically stop transition on line:
while(I2C_GetFlagStatus(&I2Cx, I2C_FLAG_STOPF) == RESET){
if((timeout--) == 0) return -EIO;
}
I2C_ClearFlag(&I2Cx, I2C_ICR_STOPCF);
This steps will write and read data. Anyway this chip has some tricky logic there, it more simplistic than it looks like.Actually it works just as simple OUTPUT. Extern input just triggers up PCF8574 pin and nothing more, no special configuration for input mode. For input monitor for sure use PCF8574 INT pin, PCF8574 will trigger INT pin.
For example:
If you want input pins, than just simply set pins to logic zero. And monitor INT pin,if change happens on input, INT pin will be triggered and you should read data via I2C .
For OUTPUT low just write zero's.
And for OUTPUT high set logic true.
You are using HAL so you have to read what happens inside HAL_I2C_Master_Transmit function. Do not forget that address is 7bit and first byte with address also includes R/W condition.First byte bit0 is R/W bit. So you should handle it.
for example with defines:
#define PCF8574_WRITE_ADRESS (0x40) /*for writing to chip*/
#define PCF8574_READ_ADRESS ((0x40)|0x01) /*for reading chip*/
Here is some links:
i2c explanations
this may help
Really nice guide!
Hope this will help to understand your problem and solve it.:)
thanks , Bulkin
I found obvious mistake . HAL libs do not i2c_address << 1 . I/YOU must put that in code not same result !
HAL_I2C_Master_Transmit(&hi2c3, (0x3f<<1), data_io, 1, 100);
or
$define i2c_address_write (0x3f <<1 )
HAL_I2C_Master_Transmit(&hi2c3, i2c_address_write , data_io, 1, 100);
to read :
$define i2c_address_read ((0x3f <<1) | 0x01)
HAL_I2C_Master_Transmit(&hi2c3, i2c_address_read , data_io, 1, 100);

STM32F446 DFU : Hard fault with free() function

I currently try to upgrade my firmware by using Dfuse from ST. In application mode USB HS in VCP mode allow a communication between computer and µC and I use this communication and a non initialized variable for reset device and configure DFU interface with the followed code.
*/void MX_USB_DEVICE_Init(void)
{
if (DFU_OR_CDC==1)
{
/* Otherwise enters DFU mode to allow user programing his application */
/* Init Device Library */
USBD_Init(&USBD_Device, &DFU_Desc, 0);
/* Add Supported Class */
USBD_RegisterClass(&USBD_Device, USBD_DFU_CLASS);
/* Add DFU Media interface */
USBD_DFU_RegisterMedia(&USBD_Device, &USBD_DFU_Flash_fops);
/* Start Device Process */
USBD_Start(&USBD_Device);
/* Set led1 for indicate that device that device works as CDC/VCP interface */
SetLed(LED2);
ResetLed(LED1);
while(1)
{
}
}
/* If CDC is selected configure and start USB CDC interface*/
else if (DFU_OR_CDC==2)
{
/* Init Device Library */
USBD_Init(&hUSBDDevice, &VCP_Desc, 0);
/* Add Supported Class */
USBD_RegisterClass(&hUSBDDevice, USBD_CDC_CLASS);
/* Add CDC Interface Class */
USBD_CDC_RegisterInterface(&hUSBDDevice, &USBD_CDC_fops);
/* Start Device Process */
USBD_Start(&hUSBDDevice);
/* Set led2 for indicate that device that device works as DFU interface */
SetLed(LED1);
ResetLed(LED2);
Readframe();
}
/*Auto select of CDC usb interface for the next plug, Reset after use of DFU mode*/
DFU_OR_CDC=2;
}
When I use only DFU by set manually the variable DFU_OR_CDC to DFU that's works fine, but if I use VCP and then DFU by using my command I have de HardFault which occur on DFU_DeInit (from example from ST), especially in free() function.
/**
* #brief USBD_DFU_Init
* De-Initialize the DFU layer
* #param pdev: device instance
* #param cfgidx: Configuration index
* #retval status
*/
static uint8_t USBD_DFU_DeInit (USBD_HandleTypeDef *pdev,
uint8_t cfgidx)
{
USBD_DFU_HandleTypeDef *hdfu;
hdfu = (USBD_DFU_HandleTypeDef*) pdev->pClassData;
hdfu->wblock_num = 0;
hdfu->wlength = 0;
hdfu->dev_state = DFU_STATE_IDLE;
hdfu->dev_status[0] = DFU_ERROR_NONE;
hdfu->dev_status[4] = DFU_STATE_IDLE;
/* DeInit physical Interface components */
if(pdev->pClassData != NULL)
{
/* De-Initialize Hardware layer */
((USBD_DFU_MediaTypeDef *)pdev->pUserData)->DeInit();
USBD_free(pdev->pClassData);
pdev->pClassData = NULL;
}
return USBD_OK;
}
The debugger indicate a UNDEFINSTR (Keil V5)with an address of 0x080089A8 for free function. UNDEFINSTR indicate that I try to branch to an address where no code is located, but I unable to understand why.
Any Help will be kind.
Its a known bug in ST's library.
Several of its versions mixes dynamic and static memory management.
Look closely to USBD_malloc / USBD_free.
Most likely, USBD_malloc simply return pointer to global variable, and USBD_free calls standart memory manager:
/* Memory management macros */
#define USBD_malloc (uint32_t *)USBD_static_malloc
#define USBD_free USBD_static_free
void *USBD_static_malloc(uint32_t size)
{
static uint8_t mem[sizeof(USBD_CDC_HandleTypeDef)];
return mem;
}
void USBD_static_free(void *p)
{
free(p);
}
To fix this, just remove call of free().
For resolve this I have used the method explained by FLIP in the following post.
StackOverFlow Jump to bootloader
I program my µC in two steps :
-DFU firmware. Programming start: beginning of Flash memory address, End : position on application firmware in flash memory.This firmware read a GPIO pin and according to this one will jump on my application or start DFU mode.
-Application firmware. Programming start: beginning of Flash memory address, End : position on application firmware in flash memory. Then I reconfigure HAL library,clocks and vector table offset then I enter in my infinity loop where my application run.

Access to internal Xilinx FPGA block RAM

I'm writing a device driver for Xilinx Virtex-6 X8 PCI Express Gen 2 Evaluation/Development Kit SX315T FPGA. My OS is openSUSE 11.3 64 bit.
In the documentation for this device (Virtex-6 FPGA Integrated Block form PCI Express User Guide UG517 (v5.0) April 19, 2010, page 219) says:
The PIO design is a simple target-only application that interfaces with the Endpoint for
PCIe core’s Transaction (TRN) interface and is provided as a starting point for customers to build their own designs. The following features are included:
• Four transaction-specific 2 KB target regions using the internal Xilinx FPGA block
RAMs, providing a total target space of 8192 bytes
• Supports single DWORD payload Read and Write PCI Express transactions to
32-/64-bit address memory spaces and I/O space with support for completion TLPs
• Utilizes the core’s trn_rbar_hit_n[6:0] signals to differentiate between TLP destination
Base Address Registers
• Provides separate implementations optimized for 32-bit, 64-bit, and 128-bit TRN
interfaces
In the device is available BAR0 and BAR2 length 128 bytes.
I'm trying to access internal Xilinx FPGA block RAM, for that I am mapping BAR0 in virtual space kernel.
struct pcie_dev {
struct pci_dev* dev;
struct cdev chr_dev;
atomic_t dev_available;
u32 IOBaseAddress;
u32 IOLastAddress;
void* __iomem bar;
void *virt_addr;
u32 length;
unsigned long sirqNum;
void *private_data; };
struct pcie_dev cur_pcie_dev;
cur_pcie_dev.IOBaseAddress = pci_resource_start(dev, 0);
cur_pcie_dev.IOLastAddress = pci_resource_end(dev, 0);
cur_pcie_dev.length=pci_resource_len(dev,0);
cur_pcie_dev.bar=pci_iomap(dev, 0,cur_pcie_dev.length);
IOBaseAddress is 0xfbbfe000
IOLastAddress is 0xfbbfe07f
length=128;
Using IOCTL I try, write/read data.
case IOCTL_INFO_DEVICE:
{
u32 *rcslave_mem = (u32 *)pCur_dev->bar;
u32 result = 0;
u32 value = 0;
int i;
for (i = 0; i <2048 ; i++) {
printk(KERN_DEBUG "Writing 0x%08x to 0x%p.\n",
(u32)value, (void *)rcslave_mem + i);
iowrite32(value, rcslave_mem + i);
value++;
}
/* read-back loop */
value = 0;
for (i = 0; i < 2048; i++) {
result = ioread32(rcslave_mem + i);
printk(KERN_DEBUG "Wrote 0x%08x to 0x%p, but read back 0x%08x.\n",
(u32)value, (void *)rcslave_mem + i, (u32)result);
value++;
}
But it turns out to write and read only 32 values​​. As I understand it, the recording takes place in BAR0 (4 byte * 32 values ​​= 128 bytes), but not in internal Xilinx memory.I tried to go the other way.
cur_pcie_dev.IOBaseAddress = pci_resource_start(dev, 0);
cur_pcie_dev.IOLastAddress = pci_resource_end(dev, 0);
cur_pcie_dev.length=pci_resource_len(dev,0);
flags = pci_resource_flags(dev,0);
if (flags & IORESOURCE_MEM) {
if (request_mem_region(cur_pcie_dev.IOBaseAddress,cur_pcie_dev.length, DEVICE_NAME)== NULL) {
return -EBUSY;}
cur_pcie_dev.virt_addr=ioremap_nocache(cur_pcie_dev.IOBaseAddress,cur_pcie_dev.length);
if (cur_pcie_dev.virt_addr == NULL) {
printk(KERN_ERR "ERROR: BAR%u remapping FAILED\n",0);
return -ENOMEM;
}
printk(KERN_INFO " Allocated I/O memory range %#lx-%#lx\n", cur_pcie_dev.IOBaseAddress,(cur_pcie_dev.IOBaseAddress+cur_pcie_dev.length-1));
} else {
printk(KERN_ERR "ERROR: Invalid PCI region flags\n");
return -EIO;
}
Then
address = ((unsigned int)pCur_dev->virt_addr+pd.Address);
iowrite32(pd.Value,(unsigned int*) address);
address = ((unsigned int)pCur_dev->virt_addr+pd.Address);
pd.Value = ioread32((unsigned int *)address);
I use a summing virtual address and the address, which specifies the user. But the result is read / write operations is also not true. Tell me what I'm doing wrong.
P.S.Sorry for my bad English
What is the reason you are trying to access internal block RAM of your board? I think a normal behavior of a device driver (which your device here is a PCI Express interface), would suffice if you are using Programmed I/O (PIO) on your FPGA. When you write to your device driver, the data would be transferred to block RAM by downloaded IP core on FPGA side (and also in reverse).
Take a look at Linux Driver in xapp1022 (Memory Endpoint Test) package from Xilinx.
P.S.: I know it's an old question and you may found your answer way sooner :)