Porting u-boot-2020.04 to a EVB board, but spi nand flash(GD5F2GQ5xExxG) is not working - u-boot

problem description:
designware-spi controller embedded in SOC
spi nor flash(MX25U25635F) on evb CAN works well.
spi nand flash(GD5F2GQ5xExxG) on evb is not working,
when entering command "nand device or nand info", u-boot returns "no devices available"
my effort:
**dtsi snippet:**
spi0: spi#80000000 {
compatible = "snps,dw-ssi";
reg = <0x0 0x80000000 0x0 0x1000>;
#address-cells = <1>;
#size-cells = <0>;
clocks = <&sysclk>;
clock-names = "baudclk";
status = "okay";
spi_nand#1 {
#address-cells = <1>;
#size-cells = <1>;
compatible = "spi-nand";
spi-cpha = <1>;
spi-cpol = <1>;
spi-tx-bus-width = <1>;
spi-rx-bus-width = <1>;
reg = <1>;
spi-max-frequency = <50000000>;
};
};
xxx_defconfig:
CONFIG_CMD_BIND=y
CONFIG_CMD_DM=y
CONFIG_CMD_GPT=y
CONFIG_CMD_MTD=y
CONFIG_CMD_NAND=y
CONFIG_CMD_NAND_TRIMFFS=y
CONFIG_CMD_NAND_LOCK_UNLOCK=y
CONFIG_CMD_NAND_TORTURE=y
CONFIG_DM_MTD=y
CONFIG_MTD_RAW_NAND=y
CONFIG_MTD_SPI_NAND=y
CONFIG_SPI=y
CONFIG_DM_SPI=y
xxx_common.h
...
#define CONFIG_SYS_NAND_SELF_INIT
...
drivers\mtd\nand\raw\nand.c
void nand_init(void)
{
...
#ifdef CONFIG_SYS_NAND_SELF_INIT
board_nand_init();
#else
...
#endif
}
Only config spi controller's related register in function board_nand_init( ),
I want to call spinand_probe() in this function, but I don't know how to do it.
Can somebody help me? I am a newbie to u-boot. Thanks

Related

Sending enable interrupts (0xF4) command to ps2 keyboard results in system crash. OSDEV

