Commit cd093723 authored by Martin Jeřábek's avatar Martin Jeřábek Committed by Martin Jeřábek

driver: first primitive working userspace driver

parent efd390e3
SRCS := ctu_can_fd_hw.c ctu_can_fd_linux_defs.c ctu_can_fd_userspace.cpp
OBJS := $(addsuffix .o,$(SRCS))
DEPS := $(addsuffix .d,$(SRCS))
P := /home/mjerabek/Xilinx/vivado-2017.3/SDK/2017.3/gnu/aarch32/lin/gcc-arm-linux-gnueabi/bin/arm-linux-gnueabihf-
#P := /home/martin/programs/Xilinx/SDK/2017.4/gnu/aarch32/lin/gcc-arm-linux-gnueabi/bin/arm-linux-gnueabihf-
#P := arm-linux-gnueabihf-
CC := $(P)gcc
CXX := $(P)g++
#CC := clang -target armv7a-pc-linux-gnueabi -march=armv7 -mthumb
XFLAGS := -Wall -Wextra -O2 -D__BIG_ENDIAN_BITFIELD
CFLAGS := $(XFLAGS) -Werror=implicit-function-declaration
CXXFLAGS := $(XFLAGS)
#LDFLAGS := -fuse-ld=gold
all: test
test: $(OBJS)
$(CXX) $(XFLAGS) -o $@ $(OBJS) $(LDFLAGS)
%.c.o: %.c
$(CC) $(CFLAGS) -MMD -MP -c $< -o $@
%.cpp.o: %.cpp
$(CXX) $(CXXFLAGS) -MMD -MP -c $< -o $@
.PHONY: all clean
clean:
-rm test $(OBJS) $(DEPS)
-include $(DEPS)
......@@ -72,6 +72,9 @@ static inline void ctu_can_fd_hwid_to_id(union ctu_can_fd_identifier_w hwid,
// TODO: use can_len2dlc
static bool ctu_can_fd_len_to_dlc(u8 len, u8 *dlc)
{
*dlc = can_len2dlc(len);
return true;
/*
if (unlikely(len > 64)) {
*dlc = 0;
return false;
......@@ -81,7 +84,8 @@ static bool ctu_can_fd_len_to_dlc(u8 len, u8 *dlc)
*dlc = 0;
return false;
}
}
}*/
/*
if (len <= 8){
*dlc = len;
......@@ -484,7 +488,7 @@ void ctu_can_fd_set_rx_tsop(struct ctucanfd_priv *priv, enum ctu_can_fd_rx_setti
ctu_can_fd_write32(priv, CTU_CAN_FD_RX_STATUS, reg.u32);
}
void ctu_can_fd_read_rx_frame(struct ctucanfd_priv *priv, unsigned char *data, u64 *ts)
void ctu_can_fd_read_rx_frame(struct ctucanfd_priv *priv, struct canfd_frame *data, u64 *ts)
{
struct canfd_frame *cf = (struct canfd_frame *)data; // TODO: may break alignment rules
union ctu_can_fd_frame_form_w ffw;
......@@ -499,7 +503,7 @@ void ctu_can_fd_read_rx_frame(struct ctucanfd_priv *priv, unsigned char *data, u
// BRS, ESI, RTR Flags
if (ffw.s.fr_type == FD_CAN){
if (ffw.s.brs == BR_SHIFT)
cf->flags |= CANFD_BRS;
cf->flags |= CANFD_BRS;
if (ffw.s.esi_resvd == ESI_ERR_PASIVE)
cf->flags |= CANFD_ESI;
}else if (ffw.s.rtr == RTR_FRAME)
......@@ -605,7 +609,7 @@ void ctu_can_fd_set_txt_priority(struct ctucanfd_priv *priv, const u8 *prio)
ctu_can_fd_write32(priv, CTU_CAN_FD_TX_PRIORITY, reg.u32);
}
bool ctu_can_fd_insert_frame(struct ctucanfd_priv *priv, const unsigned char *data, u64 ts,
bool ctu_can_fd_insert_frame(struct ctucanfd_priv *priv, const struct canfd_frame *data, u64 ts,
u8 buf)
{
enum ctu_can_fd_regs buf_base;
......@@ -631,23 +635,25 @@ bool ctu_can_fd_insert_frame(struct ctucanfd_priv *priv, const unsigned char *da
return false;
}
if (!ctu_can_fd_is_txt_buf_accessible(priv, buf))
if (!ctu_can_fd_is_txt_buf_accessible(priv, buf)) {
return false;
}
if (cf->can_id & CAN_RTR_FLAG)
ffw.s.rtr = RTR_FRAME;
if (cf->can_id & CAN_EFF_FLAG)
ffw.s.id_type = EXTENDED;
else
else
ffw.s.id_type = BASE;
ffw.s.tbf = TIME_BASED;
idw = ctu_can_fd_id_to_hwid(cf->can_id);
if (!ctu_can_fd_len_to_dlc(cf->len, &dlc))
if (!ctu_can_fd_len_to_dlc(cf->len, &dlc)) {
return false;
}
ffw.s.dlc = dlc;
// Larger data chunks and the ones where bit rate should be shifted
......
......@@ -207,14 +207,14 @@ void ctu_can_fd_enable(struct ctucanfd_priv *priv, bool enable);
/*
* Configures CTU CAN FD Core to limit the amount of retransmit attempts after
* occurence of error (Error frame, Arbitration lost). If retransmitt limit is
* disabled, the Core will attempt to retransmitt inifinitely. If rettransmitt
* occurence of error (Error frame, Arbitration lost). If retransmit limit is
* disabled, the Core will attempt to retransmit inifinitely. If retransmit
* limit is reached, the Core will finish and according TXT buffer will end up
* in TX Error state.
*
* Arguments:
* priv Private info
* enable Enable/disable the retransmitt limitation
* enable Enable/disable the retransmit limitation
* limit Number to which limit the retransmission (1-CTU_CAN_FD_RETR_MAX)
* Returns:
* True if set correctly. False if "limit" is too high.
......@@ -234,12 +234,12 @@ bool ctu_can_fd_set_ret_limit(struct ctucanfd_priv *priv, bool enable, u8 limit)
* CAN_CTRLMODE_3_SAMPLES - Tripple sampling mode
* CAN_CTRLMODE_FD - Flexible data-rate support. When not set, Core
* does not accept CAN FD Frames and interprets,
* them as form error. Capability to transmitt
* them as form error. Capability to transmit
* CAN FD Frames is not affected by this setting.
* CAN_CTRLMODE_PRESUME_ACK - When set, Core does not require dominant bit
* in ACK field to consider the transmission as
* valid.
* CAN_CTRLMODE_FD_NON_ISO - When set, the Core transmitts the frames
* CAN_CTRLMODE_FD_NON_ISO - When set, the Core transmits the frames
* according to NON-ISO FD standard.
*
* Arguments:
......@@ -354,7 +354,7 @@ void ctu_can_fd_int_mask(struct ctucanfd_priv *priv, union ctu_can_fd_int_stat m
/*
* Set the modes of CTU CAN FD IP Core. All flags from "ctu_can_fd_set_mode_reg"
* are configured, plus CAN_CTRLMODE_ONE_SHOT, CAN_CTRLMODE_BERR_REPORTING,
* which are configured via "rettransmitt limit" and enabling error interrupts.
* which are configured via "retransmit limit" and enabling error interrupts.
*
* Arguments:
* priv Private info
......@@ -658,7 +658,7 @@ void ctu_can_fd_set_rx_tsop(struct ctucanfd_priv *priv, enum ctu_can_fd_rx_setti
* data Pointer to buffer where the CAN Frame should be stored.
* ts Pointer to u64 where RX Timestamp should be stored.
*/
void ctu_can_fd_read_rx_frame(struct ctucanfd_priv *priv, unsigned char *data, u64 *ts);
void ctu_can_fd_read_rx_frame(struct ctucanfd_priv *priv, struct canfd_frame *data, u64 *ts);
/*
......@@ -765,7 +765,7 @@ void ctu_can_fd_set_txt_priority(struct ctucanfd_priv *priv, const u8 *prio);
* Returns:
* True if the frame was inserted succesfully, False otherwise.
*/
bool ctu_can_fd_insert_frame(struct ctucanfd_priv *priv, const unsigned char *data, u64 ts,
bool ctu_can_fd_insert_frame(struct ctucanfd_priv *priv, const struct canfd_frame *data, u64 ts,
u8 buf);
/*
......
#include "ctu_can_fd_linux_defs.h"
#include <errno.h>
#include <limits.h>
/* CAN DLC to real data length conversion helpers */
static const u8 dlc2len[] = {0, 1, 2, 3, 4, 5, 6, 7,
......@@ -30,3 +33,250 @@ u8 can_len2dlc(u8 len)
return len2dlc[len];
}
#define CAN_CALC_MAX_ERROR 50 /* in one-tenth of a percent */
#define CAN_CALC_SYNC_SEG 1
/*
* Bit-timing calculation derived from:
*
* Code based on LinCAN sources and H8S2638 project
* Copyright 2004-2006 Pavel Pisa - DCE FELK CVUT cz
* Copyright 2005 Stanislav Marek
* email: pisa@cmp.felk.cvut.cz
*
* Calculates proper bit-timing parameters for a specified bit-rate
* and sample-point, which can then be used to set the bit-timing
* registers of the CAN controller. You can find more information
* in the header file linux/can/netlink.h.
*/
static int can_update_sample_point(const struct can_bittiming_const *btc,
unsigned int sample_point_nominal, unsigned int tseg,
unsigned int *tseg1_ptr, unsigned int *tseg2_ptr,
unsigned int *sample_point_error_ptr)
{
unsigned int sample_point_error, best_sample_point_error = UINT_MAX;
unsigned int sample_point, best_sample_point = 0;
unsigned int tseg1, tseg2;
int i;
for (i = 0; i <= 1; i++) {
tseg2 = tseg + CAN_CALC_SYNC_SEG - (sample_point_nominal * (tseg + CAN_CALC_SYNC_SEG)) / 1000 - i;
tseg2 = clamp(tseg2, btc->tseg2_min, btc->tseg2_max);
tseg1 = tseg - tseg2;
if (tseg1 > btc->tseg1_max) {
tseg1 = btc->tseg1_max;
tseg2 = tseg - tseg1;
}
sample_point = 1000 * (tseg + CAN_CALC_SYNC_SEG - tseg2) / (tseg + CAN_CALC_SYNC_SEG);
sample_point_error = abs(sample_point_nominal - sample_point);
if ((sample_point <= sample_point_nominal) && (sample_point_error < best_sample_point_error)) {
best_sample_point = sample_point;
best_sample_point_error = sample_point_error;
*tseg1_ptr = tseg1;
*tseg2_ptr = tseg2;
}
}
if (sample_point_error_ptr)
*sample_point_error_ptr = best_sample_point_error;
return best_sample_point;
}
static int can_calc_bittiming(struct net_device *dev, struct can_bittiming *bt,
const struct can_bittiming_const *btc)
{
struct can_priv *priv = netdev_priv(dev);
unsigned int bitrate; /* current bitrate */
unsigned int bitrate_error; /* difference between current and nominal value */
unsigned int best_bitrate_error = UINT_MAX;
unsigned int sample_point_error; /* difference between current and nominal value */
unsigned int best_sample_point_error = UINT_MAX;
unsigned int sample_point_nominal; /* nominal sample point */
unsigned int best_tseg = 0; /* current best value for tseg */
unsigned int best_brp = 0; /* current best value for brp */
unsigned int brp, tsegall, tseg, tseg1 = 0, tseg2 = 0;
u64 v64;
/* Use CiA recommended sample points */
if (bt->sample_point) {
sample_point_nominal = bt->sample_point;
} else {
if (bt->bitrate > 800000)
sample_point_nominal = 750;
else if (bt->bitrate > 500000)
sample_point_nominal = 800;
else
sample_point_nominal = 875;
}
/* tseg even = round down, odd = round up */
for (tseg = (btc->tseg1_max + btc->tseg2_max) * 2 + 1;
tseg >= (btc->tseg1_min + btc->tseg2_min) * 2; tseg--) {
tsegall = CAN_CALC_SYNC_SEG + tseg / 2;
/* Compute all possible tseg choices (tseg=tseg1+tseg2) */
brp = priv->clock.freq / (tsegall * bt->bitrate) + tseg % 2;
/* choose brp step which is possible in system */
brp = (brp / btc->brp_inc) * btc->brp_inc;
if ((brp < btc->brp_min) || (brp > btc->brp_max))
continue;
bitrate = priv->clock.freq / (brp * tsegall);
bitrate_error = abs(bt->bitrate - bitrate);
/* tseg brp biterror */
if (bitrate_error > best_bitrate_error)
continue;
/* reset sample point error if we have a better bitrate */
if (bitrate_error < best_bitrate_error)
best_sample_point_error = UINT_MAX;
can_update_sample_point(btc, sample_point_nominal, tseg / 2, &tseg1, &tseg2, &sample_point_error);
if (sample_point_error > best_sample_point_error)
continue;
best_sample_point_error = sample_point_error;
best_bitrate_error = bitrate_error;
best_tseg = tseg / 2;
best_brp = brp;
if (bitrate_error == 0 && sample_point_error == 0)
break;
}
if (best_bitrate_error) {
/* Error in one-tenth of a percent */
v64 = (u64)best_bitrate_error * 1000;
do_div(v64, bt->bitrate);
bitrate_error = (u32)v64;
if (bitrate_error > CAN_CALC_MAX_ERROR) {
netdev_err(dev,
"bitrate error %d.%d%% too high\n",
bitrate_error / 10, bitrate_error % 10);
return -EDOM;
}
netdev_warn(dev, "bitrate error %d.%d%%\n",
bitrate_error / 10, bitrate_error % 10);
}
/* real sample point */
bt->sample_point = can_update_sample_point(btc, sample_point_nominal, best_tseg,
&tseg1, &tseg2, NULL);
v64 = (u64)best_brp * 1000 * 1000 * 1000;
do_div(v64, priv->clock.freq);
bt->tq = (u32)v64;
bt->prop_seg = tseg1 / 2;
bt->phase_seg1 = tseg1 - bt->prop_seg;
bt->phase_seg2 = tseg2;
/* check for sjw user settings */
if (!bt->sjw || !btc->sjw_max) {
bt->sjw = 1;
} else {
/* bt->sjw is at least 1 -> sanitize upper bound to sjw_max */
if (bt->sjw > btc->sjw_max)
bt->sjw = btc->sjw_max;
/* bt->sjw must not be higher than tseg2 */
if (tseg2 < bt->sjw)
bt->sjw = tseg2;
}
bt->brp = best_brp;
/* real bitrate */
bt->bitrate = priv->clock.freq / (bt->brp * (CAN_CALC_SYNC_SEG + tseg1 + tseg2));
return 0;
}
/*
* Checks the validity of the specified bit-timing parameters prop_seg,
* phase_seg1, phase_seg2 and sjw and tries to determine the bitrate
* prescaler value brp. You can find more information in the header
* file linux/can/netlink.h.
*/
static int can_fixup_bittiming(struct net_device *dev, struct can_bittiming *bt,
const struct can_bittiming_const *btc)
{
struct can_priv *priv = netdev_priv(dev);
int tseg1, alltseg;
u64 brp64;
tseg1 = bt->prop_seg + bt->phase_seg1;
if (!bt->sjw)
bt->sjw = 1;
if (bt->sjw > btc->sjw_max ||
tseg1 < btc->tseg1_min || tseg1 > btc->tseg1_max ||
bt->phase_seg2 < btc->tseg2_min || bt->phase_seg2 > btc->tseg2_max)
return -ERANGE;
brp64 = (u64)priv->clock.freq * (u64)bt->tq;
if (btc->brp_inc > 1)
do_div(brp64, btc->brp_inc);
brp64 += 500000000UL - 1;
do_div(brp64, 1000000000UL); /* the practicable BRP */
if (btc->brp_inc > 1)
brp64 *= btc->brp_inc;
bt->brp = (u32)brp64;
if (bt->brp < btc->brp_min || bt->brp > btc->brp_max)
return -EINVAL;
alltseg = bt->prop_seg + bt->phase_seg1 + bt->phase_seg2 + 1;
bt->bitrate = priv->clock.freq / (bt->brp * alltseg);
bt->sample_point = ((tseg1 + 1) * 1000) / alltseg;
return 0;
}
/* Checks the validity of predefined bitrate settings */
static int can_validate_bitrate(struct net_device *dev, struct can_bittiming *bt,
const u32 *bitrate_const,
const unsigned int bitrate_const_cnt)
{
struct can_priv *priv = netdev_priv(dev);
unsigned int i;
for (i = 0; i < bitrate_const_cnt; i++) {
if (bt->bitrate == bitrate_const[i])
break;
}
if (i >= priv->bitrate_const_cnt)
return -EINVAL;
return 0;
}
int can_get_bittiming(struct net_device *dev, struct can_bittiming *bt,
const struct can_bittiming_const *btc,
const u32 *bitrate_const,
const unsigned int bitrate_const_cnt)
{
int err;
/*
* Depending on the given can_bittiming parameter structure the CAN
* timing parameters are calculated based on the provided bitrate OR
* alternatively the CAN timing parameters (tq, prop_seg, etc.) are
* provided directly which are then checked and fixed up.
*/
if (!bt->tq && bt->bitrate && btc)
err = can_calc_bittiming(dev, bt, btc);
else if (bt->tq && !bt->bitrate && btc)
err = can_fixup_bittiming(dev, bt, btc);
else if (!bt->tq && bt->bitrate && bitrate_const)
err = can_validate_bitrate(dev, bt, bitrate_const,
bitrate_const_cnt);
else
err = -EINVAL;
return err;
}
......@@ -470,4 +470,80 @@ struct can_filter {
#endif /* _UAPI_CAN_H */
struct can_priv {
struct can_bittiming bittiming, data_bittiming;
const struct can_bittiming_const *bittiming_const,
*data_bittiming_const;
//const u16 *termination_const;
//unsigned int termination_const_cnt;
//u16 termination;
const u32 *bitrate_const;
unsigned int bitrate_const_cnt;
const u32 *data_bitrate_const;
unsigned int data_bitrate_const_cnt;
struct can_clock clock;
};
struct net_device {
struct can_priv can;
};
#define netdev_priv(nd) (&((nd)->can))
int can_get_bittiming(struct net_device *dev, struct can_bittiming *bt,
const struct can_bittiming_const *btc,
const u32 *bitrate_const,
const unsigned int bitrate_const_cnt);
#define min(a, b) (a < b ? a : b)
#define max(a, b) (a > b ? a : b)
#define clamp(val, lo, hi) min((typeof(val))max(val, lo), hi)
/**
* do_div - returns 2 values: calculate remainder and update new dividend
* @n: pointer to uint64_t dividend (will be updated)
* @base: uint32_t divisor
*
* Summary:
* ``uint32_t remainder = *n % base;``
* ``*n = *n / base;``
*
* Return: (uint32_t)remainder
*
* NOTE: macro parameter @n is evaluated multiple times,
* beware of side effects!
*/
# define do_div(n,base) ({ \
uint32_t __base = (base); \
uint32_t __rem; \
__rem = ((uint64_t)(n)) % __base; \
(n) = ((uint64_t)(n)) / __base; \
__rem; \
})
/**
* abs - return absolute value of an argument
* @x: the value. If it is unsigned type, it is converted to signed type first.
* char is treated as if it was signed (regardless of whether it really is)
* but the macro's return type is preserved as char.
*
* Return: an absolute value of x.
*/
#define abs(x) __abs_choose_expr(x, long long, \
__abs_choose_expr(x, long, \
__abs_choose_expr(x, int, \
__abs_choose_expr(x, short, \
__abs_choose_expr(x, char, \
__builtin_choose_expr( \
__builtin_types_compatible_p(typeof(x), char), \
(char)({ signed char __x = (x); __x<0?-__x:__x; }), \
((void)0)))))))
#define __abs_choose_expr(x, type, other) __builtin_choose_expr( \
__builtin_types_compatible_p(typeof(x), signed type) || \
__builtin_types_compatible_p(typeof(x), unsigned type), \
({ signed type __x = (x); __x < 0 ? -__x : __x; }), other)
#define netdev_warn(dev, format, ...) printf("%s" format, "netdev_warn: ", ##__VA_ARGS__);
#define netdev_err(dev, format, ...) printf("%s" format, "netdev_err: ", ##__VA_ARGS__);
#endif /* __CTU_CAN_FD_LINUX_DEFS__ */
#include "ctu_can_fd_linux_defs.h"
#include "ctu_can_fd_hw.h"
#include <sys/mman.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <inttypes.h>
#include <err.h>
#define CANFD_ADDR_BASE 0x43C30000
#define CANFD_ADDR_RANGE 0x10000
static const char *memdev = "/dev/mem";
static int mem_fd = -1;
static void mem_open()
{
mem_fd = open(memdev, O_RDWR|O_SYNC);
if (mem_fd < 0) {
err(1, "open memory device");
}
}
void *mem_map(unsigned long mem_start, unsigned long mem_length)
{
unsigned long pagesize, mem_window_size;
void *mm, *mem;
//pagesize = getpagesize();
pagesize = sysconf(_SC_PAGESIZE);
mem_window_size = ((mem_start & (pagesize-1)) + mem_length + pagesize-1) & ~(pagesize-1);
mm = mmap(NULL, mem_window_size, PROT_WRITE|PROT_READ,
MAP_SHARED, mem_fd, mem_start & ~(pagesize-1));
mem = (char*)mm + (mem_start & (pagesize-1));
if (mm == MAP_FAILED) {
err(1, "mmap");
return NULL;
}
fprintf(stderr, "mmap 0x%lx -> %p\n",mem_start,mem);
return mem;
}
int main(int argc, char *argv[])
{
mem_open();
volatile void * const base = mem_map(CANFD_ADDR_BASE, CANFD_ADDR_RANGE);
struct ctucanfd_priv _p, *priv = &_p;
priv->mem_base = base;
union ctu_can_fd_device_id_version reg;
reg.u32 = ctu_can_fd_read32(priv, CTU_CAN_FD_DEVICE_ID);
printf("DevID: 0x%08x, should be 0x%08x\n", reg.s.device_id, CTU_CAN_FD_ID);
if (!ctu_can_fd_check_access(priv))
errx(1, "error: ctu_can_fd_check_access");
u32 version = ctu_can_fd_get_version(priv);
printf("Core version: %u\n", version);
return 0;
}
extern "C" {
#include "ctu_can_fd_linux_defs.h"
#include "ctu_can_fd_hw.h"
}
#undef abs
#include <sys/mman.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <inttypes.h>
#include <err.h>
//#include "clara.hpp"
#include <iostream>
#define CANFD_ADDR_BASE 0x43C30000
#define CANFD_ADDR_RANGE 0x10000
static const char *memdev = "/dev/mem";
static int mem_fd = -1;
static void mem_open()
{
mem_fd = open(memdev, O_RDWR|O_SYNC);
if (mem_fd < 0) {
err(1, "open memory device");
}
}
void *mem_map(unsigned long mem_start, unsigned long mem_length)
{
unsigned long pagesize, mem_window_size;
void *mm, *mem;
//pagesize = getpagesize();
pagesize = sysconf(_SC_PAGESIZE);
mem_window_size = ((mem_start & (pagesize-1)) + mem_length + pagesize-1) & ~(pagesize-1);
mm = mmap(NULL, mem_window_size, PROT_WRITE|PROT_READ,
MAP_SHARED, mem_fd, mem_start & ~(pagesize-1));
mem = (char*)mm + (mem_start & (pagesize-1));
if (mm == MAP_FAILED) {
err(1, "mmap");
return NULL;
}
fprintf(stderr, "mmap 0x%lx -> %p\n",mem_start,mem);
return mem;
}
int main(int argc, char *argv[])
{
uint32_t addr_base;
unsigned ifc = 0;
bool do_transmit = false;
//bool do_showhelp = false;
int c;
char *e;
const char *progname = argv[0];
while ((c = getopt(argc, argv, "i:th")) != -1) {
switch (c) {
case 'i':
ifc = strtoul(optarg, &e, 0);