What does the variable and value of DP83867_RGMIIDCTL_2_25_NS in device tree correspond to? - linux-device-driver

I am a newbie to the embedded linux and device-tree world. I am trying to modify the device tree of ethernet phy from TI. I am interested to know what values the following variables <DP83867_RGMIIDCTL_2_25_NS>, <DP83867_RGMIIDCTL_2_75_NS> and <DP83867_PHYCR_FIFO_DEPTH_4_B_NIB> correspond to. It appears to be delay values, but I searched through the datasheet (https://www.ti.com/lit/ds/symlink/dp83867ir.pdf?HQS=dis-mous-null-mousermode-dsf-pf-null-wwe&DCM=yes&ref_url=https%3A%2F%2Fwww.mouser.co.uk%2F&distId=26) and couldn't find any reference to such variable.
Can someone with some experience in device-tree explain where these variable are coming from and how to change/configure such register values.
&gem3 { /* required by spec */
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_gem3_default>;
phy-handle = <&phy0>;
phy-mode = "rgmii-id";
mdio: mdio {
#address-cells = <1>;
#size-cells = <0>;
reset-gpios = <&gpio 38 GPIO_ACTIVE_LOW>;
reset-delay-us = <2>;
phy0: ethernet-phy#1 {
#phy-cells = <1>;
reg = <1>;
ti,rx-internal-delay = <DP83867_RGMIIDCTL_2_25_NS>;
ti,tx-internal-delay = <DP83867_RGMIIDCTL_2_75_NS>;
ti,fifo-depth = <DP83867_PHYCR_FIFO_DEPTH_4_B_NIB>;
ti,dp83867-rxctrl-strap-quirk;
};
};
};

What does the variable and value of DP83867_RGMIIDCTL_2_25_NS in device tree correspond to?
From https://elixir.bootlin.com/linux/latest/A/ident/DP83867_RGMIIDCTL_2_25_NS it corresponds to a macro with the value 0x8.
#define DP83867_RGMIIDCTL_2_25_NS 0x8

Related

STM32WL55JC1 - HAL ADC wont change channels