I have just finished initializing the IDT and started the ps2 stuff. I got the ps2 controller initializer and now I want to enable the interrupts in the first ps2 port or the keyboard. Here is my code for initializing the ps2 controller:
uint8_t initPS2()
{
// Start by disabling devices
disableFirstPS2Port();
disableSecondPS2Port();
// Flush the input buffer
inb(PS2DATA);
// Set the state of the status register.
outb(PS2COMMAND, 0x20);
uint8_t status = inb(PS2DATA);
uint8_t dual0 = status & 0b00100000 == 0b00100000; // if dual = 0, there is no second channel
status |= 0b01000011;
outb(PS2COMMAND, 0x60);
outb(PS2DATA, status);
// Perform self test
outb(PS2COMMAND, 0xAA);
uint8_t response = inb(PS2DATA);
if (response == 0xFC)
return 0xFC;
outb(PS2COMMAND, 0x60);
outb(PS2DATA, status);
// test dual channel controller
uint8_t dual = 0xFF;
if (dual0 == 1)
{
outb(PS2COMMAND, 0xAE);
outb(PS2COMMAND, 0x20);
status = inb(PS2DATA);
uint8_t dual1 = status & 0b00100000 == 0b00100000;
// if dual1 == 0, dual channel exists
outb(PS2COMMAND, 0xAD);
dual = !dual1;
}
else
{
dual = 0;
}
// Perform interface tests
outb(PS2COMMAND, 0xAB);
uint8_t responseIT1 = inb(PS2DATA);
uint8_t responseIT2;
if (dual == 1)
{
outb(PS2COMMAND, 0xA9);
responseIT2 = inb(PS2DATA);
}
if (responseIT1 != 0x0)
return responseIT1;
if (responseIT2 != 0x0 && dual == 1)
return responseIT2;
// Enable devices
enableFirstPS2Port();
if (dual == 1)
enableSecondPS2Port();
writeToFirstPS2Port(0xFF);
response = readPS2DataPort();
if (response == 0xFC)
return 0xFC;
writeToSecondPS2Port(0xFF);
response = readPS2DataPort();
if (response == 0xFC)
return 0xFC;
return 0;
}
I follow the tutorial on OSDev wiki for initializing the ps2 controller.
Now I try to enable scanning on the keyboard with the 0xf4 command:
uint8_t commandKeyboard(unsigned char byt)
{
writeToFirstPS2Port(byt);
uint8_t response = readPS2DataPort();
if (response == 0xFE)
{
writeToFirstPS2Port(byt);
response = readPS2DataPort();
if (response == 0xFE)
{
writeToFirstPS2Port(byt);
response = readPS2DataPort();
}
}
return response;
}
uint8_t initKeyboard()
{
uint8_t res = commandKeyboard(0xF4);
return res;
}
Note that the writeToFirstPS2Port and likewise other ps2 function do have a proper controllerWait function which makes sure that the data port is writable/readable.
I call this code before doing sti after I load my idt:
idt_flush(&currentIDT);
uint8_t ps2init = initPS2();
printk("PS2 Initalized, returned with 0x%x\n", ps2init);
while (1)
;
uint8_t keyboardinit = initKeyboard();
printk("PS2 Initalized, returned with 0x%x\n", keyboardinit);
outb(PIC1_DATA, 0);
outb(PIC2_DATA, 0);
//int d = 5 / 0;
asm volatile("sti");
Also here is my IRQ Handler:
void irq1_handler(interrupt_frame_t *frame)
{
inb(0x60);
default_irq_handler(frame);
}
I think the keyboard controller required for me to read 0x60 port before sending another interrupt so I thought that may help.
With this code here is what happens. I successfully initialize the IDT and then call the initializePS2 which returns with 0, meaning it succeeded. After that I start the initKeyboard call which doesn't even return let alone succeed. When I send the command to the keyboard, the system crashes. I am running this using qemu and I first thought this was a triple fault so I added -d int to see what kind of exceptions were raised but it seems that no exception is raised before the system closes itself.
Here is the github link to the project:
https://github.com/Danyy427/NEWOSDEV/tree/master/new%2064%20bit
The files are under src/Kernel/Hardware/PS2 and .../Hardware/Keyboard
The IDT is under src/Kernel/IDT
Any help is appreciated. Thank you.
First add the interrupt attribute on your interrupt handler :
void __attribute__((interrupt)) Irq1Handler(interrupt_frame* frame);
This will cause the compiler to get parameters from stack rather than getting them using __cdecl calling convention. And also performing an iret (Interrupt Return) rather than a normal ret (Return). Also add a parameter to gcc when compiling your interrupts file "-mgeneral-regs-only". This will prevent the compiler from using sse registers and generating a warning about that.

How to change stm32 PLL parameters in uboot device tree?

I'm new to u-boot and I have a custom board based on the Waveshare CoreH7 board. my crystal(HSE) frequency is 8MHz and I want to have a 400MHZ core frequency using the PLL1. according to this documentation I should add the below code to my board.dts file.
rcc {
vco1#58024430 {
#clock-cells = <0>;
compatible = "stm32,pll";
reg = <0>;
st,clock-div = <4>; // 8MHz divided by 4 = 2MHz
st,clock-mult = <400>; // (2MHz * 400)/2 = 400MHz
st,frac-status = <0>;
st,frac = <0>;
//st,vcosel = <1>;
st,pllrge = <1>;
};
};
so, now my frequency should be 400MHz. but it's not!
I set the uboot logging level to 8 to see all logs. and after checking the output logs on the serial terminal, I realized that u-boot even won't care about those "vco1" parameters in my device tree. so I dicided to take a look at the rcc driver, and when I saw the code, I realized that pll parameters are defined in the driver itself, and the driver even won't care about those parameters that I wrote them in my device tree.
this is a part of stm32h7 driver at 329th line:
/*
* OSC_HSE = 25 MHz
* VCO = 500MHz
* pll1_p = 250MHz / pll1_q = 250MHz pll1_r = 250Mhz
*/
struct pll_psc sys_pll_psc = {
.divm = 4,
.divn = 80,
.divp = 2,
.divq = 2,
.divr = 2,
};
I can't understand what's happening here! As I know, the driver should read these parameters from device tree. So, if the user should write these parameters in the device tree, why the driver has these values in it when it can read them from device tree?

