130 lines
3.1 KiB
C
130 lines
3.1 KiB
C
/*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* LR11xx Zephyr HAL — SPI and GPIO low-level operations.
|
|
*
|
|
* The LR11xx SPI protocol:
|
|
* WRITE: Assert NSS → send 2-byte opcode + param bytes → deassert NSS
|
|
* READ: Assert NSS → send 2-byte opcode → deassert NSS
|
|
* Wait for BUSY low
|
|
* Assert NSS → read STAT1 + STAT2 + N response bytes → deassert NSS
|
|
*
|
|
* All transactions must wait for BUSY=low before asserting NSS.
|
|
*/
|
|
|
|
#include "lr11xx.h"
|
|
|
|
#include <zephyr/drivers/gpio.h>
|
|
#include <zephyr/drivers/spi.h>
|
|
#include <zephyr/kernel.h>
|
|
#include <zephyr/logging/log.h>
|
|
|
|
LOG_MODULE_DECLARE(lr11xx, LOG_LEVEL_DBG);
|
|
|
|
#define BUSY_POLL_INTERVAL_US 100
|
|
#define BUSY_TIMEOUT_MS 1000
|
|
|
|
int lr11xx_hal_reset(const struct device *dev)
|
|
{
|
|
const struct lr11xx_config *cfg = dev->config;
|
|
int ret;
|
|
|
|
ret = gpio_pin_set_dt(&cfg->reset, 1);
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
k_sleep(K_USEC(200));
|
|
ret = gpio_pin_set_dt(&cfg->reset, 0);
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
k_sleep(K_MSEC(10));
|
|
|
|
return lr11xx_hal_wait_busy(dev, K_MSEC(BUSY_TIMEOUT_MS));
|
|
}
|
|
|
|
int lr11xx_hal_wait_busy(const struct device *dev, k_timeout_t timeout)
|
|
{
|
|
const struct lr11xx_config *cfg = dev->config;
|
|
uint32_t start = k_uptime_get_32();
|
|
uint32_t timeout_ms = k_ticks_to_ms_floor32(timeout.ticks);
|
|
|
|
while (gpio_pin_get_dt(&cfg->busy)) {
|
|
if (timeout_ms > 0 &&
|
|
(k_uptime_get_32() - start) > timeout_ms) {
|
|
return -ETIMEDOUT;
|
|
}
|
|
k_busy_wait(BUSY_POLL_INTERVAL_US);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int lr11xx_hal_write_cmd(const struct device *dev,
|
|
uint16_t opcode,
|
|
const uint8_t *params, size_t params_len)
|
|
{
|
|
const struct lr11xx_config *cfg = dev->config;
|
|
int ret;
|
|
|
|
ret = lr11xx_hal_wait_busy(dev, K_MSEC(BUSY_TIMEOUT_MS));
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
|
|
uint8_t opcode_buf[2] = {
|
|
(uint8_t)(opcode >> 8),
|
|
(uint8_t)(opcode & 0xFF),
|
|
};
|
|
|
|
const struct spi_buf tx_bufs[] = {
|
|
{ .buf = opcode_buf, .len = sizeof(opcode_buf) },
|
|
{ .buf = (void *)params, .len = params_len },
|
|
};
|
|
const struct spi_buf_set tx = {
|
|
.buffers = tx_bufs,
|
|
.count = (params_len > 0) ? 2 : 1,
|
|
};
|
|
|
|
return spi_write_dt(&cfg->spi, &tx);
|
|
}
|
|
|
|
int lr11xx_hal_read_resp(const struct device *dev,
|
|
uint8_t *buf, size_t len)
|
|
{
|
|
const struct lr11xx_config *cfg = dev->config;
|
|
int ret;
|
|
|
|
ret = lr11xx_hal_wait_busy(dev, K_MSEC(BUSY_TIMEOUT_MS));
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
|
|
/* First two bytes are STAT1 and STAT2 — discard for now */
|
|
uint8_t status[2];
|
|
uint8_t dummy_opcode[2] = {0x00, 0x00};
|
|
|
|
const struct spi_buf tx_bufs[] = {
|
|
{ .buf = dummy_opcode, .len = sizeof(dummy_opcode) },
|
|
};
|
|
struct spi_buf rx_bufs[] = {
|
|
{ .buf = status, .len = sizeof(status) },
|
|
{ .buf = buf, .len = len },
|
|
};
|
|
const struct spi_buf_set tx = { .buffers = tx_bufs, .count = 1 };
|
|
const struct spi_buf_set rx = { .buffers = rx_bufs, .count = (len > 0) ? 2 : 1 };
|
|
|
|
return spi_transceive_dt(&cfg->spi, &tx, &rx);
|
|
}
|
|
|
|
void lr11xx_hal_event_handler(const struct device *port,
|
|
struct gpio_callback *cb, uint32_t pins)
|
|
{
|
|
ARG_UNUSED(port);
|
|
ARG_UNUSED(pins);
|
|
|
|
struct lr11xx_data *data =
|
|
CONTAINER_OF(cb, struct lr11xx_data, event_cb);
|
|
|
|
k_sem_give(&data->event_sem);
|
|
}
|