i2c instead of mdio, marvell phy driver - linux-device-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;
}

Related

is there a way to modify SparkFun_MS5803_I2C .cpp /.h to allow user defined (non-default) I2C SDA/SCL pins for use with TwoWire?

I am using a ESP32-SIM800L with GPRS / MS5803-14BA / BME280 / SH1106 OLED
The modem and sensor setup (using TwoWire ) is as follows (relevant extracts only):
#include <Wire.h>
...
#define I2C_SDA 21
#define I2C_SCL 22
#define I2C_SDA_2 18
#define I2C_SCL_2 19
...
#include <SparkFun_MS5803_I2C.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
#include "SH1106Wire.h"
TwoWire I2CPower = TwoWire(0);
TwoWire I2CMS = TwoWire(1); // I2C bus for MS5803 # 0x77
TwoWire I2CBME = TwoWire(1); // I2C bus for BME280 # 0x76
TwoWire I2CSH = TwoWire(1); // I2C bus for SH1106 OLED # 0x3c
MS5803 sensor(ADDRESS_LOW); // default is ADDRESS_HIGH = 0x76 // ADDRESS_LOW = 0x77
Adafruit_BME280 bme;
SH1106Wire display(0x3c, I2C_SDA_2, I2C_SCL_2);
...
void setup() {
...
I2CPower.begin(I2C_SDA, I2C_SCL, 400000); // start I2C bus No. 1 for SIM800L
I2CMS.begin(I2C_SDA_2, I2C_SCL_2, 400000); // start I2C bus No. 2 for MS5803
I2CBME.begin(I2C_SDA_2, I2C_SCL_2, 400000); // start I2C bus No. 2 for BME280
I2CSH.begin(I2C_SDA_2, I2C_SCL_2, 400000); // start I2C bus No. 2 for SH1106 OLED
...
// Init BME280 # 0x76
if (!bme.begin(0x76, &I2CBME)) {
Serial.println("No valid BME280 sensor # 0x76, check wiring!");
} else {
Serial.println("BME280 # 0x76 : Ok");
}
// Init MS5803 # 0x77
sensor.reset();
delay(500);
//sensor.begin();
//delay(500);
if (!sensor.begin()) {
Serial.println("No valid MS5803 sensor # 0x77");
} else {
Serial.println("MS5803 # 0x77 : Ok");
}
// Init SH1106 OLED # 0x3c
display.init();
Serial.println("SH1106 OLED # 0x3c : SCREEN CHECK");
display.flipScreenVertically();
display.setFont(ArialMT_Plain_16);
display.setTextAlignment(TEXT_ALIGN_LEFT);
display.drawString(0,0,"SCREEN ON");
display.display();
...
} // End void setup()
void loop() {
read sensors and report & etc. ...
}
The Modem, BME280 and SH1106 OLED are all found and work, however the MS5803 is not picked up.
There are no I2C address clashes (0x76,0x33,0x77 respectivly).
Object creation or initialization of the BME280 and SH1106 OL allow explicit SDA/SCL pin assignments:
SH1106Wire display(0x3c, I2C_SDA_2, I2C_SCL_2);
...
bme.begin(0x76, &I2CBME)
however creation of the MS5803 object and its initialization has no provision for this:
MS5803 sensor(ADDRESS_LOW);
...
sensor.begin();
Trying to enter explicit pin numbers or reference to its TwoWire object in these expressions produces an error warning that there is no such function to allow such assignment.
Basically, if the modem, the BME280 and SH1106 OLED are not used, and TwoWire is not needed or invoked, then the MS5803 runs perfectly without pin assignment using the default I2C Pins (which for my ESP32Sim800L are 21/22). It is the issue of trying to put the sensor on a second I2C wire that seems problematic.
Can anyone explain how to use the MS5803 with TwoWire? Having looked on GitHub at the Sparkfun .h & .cpp I cant see one or how to explicitly set the SDA/SCL pins. Is there a way. Any pointers much appreciated.

Kernel driver, is pinctrl property always needed when using GPIO overlay?