How to use I2C GPIO expander's pin instead of RTS to control the RS485 direction, in Linux AUART kernel driver?

I'm creating an embedded system based on i.MX287 processor from NXP(Freescale). I'm using a core processing board which is connected to my evaluation board via a mini PCIe connector.
UARTs 0,3,4 are used as RS232 and UARTs 1,2 as RS485. The core board does not provide the RTS signals in its pinout, so I have to use pins from an I2C GPIO expander to control the RS485 direction. The GPIO expander module is also used for controlling some other devices on the board.
In user-space, I can control the direction pin using libi2c, but my client asked me to put the direction pin control in the UART driver.
Questions:
1- how can I interact with an i2c device inside the auart driver? (is it possible)
2- if it is possible, then how to prevent the i2c-0 bus from being blocked by the kernel? (I also need the userspace calls to the libi2c to work properly)
I googled a lot, but most cases are about how to use the I2C driver or how to activate GPIO pins in the sysfs, and I was able to do all of those.
The libi2c is for userspace so I cannot call it here. I also know that opening a file(/dev/i2c-0) in kernel and reading or writing to it is not a good idea. I am trying to understand what is the best way to handle this problem, without causing any concurrent access issues.
I would appreciate any ideas
P.S. - I don't have a deep understanding of how Linux kernel works, so sorry if my question is a little vague.
Edit 1:
based on #0andriy 's suggestion, I edited the DTS file and added the following to /arch/arm/boot/dts/my_dts_file.dts:
/dts-v1/;
#include "imx28.dtsi"
/ {
// some definitions
apbx#80040000 {
i2c0: i2c#80058000 {
pca8575: gpio#20 {
compatible = "nxp,pca8575";
reg = <0x20>; // PCA8575PW Address -0-0-0
gpio-controller;
#gpio-cells = <2>;
};
};
auart1: serial#8006c000 {
pinctrl-names = "default";
pinctrl-0 = <&auart1_2pins_a>;
linux,rs485-enabled-at-boot-time;
rs485-rts-delay = <0 0>; // in milliseconds
rts-gpios = <&pca8575 4 GPIO_ACTIVE_LOW>;
rs485-rts-active-low;
status = "okay";
};
auart2: serial#8006e000 {
pinctrl-names = "default";
pinctrl-0 = <&auart2_2pins_b>;
linux,rs485-enabled-at-boot-time;
rs485-rts-delay = <0 0>; // in milliseconds
rts-gpios = <&pca8575 5 GPIO_ACTIVE_LOW>;
rs485-rts-active-low;
status = "okay";
};
};
// some definitions
};
and then rebuilt the kernel. I also edited the mxs_auart_init_gpios function in the mxs-auart.c driver to print out the pin description of all the auart GPIOs at boot time. but gpiod = mctrl_gpio_to_gpiod(s->gpios, i) is always NULL.
the pca8575 GPIO controller is not added under /sys/class/gpio/
root# ls /sys/class/gpio
export gpiochip128 gpiochip64 unexport
gpiochip0 gpiochip32 gpiochip96
Edit 2:
auart1_2pins_a and auart2_2pins_b from the imx28.dtsi file :
auart2_2pins_b: auart2-2pins#1 {
reg = <1>;
fsl,pinmux-ids = <
MX28_PAD_AUART2_RX__AUART2_RX
MX28_PAD_AUART2_TX__AUART2_TX
>;
fsl,drive-strength = <MXS_DRIVE_4mA>;
fsl,voltage = <MXS_VOLTAGE_HIGH>;
fsl,pull-up = <MXS_PULL_DISABLE>;
};
auart1_2pins_a: auart1-2pins#0 {
reg = <0>;
fsl,pinmux-ids = <
MX28_PAD_AUART1_RX__AUART1_RX
MX28_PAD_AUART1_TX__AUART1_TX
>;
fsl,drive-strength = <MXS_DRIVE_4mA>;
fsl,voltage = <MXS_VOLTAGE_HIGH>;
fsl,pull-up = <MXS_PULL_DISABLE>;
};
I'm using kernel 4.14.13
the figure below demonstrates what I'm trying to achieve :
I'm not familiar at all with your board so take this answer with a pinch of salt, but I've noticed some funny things on your files.
First off, you need to define the I2C pin you want to use for toggling the direction inside the UART pinmux:
auart2_2pins_b: auart2-2pins#1 {
reg = <1>;
fsl,pinmux-ids = <
MX28_PAD_AUART2_RX__AUART2_RX
MX28_PAD_AUART2_TX__AUART2_TX
MX28_PAD_I2C0_SCL__I2C0_SCL
>;
fsl,drive-strength = <MXS_DRIVE_4mA>;
fsl,voltage = <MXS_VOLTAGE_HIGH>;
fsl,pull-up = <MXS_PULL_DISABLE>;
};
Make sure you double-check the pin name you want to use, I cannot be sure that is the right one.
Then, you seem to be missing the pinctrl for the I2C controller:
i2c0: i2c#80058000 {
pinctrl-names = "default";
pinctrl-0 = <&i2c0_pins_a>;
status = "okay";
pca8575: gpio#20 {
compatible = "nxp,pca8575";
reg = <0x20>; // PCA8575PW Address -0-0-0
gpio-controller;
#gpio-cells = <2>;
};
};
I could not confirm your reg and your pin numbers but I'm assuming you took it from your board's documentation. If you didn't, make sure you find a reliable source for your hardware.
Finally, I'm not sure why you want to have the RTS line active low, most transceivers have a DE/~RE input, which means you need to have the line active high to drive the bus. Maybe your driver is different...
What you are trying to do is documented to be working for other boards so I guess unless there is a bug you should be able to make it work.

