kopia lustrzana https://github.com/pa3gsb/Radioberry-2.x
radioberry @pi-5
rodzic
9a57ad6083
commit
80f256ab91
|
@ -0,0 +1,25 @@
|
|||
KERNEL_HEADERS=/lib/modules/$(shell uname -r)/build
|
||||
|
||||
obj-m := radioberry.o
|
||||
|
||||
all: radioberry dt
|
||||
echo Build Device Tree Overlay and kernel module.
|
||||
|
||||
|
||||
radioberry:
|
||||
@$(MAKE) -C $(KERNEL_HEADERS) M=$(PWD) modules
|
||||
|
||||
dt:
|
||||
dtc -@ -I dts -O dtb -o radioberry.dtbo radioberry.dts
|
||||
|
||||
clean:
|
||||
@$(MAKE) -C $(KERNEL_HEADERS) M=$(PWD) clean
|
||||
|
||||
install: radioberry.ko
|
||||
cp radioberry_ioctl.h /usr/local/include
|
||||
sudo insmod radioberry.ko
|
||||
sudo chmod 666 /dev/radioberry
|
||||
|
||||
uninstall:
|
||||
sudo rmmod radioberry.ko
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
## Radioberry Device Driver
|
||||
|
||||
|
||||
This device driver is a special for the RPI-5.
|
||||
|
||||
|
||||
Build step:
|
||||
|
||||
First get the linux headers:
|
||||
|
||||
sudo apt-get install raspberrypi-kernel-headers
|
||||
|
||||
Second you need to start the build process:
|
||||
|
||||
use in a command window, in the folder driver (which contains the Makefile) : make
|
||||
|
||||
This will results in a radioberry.ko file and radioberry.dtbo file.
|
||||
|
||||
|
||||
|
||||
Hereby a step by step setup:
|
||||
|
||||
|
||||
Step -1-
|
||||
|
||||
CL025 FPGA Radioberry users:
|
||||
Copy the gateware radioberry.rbf into the folder /lib/firmware
|
||||
|
||||
CL016 FPGA Radioberry users:
|
||||
Rename your radioberry-10CL016.rbf gateware to radioberry.rbf
|
||||
Copy the gateware radioberry.rbf into the folder /lib/firmware
|
||||
|
||||
Loading the device driver will also load the gateware (content of the rbf file) into the FPGA
|
||||
|
||||
|
||||
|
||||
Step -2-
|
||||
|
||||
Make a module folder in the driver area of your running kernel, and install the driver called radioberry.ko in this folder.
|
||||
|
||||
cd /lib/modules/$(uname -r)/kernel/drivers
|
||||
|
||||
sudo mkdir sdr
|
||||
|
||||
copy the radioberry.ko to /lib/modules/$(uname -r)/kernel/drivers/sdr using the command:
|
||||
|
||||
sudo cp radioberry.ko /lib/modules/$(uname -r)/kernel/drivers/sdr
|
||||
|
||||
|
||||
|
||||
Step -3-
|
||||
|
||||
run the command: sudo depmod
|
||||
|
||||
Check: use the command: sudo modinfo radioberry
|
||||
|
||||
Gives you detailed info about the radioberry device driver.
|
||||
|
||||
|
||||
|
||||
Step -4-
|
||||
|
||||
load the device driver:
|
||||
|
||||
run the command sudo dtoverlay radioberry.dtbo
|
||||
|
||||
run the command: sudo modprobe radioberry
|
||||
|
||||
In the folder /dev the radioberry must be present using the ls command.
|
||||
Also possible to check by the command: lsmod |grep radioberry
|
||||
|
||||
|
||||
Step -5-
|
||||
|
||||
Optional step.
|
||||
|
||||
|
||||
cp this radioberry.dtbo into /boot/overlays
|
||||
|
||||
add the following line in config.txt:
|
||||
|
||||
(location /boot/firmware/config.txt)
|
||||
|
||||
dtoverlay=radioberry
|
||||
|
||||
This loads the kernel module during boot.
|
||||
|
||||
|
||||
|
||||
Step -6-
|
||||
|
||||
Optional step.
|
||||
|
||||
sudo chmod 666 /dev/radioberry
|
||||
|
||||
Makes it possible to run the radioberry firmware version for the device driver, running as the logged in user:
|
||||
|
||||
|
||||
|
||||
Step -7-
|
||||
|
||||
Run using the command ./radioberry firmware or sudo ./radioberry
|
||||
|
||||
|
||||
Step -8-
|
||||
Start a SDR program!
|
||||
|
||||
|
||||
Have fun listening to your Radioberry using the radioberry device driver.
|
||||
|
||||
73 Johan
|
||||
PA3GSB
|
||||
|
||||
|
|
@ -0,0 +1,414 @@
|
|||
/*
|
||||
This is free and unencumbered software released into the public domain.
|
||||
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
distribute this software, either in source code form or as a compiled
|
||||
binary, for any purpose, commercial or non-commercial, and by any
|
||||
means.
|
||||
|
||||
In jurisdictions that recognize copyright laws, the author or authors
|
||||
of this software dedicate any and all copyright interest in the
|
||||
software to the public domain. We make this dedication for the benefit
|
||||
of the public at large and to the detriment of our heirs and
|
||||
successors. We intend this dedication to be an overt act of
|
||||
relinquishment in perpetuity of all present and future rights to this
|
||||
software under copyright law.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
For more information, please refer to <http://unlicense.org/>
|
||||
|
||||
sudo cp radioberry.ko /lib/modules/$(uname -r)/kernel/drivers/sdr
|
||||
*/
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/fcntl.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/circ_buf.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
|
||||
static struct mutex spi_mutex;
|
||||
|
||||
#include "radioberry_rpi.h"
|
||||
#include "radioberry_ioctl.h"
|
||||
#include "radioberry_gateware.h"
|
||||
#include "radioberry_firmware.h"
|
||||
|
||||
#define VERSION "5.01"
|
||||
#define VERSION_INT 501
|
||||
|
||||
static DEFINE_MUTEX(radioberry_mutex);
|
||||
|
||||
#define DEVICE_NAME "radioberry"
|
||||
#define DRIVER_NAME "radioberry"
|
||||
#define CLASS_NAME "radioberry"
|
||||
|
||||
static int majorNumber;
|
||||
static struct class* radioberryCharClass = NULL;
|
||||
static struct device* radioberryCharDevice = NULL;
|
||||
|
||||
#define SAMPLE_BYTES 512
|
||||
static int _nrx = 1;
|
||||
|
||||
static int spi_ctrl_probe(struct spi_device *spi)
|
||||
{
|
||||
printk(KERN_INFO "inside %s function \n", __FUNCTION__);
|
||||
|
||||
spi_ctrl_dev = spi;
|
||||
|
||||
int ret = spi_setup(spi);
|
||||
if (ret) {
|
||||
pr_err("Failed to set up SPI communication\n");
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void spi_ctrl_remove(struct spi_device *spi)
|
||||
{
|
||||
printk(KERN_INFO "inside %s function \n", __FUNCTION__);
|
||||
}
|
||||
|
||||
// Declare the SPI driver structure
|
||||
static struct spi_driver radioberry_spi_ctrl_driver = {
|
||||
.driver = {
|
||||
.name = "radioberry_ctrl_spi",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = spi_ctrl_probe,
|
||||
.remove = spi_ctrl_remove,
|
||||
};
|
||||
|
||||
|
||||
static int spi_tx_probe(struct spi_device *spi)
|
||||
{
|
||||
printk(KERN_INFO "inside %s function \n", __FUNCTION__);
|
||||
|
||||
spi_tx_dev = spi;
|
||||
|
||||
int ret = spi_setup(spi);
|
||||
if (ret) {
|
||||
pr_err("Failed to set up SPI communication\n");
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void spi_tx_remove(struct spi_device *spi)
|
||||
{
|
||||
printk(KERN_INFO "inside %s function \n", __FUNCTION__);
|
||||
}
|
||||
|
||||
// Declare the SPI driver structure
|
||||
static struct spi_driver radioberry_spi_tx_driver = {
|
||||
.driver = {
|
||||
.name = "radioberry_tx_spi",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = spi_tx_probe,
|
||||
.remove = spi_tx_remove,
|
||||
};
|
||||
|
||||
|
||||
static void firmware_load(char *firmware, int size) {
|
||||
printk(KERN_INFO "inside %s function \n", __FUNCTION__);
|
||||
|
||||
u8 *buf = kmalloc(size + 1, GFP_KERNEL);
|
||||
memcpy(buf, firmware, size);
|
||||
buf[size] = '\0';
|
||||
|
||||
printk(KERN_INFO "Radioberry gateware file size: %d\n", size);
|
||||
|
||||
initialize_gateware();
|
||||
prepare_gateware_loading();
|
||||
int b = 0;
|
||||
for (b = 0; b < size; b++) {
|
||||
upload_gateware_byte(buf[b]);
|
||||
}
|
||||
activate_gateware();
|
||||
|
||||
kfree(buf);
|
||||
}
|
||||
|
||||
static void loading_radioberry_gateware(struct device *dev) {
|
||||
printk(KERN_INFO "inside %s function \n", __FUNCTION__);
|
||||
|
||||
const struct firmware *fw_entry;
|
||||
if (request_firmware(&fw_entry, "radioberry.rbf", dev) != 0 ) {
|
||||
printk(KERN_ERR "gateware radioberry.rbf: Firmware not available\n");
|
||||
return;
|
||||
}
|
||||
|
||||
firmware_load(fw_entry->data, fw_entry->size);
|
||||
|
||||
release_firmware(fw_entry);
|
||||
}
|
||||
|
||||
ssize_t radioberry_read(struct file *flip, char *buf, size_t count, loff_t *pos) {
|
||||
unsigned char rx_stream[SAMPLE_BYTES]={};
|
||||
while (read_pin(25) == 0) {udelay(1);};
|
||||
count = rxStream(_nrx, rx_stream);
|
||||
if (copy_to_user((char *)buf, &rx_stream, count)) return -EFAULT;
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t radioberry_write(struct file *flip, const char *buf, size_t count, loff_t *pos) {
|
||||
|
||||
unsigned char tx_stream[4];
|
||||
|
||||
if (count > 0) {
|
||||
if (copy_from_user(&tx_stream, buf, sizeof(tx_stream))) {
|
||||
return -EFAULT;
|
||||
}
|
||||
return write_iq_sample(tx_stream);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int radioberry_open(struct inode *inode, struct file *filep) {
|
||||
|
||||
printk(KERN_INFO "inside %s function \n", __FUNCTION__);
|
||||
|
||||
if(!mutex_trylock(&radioberry_mutex)){
|
||||
printk(KERN_ALERT "Radioberry Char: Device in use by another process");
|
||||
return -EBUSY;
|
||||
}
|
||||
int *minor = (int *)kmalloc(sizeof(int), GFP_KERNEL);
|
||||
int major = MAJOR(inode->i_rdev);
|
||||
*minor = MINOR(inode->i_rdev);
|
||||
filep->private_data = (void *)minor;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int radioberry_release(struct inode *inode, struct file *filep) {
|
||||
|
||||
printk(KERN_INFO "inside %s function \n", __FUNCTION__);
|
||||
|
||||
kfree(filep->private_data);
|
||||
mutex_unlock(&radioberry_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long radioberry_ioctl(struct file *fp, unsigned int cmd, unsigned long arg){
|
||||
|
||||
//printk(KERN_INFO "inside %s function \n", __FUNCTION__);
|
||||
|
||||
unsigned char data[6];
|
||||
int lnrx = _nrx;
|
||||
|
||||
int rc;
|
||||
struct rb_info_arg_t *rb_info= kmalloc(sizeof(struct rb_info_arg_t), GFP_DMA);
|
||||
|
||||
struct rb_info_arg_t rb_info_ret;
|
||||
|
||||
switch(cmd){
|
||||
case RADIOBERRY_IOC_COMMAND:
|
||||
|
||||
rc = copy_from_user(rb_info, (void *)arg, sizeof(struct rb_info_arg_t));
|
||||
|
||||
data[0] = ( rb_info->rb_command & 0xFF); //MSB
|
||||
data[1] = ( rb_info->command & 0xFF);
|
||||
data[2] = ((rb_info->command_data >> 24) & 0xFF);
|
||||
data[3] = ((rb_info->command_data >> 16) & 0xFF);
|
||||
data[4] = ((rb_info->command_data >> 8) & 0xFF);
|
||||
data[5] = ( rb_info->command_data & 0xFF);
|
||||
|
||||
//printk(KERN_INFO "Command kernel %2X - %2X - %2X - %2X - %2X - %2X \n", data[0], data[1], data[2], data[3], data[4], data[5]);
|
||||
if ((data[1] & 0xFE) == 0x00) lnrx = ((data[5] & 0x38) >> 3) + 1;
|
||||
|
||||
mutex_lock(&spi_mutex);
|
||||
spi_ctrl_Xfer(0, data, data, 6); //spi channel 0 // tell the gateware the command.
|
||||
mutex_unlock(&spi_mutex);
|
||||
|
||||
_nrx = lnrx;
|
||||
|
||||
//printk(KERN_INFO "SDR info %2X - %2X - %2X - %2X - %2X - %2X \n", data[0], data[1], data[2], data[3], data[4], data[5]);
|
||||
|
||||
// give feedback to firmware.
|
||||
rb_info_ret.rb_command = data[0]; // return the radioberry status information.
|
||||
rb_info_ret.major = data[4];
|
||||
rb_info_ret.minor = data[5];
|
||||
|
||||
rb_info_ret.fpga = data[3] & 0x03;
|
||||
rb_info_ret.version = VERSION_INT;
|
||||
|
||||
if (copy_to_user((struct rb_info_arg_t *)arg, &rb_info_ret, sizeof(struct rb_info_arg_t))) return -EACCES;
|
||||
|
||||
break;
|
||||
default:
|
||||
return -ENOTTY;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct file_operations radioberry_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = radioberry_open,
|
||||
.release = radioberry_release,
|
||||
.write = radioberry_write,
|
||||
.read = radioberry_read,
|
||||
.unlocked_ioctl = radioberry_ioctl
|
||||
};
|
||||
|
||||
static int radioberry_probe(struct platform_device *pdev)
|
||||
{
|
||||
printk(KERN_INFO "inside %s function \n", __FUNCTION__);
|
||||
struct device *dev = &pdev->dev;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int radioberry_remove(struct platform_device *pdev)
|
||||
{
|
||||
printk(KERN_INFO "inside %s function \n", __FUNCTION__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id of_radioberry_match[] = {
|
||||
{.compatible = "sdr,radioberry" },
|
||||
{/*end of list */},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, of_radioberry_match);
|
||||
|
||||
static struct platform_driver radioberry_driver = {
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(of_radioberry_match),
|
||||
},
|
||||
.probe = radioberry_probe,
|
||||
.remove = radioberry_remove,
|
||||
};
|
||||
|
||||
static int __init radioberry_init(void) {
|
||||
int retval;
|
||||
size_t size;
|
||||
|
||||
printk(KERN_INFO "inside %s function \n", __FUNCTION__);
|
||||
printk(KERN_INFO "%s loading...\n", DRIVER_NAME);
|
||||
|
||||
// Register the SPI driver
|
||||
int ret = spi_register_driver(&radioberry_spi_ctrl_driver);
|
||||
if (ret != 0) {
|
||||
pr_err("Failed to register SPI driver\n");
|
||||
return ret;
|
||||
}
|
||||
printk(KERN_INFO "spi register driver executed with result %d\n", ret);
|
||||
|
||||
// Register the SPI driver
|
||||
ret = spi_register_driver(&radioberry_spi_tx_driver);
|
||||
if (ret != 0) {
|
||||
pr_err("Failed to register SPI driver\n");
|
||||
return ret;
|
||||
}
|
||||
printk(KERN_INFO "spi register driver executed with result %d\n", ret);
|
||||
|
||||
int result = platform_driver_register(&radioberry_driver);
|
||||
printk(KERN_INFO "platform driver registered %d \n", result);
|
||||
|
||||
// Dynamically allocate a major number for the device
|
||||
majorNumber = register_chrdev(0, DEVICE_NAME, &radioberry_fops);
|
||||
if (majorNumber<0){
|
||||
printk(KERN_ALERT "Radioberry driver failed to register a major number\n");
|
||||
return majorNumber;
|
||||
}
|
||||
printk(KERN_INFO "Radioberry: registered correctly with major number %d\n", majorNumber);
|
||||
|
||||
// Register the device class
|
||||
radioberryCharClass = class_create(CLASS_NAME);
|
||||
if (IS_ERR(radioberryCharClass)){
|
||||
unregister_chrdev(majorNumber, DEVICE_NAME);
|
||||
printk(KERN_ALERT "Failed to register device class\n");
|
||||
return PTR_ERR(radioberryCharClass);
|
||||
}
|
||||
printk(KERN_INFO "Radioberry: device class registered correctly\n");
|
||||
|
||||
// Register the device driver
|
||||
radioberryCharDevice = device_create(radioberryCharClass, NULL, MKDEV(majorNumber, 0), NULL, DEVICE_NAME);
|
||||
if (IS_ERR(radioberryCharDevice)){
|
||||
class_destroy(radioberryCharClass);
|
||||
unregister_chrdev(majorNumber, DEVICE_NAME);
|
||||
printk(KERN_ALERT "Failed to create the device\n");
|
||||
return PTR_ERR(radioberryCharDevice);
|
||||
}
|
||||
printk(KERN_INFO "Radioberry char: device class created correctly\n");
|
||||
|
||||
mutex_init(&radioberry_mutex);
|
||||
|
||||
mutex_init(&spi_mutex);
|
||||
|
||||
initialize_rpi();
|
||||
loading_radioberry_gateware(radioberryCharDevice);
|
||||
initialize_firmware();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void __exit radioberry_exit(void) {
|
||||
int i;
|
||||
|
||||
printk(KERN_INFO "inside %s function \n", __FUNCTION__);
|
||||
|
||||
platform_driver_unregister(&radioberry_driver);
|
||||
|
||||
device_destroy(radioberryCharClass, MKDEV(majorNumber, 0));
|
||||
class_unregister(radioberryCharClass);
|
||||
class_destroy(radioberryCharClass);
|
||||
unregister_chrdev(majorNumber, DEVICE_NAME);
|
||||
|
||||
mutex_destroy(&radioberry_mutex);
|
||||
mutex_destroy(&spi_mutex);
|
||||
|
||||
// Unregister the SPI driver
|
||||
spi_unregister_driver(&radioberry_spi_ctrl_driver);
|
||||
spi_unregister_driver(&radioberry_spi_tx_driver);
|
||||
|
||||
deinitialize_rpi();
|
||||
|
||||
printk(KERN_INFO "Radioberry: Module removed!\n");
|
||||
}
|
||||
|
||||
module_init(radioberry_init);
|
||||
module_exit(radioberry_exit);
|
||||
|
||||
/**
|
||||
*
|
||||
* Radioberry Driver Info
|
||||
*
|
||||
*/
|
||||
MODULE_AUTHOR("Johan Maas - pa3gsb@gmail.com");
|
||||
MODULE_DESCRIPTION("Radioberry SDR device driver. (rpi-5)");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_VERSION(VERSION);
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Device tree overlay for radioberry
|
||||
*
|
||||
* Compile:
|
||||
* dtc -@ -I dts -O dtb -o radioberry.dtbo radioberry.dts
|
||||
*
|
||||
* for testing: sudo dtoverlay radioberry.dtbo
|
||||
*
|
||||
* add following line in config.txt: dtoverlay=radioberry followed by reboot
|
||||
*
|
||||
*
|
||||
* dtc -I fs /proc/device-tree > devtree.txt (decompile the dt and find the definition for radioberry)
|
||||
*
|
||||
* using : dtc -v Version: DTC 1.4.7
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
/dts-v1/;
|
||||
/plugin/;
|
||||
|
||||
/{
|
||||
compatible = "brcm,bcm2712";
|
||||
|
||||
fragment@0 {
|
||||
target = <&spidev0>;
|
||||
__overlay__ {
|
||||
status = "disabled";
|
||||
};
|
||||
};
|
||||
|
||||
fragment@1 {
|
||||
target = <&spidev1>;
|
||||
__overlay__ {
|
||||
status = "disabled";
|
||||
};
|
||||
};
|
||||
|
||||
fragment@2 {
|
||||
target = <&spi0>;
|
||||
__overlay__ {
|
||||
status = "okay";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
spi_device_1: radioberry_spi@0 {
|
||||
compatible = "radioberry_ctrl_spi";
|
||||
spi-max-frequency = <48000000>;
|
||||
reg = <0x0>;
|
||||
spi-bits-per-word = <8>;
|
||||
spi-mode = <3>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
spi_device_2: radioberry_spi@1 {
|
||||
compatible = "radioberry_tx_spi";
|
||||
reg = <0x1>;
|
||||
spi-max-frequency = <125000000>;
|
||||
spi-bits-per-word = <8>;
|
||||
spi-mode = <3>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
fragment@3 {
|
||||
target-path = "/";
|
||||
__overlay__ {
|
||||
radioberry {
|
||||
compatible = "sdr,radioberry";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
};
|
|
@ -0,0 +1,204 @@
|
|||
#ifndef __RADIOBERRY_FIRMWARE_H__
|
||||
#define __RADIOBERRY_FIRMWARE_H__
|
||||
|
||||
#define GPIO_FUNC_SPI 0
|
||||
#define RPI_RX_CLK 6
|
||||
|
||||
#define RPI_SPI_CE0 8
|
||||
#define RPI_SPI_CE1 7
|
||||
#define RPI_SPI_SCLK 11
|
||||
#define RPI_SPI_MISO 9
|
||||
#define RPI_SPI_MOSI 10
|
||||
|
||||
static struct spi_device *spi_ctrl_dev;
|
||||
static struct spi_device *spi_tx_dev;
|
||||
|
||||
static int initialize_firmware(void);
|
||||
int rxStream(int nrx, unsigned char stream[]);
|
||||
void read_iq_sample(int lnrx, int iqs, unsigned char iqdata[]);
|
||||
int spiXfer(int spi_channel, char *txBuf, char *rxBuf, unsigned cnt);
|
||||
int write_iq_sample(unsigned char tx_iqdata[]);
|
||||
|
||||
static int initialize_firmware() {
|
||||
|
||||
printk(KERN_INFO "initialize_firmware: make GPIO ready for rx and tx streaming...\n");
|
||||
|
||||
// disable the SPI
|
||||
*(volatile uint32_t *)(SPIBase + DW_SPI_SSIENR) = 0x0;
|
||||
|
||||
// SPI Mode Pins
|
||||
setPinMode(RPI_SPI_CE0, GPIO_FUNC_SPI);
|
||||
setPinMode(RPI_SPI_CE1, GPIO_FUNC_SPI);
|
||||
|
||||
setPinMode(RPI_SPI_SCLK, GPIO_FUNC_SPI);
|
||||
setPinMode(RPI_SPI_MISO, GPIO_FUNC_SPI);
|
||||
setPinMode(RPI_SPI_MOSI, GPIO_FUNC_SPI);
|
||||
|
||||
// SPI control settings
|
||||
// set the speed - this is the divisor from 200MHz in the RPi5
|
||||
*(volatile uint32_t *)(SPIBase + DW_SPI_BAUDR) = 20;
|
||||
// set mode - CPOL = 1, CPHA = 1 (Mode 3)
|
||||
*(volatile uint32_t *)(SPIBase + DW_SPI_CTRLR0) |= DW_PSSI_CTRLR0_SCPHA;
|
||||
*(volatile uint32_t *)(SPIBase + DW_SPI_CTRLR0) |= DW_PSSI_CTRLR0_SCPOL;
|
||||
|
||||
// enable the SPI
|
||||
*(volatile uint32_t *)(SPIBase + DW_SPI_SSIENR) = 0x1;
|
||||
|
||||
//RX IO Init part
|
||||
initialize_gpio_for_output(RPI_RX_CLK);
|
||||
clr_pin(RPI_RX_CLK); // init pi-rx_clk
|
||||
|
||||
initialize_gpio_for_input(23); // rx iq data
|
||||
initialize_gpio_for_input(20); // rx iq data
|
||||
initialize_gpio_for_input(19); // rx iq data
|
||||
initialize_gpio_for_input(18); // rx iq data
|
||||
initialize_gpio_for_input(16); // rx iq data
|
||||
initialize_gpio_for_input(13); // rx iq data
|
||||
initialize_gpio_for_input(12); // rx iq data
|
||||
initialize_gpio_for_input( 5); // rx iq data
|
||||
|
||||
initialize_gpio_for_input(25); // available samples.
|
||||
|
||||
printk(KERN_INFO "GPIO ready for rx and tx streaming...\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int spi_ctrl_Xfer(int spi_channel, char *txBuf, char *rxBuf, unsigned cnt){
|
||||
|
||||
//printk(KERN_INFO "inside %s function \n", __FUNCTION__);
|
||||
|
||||
struct spi_transfer t = {
|
||||
.tx_buf = txBuf,
|
||||
.rx_buf = rxBuf,
|
||||
.len = cnt,
|
||||
};
|
||||
struct spi_message m;
|
||||
|
||||
spi_message_init(&m);
|
||||
spi_message_add_tail(&t, &m);
|
||||
|
||||
//spi_ctrl_dev->chip_select = spi_channel;
|
||||
|
||||
// Send and receive the message to/from the Radioberry SPI device
|
||||
int ret = spi_sync(spi_ctrl_dev, &m);
|
||||
if (ret) {
|
||||
pr_err("SPI transfer failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Using the spi driver was not working fast enough.
|
||||
int spi_tx_Xfer(int spi_channel, char *txBuf, char *rxBuf, unsigned cnt){
|
||||
|
||||
//printk(KERN_INFO "inside %s function \n", __FUNCTION__);
|
||||
|
||||
struct spi_transfer t = {
|
||||
.tx_buf = txBuf,
|
||||
.rx_buf = rxBuf,
|
||||
.len = cnt,
|
||||
};
|
||||
struct spi_message m;
|
||||
|
||||
spi_message_init(&m);
|
||||
spi_message_add_tail(&t, &m);
|
||||
|
||||
// Send and receive the message to/from the Radioberry SPI device
|
||||
int ret = spi_sync(spi_tx_dev, &m);
|
||||
if (ret) {
|
||||
pr_err("SPI transfer failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
int spi_tx_Xfer(int spi_channel, char *txBuf, char *rxBuf, unsigned cnt)
|
||||
{
|
||||
//printk(KERN_INFO "inside %s function \n", __FUNCTION__);
|
||||
|
||||
// set the frame size to 32 bits
|
||||
*(volatile uint32_t *)(SPIBase + DW_SPI_CTRLR0) = ((*(volatile uint32_t *)(SPIBase + DW_SPI_CTRLR0)) | DW_PSSI_CTRLR0_DFS32_MASK | DW_PSSI_CTRLR0_DFS_MASK);
|
||||
|
||||
// write the tx fifo
|
||||
unsigned txCnt = 0;
|
||||
while((*(volatile uint32_t *)(SPIBase + DW_SPI_SR) & DW_SPI_SR_TF_NOT_FULL) && (txCnt < cnt))
|
||||
{
|
||||
*(volatile uint8_t *)(SPIBase + DW_SPI_DR) = (uint8_t)txBuf[txCnt];
|
||||
txCnt++;
|
||||
}
|
||||
//spi control
|
||||
*(volatile uint32_t *)(SPIBase + DW_SPI_SER) = 1 << spi_channel; // CE1 active
|
||||
|
||||
// transfer the tx fifo and fill the rx fifo
|
||||
int rxCnt = 0;
|
||||
while(rxCnt < cnt)
|
||||
{
|
||||
// check if there is data to read (check status register for Read Fifo Not Empty)
|
||||
if(*(volatile uint32_t *)(SPIBase + DW_SPI_SR) & DW_SPI_SR_RF_NOT_EMPT)
|
||||
{
|
||||
rxBuf[rxCnt] = *(volatile uint8_t *)(SPIBase + DW_SPI_DR);
|
||||
rxCnt++;
|
||||
}
|
||||
}
|
||||
|
||||
//spi control
|
||||
*(volatile uint32_t *)(SPIBase + DW_SPI_SER) = 0x00; //CE1 inactive
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rxStream(int nrx, unsigned char stream[]){
|
||||
unsigned char iqdata[6];
|
||||
int iqs = 1;
|
||||
|
||||
int nr_samples = (nrx == 1)? 63 : (nrx == 2)? 72: (nrx ==3)? 75: (nrx ==4)? 76: (nrx ==5)? 75: (nrx ==6)? 78: (nrx ==7)? 77: 80;
|
||||
|
||||
int s = 0;
|
||||
for (s = 0; s < nr_samples; s++) {
|
||||
read_iq_sample(nrx, iqs, iqdata);
|
||||
memcpy(stream + (s * 6), iqdata, 6);
|
||||
iqs++;
|
||||
}
|
||||
return nr_samples * 6;
|
||||
}
|
||||
|
||||
void read_iq_sample(int lnrx, int iqs, unsigned char iqdata[]){
|
||||
uint32_t value = 0;
|
||||
|
||||
for (int i = 0; i < 6 ; i++) {
|
||||
|
||||
if (i % 2 == 0) set_pin(RPI_RX_CLK); else clr_pin(RPI_RX_CLK);
|
||||
ndelay(100);
|
||||
value = read_pin_all();
|
||||
|
||||
iqdata[i] = (((value >> 23) & 1) << 7);
|
||||
iqdata[i] |= (((value >> 20) & 1) << 6);
|
||||
iqdata[i] |= (((value >> 19) & 1) << 5);
|
||||
iqdata[i] |= (((value >> 18) & 1) << 4);
|
||||
iqdata[i] |= (((value >> 16) & 1) << 3);
|
||||
iqdata[i] |= (((value >> 13) & 1) << 2);
|
||||
iqdata[i] |= (((value >> 12) & 1) << 1);
|
||||
iqdata[i] |= (((value >> 5) & 1));
|
||||
}
|
||||
}
|
||||
|
||||
int write_iq_sample(unsigned char tx_iqdata[]) {
|
||||
|
||||
unsigned char data[4];
|
||||
|
||||
mutex_lock(&spi_mutex);
|
||||
spi_tx_Xfer(1, tx_iqdata, data, 4); //spi channel 1; write IQ sample (4bytes)
|
||||
mutex_unlock(&spi_mutex);
|
||||
|
||||
return 4;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
/**
|
||||
*
|
||||
* Loading the gateware into the FPGA.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __RADIOBERRY_GATEWARE_H__
|
||||
#define __RADIOBERRY_GATEWARE_H__
|
||||
|
||||
#include <linux/delay.h>
|
||||
|
||||
|
||||
#define iPinCONF_DONE 22
|
||||
#define iPinNSTATUS 26
|
||||
|
||||
#define oPinNCONFIG 27
|
||||
#define oPinDATA 13
|
||||
#define oPinDCLK 24
|
||||
|
||||
static void initialize_gateware(void);
|
||||
static int prepare_gateware_loading(void) ;
|
||||
static void upload_gateware_byte( int one_byte );
|
||||
static int activate_gateware(void);
|
||||
|
||||
static void upload_gateware_byte( int one_byte ) {
|
||||
int bit = 0;
|
||||
int i = 0;
|
||||
|
||||
/* write from LSb to MSb */
|
||||
for ( i = 0; i < 8; i++ )
|
||||
{
|
||||
bit = one_byte >> i;
|
||||
bit = bit & 0x1;
|
||||
|
||||
if (bit) set_pin(oPinDATA); else clr_pin(oPinDATA);
|
||||
udelay(1);
|
||||
set_pin(oPinDCLK);
|
||||
clr_pin(oPinDCLK);
|
||||
}
|
||||
}
|
||||
|
||||
static int prepare_gateware_loading() {
|
||||
printk(KERN_INFO "Info: prepare FPGA for loading image\n");
|
||||
|
||||
clr_pin(oPinNCONFIG);
|
||||
clr_pin(oPinDATA);
|
||||
clr_pin(oPinDCLK);
|
||||
|
||||
msleep(1000);
|
||||
|
||||
set_pin(oPinNCONFIG);
|
||||
|
||||
int count = 0;
|
||||
|
||||
while (read_pin(iPinNSTATUS) == 0) {
|
||||
count++;
|
||||
msleep(1000);
|
||||
if (count >= 2) {
|
||||
printk(KERN_INFO "Error: prepareLoading failed\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void initialize_gateware() {
|
||||
|
||||
initialize_gpio_for_input(iPinCONF_DONE);
|
||||
initialize_gpio_for_input(iPinNSTATUS);
|
||||
|
||||
initialize_gpio_for_output(oPinNCONFIG);
|
||||
initialize_gpio_for_output(oPinDATA);
|
||||
initialize_gpio_for_output(oPinDCLK);
|
||||
|
||||
printk(KERN_INFO "GPIO ready for uploading gateware...\n");
|
||||
|
||||
}
|
||||
|
||||
static int activate_gateware() {
|
||||
|
||||
/* Check if loading succeeded*/
|
||||
if (read_pin(iPinNSTATUS) == 0) {
|
||||
printk(KERN_INFO "Error: programming failed; nstatus is low\n");
|
||||
return 0;
|
||||
} else if (read_pin(iPinCONF_DONE) == 0) {
|
||||
printk(KERN_INFO "Error: programming failed; conf done is low\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Initialize device */
|
||||
set_pin(oPinDCLK);
|
||||
clr_pin(oPinDCLK);
|
||||
set_pin(oPinDCLK);
|
||||
clr_pin(oPinDCLK);
|
||||
|
||||
printk(KERN_INFO "Info: gateware upload and activation succeeded\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
#ifndef __RADIOBERRY_IOCTL_H__
|
||||
#define __RADIOBERRY_IOCTL_H__
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/ioctl.h>
|
||||
|
||||
#define RADIOBERRY_MAGIC ('x')
|
||||
|
||||
#define RADIOBERRY_IOC_COMMAND _IOW(RADIOBERRY_MAGIC, 1, __u8)
|
||||
|
||||
struct rb_info_arg_t
|
||||
{
|
||||
int major, minor;
|
||||
|
||||
int fpga;
|
||||
|
||||
int version;
|
||||
|
||||
int rb_command;
|
||||
int command;
|
||||
int command_data;
|
||||
|
||||
} ;
|
||||
|
||||
#endif
|
|
@ -0,0 +1,133 @@
|
|||
/**
|
||||
*
|
||||
* GPIO RPI-5 functions.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __RADIOBERRY_RPI_H__
|
||||
#define __RADIOBERRY_RPI_H__
|
||||
|
||||
#define GPIO ((GPIOregs*)GPIOBase)
|
||||
|
||||
|
||||
#define PERI_BASE 0x1F00000000
|
||||
#define MEM_SIZE (64 * 1024 * 1024)
|
||||
|
||||
#define RP1_SPI0_BASE 0x050000
|
||||
|
||||
#define DW_SPI_CTRLR0 0x00 // control register 0 (frame format, clock polarity, phase, etc.)
|
||||
#define DW_SPI_SSIENR 0x04 // enable register (enable/disable the SPI controller) (0x08)
|
||||
#define DW_SPI_SER 0x04 // chip select, bit = chip select line (0x10)
|
||||
#define DW_SPI_BAUDR 0x05 // baud rate register (0x14)
|
||||
#define DW_SPI_SR 0x0a // status register (0x28)
|
||||
#define DW_SPI_DR 0x18 // data register (0x60)
|
||||
|
||||
// 10987654321098765432109876543210
|
||||
#define DW_PSSI_CTRLR0_DFS_MASK 0b00000000000000000000000000001111 //GENMASK(3, 0)
|
||||
#define DW_PSSI_CTRLR0_DFS32_MASK 0b00000000000111110000000000000000 //GENMASK(20, 16)
|
||||
|
||||
//SPI
|
||||
#define DW_PSSI_CTRLR0_MODE_MASK 0b00000000000000000000000011000000 //GENMASK(7, 6)
|
||||
#define DW_PSSI_CTRLR0_SCPHA 0b00000000000000000000000001000000 // BIT(6)
|
||||
#define DW_PSSI_CTRLR0_SCPOL 0b00000000000000000000000010000000 //BIT(7)
|
||||
|
||||
/* Bit fields in SR, 7 bits */
|
||||
#define DW_SPI_SR_TF_NOT_FULL 0b00000000000000000000000000000010 // BIT(1)
|
||||
#define DW_SPI_SR_RF_NOT_EMPT 0b00000000000000000000000000001000 // BIT(3)
|
||||
|
||||
|
||||
typedef struct{
|
||||
uint32_t status;
|
||||
uint32_t ctrl;
|
||||
} GPIOregs;
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t out;
|
||||
uint32_t oe;
|
||||
uint32_t in;
|
||||
uint32_t inSync;
|
||||
} rioregs;
|
||||
|
||||
volatile uint32_t *PERIBase = NULL;
|
||||
volatile uint32_t *GPIOBase = NULL;
|
||||
volatile uint32_t *RIOBase = NULL;
|
||||
volatile uint32_t *PADBase = NULL;
|
||||
volatile uint32_t *SPIBase = NULL;
|
||||
|
||||
uint32_t *Pad = NULL;
|
||||
|
||||
#define rio ((rioregs *)RIOBase)
|
||||
#define rioSET ((rioregs *)(RIOBase + 0x2000 / 4))
|
||||
#define rioCLR ((rioregs *)(RIOBase + 0x3000 / 4))
|
||||
|
||||
#define GPIO_FUNC_RIO 5
|
||||
#define PAD_FUNC_IN 0xC8
|
||||
#define PAD_FUNC_OUT 0x10
|
||||
|
||||
|
||||
static int initialize_rpi(void) {
|
||||
|
||||
int ret = 0;
|
||||
|
||||
printk(KERN_INFO "make GPIO ready for use\n");
|
||||
|
||||
if((PERIBase = ioremap(PERI_BASE, MEM_SIZE)) == NULL){
|
||||
printk(KERN_INFO "Failed mapping registers...\n");
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
GPIOBase = PERIBase + 0xD0000 / 4;
|
||||
RIOBase = PERIBase + 0xE0000 / 4;
|
||||
PADBase = PERIBase + 0xF0000 / 4;
|
||||
Pad = PADBase + 1;
|
||||
SPIBase = PERIBase + RP1_SPI0_BASE / 4;
|
||||
|
||||
|
||||
printk(KERN_INFO "GPIO ready for use\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void deinitialize_rpi(void) {
|
||||
iounmap(PERIBase);
|
||||
printk(KERN_INFO "GPIO resources free. \n");
|
||||
}
|
||||
|
||||
static void initialize_gpio_for_output(uint32_t pin) {
|
||||
GPIO[pin].ctrl=GPIO_FUNC_RIO;
|
||||
Pad[pin] = PAD_FUNC_OUT;
|
||||
rioSET->oe = 0x01<<pin; // output driver
|
||||
}
|
||||
|
||||
static void initialize_gpio_for_input(uint32_t pin) {
|
||||
GPIO[pin].ctrl=GPIO_FUNC_RIO;
|
||||
Pad[pin] = PAD_FUNC_IN;
|
||||
rioCLR->oe = 0x01<<pin; // high impedance
|
||||
}
|
||||
|
||||
static void set_pin(uint32_t pin) {
|
||||
rioSET->oe = 0x01<<pin; // output driver
|
||||
rioSET->out = 0x01<<pin;
|
||||
}
|
||||
|
||||
static void clr_pin(uint32_t pin) {
|
||||
rioSET->oe = 0x01<<pin; // output driver
|
||||
rioCLR->out = 0x01<<pin;
|
||||
}
|
||||
|
||||
static uint32_t read_pin(uint32_t pin) {
|
||||
return (rio->in>>pin & 0x01);
|
||||
}
|
||||
|
||||
static uint32_t read_pin_all(void) {
|
||||
return rio->in;
|
||||
}
|
||||
|
||||
static void setPinMode(uint32_t pin, uint32_t mode)
|
||||
{
|
||||
GPIO[pin].ctrl = mode;
|
||||
}
|
||||
|
||||
#endif
|
Ładowanie…
Reference in New Issue