What I want to accomplish
So I want to accomplish the following:
I have 3 FreeRTOS-Threads which all shall read one of 3 (5) channels of my ADC. I want to poll the ADC. The Threads then enter the read value into a FreeRTOS-queue.
My code so far
I have the following functions:
ADC initialisation
void MX_ADC_Init(void)
{
hadc.Instance = ADC;
hadc.Init.Resolution = ADC_RESOLUTION_12B;
hadc.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV4;
hadc.Init.ScanConvMode = DISABLE;
hadc.Init.ContinuousConvMode = DISABLE;
hadc.Init.DiscontinuousConvMode = DISABLE;
hadc.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc.Init.NbrOfConversion = 1;
hadc.Init.DMAContinuousRequests = DISABLE;
hadc.Init.Overrun = ADC_OVR_DATA_PRESERVED;
hadc.Init.EOCSelection = ADC_EOC_SEQ_CONV;
hadc.Init.LowPowerAutoPowerOff = DISABLE;
hadc.Init.LowPowerAutoWait = DISABLE;
if (HAL_ADC_Init(&hadc) != HAL_OK)
{
Error_Handler();
}
for(int ch = 0; ch < GPIO_AI_COUNT; ch++)
{
ADC_Select_Ch(ch);
}
}
GPIO initialisation
GPIO_InitTypeDef GpioInitStruct = {0};
GpioInitStruct.Pin = GPIO_AI1_PIN | GPIO_AI2_PIN | GPIO_AI3_PIN | GPIO_AI4_PIN | GPIO_AI5_PIN;
GpioInitStruct.Pull = GPIO_NOPULL;
GpioInitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GpioInitStruct.Mode = GPIO_MODE_ANALOG;
HAL_GPIO_Init(GPIOB, &GpioInitStruct);
Where the GPIO_AI2_PIN definition is defined as:
/* Analog Inputs ----------------------------------------------------------- */
#define GPIO_AI_COUNT 5
#define GPIO_AI1_PIN GPIO_PIN_3
#define GPIO_AI1_PORT GPIOB
#define GPIO_AI1_CH ADC_CHANNEL_2 /* ADC_IN2, Datasheet P. 51 */
#define GPIO_AI2_PIN GPIO_PIN_4
#define GPIO_AI2_PORT GPIOB
#define GPIO_AI2_CH ADC_CHANNEL_3 /* ADC_IN3, Datasheet P. 51 */
#define GPIO_AI3_PIN GPIO_PIN_14
#define GPIO_AI3_PORT GPIOB
#define GPIO_AI3_CH ADC_CHANNEL_1 /* ADC_IN1, Datasheet P. 55 */
#define GPIO_AI4_PIN GPIO_PIN_13
#define GPIO_AI4_PORT GPIOB
#define GPIO_AI4_CH ADC_CHANNEL_0 /* ADC_IN0, Datasheet P. 55 */
#define GPIO_AI5_PIN GPIO_PIN_2
#define GPIO_AI5_PORT GPIOB
#define GPIO_AI5_CH ADC_CHANNEL_4 /* ADC_IN4, Datasheet P. 54 */
Changing channel
void ADC_Select_Ch(uint8_t channelNb)
{
adcConf.Rank = ADC_RANKS[channelNb];
adcConf.Channel = GPIO_AI_CH[channelNb];
adcConf.SamplingTime = ADC_SAMPLETIME_12CYCLES_5;
if (HAL_ADC_ConfigChannel(&hadc, &adcConf) != HAL_OK)
{
Error_Handler();
}
}
Where ADC_RANKS and GPIO_AI_CH are static arrays of the channels and ranks I want to use. The ranks increase with every channel.
Reading a channel
uint32_t ADC_Read_Ch(uint8_t channelNb)
{
uint32_t adc_value = 0;
ADC_Select_Ch(channelNb);
HAL_ADC_Start(&hadc);
if(HAL_OK == HAL_ADC_PollForConversion(&hadc, ADC_CONVERSION_TIMEOUT))
{
adc_value = HAL_ADC_GetValue(&hadc);
}
HAL_ADC_Stop(&hadc);
printf("Ch%d / %x) %d\r\n", channelNb, adcConf.Channel, adc_value);
return adc_value;
}
The problem
No matter what I try, the ADC only ever reads in the channel before the last channel I defined. Every time a conversion happens, the method HAL_ADC_GetValue(...) returns only the value of one channel, one, which I haven't even "selected" with my method.
What I've tried so far
I tried several different things:
Change NumberOfConversions
Change ScanMode, ContinuousConvMode, Overrun, EOCSelection, etc.
Use only Rank "1" when choosing a channel
Not use HAL_ADC_Stop(...), that however resulted in a failure (error handler was called)
Using the read functions etc. in the main(), not in a FreeRTOS thread - this also resulted in only one channel being read.
Change GPIO setup
Make the adcConfig global and public, so that maybe the config is shared among the channel selections.
Different clock settings
"Disabling" all other channels but the one I want to use (*)
Several other things which I've already forgotten
There seems to be one big thing I completely miss. Most of the examples are with one of the STM32Fxx microcontrollers, so maybe the ADC hardware is not the same and I can't do it this way. However, since I am using HAL, I should be able to do it this way. It would be weird, if it wouldn't be somehow the same across different uC families.
I really want to use polling, and ask one channel of the ADC by using some kind of channel selection, so that I can read them in different FreeRTOS tasks.
Disabling channels
I tried "disabling" channels but the one I've used with this function:
void ADC_Select_Ch(uint8_t channelNb)
{
for(int ch = 0; ch < GPIO_AI_COUNT; ch++)
{
adcConf.SamplingTime = ADC_SAMPLETIME_12CYCLES_5;
adcConf.Channel = GPIO_AI_CH[ch];
adcConf.Rank = ADC_RANK_NONE;
if (HAL_ADC_ConfigChannel(&hadc, &adcConf) != HAL_OK)
{
Error_Handler();
}
}
adcConf.SamplingTime = ADC_SAMPLETIME_12CYCLES_5;
adcConf.Channel = GPIO_AI_CH[channelNb];
if (HAL_ADC_ConfigChannel(&hadc, &adcConf) != HAL_OK)
{
Error_Handler();
}
}
Can anyone help me? I'm really stuck, and the Reference Manual does not provide a good "guide" on how to use it. Only technical information, lol.
Thank you!
I think your general approach seems reasonable. I've done something similar on a project (for an STM32F0), where I had to switch the ADC between two channels. I think you do need to disable the unused channels. Here is some code verbatim from my project:
static void configure_channel_as( uint32_t channel, uint32_t rank )
{
ADC_ChannelConfTypeDef sConfig = { 0 };
sConfig.Channel = channel;
sConfig.Rank = rank;
sConfig.SamplingTime = ADC_SAMPLETIME_1CYCLE_5;
if ( HAL_ADC_ConfigChannel ( &hadc, &sConfig ) != HAL_OK )
{
dprintf ( "Failed to configure channel\r\n" );
}
}
void adc_configure_for_head( void )
{
configure_channel_as ( ADC_CHANNEL_0, ADC_RANK_CHANNEL_NUMBER );
configure_channel_as ( ADC_CHANNEL_6, ADC_RANK_NONE );
}
void adc_configure_for_voltage( void )
{
configure_channel_as ( ADC_CHANNEL_6, ADC_RANK_CHANNEL_NUMBER );
configure_channel_as ( ADC_CHANNEL_0, ADC_RANK_NONE );
}
uint16_t adc_read_single_sample( void )
{
uint16_t result;
if ( HAL_ADC_Start ( &hadc ) != HAL_OK )
dprintf ( "Failed to start ADC for single sample\r\n" );
if ( HAL_ADC_PollForConversion ( &hadc, 100u ) != HAL_OK )
dprintf ( "ADC conversion didn't complete\r\n" );
result = HAL_ADC_GetValue ( &hadc );
if ( HAL_ADC_Stop ( &hadc ) != HAL_OK )
dprintf ( "Failed to stop DMA\r\n" );
return result;
}
My project was bare-metal (no-OS) and had a single thread. I don't know enough about how your tasks are scheduled, but I'd be concerned that they might be "fighting over" the ADC if there is a chance they could be run concurrently (or pre-empt each other). Make sure the entire configure / read sequence is protected by some kind of mutex or semaphore.
EDIT: I notice a bug in your "Disabling channels" code, where you don't seem to set the rank of your enabled channel. I don't know if that is a transcription error, or an actual bug.
Ranks are used to sort the ADC channels for cases of continuous measurrements or channel scans. HAL_ADC_PollForConversion only works on a single channel and somehow needs to now which channel to pick, therefore it will use the one with the lowest rank. To configure a specific channel to be measured once, set its rank to ADC_REGULAR_RANK_1.
No need to disable any other channels, but remember to properly configure the ranks of all channels if you want to switch to channel scanning or continuous measurements.
HAL_ADC_ConfigChannel(&hadc1, &channel_config) only updates the configuration of the channel itself but does not update the configuration of the ADC peripheral itself. So it has to be understood as "configure this channel" and not as "configure ADC to use this channel"