How to make transmission code about CAN protocol

I'm using NUCLEO-F103RB Board and SN65HVD230 CAN transceiver.
and CAN mode is CAN_MODE_LOOPBACK.
workspace is keil uvision(IDE), STM32 ST-LINK(debugger?).
I have a sample code what my teacher gives me.
it's STMICRO'S sample code.
but i really don't know how to use..
first, I know how to set stdId, DLC, DATA[0]~[7]
it is like this.
tx_msg.StdId = 0x701;
tx_msg.DLC = 8;
tx_msg.Data[0] = 0x11;
tx_msg.Data[1] = 0x22;
tx_msg.Data[2] = 0x33;
tx_msg.Data[3] = 0x44;
tx_msg.Data[4] = 0x55;
tx_msg.Data[5] = 0x66;
tx_msg.Data[6] = 0x77;
tx_msg.Data[7] = 0x88;
as I know, next step is making transmission code.
but how to make trasmission code?
using can_tx(argument...) like this?
setting register about CAN protocol (but my teacher said there is no changing register or interrupt.)
also, I think this is transmit function, maybe....
can_tx(uint32_t CanTxMsgTypeDef *tx_msg),uint32_t timeout)
internal argument->()

Linux 4.5 GPIO Interrupt Through Devicetree on Xilinx Zynq Platform

