[PATCH v3] staging: nrf24: add new driver for 2.4GHz radio transceiver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This patch adds driver for Nordic Semiconductor nRF24L01+ radio
transceiver.

Signed-off-by: Marcin Ciupak <marcin.s.ciupak@xxxxxxxxx>
---
Changes in v2:
  - add terminating newlines to all logging formats
Changes in v3:
  - patch subject
  - comments cleanup
  - goto labels cleanup
  - scnprintf bugfix
  - ida_simple_remove bugfix

 drivers/staging/Kconfig                       |   2 +
 drivers/staging/Makefile                      |   1 +
 drivers/staging/nrf24/Kconfig                 |  16 +
 drivers/staging/nrf24/Makefile                |   3 +
 drivers/staging/nrf24/TODO                    |   7 +
 .../nrf24/devicetree/nrf24-spi0-overlay.dts   |  54 ++
 .../nrf24/devicetree/nrf24-spi1-overlay.dts   |  54 ++
 drivers/staging/nrf24/devicetree/nrf24.txt    |   1 +
 drivers/staging/nrf24/nRF24L01.h              |  82 ++
 drivers/staging/nrf24/nrf24_enums.h           |  60 ++
 drivers/staging/nrf24/nrf24_hal.c             | 764 +++++++++++++++
 drivers/staging/nrf24/nrf24_hal.h             |  54 ++
 drivers/staging/nrf24/nrf24_if.c              | 893 ++++++++++++++++++
 drivers/staging/nrf24/nrf24_if.h              |  63 ++
 drivers/staging/nrf24/nrf24_sysfs.c           | 707 ++++++++++++++
 drivers/staging/nrf24/nrf24_sysfs.h           |  14 +
 16 files changed, 2775 insertions(+)
 create mode 100644 drivers/staging/nrf24/Kconfig
 create mode 100644 drivers/staging/nrf24/Makefile
 create mode 100644 drivers/staging/nrf24/TODO
 create mode 100644 drivers/staging/nrf24/devicetree/nrf24-spi0-overlay.dts
 create mode 100644 drivers/staging/nrf24/devicetree/nrf24-spi1-overlay.dts
 create mode 100644 drivers/staging/nrf24/devicetree/nrf24.txt
 create mode 100644 drivers/staging/nrf24/nRF24L01.h
 create mode 100644 drivers/staging/nrf24/nrf24_enums.h
 create mode 100644 drivers/staging/nrf24/nrf24_hal.c
 create mode 100644 drivers/staging/nrf24/nrf24_hal.h
 create mode 100644 drivers/staging/nrf24/nrf24_if.c
 create mode 100644 drivers/staging/nrf24/nrf24_if.h
 create mode 100644 drivers/staging/nrf24/nrf24_sysfs.c
 create mode 100644 drivers/staging/nrf24/nrf24_sysfs.h

diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index 1abf76be2aa8..55d688f3112e 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -126,4 +126,6 @@ source "drivers/staging/axis-fifo/Kconfig"
 
 source "drivers/staging/erofs/Kconfig"
 
+source "drivers/staging/nrf24/Kconfig"
+
 endif # STAGING
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index ab0cbe8815b1..c18e74df03af 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -53,3 +53,4 @@ obj-$(CONFIG_SOC_MT7621)	+= mt7621-dts/
 obj-$(CONFIG_STAGING_GASKET_FRAMEWORK)	+= gasket/
 obj-$(CONFIG_XIL_AXIS_FIFO)	+= axis-fifo/
 obj-$(CONFIG_EROFS_FS)		+= erofs/
