Open MackoLysy opened 6 years ago
Thanks for that request, indeed does the library not include slave support yet. The controller of the RK3288 anyway does support operation in slave mode. Please feel free to add this feature a make a PR. You can find some documentation directly in the wiki of Rockchip. And the Firefly people also host a lot of reference documents.
I can do that but can You give some adivce? From datasheet http://www.t-firefly.com/download/firefly-rk3288/docs/TRM/rk3288-chapter-42-serial-peripheral-interface-(spi).pdf I read that i need to change SPI_CTRLR0
bit 20
from 0x00
to 0x01
but how to configurate rest?? Like on Slave Mode i dont need to configurate cloack
Well. After hours of reading datasheet I make conclustion. I have to make something like this
void tinkerboard_spi_slave_init(enum SPIController controller, struct spi_mode_config_t mode_config) {
int32_t pin = _spi_configs[controller].clk;
_set_config(_rk3288_gpio_block_base + ALIGN(_gpio_header_pins[pin].grf_bank_offset),
_gpio_header_pins[pin].grf_pin_offset, 1, _gpio_header_pins[pin].grf_config_size);
_gpio_header_pins[pin].mode = SPI;
printf("Pin %d", &pin);
pin = _spi_configs[controller].txd;
_set_config(_rk3288_gpio_block_base + ALIGN(_gpio_header_pins[pin].grf_bank_offset),
_gpio_header_pins[pin].grf_pin_offset, 1, _gpio_header_pins[pin].grf_config_size);
_gpio_header_pins[pin].mode = SPI;
printf("Pin %d", &pin);
pin = _spi_configs[controller].rxd;
_set_config(_rk3288_gpio_block_base + ALIGN(_gpio_header_pins[pin].grf_bank_offset),
_gpio_header_pins[pin].grf_pin_offset, 1, _gpio_header_pins[pin].grf_config_size);
_gpio_header_pins[pin].mode = SPI;
printf("Pin %d", &pin);
_spi_internals[controller].cs_pin = mode_config.slave_select;
tinkerboard_set_gpio_mode(_spi_internals[controller].cs_pin, INPUT);
tinkerboard_set_gpio_pud(_spi_internals[controller].cs_pin, PULLUP);
uint32_t config = 0;
config |= 1 << 20;
config |= mode_config.data_frame_size << CR0_DFS_OFFSET;
config |= mode_config.clk_mode << CR0_SCPH_OFFSET;
config |= mode_config.byte_order << CR0_FBM_OFFSET;
config |= 1 << CR0_BHT_OFFSET;
config |= mode_config.transfer_mode << CR0_XFM_OFFSET;
_spi_set_ctrlr0(controller, config);
_spi_set_fifo_size(controller , 31);
_spi_enable_controller(controller, 1);
_spi_internals[controller].fifo_len = 31;
_spi_configs[controller].initialized = 1;
}
This is my init function. I use build in transfer function. I dont know if my clock function is set for slave. Can You plase help me a bit?
Great that you already had a look into it. Unfortunately I am pretty busy at the moment so I can't help you by experimenting myself with the RK3288 SPI controller. But I can give you my idea how it should work from my expectation.
The naive approach should be totally possible by just unmasking the interrupts and checking for them. About the DMA, I saw the register for activating it but did no further research on how to use and configure them. For the interrupt based approach, I know that a general interrupt controller exists and just assume that it can be possible to be used that way. For the DMA with and without the interrupt part, it would be maybe helpful to read the spi driver code found in the Linux kernel.
About your question about the clock speed. I assume that as long as the internal clockspeed for the controller is high enough it will adjust to the clk signal of the master. I set the divider in a way that 66.7MHz are possible so even if there is an additional division by 2 it is still way fast enough to read the data from a Raspberry Pi on max speed.
I hope my explanation helped you a bit finding your way into this topic. It might be difficult but should be totally possible. And it would be really cool if this feature can be added. Further maybe you should consider my patched build of armbian. It has the rt preempt patch set and SPI support of the kernel is disabled for those on the header. So these drivers wouldn't interfere with your code. https://github.com/arne48/armbian_build
Thanks for tips. I would like to know what static inline void _set_config(uint32_t *register_addr, uint32_t offset, uint32_t config, uint32_t config_length)
do. I try to figure out from this line of code.
uint32_t pin = _spi_configs[controller].clk;
_set_config(_rk3288_gpio_block_base + ALIGN(_gpio_header_pins[pin].grf_bank_offset),
_gpio_header_pins[pin].grf_pin_offset, 1, _gpio_header_pins[pin].grf_config_size);
_gpio_header_pins[pin].mode = SPI;
From your tips it looks like i have to:
config |= 1 << 20;
)tinkerboard_set_gpio_mode
)As a CPU resources it wont be a problem and it have to be SPI. Any request from you will be helpful.
And how I can recive Data from FIFO?
Now i have problem. My code dont work's on line before load config
you have ideas how to repair that?
The _set_config function sets the pin function by configuring the IOMUX. Because almost every pin can have several functions you need to set which component is finally using the pin. And here is the first thing what could be wrong. You need to set one (and I would start with CS0) of the chip select pins using this function. The register for that is GPIO8A[7] which you can find in the documentation of the GRF.
But the value to set it to the SPI function is 1 just as it is for the .clk, .rxd and .txd. So this code should do it:
uint32_t pin = _spi_configs[controller].cs0;
_set_config(_rk3288_gpio_block_base + ALIGN(_gpio_header_pins[pin].grf_bank_offset),
_gpio_header_pins[pin].grf_pin_offset, 1, _gpio_header_pins[pin].grf_config_size);
Reading and writing data from and to the FIFOs is done via the RXDR and TXDR registers.
About the interrupt handling. This is maybe the most tricky part. First I would try if after unmasking the interrupts in SPI_ISR gets set after writing data to the slave. An even more basic test should be if the SPI_RXFLR is counting up. With these tests you can check if the SPI controller is in general working in slave mode and reacting to its CS and the CLK of the master.
After that I would try to figure out how to use the DMA and then how Interrupt handling can be realized. Because as you said you basically don't care about how much the CPU has to work on this, I would approach it like this.
i finally set all to spi slave but now i get errors when i try to send and recive files. My buffers are stuck and use this function.
void tinkerboard_spi_transfer(enum SPIController controller, uint8_t* tx_buff, uint8_t* rx_buff, uint32_t length, struct spi_mode_config_t mode_config) {
_spi_enable_controller(controller, 1);
unsigned long remain = 0;
_spi_internals[controller].tx = tx_buff;
_spi_internals[controller].tx_end = tx_buff + length;
_spi_internals[controller].rx = rx_buff;
_spi_internals[controller].rx_end = rx_buff + length;
// Check if slave select is used
if(_spi_internals[controller].cs_pin != NO_SS) {
tinkerboard_set_gpio_state(_spi_internals[controller].cs_pin, LOW);
}
do {
if(_spi_internals[controller].tx) {
remain = _spi_internals[controller].tx_end - _spi_internals[controller].tx;
_spi_send(controller, mode_config);
}
if(_spi_internals[controller].rx) {
remain = _spi_internals[controller].rx_end - _spi_internals[controller].rx;
_spi_receive(controller, mode_config);
}
} while (remain);
// If spi controller is still busy keep waiting and eventually timing out
if(_spi_internals[controller].tx) {
_spi_wait_for_idle(controller);
}
// Check if slave select is used
if(_spi_internals[controller].cs_pin != NO_SS) {
tinkerboard_set_gpio_state(_spi_internals[controller].cs_pin, HIGH);
}
_spi_enable_controller(controller, 0);
}
Can you help me with this matter?
So i get to the point where i need to set interrupt on chip CS(or on whatever chip) and read data baiscly. But the biggest problem is how to set PIN interrupt.
and from my logic it should work like this
The main problem is how to set interrrupt and how to get data as slave. This buffer
Normally the CS when in slave mode should be handled by the hardware. The driver then works with the status registers to know when new data is available. The interrupt I was talking about should be raised when the buffers are empty. Like this you will have the most amount of free CPU cycles by utilizing the available hardware.
@MackoLysy how is it going, could you make some progress on the SPI slave topic?
Hello, after I configured SPI slave mode, the BUSY bit of the SR register remains BUSY, even if I fail to SPI.
@MackoLysy could you make some progress on the SPI slave topic?
Is a possible to set Tinker Board as slave via your library?