I've asked this question on Unix Stackexchange, but it seems it was a wrong place for this kind of problem. Ad rem:
I'm creating a kernel driver for SPI controlled display, which is meant to be working with Raspberry PI. Besides the three SPI lines, the display has 3 additional control lines: BUSY, RST and DC. In order to has a possibility of controlling these lines, my DTS has to include additional fragment for GPIO.
fragment#0 {
target = <&spi0>;
__overlay__ {
#address-cells = <1>;
#size-cells = <0>;
status = "okay";
spidev#0 {
status = "disabled";
};
epd0: epd#0 {
compatible = "waveshare,epd";
reg = <0>;
pinctrl-names = "default";
pinctrl-0 = <&epd_pins>;
spi-max-frequency = <1000000>;
width = <128>;
height = <296>;
dc-gpios = <&gpio 16 0>;
reset-gpios = <&gpio 20 0>;
busy-gpios = <&gpio 21 0>;
status = "okay";
};
};
};
fragment#1 {
target = <&gpio>;
__overlay__ {
epd_pins: epd_pins {
brcm,pins = <16 20 21>; /* DC RST BUSY */
brcm,function = <1 1 0>; /* out out in */
};
};
};
That DTS works perfectly fine and I didn't expect any troubles. But there is one thing I'm not sure about:
pinctrl-names = "default";
pinctrl-0 = <&epd_pins>;
I've seen properties like that in other's DTs with gpio fragments, but not always; sometimes they are, sometimes they're not. If I comment out these two lines, it seems like nothing changes, and my driver still works as it should.
I have two questions:
What is the purpose of those pinctrl lines? I'm aware of pin controller subsystem, but I'm asking strictly in context of my DT.
Why do I need to declare the gpio overlay? I set IN or OUT function directly from my driver code anyway and my gpio numbers are defined in spi overlay (dc-gpios, reset-gpios, busy-gpios).
To answer you question (assuming you understand the function of the pinctrl line in the device tree in general).
When your device is probed by the kernel, if you have those pinctrl lines in your dts then the kernel requests the pinctrl subsystem to configure the pins listed under brcm,pins as their respective functions defined under brcm,function. The pinctrl state named default is requested to be set by the kernel automatically. You may define other states as
pinctrl-names = "default", "sleep";
pinctrl-0 = <&spi1_default>;
pinctrl-1 = <&spi1_sleep>;
and for other states like sleep or idle you will have to explicitly call them when the driver changes state for power management by calling functions pinctrl_pm_select_sleep_state or pinctrl_pm_select_idle_state respectively. You can only call these functions if the respective pin states are defined in device tree else you might have to do the configuring manually calling the pinctrl apis.
Not necessary in your case, as you are said that you are explicitly setting the pin modes and configuration in device driver, then in that case for your particular case you might not need these lines in your device tree.

Writing PCI driver for DMA transfer on Qemu

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;

Receiving only serial data in the power of 2 when sending the alphabeth

I am trying to perform a simple experiment with an Arduino Uno and the BlueSmirf Bluetooth module from Sparkfun (documentation).
My hardware setup looks like this:
Arduino(power through USB)->BlueSmirf ---(bluetooth)--> PC(no wired connection the the Arduino)->RealTerm
On the Arduino, the following sketch is running:
#include <SoftwareSerial.h>
int txPin = 2;
int rxPin = 3;
SoftwareSerial bluetooth(txPin, rxPin);
void setup() {
bluetooth.begin(115200);
delay(100);
}
void loop() {
String textToSend = "abcdefghijklmnopqrstuvw123456789";
bluetooth.print(textToSend);
delay(5000);
}
Now, the bluetooth connects to the PC just fine, but when I inspect the COM port in RealTerm, I only get the following output:
abdhp1248
Where did the remaining letters and numbers go? It seems like all of the letters that follow the power of two, (i.e. a=1, b=2, d=4, h=8, p=16) print, but none of the rest. Is this just a coincidence?
I think you're running the serial port too fast. As per the comments in the sparkfun BlueSmirf example at https://learn.sparkfun.com/tutorials/using-the-bluesmirf - "115200 can be too fast at times for NewSoftSerial to relay the data reliably".
Reduce the baud rate to 9600 using the code example below, modified from the above web page.
/*
Example Bluetooth Serial Passthrough Sketch
by: Jim Lindblom
SparkFun Electronics
date: February 26, 2013
license: Public domain
This example sketch converts an RN-42 bluetooth module to
communicate at 9600 bps (from 115200), and passes any serial
data between Serial Monitor and bluetooth module.
*/
#include <SoftwareSerial.h>
int bluetoothTx = 2; // TX-O pin of bluetooth mate, Arduino D2
int bluetoothRx = 3; // RX-I pin of bluetooth mate, Arduino D3
SoftwareSerial bluetooth(bluetoothTx, bluetoothRx);
void setup()
{
bluetooth.begin(115200); // The Bluetooth Mate defaults to 115200bps
bluetooth.print("$"); // Print three times individually
bluetooth.print("$");
bluetooth.print("$"); // Enter command mode
delay(100); // Short delay, wait for the Mate to send back CMD
bluetooth.println("U,9600,N"); // Temporarily Change the baudrate to 9600, no parity
// 115200 can be too fast at times for NewSoftSerial to relay the data reliably
bluetooth.begin(9600); // Start bluetooth serial at 9600
}
void loop()
{
String textToSend = "abcdefghijklmnopqrstuvw123456789";
bluetooth.print(textToSend);
delay(5000);
}

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.