x710 VF use dpdk rte_flow_valida() return Function not implemented

OS:CentOS 7.3
DPDK:19.08
I use one X710 NIC, create 2 VFs in kernel driver i40e, and bind vfio-pci driver on VF 0 and Start a DPDK PMD application.
Then I try to create a Flow Rule use rte_flow, but it returns -38, Function not implemented when I called rte_flow_validate().
Does it means this VF doesn't support rte_flow API? or there are some configure or flags need to be set on VF?
DPDK RTE_FLOW are supported on both PF and VF for X710 (Fortville) NIC, with actions like
RTE_FLOW_ACTION_TYPE_QUEUE
RTE_FLOW_ACTION_TYPE_DROP
RTE_FLOW_ACTION_TYPE_PASSTHRU
RTE_FLOW_ACTION_TYPE_MARK
RTE_FLOW_ACTION_TYPE_RSS
The return value -38 for DPDK API is not Function not implemented, but actually I40E_ERR_OPCODE_MISMATCH. This means either Lookup parameters or match cases are improperly configured. Code Snippet that works on X710 VF, shared below
/* configure for 2 RX queues */
struct rte_flow_attr attr = { .ingress = 1 };
struct rte_flow_item pattern[10];
struct rte_flow_action actions[10];
struct rte_flow_item_eth eth;
struct rte_flow_item_eth eth_mask;
struct rte_flow_item_vlan vlan;
struct rte_flow_item_vlan vlan_mask;
struct rte_flow_item_ipv4 ipv4;
struct rte_flow_item_ipv4 ipv4_mask;
struct rte_flow *flow;
struct rte_flow_action_mark mark = { .id = 0xdeadbeef };
struct rte_flow_action_queue queue = { .index = 0x3 };
memset(&pattern, 0, sizeof(pattern));
memset(&actions, 0, sizeof(actions));
memset(&attr, 0, sizeof(attr));
attr.group = 0;
attr.priority = 0;
attr.ingress = 1;
attr.egress = 0;
memset(&eth_mask, 0, sizeof(struct rte_flow_item_eth));
pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH;
pattern[0].spec = ð
pattern[0].last = NULL;
pattern[0].mask = NULL;
memset(&vlan_mask, 0, sizeof(struct rte_flow_item_vlan));
pattern[1].type = RTE_FLOW_ITEM_TYPE_VLAN;
pattern[1].spec = &vlan;
pattern[1].last = NULL;
pattern[1].mask = NULL;
/* set the dst ipv4 packet to the required value */
pattern[1].type = RTE_FLOW_ITEM_TYPE_IPV4;
pattern[1].spec = NULL;
pattern[1].last = NULL;
pattern[1].mask = NULL;
pattern[2].type = RTE_FLOW_ITEM_TYPE_UDP;
pattern[2].spec = NULL;
pattern[2].last = NULL;
pattern[2].mask = NULL;
/* end the pattern array */
pattern[3].type = RTE_FLOW_ITEM_TYPE_END;
/* create the drop action */
actions[0].type = RTE_FLOW_ACTION_TYPE_MARK;
actions[0].conf = &mark;
actions[1].type = RTE_FLOW_ACTION_TYPE_END;
note: request #myzhu in comments to share the actual code snippet to root cause the issue too.