+obj-$(CONFIG_NRF24)		+= nrf24/
diff --git a/drivers/staging/nrf24/Kconfig b/drivers/staging/nrf24/Kconfig
new file mode 100644
index 000000000000..67ebf14dd982
--- /dev/null
+++ b/drivers/staging/nrf24/Kconfig
@@ -0,0 +1,16 @@
+config NRF24
+        tristate "nRF24L01+ 2.4GHz radio module support"
+        depends on SPI
+        help
+          This enables support for Nordic Semiconductor nRF24L01+ radio module,
+          with the following features:
+            - multiple radio module instances via nrfX
+            - dedicated /dev/nrfX.Y device per pipe per instance
+            - dynamic and static payload lengths
+            - configuration via sysfs (/sys/class/nrfX)
+            - poll mechanism
+            - 64kB RX FIFO per pipe
+            - 64kB TX FIFO
+
+          To compile this driver as a module, choose M here: the module will be
+          called nrf24.
diff --git a/drivers/staging/nrf24/Makefile b/drivers/staging/nrf24/Makefile
new file mode 100644
index 000000000000..f5222567c632
--- /dev/null
+++ b/drivers/staging/nrf24/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_NRF24) += nrf24.o
+
+nrf24-objs := nrf24_if.o nrf24_hal.o nrf24_sysfs.o
diff --git a/drivers/staging/nrf24/TODO b/drivers/staging/nrf24/TODO
new file mode 100644
index 000000000000..a089e43faac5
--- /dev/null
+++ b/drivers/staging/nrf24/TODO
@@ -0,0 +1,7 @@
+Todo:
+- opening and closing pipes via sysfs
+- improve switching in between RX and TX
+- improve handling of MAX_RT interrupt
+- find and fix bugs
+- code cleanup
+
diff --git a/drivers/staging/nrf24/devicetree/nrf24-spi0-overlay.dts b/drivers/staging/nrf24/devicetree/nrf24-spi0-overlay.dts
new file mode 100644
index 000000000000..130e6787b76d
--- /dev/null
+++ b/drivers/staging/nrf24/devicetree/nrf24-spi0-overlay.dts
@@ -0,0 +1,54 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//
+// Copyright (C) 2017 Marcin Ciupak <marcin.s.ciupak@xxxxxxxxx>
+//
+
+// Definitions for NRF24
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "bcm,bcm2835", "bcm,bcm2708", "bcm,bcm2709";
+
+	fragment@0 {
+		target = <&spi0>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			spidev@0 {
+				status = "disabled";
+			};
+
+			nrf0: nrf0@0 {
+				compatible = "nordic,nrf24";
+				reg = <0>; /* CS0 */
+				pinctrl-names = "default";
+				pinctrl-0 = <&nrf0_pins>;
+				interrupt-parent = <&gpio>;
+				interrupts = <24 0x2>; /* falling edge */
+				irq-gpio = <&gpio 24 0>;
+				ce-gpio = <&gpio 25 0>;
+				spi-max-frequency = <5000000>;
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@1 {
+		target = <&gpio>;
+		__overlay__ {
+			nrf0_pins: nrf0_pins {
+				brcm,pins = <24 25>;
+				brcm,function = <0 1>; /* in out */
+			};
+		};
+	};
+
+	__overrides__ {
+		int_pin = <&nrf0>, "interrupts:0", <&nrf0_pins>, "brcm,pins:0";
+		speed   = <&nrf0>, "spi-max-frequency:0";
+	};
+};
diff --git a/drivers/staging/nrf24/devicetree/nrf24-spi1-overlay.dts b/drivers/staging/nrf24/devicetree/nrf24-spi1-overlay.dts
new file mode 100644
index 000000000000..ff07507d0a14
--- /dev/null
+++ b/drivers/staging/nrf24/devicetree/nrf24-spi1-overlay.dts
@@ -0,0 +1,54 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//
+// Copyright (C) 2017 Marcin Ciupak <marcin.s.ciupak@xxxxxxxxx>
+//
+
+/* Definitions for NRF24 */
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "bcm,bcm2835", "bcm,bcm2708", "bcm,bcm2709";
+
+	fragment@0 {
+		target = <&spi1>;
+		__overlay__ {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "okay";
+
+			spidev@0 {
+				status = "disabled";
+			};
+
+			nrf1: nrf1@0 {
+				compatible = "nordic,nrf24";
+				reg = <0>; /* CS0 */
+				pinctrl-names = "default";
+				pinctrl-0 = <&nrf1_pins>;
+				interrupt-parent = <&gpio>;
+				interrupts = <17 0x2>; /* falling edge */
+				irq-gpio = <&gpio 17 0>;
+				ce-gpio = <&gpio 27 0>;
+				spi-max-frequency = <5000000>;
+				status = "okay";
+			};
+		};
+	};
+
+	fragment@1 {
+		target = <&gpio>;
+		__overlay__ {
+			nrf1_pins: nrf1_pins {
+				brcm,pins = <17 27>;
+				brcm,function = <0 1>; // in out
+			};
+		};
+	};
+
+	__overrides__ {
+		int_pin = <&nrf1>, "interrupts:0", <&nrf1_pins>, "brcm,pins:0";
+		speed   = <&nrf1>, "spi-max-frequency:0";
+	};
+};
diff --git a/drivers/staging/nrf24/devicetree/nrf24.txt b/drivers/staging/nrf24/devicetree/nrf24.txt
new file mode 100644
index 000000000000..0eff8bfae97f
--- /dev/null
+++ b/drivers/staging/nrf24/devicetree/nrf24.txt
@@ -0,0 +1 @@
+scripts/dtc/dtc -@ -I dts -O dtb -o nrf24-spi0.dtbo nrf24-spi0-overlay.dts
diff --git a/drivers/staging/nrf24/nRF24L01.h b/drivers/staging/nrf24/nRF24L01.h
new file mode 100644
index 000000000000..ed7fac6c9c38
--- /dev/null
+++ b/drivers/staging/nrf24/nRF24L01.h
@@ -0,0 +1,82 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/*
+ * Copyright (C) 2017 Marcin Ciupak <marcin.s.ciupak@xxxxxxxxx>
+ *
+ */
+
+#ifndef NRF24L01_H
+#define NRF24L01_H
+
+/* nRF24L01 Register map */
+
+#define CONFIG			0x00
+#define EN_AA			0x01
+#define EN_RXADDR		0x02
+#define SETUP_AW		0x03
+#define SETUP_RETR		0x04
+#define RF_CH			0x05
+#define RF_SETUP		0x06
+#define STATUS			0x07
+#define OBSERVE_TX		0x08
+#define	CD			0x09
+#define RX_ADDR_P0		0x0A
+#define RX_ADDR_P1		0x0B
+#define RX_ADDR_P2		0x0C
+#define RX_ADDR_P3		0x0D
+#define RX_ADDR_P4		0x0E
+#define RX_ADDR_P5		0x0F
+#define TX_ADDR			0x10
+#define RX_PW_P0		0x11
+#define RX_PW_P1		0x12
+#define RX_PW_P2		0x13
+#define RX_PW_P3		0x14
+#define RX_PW_P4		0x15
+#define RX_PW_P5		0x16
+#define FIFO_STATUS		0x17
+#define DYNPD			0x1C
+#define FEATURE			0x1D
+
+/* nRF24L01 Instruction Definitions */
+#define W_REGISTER		0x20
+#define R_RX_PL_WID		0x60
+#define R_RX_PAYLOAD		0x61
+#define W_TX_PAYLOAD		0xA0
+#define W_ACK_PAYLOAD		0xA8
+#define W_TX_PAYLOAD_NOACK	0xB0
+#define FLUSH_TX		0xE1
+#define FLUSH_RX		0xE2
+#define REUSE_TX_PL		0xE3
+#define LOCK_UNLOCK		0x50
+#define NOP			0xFF
+
+/* CONFIG 0x00 */
+#define MASK_RX_DR		0x40
+#define MASK_TX_DS		0x20
+#define MASK_MAX_RT		0x10
+#define EN_CRC			0x08
+#define CRCO			0x04
+#define PWR_UP			0x02
+#define PRIM_RX			0x01
+
+/* RF_SETUP 0x06 */
+#define RF_DR_LO		0x20
+#define PLL_LOCK		0x10
+#define RF_DR_HI		0x08
+#define RF_PWR1			0x04
+#define RF_PWR0			0x02
+
+/* STATUS 0x07 */
+#define RX_DR			0x40
+#define TX_DS			0x20
+#define MAX_RT			0x10
+#define TX_FULL			0x01
+
+/* FEATURE 0x1D */
+#define EN_DPL			0x04
+#define EN_ACK_PAY		0x02
+#define EN_DYN_ACK		0x01
+
+#define PLOAD_MAX		32
+
+#endif
diff --git a/drivers/staging/nrf24/nrf24_enums.h b/drivers/staging/nrf24/nrf24_enums.h
new file mode 100644
index 000000000000..89f35db370b6
--- /dev/null
+++ b/drivers/staging/nrf24/nrf24_enums.h
@@ -0,0 +1,60 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/*
+ * Copyright (C) 2017 Marcin Ciupak <marcin.s.ciupak@xxxxxxxxx>
+ *
+ */
+
+#ifndef NRF24_ENUMS_H
+#define NRF24_ENUMS_H
+
+enum nrf24_pipe_num {
+	NRF24_PIPE0,
+	NRF24_PIPE1,
+	NRF24_PIPE2,
+	NRF24_PIPE3,
+	NRF24_PIPE4,
+	NRF24_PIPE5,
+	NRF24_TX,
+	NRF24_PIPE_ALL = 0xFF
+};
+
+enum nrf24_crc_mode {
+	NRF24_CRC_OFF,
+	NRF24_CRC_8BIT = 2,
+	NRF24_CRC_16BIT
+};
+
+enum nrf24_address_width {
+	NRF24_AW_3 = 3,
+	NRF24_AW_4,
+	NRF24_AW_5
+};
+
+enum nrf24_pload {
+	NRF24_TX_PLOAD = 7,
+	NRF24_TX_PLOAD_NOACK,
+	NRF24_RX_PLOAD,
+	NRF24_ACK_PLOAD
+};
+
+enum nrf24_datarate {
+	NRF24_DATARATE_1MBPS,
+	NRF24_DATARATE_2MBPS,
+	NRF24_DATARATE_256KBPS
+};
+
+enum nrf24_mode {
+	NRF24_MODE_TX,
+	NRF24_MODE_RX
+};
+
+enum nrf24_rf_power {
+	NRF24_POWER_18DBM,
+	NRF24_POWER_12DBM,
+	NRF24_POWER_6DBM,
+	NRF24_POWER_0DBM
+};
+
+#endif
+
diff --git a/drivers/staging/nrf24/nrf24_hal.c b/drivers/staging/nrf24/nrf24_hal.c
new file mode 100644
index 000000000000..0d2242276d40
--- /dev/null
+++ b/drivers/staging/nrf24/nrf24_hal.c
@@ -0,0 +1,764 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright (C) 2017 Marcin Ciupak <marcin.s.ciupak@xxxxxxxxx>
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/spi/spi.h>
+
+#include "nrf24_hal.h"
+
+static ssize_t nrf24_read_reg(struct spi_device *spi, u8 addr)
+{
+	ssize_t ret;
+
+	ret = spi_w8r8(spi, addr);
+
+	if (ret < 0)
+		dev_dbg(&spi->dev, "%s: read 0x%X FAILED\n", __func__, addr);
+
+	return ret;
+}
+
+static ssize_t nrf24_write_reg(struct spi_device *spi, u8 addr, u8 val)
+{
+	ssize_t ret;
+	u8 buffer[2];
+
+	buffer[0] = addr;
+	buffer[1] = val;
+
+	if (addr < W_REGISTER) {
+		buffer[0] = buffer[0] + W_REGISTER;
+		ret = spi_write(spi, buffer, 2);
+	} else if (addr != FLUSH_TX &&
+		   addr != FLUSH_RX &&
+		   addr != REUSE_TX_PL) {
+		ret = spi_write(spi, buffer, 2);
+	} else {
+		ret = spi_write(spi, buffer, 1);
+	}
+	if (ret < 0)
+		dev_dbg(&spi->dev, "%s: write 0x%X to 0x%X  FAILED\n",
+			__func__, val, addr);
+
+	return ret;
+}
+
+static ssize_t nrf24_write_multireg(struct spi_device *spi,
+				    u8 reg,
+				    u8 *buf,
+				    u8 length)
+{
+	u8 buffer[PLOAD_MAX + 1];
+
+	if (!length)
+		return -EINVAL;
+
+	switch (reg) {
+	case NRF24_PIPE0:
+	case NRF24_PIPE1:
+	case NRF24_TX:
+		buffer[0] = W_REGISTER + RX_ADDR_P0 + reg;
+		break;
+	case NRF24_TX_PLOAD:
+		buffer[0] = W_TX_PAYLOAD;
+		break;
+	case NRF24_TX_PLOAD_NOACK:
+		buffer[0] = W_TX_PAYLOAD_NOACK;
+		break;
+	default:
+		dev_dbg(&spi->dev, "%s: invalid parameter\n", __func__);
+		return -EINVAL;
+	}
+
+	memcpy(buffer + 1, buf, length);
+
+	return spi_write(spi, buffer, length + 1);
+}
+
+static ssize_t nrf24_read_multireg(struct spi_device *spi, u8 reg, u8 *buf)
+{
+	ssize_t ret;
+	u8 reg_addr;
+	ssize_t length = 0;
+
+	switch (reg) {
+	case NRF24_PIPE0:
+	case NRF24_PIPE1:
+	case NRF24_TX:
+		length = nrf24_get_address_width(spi);
+		if (length < 0)
+			return length;
+		reg_addr = RX_ADDR_P0 + reg;
+		break;
+	case NRF24_RX_PLOAD:
+		ret = nrf24_get_rx_data_source(spi);
+		if (ret < 0)
+			return ret;
+
+		if (ret < NRF24_TX_PLOAD) {
+			length = nrf24_get_rx_pl_w(spi);
+			if (length < 0)
+				return length;
+			reg_addr = R_RX_PAYLOAD;
+		}
+		break;
+	default:
+		dev_dbg(&spi->dev, "%s: invalid parameter\n", __func__);
+		return -EINVAL;
+	}
+
+	if (length > 0) {
+		ret = spi_write_then_read(spi,
+					  &reg_addr,
+					  1,
+					  buf,
+					  length);
+
+		if (ret < 0)
+			return ret;
+	}
+
+	return length;
+}
+
+ssize_t nrf24_get_dynamic_pl(struct spi_device *spi)
+{
+	ssize_t feature;
+
+	feature = nrf24_read_reg(spi, FEATURE);
+	if (feature < 0)
+		return feature;
+
+	return (feature & EN_DPL) == EN_DPL;
+}
+
+ssize_t nrf24_enable_dynamic_pl(struct spi_device *spi)
+{
+	ssize_t feature;
+
+	feature = nrf24_read_reg(spi, FEATURE);
+	if (feature < 0)
+		return feature;
+	return nrf24_write_reg(spi, FEATURE, feature | EN_DPL);
+}
+
+ssize_t nrf24_disable_dynamic_pl(struct spi_device *spi)
+{
+	ssize_t feature;
+
+	feature = nrf24_read_reg(spi, FEATURE);
+	if (feature < 0)
+		return feature;
+	return nrf24_write_reg(spi, FEATURE, feature & ~EN_DPL);
+}
+
+static ssize_t nrf24_setup_dynamic_pl(struct spi_device *spi,
+				      u8 pipe,
+				      bool enable)
+{
+	ssize_t dynpd;
+	ssize_t ret;
+
+	if (pipe != NRF24_PIPE0 && enable) {
+		ret = nrf24_setup_dynamic_pl(spi, NRF24_PIPE0, enable);
+		if (ret < 0)
+			return ret;
+	}
+	dynpd = nrf24_read_reg(spi, DYNPD);
+	if (dynpd < 0)
+		return dynpd;
+
+	if (enable) {
+		ret = nrf24_setup_auto_ack(spi, pipe, enable);
+		if (ret < 0)
+			return ret;
+
+		dynpd |= BIT(pipe);
+	} else {
+		dynpd &= ~BIT(pipe);
+	}
+	ret = nrf24_write_reg(spi, DYNPD, dynpd);
+	if (ret < 0)
+		return ret;
+
+	if (dynpd)
+		ret = nrf24_enable_dynamic_pl(spi);
+	else
+		ret = nrf24_disable_dynamic_pl(spi);
+
+	return ret;
+}
+
+ssize_t nrf24_setup_auto_ack(struct spi_device *spi, u8 pipe, bool enable)
+{
+	ssize_t aa;
+
+	aa = nrf24_read_reg(spi, EN_AA);
+	if (aa < 0)
+		return aa;
+	if (enable)
+		aa |= BIT(pipe);
+	else
+		aa &= ~BIT(pipe);
+
+	return nrf24_write_reg(spi, EN_AA, aa);
+}
+
+static ssize_t nrf24_enable_pipe(struct spi_device *spi, u8 pipe)
+{
+	ssize_t rxaddr;
+
+	rxaddr = nrf24_read_reg(spi, EN_RXADDR);
+	if (rxaddr < 0)
+		return rxaddr;
+	return nrf24_write_reg(spi, EN_RXADDR, rxaddr | BIT(pipe));
+}
+
+static ssize_t nrf24_disable_pipe(struct spi_device *spi, u8 pipe)
+{
+	ssize_t rxaddr;
+
+	rxaddr = nrf24_read_reg(spi, EN_RXADDR);
+	if (rxaddr < 0)
+		return rxaddr;
+	return nrf24_write_reg(spi, EN_RXADDR, rxaddr & ~BIT(pipe));
+}
+
+ssize_t nrf24_open_pipe(struct spi_device *spi, enum nrf24_pipe_num pipe)
+{
+	ssize_t ret = -EINVAL;
+
+	switch (pipe) {
+	case NRF24_PIPE0:
+	case NRF24_PIPE1:
+	case NRF24_PIPE2:
+	case NRF24_PIPE3:
+	case NRF24_PIPE4:
+	case NRF24_PIPE5:
+		ret = nrf24_enable_pipe(spi, pipe);
+		break;
+	case NRF24_PIPE_ALL:
+		ret = nrf24_write_reg(spi, EN_RXADDR, 0x3F);
+		break;
+	default:
+		dev_dbg(&spi->dev, "%s: invalid parameter\n", __func__);
+	}
+
+	return ret;
+}
+
+ssize_t nrf24_close_pipe(struct spi_device *spi, enum nrf24_pipe_num pipe)
+{
+	ssize_t ret = -EINVAL;
+
+	switch (pipe) {
+	case NRF24_PIPE0:
+	case NRF24_PIPE1:
+	case NRF24_PIPE2:
+	case NRF24_PIPE3:
+	case NRF24_PIPE4:
+	case NRF24_PIPE5:
+		ret = nrf24_disable_pipe(spi, pipe);
+		if (ret < 0)
+			break;
+		ret = nrf24_setup_auto_ack(spi, pipe, false);
+		break;
+	case NRF24_PIPE_ALL:
+		ret = nrf24_write_reg(spi, EN_RXADDR, 0x00);
+		if (ret)
+			break;
+		ret = nrf24_write_reg(spi, EN_AA, 0x00);
+		break;
+	default:
+		dev_dbg(&spi->dev, "%s: invalid parameter\n", __func__);
+	}
+
+	return ret;
+}
+
+ssize_t nrf24_set_address(struct spi_device *spi,
+			  enum nrf24_pipe_num pipe,
+			  u8 *addr)
+{
+	ssize_t ret = -EINVAL;
+	ssize_t length;
+
+	switch (pipe) {
+	case NRF24_TX:
+	case NRF24_PIPE0:
+	case NRF24_PIPE1:
+		length = nrf24_get_address_width(spi);
+		if (length < 0)
+			return length;
+		ret = nrf24_write_multireg(spi, pipe, addr, length);
+		break;
+	case NRF24_PIPE2:
+	case NRF24_PIPE3:
+	case NRF24_PIPE4:
+	case NRF24_PIPE5:
+		ret = nrf24_write_reg(spi, RX_ADDR_P0 + pipe, *addr);
+		break;
+	default:
+		dev_dbg(&spi->dev, "%s: invalid parameter\n", __func__);
+	}
+
+	return ret;
+}
+
+ssize_t nrf24_get_address(struct spi_device *spi,
+			  enum nrf24_pipe_num pipe,
+			  u8 *addr)
+{
+	ssize_t ret;
+	ssize_t length;
+
+	switch (pipe) {
+	case NRF24_PIPE0:
+	case NRF24_PIPE1:
+	case NRF24_TX:
+		ret = nrf24_read_multireg(spi, pipe, addr);
+		if (ret < 0)
+			return ret;
+		break;
+	default:
+		length = nrf24_read_multireg(spi, NRF24_PIPE1, addr);
+		if (length < 0)
+			return length;
+		ret = nrf24_read_reg(spi, RX_ADDR_P0 + pipe);
+		if (ret < 0)
+			return ret;
+		*(addr) = ret;
+		break;
+	}
+
+	return nrf24_get_address_width(spi);
+}
+
+ssize_t nrf24_set_crc_mode(struct spi_device *spi, enum nrf24_crc_mode mode)
+{
+	ssize_t config;
+
+	config = nrf24_read_reg(spi, CONFIG);
+	if (config < 0)
+		return config;
+	config &= ~(EN_CRC | CRCO);
+	config |= (mode << 2);
+
+	config = nrf24_write_reg(spi, CONFIG, config);
+
+	return config;
+}
+
+ssize_t nrf24_get_crc_mode(struct spi_device *spi)
+{
+	ssize_t config;
+
+	config = nrf24_read_reg(spi, CONFIG);
+	if (config < 0)
+		return config;
+	config &= (EN_CRC | CRCO);
+	config >>= 2;
+
+	return config;
+}
+
+ssize_t nrf24_set_auto_retr_delay(struct spi_device *spi, u16 delay)
+{
+	ssize_t retr;
+
+	retr = nrf24_read_reg(spi, SETUP_RETR);
+	if (retr < 0)
+		return retr;
+
+	retr &= 0x0F;
+	retr |= (((delay / 250) - 1) << 4);
+
+	return nrf24_write_reg(spi, SETUP_RETR, retr);
+}
+
+ssize_t nrf24_get_auto_retr_delay(struct spi_device *spi)
+{
+	ssize_t retr;
+
+	retr = nrf24_read_reg(spi, SETUP_RETR);
+	if (retr < 0)
+		return retr;
+
+	return ((retr >> 4) + 1) * 250;
+}
+
+ssize_t nrf24_set_auto_retr_count(struct spi_device *spi, u8 count)
+{
+	ssize_t retr;
+
+	retr = nrf24_read_reg(spi, SETUP_RETR);
+	if (retr < 0)
+		return retr;
+
+	retr &= 0xF0;
+	retr |= (count & 0x0F);
+
+	return nrf24_write_reg(spi, SETUP_RETR, retr);
+}
+
+ssize_t nrf24_get_auto_retr_count(struct spi_device *spi)
+{
+	ssize_t retr;
+
+	retr = nrf24_read_reg(spi, SETUP_RETR);
+
+	if (retr < 0)
+		return retr;
+
+	return retr & 0x0F;
+}
+
+ssize_t nrf24_set_address_width(struct spi_device *spi,
+				enum nrf24_address_width aw)
+{
+	return nrf24_write_reg(spi, SETUP_AW, aw - 2);
+}
+
+ssize_t nrf24_get_address_width(struct spi_device *spi)
+{
+	return nrf24_read_reg(spi, SETUP_AW) + 2;
+}
+
+ssize_t nrf24_lock_unlock(struct spi_device *spi)
+{
+	return nrf24_write_reg(spi, LOCK_UNLOCK, 0x73);
+}
+
+ssize_t nrf24_set_datarate(struct spi_device *spi, enum nrf24_datarate datarate)
+{
+	ssize_t rf;
+
+	rf = nrf24_read_reg(spi, RF_SETUP);
+	if (rf < 0)
+		return rf;
+	if (datarate == NRF24_DATARATE_1MBPS)
+		rf &= ~RF_DR_HI;
+	else
+		rf |= RF_DR_HI;
+
+	return nrf24_write_reg(spi, RF_SETUP, rf);
+}
+
+ssize_t nrf24_get_datarate(struct spi_device *spi)
+{
+	ssize_t rf;
+	ssize_t lo;
+	ssize_t hi;
+
+	rf = nrf24_read_reg(spi, RF_SETUP);
+	if (rf < 0)
+		return rf;
+
+	lo = rf & RF_DR_LO;
+	hi = rf & RF_DR_HI;
+
+	if (lo && hi)
+		return -EINVAL;
+	if (lo)
+		return NRF24_DATARATE_256KBPS;
+	if (hi)
+		return NRF24_DATARATE_2MBPS;
+	return NRF24_DATARATE_1MBPS;
+}
+
+ssize_t nrf24_set_mode(struct spi_device *spi, enum nrf24_mode mode)
+{
+	ssize_t config;
+
+	config = nrf24_read_reg(spi, CONFIG);
+
+	if (config < 0)
+		return config;
+
+	if (mode == NRF24_MODE_RX)
+		config |= PRIM_RX;
+	else
+		config &= ~PRIM_RX;
+
+	return nrf24_write_reg(spi, CONFIG, config);
+}
+
+ssize_t nrf24_set_rf_power(struct spi_device *spi, enum nrf24_rf_power rf_pwr)
+{
+	ssize_t rf_setup;
+
+	rf_setup = nrf24_read_reg(spi, RF_SETUP);
+
+	if (rf_setup < 0)
+		return rf_setup;
+
+	rf_setup &= ~(RF_PWR1 | RF_PWR0);
+	rf_setup |= (rf_pwr << 1);
+
+	return nrf24_write_reg(spi, RF_SETUP, rf_setup);
+}
+
+ssize_t nrf24_get_rf_power(struct spi_device *spi)
+{
+	ssize_t rf;
+
+	rf = nrf24_read_reg(spi, RF_SETUP);
+
+	if (rf < 0)
+		return rf;
+
+	rf &= (RF_PWR1 | RF_PWR0);
+	rf >>= 1;
+
+	return rf;
+}
+
+//plw = 0 -> dynamic
+ssize_t nrf24_set_rx_pload_width(struct spi_device *spi, u8 pipe, u8 plw)
+{
+	ssize_t ret;
+
+	if (plw > PLOAD_MAX)
+		return -EINVAL;
+
+	ret = nrf24_write_reg(spi, RX_PW_P0 + pipe, plw);
+	if (ret < 0)
+		return ret;
+
+	return nrf24_setup_dynamic_pl(spi, pipe, plw == 0);
+}
+
+ssize_t nrf24_set_rf_channel(struct spi_device *spi, u8 channel)
+{
+	return nrf24_write_reg(spi, RF_CH, channel);
+}
+
+ssize_t nrf24_power_up(struct spi_device *spi)
+{
+	ssize_t config;
+
+	config = nrf24_read_reg(spi, CONFIG);
+
+	if (config < 0)
+		return config;
+
+	return nrf24_write_reg(spi, CONFIG, config | PWR_UP);
+}
+
+ssize_t nrf24_write_tx_pload(struct spi_device *dev, u8 *buf, u8 length)
+{
+	return nrf24_write_multireg(dev, NRF24_TX_PLOAD, buf, length);
+}
+
+ssize_t nrf24_write_tx_pload_noack(struct spi_device *dev, u8 *buf, u8 length)
+{
+	return nrf24_write_multireg(dev, NRF24_TX_PLOAD_NOACK, buf, length);
+}
+
+ssize_t nrf24_read_rx_pload(struct spi_device *spi, u8 *buf)
+{
+	return nrf24_read_multireg(spi, NRF24_RX_PLOAD, buf);
+}
+
+ssize_t nrf24_get_status(struct spi_device *spi)
+{
+	return nrf24_read_reg(spi, STATUS);
+}
+
+ssize_t nrf24_get_rx_data_source(struct spi_device *spi)
+{
+	ssize_t status;
+
+	status = nrf24_get_status(spi);
+	if (status < 0)
+		return status;
+	return (status & 0x0E) >> 1;
+}
+
+ssize_t nrf24_get_rx_pload_width(struct spi_device *spi, u8 pipe)
+{
+	return nrf24_read_reg(spi, RX_PW_P0 + pipe);
+}
+
+ssize_t nrf24_get_rx_pl_w(struct spi_device *spi)
+{
+	return nrf24_read_reg(spi, R_RX_PL_WID);
+}
+
+ssize_t nrf24_soft_reset(struct spi_device *spi)
+{
+	ssize_t ret;
+	u8 addr0[5] = {0xE7, 0xE7, 0xE7, 0xE7, 0xE7};
+	u8 addr1[5] = {0xC2, 0xC2, 0xC2, 0xC2, 0xC2};
+
+	ret = nrf24_write_reg(spi, CONFIG, 0x08);
+	if (ret < 0)
+		return ret;
+	ret = nrf24_write_reg(spi, EN_AA, 0x3F);
+	if (ret < 0)
+		return ret;
+	ret = nrf24_write_reg(spi, EN_RXADDR, 0x03);
+	if (ret < 0)
+		return ret;
+	ret = nrf24_write_reg(spi, SETUP_AW, 0x03);
+	if (ret < 0)
+		return ret;
+	ret = nrf24_write_reg(spi, SETUP_RETR, 0x03);
+	if (ret < 0)
+		return ret;
+	ret = nrf24_write_reg(spi, RF_CH, 0x02);
+	if (ret < 0)
+		return ret;
+	ret = nrf24_write_reg(spi, RF_SETUP, 0x07);
+	if (ret < 0)
+		return ret;
+	ret = nrf24_write_reg(spi, STATUS, 0x70);
+	if (ret < 0)
+		return ret;
+	ret = nrf24_set_address(spi, NRF24_PIPE0, addr0);
+	if (ret < 0)
+		return ret;
+	ret = nrf24_set_address(spi, NRF24_PIPE1, addr1);
+	if (ret < 0)
+		return ret;
+	ret = nrf24_write_reg(spi, RX_ADDR_P2, 0xC3);
+	if (ret < 0)
+		return ret;
+	ret = nrf24_write_reg(spi, RX_ADDR_P3, 0xC4);
+	if (ret < 0)
+		return ret;
+	ret = nrf24_write_reg(spi, RX_ADDR_P4, 0xC5);
+	if (ret < 0)
+		return ret;
+	ret = nrf24_write_reg(spi, RX_ADDR_P5, 0xC6);
+	if (ret < 0)
+		return ret;
+	ret = nrf24_set_address(spi, NRF24_TX, addr0);
+	if (ret < 0)
+		return ret;
+	ret = nrf24_write_reg(spi, RX_PW_P0, 0x00);
+	if (ret < 0)
+		return ret;
+	ret = nrf24_write_reg(spi, RX_PW_P1, 0x00);
+	if (ret < 0)
+		return ret;
+	ret = nrf24_write_reg(spi, RX_PW_P2, 0x00);
+	if (ret < 0)
+		return ret;
+	ret = nrf24_write_reg(spi, RX_PW_P3, 0x00);
+	if (ret < 0)
+		return ret;
+	ret = nrf24_write_reg(spi, RX_PW_P4, 0x00);
+	if (ret < 0)
+		return ret;
+	ret = nrf24_write_reg(spi, RX_PW_P5, 0x00);
+	if (ret < 0)
+		return ret;
+	ret = nrf24_write_reg(spi, DYNPD, 0x00);
+	if (ret < 0)
+		return ret;
+	ret = nrf24_write_reg(spi, FEATURE, 0x00);
+	if (ret < 0)
+		return ret;
+
+	return ret;
+}
+
+ssize_t nrf24_clear_irq(struct spi_device *spi, u8 irq)
+{
+	return nrf24_write_reg(spi, STATUS, irq);
+}
+
+ssize_t nrf24_flush_fifo(struct spi_device *spi)
+{
+	ssize_t ret;
+
+	ret = nrf24_write_reg(spi, FLUSH_RX, 0);
+	if (ret < 0)
+		return ret;
+	return nrf24_write_reg(spi, FLUSH_TX, 0);
+}
+
+ssize_t nrf24_print_status(struct spi_device *spi)
+{
+const u8 nrf_reg[] = {
+	CONFIG,
+	EN_AA,
+	EN_RXADDR,
+	SETUP_AW,
+	SETUP_RETR,
+	RF_CH,
+	RF_SETUP,
+	STATUS,
+	OBSERVE_TX,
+	CD,
+	FIFO_STATUS,
+	DYNPD,
+	FEATURE
+};
+
+char *nrf_reg_name[] = {
+	"CONFIG",
+	"EN_AA",
+	"EN_RXADDR",
+	"SETUP_AW",
+	"SETUP_RETR",
+	"RF_CH",
+	"RF_SETUP",
+	"STATUS",
+	"OBSERVE_TX",
+	"CD",
+	"FIFO_STATUS",
+	"DYNPD",
+	"FEATURE"
+};
+
+	ssize_t loop;
+	ssize_t ret;
+
+	for (loop = 0; loop < 13; loop++) {
+		ret = spi_w8r8(spi, nrf_reg[loop]);
+		if (ret < 0)
+			return ret;
+
+		dev_dbg(&spi->dev,
+			"%s: %s = 0%02zx\n",
+			__func__,
+			nrf_reg_name[loop],
+			ret);
+	}
+
+	return 0;
+}
+
+ssize_t nrf24_get_auto_ack(struct spi_device *spi, u8 pipe)
+{
+	ssize_t aa;
+
+	aa = nrf24_read_reg(spi, EN_AA);
+	if (aa < 0)
+		return aa;
+
+	return (aa & BIT(pipe)) == BIT(pipe);
+}
+
+ssize_t nrf24_is_rx_fifo_empty(struct spi_device *spi)
+{
+	ssize_t fifo;
+
+	fifo = nrf24_read_reg(spi, FIFO_STATUS);
+	if (fifo < 0)
+		return fifo;
+
+	return fifo & 0x01;
+}
+
+ssize_t nrf24_reuse_tx_pl(struct spi_device *spi)
+{
+	return nrf24_write_reg(spi, REUSE_TX_PL, 0);
+}
+
diff --git a/drivers/staging/nrf24/nrf24_hal.h b/drivers/staging/nrf24/nrf24_hal.h
new file mode 100644
index 000000000000..ce7fc190e286
--- /dev/null
+++ b/drivers/staging/nrf24/nrf24_hal.h
@@ -0,0 +1,54 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/*
+ * Copyright (C) 2017 Marcin Ciupak <marcin.s.ciupak@xxxxxxxxx>
+ *
+ */
+
+#ifndef NRF24_HAL_H
+#define NRF24_HAL_H
+
+#include "nRF24L01.h"
+#include "nrf24_enums.h"
+
+ssize_t nrf24_open_pipe(struct spi_device *spi, enum nrf24_pipe_num pipe);
+ssize_t nrf24_close_pipe(struct spi_device *spi, enum nrf24_pipe_num pipe);
+ssize_t nrf24_set_address(struct spi_device *spi, enum nrf24_pipe_num pipe, u8 *addr);
+ssize_t nrf24_get_address(struct spi_device *spi, enum nrf24_pipe_num pipe, u8 *addr);
+ssize_t nrf24_set_crc_mode(struct spi_device *spi, enum nrf24_crc_mode mode);
+ssize_t nrf24_get_crc_mode(struct spi_device *spi);
+ssize_t nrf24_set_auto_retr_count(struct spi_device *spi, u8 count);
+ssize_t nrf24_get_auto_retr_count(struct spi_device *spi);
+ssize_t nrf24_set_auto_retr_delay(struct spi_device *spi, u16 delay);
+ssize_t nrf24_get_auto_retr_delay(struct spi_device *spi);
+ssize_t nrf24_set_address_width(struct spi_device *spi, enum nrf24_address_width aw);
+ssize_t nrf24_get_address_width(struct spi_device *spi);
+ssize_t nrf24_lock_unlock(struct spi_device *spi);
+ssize_t nrf24_set_datarate(struct spi_device *spi, enum nrf24_datarate datarate);
+ssize_t nrf24_get_datarate(struct spi_device *spi);
+ssize_t nrf24_set_mode(struct spi_device *spi, enum nrf24_mode mode);
+ssize_t nrf24_set_rf_power(struct spi_device *spi, enum nrf24_rf_power rf_pwr);
+ssize_t nrf24_get_rf_power(struct spi_device *spi);
+ssize_t nrf24_set_rx_pload_width(struct spi_device *spi, u8 pipe_no, u8 plw);
+ssize_t nrf24_set_rf_channel(struct spi_device *spi, u8 channel);
+ssize_t nrf24_write_tx_pload(struct spi_device *dev, u8 *buf, u8 length);
+ssize_t nrf24_write_tx_pload_noack(struct spi_device *dev, u8 *buf, u8 length);
+ssize_t nrf24_read_rx_pload(struct spi_device *spi, u8 *buf);
+ssize_t nrf24_power_up(struct spi_device *spi);
+ssize_t nrf24_get_status(struct spi_device *spi);
+ssize_t nrf24_get_rx_data_source(struct spi_device *spi);
+ssize_t nrf24_get_rx_pload_width(struct spi_device *spi, u8 pipe);
+ssize_t nrf24_soft_reset(struct spi_device *spi);
+ssize_t nrf24_clear_irq(struct spi_device *spi, u8 irq);
+ssize_t nrf24_flush_fifo(struct spi_device *spi);
+ssize_t nrf24_get_rx_pl_w(struct spi_device *spi);
+ssize_t nrf24_print_status(struct spi_device *spi);
+ssize_t nrf24_get_auto_ack(struct spi_device *spi, u8 pipe);
+ssize_t nrf24_setup_auto_ack(struct spi_device *spi, u8 pipe, bool enable);
+ssize_t nrf24_is_rx_fifo_empty(struct spi_device *spi);
+ssize_t nrf24_reuse_tx_pl(struct spi_device *spi);
+ssize_t nrf24_get_dynamic_pl(struct spi_device *spi);
+ssize_t nrf24_enable_dynamic_pl(struct spi_device *spi);
+ssize_t nrf24_disable_dynamic_pl(struct spi_device *spi);
+
+#endif
diff --git a/drivers/staging/nrf24/nrf24_if.c b/drivers/staging/nrf24/nrf24_if.c
new file mode 100644
index 000000000000..e2f539c7a420
--- /dev/null
+++ b/drivers/staging/nrf24/nrf24_if.c
@@ -0,0 +1,893 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright (C) 2017 Marcin Ciupak <marcin.s.ciupak@xxxxxxxxx>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/cdev.h>
+#include <linux/spi/spi.h>
+#include <linux/err.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/of.h>
+#include <linux/gpio/consumer.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/kfifo.h>
+#include <linux/wait.h>
+#include <linux/poll.h>
+#include <linux/list.h>
+
+#include "nrf24_if.h"
+#include "nrf24_sysfs.h"
+#include "nrf24_hal.h"
+
+#define N_NRF24_MINORS                  BIT(MINORBITS)
+
+static dev_t nrf24_dev;
+static DEFINE_IDA(nrf24_ida_pipe);
+static DEFINE_IDA(nrf24_ida_dev);
+static struct class *nrf24_class;
+
+ATTRIBUTE_GROUPS(nrf24_pipe);
+ATTRIBUTE_GROUPS(nrf24);
+
+static bool nrf24_is_rx_active(struct nrf24_device *device)
+{
+	struct nrf24_pipe *pipe;
+
+	bool active = false;
+
+	list_for_each_entry(pipe, &device->pipes, list)
+		active |= pipe->rx_size > 0;
+
+	return active;
+}
+
+static void nrf24_ce_hi(struct nrf24_device *device)
+{
+	gpiod_set_value(device->ce, 1);
+}
+
+static void nrf24_ce_lo(struct nrf24_device *device)
+{
+	gpiod_set_value(device->ce, 0);
+}
+
+static struct nrf24_pipe *nrf24_find_pipe_id(struct nrf24_device *device, int id)
+{
+	struct nrf24_pipe *pipe;
+
+	list_for_each_entry(pipe, &device->pipes, list)
+		if (pipe->id == id)
+			return pipe;
+
+	return ERR_PTR(-ENODEV);
+}
+
+static int nrf24_tx_thread(void *data)
+{
+	struct nrf24_device *device = data;
+	struct nrf24_pipe *p;
+	u8 pload[PLOAD_MAX];
+	int ret;
+	ssize_t size;
+	ssize_t pload_length;
+	ssize_t sent = 0;
+	u8 *buf;
+	bool spl;
+	bool dpl = false;
+
+	while (true) {
+		dev_dbg(&device->dev,
+			"%s: waiting for new messages\n",
+			__func__);
+		wait_event_interruptible(device->tx_wait_queue,
+					 kthread_should_stop() ||
+					 (!nrf24_is_rx_active(device) && !kfifo_is_empty(&device->tx_fifo)));
+
+		if (kthread_should_stop())
+			return 0;
+
+		device->tx_done = false;
+
+		//fifo lock is needed as write to tx fifo may be done by 6 pipes
+		mutex_lock(&device->tx_fifo_mutex);
+
+		ret = kfifo_out(&device->tx_fifo, &p, sizeof(p));
+		if (ret != sizeof(p)) {
+			dev_dbg(&device->dev, "get pipe from fifo failed\n");
+			mutex_unlock(&device->tx_fifo_mutex);
+			continue;
+		}
+
+		ret = kfifo_out(&device->tx_fifo, &size, sizeof(size));
+		if (ret != sizeof(size)) {
+			dev_dbg(&device->dev, "get size from fifo failed\n");
+			mutex_unlock(&device->tx_fifo_mutex);
+			continue;
+		}
+
+		buf = kzalloc(size, GFP_KERNEL);
+		if (!buf) {
+			dev_dbg(&device->dev, "buf alloc failed\n");
+			mutex_unlock(&device->tx_fifo_mutex);
+			continue;
+		}
+
+		ret = kfifo_out(&device->tx_fifo, buf, size);
+		if (ret != size) {
+			dev_dbg(&device->dev, "get buf from fifo failed\n");
+			mutex_unlock(&device->tx_fifo_mutex);
+			goto next;
+		}
+
+		mutex_unlock(&device->tx_fifo_mutex);
+
+		//enter Standby-I mode
+		nrf24_ce_lo(device);
+
+		ret = nrf24_set_mode(device->spi, NRF24_MODE_TX);
+		if (ret < 0)
+			goto next;
+
+		//set PIPE0 address in order to receive ACK
+		ret = nrf24_set_address(device->spi,
+					NRF24_PIPE0,
+					(u8 *)&p->cfg.address);
+		if (ret < 0) {
+			dev_dbg(&device->dev, "set PIPE0 address failed (%d)\n", ret);
+			goto next;
+		}
+
+		ret = nrf24_set_address(device->spi,
+					NRF24_TX,
+					(u8 *)&p->cfg.address);
+		if (ret < 0) {
+			dev_dbg(&device->dev, "set TX address failed (%d)\n", ret);
+			goto next;
+		}
+
+		//check if pipe uses static payload length
+		spl = p->cfg.plw != 0;
+
+		//check if dynamic payload length is enabled
+		dpl = nrf24_get_dynamic_pl(device->spi);
+
+		if (spl && dpl) {
+			//disable dynamic payload if pipe
+			//does not use dynamic payload
+			//and dynamic paload is enabled
+			ret = nrf24_disable_dynamic_pl(device->spi);
+			if (ret < 0)
+				goto next;
+		}
+
+		memset(pload, 0, PLOAD_MAX);
+		memcpy(pload, &size, sizeof(size));
+
+		//calculate payload length
+		pload_length = spl ? p->cfg.plw : sizeof(size);
+
+		//send size
+		nrf24_write_tx_pload(device->spi, pload, pload_length);
+		if (ret < 0) {
+			dev_dbg(&device->dev, "write TX PLOAD failed (%d)\n", ret);
+			goto next;
+		}
+
+		//enter TX MODE and start transmission
+		nrf24_ce_hi(device);
+
+		//wait for ACK
+		wait_event_interruptible(device->tx_done_wait_queue,
+					 (device->tx_done ||
+					 kthread_should_stop()));
+
+		if (kthread_should_stop())
+			goto abort;
+
+		sent = 0;
+
+		while (size > 0) {
+			pload_length = spl ? p->cfg.plw : min_t(ssize_t, size, PLOAD_MAX);
+
+			dev_dbg(&device->dev, "tx %zd bytes\n", pload_length);
+
+			memset(pload, 0, PLOAD_MAX);
+			memcpy(pload, buf + sent, pload_length);
+
+			ret = nrf24_write_tx_pload(device->spi, pload, pload_length);
+
+			if (ret < 0) {
+				dev_dbg(&device->dev,
+					"write TX PLOAD failed (%d)\n",
+					ret);
+				goto next;
+			}
+
+			sent += pload_length;
+			size -= pload_length;
+
+			device->tx_done = false;
+
+			//wait for ACK
+			wait_event_interruptible(device->tx_done_wait_queue,
+						 (device->tx_done ||
+						 kthread_should_stop()));
+
+			if (kthread_should_stop())
+				goto abort;
+		}
+next:
+		kfree(buf);
+
+		//restore dynamic payload feature
+		if (dpl)
+			nrf24_enable_dynamic_pl(device->spi);
+
+		//if all sent enter RX MODE and start receiving
+		if (kfifo_is_empty(&device->tx_fifo)) {
+			dev_dbg(&device->dev, "%s: NRF24_MODE_RX\n", __func__);
+
+			//enter Standby-I
+			nrf24_ce_lo(device);
+
+			p = nrf24_find_pipe_id(device, NRF24_PIPE0);
+			if (!IS_ERR(p)) {
+				//restore PIPE0 address as it was corrupted
+				nrf24_set_address(device->spi,
+						  p->id,
+						  (u8 *)&p->cfg.address);
+			}
+
+			nrf24_set_mode(device->spi, NRF24_MODE_RX);
+			nrf24_ce_hi(device);
+		}
+	}
+abort:
+	kfree(buf);
+
+	return 0;
+}
+
+static int nrf24_rx_thread(void *data)
+{
+	struct nrf24_device *device = data;
+	ssize_t pipe;
+	ssize_t length;
+	u8 pload[PLOAD_MAX];
+	struct nrf24_pipe *p;
+
+	while (true) {
+		wait_event_interruptible(device->rx_wait_queue,
+					 (!nrf24_is_rx_fifo_empty(device->spi) ||
+					 kthread_should_stop()));
+		if (kthread_should_stop())
+			return 0;
+
+		pipe = nrf24_get_rx_data_source(device->spi);
+		if (pipe < 0) {
+			dev_dbg(&device->dev,
+				"%s: get pipe failed (err: %zd)\n",
+				__func__,
+				pipe);
+			continue;
+		}
+
+		if (pipe > NRF24_PIPE5) {
+			dev_dbg(&device->dev,
+				"%s: RX FIFO is empty!\n",
+				__func__);
+			continue;
+		}
+
+		p = nrf24_find_pipe_id(device, pipe);
+		if (IS_ERR(p))
+			continue;
+
+		memset(pload, 0, PLOAD_MAX);
+		length = nrf24_read_rx_pload(device->spi, pload);
+		if (length < 0) {
+			dev_dbg(&device->dev,
+				"%s: could not read pload (err = %zd)\n",
+				__func__,
+				length);
+			continue;
+		}
+
+		dev_dbg(p->dev, "rx %zd bytes\n", length);
+		if (p->rx_size <= 0) {
+			memcpy(&p->rx_size, pload, sizeof(p->rx_size));
+			dev_dbg(p->dev, "RX active\n");
+		} else {
+			length = p->rx_size < p->cfg.plw ? p->rx_size : length;
+
+			p->rx_size -= kfifo_in(&p->rx_fifo, &pload, length);
+
+			if (p->rx_size <= 0) {
+				dev_dbg(p->dev, "RX done\n");
+				wake_up_interruptible(&p->poll_wait_queue);
+			}
+		}
+
+		//start tx if all rx done and tx requested during active rx
+		if (!nrf24_is_rx_active(device) && !kfifo_is_empty(&device->tx_fifo)) {
+			dev_dbg(&device->dev, "wake up TX...\n");
+			wake_up_interruptible(&device->tx_wait_queue);
+		}
+	}
+}
+
+static void nrf24_isr_work_handler(struct work_struct *work)
+{
+	struct nrf24_device *device;
+	ssize_t status;
+
+	device = container_of(work, struct nrf24_device, isr_work);
+
+	status = nrf24_get_status(device->spi);
+	if (status < 0)
+		return;
+
+	if (status & RX_DR) {
+		dev_dbg(&device->dev, "%s: RX_DR\n", __func__);
+		nrf24_clear_irq(device->spi, RX_DR);
+		wake_up_interruptible(&device->rx_wait_queue);
+	}
+
+	if (status & TX_DS) {
+		dev_dbg(&device->dev, "%s: TX_DS\n", __func__);
+		nrf24_clear_irq(device->spi, TX_DS);
+		device->tx_done = true;
+		wake_up_interruptible(&device->tx_done_wait_queue);
+	}
+
+	if (status & MAX_RT) {
+		nrf24_ce_lo(device);
+		dev_dbg_ratelimited(&device->dev, "%s: MAX_RT\n", __func__);
+		nrf24_clear_irq(device->spi, MAX_RT);
+		nrf24_reuse_tx_pl(device->spi);
+		nrf24_ce_hi(device);
+	}
+}
+
+static irqreturn_t nrf24_isr(int irq, void *dev_id)
+{
+	unsigned long flags;
+	struct nrf24_device *device = dev_id;
+
+	spin_lock_irqsave(&device->lock, flags);
+
+	schedule_work(&device->isr_work);
+
+	spin_unlock_irqrestore(&device->lock, flags);
+
+	return IRQ_HANDLED;
+}
+
+static ssize_t nrf24_read(struct file *filp,
+			  char __user *buf,
+			  size_t size,
+			  loff_t *f_pos)
+{
+	struct nrf24_pipe *p;
+	unsigned int copied;
+	ssize_t n;
+
+	p = filp->private_data;
+
+	if (kfifo_is_empty(&p->rx_fifo) && (filp->f_flags & O_NONBLOCK))
+		return -EAGAIN;
+
+	n = kfifo_to_user(&p->rx_fifo, buf, size, &copied);
+	if (n)
+		return n;
+	return copied;
+}
+
+static ssize_t nrf24_write(struct file *filp,
+			   const char __user *buf,
+			   size_t size,
+			   loff_t *f_pos)
+{
+	struct nrf24_device *device;
+	struct nrf24_pipe *p;
+	ssize_t n;
+	unsigned int copied;
+
+	p = filp->private_data;
+	device = to_nrf24_device(p->dev->parent);
+
+	dev_dbg(p->dev, "write (%zd)\n", size);
+
+	mutex_lock(&device->tx_fifo_mutex);
+
+	n = kfifo_in(&device->tx_fifo, &p, sizeof(p));
+	if (n != sizeof(p))
+		goto err_kfifo_reset;
+
+	n = kfifo_in(&device->tx_fifo, &size, sizeof(size));
+	if (n != sizeof(size))
+		goto err_kfifo_reset;
+
+	n = kfifo_from_user(&device->tx_fifo,
+			    buf,
+			    size,
+			    &copied);
+	if (n || size != copied)
+		goto err_kfifo_reset;
+
+	mutex_unlock(&device->tx_fifo_mutex);
+
+	wake_up_interruptible(&device->tx_wait_queue);
+
+	return copied;
+err_kfifo_reset:
+	kfifo_reset(&device->tx_fifo);
+	mutex_unlock(&device->tx_fifo_mutex);
+	return -EAGAIN;
+}
+
+static int nrf24_open(struct inode *inode, struct file *filp)
+{
+	struct nrf24_pipe *pipe;
+
+	pipe = container_of(inode->i_cdev, struct nrf24_pipe, cdev);
+
+	if (!pipe) {
+		pr_err("device: minor %d unknown.\n", iminor(inode));
+		return -ENODEV;
+	}
+
+	filp->private_data = pipe;
+	nonseekable_open(inode, filp);
+
+	return 0;
+}
+
+static int nrf24_release(struct inode *inode, struct file *filp)
+{
+	filp->private_data = NULL;
+
+	return 0;
+}
+
+static unsigned int nrf24_poll(struct file *filp,
+			       struct poll_table_struct *wait)
+{
+	struct nrf24_device *device;
+	struct nrf24_pipe *p;
+
+	p = filp->private_data;
+	device = to_nrf24_device(p->dev->parent);
+
+	dev_dbg(p->dev, "%s: waiting...\n", __func__);
+	poll_wait(filp, &p->poll_wait_queue, wait);
+	if (!kfifo_is_empty(&p->rx_fifo)) {
+		dev_dbg(p->dev, "%s: got data!\n", __func__);
+		return POLLIN | POLLRDNORM;
+	}
+	dev_dbg(p->dev, "%s: no data!\n", __func__);
+	return 0;
+}
+
+static void nrf24_destroy_devices(struct nrf24_device *device)
+{
+	struct nrf24_pipe *pipe, *temp;
+
+	list_for_each_entry_safe(pipe, temp, &device->pipes, list) {
+		cdev_del(&pipe->cdev);
+		device_destroy(nrf24_class, pipe->devt);
+		ida_simple_remove(&nrf24_ida_pipe, MINOR(pipe->devt));
+		list_del(&pipe->list);
+		kfree(pipe);
+	}
+}
+
+static const struct file_operations nrf24_fops = {
+	.owner = THIS_MODULE,
+	.open = nrf24_open,
+	.release = nrf24_release,
+	.read = nrf24_read,
+	.write = nrf24_write,
+	.llseek = no_llseek,
+	.poll = nrf24_poll,
+};
+
+static struct nrf24_pipe *nrf24_create_pipe(struct nrf24_device *device, int id)
+{
+	int ret;
+	struct nrf24_pipe *p;
+
+	//sets flags to false as well
+	p = kzalloc(sizeof(*p), GFP_KERNEL);
+	if (!p) {
+		ret = -ENOMEM;
+		goto err_return;
+	}
+
+	ret = ida_simple_get(&nrf24_ida_pipe, 0, 0, GFP_KERNEL);
+	if (ret < 0) {
+		dev_err(&device->dev, "%s: get_minor failed\n", __func__);
+		goto err_free_mem;
+	}
+
+	p->devt = MKDEV(MAJOR(nrf24_dev), ret);
+	p->id = id;
+
+	INIT_KFIFO(p->rx_fifo);
+	init_waitqueue_head(&p->poll_wait_queue);
+
+	p->dev = device_create_with_groups(nrf24_class,
+					   &device->dev,
+					   p->devt,
+					   p,
+					   nrf24_pipe_groups,
+					   "%s.%d",
+					   dev_name(&device->dev),
+					   id);
+
+	if (IS_ERR(p->dev)) {
+		dev_err(&device->dev,
+			"%s: device_create of '%s' failed\n",
+			__func__,
+			dev_name(p->dev));
+		ret = PTR_ERR(p->dev);
+		goto err_ida_remove;
+	}
+
+	cdev_init(&p->cdev, &nrf24_fops);
+	p->cdev.owner = THIS_MODULE;
+	ret = cdev_add(&p->cdev, p->devt, 1);
+	if (ret < 0) {
+		dev_err(&device->dev, "%s: cdev failed\n", __func__);
+		goto err_dev_destroy;
+	}
+
+	dev_dbg(&device->dev,
+		"%s: device created: major(%d), minor(%d)\n",
+		__func__,
+		MAJOR(p->devt),
+		MINOR(p->devt));
+
+	return p;
+
+err_dev_destroy:
+	device_destroy(nrf24_class, p->devt);
+err_ida_remove:
+	ida_simple_remove(&nrf24_ida_pipe, MINOR(p->devt));
+err_free_mem:
+	kfree(p);
+err_return:
+	return ERR_PTR(ret);
+}
+
+static void nrf24_gpio_free(struct nrf24_device *device)
+{
+	if (!IS_ERR(device->ce))
+		gpiod_put(device->ce);
+
+	free_irq(device->spi->irq, device);
+}
+
+static int nrf24_gpio_setup(struct nrf24_device *device)
+{
+	int ret;
+
+	device->ce = gpiod_get(&device->spi->dev, "ce", 0);
+
+	if (device->ce == ERR_PTR(-ENOENT))
+		dev_dbg(&device->dev, "%s: no entry for CE\n", __func__);
+	else if (device->ce == ERR_PTR(-EBUSY))
+		dev_dbg(&device->dev, "%s: CE is busy\n", __func__);
+
+	if (IS_ERR(device->ce)) {
+		ret = PTR_ERR(device->ce);
+		dev_err(&device->dev, "%s: CE gpio setup error\n", __func__);
+		return ret;
+	}
+
+	nrf24_ce_lo(device);
+
+	ret = request_irq(device->spi->irq,
+			  nrf24_isr,
+			  0,
+			  dev_name(&device->dev),
+			  device);
+	if (ret < 0) {
+		gpiod_put(device->ce);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void nrf24_dev_release(struct device *dev)
+{
+	struct nrf24_device *device = to_nrf24_device(dev);
+
+	ida_simple_remove(&nrf24_ida_dev, device->id);
+	kfree(device);
+}
+
+static struct device_type nrf24_dev_type = {
+	.name = "nrf24_device",
+	.release = nrf24_dev_release,
+};
+
+static struct nrf24_device *nrf24_dev_init(struct spi_device *spi)
+{
+	int ret;
+	struct nrf24_device *device;
+	int id;
+
+	id = ida_simple_get(&nrf24_ida_dev, 0, 0, GFP_KERNEL);
+	if (id < 0)
+		return ERR_PTR(id);
+
+	//sets flags to false as well
+	device = kzalloc(sizeof(*device), GFP_KERNEL);
+	if (!device) {
+		ida_simple_remove(&nrf24_ida_dev, id);
+		return ERR_PTR(-ENOMEM);
+	}
+	device->spi = spi;
+
+	dev_set_name(&device->dev, "nrf%d", id);
+	device->id = id;
+	device->dev.parent = &spi->dev;
+	device->dev.class = nrf24_class;
+	device->dev.type = &nrf24_dev_type;
+	device->dev.groups = nrf24_groups;
+	ret = device_register(&device->dev);
+	if (ret < 0) {
+		put_device(&device->dev);
+		ida_simple_remove(&nrf24_ida_dev, id);
+
+		return ERR_PTR(ret);
+	}
+
+	init_waitqueue_head(&device->tx_wait_queue);
+	init_waitqueue_head(&device->tx_done_wait_queue);
+	init_waitqueue_head(&device->rx_wait_queue);
+
+	INIT_WORK(&device->isr_work, nrf24_isr_work_handler);
+	INIT_KFIFO(device->tx_fifo);
+	spin_lock_init(&device->lock);
+	mutex_init(&device->tx_fifo_mutex);
+
+	INIT_LIST_HEAD(&device->pipes);
+
+	return device;
+}
+
+static int nrf24_hal_init(struct nrf24_device *device)
+{
+	int ret;
+	struct spi_device *spi = device->spi;
+	struct nrf24_pipe *pipe;
+
+	ret = nrf24_soft_reset(spi);
+	if (ret < 0)
+		return ret;
+
+	list_for_each_entry(pipe, &device->pipes, list) {
+		ret = nrf24_get_address(spi,
+					pipe->id,
+					(u8 *)&pipe->cfg.address);
+		if (ret < 0)
+			return ret;
+		ret = nrf24_get_auto_ack(spi, pipe->id);
+		if (ret < 0)
+			return ret;
+		pipe->cfg.ack = ret;
+
+		//0 -> dynamic pload
+		pipe->cfg.plw = 0;
+		ret = nrf24_set_rx_pload_width(spi, pipe->id, 0);
+		if (ret < 0)
+			return ret;
+	}
+
+	ret = nrf24_flush_fifo(spi);
+	if (ret < 0)
+		return ret;
+	ret = nrf24_open_pipe(spi, NRF24_PIPE_ALL);
+	if (ret < 0)
+		return ret;
+	ret = nrf24_lock_unlock(spi);
+	if (ret < 0)
+		return ret;
+	ret = nrf24_set_mode(spi, NRF24_MODE_RX);
+	if (ret < 0)
+		return ret;
+	ret = nrf24_set_crc_mode(spi, NRF24_CRC_16BIT);
+	if (ret < 0)
+		return ret;
+	ret = nrf24_set_auto_retr_count(spi, 15);
+	if (ret < 0)
+		return ret;
+	ret = nrf24_set_auto_retr_delay(spi, 4000);
+	if (ret < 0)
+		return ret;
+	ret = nrf24_set_rf_power(spi, NRF24_POWER_0DBM);
+	if (ret < 0)
+		return ret;
+	ret = nrf24_set_datarate(spi, NRF24_DATARATE_2MBPS);
+	if (ret < 0)
+		return ret;
+	ret = nrf24_power_up(spi);
+	if (ret < 0)
+		return ret;
+
+	nrf24_ce_hi(device);
+
+	return ret;
+}
+
+static int nrf24_probe(struct spi_device *spi)
+{
+	int ret;
+	struct nrf24_device *device;
+	struct nrf24_pipe *pipe;
+	int i;
+
+	spi->mode = SPI_MODE_0;
+	spi->bits_per_word = 8;
+
+	ret = spi_setup(spi);
+	if (ret < 0) {
+		dev_err(&spi->dev, "%s: spi_setup failed\n", __func__);
+		return ret;
+	}
+
+	device = nrf24_dev_init(spi);
+	if (IS_ERR(device)) {
+		dev_err(&device->spi->dev, "%s: dev_init failed\n", __func__);
+		return PTR_ERR(device);
+	}
+
+	ret = nrf24_gpio_setup(device);
+	if (ret < 0) {
+		dev_err(&device->dev, "%s: gpio_setup failed\n", __func__);
+		goto err_dev_unregister;
+	}
+
+	for (i = 0; i <= NRF24_PIPE5; i++) {
+		pipe = nrf24_create_pipe(device, i);
+		if (IS_ERR(pipe)) {
+			ret = PTR_ERR(pipe);
+			goto err_devs_destroy;
+		}
+		list_add(&pipe->list, &device->pipes);
+	}
+
+	ret = nrf24_hal_init(device);
+	if (ret < 0)
+		goto err_devs_destroy;
+
+	device->rx_task_struct = kthread_run(nrf24_rx_thread,
+					     device,
+					     "nrf%d_rx_thread",
+					     device->id);
+	if (IS_ERR(device->rx_task_struct)) {
+		dev_err(&device->dev, "start of tx thread failed\n");
+		goto err_devs_destroy;
+	}
+
+	device->tx_task_struct = kthread_run(nrf24_tx_thread,
+					     device,
+					     "nrf%d_tx_thread",
+					     device->id);
+	if (IS_ERR(device->tx_task_struct)) {
+		dev_err(&device->dev, "start of tx thread failed\n");
+		goto err_kthread_stop;
+	}
+
+	spi_set_drvdata(spi, device);
+
+	return 0;
+
+err_kthread_stop:
+	kthread_stop(device->rx_task_struct);
+err_devs_destroy:
+	nrf24_destroy_devices(device);
+	nrf24_gpio_free(device);
+err_dev_unregister:
+	device_unregister(&device->dev);
+	return ret;
+}
+
+static int nrf24_remove(struct spi_device *spi)
+{
+	struct nrf24_device *device = spi_get_drvdata(spi);
+
+	nrf24_gpio_free(device);
+
+	kthread_stop(device->tx_task_struct);
+	kthread_stop(device->rx_task_struct);
+
+	nrf24_destroy_devices(device);
+
+	device_unregister(&device->dev);
+
+	return 0;
+}
+
+static const struct of_device_id nrf24_dt_ids[] = {
+	{ .compatible = "nordic,nrf24" },
+	{},
+};
+
+MODULE_DEVICE_TABLE(of, nrf24_dt_ids);
+
+static  struct spi_driver nrf24_spi_driver = {
+	.driver = {
+		.name = "nrf24",
+		.owner = THIS_MODULE,
+		.of_match_table = of_match_ptr(nrf24_dt_ids),
+	},
+	.probe = nrf24_probe,
+	.remove = nrf24_remove,
+};
+
+static int __init nrf24_init(void)
+{
+	int ret;
+
+	ret = alloc_chrdev_region(&nrf24_dev, 0, N_NRF24_MINORS,
+				  nrf24_spi_driver.driver.name);
+	if (ret < 0) {
+		pr_err("Unable to alloc chrdev region\n");
+		goto err_ida_destroy;
+	}
+
+	nrf24_class = class_create(THIS_MODULE, nrf24_spi_driver.driver.name);
+	if (IS_ERR(nrf24_class)) {
+		pr_err("Unable to create class\n");
+		ret = PTR_ERR(nrf24_class);
+		goto err_unreg_chrdev;
+	}
+
+	ret = spi_register_driver(&nrf24_spi_driver);
+	if (ret < 0) {
+		pr_err("Unable to register spi driver\n");
+		goto err_class_destroy;
+	}
+
+	return 0;
+
+err_class_destroy:
+	class_destroy(nrf24_class);
+err_unreg_chrdev:
+	unregister_chrdev(MAJOR(nrf24_dev), nrf24_spi_driver.driver.name);
+err_ida_destroy:
+	ida_destroy(&nrf24_ida_dev);
+	ida_destroy(&nrf24_ida_pipe);
+
+	return ret;
+}
+module_init(nrf24_init);
+
+static void __exit nrf24_exit(void)
+{
+	spi_unregister_driver(&nrf24_spi_driver);
+	class_destroy(nrf24_class);
+	unregister_chrdev(MAJOR(nrf24_dev), nrf24_spi_driver.driver.name);
+	ida_destroy(&nrf24_ida_dev);
+	ida_destroy(&nrf24_ida_pipe);
+}
+module_exit(nrf24_exit);
+
+MODULE_AUTHOR("Marcin Ciupak <marcin.s.ciupak@xxxxxxxxx>");
+MODULE_DESCRIPTION("Driver for NRF24L01+");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("spi:nrf24");
+
diff --git a/drivers/staging/nrf24/nrf24_if.h b/drivers/staging/nrf24/nrf24_if.h
new file mode 100644
index 000000000000..2d9b0a8eaedc
--- /dev/null
+++ b/drivers/staging/nrf24/nrf24_if.h
@@ -0,0 +1,63 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/*
+ * Copyright (C) 2017 Marcin Ciupak <marcin.s.ciupak@xxxxxxxxx>
+ *
+ */
+
+#ifndef NRF24_IF_H
+#define NRF24_IF_H
+
+#define FIFO_SIZE			65536
+
+struct nrf24_pipe_cfg {
+	u64			address;
+	u8			ack;
+	ssize_t			plw;
+};
+
+struct nrf24_pipe {
+	dev_t			devt;
+	struct device		*dev;
+	struct cdev		cdev;
+	int			id;
+	struct nrf24_pipe_cfg	cfg;
+
+	STRUCT_KFIFO_REC_1(FIFO_SIZE) rx_fifo;
+	wait_queue_head_t	poll_wait_queue;
+	ssize_t			rx_size;
+
+	struct list_head list;
+};
+
+struct nrf24_device {
+	u32			id;
+	struct device		dev;
+	struct spi_device	*spi;
+	struct list_head	pipes;
+
+	struct gpio_desc	*ce;
+
+	/* for irqsave */
+	spinlock_t		lock;
+
+	struct work_struct	isr_work;
+
+	/* tx */
+	STRUCT_KFIFO_REC_2(FIFO_SIZE) tx_fifo;
+
+	/* tx fifo lock */
+	struct mutex		tx_fifo_mutex;
+	struct task_struct	*tx_task_struct;
+	wait_queue_head_t	tx_wait_queue;
+	wait_queue_head_t	tx_done_wait_queue;
+
+	struct task_struct	*rx_task_struct;
+	wait_queue_head_t	rx_wait_queue;
+
+	u8			tx_done;
+};
+
+#define to_nrf24_device(device)	container_of(device, struct nrf24_device, dev)
+
+#endif /* NRF24_IF_H */
diff --git a/drivers/staging/nrf24/nrf24_sysfs.c b/drivers/staging/nrf24/nrf24_sysfs.c
new file mode 100644
index 000000000000..8449bc0afced
--- /dev/null
+++ b/drivers/staging/nrf24/nrf24_sysfs.c
@@ -0,0 +1,707 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright (C) 2017 Marcin Ciupak <marcin.s.ciupak@xxxxxxxxx>
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/cdev.h>
+#include <linux/err.h>
+#include <linux/kfifo.h>
+#include <linux/list.h>
+
+#include "nrf24_if.h"
+#include "nrf24_hal.h"
+#include "nrf24_enums.h"
+
+static struct nrf24_pipe *nrf24_find_pipe_ptr(struct device *dev)
+{
+	struct nrf24_device *device = to_nrf24_device(dev->parent);
+	struct nrf24_pipe *pipe;
+
+	list_for_each_entry(pipe, &device->pipes, list)
+		if (pipe->dev == dev)
+			return pipe;
+
+	return ERR_PTR(-ENODEV);
+}
+
+static ssize_t ack_show(struct device *dev,
+			struct device_attribute *attr,
+			char *buf)
+{
+	struct nrf24_device *device = to_nrf24_device(dev->parent);
+	int ret;
+	struct nrf24_pipe *pipe;
+
+	pipe = nrf24_find_pipe_ptr(dev);
+	if (IS_ERR(pipe))
+		return PTR_ERR(pipe);
+
+	ret = nrf24_get_auto_ack(device->spi, pipe->id);
+	if (ret < 0)
+		return ret;
+
+	return scnprintf(buf, PAGE_SIZE, "%d\n", ret);
+}
+
+static ssize_t ack_store(struct device *dev,
+			 struct device_attribute *attr,
+			 const char *buf,
+			 size_t count)
+{
+	struct nrf24_device *device = to_nrf24_device(dev->parent);
+	int ret;
+	u8 new;
+	struct nrf24_pipe *pipe;
+
+	pipe = nrf24_find_pipe_ptr(dev);
+	if (IS_ERR(pipe))
+		return PTR_ERR(pipe);
+
+	ret = kstrtou8(buf, 10, &new);
+	if (ret < 0)
+		return ret;
+	if (new < 0 || new > 1)
+		return -EINVAL;
+
+	ret = nrf24_setup_auto_ack(device->spi, pipe->id, new);
+	if (ret < 0)
+		return ret;
+
+	return count;
+}
+
+static ssize_t plw_show(struct device *dev,
+			struct device_attribute *attr,
+			char *buf)
+{
+	struct nrf24_device *device = to_nrf24_device(dev->parent);
+	int ret;
+	struct nrf24_pipe *pipe;
+
+	pipe = nrf24_find_pipe_ptr(dev);
+	if (IS_ERR(pipe))
+		return PTR_ERR(pipe);
+
+	ret = nrf24_get_rx_pload_width(device->spi, pipe->id);
+	if (ret < 0)
+		return ret;
+
+	return scnprintf(buf, PAGE_SIZE, "%d\n", ret);
+}
+
+static ssize_t plw_store(struct device *dev,
+			 struct device_attribute *attr,
+			 const char *buf,
+			 size_t count)
+{
+	struct nrf24_device *device = to_nrf24_device(dev->parent);
+	int ret;
+	u8 new;
+	u8 old;
+	struct nrf24_pipe *pipe;
+
+	pipe = nrf24_find_pipe_ptr(dev);
+	if (IS_ERR(pipe))
+		return PTR_ERR(pipe);
+
+	ret = kstrtou8(buf, 10, &new);
+	if (ret < 0)
+		return ret;
+
+	if (new < 0 || new > PLOAD_MAX)
+		return -EINVAL;
+	old = nrf24_get_rx_pload_width(device->spi, pipe->id);
+	if (old < 0)
+		return old;
+
+	if (old != new) {
+		ret = nrf24_set_rx_pload_width(device->spi, pipe->id, new);
+		if (ret < 0)
+			return ret;
+		pipe->cfg.plw = new;
+	}
+
+	return count;
+}
+
+static ssize_t address_show(struct device *dev,
+			    struct device_attribute *attr,
+			    char *buf)
+{
+	struct nrf24_device *device = to_nrf24_device(dev->parent);
+	u8 addr[16];
+	int ret;
+	int count;
+	int i;
+	struct nrf24_pipe *pipe;
+
+	pipe = nrf24_find_pipe_ptr(dev);
+	if (IS_ERR(pipe))
+		return PTR_ERR(pipe);
+
+	ret = nrf24_get_address(device->spi, pipe->id, addr);
+	if (ret < 0)
+		return ret;
+
+	count = scnprintf(buf, PAGE_SIZE, "0x");
+	for (i = --ret; i >= 0; i--)
+		count += scnprintf(buf + count, PAGE_SIZE - count, "%02X", addr[i]);
+	count += scnprintf(buf + count, PAGE_SIZE - count, "\n");
+
+	return count;
+}
+
+static ssize_t address_store(struct device *dev,
+			     struct device_attribute *attr,
+			     const char *buf,
+			     size_t count)
+{
+	struct nrf24_device *device = to_nrf24_device(dev->parent);
+	int ret;
+	u64 address;
+	int len;
+	struct nrf24_pipe *pipe;
+
+	ret = kstrtoull(buf, 16, &address);
+	if (ret < 0)
+		return ret;
+
+	len = nrf24_get_address_width(device->spi);
+	if (len < 0)
+		return len;
+
+	if (address >= BIT_ULL(len * BITS_PER_BYTE))
+		return -EINVAL;
+
+	pipe = nrf24_find_pipe_ptr(dev);
+	if (IS_ERR(pipe))
+		return PTR_ERR(pipe);
+
+	ret = nrf24_set_address(device->spi, pipe->id, (u8 *)&address);
+	if (ret < 0)
+		return ret;
+
+	return count;
+}
+
+static DEVICE_ATTR_RW(ack);
+static DEVICE_ATTR_RW(plw);
+static DEVICE_ATTR_RW(address);
+
+struct attribute *nrf24_pipe_attrs[] = {
+	&dev_attr_ack.attr,
+	&dev_attr_plw.attr,
+	&dev_attr_address.attr,
+	NULL,
+};
+
+static ssize_t tx_address_show(struct device *dev,
+			       struct device_attribute *attr,
+			       char *buf)
+{
+	struct nrf24_device *device = to_nrf24_device(dev);
+	u8 addr[16];
+	int ret;
+	int count;
+	int i;
+
+	ret = nrf24_get_address(device->spi, NRF24_TX, addr);
+	if (ret < 0)
+		return ret;
+
+	count = scnprintf(buf, PAGE_SIZE, "0x");
+	for (i = --ret; i >= 0; i--)
+		count += scnprintf(buf + count, PAGE_SIZE - count, "%02X", addr[i]);
+	count += scnprintf(buf + count, PAGE_SIZE - count, "\n");
+
+	return count;
+}
+
+static ssize_t tx_address_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf,
+				size_t count)
+{
+	struct nrf24_device *device = to_nrf24_device(dev);
+	int ret;
+	u64 address;
+	int len;
+
+	ret = kstrtoull(buf, 16, &address);
+	if (ret < 0)
+		return ret;
+
+	len = nrf24_get_address_width(device->spi);
+	if (len < 0)
+		return len;
+
+	if (address >= BIT_ULL(len * BITS_PER_BYTE))
+		return -EINVAL;
+
+	ret = nrf24_set_address(device->spi, NRF24_TX, (u8 *)&address);
+	if (ret < 0)
+		return ret;
+
+	return count;
+}
+
+static ssize_t status_show(struct device *dev,
+			   struct device_attribute *attr,
+			   char *buf)
+{
+	int ret;
+	struct nrf24_device *device = to_nrf24_device(dev);
+
+	nrf24_print_status(device->spi);
+	ret = nrf24_get_status(device->spi);
+	if (ret < 0)
+		return ret;
+	return scnprintf(buf, PAGE_SIZE, "STATUS = 0x%02X\n", ret);
+}
+
+static ssize_t available_crc_show(struct device *dev,
+				  struct device_attribute *attr,
+				  char *buf)
+{
+	return scnprintf(buf, PAGE_SIZE, "0 8 16\n");
+}
+
+static ssize_t crc_show(struct device *dev,
+			struct device_attribute *attr,
+			char *buf)
+{
+	int ret;
+	struct nrf24_device *device = to_nrf24_device(dev);
+
+	ret = nrf24_get_crc_mode(device->spi);
+	if (ret < 0)
+		return ret;
+
+	switch (ret) {
+	case NRF24_CRC_OFF:
+		ret = scnprintf(buf, PAGE_SIZE, "0\n");
+		break;
+	case NRF24_CRC_8BIT:
+		ret = scnprintf(buf, PAGE_SIZE, "8\n");
+		break;
+	case NRF24_CRC_16BIT:
+		ret = scnprintf(buf, PAGE_SIZE, "16\n");
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return ret;
+}
+
+static ssize_t crc_store(struct device *dev,
+			 struct device_attribute *attr,
+			 const char *buf,
+			 size_t count)
+{
+	int ret;
+	u8 new;
+	struct nrf24_device *device;
+
+	device = to_nrf24_device(dev);
+
+	ret = kstrtou8(buf, 10, &new);
+	if (ret < 0)
+		return ret;
+
+	switch (new) {
+	case 0:
+		new = NRF24_CRC_OFF;
+		break;
+	case 8:
+		new = NRF24_CRC_8BIT;
+		break;
+	case 16:
+		new = NRF24_CRC_16BIT;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ret = nrf24_get_crc_mode(device->spi);
+	if (ret < 0)
+		return ret;
+
+	if (new != ret) {
+		ret = nrf24_set_crc_mode(device->spi, new);
+		if (ret < 0)
+			return ret;
+		dev_dbg(dev, "%s: new crc mode = %d\n", __func__, new);
+	}
+	return count;
+}
+
+static ssize_t available_address_width_show(struct device *dev,
+					    struct device_attribute *attr,
+					    char *buf)
+{
+	return scnprintf(buf, PAGE_SIZE, "3 4 5\n");
+}
+
+static ssize_t address_width_show(struct device *dev,
+				  struct device_attribute *attr,
+				  char *buf)
+{
+	int ret;
+	struct nrf24_device *device = to_nrf24_device(dev);
+
+	ret = nrf24_get_address_width(device->spi);
+	if (ret < 0)
+		return ret;
+
+	return scnprintf(buf, PAGE_SIZE, "%d\n", ret);
+}
+
+static ssize_t address_width_store(struct device *dev,
+				   struct device_attribute *attr,
+				   const char *buf,
+				   size_t count)
+{
+	int ret;
+	u8 new;
+	struct nrf24_device *device;
+
+	device = to_nrf24_device(dev);
+
+	ret = kstrtou8(buf, 10, &new);
+	if (ret < 0)
+		return ret;
+
+	if (new != NRF24_AW_3 &&
+	    new != NRF24_AW_4 &&
+	    new != NRF24_AW_5)
+		return -EINVAL;
+
+	ret = nrf24_get_address_width(device->spi);
+	if (ret < 0)
+		return ret;
+
+	if (new != ret) {
+		ret = nrf24_set_address_width(device->spi, new);
+		if (ret < 0)
+			return ret;
+		dev_dbg(dev, "%s: new address width = %d\n", __func__, new);
+	}
+	return count;
+}
+
+static ssize_t available_output_power_show(struct device *dev,
+					   struct device_attribute *attr,
+					   char *buf)
+{
+	return scnprintf(buf, PAGE_SIZE, "0 -6 -12 -18\n");
+}
+
+static ssize_t rf_power_show(struct device *dev,
+			     struct device_attribute *attr,
+			     char *buf)
+{
+	int ret;
+	struct nrf24_device *device = to_nrf24_device(dev);
+
+	ret = nrf24_get_rf_power(device->spi);
+	if (ret < 0)
+		return ret;
+
+	switch (ret) {
+	case NRF24_POWER_0DBM:
+		ret = scnprintf(buf, PAGE_SIZE, "0\n");
+		break;
+	case NRF24_POWER_6DBM:
+		ret = scnprintf(buf, PAGE_SIZE, "-6\n");
+		break;
+	case NRF24_POWER_12DBM:
+		ret = scnprintf(buf, PAGE_SIZE, "-12\n");
+		break;
+	case NRF24_POWER_18DBM:
+		ret = scnprintf(buf, PAGE_SIZE, "-18\n");
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return ret;
+}
+
+static ssize_t rf_power_store(struct device *dev,
+			      struct device_attribute *attr,
+			      const char *buf,
+			      size_t count)
+{
+	int ret;
+	u8 new;
+	s8 tmp;
+	struct nrf24_device *device;
+
+	device = to_nrf24_device(dev);
+
+	ret = kstrtos8(buf, 10, &tmp);
+	if (ret < 0)
+		return ret;
+
+	switch (abs(tmp)) {
+	case 0:
+		new = NRF24_POWER_0DBM;
+		break;
+	case 6:
+		new = NRF24_POWER_6DBM;
+		break;
+	case 12:
+		new = NRF24_POWER_12DBM;
+		break;
+	case 18:
+		new = NRF24_POWER_18DBM;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ret = nrf24_get_rf_power(device->spi);
+	if (ret < 0)
+		return ret;
+
+	if (new != ret) {
+		ret = nrf24_set_rf_power(device->spi, new);
+		if (ret < 0)
+			return ret;
+		dev_dbg(dev, "%s: new rf power level = %d\n", __func__, new);
+	}
+	return count;
+}
+
+static ssize_t available_data_rate_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	return scnprintf(buf, PAGE_SIZE, "256 1024 2048\n");
+}
+
+static ssize_t data_rate_show(struct device *dev,
+			      struct device_attribute *attr,
+			      char *buf)
+{
+	int ret;
+	struct nrf24_device *device = to_nrf24_device(dev);
+
+	ret = nrf24_get_datarate(device->spi);
+	if (ret < 0)
+		return ret;
+
+	switch (ret) {
+	case NRF24_DATARATE_256KBPS:
+		ret = scnprintf(buf, PAGE_SIZE, "256\n");
+		break;
+	case NRF24_DATARATE_1MBPS:
+		ret = scnprintf(buf, PAGE_SIZE, "1024\n");
+		break;
+	case NRF24_DATARATE_2MBPS:
+		ret = scnprintf(buf, PAGE_SIZE, "2048\n");
+		break;
+	}
+
+	return ret;
+}
+
+static ssize_t data_rate_store(struct device *dev,
+			       struct device_attribute *attr,
+			       const char *buf,
+			       size_t count)
+{
+	int ret;
+	u8 new;
+	u16 tmp;
+	struct nrf24_device *device;
+
+	device = to_nrf24_device(dev);
+
+	ret = kstrtou16(buf, 10, &tmp);
+	if (ret < 0)
+		return ret;
+
+	switch (tmp) {
+	case 256:
+		new = NRF24_DATARATE_256KBPS;
+		break;
+	case 1024:
+		new = NRF24_DATARATE_1MBPS;
+		break;
+	case 2048:
+		new = NRF24_DATARATE_2MBPS;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ret = nrf24_get_datarate(device->spi);
+	if (ret < 0)
+		return ret;
+
+	if (new != ret) {
+		ret = nrf24_set_datarate(device->spi, new);
+		if (ret < 0)
+			return ret;
+		dev_dbg(dev, "%s: new datarate = %d\n", __func__, new);
+	}
+	return count;
+}
+
+static ssize_t available_retr_delay_show(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf)
+{
+	int i;
+	int count = 0;
+
+	for (i = 1; i <= 16; i++)
+		count += scnprintf(buf + count, PAGE_SIZE - count, "%d ", i * 250);
+	count += scnprintf(buf + count, PAGE_SIZE - count, "\n");
+
+	return count;
+}
+
+static ssize_t retr_delay_show(struct device *dev,
+			       struct device_attribute *attr,
+			       char *buf)
+{
+	int ret;
+	struct nrf24_device *device = to_nrf24_device(dev);
+
+	ret = nrf24_get_auto_retr_delay(device->spi);
+	if (ret < 0)
+		return ret;
+
+	return sprintf(buf, "%d\n", ret);
+}
+
+static ssize_t retr_delay_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf,
+				size_t count)
+{
+	int ret;
+	u16 new;
+	struct nrf24_device *device;
+
+	device = to_nrf24_device(dev);
+
+	ret = kstrtou16(buf, 10, &new);
+	if (ret < 0)
+		return ret;
+
+	if (new < 250 || new > 4000 || new % 250)
+		return -EINVAL;
+
+	ret = nrf24_get_auto_retr_delay(device->spi);
+	if (ret < 0)
+		return ret;
+
+	if (new != ret) {
+		ret = nrf24_set_auto_retr_delay(device->spi, new);
+		if (ret < 0)
+			return ret;
+		dev_dbg(dev, "%s: new autr retr delay = %d\n", __func__, new);
+	}
+	return count;
+}
+
+static ssize_t available_retr_count_show(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf)
+{
+	int i;
+	int count = 0;
+
+	for (i = 0; i < 16; i++)
+		count += scnprintf(buf + count, PAGE_SIZE - count, "%d ", i);
+	count += scnprintf(buf + count, PAGE_SIZE - count, "\n");
+
+	return count;
+}
+
+static ssize_t retr_count_show(struct device *dev,
+			       struct device_attribute *attr,
+			       char *buf)
+{
+	int ret;
+	struct nrf24_device *device = to_nrf24_device(dev);
+
+	ret = nrf24_get_auto_retr_count(device->spi);
+	if (ret < 0)
+		return ret;
+
+	return scnprintf(buf, PAGE_SIZE, "%d\n", ret);
+}
+
+static ssize_t retr_count_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf,
+				size_t count)
+{
+	int ret;
+	u16 new;
+	struct nrf24_device *device;
+
+	device = to_nrf24_device(dev);
+
+	ret = kstrtou16(buf, 10, &new);
+	if (ret < 0)
+		return ret;
+
+	if (new < 0 || new > 15)
+		return -EINVAL;
+
+	ret = nrf24_get_auto_retr_count(device->spi);
+	if (ret < 0)
+		return ret;
+
+	if (new != ret) {
+		ret = nrf24_set_auto_retr_count(device->spi, new);
+		if (ret < 0)
+			return ret;
+		dev_dbg(dev, "%s: new autr retr count = %d\n", __func__, new);
+	}
+	return count;
+}
+
+static DEVICE_ATTR_RW(tx_address);
+static DEVICE_ATTR_RO(status);
+static DEVICE_ATTR_RO(available_crc);
+static DEVICE_ATTR_RW(crc);
+static DEVICE_ATTR_RO(available_address_width);
+static DEVICE_ATTR_RW(address_width);
+static DEVICE_ATTR_RO(available_output_power);
+static DEVICE_ATTR_RW(rf_power);
+static DEVICE_ATTR_RO(available_data_rate);
+static DEVICE_ATTR_RW(data_rate);
+static DEVICE_ATTR_RO(available_retr_delay);
+static DEVICE_ATTR_RW(retr_delay);
+static DEVICE_ATTR_RO(available_retr_count);
+static DEVICE_ATTR_RW(retr_count);
+
+struct attribute *nrf24_attrs[] = {
+	&dev_attr_tx_address.attr,
+	&dev_attr_status.attr,
+	&dev_attr_crc.attr,
+	&dev_attr_available_crc.attr,
+	&dev_attr_address_width.attr,
+	&dev_attr_available_address_width.attr,
+	&dev_attr_rf_power.attr,
+	&dev_attr_available_output_power.attr,
+	&dev_attr_data_rate.attr,
+	&dev_attr_available_data_rate.attr,
+	&dev_attr_retr_delay.attr,
+	&dev_attr_available_retr_delay.attr,
+	&dev_attr_retr_count.attr,
+	&dev_attr_available_retr_count.attr,
+	NULL,
+};
+
diff --git a/drivers/staging/nrf24/nrf24_sysfs.h b/drivers/staging/nrf24/nrf24_sysfs.h
new file mode 100644
index 000000000000..ae6575b13ffe
--- /dev/null
+++ b/drivers/staging/nrf24/nrf24_sysfs.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/*
+ * Copyright (C) 2017 Marcin Ciupak <marcin.s.ciupak@xxxxxxxxx>
+ *
+ */
+
+#ifndef NRF24_SYSFS_H
+#define NRF24_SYSFS_H
+
+extern struct attribute *nrf24_pipe_attrs[];
+extern struct attribute *nrf24_attrs[];
+
+#endif /* NRF24_SYSFS_H */
-- 
2.19.1

_______________________________________________
devel mailing list
devel@xxxxxxxxxxxxxxxxxxxxxx
http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel




[Index of Archives]     [Linux Driver Backports]     [DMA Engine]     [Linux GPIO]     [Linux SPI]     [Video for Linux]     [Linux USB Devel]     [Linux Coverity]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux