mirror of
git://nv-tegra.nvidia.com/linux-nv-oot.git
synced 2025-12-23 01:31:30 +03:00
Using this patch we are adding support for mttcan driver in oot kernel. JIRA ESLC-6885 Signed-off-by: Manish Bhardwaj <mbhardwaj@nvidia.com> Change-Id: I83a6d43aa99278a546778cf1700e2bd106ec42a9 Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/2785360 Reviewed-by: Bitan Biswas <bbiswas@nvidia.com> GVS: Gerrit_Virtual_Submit <buildbot_gerritrpt@nvidia.com>
742 lines
20 KiB
C
742 lines
20 KiB
C
/* SPDX-License-Identifier: GPL-2.0-only */
|
|
/*
|
|
* Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
*/
|
|
|
|
#include "../include/m_ttcan.h"
|
|
|
|
#define spec_bar() asm volatile(ALTERNATIVE("dsb nsh\nisb\n", \
|
|
SB_BARRIER_INSN"nop\n", \
|
|
ARM64_HAS_SB))
|
|
|
|
static int mttcan_check_fec_validity(struct mttcan_priv *priv,
|
|
unsigned int fec);
|
|
|
|
static ssize_t show_std_fltr(struct device *dev,
|
|
struct device_attribute *devattr, char *buf)
|
|
{
|
|
struct mttcan_priv *priv = netdev_priv(to_net_dev(dev));
|
|
int cur_filter_size = priv->ttcan->fltr_config.std_fltr_size;
|
|
ssize_t ret, total = 0;
|
|
int i = 0;
|
|
|
|
ret = sprintf(buf, "%s\n", "Standard Filters");
|
|
if (ret < 0) {
|
|
pr_err("sprintf() failed at line %d\n", __LINE__);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
total += ret;
|
|
while (cur_filter_size--) {
|
|
ret = sprintf(buf+total, "%d. 0x%x\n", i,
|
|
ttcan_get_std_id_filter(priv->ttcan, i));
|
|
if (ret < 0) {
|
|
pr_err("sprintf() failed at line %d\n", __LINE__);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
total += ret;
|
|
i++;
|
|
}
|
|
return total;
|
|
}
|
|
|
|
static ssize_t show_xtd_fltr(struct device *dev,
|
|
struct device_attribute *devattr, char *buf)
|
|
{
|
|
struct mttcan_priv *priv = netdev_priv(to_net_dev(dev));
|
|
int cur_filter_size = priv->ttcan->fltr_config.xtd_fltr_size;
|
|
ssize_t ret, total = 0;
|
|
int i = 0;
|
|
|
|
ret = sprintf(buf, "%s\n", "Extended Filters");
|
|
if (ret < 0) {
|
|
pr_err("sprintf() failed at line %d\n", __LINE__);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
total += ret;
|
|
while (cur_filter_size--) {
|
|
ret = sprintf(buf+total, "%d. 0x%llx\n", i,
|
|
ttcan_get_xtd_id_filter(priv->ttcan, i));
|
|
if (ret < 0) {
|
|
pr_err("sprintf() failed at line %d\n", __LINE__);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
total += ret;
|
|
i++;
|
|
}
|
|
return total;
|
|
}
|
|
|
|
static ssize_t show_gfc_fltr(struct device *dev,
|
|
struct device_attribute *devattr, char *buf)
|
|
{
|
|
struct mttcan_priv *priv = netdev_priv(to_net_dev(dev));
|
|
|
|
return sprintf(buf, "%s\n0x%x\n", "Global filter",
|
|
ttcan_get_gfc(priv->ttcan));
|
|
}
|
|
|
|
static ssize_t store_gfc_fltr(struct device *dev,
|
|
struct device_attribute *devattr,
|
|
const char *buf, size_t count)
|
|
{
|
|
struct net_device *ndev = to_net_dev(dev);
|
|
struct mttcan_priv *priv = netdev_priv(ndev);
|
|
struct ttcan_controller *ttcan = priv->ttcan;
|
|
unsigned int anfs, anfe;
|
|
unsigned int rrfs, rrfe;
|
|
u32 gfc;
|
|
int ret;
|
|
|
|
if (ndev->flags & IFF_UP) {
|
|
dev_err(dev, "GFC cannot be configured as device is running\n");
|
|
return -EBUSY;
|
|
}
|
|
|
|
ret = sscanf(buf, "anfs=%u anfe=%u rrfs=%u rrfe=%u", &anfs,
|
|
&anfe, &rrfs, &rrfe);
|
|
if ((ret < 4) || ((anfs | anfe) > 3) || ((rrfs | rrfe) > 1)) {
|
|
dev_err(dev, "Invalid Global filter\n");
|
|
pr_err("usage:anfs=0..3 anfe=0..3 rrfs=0/1 rrfe=0/1\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (((anfs == GFC_ANFS_RXFIFO_0) || (anfe == GFC_ANFE_RXFIFO_0))
|
|
&& (ttcan->mram_cfg[MRAM_RXF0].num == 0U)) {
|
|
dev_err(priv->device, "RX FIFO 0 is not used currently.");
|
|
dev_err(priv->device, " Change it via DT\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (((anfs == GFC_ANFS_RXFIFO_1) || (anfe == GFC_ANFE_RXFIFO_1))
|
|
&& (ttcan->mram_cfg[MRAM_RXF1].num == 0U)) {
|
|
dev_err(priv->device, "RX FIFO 1 is not used currently.");
|
|
dev_err(priv->device, " Change it via DT\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
gfc = 0;
|
|
gfc = (anfs << MTT_GFC_ANFS_SHIFT) & MTT_GFC_ANFS_MASK;
|
|
gfc |= (anfe << MTT_GFC_ANFE_SHIFT) & MTT_GFC_ANFE_MASK;
|
|
gfc |= (rrfs << MTT_GFC_RRFS_SHIFT) & MTT_GFC_RRFS_MASK;
|
|
gfc |= (rrfe << MTT_GFC_RRFE_SHIFT) & MTT_GFC_RRFE_MASK;
|
|
|
|
priv->gfc_reg = gfc;
|
|
ttcan_set_gfc(priv->ttcan, gfc);
|
|
|
|
return count;
|
|
}
|
|
|
|
static ssize_t show_xidam(struct device *dev,
|
|
struct device_attribute *devattr, char *buf)
|
|
{
|
|
struct mttcan_priv *priv = netdev_priv(to_net_dev(dev));
|
|
|
|
return sprintf(buf, "%s\n0x%x\n", "XIDAM",
|
|
ttcan_get_xidam(priv->ttcan));
|
|
}
|
|
|
|
static ssize_t store_xidam(struct device *dev,
|
|
struct device_attribute *devattr,
|
|
const char *buf, size_t count)
|
|
{
|
|
struct net_device *ndev = to_net_dev(dev);
|
|
struct mttcan_priv *priv = netdev_priv(ndev);
|
|
unsigned int xidam;
|
|
|
|
if (ndev->flags & IFF_UP) {
|
|
dev_err(dev, "XIDAM is protected, device is running\n");
|
|
return -EBUSY;
|
|
}
|
|
/* usage: xidam=MASK */
|
|
if (sscanf(buf, "xidam=%u", &xidam) != 1) {
|
|
dev_err(dev, "Invalid XIDAM MASK\n");
|
|
pr_err("usage: xidam=MASK\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
priv->xidam_reg = xidam;
|
|
ttcan_set_xidam(priv->ttcan, xidam);
|
|
|
|
return count;
|
|
}
|
|
|
|
static int mttcan_check_fec_validity(struct mttcan_priv *priv,
|
|
unsigned int fec)
|
|
{
|
|
struct ttcan_controller *ttcan = priv->ttcan;
|
|
|
|
if (fec > FEC_RXBUF) {
|
|
dev_err(priv->device, "sfec/efec should be in range 0-7\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (((fec == FEC_RXFIFO_0) || (fec == FEC_RXFIFO_0_PRIO)) &&
|
|
(ttcan->mram_cfg[MRAM_RXF0].num == 0U)) {
|
|
dev_err(priv->device, "RX FIFO 0 is not used currently.");
|
|
dev_err(priv->device, " Change it via DT\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (((fec == FEC_RXFIFO_1) || (fec == FEC_RXFIFO_1_PRIO)) &&
|
|
(ttcan->mram_cfg[MRAM_RXF1].num == 0U)) {
|
|
dev_err(priv->device, "RX FIFO 1 is not used currently.");
|
|
dev_err(priv->device, " Change it via DT\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if ((fec == FEC_RXBUF) && (ttcan->mram_cfg[MRAM_RXB].num == 0U)) {
|
|
dev_err(priv->device, "RX Buffer is not used currently.");
|
|
dev_err(priv->device, " Change it via DT\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static ssize_t store_std_fltr(struct device *dev,
|
|
struct device_attribute *devattr,
|
|
const char *buf, size_t count)
|
|
{
|
|
struct mttcan_priv *priv = netdev_priv(to_net_dev(dev));
|
|
struct net_device *ndev = to_net_dev(dev);
|
|
unsigned int sft, sfec;
|
|
unsigned int sfid1, sfid2;
|
|
int idx = -1, cur_filter_size;
|
|
int items;
|
|
int ret;
|
|
|
|
if (ndev->flags & IFF_UP) {
|
|
dev_err(dev, "device is running\n");
|
|
return -EBUSY;
|
|
}
|
|
/* usage: sft="0/1/2/3" sfec=1...7 sfid1="ID1" sfid2="ID2" idx=%u
|
|
*/
|
|
ret = sscanf(buf, "sft=%u sfec=%u sfid1=%X sfid2=%X idx=%u", &sft,
|
|
&sfec, &sfid1, &sfid2, &idx);
|
|
if (ret < 4) {
|
|
/* Not passing index is allowed */
|
|
dev_err(dev, "Invalid std filter\n");
|
|
pr_err("usage:sft=0..3 sfec=0..7 sfid1=ID1h sfid2=ID2h idx=i\n");
|
|
return -EINVAL;
|
|
}
|
|
items = ret;
|
|
|
|
cur_filter_size = priv->ttcan->fltr_config.std_fltr_size;
|
|
|
|
if ((idx > cur_filter_size) || (idx == -1)) {
|
|
if (cur_filter_size >= priv->ttcan->mram_cfg[MRAM_SIDF].num) {
|
|
dev_err(dev, "Max Invalid std filter Index\n");
|
|
return -ENOSPC;
|
|
}
|
|
}
|
|
|
|
ret = mttcan_check_fec_validity(priv, sfec);
|
|
if (ret < 0) {
|
|
dev_err(dev, "Invalid sfec value\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (items == 5) {
|
|
if (idx > cur_filter_size) {
|
|
dev_err(dev, "Invalid std filter Index\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* array access based on user provided index/data */
|
|
spec_bar();
|
|
ttcan_set_std_id_filter(priv->ttcan, priv->std_shadow,
|
|
idx, (u8)sft, (u8)sfec, sfid1, sfid2);
|
|
if (idx == cur_filter_size)
|
|
priv->ttcan->fltr_config.std_fltr_size++;
|
|
} else {
|
|
/* array access based on user provided index/data */
|
|
spec_bar();
|
|
ttcan_set_std_id_filter(priv->ttcan, priv->std_shadow,
|
|
cur_filter_size, (u8) sft, (u8)sfec, sfid1, sfid2);
|
|
priv->ttcan->fltr_config.std_fltr_size++;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
static ssize_t store_xtd_fltr(struct device *dev,
|
|
struct device_attribute *devattr,
|
|
const char *buf, size_t count)
|
|
{
|
|
struct mttcan_priv *priv = netdev_priv(to_net_dev(dev));
|
|
struct net_device *ndev = to_net_dev(dev);
|
|
unsigned int eft, efec;
|
|
unsigned int efid1, efid2;
|
|
int idx = -1, cur_filter_size;
|
|
int items;
|
|
int ret;
|
|
|
|
if (ndev->flags & IFF_UP) {
|
|
dev_err(dev, "device is running\n");
|
|
return -EBUSY;
|
|
}
|
|
/* usage: eft="0/1/2/3" efec=1...7 efid1="ID1h" efid2="ID2h" idx=%u
|
|
*/
|
|
ret = sscanf(buf, "eft=%u efec=%u efid1=%X efid2=%X idx=%u", &eft,
|
|
&efec, &efid1, &efid2, &idx);
|
|
if (ret < 4) {
|
|
/* Not passing index is allowed */
|
|
dev_err(dev, "Invalid xtd filter\n");
|
|
pr_err("usage:eft=0..3 efec=0..7 efid1=ID1h efid2=ID2h idx=i\n");
|
|
return -EINVAL;
|
|
}
|
|
items = ret;
|
|
|
|
cur_filter_size = priv->ttcan->fltr_config.xtd_fltr_size;
|
|
|
|
if ((idx > cur_filter_size) || (idx == -1)) {
|
|
if (cur_filter_size >= priv->ttcan->mram_cfg[MRAM_XIDF].num) {
|
|
dev_err(dev, "Max Invalid xtd filter Index\n");
|
|
return -ENOSPC;
|
|
}
|
|
}
|
|
|
|
ret = mttcan_check_fec_validity(priv, efec);
|
|
if (ret < 0) {
|
|
dev_err(dev, "Invalid efec value\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (items == 5) {
|
|
if (idx > cur_filter_size) {
|
|
dev_err(dev, "Invalid xtd filter Index\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* array access based on user provided index/data */
|
|
spec_bar();
|
|
ttcan_set_xtd_id_filter(priv->ttcan, priv->xtd_shadow,
|
|
idx, (u8) eft, (u8) efec, efid1, efid2);
|
|
if (idx == cur_filter_size)
|
|
priv->ttcan->fltr_config.xtd_fltr_size++;
|
|
} else {
|
|
/* array access based on user provided index/data */
|
|
spec_bar();
|
|
ttcan_set_xtd_id_filter(priv->ttcan, priv->xtd_shadow,
|
|
cur_filter_size, (u8) eft, (u8) efec, efid1, efid2);
|
|
priv->ttcan->fltr_config.xtd_fltr_size++;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
static ssize_t show_tx_cancel(struct device *dev,
|
|
struct device_attribute *devattr, char *buf)
|
|
{
|
|
struct mttcan_priv *priv = netdev_priv(to_net_dev(dev));
|
|
|
|
return sprintf(buf, "%s\n0x%x\n", "TXBCF",
|
|
ttcan_read_tx_cancelled_reg(priv->ttcan));
|
|
}
|
|
|
|
static ssize_t store_tx_cancel(struct device *dev,
|
|
struct device_attribute *devattr,
|
|
const char *buf, size_t count)
|
|
{
|
|
struct net_device *ndev = to_net_dev(dev);
|
|
struct mttcan_priv *priv = netdev_priv(ndev);
|
|
unsigned int txcbr;
|
|
|
|
/* usage: txbcr=bit_mask for buffer */
|
|
if (sscanf(buf, "txbcr=%X", &txcbr) != 1) {
|
|
dev_err(dev, "Invalid TXBCR value\n");
|
|
pr_err("%s usage: txbcr=bit_mask to cancel\n", buf);
|
|
return -EINVAL;
|
|
}
|
|
|
|
ttcan_set_tx_cancel_request(priv->ttcan, txcbr);
|
|
|
|
return count;
|
|
}
|
|
|
|
static ssize_t show_ttrmc(struct device *dev,
|
|
struct device_attribute *devattr, char *buf)
|
|
{
|
|
struct mttcan_priv *priv = netdev_priv(to_net_dev(dev));
|
|
|
|
return sprintf(buf, "%s\n0x%x\n", "TTRMC",
|
|
ttcan_get_ttrmc(priv->ttcan));
|
|
}
|
|
|
|
static ssize_t store_ttrmc(struct device *dev,
|
|
struct device_attribute *devattr,
|
|
const char *buf, size_t count)
|
|
{
|
|
struct net_device *ndev = to_net_dev(dev);
|
|
struct mttcan_priv *priv = netdev_priv(ndev);
|
|
unsigned int rmps, xtd, rid;
|
|
|
|
if (ndev->flags & IFF_UP) {
|
|
dev_err(dev, "TTRMC is protected, device is running\n");
|
|
return -EBUSY;
|
|
}
|
|
/* usage: rmps=0/1 xtd=0/1 rid=ReferenceID */
|
|
if (sscanf(buf, "rmps=%u xtd=%u rid=%X", &rmps, &xtd, &rid) != 3) {
|
|
dev_err(dev, "Invalid TTRMC\n");
|
|
pr_err("usage: rmps=0/1 xtd=0/1 rid=ReferenceID in hex\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
ttcan_set_ref_mesg(priv->ttcan, rid, rmps, xtd);
|
|
|
|
return count;
|
|
}
|
|
|
|
static ssize_t show_ttocf(struct device *dev,
|
|
struct device_attribute *devattr, char *buf)
|
|
{
|
|
struct mttcan_priv *priv = netdev_priv(to_net_dev(dev));
|
|
|
|
return sprintf(buf, "%s\n0x%x\n", "TTOCF",
|
|
ttcan_get_ttocf(priv->ttcan));
|
|
}
|
|
|
|
static ssize_t store_ttocf(struct device *dev,
|
|
struct device_attribute *devattr,
|
|
const char *buf, size_t count)
|
|
{
|
|
struct net_device *ndev = to_net_dev(dev);
|
|
struct mttcan_priv *priv = netdev_priv(ndev);
|
|
unsigned int evtp, ecc, egtf, awl, eecs, irto, ldsdl, tm, gen, om;
|
|
|
|
if (ndev->flags & IFF_UP) {
|
|
dev_err(dev, "TTCOF is protected, device is running\n");
|
|
return -EBUSY;
|
|
}
|
|
/* usage: evtp=0/1 ecc=0/1 egtf=0/1 awl=0..255 eecs=0 irto=0..127
|
|
* ldsdl=0..7 tm=0/1 gen=0/1 om=0..4 */
|
|
if (sscanf(buf,
|
|
"evtp=%u ecc=%u egtf=%u awl=%u eecs=%u irto=%u ldsdl=%u tm=%u gen=%u om=%u",
|
|
&evtp, &ecc, &egtf, &awl, &eecs, &irto, &ldsdl,
|
|
&tm, &gen, &om) != 10) {
|
|
dev_err(dev, "Invalid TTOCF\n");
|
|
pr_err("usage: evtp=0/1 ecc=0/1 egtf=0/1 awl=0..255 eecs=0 irto=0..127 ldsdl=0..7 tm=0/1 gen=0/1 om=0..4\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
ttcan_set_tt_config(priv->ttcan, evtp, ecc, egtf,
|
|
awl, eecs, irto, ldsdl, tm, gen, om);
|
|
|
|
return count;
|
|
}
|
|
|
|
static ssize_t show_ttmlm(struct device *dev,
|
|
struct device_attribute *devattr, char *buf)
|
|
{
|
|
struct mttcan_priv *priv = netdev_priv(to_net_dev(dev));
|
|
|
|
return sprintf(buf, "%s\n0x%x\n", "TTMLM",
|
|
ttcan_get_ttmlm(priv->ttcan));
|
|
}
|
|
|
|
static ssize_t store_ttmlm(struct device *dev,
|
|
struct device_attribute *devattr,
|
|
const char *buf, size_t count)
|
|
{
|
|
struct net_device *ndev = to_net_dev(dev);
|
|
struct mttcan_priv *priv = netdev_priv(ndev);
|
|
unsigned int entt, txew, css, ccm;
|
|
|
|
if (ndev->flags & IFF_UP) {
|
|
dev_err(dev, "TTMLM is protected, device is running\n");
|
|
return -EBUSY;
|
|
}
|
|
/* usage: entt=0...4095 txew=0..15 css=0..2 ccm=0..63(2*pow(n)-1) */
|
|
if (sscanf(buf, "entt=%u txew=%u css=%u ccm=%u",
|
|
&entt, &txew, &css, &ccm) != 4) {
|
|
dev_err(dev, "Invalid TTMLM\n");
|
|
pr_err("usage: entt=0...4095 txew=0..15 css=0..2 ccm=0..63(2*pow(n)-1)\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
ttcan_set_matrix_limits(priv->ttcan, entt, txew, css, ccm);
|
|
|
|
return count;
|
|
}
|
|
|
|
static ssize_t show_tttmc(struct device *dev,
|
|
struct device_attribute *devattr, char *buf)
|
|
{
|
|
struct mttcan_priv *priv = netdev_priv(to_net_dev(dev));
|
|
|
|
return sprintf(buf, "%s\n0x%x\n", "TTTMC",
|
|
ttcan_get_tttmc(priv->ttcan));
|
|
}
|
|
|
|
static ssize_t store_tttmc(struct device *dev,
|
|
struct device_attribute *devattr,
|
|
const char *buf, size_t count)
|
|
{
|
|
struct net_device *ndev = to_net_dev(dev);
|
|
struct mttcan_priv *priv = netdev_priv(ndev);
|
|
u32 tme, tttmc;
|
|
|
|
if (ndev->flags & IFF_UP) {
|
|
dev_err(dev, "TTTMC is protected, device is running\n");
|
|
return -EBUSY;
|
|
}
|
|
/* usage: tme=number of elements */
|
|
if (sscanf(buf, "tme=%u", &tme) != 1) {
|
|
dev_err(dev, "Invalid TTTMC\n");
|
|
pr_err("usage: tme=0..64 (Num Elements)\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (tme > 64)
|
|
tme = 64;
|
|
|
|
tttmc = ttcan_get_tttmc(priv->ttcan);
|
|
tttmc &= ~MTT_TTTMC_TME_MASK;
|
|
tttmc |= (tme << MTT_TTTMC_TME_SHIFT) &
|
|
MTT_TTTMC_TME_MASK;
|
|
|
|
ttcan_set_tttmc(priv->ttcan, tttmc);
|
|
|
|
return count;
|
|
}
|
|
|
|
static ssize_t show_cccr_txbar(struct device *dev,
|
|
struct device_attribute *devattr, char *buf)
|
|
{
|
|
struct mttcan_priv *priv = netdev_priv(to_net_dev(dev));
|
|
unsigned int init = 0;
|
|
|
|
init = ttcan_get_cccr(priv->ttcan) & 0x1;
|
|
return sprintf(buf, "CCCR.INIT %s\n", init ? "set" : "reset");
|
|
}
|
|
|
|
static ssize_t store_cccr_txbar(struct device *dev,
|
|
struct device_attribute *devattr,
|
|
const char *buf, size_t count)
|
|
{
|
|
struct mttcan_priv *priv = netdev_priv(to_net_dev(dev));
|
|
char str[32];
|
|
int txbar = 0;
|
|
|
|
if ((sscanf(buf, "%s txbar=%d", str, &txbar) != 2) || (txbar > 32)) {
|
|
dev_err(dev, "Invalid String or txbar\n");
|
|
pr_err("usage: reset/set txbar=0..32 (Num Elements)\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* usage: set/reset */
|
|
if (strcmp("set", str) == 0)
|
|
ttcan_set_config_change_enable(priv->ttcan);
|
|
else if (strcmp("reset", str) == 0)
|
|
ttcan_reset_config_change_enable(priv->ttcan);
|
|
else {
|
|
dev_err(dev, "Invalid String\n");
|
|
pr_err("valid strings: set/reset\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
txbar = (1UL << txbar) - 1;
|
|
ttcan_set_txbar(priv->ttcan, txbar);
|
|
|
|
return count;
|
|
}
|
|
|
|
static ssize_t show_txbar(struct device *dev,
|
|
struct device_attribute *devattr, char *buf)
|
|
{
|
|
return sprintf(buf, "Not implemented\n");
|
|
}
|
|
|
|
static ssize_t store_txbar(struct device *dev,
|
|
struct device_attribute *devattr,
|
|
const char *buf, size_t count)
|
|
{
|
|
struct mttcan_priv *priv = netdev_priv(to_net_dev(dev));
|
|
unsigned int txbar = 0;
|
|
|
|
/* usage: txbar=1...32*/
|
|
if ((sscanf(buf, "txbar=%u", &txbar) != 1) || (txbar > 32)) {
|
|
dev_err(dev, "Invalid TXBAR\n");
|
|
pr_err("usage: txbar=1..32\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
txbar = (1UL << txbar) - 1;
|
|
ttcan_set_txbar(priv->ttcan, txbar);
|
|
|
|
return count;
|
|
}
|
|
|
|
static ssize_t show_trigger_mem(struct device *dev,
|
|
struct device_attribute *devattr, char *buf)
|
|
{
|
|
struct mttcan_priv *priv = netdev_priv(to_net_dev(dev));
|
|
int cur = priv->ttcan->tt_mem_elements;
|
|
ssize_t ret, total = 0;
|
|
int i = 0;
|
|
|
|
ret = sprintf(buf, "%s\n", "Trigger Memory Elements");
|
|
if (ret < 0) {
|
|
pr_err("sprintf() failed at line %d\n", __LINE__);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
total += ret;
|
|
while (cur--) {
|
|
ret = sprintf(buf+total, "%d. 0x%llx\n", i,
|
|
ttcan_get_trigger_mem(priv->ttcan, i));
|
|
if (ret < 0) {
|
|
pr_err("sprintf() failed at line %d\n", __LINE__);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
total += ret;
|
|
i++;
|
|
}
|
|
return total;
|
|
}
|
|
|
|
static ssize_t store_trigger_mem(struct device *dev,
|
|
struct device_attribute *devattr,
|
|
const char *buf, size_t count)
|
|
{
|
|
unsigned int tm, cc, tmin, tmex, type;
|
|
unsigned int ftype, mnr;
|
|
int idx = -1, cur;
|
|
int ret;
|
|
|
|
struct net_device *ndev = to_net_dev(dev);
|
|
struct mttcan_priv *priv = netdev_priv(ndev);
|
|
|
|
if (ndev->flags & IFF_UP) {
|
|
dev_err(dev, "Trigger Mem is protected, device is running\n");
|
|
return -EBUSY;
|
|
}
|
|
/* usage: tm=0..FFFF cc=0..127 tmin=0/1 tmex=0/1 type=0..10
|
|
ftype=0/1 mnr=0..31 idx=%u */
|
|
ret = sscanf(buf,
|
|
"tm=%X cc=%u tmin=%u tmex=%u type=%u ftype=%u mnr=%u idx=%u",
|
|
&tm, &cc, &tmin, &tmex, &type, &ftype, &mnr, &idx);
|
|
if (ret < 7) {
|
|
/* Not passing index is allowed */
|
|
dev_err(dev, "Invalid Trigger Element\n");
|
|
pr_err("tm=0..0xFFFF cc=0..127 tmin=0/1 tmex=0/1 type=0..10 ftype=0/1 mnr=0..31 idx=i\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
cur = priv->ttcan->tt_mem_elements;
|
|
|
|
if ((idx > cur) || (idx == -1))
|
|
if (cur >= priv->ttcan->mram_cfg[MRAM_TMC].num) {
|
|
dev_err(dev, "Max Invalid Trigger mem Index\n");
|
|
return -ENOSPC;
|
|
}
|
|
|
|
if (ret == 8) {
|
|
if (idx > cur) {
|
|
dev_err(dev, "Invalid Trigger Mem Index\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* array access based on user provided index/data */
|
|
spec_bar();
|
|
ttcan_set_trigger_mem(priv->ttcan, priv->tmc_shadow, idx, tm,
|
|
cc, tmin, tmex, type, ftype, mnr);
|
|
|
|
if (idx == cur)
|
|
priv->ttcan->tt_mem_elements++;
|
|
} else {
|
|
/* array access based on user provided index/data */
|
|
spec_bar();
|
|
ttcan_set_trigger_mem(priv->ttcan, priv->tmc_shadow, cur, tm,
|
|
cc, tmin, tmex, type, ftype, mnr);
|
|
priv->ttcan->tt_mem_elements++;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
static ssize_t show_tdc_offset(struct device *dev,
|
|
struct device_attribute *devattr, char *buf)
|
|
{
|
|
struct mttcan_priv *priv = netdev_priv(to_net_dev(dev));
|
|
|
|
return sprintf(buf, "tdc_offset=0x%x, DBTP.tdc=%d\n",
|
|
priv->ttcan->tdc_offset, priv->ttcan->tdc);
|
|
}
|
|
|
|
static ssize_t store_tdc_offset(struct device *dev,
|
|
struct device_attribute *devattr,
|
|
const char *buf, size_t count)
|
|
{
|
|
struct mttcan_priv *priv = netdev_priv(to_net_dev(dev));
|
|
unsigned int tdc_offset = 0;
|
|
|
|
if ((sscanf(buf, "%X", &tdc_offset) != 1)) {
|
|
dev_err(dev, "wrong tdc_offset\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (tdc_offset != 0)
|
|
priv->ttcan->tdc = 1;
|
|
else
|
|
priv->ttcan->tdc = 0;
|
|
|
|
priv->ttcan->tdc_offset = tdc_offset;
|
|
|
|
return count;
|
|
}
|
|
|
|
static DEVICE_ATTR(std_filter, S_IRUGO | S_IWUSR, show_std_fltr,
|
|
store_std_fltr);
|
|
static DEVICE_ATTR(xtd_filter, S_IRUGO | S_IWUSR, show_xtd_fltr,
|
|
store_xtd_fltr);
|
|
static DEVICE_ATTR(gfc_filter, S_IRUGO | S_IWUSR, show_gfc_fltr,
|
|
store_gfc_fltr);
|
|
static DEVICE_ATTR(xidam, S_IRUGO | S_IWUSR, show_xidam, store_xidam);
|
|
static DEVICE_ATTR(tx_cancel, S_IRUGO | S_IWUSR, show_tx_cancel,
|
|
store_tx_cancel);
|
|
static DEVICE_ATTR(ttrmc, S_IRUGO | S_IWUSR, show_ttrmc, store_ttrmc);
|
|
static DEVICE_ATTR(ttocf, S_IRUGO | S_IWUSR, show_ttocf, store_ttocf);
|
|
static DEVICE_ATTR(ttmlm, S_IRUGO | S_IWUSR, show_ttmlm, store_ttmlm);
|
|
static DEVICE_ATTR(tttmc, S_IRUGO | S_IWUSR, show_tttmc, store_tttmc);
|
|
static DEVICE_ATTR(txbar, S_IRUGO | S_IWUSR, show_txbar, store_txbar);
|
|
static DEVICE_ATTR(cccr_init_txbar, S_IRUGO | S_IWUSR, show_cccr_txbar,
|
|
store_cccr_txbar);
|
|
static DEVICE_ATTR(trigger_mem, S_IRUGO | S_IWUSR, show_trigger_mem,
|
|
store_trigger_mem);
|
|
static DEVICE_ATTR(tdc_offset, S_IRUGO | S_IWUSR, show_tdc_offset,
|
|
store_tdc_offset);
|
|
|
|
static struct attribute *mttcan_attr[] = {
|
|
&dev_attr_std_filter.attr,
|
|
&dev_attr_xtd_filter.attr,
|
|
&dev_attr_gfc_filter.attr,
|
|
&dev_attr_xidam.attr,
|
|
&dev_attr_tx_cancel.attr,
|
|
&dev_attr_ttrmc.attr,
|
|
&dev_attr_ttocf.attr,
|
|
&dev_attr_ttmlm.attr,
|
|
&dev_attr_tttmc.attr,
|
|
&dev_attr_txbar.attr,
|
|
&dev_attr_cccr_init_txbar.attr,
|
|
&dev_attr_trigger_mem.attr,
|
|
&dev_attr_tdc_offset.attr,
|
|
NULL
|
|
};
|
|
|
|
static const struct attribute_group mttcan_attr_group = {
|
|
.attrs = mttcan_attr,
|
|
};
|
|
|
|
int mttcan_create_sys_files(struct device *dev)
|
|
{
|
|
return sysfs_create_group(&dev->kobj, &mttcan_attr_group);
|
|
}
|
|
|
|
void mttcan_delete_sys_files(struct device *dev)
|
|
{
|
|
sysfs_remove_group(&dev->kobj, &mttcan_attr_group);
|
|
}
|