GPIO is uncontrolled when setting device-tree node without pinctrl-names

I'm working on imx8mm and testing GPIO with Linux kernel v4.14.98.
Device tree node is:
&iomuxc {
pinctrl-names = "default";
...
imx8mm-evk {
pinctrl_gpio_plural: gpiopluralgrp {
fsl,pins = <
MX8MM_IOMUXC_GPIO1_IO11_GPIO1_IO11 0x41
>;
};
};
};
...
plural {
compatible = "gpio-plural";
/* pinctrl-names = "default"; */
pinctrl-0 = <&pinctrl_gpio_plural>;
reset-gpios = <&gpio1 11 GPIO_ACTIVE_HIGH>;
};
and I wrote a driver to testing this
static int gpio_plural_probe(struct platform_device *pdev)
{
struct gpio_plural_data *drvdata;
drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL);
if (drvdata == NULL)
return -ENOMEM;
drvdata->reset = devm_gpiod_get(&pdev->dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(drvdata->reset)) {
printk("Error: reset not found\n");
return -EINVAL;
}
gpiod_set_value(drvdata->reset, 0);
mdelay(100);
gpiod_set_value(drvdata->reset, 1);
mdelay(100);
gpiod_set_value(drvdata->reset, 0);
mdelay(100);
gpiod_set_value(drvdata->reset, 1);
return 0;
}
However, I can't control GPIO pin when I comment pinctrl-names as device tree shown above. The GPIO pin always remains high.
In devicetree.c, the statename would be replaced to propname suffix, which in here is "0". But it just a constant name which could be any string. So my question is why I can't control GPIO pin without setting pinctrl-names?
It can't be any name, most of the node will have pinctrl-names = "default"; because this make pinctrl-0 the default state for the pins of the device.
This is actually quite important because the device core will use that to retrieve and set the proper state before probing the device, see pinctrl_bind_pins. It does:
dev->pins->default_state = pinctrl_lookup_state(dev->pins->p,
PINCTRL_STATE_DEFAULT);
where PINCTRL_STATE_DEFAULT is "default".
Then, it selects the state with:
ret = pinctrl_select_state(dev->pins->p, dev->pins->init_state);
If you don't want to use the default name, then, you'll have to select the proper state in your driver.
Other generic state names are:
#define PINCTRL_STATE_DEFAULT "default"
#define PINCTRL_STATE_INIT "init"
#define PINCTRL_STATE_IDLE "idle"
#define PINCTRL_STATE_SLEEP "sleep"

Linux, spidev: why it shouldn't be directly in devicetree?

