diff options
author | 2023-02-21 18:24:12 -0800 | |
---|---|---|
committer | 2023-02-21 18:24:12 -0800 | |
commit | 5b7c4cabbb65f5c469464da6c5f614cbd7f730f2 (patch) | |
tree | cc5c2d0a898769fd59549594fedb3ee6f84e59a0 /drivers/staging/pi433/rf69.c | |
download | linux-5b7c4cabbb65f5c469464da6c5f614cbd7f730f2.tar.gz linux-5b7c4cabbb65f5c469464da6c5f614cbd7f730f2.zip |
Merge tag 'net-next-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-nextgrafted
Pull networking updates from Jakub Kicinski:
"Core:
- Add dedicated kmem_cache for typical/small skb->head, avoid having
to access struct page at kfree time, and improve memory use.
- Introduce sysctl to set default RPS configuration for new netdevs.
- Define Netlink protocol specification format which can be used to
describe messages used by each family and auto-generate parsers.
Add tools for generating kernel data structures and uAPI headers.
- Expose all net/core sysctls inside netns.
- Remove 4s sleep in netpoll if carrier is instantly detected on
boot.
- Add configurable limit of MDB entries per port, and port-vlan.
- Continue populating drop reasons throughout the stack.
- Retire a handful of legacy Qdiscs and classifiers.
Protocols:
- Support IPv4 big TCP (TSO frames larger than 64kB).
- Add IP_LOCAL_PORT_RANGE socket option, to control local port range
on socket by socket basis.
- Track and report in procfs number of MPTCP sockets used.
- Support mixing IPv4 and IPv6 flows in the in-kernel MPTCP path
manager.
- IPv6: don't check net.ipv6.route.max_size and rely on garbage
collection to free memory (similarly to IPv4).
- Support Penultimate Segment Pop (PSP) flavor in SRv6 (RFC8986).
- ICMP: add per-rate limit counters.
- Add support for user scanning requests in ieee802154.
- Remove static WEP support.
- Support minimal Wi-Fi 7 Extremely High Throughput (EHT) rate
reporting.
- WiFi 7 EHT channel puncturing support (client & AP).
BPF:
- Add a rbtree data structure following the "next-gen data structure"
precedent set by recently added linked list, that is, by using
kfunc + kptr instead of adding a new BPF map type.
- Expose XDP hints via kfuncs with initial support for RX hash and
timestamp metadata.
- Add BPF_F_NO_TUNNEL_KEY extension to bpf_skb_set_tunnel_key to
better support decap on GRE tunnel devices not operating in collect
metadata.
- Improve x86 JIT's codegen for PROBE_MEM runtime error checks.
- Remove the need for trace_printk_lock for bpf_trace_printk and
bpf_trace_vprintk helpers.
- Extend libbpf's bpf_tracing.h support for tracing arguments of
kprobes/uprobes and syscall as a special case.
- Significantly reduce the search time for module symbols by
livepatch and BPF.
- Enable cpumasks to be used as kptrs, which is useful for tracing
programs tracking which tasks end up running on which CPUs in
different time intervals.
- Add support for BPF trampoline on s390x and riscv64.
- Add capability to export the XDP features supported by the NIC.
- Add __bpf_kfunc tag for marking kernel functions as kfuncs.
- Add cgroup.memory=nobpf kernel parameter option to disable BPF
memory accounting for container environments.
Netfilter:
- Remove the CLUSTERIP target. It has been marked as obsolete for
years, and we still have WARN splats wrt races of the out-of-band
/proc interface installed by this target.
- Add 'destroy' commands to nf_tables. They are identical to the
existing 'delete' commands, but do not return an error if the
referenced object (set, chain, rule...) did not exist.
Driver API:
- Improve cpumask_local_spread() locality to help NICs set the right
IRQ affinity on AMD platforms.
- Separate C22 and C45 MDIO bus transactions more clearly.
- Introduce new DCB table to control DSCP rewrite on egress.
- Support configuration of Physical Layer Collision Avoidance (PLCA)
Reconciliation Sublayer (RS) (802.3cg-2019). Modern version of
shared medium Ethernet.
- Support for MAC Merge layer (IEEE 802.3-2018 clause 99). Allowing
preemption of low priority frames by high priority frames.
- Add support for controlling MACSec offload using netlink SET.
- Rework devlink instance refcounts to allow registration and
de-registration under the instance lock. Split the code into
multiple files, drop some of the unnecessarily granular locks and
factor out common parts of netlink operation handling.
- Add TX frame aggregation parameters (for USB drivers).
- Add a new attr TCA_EXT_WARN_MSG to report TC (offload) warning
messages with notifications for debug.
- Allow offloading of UDP NEW connections via act_ct.
- Add support for per action HW stats in TC.
- Support hardware miss to TC action (continue processing in SW from
a specific point in the action chain).
- Warn if old Wireless Extension user space interface is used with
modern cfg80211/mac80211 drivers. Do not support Wireless
Extensions for Wi-Fi 7 devices at all. Everyone should switch to
using nl80211 interface instead.
- Improve the CAN bit timing configuration. Use extack to return
error messages directly to user space, update the SJW handling,
including the definition of a new default value that will benefit
CAN-FD controllers, by increasing their oscillator tolerance.
New hardware / drivers:
- Ethernet:
- nVidia BlueField-3 support (control traffic driver)
- Ethernet support for imx93 SoCs
- Motorcomm yt8531 gigabit Ethernet PHY
- onsemi NCN26000 10BASE-T1S PHY (with support for PLCA)
- Microchip LAN8841 PHY (incl. cable diagnostics and PTP)
- Amlogic gxl MDIO mux
- WiFi:
- RealTek RTL8188EU (rtl8xxxu)
- Qualcomm Wi-Fi 7 devices (ath12k)
- CAN:
- Renesas R-Car V4H
Drivers:
- Bluetooth:
- Set Per Platform Antenna Gain (PPAG) for Intel controllers.
- Ethernet NICs:
- Intel (1G, igc):
- support TSN / Qbv / packet scheduling features of i226 model
- Intel (100G, ice):
- use GNSS subsystem instead of TTY
- multi-buffer XDP support
- extend support for GPIO pins to E823 devices
- nVidia/Mellanox:
- update the shared buffer configuration on PFC commands
- implement PTP adjphase function for HW offset control
- TC support for Geneve and GRE with VF tunnel offload
- more efficient crypto key management method
- multi-port eswitch support
- Netronome/Corigine:
- add DCB IEEE support
- support IPsec offloading for NFP3800
- Freescale/NXP (enetc):
- support XDP_REDIRECT for XDP non-linear buffers
- improve reconfig, avoid link flap and waiting for idle
- support MAC Merge layer
- Other NICs:
- sfc/ef100: add basic devlink support for ef100
- ionic: rx_push mode operation (writing descriptors via MMIO)
- bnxt: use the auxiliary bus abstraction for RDMA
- r8169: disable ASPM and reset bus in case of tx timeout
- cpsw: support QSGMII mode for J721e CPSW9G
- cpts: support pulse-per-second output
- ngbe: add an mdio bus driver
- usbnet: optimize usbnet_bh() by avoiding unnecessary queuing
- r8152: handle devices with FW with NCM support
- amd-xgbe: support 10Mbps, 2.5GbE speeds and rx-adaptation
- virtio-net: support multi buffer XDP
- virtio/vsock: replace virtio_vsock_pkt with sk_buff
- tsnep: XDP support
- Ethernet high-speed switches:
- nVidia/Mellanox (mlxsw):
- add support for latency TLV (in FW control messages)
- Microchip (sparx5):
- separate explicit and implicit traffic forwarding rules, make
the implicit rules always active
- add support for egress DSCP rewrite
- IS0 VCAP support (Ingress Classification)
- IS2 VCAP filters (protos, L3 addrs, L4 ports, flags, ToS
etc.)
- ES2 VCAP support (Egress Access Control)
- support for Per-Stream Filtering and Policing (802.1Q,
8.6.5.1)
- Ethernet embedded switches:
- Marvell (mv88e6xxx):
- add MAB (port auth) offload support
- enable PTP receive for mv88e6390
- NXP (ocelot):
- support MAC Merge layer
- support for the the vsc7512 internal copper phys
- Microchip:
- lan9303: convert to PHYLINK
- lan966x: support TC flower filter statistics
- lan937x: PTP support for KSZ9563/KSZ8563 and LAN937x
- lan937x: support Credit Based Shaper configuration
- ksz9477: support Energy Efficient Ethernet
- other:
- qca8k: convert to regmap read/write API, use bulk operations
- rswitch: Improve TX timestamp accuracy
- Intel WiFi (iwlwifi):
- EHT (Wi-Fi 7) rate reporting
- STEP equalizer support: transfer some STEP (connection to radio
on platforms with integrated wifi) related parameters from the
BIOS to the firmware.
- Qualcomm 802.11ax WiFi (ath11k):
- IPQ5018 support
- Fine Timing Measurement (FTM) responder role support
- channel 177 support
- MediaTek WiFi (mt76):
- per-PHY LED support
- mt7996: EHT (Wi-Fi 7) support
- Wireless Ethernet Dispatch (WED) reset support
- switch to using page pool allocator
- RealTek WiFi (rtw89):
- support new version of Bluetooth co-existance
- Mobile:
- rmnet: support TX aggregation"
* tag 'net-next-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next: (1872 commits)
page_pool: add a comment explaining the fragment counter usage
net: ethtool: fix __ethtool_dev_mm_supported() implementation
ethtool: pse-pd: Fix double word in comments
xsk: add linux/vmalloc.h to xsk.c
sefltests: netdevsim: wait for devlink instance after netns removal
selftest: fib_tests: Always cleanup before exit
net/mlx5e: Align IPsec ASO result memory to be as required by hardware
net/mlx5e: TC, Set CT miss to the specific ct action instance
net/mlx5e: Rename CHAIN_TO_REG to MAPPED_OBJ_TO_REG
net/mlx5: Refactor tc miss handling to a single function
net/mlx5: Kconfig: Make tc offload depend on tc skb extension
net/sched: flower: Support hardware miss to tc action
net/sched: flower: Move filter handle initialization earlier
net/sched: cls_api: Support hardware miss to tc action
net/sched: Rename user cookie and act cookie
sfc: fix builds without CONFIG_RTC_LIB
sfc: clean up some inconsistent indentings
net/mlx4_en: Introduce flexible array to silence overflow warning
net: lan966x: Fix possible deadlock inside PTP
net/ulp: Remove redundant ->clone() test in inet_clone_ulp().
...
Diffstat (limited to 'drivers/staging/pi433/rf69.c')
-rw-r--r-- | drivers/staging/pi433/rf69.c | 832 |
1 files changed, 832 insertions, 0 deletions
diff --git a/drivers/staging/pi433/rf69.c b/drivers/staging/pi433/rf69.c new file mode 100644 index 000000000..8c7fab6a4 --- /dev/null +++ b/drivers/staging/pi433/rf69.c @@ -0,0 +1,832 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * abstraction of the spi interface of HopeRf rf69 radio module + * + * Copyright (C) 2016 Wolf-Entwicklungen + * Marcus Wolf <linux@wolf-entwicklungen.de> + */ + +#include <linux/types.h> +#include <linux/spi/spi.h> + +#include "rf69.h" +#include "rf69_registers.h" + +#define F_OSC 32000000 /* in Hz */ +#define FIFO_SIZE 66 /* in byte */ + +/*-------------------------------------------------------------------------*/ + +u8 rf69_read_reg(struct spi_device *spi, u8 addr) +{ + return spi_w8r8(spi, addr); +} + +static int rf69_write_reg(struct spi_device *spi, u8 addr, u8 value) +{ + char buffer[2]; + + buffer[0] = addr | WRITE_BIT; + buffer[1] = value; + + return spi_write(spi, &buffer, ARRAY_SIZE(buffer)); +} + +/*-------------------------------------------------------------------------*/ + +static int rf69_set_bit(struct spi_device *spi, u8 reg, u8 mask) +{ + u8 tmp; + + tmp = rf69_read_reg(spi, reg); + tmp = tmp | mask; + return rf69_write_reg(spi, reg, tmp); +} + +static int rf69_clear_bit(struct spi_device *spi, u8 reg, u8 mask) +{ + u8 tmp; + + tmp = rf69_read_reg(spi, reg); + tmp = tmp & ~mask; + return rf69_write_reg(spi, reg, tmp); +} + +static inline int rf69_read_mod_write(struct spi_device *spi, u8 reg, + u8 mask, u8 value) +{ + u8 tmp; + + tmp = rf69_read_reg(spi, reg); + tmp = (tmp & ~mask) | value; + return rf69_write_reg(spi, reg, tmp); +} + +/*-------------------------------------------------------------------------*/ + +int rf69_get_version(struct spi_device *spi) +{ + return rf69_read_reg(spi, REG_VERSION); +} + +int rf69_set_mode(struct spi_device *spi, enum mode mode) +{ + static const u8 mode_map[] = { + [transmit] = OPMODE_MODE_TRANSMIT, + [receive] = OPMODE_MODE_RECEIVE, + [synthesizer] = OPMODE_MODE_SYNTHESIZER, + [standby] = OPMODE_MODE_STANDBY, + [mode_sleep] = OPMODE_MODE_SLEEP, + }; + + if (unlikely(mode >= ARRAY_SIZE(mode_map))) { + dev_dbg(&spi->dev, "set: illegal mode %u\n", mode); + return -EINVAL; + } + + return rf69_read_mod_write(spi, REG_OPMODE, MASK_OPMODE_MODE, + mode_map[mode]); + + /* + * we are using packet mode, so this check is not really needed + * but waiting for mode ready is necessary when going from sleep + * because the FIFO may not be immediately available from previous mode + * while (_mode == RF69_MODE_SLEEP && (READ_REG(REG_IRQFLAGS1) & + RF_IRQFLAGS1_MODEREADY) == 0x00); // Wait for ModeReady + */ +} + +int rf69_set_data_mode(struct spi_device *spi, u8 data_mode) +{ + return rf69_read_mod_write(spi, REG_DATAMODUL, MASK_DATAMODUL_MODE, + data_mode); +} + +int rf69_set_modulation(struct spi_device *spi, enum modulation modulation) +{ + static const u8 modulation_map[] = { + [OOK] = DATAMODUL_MODULATION_TYPE_OOK, + [FSK] = DATAMODUL_MODULATION_TYPE_FSK, + }; + + if (unlikely(modulation >= ARRAY_SIZE(modulation_map))) { + dev_dbg(&spi->dev, "set: illegal modulation %u\n", modulation); + return -EINVAL; + } + + return rf69_read_mod_write(spi, REG_DATAMODUL, + MASK_DATAMODUL_MODULATION_TYPE, + modulation_map[modulation]); +} + +static enum modulation rf69_get_modulation(struct spi_device *spi) +{ + u8 modulation_reg; + + modulation_reg = rf69_read_reg(spi, REG_DATAMODUL); + + switch (modulation_reg & MASK_DATAMODUL_MODULATION_TYPE) { + case DATAMODUL_MODULATION_TYPE_OOK: + return OOK; + case DATAMODUL_MODULATION_TYPE_FSK: + return FSK; + default: + return UNDEF; + } +} + +int rf69_set_modulation_shaping(struct spi_device *spi, + enum mod_shaping mod_shaping) +{ + switch (rf69_get_modulation(spi)) { + case FSK: + switch (mod_shaping) { + case SHAPING_OFF: + return rf69_read_mod_write(spi, REG_DATAMODUL, + MASK_DATAMODUL_MODULATION_SHAPE, + DATAMODUL_MODULATION_SHAPE_NONE); + case SHAPING_1_0: + return rf69_read_mod_write(spi, REG_DATAMODUL, + MASK_DATAMODUL_MODULATION_SHAPE, + DATAMODUL_MODULATION_SHAPE_1_0); + case SHAPING_0_5: + return rf69_read_mod_write(spi, REG_DATAMODUL, + MASK_DATAMODUL_MODULATION_SHAPE, + DATAMODUL_MODULATION_SHAPE_0_5); + case SHAPING_0_3: + return rf69_read_mod_write(spi, REG_DATAMODUL, + MASK_DATAMODUL_MODULATION_SHAPE, + DATAMODUL_MODULATION_SHAPE_0_3); + default: + dev_dbg(&spi->dev, "set: illegal mod shaping for FSK %u\n", mod_shaping); + return -EINVAL; + } + case OOK: + switch (mod_shaping) { + case SHAPING_OFF: + return rf69_read_mod_write(spi, REG_DATAMODUL, + MASK_DATAMODUL_MODULATION_SHAPE, + DATAMODUL_MODULATION_SHAPE_NONE); + case SHAPING_BR: + return rf69_read_mod_write(spi, REG_DATAMODUL, + MASK_DATAMODUL_MODULATION_SHAPE, + DATAMODUL_MODULATION_SHAPE_BR); + case SHAPING_2BR: + return rf69_read_mod_write(spi, REG_DATAMODUL, + MASK_DATAMODUL_MODULATION_SHAPE, + DATAMODUL_MODULATION_SHAPE_2BR); + default: + dev_dbg(&spi->dev, "set: illegal mod shaping for OOK %u\n", mod_shaping); + return -EINVAL; + } + default: + dev_dbg(&spi->dev, "set: modulation undefined\n"); + return -EINVAL; + } +} + +int rf69_set_bit_rate(struct spi_device *spi, u16 bit_rate) +{ + int retval; + u32 bit_rate_reg; + u8 msb; + u8 lsb; + enum modulation mod; + + // check if modulation is configured + mod = rf69_get_modulation(spi); + if (mod == UNDEF) { + dev_dbg(&spi->dev, "setBitRate: modulation is undefined\n"); + return -EINVAL; + } + + // check input value + if (bit_rate < 1200 || (mod == OOK && bit_rate > 32768)) { + dev_dbg(&spi->dev, "setBitRate: illegal input param\n"); + return -EINVAL; + } + + // calculate reg settings + bit_rate_reg = (F_OSC / bit_rate); + + msb = (bit_rate_reg & 0xff00) >> 8; + lsb = (bit_rate_reg & 0xff); + + // transmit to RF 69 + retval = rf69_write_reg(spi, REG_BITRATE_MSB, msb); + if (retval) + return retval; + retval = rf69_write_reg(spi, REG_BITRATE_LSB, lsb); + if (retval) + return retval; + + return 0; +} + +int rf69_set_deviation(struct spi_device *spi, u32 deviation) +{ + int retval; + u64 f_reg; + u64 f_step; + u32 bit_rate_reg; + u32 bit_rate; + u8 msb; + u8 lsb; + u64 factor = 1000000; // to improve precision of calculation + + // calculate bit rate + bit_rate_reg = rf69_read_reg(spi, REG_BITRATE_MSB) << 8; + bit_rate_reg |= rf69_read_reg(spi, REG_BITRATE_LSB); + bit_rate = F_OSC / bit_rate_reg; + + /* + * frequency deviation must exceed 600 Hz but not exceed + * 500kHz when taking bitrate dependency into consideration + * to ensure proper modulation + */ + if (deviation < 600 || (deviation + (bit_rate / 2)) > 500000) { + dev_dbg(&spi->dev, + "set_deviation: illegal input param: %u\n", deviation); + return -EINVAL; + } + + // calculat f step + f_step = F_OSC * factor; + do_div(f_step, 524288); // 524288 = 2^19 + + // calculate register settings + f_reg = deviation * factor; + do_div(f_reg, f_step); + + msb = (f_reg & 0xff00) >> 8; + lsb = (f_reg & 0xff); + + // check msb + if (msb & ~FDEVMASB_MASK) { + dev_dbg(&spi->dev, "set_deviation: err in calc of msb\n"); + return -EINVAL; + } + + // write to chip + retval = rf69_write_reg(spi, REG_FDEV_MSB, msb); + if (retval) + return retval; + retval = rf69_write_reg(spi, REG_FDEV_LSB, lsb); + if (retval) + return retval; + + return 0; +} + +int rf69_set_frequency(struct spi_device *spi, u32 frequency) +{ + int retval; + u32 f_max; + u64 f_reg; + u64 f_step; + u8 msb; + u8 mid; + u8 lsb; + u64 factor = 1000000; // to improve precision of calculation + + // calculat f step + f_step = F_OSC * factor; + do_div(f_step, 524288); // 524288 = 2^19 + + // check input value + f_max = div_u64(f_step * 8388608, factor); + if (frequency > f_max) { + dev_dbg(&spi->dev, "setFrequency: illegal input param\n"); + return -EINVAL; + } + + // calculate reg settings + f_reg = frequency * factor; + do_div(f_reg, f_step); + + msb = (f_reg & 0xff0000) >> 16; + mid = (f_reg & 0xff00) >> 8; + lsb = (f_reg & 0xff); + + // write to chip + retval = rf69_write_reg(spi, REG_FRF_MSB, msb); + if (retval) + return retval; + retval = rf69_write_reg(spi, REG_FRF_MID, mid); + if (retval) + return retval; + retval = rf69_write_reg(spi, REG_FRF_LSB, lsb); + if (retval) + return retval; + + return 0; +} + +int rf69_enable_amplifier(struct spi_device *spi, u8 amplifier_mask) +{ + return rf69_set_bit(spi, REG_PALEVEL, amplifier_mask); +} + +int rf69_disable_amplifier(struct spi_device *spi, u8 amplifier_mask) +{ + return rf69_clear_bit(spi, REG_PALEVEL, amplifier_mask); +} + +int rf69_set_output_power_level(struct spi_device *spi, u8 power_level) +{ + u8 pa_level, ocp, test_pa1, test_pa2; + bool pa0, pa1, pa2, high_power; + u8 min_power_level; + + // check register pa_level + pa_level = rf69_read_reg(spi, REG_PALEVEL); + pa0 = pa_level & MASK_PALEVEL_PA0; + pa1 = pa_level & MASK_PALEVEL_PA1; + pa2 = pa_level & MASK_PALEVEL_PA2; + + // check high power mode + ocp = rf69_read_reg(spi, REG_OCP); + test_pa1 = rf69_read_reg(spi, REG_TESTPA1); + test_pa2 = rf69_read_reg(spi, REG_TESTPA2); + high_power = (ocp == 0x0f) && (test_pa1 == 0x5d) && (test_pa2 == 0x7c); + + if (pa0 && !pa1 && !pa2) { + power_level += 18; + min_power_level = 0; + } else if (!pa0 && pa1 && !pa2) { + power_level += 18; + min_power_level = 16; + } else if (!pa0 && pa1 && pa2) { + if (high_power) + power_level += 11; + else + power_level += 14; + min_power_level = 16; + } else { + goto failed; + } + + // check input value + if (power_level > 0x1f) + goto failed; + + if (power_level < min_power_level) + goto failed; + + // write value + return rf69_read_mod_write(spi, REG_PALEVEL, MASK_PALEVEL_OUTPUT_POWER, + power_level); +failed: + dev_dbg(&spi->dev, "set: illegal power level %u\n", power_level); + return -EINVAL; +} + +int rf69_set_pa_ramp(struct spi_device *spi, enum pa_ramp pa_ramp) +{ + static const u8 pa_ramp_map[] = { + [ramp3400] = PARAMP_3400, + [ramp2000] = PARAMP_2000, + [ramp1000] = PARAMP_1000, + [ramp500] = PARAMP_500, + [ramp250] = PARAMP_250, + [ramp125] = PARAMP_125, + [ramp100] = PARAMP_100, + [ramp62] = PARAMP_62, + [ramp50] = PARAMP_50, + [ramp40] = PARAMP_40, + [ramp31] = PARAMP_31, + [ramp25] = PARAMP_25, + [ramp20] = PARAMP_20, + [ramp15] = PARAMP_15, + [ramp10] = PARAMP_10, + }; + + if (unlikely(pa_ramp >= ARRAY_SIZE(pa_ramp_map))) { + dev_dbg(&spi->dev, "set: illegal pa_ramp %u\n", pa_ramp); + return -EINVAL; + } + + return rf69_write_reg(spi, REG_PARAMP, pa_ramp_map[pa_ramp]); +} + +int rf69_set_antenna_impedance(struct spi_device *spi, + enum antenna_impedance antenna_impedance) +{ + switch (antenna_impedance) { + case fifty_ohm: + return rf69_clear_bit(spi, REG_LNA, MASK_LNA_ZIN); + case two_hundred_ohm: + return rf69_set_bit(spi, REG_LNA, MASK_LNA_ZIN); + default: + dev_dbg(&spi->dev, "set: illegal antenna impedance %u\n", antenna_impedance); + return -EINVAL; + } +} + +int rf69_set_lna_gain(struct spi_device *spi, enum lna_gain lna_gain) +{ + static const u8 lna_gain_map[] = { + [automatic] = LNA_GAIN_AUTO, + [max] = LNA_GAIN_MAX, + [max_minus_6] = LNA_GAIN_MAX_MINUS_6, + [max_minus_12] = LNA_GAIN_MAX_MINUS_12, + [max_minus_24] = LNA_GAIN_MAX_MINUS_24, + [max_minus_36] = LNA_GAIN_MAX_MINUS_36, + [max_minus_48] = LNA_GAIN_MAX_MINUS_48, + }; + + if (unlikely(lna_gain >= ARRAY_SIZE(lna_gain_map))) { + dev_dbg(&spi->dev, "set: illegal lna gain %u\n", lna_gain); + return -EINVAL; + } + + return rf69_read_mod_write(spi, REG_LNA, MASK_LNA_GAIN, + lna_gain_map[lna_gain]); +} + +static int rf69_set_bandwidth_intern(struct spi_device *spi, u8 reg, + enum mantisse mantisse, u8 exponent) +{ + u8 bandwidth; + + // check value for mantisse and exponent + if (exponent > 7) { + dev_dbg(&spi->dev, "set: illegal bandwidth exponent %u\n", exponent); + return -EINVAL; + } + + if (mantisse != mantisse16 && + mantisse != mantisse20 && + mantisse != mantisse24) { + dev_dbg(&spi->dev, "set: illegal bandwidth mantisse %u\n", mantisse); + return -EINVAL; + } + + // read old value + bandwidth = rf69_read_reg(spi, reg); + + // "delete" mantisse and exponent = just keep the DCC setting + bandwidth = bandwidth & MASK_BW_DCC_FREQ; + + // add new mantisse + switch (mantisse) { + case mantisse16: + bandwidth = bandwidth | BW_MANT_16; + break; + case mantisse20: + bandwidth = bandwidth | BW_MANT_20; + break; + case mantisse24: + bandwidth = bandwidth | BW_MANT_24; + break; + } + + // add new exponent + bandwidth = bandwidth | exponent; + + // write back + return rf69_write_reg(spi, reg, bandwidth); +} + +int rf69_set_bandwidth(struct spi_device *spi, enum mantisse mantisse, + u8 exponent) +{ + return rf69_set_bandwidth_intern(spi, REG_RXBW, mantisse, exponent); +} + +int rf69_set_bandwidth_during_afc(struct spi_device *spi, + enum mantisse mantisse, + u8 exponent) +{ + return rf69_set_bandwidth_intern(spi, REG_AFCBW, mantisse, exponent); +} + +int rf69_set_ook_threshold_dec(struct spi_device *spi, + enum threshold_decrement threshold_decrement) +{ + static const u8 td_map[] = { + [dec_every8th] = OOKPEAK_THRESHDEC_EVERY_8TH, + [dec_every4th] = OOKPEAK_THRESHDEC_EVERY_4TH, + [dec_every2nd] = OOKPEAK_THRESHDEC_EVERY_2ND, + [dec_once] = OOKPEAK_THRESHDEC_ONCE, + [dec_twice] = OOKPEAK_THRESHDEC_TWICE, + [dec_4times] = OOKPEAK_THRESHDEC_4_TIMES, + [dec_8times] = OOKPEAK_THRESHDEC_8_TIMES, + [dec_16times] = OOKPEAK_THRESHDEC_16_TIMES, + }; + + if (unlikely(threshold_decrement >= ARRAY_SIZE(td_map))) { + dev_dbg(&spi->dev, "set: illegal OOK threshold decrement %u\n", + threshold_decrement); + return -EINVAL; + } + + return rf69_read_mod_write(spi, REG_OOKPEAK, MASK_OOKPEAK_THRESDEC, + td_map[threshold_decrement]); +} + +int rf69_set_dio_mapping(struct spi_device *spi, u8 dio_number, u8 value) +{ + u8 mask; + u8 shift; + u8 dio_addr; + u8 dio_value; + + switch (dio_number) { + case 0: + mask = MASK_DIO0; + shift = SHIFT_DIO0; + dio_addr = REG_DIOMAPPING1; + break; + case 1: + mask = MASK_DIO1; + shift = SHIFT_DIO1; + dio_addr = REG_DIOMAPPING1; + break; + case 2: + mask = MASK_DIO2; + shift = SHIFT_DIO2; + dio_addr = REG_DIOMAPPING1; + break; + case 3: + mask = MASK_DIO3; + shift = SHIFT_DIO3; + dio_addr = REG_DIOMAPPING1; + break; + case 4: + mask = MASK_DIO4; + shift = SHIFT_DIO4; + dio_addr = REG_DIOMAPPING2; + break; + case 5: + mask = MASK_DIO5; + shift = SHIFT_DIO5; + dio_addr = REG_DIOMAPPING2; + break; + default: + dev_dbg(&spi->dev, "set: illegal dio number %u\n", dio_number); + return -EINVAL; + } + + // read reg + dio_value = rf69_read_reg(spi, dio_addr); + // delete old value + dio_value = dio_value & ~mask; + // add new value + dio_value = dio_value | value << shift; + // write back + return rf69_write_reg(spi, dio_addr, dio_value); +} + +int rf69_set_rssi_threshold(struct spi_device *spi, u8 threshold) +{ + /* no value check needed - u8 exactly matches register size */ + + return rf69_write_reg(spi, REG_RSSITHRESH, threshold); +} + +int rf69_set_preamble_length(struct spi_device *spi, u16 preamble_length) +{ + int retval; + u8 msb, lsb; + + /* no value check needed - u16 exactly matches register size */ + + /* calculate reg settings */ + msb = (preamble_length & 0xff00) >> 8; + lsb = (preamble_length & 0xff); + + /* transmit to chip */ + retval = rf69_write_reg(spi, REG_PREAMBLE_MSB, msb); + if (retval) + return retval; + return rf69_write_reg(spi, REG_PREAMBLE_LSB, lsb); +} + +int rf69_enable_sync(struct spi_device *spi) +{ + return rf69_set_bit(spi, REG_SYNC_CONFIG, MASK_SYNC_CONFIG_SYNC_ON); +} + +int rf69_disable_sync(struct spi_device *spi) +{ + return rf69_clear_bit(spi, REG_SYNC_CONFIG, MASK_SYNC_CONFIG_SYNC_ON); +} + +int rf69_set_fifo_fill_condition(struct spi_device *spi, + enum fifo_fill_condition fifo_fill_condition) +{ + switch (fifo_fill_condition) { + case always: + return rf69_set_bit(spi, REG_SYNC_CONFIG, + MASK_SYNC_CONFIG_FIFO_FILL_CONDITION); + case after_sync_interrupt: + return rf69_clear_bit(spi, REG_SYNC_CONFIG, + MASK_SYNC_CONFIG_FIFO_FILL_CONDITION); + default: + dev_dbg(&spi->dev, "set: illegal fifo fill condition %u\n", fifo_fill_condition); + return -EINVAL; + } +} + +int rf69_set_sync_size(struct spi_device *spi, u8 sync_size) +{ + // check input value + if (sync_size > 0x07) { + dev_dbg(&spi->dev, "set: illegal sync size %u\n", sync_size); + return -EINVAL; + } + + // write value + return rf69_read_mod_write(spi, REG_SYNC_CONFIG, + MASK_SYNC_CONFIG_SYNC_SIZE, + (sync_size << 3)); +} + +int rf69_set_sync_values(struct spi_device *spi, u8 sync_values[8]) +{ + int retval = 0; + + retval += rf69_write_reg(spi, REG_SYNCVALUE1, sync_values[0]); + retval += rf69_write_reg(spi, REG_SYNCVALUE2, sync_values[1]); + retval += rf69_write_reg(spi, REG_SYNCVALUE3, sync_values[2]); + retval += rf69_write_reg(spi, REG_SYNCVALUE4, sync_values[3]); + retval += rf69_write_reg(spi, REG_SYNCVALUE5, sync_values[4]); + retval += rf69_write_reg(spi, REG_SYNCVALUE6, sync_values[5]); + retval += rf69_write_reg(spi, REG_SYNCVALUE7, sync_values[6]); + retval += rf69_write_reg(spi, REG_SYNCVALUE8, sync_values[7]); + + return retval; +} + +int rf69_set_packet_format(struct spi_device *spi, + enum packet_format packet_format) +{ + switch (packet_format) { + case packet_length_var: + return rf69_set_bit(spi, REG_PACKETCONFIG1, + MASK_PACKETCONFIG1_PACKET_FORMAT_VARIABLE); + case packet_length_fix: + return rf69_clear_bit(spi, REG_PACKETCONFIG1, + MASK_PACKETCONFIG1_PACKET_FORMAT_VARIABLE); + default: + dev_dbg(&spi->dev, "set: illegal packet format %u\n", packet_format); + return -EINVAL; + } +} + +int rf69_enable_crc(struct spi_device *spi) +{ + return rf69_set_bit(spi, REG_PACKETCONFIG1, MASK_PACKETCONFIG1_CRC_ON); +} + +int rf69_disable_crc(struct spi_device *spi) +{ + return rf69_clear_bit(spi, REG_PACKETCONFIG1, MASK_PACKETCONFIG1_CRC_ON); +} + +int rf69_set_address_filtering(struct spi_device *spi, + enum address_filtering address_filtering) +{ + static const u8 af_map[] = { + [filtering_off] = PACKETCONFIG1_ADDRESSFILTERING_OFF, + [node_address] = PACKETCONFIG1_ADDRESSFILTERING_NODE, + [node_or_broadcast_address] = + PACKETCONFIG1_ADDRESSFILTERING_NODEBROADCAST, + }; + + if (unlikely(address_filtering >= ARRAY_SIZE(af_map))) { + dev_dbg(&spi->dev, "set: illegal address filtering %u\n", address_filtering); + return -EINVAL; + } + + return rf69_read_mod_write(spi, REG_PACKETCONFIG1, + MASK_PACKETCONFIG1_ADDRESSFILTERING, + af_map[address_filtering]); +} + +int rf69_set_payload_length(struct spi_device *spi, u8 payload_length) +{ + return rf69_write_reg(spi, REG_PAYLOAD_LENGTH, payload_length); +} + +int rf69_set_node_address(struct spi_device *spi, u8 node_address) +{ + return rf69_write_reg(spi, REG_NODEADRS, node_address); +} + +int rf69_set_broadcast_address(struct spi_device *spi, u8 broadcast_address) +{ + return rf69_write_reg(spi, REG_BROADCASTADRS, broadcast_address); +} + +int rf69_set_tx_start_condition(struct spi_device *spi, + enum tx_start_condition tx_start_condition) +{ + switch (tx_start_condition) { + case fifo_level: + return rf69_clear_bit(spi, REG_FIFO_THRESH, + MASK_FIFO_THRESH_TXSTART); + case fifo_not_empty: + return rf69_set_bit(spi, REG_FIFO_THRESH, + MASK_FIFO_THRESH_TXSTART); + default: + dev_dbg(&spi->dev, "set: illegal tx start condition %u\n", tx_start_condition); + return -EINVAL; + } +} + +int rf69_set_fifo_threshold(struct spi_device *spi, u8 threshold) +{ + int retval; + + /* check input value */ + if (threshold & ~MASK_FIFO_THRESH_VALUE) { + dev_dbg(&spi->dev, "set: illegal fifo threshold %u\n", threshold); + return -EINVAL; + } + + /* write value */ + retval = rf69_read_mod_write(spi, REG_FIFO_THRESH, + MASK_FIFO_THRESH_VALUE, + threshold); + if (retval) + return retval; + + /* + * access the fifo to activate new threshold + * retval (mis-) used as buffer here + */ + return rf69_read_fifo(spi, (u8 *)&retval, 1); +} + +int rf69_set_dagc(struct spi_device *spi, enum dagc dagc) +{ + static const u8 dagc_map[] = { + [normal_mode] = DAGC_NORMAL, + [improve] = DAGC_IMPROVED_LOWBETA0, + [improve_for_low_modulation_index] = DAGC_IMPROVED_LOWBETA1, + }; + + if (unlikely(dagc >= ARRAY_SIZE(dagc_map))) { + dev_dbg(&spi->dev, "set: illegal dagc %u\n", dagc); + return -EINVAL; + } + + return rf69_write_reg(spi, REG_TESTDAGC, dagc_map[dagc]); +} + +/*-------------------------------------------------------------------------*/ + +int rf69_read_fifo(struct spi_device *spi, u8 *buffer, unsigned int size) +{ + int i; + struct spi_transfer transfer; + u8 local_buffer[FIFO_SIZE + 1] = {}; + int retval; + + if (size > FIFO_SIZE) { + dev_dbg(&spi->dev, + "read fifo: passed in buffer bigger then internal buffer\n"); + return -EMSGSIZE; + } + + /* prepare a bidirectional transfer */ + local_buffer[0] = REG_FIFO; + memset(&transfer, 0, sizeof(transfer)); + transfer.tx_buf = local_buffer; + transfer.rx_buf = local_buffer; + transfer.len = size + 1; + + retval = spi_sync_transfer(spi, &transfer, 1); + + /* print content read from fifo for debugging purposes */ + for (i = 0; i < size; i++) + dev_dbg(&spi->dev, "%d - 0x%x\n", i, local_buffer[i + 1]); + + memcpy(buffer, &local_buffer[1], size); + + return retval; +} + +int rf69_write_fifo(struct spi_device *spi, u8 *buffer, unsigned int size) +{ + int i; + u8 local_buffer[FIFO_SIZE + 1]; + + if (size > FIFO_SIZE) { + dev_dbg(&spi->dev, + "write fifo: passed in buffer bigger then internal buffer\n"); + return -EMSGSIZE; + } + + local_buffer[0] = REG_FIFO | WRITE_BIT; + memcpy(&local_buffer[1], buffer, size); + + /* print content written from fifo for debugging purposes */ + for (i = 0; i < size; i++) + dev_dbg(&spi->dev, "%d - 0x%x\n", i, buffer[i]); + + return spi_write(spi, local_buffer, size + 1); +} + |