nimble/transport/da1469x: Add CMAC driver

This adds CMAC driver which is responsible for initializing CMAC
(internal BLE controller) and loading Dialog's firmware.

This is (almost) the same code which was present in da1469x_cmac.c in
MCU package which will be removed from there. The reason is that driver
works with specific firmware which uses specific transport so it makes
sense to distribute them together. Also new driver is a bit smarter as
it does not need tricks to link Dialog's library with our code. Instead,
we just use object with firmware from that library to create our own
object with proper section names so this can play nicely with our linker
script (the firmware itself is not modified in any way).

Note that this requires recent newt version with support for build
scripts.
This commit is contained in:
Andrzej Kaczmarek
2019-11-22 19:22:28 +01:00
parent 584ba1b7dc
commit 0839723a40
8 changed files with 481 additions and 20 deletions
+9 -6
View File
@@ -1,10 +1,13 @@
In order to use integrated BLE Controller (CMAC) on DA1469x you will need
a binary firmware to be loaded to CMAC. Library containing such firmware can
be found in SDK package available at:
Integrated BLE Controller (CMAC) requires a binary firmware to be loaded. Such
firmware is distributed by Dialog Semiconductor in SDK package which has to be
obtained separately, see:
https://www.dialog-semiconductor.com/products/da1469x-product-family
Copy sdk/interfaces/ble/binaries/DA1469x-Release/libble_stack_da1469x.a file
to src/ subdirectory and rebuild you application.
Firmware is available as part of following library in SDK package:
sdk/interfaces/ble/binaries/DA1469x-Release/libble_stack_da1469x.a
Current version was tested with SDK version 10.0.4.66.
By default, CMAC driver will look for this file in its package root directory
(i.e. nimble/transport/da1469x/cmac_driver) but this can be changed using
CMAC_IMAGE_FILE_NAME syscfg variable.
Current version of CMAC driver was tested with SDK version 10.0.4.66.
@@ -0,0 +1,37 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#ifndef __CMAC_HOST_H_
#define __CMAC_HOST_H_
#ifdef __cplusplus
extern "C" {
#endif
typedef int (* cmac_mbox_read_cb)(const uint8_t *, uint16_t len);
void cmac_host_init(void);
void cmac_mbox_write(const uint8_t *buf, size_t len);
void cmac_mbox_set_read_cb(cmac_mbox_read_cb cb);
#ifdef __cplusplus
}
#endif
#endif /* __CMAC_HOST_H_ */
@@ -0,0 +1,27 @@
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
pkg.name: nimble/transport/da1469x/cmac_driver
pkg.description: Driver for Dialog's BLE controller
pkg.author:
pkg.homepage:
pkg.keywords:
pkg.pre_link_cmds.BLE_HOST:
scripts/build_libcmac.sh: 100
@@ -0,0 +1,51 @@
#!/bin/bash
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
if [ ! -f ${MYNEWT_VAL_CMAC_IMAGE_FILE_NAME} ]; then
>&2 echo ERROR: BLE stack library not found. Please check nimble/transport/da1469x/README for details.
exit 1
fi
OBJCOPY=${MYNEWT_OBJCOPY_PATH}
AR=${MYNEWT_AR_PATH}
LIBBLE_A=$(readlink -e ${MYNEWT_VAL_CMAC_IMAGE_FILE_NAME})
LIBCMAC_A=${MYNEWT_USER_SRC_DIR}/libcmac.a
BASENAME_ROM=cmac.rom
BASENAME_RAM=cmac.ram
cd ${MYNEWT_USER_WORK_DIR}
# Extract firmware binary from .a since we do not need to link all other
# objects with our image.
${AR} x ${LIBBLE_A} cmac_fw.c.obj
${OBJCOPY} -O binary --only-section=.cmi_fw_area cmac_fw.c.obj cmac.bin
# We need separate copies for RAM and ROM image since section names are derived
# from file names. For now files are the same, but it would be possible to
# link ROM image without data and shared sections which contains zeroes anyway.
cp cmac.bin ${BASENAME_ROM}.bin
cp cmac.bin ${BASENAME_RAM}.bin
# Convert both binaries to objects and create archive to link
${OBJCOPY} -I binary -O elf32-littlearm -B armv8-m.main \
--rename-section .data=.libcmac.rom ${BASENAME_ROM}.bin ${BASENAME_ROM}.o
${OBJCOPY} -I binary -O elf32-littlearm -B armv8-m.main \
--rename-section .data=.libcmac.ram ${BASENAME_RAM}.bin ${BASENAME_RAM}.o
${AR} -rcs ${LIBCMAC_A} ${BASENAME_ROM}.o ${BASENAME_RAM}.o
@@ -0,0 +1,326 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include "os/os.h"
#include "mcu/cmsis_nvic.h"
#include "mcu/da1469x_lpclk.h"
#include "mcu/da1469x_hal.h"
#include "mcu/da1469x_pdc.h"
#include "mcu/mcu.h"
#include "cmac_driver/cmac_host.h"
#define CMAC_SYM_CONFIG ((void *)(0x00818f20 + MEMCTRL->CMI_CODE_BASE_REG))
#define CMAC_SYM_CONFIG_DYN ((void *)(0x00821af8 + MEMCTRL->CMI_CODE_BASE_REG))
#define CMAC_SYM_MBOX_RX ((void *)(0x008216b0 + MEMCTRL->CMI_CODE_BASE_REG))
#define CMAC_SYM_MBOX_TX ((void *)(0x008218b0 + MEMCTRL->CMI_CODE_BASE_REG))
#define CMAC_MBOX_SIZE 504
#define CMAC_MBOX_F_RESET 0x0008
#define CMAC_MBOX_F_WRITEPENDING 0x0010
struct cmac_config {
uint8_t bdaddr[6]; /* Device address */
uint8_t rf_calibration_delay;
uint8_t lp_clock_freq; /* Sleep clock frequency (0 = 32768Hz, 1 = 32000Hz) */
uint16_t lp_clock_sca; /* Sleep clock accuracy [ppm] */
uint16_t rx_buf_len; /* RX buffer size */
uint16_t tx_buf_len; /* TX buffer size */
bool initial_length_req;
/* Channel assessment algorithm settings */
uint16_t chan_assess_itvl;
uint8_t chan_assess_itvl_mult;
int8_t chan_assess_min_rssi;
uint16_t chan_assess_pkt_num;
uint16_t chan_assess_bad_pkt_num;
/* Calibration settings */
uint8_t system_tcs_length;
uint8_t synth_tcs_length;
uint8_t rfcu_tcs_length;
uint8_t default_tx_power; /* Default TX power for connection/advertising */
bool filter_dup_ov_discard; /* Discard unknown devices when filter buffer is full */
bool use_hp_1m;
bool use_hp_2m;
};
struct cmac_config_dynamic {
bool enable_sleep; /* Enable sleep */
/* More options here, don't care now */
};
struct cmac_mbox {
volatile uint16_t magic;
volatile uint16_t flags;
volatile uint16_t wr_off;
volatile uint16_t rd_off;
uint8_t data[CMAC_MBOX_SIZE];
};
/* CMAC data */
extern char _binary_cmac_rom_bin_start[];
extern char _binary_cmac_rom_bin_end;
extern char _binary_cmac_ram_bin_start[];
extern char _binary_cmac_ram_bin_end;
struct cmac_image_info {
uint32_t _dummy1;
uint32_t size_rom;
uint32_t _dummy2;
uint32_t magic;
uint32_t _dummy3[5];
};
/* Mailboxes for SYS<->CMAC communication */
static struct cmac_mbox *cmac_mbox_rx;
static struct cmac_mbox *cmac_mbox_tx;
/* PDC entry for waking up CMAC */
static int8_t g_cmac_host_pdc_sys2cmac;
/* PDC entry for waking up M33 */
static int8_t g_cmac_host_pdc_cmac2sys;
/* Callback for data ready from CMAC */
static cmac_mbox_read_cb g_cmac_mbox_read_cb;
static inline void
cmac_host_signal2cmac(void)
{
da1469x_pdc_set(g_cmac_host_pdc_sys2cmac);
}
static void
cmac2sys_isr(void)
{
uint16_t wr_off;
uint16_t rd_off;
uint16_t chunk;
uint16_t len;
os_trace_isr_enter();
/* Clear CMAC2SYS interrupt */
*(volatile uint32_t *)0x40002000 = 2;
if (*(volatile uint32_t *)0x40002000 & 0x1c00) {
/* XXX CMAC is in error state, need to recover */
assert(0);
}
if (cmac_mbox_rx->flags & CMAC_MBOX_F_RESET) {
cmac_mbox_rx->flags &= ~CMAC_MBOX_F_RESET;
goto done;
}
if (!g_cmac_mbox_read_cb) {
cmac_mbox_rx->rd_off = cmac_mbox_rx->wr_off;
goto done;
}
do {
rd_off = cmac_mbox_rx->rd_off;
wr_off = cmac_mbox_rx->wr_off;
if (rd_off <= wr_off) {
chunk = wr_off - rd_off;
} else {
chunk = CMAC_MBOX_SIZE - rd_off;
}
while (chunk) {
len = g_cmac_mbox_read_cb(&cmac_mbox_rx->data[rd_off], chunk);
rd_off += len;
chunk -= len;
};
cmac_mbox_rx->rd_off = rd_off % CMAC_MBOX_SIZE;
} while (cmac_mbox_rx->rd_off != cmac_mbox_rx->wr_off);
done:
if (cmac_mbox_rx->flags & CMAC_MBOX_F_WRITEPENDING) {
cmac_host_signal2cmac();
}
os_trace_isr_exit();
}
static void
cmac_host_lpclk_cb(uint32_t freq)
{
struct cmac_config_dynamic *cmac_config_dyn;
cmac_config_dyn = CMAC_SYM_CONFIG_DYN;
cmac_config_dyn->enable_sleep = freq == 32768;
}
void
cmac_mbox_write(const uint8_t *buf, size_t len)
{
uint16_t wr_off;
uint16_t rd_off;
uint16_t chunk;
uint32_t primask;
__HAL_DISABLE_INTERRUPTS(primask);
while (len) {
rd_off = cmac_mbox_tx->rd_off;
wr_off = cmac_mbox_tx->wr_off;
if (rd_off > wr_off) {
chunk = min(len, rd_off - wr_off);
} else {
chunk = min(len, CMAC_MBOX_SIZE - wr_off);
}
if (chunk == 0) {
continue;
}
memcpy(&cmac_mbox_tx->data[wr_off], buf, chunk);
wr_off += chunk;
cmac_mbox_tx->wr_off = wr_off % CMAC_MBOX_SIZE;
cmac_host_signal2cmac();
len -= chunk;
buf += chunk;
}
__HAL_ENABLE_INTERRUPTS(primask);
}
void
cmac_mbox_set_read_cb(cmac_mbox_read_cb cb)
{
g_cmac_mbox_read_cb = cb;
}
void
cmac_host_init(void)
{
struct cmac_image_info ii;
uint32_t cmac_rom_size;
uint32_t cmac_ram_size;
struct cmac_config *cmac_config;
struct cmac_config_dynamic *cmac_config_dyn;
/* Add PDC entry to wake up CMAC from M33 */
g_cmac_host_pdc_sys2cmac = da1469x_pdc_add(MCU_PDC_TRIGGER_MAC_TIMER,
MCU_PDC_MASTER_CMAC,
MCU_PDC_EN_XTAL);
da1469x_pdc_set(g_cmac_host_pdc_sys2cmac);
da1469x_pdc_ack(g_cmac_host_pdc_sys2cmac);
/* Add PDC entry to wake up M33 from CMAC, if does not exist yet */
g_cmac_host_pdc_cmac2sys = da1469x_pdc_find(MCU_PDC_TRIGGER_COMBO,
MCU_PDC_MASTER_M33, 0);
if (g_cmac_host_pdc_cmac2sys < 0) {
g_cmac_host_pdc_cmac2sys = da1469x_pdc_add(MCU_PDC_TRIGGER_COMBO,
MCU_PDC_MASTER_M33,
MCU_PDC_EN_XTAL);
da1469x_pdc_set(g_cmac_host_pdc_cmac2sys);
da1469x_pdc_ack(g_cmac_host_pdc_cmac2sys);
}
/* Enable Radio LDO */
CRG_TOP->POWER_CTRL_REG |= CRG_TOP_POWER_CTRL_REG_LDO_RADIO_ENABLE_Msk;
/* Enable CMAC, but keep it in reset */
CRG_TOP->CLK_RADIO_REG = (1 << CRG_TOP_CLK_RADIO_REG_RFCU_ENABLE_Pos) |
(1 << CRG_TOP_CLK_RADIO_REG_CMAC_SYNCH_RESET_Pos) |
(0 << CRG_TOP_CLK_RADIO_REG_CMAC_CLK_SEL_Pos) |
(1 << CRG_TOP_CLK_RADIO_REG_CMAC_CLK_ENABLE_Pos) |
(0 << CRG_TOP_CLK_RADIO_REG_CMAC_DIV_Pos);
/* Calculate size of ROM and RAM area (for now they should be the same) */
cmac_rom_size = &_binary_cmac_rom_bin_end - &_binary_cmac_rom_bin_start[0];
cmac_ram_size = &_binary_cmac_ram_bin_end - &_binary_cmac_ram_bin_start[0];
assert(cmac_rom_size == cmac_ram_size);
/* Load image header and check if image can be loaded */
memcpy(&ii, &_binary_cmac_rom_bin_start, sizeof(ii));
assert(ii.magic == 0x43414d43); /* "CMAC" */
/* Copy CMAC image to RAM */
memset(&_binary_cmac_ram_bin_start, 0, cmac_ram_size);
memcpy(&_binary_cmac_ram_bin_start, &_binary_cmac_rom_bin_start[sizeof(ii)],
ii.size_rom);
/*
* Setup memory controller for CMAC
* Code and data are set to the same address initially since CMAC will
* update data address on init. Also shared address is updated on init.
*/
MEMCTRL->CMI_CODE_BASE_REG = (uint32_t)&_binary_cmac_ram_bin_start;
MEMCTRL->CMI_DATA_BASE_REG = MEMCTRL->CMI_CODE_BASE_REG;
MEMCTRL->CMI_SHARED_BASE_REG = 0;
MEMCTRL->CMI_END_REG = (uint32_t)&_binary_cmac_ram_bin_end;
/* Symbols below are in shared memory, can update them now */
cmac_config = CMAC_SYM_CONFIG;
cmac_config_dyn = CMAC_SYM_CONFIG_DYN;
cmac_mbox_rx = CMAC_SYM_MBOX_RX;
cmac_mbox_tx = CMAC_SYM_MBOX_TX;
/* Update CMAC configuration */
cmac_config->lp_clock_freq = 0;
cmac_config->lp_clock_sca = 50;
cmac_config->rx_buf_len = 251 + 11;
cmac_config->tx_buf_len = 251 + 11;
cmac_config->initial_length_req = 0;
cmac_config->system_tcs_length = 0;
cmac_config->synth_tcs_length = 0;
cmac_config->rfcu_tcs_length = 0;
cmac_config->default_tx_power = 4;
cmac_config_dyn->enable_sleep = false;
/* Release CMAC from reset */
CRG_TOP->CLK_RADIO_REG &= ~CRG_TOP_CLK_RADIO_REG_CMAC_SYNCH_RESET_Msk;
/* Wait for CMAC to update registers */
while (MEMCTRL->CMI_DATA_BASE_REG == MEMCTRL->CMI_CODE_BASE_REG);
while (MEMCTRL->CMI_SHARED_BASE_REG != (MEMCTRL->CMI_END_REG & 0xfffffc00));
/* Initialize mailboxes and sync with CMAC */
cmac_mbox_tx->flags = CMAC_MBOX_F_RESET;
cmac_mbox_tx->wr_off = 0;
cmac_mbox_tx->rd_off = 0;
cmac_mbox_tx->magic = 0xa55a;
while (cmac_mbox_rx->magic != 0xa55a);
NVIC_SetVector(CMAC2SYS_IRQn, (uint32_t)cmac2sys_isr);
NVIC_SetPriority(CMAC2SYS_IRQn, 0);
NVIC_EnableIRQ(CMAC2SYS_IRQn);
cmac_host_signal2cmac();
da1469x_lpclk_register_cmac_cb(cmac_host_lpclk_cb);
}
@@ -0,0 +1,23 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
syscfg.defs:
CMAC_IMAGE_FILE_NAME:
description: >
Path to library with CMAC firmware. See README for details.
value: "libble_stack_da1469x.a"
+1 -7
View File
@@ -27,18 +27,12 @@ pkg.keywords:
pkg.deps:
- "@apache-mynewt-nimble/nimble"
- "@apache-mynewt-core/hw/mcu/dialog/da1469x"
- "@apache-mynewt-nimble/nimble/transport/da1469x/cmac_driver"
- "@apache-mynewt-core/kernel/os"
pkg.apis:
- ble_transport
# By explicitly marking 'cmac_fw_prepare' symbol as undefined we force
# linker to pull object from firmware library which contains section with
# firmware blob. This does not pull any extra symbols into our binary.
pkg.lflags:
- -Wl,--undefined=cmac_fw_prepare
pkg.init:
da1469x_ble_hci_init: 100
da1469x_ble_hci_cmac_init: 201
@@ -23,7 +23,7 @@
#include "nimble/ble.h"
#include "nimble/ble_hci_trans.h"
#include "nimble/hci_common.h"
#include "mcu/da1469x_cmac.h"
#include "cmac_driver/cmac_host.h"
#define HCI_PKT_NONE 0x00
#define HCI_PKT_CMD 0x01
@@ -154,8 +154,8 @@ ble_hci_trans_hs_cmd_tx(uint8_t *cmd)
uint8_t ind = HCI_PKT_CMD;
int len = 3 + cmd[2];
da1469x_cmac_mbox_write(&ind, 1);
da1469x_cmac_mbox_write(cmd, len);
cmac_mbox_write(&ind, 1);
cmac_mbox_write(cmd, len);
ble_hci_trans_buf_free(cmd);
@@ -168,11 +168,11 @@ ble_hci_trans_hs_acl_tx(struct os_mbuf *om)
uint8_t ind = HCI_PKT_ACL;
struct os_mbuf *x;
da1469x_cmac_mbox_write(&ind, 1);
cmac_mbox_write(&ind, 1);
x = om;
while (x) {
da1469x_cmac_mbox_write(x->om_data, x->om_len);
cmac_mbox_write(x->om_data, x->om_len);
x = SLIST_NEXT(x, om_next);
}
@@ -363,6 +363,6 @@ da1469x_ble_hci_init(void)
void
da1469x_ble_hci_cmac_init(void)
{
da1469x_cmac_mbox_set_read_cb(da1469x_ble_hci_read_cb);
da1469x_cmac_init();
cmac_mbox_set_read_cb(da1469x_ble_hci_read_cb);
cmac_host_init();
}