I want to define a SPI device with usermode access, as explained for example in http://linux-sunxi.org/SPIdev
Following these examples, I added in the devicetree this :
&ecspi1 {
.... other stuff ...
mydev#0 {
compatible = "spidev";
spi-max-frequency = <5000000>;
reg = <2>; /*chipselect*/
};
};
The platform is i.MX6. ecspi1 seems to be their SPI controller.
Then I indeed get /dev/spi0.2 and /sys/class/spidev/spidev0.2
But in kernel trace there's a WARNING saying this:
spidev spi0.2: buggy DT: spidev listed directly in DT
So how else the spidev should be described? What is the right syntax?
spidev: why it shouldn't be directly in devicetree?
The Device Tree should describe the board's hardware, but
spidev does not describe/identify any hardware.
Mark Brown wrote:
Since spidev is a detail of how Linux controls a device rather than a
description of the hardware in the system we should never have a node
described as "spidev" in DT, any SPI device could be a spidev so this
is just not a useful description.
The rationale and workaround for this kernel patch is https://patchwork.kernel.org/patch/6113191/
So how else the spidev should be described? What is the right syntax?
Instead of explicit use of spidev in your Device Tree source, you instead need to identify the actual device that you're controlling, e.g.
mydev#0 {
- compatible = "spidev";
+ compatible = "my_spi_device";
spi-max-frequency = <5000000>;
Then (as Geert Uytterhoeven explains), modify drivers/spi/spidev.c in the kernel source code by adding the compatible value for your device to the spidev_dt_ids[] array
static const struct of_device_id spidev_dt_ids[] = {
{ .compatible = "rohm,dh2228fv" },
{ .compatible = "lineartechnology,ltc2488" },
{ .compatible = "ge,achc" },
{ .compatible = "semtech,sx1301" },
+ { .compatible = "my_spi_device" },
{},
}
An alternate solution, which involves a quick-n-dirty change to just the Device Tree, is suggested by this article.
Simply replace the "spidev" compatible string with a proper string that already does exist:
mydev#0 {
- compatible = "spidev";
+ compatible = "rohm,dh2228fv"; /* actually spidev for my_spi_dev */
spi-max-frequency = <5000000>;
Since "rohm,dh2228fv" is already in the spidev_dt_ids[] list, no edit to drivers/spi/spidev.c is needed.
To avoid this issue just use linux,spidev instead of spidev:
&spi0 {
mydev#0 {
compatible = "linux,spidev";
};
};

spidev to read eeprom id

OS: Linux
I'm writing a spidev application in userspace to read EEPROM id. I have my device tree entry as following:
spi0: spi#ffda4000 {
compatible = "snps,dw-apb-ssi";
#address-cells = <1>;
#size-cells = <0>;
reg = <0xffda4000 0x100>;
interrupt-parent = <&intc>;
interrupts = <GIC_SPI 101 IRQ_TYPE_LEVEL_HIGH>;
num-cs = <2>;
cs-gpios = <&porta 7 GPIO_ACTIVE_HIGH>, <&porta 0 GPIO_ACTIVE_HIGH>;
bus-num = <0>;
tx-dma-channel = <&pdma 16>;
rx-dma-channel = <&pdma 17>;
clocks = <&spi_m_clk>;
status = "disabled";
};
and then:
&spi0 {
status = "okay";
m25p10_spi#0 {
compatible = "m25p10";
reg = <0>; /* chip select */
spi-max-frequency = <20000000>;
/* m25p,fast-read; */
enable-dma = <0>;
};
spidev#0 {
compatible = "rohm,dh2228fv";
reg = <0>; /* chip select */
spi-max-frequency = <20000000>;
enable-dma = <0>;
};
};
Idea is to have spidev at same node as m25p10 so that when user space application open handle to "/dev/spidev0.0", it is actually talking to m25p10. But I can't get linux boot up. Is there anything wrong with this approach?
This will not work. One device - one definition in DT.
Also, why do you need to have spidev device? You already have m25p10_spi, which should show up as MTD device (something like /dev/mtd0), and there should be no problems accessing it from user space.
UPDATE:
It looks like OP wants to keep MTD and read device unique ID via RDID command, which is not supported by current m25p10 driver.
I might be wrong, but for me the easiest solution would be to extend the driver to create sysfs entry with RDID data, that is read during probing.
Some valuable resources:
LDD3
Linux kernel documentation