I am using a custom development board with a Zynq XC72010 used to run a Linux 4.5 kernel. I am developing a device driver for a chip we are testing in house and I am having a lot of issues trying to bind a GPIO line to a software IRQ. So far I have tried a few methods and exhausted any google search I can think of. The relevant parts of my devicetree configuration:
/ {
compatible = "xlnx,zynq-7000";
amba {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <1>;
interrupt-parent = <&intc>;
ranges;
intc: interrupt-controller#f8f01000 {
compatible = "arm,cortex-a9-gic";
#interrupt-cells = <3>;
interrupt-controller;
reg = <0xF8F01000 0x1000>,
<0xF8F00100 0x100>;
};
i2c0: i2c#e0004000 {
compatible = "cdns,i2c-r1p10";
status = "disabled";
clocks = <&clkc 38>;
interrupt-parent = <&intc>;
interrupts = <0 25 4>;
reg = <0xe0004000 0x1000>;
#address-cells = <1>;
#size-cells = <0>;
// I WANT INTERRUPT TO TRIGGER
// ON THIS DEVICE (axi_gpio_0, pin 2)
device: device#48 {
compatible = "device,name";
reg = <0x48>;
reset-gpios = <&axi_gpio_0 1 0>;
interrupt-parent = <&axi_gpio_0>;
interrupt-gpios = <&axi_gpio_0 2 0>;
};
};
};
amba_pl {
#address-cells = <1>;
#size-cells = <1>;
compatible = "simple-bus";
ranges ;
axi_gpio_0: gpio#41200000 {
#gpio-cells = <2>;
compatible = "xlnx,xps-gpio-1.00.a";
gpio-controller;
interrupt-parent = <&intc>;
interrupts = <0 31 4>;
reg = <0x41200000 0x10000>;
xlnx,all-inputs = <0x0>;
xlnx,all-inputs-2 = <0x0>;
xlnx,all-outputs = <0x0>;
xlnx,all-outputs-2 = <0x0>;
xlnx,dout-default = <0x00000000>;
xlnx,dout-default-2 = <0x00000000>;
xlnx,gpio-width = <0x10>;
xlnx,gpio2-width = <0x20>;
xlnx,interrupt-present = <0x1>;
xlnx,is-dual = <0x0>;
xlnx,tri-default = <0xFFFFFFFF>;
xlnx,tri-default-2 = <0xFFFFFFFF>;
};
};
I am trying to assign an interrupt to pin 2 of 'axi_gpio_0' inside of 'device'.
Browsing google yielded 3 common methods for binding the interrupt in driver code:
/* Method 1 */
device->interrupt_gpio = devm_gpiod_get_optional(&i2c_client->dev,
"interrupt", GPIOD_IN);
if(IS_ERR(device->interrupt_gpio))
return PTR_ERR(device->interrupt_gpio);
printk("device: Interrupt GPIO = %d\n",desc_to_gpio(device->interrupt_gpio));
irq = gpiod_to_irq(device->interrupt_gpio);
printk("device: IRQ = %d\n",irq);
ret = devm_request_threaded_irq(&i2c_client->dev, irq,
NULL, device_irq_thread, IRQF_ONESHOT | IRQF_TRIGGER_HIGH,
"device", device);
if (ret != 0)
dev_err(&i2c_client->dev, "Failed to request IRQ: %d\n", ret);
/* Method 2 */
device->interrupt_gpio = devm_gpiod_get_optional(&i2c_client->dev,
"interrupt", GPIOD_ASIS);
if (IS_ERR(device->interrupt_gpio))
return PTR_ERR(device->interrupt_gpio);
if (device->interrupt_gpio) {
dev_info(&i2c_client->dev, "Found interrupt GPIO: %d\n",desc_to_gpio(device->interrupt_gpio));
dev_info(&i2c_client->dev, "IRQ Number: %d\n",gpiod_to_irq(device->interrupt_gpio));
gpio_request(desc_to_gpio(device->interrupt_gpio), "DEVICE_INT"); // Request a GPIO pin from the driver
gpio_direction_input(desc_to_gpio(device->interrupt_gpio)); // Set GPIO as input
gpio_set_debounce(desc_to_gpio(device->interrupt_gpio), 50); // Set a 50ms debounce, adjust to your needs
gpio_export(desc_to_gpio(device->interrupt_gpio), false); // The GPIO will appear in /sys/class/gpio
ret = request_irq(gpiod_to_irq(device->interrupt_gpio), // requested interrupt
(irq_handler_t) irqHandler, // pointer to handler function
IRQF_TRIGGER_RISING, // interrupt mode flag
"DEVICE_IRQ_HANDLER", // used in /proc/interrupts
NULL); // the *dev_id shared interrupt lines, NULL is okay
if (ret != 0) {
dev_err(&i2c_client->dev,
"Failed to request IRQ: %d\n", ret);
}
}
else {
dev_err(&i2c_client->dev, "Failed to get interrupt GPIO pin\n");
}
/* Method 3 */
dev_info(&i2c_client->dev, "IRQ requested: %d\n", i2c_client->irq);
ret = devm_request_threaded_irq(&i2c_client->dev, i2c_client->irq,
NULL, device_irq_thread, IRQF_ONESHOT | IRQF_TRIGGER_LOW,
"device", device);
if (ret != 0)
dev_err(&i2c_client->dev, "Failed to request IRQ: %d\n", ret);
I tried combinations of all of these methods and various devicetree configurations, but none of them achieved the functionality I need.
Method 1 results in this output:
device: Interrupt GPIO = 892
device: IRQ = -6
device 0-0048: Failed to request IRQ: -22
Method 2 results in this output:
device 0-0048: Found interrupt GPIO: 892
device 0-0048: IRQ Number: -6
device 0-0048: Failed to request IRQ: -22
So, trying to use the descriptor GPIO and the old GPIO api's are both unsuccessful in binding an interrupt.
To try method 3, I tweaked the devicetree:
device: device#48 {
compatible = "device,name";
reg = <0x48>;
interrupt-parent = <&axi_gpio_0>; // or <&intc>?
interrupts = <0 2 0x02>; // trying to grab pin 2
};
Method 3 results in this output:
genirq: Setting trigger mode 2 for irq 168 failed (gic_set_type+0x0/0x48)
device 0-0048: IRQ requested: 168
genirq: Setting trigger mode 8 for irq 168 failed (gic_set_type+0x0/0x48)
device 0-0048: Failed to request IRQ: -22
It seems the problem is assigning a software interrupt to a specific GPIO in Linux. I don't see what I'm missing here. Any advice is appreciated.
EDIT 1:
I found out that Linux doesn't like low-level interrupts for whatever reason. Changing method 3 to:
device: device#48 {
compatible = "device,name";
reg = <0x48>;
interrupt-parent = <&axi_gpio_0>;
interrupts = <0 2 0x04>;
};
And driver code to:
dev_info(&i2c_client->dev, "IRQ requested: %d\n", i2c_client->irq);
ret = devm_request_threaded_irq(&i2c_client->dev, i2c_client->irq,
NULL, device_irq_thread, IRQF_ONESHOT | IRQF_TRIGGER_HIGH,
"device", device);
if (ret != 0)
dev_err(&i2c_client->dev, "Failed to request IRQ: %d\n", ret);
allows me to successfully request an IRQ. However, my signal is active low, so this doesn't really help me much. Also, I'm not sure that this method is referencing axi_gpio_0 pin 2 as the interrupt signal. I can use both intc and axi_gpio_0 as interrupt-parent and it maps to the same IRQ number (I see this from cat /proc/interrupts). So, ignoring the polarity of the signal, how do I make sure that my registered interrupt is triggered based on the toggling of axi_gpio_0 pin 2?
EDIT 2:
I traced the issue with requesting an active-low interrupt to the driver for the interrupt controller: kernel/drivers/irqchip/irq-gic.c. This section of code is what causes the problem:
static int gic_set_type(struct irq_data *d, unsigned int type)
{
void __iomem *base = gic_dist_base(d);
unsigned int gicirq = gic_irq(d);
/* Interrupt configuration for SGIs can't be changed */
if (gicirq < 16)
return -EINVAL;
/* SPIs have restrictions on the supported types */
if (gicirq >= 32 && type != IRQ_TYPE_LEVEL_HIGH &&
type != IRQ_TYPE_EDGE_RISING)
return -EINVAL;
return gic_configure_irq(gicirq, type, base, NULL);
}
Hacking the kernel is not at all what I want to do, but commenting out:
/* SPIs have restrictions on the supported types */
/*if (gicirq >= 32 && type != IRQ_TYPE_LEVEL_HIGH &&
type != IRQ_TYPE_EDGE_RISING)
return -EINVAL;*/
allows me to request an active-low interrupt. For testing purposes, this should work temporarily.
UPDATE:
I have successfully created an IRQ on a GPIO pin. My problem was with the GPIO controller I was using. The controller was a Xilinx IP block inside of the Zynq Programmable Logic block and this controller is unable to trigger interrupts on GPIO pins (for reasons unknown to me). I soldered the interrupt pin on the board I was working on to a GPIO pin on a different, more generic controller and now Linux is playing nicely with me.
To summarize, a GPIO controller that matches compatible = "xlnx,xps-gpio-1.00.a"; is unable to bind to software interrupts in Linux. If you have this problem, USE A DIFFERENT GPIO CONTROLLER.
Thanks everyone for the help.
Using the device tree node of method 3, you should be able to retrieve the IRQ using irq_of_parse_and_map(i2c_client->dev.of_node, 0).
The retrieved IRQ can then be requested as you have done it with devm_request_threaded_irq().

Resources