RK3568 CAN驱动更新说明

2023-11-18

RK3568 CAN问题:同时收发数据一段时间(几秒钟)can出现错误收发功能异常,必须重新down、up恢复正常
内核更新rockchip_canfd.c、iopoll.h,配置Networking support  --->CAN bus subsystem support  --->CAN Device Drivers  --->Rockchip CANFD controller, make ARCH=arm64 menuconfig;内核配置后将.config覆盖到arch/arm64/configs/rockchip_linux_defconfig
修改设备树can节点.arch/arm64/boot/dts/rockchip/rk3568-evb1-ddr4-v10-linux.dts
compatible = "rockchip,rk3568-can-2.0",assigned-clock-rates = <300000000>;//如果CAN的比特率1M建议修改CAN时钟到300M,信号更稳定
./build.sh kernerl
注意:测试时两个节点帧ID不能相同,否则会发生数据错误,且负载率宜控制在30%以内
【负载率计算】
假设报文以定周期发送,总线上的报文只有数据帧一种类型,不存在远程帧、错误帧和过载帧这三种类型。因此每帧报文包括:帧起始(1bit)、仲裁域(29bit)、控制域(6bit)、数据域(8×8bit)、循环冗余码域(15bit)、应答域(2bit)和帧结尾(7bit),共124位。
假设每秒发送的数据帧数为185帧
对于高速CAN总线(500kbps)的负载率 = frac{185*124}\{500*1000}*100\% = 4.58\%
对于低速CAN总线(250kbps)的负载率 = frac{185*124}\{250*1000}*100\% = 9.16\%
【rockchip_canfd.c】
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (c) 2020 Rockchip Electronics Co. Ltd.
 * Rockchip CANFD driver
 */

#include <linux/delay.h>
#include <linux/iopoll.h>
#include <linux/pinctrl/consumer.h>
#include <linux/clk.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/skbuff.h>
#include <linux/spinlock.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/can/dev.h>
#include <linux/can/error.h>
#include <linux/can/led.h>
#include <linux/reset.h>
#include <linux/pm_runtime.h>

/* registers definition */
enum rockchip_canfd_reg {
        CAN_MODE = 0x00,
        CAN_CMD = 0x04,
        CAN_STATE = 0x08,
        CAN_INT = 0x0c,
        CAN_INT_MASK = 0x10,
        CAN_LOSTARB_CODE = 0x28,
        CAN_ERR_CODE = 0x2c,
        CAN_RX_ERR_CNT = 0x34,
        CAN_TX_ERR_CNT = 0x38,
        CAN_IDCODE = 0x3c,
        CAN_IDMASK = 0x40,
        CAN_TX_CHECK_FIC = 0x50,
        CAN_NBTP = 0x100,
        CAN_DBTP = 0x104,
        CAN_TDCR = 0x108,
        CAN_TSCC = 0x10c,
        CAN_TSCV = 0x110,
        CAN_TXEFC = 0x114,
        CAN_RXFC = 0x118,
        CAN_AFC = 0x11c,
        CAN_IDCODE0 = 0x120,
        CAN_IDMASK0 = 0x124,
        CAN_IDCODE1 = 0x128,
        CAN_IDMASK1 = 0x12c,
        CAN_IDCODE2 = 0x130,
        CAN_IDMASK2 = 0x134,
        CAN_IDCODE3 = 0x138,
        CAN_IDMASK3 = 0x13c,
        CAN_IDCODE4 = 0x140,
        CAN_IDMASK4 = 0x144,
        CAN_TXFIC = 0x200,
        CAN_TXID = 0x204,
        CAN_TXDAT0 = 0x208,
        CAN_TXDAT1 = 0x20c,
        CAN_TXDAT2 = 0x210,
        CAN_TXDAT3 = 0x214,
        CAN_TXDAT4 = 0x218,
        CAN_TXDAT5 = 0x21c,
        CAN_TXDAT6 = 0x220,
        CAN_TXDAT7 = 0x224,
        CAN_TXDAT8 = 0x228,
        CAN_TXDAT9 = 0x22c,
        CAN_TXDAT10 = 0x230,
        CAN_TXDAT11 = 0x234,
        CAN_TXDAT12 = 0x238,
        CAN_TXDAT13 = 0x23c,
        CAN_TXDAT14 = 0x240,
        CAN_TXDAT15 = 0x244,
        CAN_RXFIC = 0x300,
        CAN_RXID = 0x304,
        CAN_RXTS = 0x308,
        CAN_RXDAT0 = 0x30c,
        CAN_RXDAT1 = 0x310,
        CAN_RXDAT2 = 0x314,
        CAN_RXDAT3 = 0x318,
        CAN_RXDAT4 = 0x31c,
        CAN_RXDAT5 = 0x320,
        CAN_RXDAT6 = 0x324,
        CAN_RXDAT7 = 0x328,
        CAN_RXDAT8 = 0x32c,
        CAN_RXDAT9 = 0x330,
        CAN_RXDAT10 = 0x334,
        CAN_RXDAT11 = 0x338,
        CAN_RXDAT12 = 0x33c,
        CAN_RXDAT13 = 0x340,
        CAN_RXDAT14 = 0x344,
        CAN_RXDAT15 = 0x348,
        CAN_RXFRD = 0x400,
        CAN_TXEFRD = 0x500,
};

enum {
        ROCKCHIP_CANFD_MODE = 0,
        ROCKCHIP_CAN_MODE,
        ROCKCHIP_RK3568_CAN_MODE,
};

#define DATE_LENGTH_12_BYTE     (0x9)
#define DATE_LENGTH_16_BYTE     (0xa)
#define DATE_LENGTH_20_BYTE     (0xb)
#define DATE_LENGTH_24_BYTE     (0xc)
#define DATE_LENGTH_32_BYTE     (0xd)
#define DATE_LENGTH_48_BYTE     (0xe)
#define DATE_LENGTH_64_BYTE     (0xf)

#define CAN_TX0_REQ             BIT(0)
#define CAN_TX1_REQ             BIT(1)
#define CAN_TX_REQ_FULL         ((CAN_TX0_REQ) | (CAN_TX1_REQ))

#define MODE_FDOE               BIT(15)
#define MODE_BRSD               BIT(13)
#define MODE_SPACE_RX           BIT(12)
#define MODE_AUTO_RETX          BIT(10)
#define MODE_RXSORT             BIT(7)
#define MODE_TXORDER            BIT(6)
#define MODE_RXSTX              BIT(5)
#define MODE_LBACK              BIT(4)
#define MODE_SILENT             BIT(3)
#define MODE_SELF_TEST          BIT(2)
#define MODE_SLEEP              BIT(1)
#define RESET_MODE              0
#define WORK_MODE               BIT(0)

#define RX_FINISH_INT           BIT(0)
#define TX_FINISH_INT           BIT(1)
#define ERR_WARN_INT            BIT(2)
#define RX_BUF_OV_INT           BIT(3)
#define PASSIVE_ERR_INT         BIT(4)
#define TX_LOSTARB_INT          BIT(5)
#define BUS_ERR_INT             BIT(6)
#define RX_FIFO_FULL_INT        BIT(7)
#define RX_FIFO_OV_INT          BIT(8)
#define BUS_OFF_INT             BIT(9)
#define BUS_OFF_RECOVERY_INT    BIT(10)
#define TSC_OV_INT              BIT(11)
#define TXE_FIFO_OV_INT         BIT(12)
#define TXE_FIFO_FULL_INT       BIT(13)
#define WAKEUP_INT              BIT(14)

#define ERR_TYPE_MASK           GENMASK(28, 26)
#define ERR_TYPE_SHIFT          26
#define BIT_ERR                 0
#define STUFF_ERR               1
#define FORM_ERR                2
#define ACK_ERR                 3
#define CRC_ERR                 4
#define ERR_DIR_RX              BIT(25)
#define ERR_LOC_MASK            GENMASK(15, 0)

/* Nominal Bit Timing & Prescaler Register (NBTP) */
#define NBTP_MODE_3_SAMPLES     BIT(31)
#define NBTP_NSJW_SHIFT         24
#define NBTP_NSJW_MASK          (0x7f << NBTP_NSJW_SHIFT)
#define NBTP_NBRP_SHIFT         16
#define NBTP_NBRP_MASK          (0xff << NBTP_NBRP_SHIFT)
#define NBTP_NTSEG2_SHIFT       8
#define NBTP_NTSEG2_MASK        (0x7f << NBTP_NTSEG2_SHIFT)
#define NBTP_NTSEG1_SHIFT       0
#define NBTP_NTSEG1_MASK        (0x7f << NBTP_NTSEG1_SHIFT)

/* Data Bit Timing & Prescaler Register (DBTP) */
#define DBTP_MODE_3_SAMPLES     BIT(21)
#define DBTP_DSJW_SHIFT         17
#define DBTP_DSJW_MASK          (0xf << DBTP_DSJW_SHIFT)
#define DBTP_DBRP_SHIFT         9
#define DBTP_DBRP_MASK          (0xff << DBTP_DBRP_SHIFT)
#define DBTP_DTSEG2_SHIFT       5
#define DBTP_DTSEG2_MASK        (0xf << DBTP_DTSEG2_SHIFT)
#define DBTP_DTSEG1_SHIFT       0
#define DBTP_DTSEG1_MASK        (0x1f << DBTP_DTSEG1_SHIFT)

/* Transmitter Delay Compensation Register (TDCR) */
#define TDCR_TDCO_SHIFT         1
#define TDCR_TDCO_MASK          (0x3f << TDCR_TDCO_SHIFT)
#define TDCR_TDC_ENABLE         BIT(0)

#define TX_FD_ENABLE            BIT(5)
#define TX_FD_BRS_ENABLE        BIT(4)

#define FIFO_ENABLE             BIT(0)
#define RX_FIFO_CNT0_SHIFT      4
#define RX_FIFO_CNT0_MASK       (0x7 << RX_FIFO_CNT0_SHIFT)
#define RX_FIFO_CNT1_SHIFT      5
#define RX_FIFO_CNT1_MASK       (0x7 << RX_FIFO_CNT1_SHIFT)

#define FORMAT_SHIFT            7
#define FORMAT_MASK             (0x1 << FORMAT_SHIFT)
#define RTR_SHIFT               6
#define RTR_MASK                (0x1 << RTR_SHIFT)
#define FDF_SHIFT               5
#define FDF_MASK                (0x1 << FDF_SHIFT)
#define BRS_SHIFT               4
#define BRS_MASK                (0x1 << BRS_SHIFT)
#define DLC_SHIFT               0
#define DLC_MASK                (0xF << DLC_SHIFT)

#define CAN_RF_SIZE             0x48
#define CAN_TEF_SIZE            0x8
#define CAN_TXEFRD_OFFSET(n)    (CAN_TXEFRD + CAN_TEF_SIZE * (n))
#define CAN_RXFRD_OFFSET(n)     (CAN_RXFRD + CAN_RF_SIZE * (n))

#define CAN_RX_FILTER_MASK      0x1fffffff

#define DRV_NAME        "rockchip_canfd"

/* rockchip_canfd private data structure */

struct rockchip_canfd {
        struct can_priv can;
        struct device *dev;
        struct clk_bulk_data *clks;
        int num_clks;
        struct reset_control *reset;
        void __iomem *base;
        u32 irqstatus;
        unsigned long mode;
        int rx_fifo_shift;
        u32 rx_fifo_mask;
        bool txtorx;
        u32 tx_invalid[4];
        struct delayed_work tx_err_work;
};

static inline u32 rockchip_canfd_read(const struct rockchip_canfd *priv,
                                      enum rockchip_canfd_reg reg)
{
        return readl(priv->base + reg);
}

static inline void rockchip_canfd_write(const struct rockchip_canfd *priv,
                                        enum rockchip_canfd_reg reg, u32 val)
{
        writel(val, priv->base + reg);
}

static const struct can_bittiming_const rockchip_canfd_bittiming_const = {
        .name = DRV_NAME,
        .tseg1_min = 1,
        .tseg1_max = 128,
        .tseg2_min = 1,
        .tseg2_max = 128,
        .sjw_max = 128,
        .brp_min = 1,
        .brp_max = 256,
        .brp_inc = 2,
};

static const struct can_bittiming_const rockchip_canfd_data_bittiming_const = {
        .name = DRV_NAME,
        .tseg1_min = 1,
        .tseg1_max = 32,
        .tseg2_min = 1,
        .tseg2_max = 16,
        .sjw_max = 16,
        .brp_min = 1,
        .brp_max = 256,
        .brp_inc = 2,
};

static int set_reset_mode(struct net_device *ndev)
{
        struct rockchip_canfd *rcan = netdev_priv(ndev);

        reset_control_assert(rcan->reset);
        udelay(2);
        reset_control_deassert(rcan->reset);

        rockchip_canfd_write(rcan, CAN_MODE, 0);

        netdev_dbg(ndev, "%s MODE=0x%08x\n", __func__,
                   rockchip_canfd_read(rcan, CAN_MODE));

        return 0;
}

static int set_normal_mode(struct net_device *ndev)
{
        struct rockchip_canfd *rcan = netdev_priv(ndev);
        u32 val;

        val = rockchip_canfd_read(rcan, CAN_MODE);
        val |= WORK_MODE;
        rockchip_canfd_write(rcan, CAN_MODE, val);

        netdev_dbg(ndev, "%s MODE=0x%08x\n", __func__,
                   rockchip_canfd_read(rcan, CAN_MODE));
        return 0;
}

/* bittiming is called in reset_mode only */
static int rockchip_canfd_set_bittiming(struct net_device *ndev)
{
        struct rockchip_canfd *rcan = netdev_priv(ndev);
        const struct can_bittiming *bt = &rcan->can.bittiming;
        const struct can_bittiming *dbt = &rcan->can.data_bittiming;
        u16 brp, sjw, tseg1, tseg2;
        u32 reg_btp;

        brp = (bt->brp >> 1) - 1;
        sjw = bt->sjw - 1;
        tseg1 = bt->prop_seg + bt->phase_seg1 - 1;
        tseg2 = bt->phase_seg2 - 1;
        reg_btp = (brp << NBTP_NBRP_SHIFT) | (sjw << NBTP_NSJW_SHIFT) |
                  (tseg1 << NBTP_NTSEG1_SHIFT) |
                  (tseg2 << NBTP_NTSEG2_SHIFT);

        if (rcan->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES)
                reg_btp |= NBTP_MODE_3_SAMPLES;

        rockchip_canfd_write(rcan, CAN_NBTP, reg_btp);

        if (rcan->can.ctrlmode & CAN_CTRLMODE_FD) {
                reg_btp = 0;
                brp = (dbt->brp >> 1) - 1;
                sjw = dbt->sjw - 1;
                tseg1 = dbt->prop_seg + dbt->phase_seg1 - 1;
                tseg2 = dbt->phase_seg2 - 1;

                if (dbt->bitrate > 2200000) {
                        u32 tdco;

                        /* Equation based on Bosch's ROCKCHIP_CAN User Manual's
                         * Transmitter Delay Compensation Section
                         */
                        tdco = (rcan->can.clock.freq / dbt->bitrate) * 2 / 3;
                        /* Max valid TDCO value is 63 */
                        if (tdco > 63)
                                tdco = 63;

                        rockchip_canfd_write(rcan, CAN_TDCR,
                                             (tdco << TDCR_TDCO_SHIFT) |
                                             TDCR_TDC_ENABLE);
                }

                reg_btp |= (brp << DBTP_DBRP_SHIFT) |
                           (sjw << DBTP_DSJW_SHIFT) |
                           (tseg1 << DBTP_DTSEG1_SHIFT) |
                           (tseg2 << DBTP_DTSEG2_SHIFT);

                if (rcan->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES)
                        reg_btp |= DBTP_MODE_3_SAMPLES;

                rockchip_canfd_write(rcan, CAN_DBTP, reg_btp);
        }

        netdev_dbg(ndev, "%s NBTP=0x%08x, DBTP=0x%08x, TDCR=0x%08x\n", __func__,
                   rockchip_canfd_read(rcan, CAN_NBTP),
                   rockchip_canfd_read(rcan, CAN_DBTP),
                   rockchip_canfd_read(rcan, CAN_TDCR));
        return 0;
}

static int rockchip_canfd_get_berr_counter(const struct net_device *ndev,
                                           struct can_berr_counter *bec)
{
        struct rockchip_canfd *rcan = netdev_priv(ndev);
        int err;

        err = pm_runtime_get_sync(rcan->dev);
        if (err < 0) {
                netdev_err(ndev, "%s: pm_runtime_get failed(%d)\n",
                           __func__, err);
                return err;
        }

        bec->rxerr = rockchip_canfd_read(rcan, CAN_RX_ERR_CNT);
        bec->txerr = rockchip_canfd_read(rcan, CAN_TX_ERR_CNT);

        pm_runtime_put(rcan->dev);

        netdev_dbg(ndev, "%s RX_ERR_CNT=0x%08x, TX_ERR_CNT=0x%08x\n", __func__,
                   rockchip_canfd_read(rcan, CAN_RX_ERR_CNT),
                   rockchip_canfd_read(rcan, CAN_TX_ERR_CNT));

        return 0;
}

static int rockchip_canfd_start(struct net_device *ndev)
{
        struct rockchip_canfd *rcan = netdev_priv(ndev);
        u32 val;

        /* we need to enter the reset mode */
        set_reset_mode(ndev);

        rockchip_canfd_write(rcan, CAN_INT_MASK, 0);

        /* RECEIVING FILTER, accept all */
        rockchip_canfd_write(rcan, CAN_IDCODE, 0);
        rockchip_canfd_write(rcan, CAN_IDMASK, CAN_RX_FILTER_MASK);
        rockchip_canfd_write(rcan, CAN_IDCODE0, 0);
        rockchip_canfd_write(rcan, CAN_IDMASK0, CAN_RX_FILTER_MASK);
        rockchip_canfd_write(rcan, CAN_IDCODE1, 0);
        rockchip_canfd_write(rcan, CAN_IDMASK1, CAN_RX_FILTER_MASK);
        rockchip_canfd_write(rcan, CAN_IDCODE2, 0);
        rockchip_canfd_write(rcan, CAN_IDMASK2, CAN_RX_FILTER_MASK);
        rockchip_canfd_write(rcan, CAN_IDCODE3, 0);
        rockchip_canfd_write(rcan, CAN_IDMASK3, CAN_RX_FILTER_MASK);
        rockchip_canfd_write(rcan, CAN_IDCODE4, 0);
        rockchip_canfd_write(rcan, CAN_IDMASK4, CAN_RX_FILTER_MASK);

        /* set mode */
        val = rockchip_canfd_read(rcan, CAN_MODE);

        /* rx fifo enable */
        rockchip_canfd_write(rcan, CAN_RXFC,
                             rockchip_canfd_read(rcan, CAN_RXFC) | FIFO_ENABLE);

        /* Mode */
        val |= MODE_FDOE;

        /* Loopback Mode */
        if (rcan->can.ctrlmode & CAN_CTRLMODE_LOOPBACK)
                val |= MODE_SELF_TEST | MODE_LBACK;

        val |= MODE_AUTO_RETX;

        rockchip_canfd_write(rcan, CAN_MODE, val);

        rockchip_canfd_set_bittiming(ndev);

        set_normal_mode(ndev);

        rcan->can.state = CAN_STATE_ERROR_ACTIVE;

        netdev_dbg(ndev, "%s MODE=0x%08x, INT_MASK=0x%08x\n", __func__,
                   rockchip_canfd_read(rcan, CAN_MODE),
                   rockchip_canfd_read(rcan, CAN_INT_MASK));

        return 0;
}

static int rockchip_canfd_stop(struct net_device *ndev)
{
        struct rockchip_canfd *rcan = netdev_priv(ndev);

        rcan->can.state = CAN_STATE_STOPPED;
        /* we need to enter reset mode */
        set_reset_mode(ndev);

        /* disable all interrupts */
        rockchip_canfd_write(rcan, CAN_INT_MASK, 0xffff);

        netdev_dbg(ndev, "%s MODE=0x%08x, INT_MASK=0x%08x\n", __func__,
                   rockchip_canfd_read(rcan, CAN_MODE),
                   rockchip_canfd_read(rcan, CAN_INT_MASK));
        return 0;
}

static int rockchip_canfd_set_mode(struct net_device *ndev,
                                   enum can_mode mode)
{
        int err;

        switch (mode) {
        case CAN_MODE_START:
                err = rockchip_canfd_start(ndev);
                if (err) {
                        netdev_err(ndev, "starting CAN controller failed!\n");
                        return err;
                }
                if (netif_queue_stopped(ndev))
                        netif_wake_queue(ndev);
                break;

        default:
                return -EOPNOTSUPP;
        }

        return 0;
}

static void rockchip_canfd_tx_err_delay_work(struct work_struct *work)
{
        struct rockchip_canfd *rcan =
                container_of(work, struct rockchip_canfd, tx_err_work.work);
        u32 mode, err_code, id;

        id = rockchip_canfd_read(rcan, CAN_TXID);
        err_code = rockchip_canfd_read(rcan, CAN_ERR_CODE);
        if (err_code & 0x1fe0000) {
                mode = rockchip_canfd_read(rcan, CAN_MODE);
                rockchip_canfd_write(rcan, CAN_MODE, 0);
                rockchip_canfd_write(rcan, CAN_MODE, mode);
                rockchip_canfd_write(rcan, CAN_CMD, CAN_TX1_REQ);
                schedule_delayed_work(&rcan->tx_err_work, 1);
        } else if (rcan->txtorx && rcan->mode >= ROCKCHIP_CAN_MODE && id & CAN_EFF_FLAG) {
                schedule_delayed_work(&rcan->tx_err_work, 1);
        }
}

/* transmit a CAN message
 * message layout in the sk_buff should be like this:
 * xx xx xx xx         ff         ll 00 11 22 33 44 55 66 77
 * [ can_id ] [flags] [len] [can data (up to 8 bytes]
 */
static int rockchip_canfd_start_xmit(struct sk_buff *skb,
                                     struct net_device *ndev)
{
        struct rockchip_canfd *rcan = netdev_priv(ndev);
        struct canfd_frame *cf = (struct canfd_frame *)skb->data;
        u32 id, dlc;
        u32 cmd = CAN_TX0_REQ;
        int i;
        unsigned long flags;

        if (can_dropped_invalid_skb(ndev, skb))
                return NETDEV_TX_OK;

        netif_stop_queue(ndev);

        if (rockchip_canfd_read(rcan, CAN_CMD) & CAN_TX0_REQ)
                cmd = CAN_TX1_REQ;

        /* Watch carefully on the bit sequence */
        if (cf->can_id & CAN_EFF_FLAG) {
                /* Extended CAN ID format */
                id = cf->can_id & CAN_EFF_MASK;
                dlc = can_len2dlc(cf->len) & DLC_MASK;
                dlc |= FORMAT_MASK;

                /* Extended frames remote TX request */
                if (cf->can_id & CAN_RTR_FLAG)
                        dlc |= RTR_MASK;
        } else {
                /* Standard CAN ID format */
                id = cf->can_id & CAN_SFF_MASK;
                dlc = can_len2dlc(cf->len) & DLC_MASK;

                /* Standard frames remote TX request */
                if (cf->can_id & CAN_RTR_FLAG)
                        dlc |= RTR_MASK;
        }

        if ((rcan->can.ctrlmode & CAN_CTRLMODE_FD) && can_is_canfd_skb(skb)) {
                dlc |= TX_FD_ENABLE;
                if (cf->flags & CANFD_BRS)
                        dlc |= TX_FD_BRS_ENABLE;
        }

        if (rcan->txtorx && rcan->mode >= ROCKCHIP_CAN_MODE && cf->can_id & CAN_EFF_FLAG)
                rockchip_canfd_write(rcan, CAN_MODE, rockchip_canfd_read(rcan, CAN_MODE) | MODE_RXSTX);
        else
                rockchip_canfd_write(rcan, CAN_MODE, rockchip_canfd_read(rcan, CAN_MODE) & (~MODE_RXSTX));

        if (!rcan->txtorx && rcan->mode >= ROCKCHIP_CAN_MODE && cf->can_id & CAN_EFF_FLAG) {
                /* Two frames are sent consecutively.
                 * Before the first frame is tx finished,
                 * the register of the second frame is configured.
                 * Don't be interrupted in the middle.
                 */
                local_irq_save(flags);
                rockchip_canfd_write(rcan, CAN_TXID, rcan->tx_invalid[1]);
                rockchip_canfd_write(rcan, CAN_TXFIC, rcan->tx_invalid[0]);
                rockchip_canfd_write(rcan, CAN_TXDAT0, rcan->tx_invalid[2]);
                rockchip_canfd_write(rcan, CAN_TXDAT1, rcan->tx_invalid[3]);
                rockchip_canfd_write(rcan, CAN_CMD, CAN_TX0_REQ);
                rockchip_canfd_write(rcan, CAN_TXID, id);
                rockchip_canfd_write(rcan, CAN_TXFIC, dlc);
                for (i = 0; i < cf->len; i += 4)
                        rockchip_canfd_write(rcan, CAN_TXDAT0 + i,
                                             *(u32 *)(cf->data + i));
                rockchip_canfd_write(rcan, CAN_CMD, CAN_TX1_REQ);
                local_irq_restore(flags);
                can_put_echo_skb(skb, ndev, 0);

                return NETDEV_TX_OK;
        }

        rockchip_canfd_write(rcan, CAN_TXID, id);
        rockchip_canfd_write(rcan, CAN_TXFIC, dlc);

        for (i = 0; i < cf->len; i += 4)
                rockchip_canfd_write(rcan, CAN_TXDAT0 + i,
                                     *(u32 *)(cf->data + i));

        rockchip_canfd_write(rcan, CAN_CMD, CAN_TX0_REQ);

        if (rcan->txtorx && rcan->mode >= ROCKCHIP_CAN_MODE && cf->can_id & CAN_EFF_FLAG)
                schedule_delayed_work(&rcan->tx_err_work, 1);

        can_put_echo_skb(skb, ndev, 0);

        return NETDEV_TX_OK;
}

static int rockchip_canfd_rx(struct net_device *ndev)
{
        struct rockchip_canfd *rcan = netdev_priv(ndev);
        struct net_device_stats *stats = &ndev->stats;
        struct canfd_frame *cf;
        struct sk_buff *skb;
        u32 id_rockchip_canfd, dlc;
        int i = 0;
        u32 __maybe_unused ts, ret;
        u32 data[16] = {0};

        dlc = rockchip_canfd_read(rcan, CAN_RXFRD);
        id_rockchip_canfd = rockchip_canfd_read(rcan, CAN_RXFRD);
        ts = rockchip_canfd_read(rcan, CAN_RXFRD);
        for (i = 0; i < 16; i++)
                data[i] = rockchip_canfd_read(rcan, CAN_RXFRD);

        if (rcan->mode >= ROCKCHIP_CAN_MODE) {
                /* may be an empty frame */
                if (!dlc && !id_rockchip_canfd)
                        return 1;

                if (rcan->txtorx) {
                        if (rockchip_canfd_read(rcan, CAN_TX_CHECK_FIC) & FORMAT_MASK) {
                                ret = rockchip_canfd_read(rcan, CAN_TXID) & CAN_SFF_MASK;
                                if ((id_rockchip_canfd == ret) && !(dlc & FORMAT_MASK)) {
                                        rockchip_canfd_write(rcan, CAN_TX_CHECK_FIC,
                                                             ts | CAN_TX0_REQ);
                                        return 1;
                                } else {
                                        return 1;
                                }
                        }
                }
        }

        /* create zero'ed CAN frame buffer */
        if (dlc & FDF_MASK)
                skb = alloc_canfd_skb(ndev, &cf);
        else
                skb = alloc_can_skb(ndev, (struct can_frame **)&cf);
        if (!skb) {
                stats->rx_dropped++;
                return 1;
        }

        /* Change CAN data length format to socketCAN data format */
        if (dlc & FDF_MASK)
                cf->len = can_dlc2len(dlc & DLC_MASK);
        else
                cf->len = get_can_dlc(dlc & DLC_MASK);

        /* Change CAN ID format to socketCAN ID format */
        if (dlc & FORMAT_MASK) {
                /* The received frame is an Extended format frame */
                cf->can_id = id_rockchip_canfd;
                cf->can_id |= CAN_EFF_FLAG;
                if (dlc & RTR_MASK)
                        cf->can_id |= CAN_RTR_FLAG;
        } else {
                /* The received frame is a standard format frame */
                cf->can_id = id_rockchip_canfd;
                if (dlc & RTR_MASK)
                        cf->can_id |= CAN_RTR_FLAG;
        }

        if (dlc & BRS_MASK)
                cf->flags |= CANFD_BRS;

        if (!(cf->can_id & CAN_RTR_FLAG)) {
                /* Change CAN data format to socketCAN data format */
                for (i = 0; i < cf->len; i += 4)
                        *(u32 *)(cf->data + i) = data[i / 4];
        }

        stats->rx_packets++;
        stats->rx_bytes += cf->len;
        netif_rx(skb);

        can_led_event(ndev, CAN_LED_EVENT_RX);

        return 1;
}

static int rockchip_canfd_err(struct net_device *ndev, u32 isr)
{
        struct rockchip_canfd *rcan = netdev_priv(ndev);
        struct net_device_stats *stats = &ndev->stats;
        struct can_frame *cf;
        struct sk_buff *skb;
        unsigned int rxerr, txerr;
        u32 sta_reg;

        skb = alloc_can_err_skb(ndev, &cf);

        rxerr = rockchip_canfd_read(rcan, CAN_RX_ERR_CNT);
        txerr = rockchip_canfd_read(rcan, CAN_TX_ERR_CNT);
        sta_reg = rockchip_canfd_read(rcan, CAN_STATE);

        if (skb) {
                cf->data[6] = txerr;
                cf->data[7] = rxerr;
        }

        if (isr & BUS_OFF_INT) {
                rcan->can.state = CAN_STATE_BUS_OFF;
                rcan->can.can_stats.bus_off++;
                cf->can_id |= CAN_ERR_BUSOFF;
        } else if (isr & ERR_WARN_INT) {
                rcan->can.can_stats.error_warning++;
                rcan->can.state = CAN_STATE_ERROR_WARNING;
                /* error warning state */
                if (likely(skb)) {
                        cf->can_id |= CAN_ERR_CRTL;
                        cf->data[1] = (txerr > rxerr) ?
                                CAN_ERR_CRTL_TX_WARNING :
                                CAN_ERR_CRTL_RX_WARNING;
                        cf->data[6] = txerr;
                        cf->data[7] = rxerr;
                }
        } else if (isr & PASSIVE_ERR_INT) {
                rcan->can.can_stats.error_passive++;
                rcan->can.state = CAN_STATE_ERROR_PASSIVE;
                /* error passive state */
                cf->can_id |= CAN_ERR_CRTL;
                cf->data[1] = (txerr > rxerr) ?
                                        CAN_ERR_CRTL_TX_WARNING :
                                        CAN_ERR_CRTL_RX_WARNING;
                cf->data[6] = txerr;
                cf->data[7] = rxerr;
        }

        if (rcan->can.state >= CAN_STATE_BUS_OFF ||
            ((sta_reg & 0x20) == 0x20)) {
                can_bus_off(ndev);
        }

        stats->rx_packets++;
        stats->rx_bytes += cf->can_dlc;
        netif_rx(skb);

        return 0;
}

static irqreturn_t rockchip_canfd_interrupt(int irq, void *dev_id)
{
        struct net_device *ndev = (struct net_device *)dev_id;
        struct rockchip_canfd *rcan = netdev_priv(ndev);
        struct net_device_stats *stats = &ndev->stats;
        u32 err_int = ERR_WARN_INT | RX_BUF_OV_INT | PASSIVE_ERR_INT |
                      TX_LOSTARB_INT | BUS_ERR_INT | BUS_OFF_INT;
        u32 isr;
        u32 dlc = 0;
        u32 quota, work_done = 0;

        isr = rockchip_canfd_read(rcan, CAN_INT);
        if (isr & TX_FINISH_INT) {
                dlc = rockchip_canfd_read(rcan, CAN_TXFIC);
                /* transmission complete interrupt */
                if (dlc & FDF_MASK)
                        stats->tx_bytes += can_dlc2len(dlc & DLC_MASK);
                else
                        stats->tx_bytes += (dlc & DLC_MASK);
                stats->tx_packets++;
                if (rcan->txtorx && rcan->mode >= ROCKCHIP_CAN_MODE && dlc & FORMAT_MASK) {
                        cancel_delayed_work(&rcan->tx_err_work);
                        rockchip_canfd_write(rcan, CAN_TX_CHECK_FIC, FORMAT_MASK);
                        do {
                                quota = (rockchip_canfd_read(rcan, CAN_RXFC) &
                                         rcan->rx_fifo_mask) >>
                                        rcan->rx_fifo_shift;
                        } while (quota == 0); 
                        if (quota) {
                                while (work_done < quota)
                                        work_done += rockchip_canfd_rx(ndev);
                        }
                        if (rockchip_canfd_read(rcan, CAN_TX_CHECK_FIC) & CAN_TX0_REQ) {
                                rockchip_canfd_write(rcan, CAN_CMD, CAN_TX1_REQ);
                                read_poll_timeout_atomic(rockchip_canfd_read, quota,
                                     (quota & 0x3),
                                     0, 5000000, false, rcan, CAN_CMD);
                        }
                        rockchip_canfd_write(rcan, CAN_TX_CHECK_FIC, 0);
                }
                rockchip_canfd_write(rcan, CAN_CMD, 0);
                can_get_echo_skb(ndev, 0);
                netif_wake_queue(ndev);
                can_led_event(ndev, CAN_LED_EVENT_TX);
        }

        if (isr & RX_FINISH_INT) {
                quota = (rockchip_canfd_read(rcan, CAN_RXFC) & rcan->rx_fifo_mask) >>
                        rcan->rx_fifo_shift;
                if (quota) {
                        while (work_done < quota)
                                work_done += rockchip_canfd_rx(ndev);
                }
        }

        if (isr & err_int) {
                /* error interrupt */
                if (rockchip_canfd_err(ndev, isr))
                        netdev_err(ndev, "can't allocate buffer - clearing pending interrupts\n");
        }

        rockchip_canfd_write(rcan, CAN_INT, isr);
        return IRQ_HANDLED;
}

static int rockchip_canfd_open(struct net_device *ndev)
{
        struct rockchip_canfd *rcan = netdev_priv(ndev);
        int err;

        /* common open */
        err = open_candev(ndev);
        if (err)
                return err;

        err = pm_runtime_get_sync(rcan->dev);
        if (err < 0) {
                netdev_err(ndev, "%s: pm_runtime_get failed(%d)\n",
                           __func__, err);
                goto exit;
        }

        err = rockchip_canfd_start(ndev);
        if (err) {
                netdev_err(ndev, "could not start CAN peripheral\n");
                goto exit_can_start;
        }

        can_led_event(ndev, CAN_LED_EVENT_OPEN);
        netif_start_queue(ndev);

        netdev_dbg(ndev, "%s\n", __func__);
        return 0;

exit_can_start:
        pm_runtime_put(rcan->dev);
exit:
        close_candev(ndev);
        return err;
}

static int rockchip_canfd_close(struct net_device *ndev)
{
        struct rockchip_canfd *rcan = netdev_priv(ndev);

        netif_stop_queue(ndev);
        rockchip_canfd_stop(ndev);
        close_candev(ndev);
        can_led_event(ndev, CAN_LED_EVENT_STOP);
        pm_runtime_put(rcan->dev);
        cancel_delayed_work_sync(&rcan->tx_err_work);

        netdev_dbg(ndev, "%s\n", __func__);
        return 0;
}

static const struct net_device_ops rockchip_canfd_netdev_ops = {
        .ndo_open = rockchip_canfd_open,
        .ndo_stop = rockchip_canfd_close,
        .ndo_start_xmit = rockchip_canfd_start_xmit,
        .ndo_change_mtu = can_change_mtu,
};

/**
 * rockchip_canfd_suspend - Suspend method for the driver
 * @dev:        Address of the device structure
 *
 * Put the driver into low power mode.
 * Return: 0 on success and failure value on error
 */
static int __maybe_unused rockchip_canfd_suspend(struct device *dev)
{
        struct net_device *ndev = dev_get_drvdata(dev);

        if (netif_running(ndev)) {
                netif_stop_queue(ndev);
                netif_device_detach(ndev);
                rockchip_canfd_stop(ndev);
        }

        return pm_runtime_force_suspend(dev);
}

/**
 * rockchip_canfd_resume - Resume from suspend
 * @dev:        Address of the device structure
 *
 * Resume operation after suspend.
 * Return: 0 on success and failure value on error
 */
static int __maybe_unused rockchip_canfd_resume(struct device *dev)
{
        struct net_device *ndev = dev_get_drvdata(dev);
        int ret;

        ret = pm_runtime_force_resume(dev);
        if (ret) {
                dev_err(dev, "pm_runtime_force_resume failed on resume\n");
                return ret;
        }

        if (netif_running(ndev)) {
                ret = rockchip_canfd_start(ndev);
                if (ret) {
                        dev_err(dev, "rockchip_canfd_chip_start failed on resume\n");
                        return ret;
                }

                netif_device_attach(ndev);
                netif_start_queue(ndev);
        }

        return 0;
}

/**
 * rockchip_canfd_runtime_suspend - Runtime suspend method for the driver
 * @dev:        Address of the device structure
 *
 * Put the driver into low power mode.
 * Return: 0 always
 */
static int __maybe_unused rockchip_canfd_runtime_suspend(struct device *dev)
{
        struct net_device *ndev = dev_get_drvdata(dev);
        struct rockchip_canfd *rcan = netdev_priv(ndev);

        clk_bulk_disable_unprepare(rcan->num_clks, rcan->clks);

        return 0;
}

/**
 * rockchip_canfd_runtime_resume - Runtime resume from suspend
 * @dev:        Address of the device structure
 *
 * Resume operation after suspend.
 * Return: 0 on success and failure value on error
 */
static int __maybe_unused rockchip_canfd_runtime_resume(struct device *dev)
{
        struct net_device *ndev = dev_get_drvdata(dev);
        struct rockchip_canfd *rcan = netdev_priv(ndev);
        int ret;

        ret = clk_bulk_prepare_enable(rcan->num_clks, rcan->clks);
        if (ret) {
                dev_err(dev, "Cannot enable clock.\n");
                return ret;
        }

        return 0;
}

static const struct dev_pm_ops rockchip_canfd_dev_pm_ops = {
        SET_SYSTEM_SLEEP_PM_OPS(rockchip_canfd_suspend, rockchip_canfd_resume)
        SET_RUNTIME_PM_OPS(rockchip_canfd_runtime_suspend,
                           rockchip_canfd_runtime_resume, NULL)
};

static const struct of_device_id rockchip_canfd_of_match[] = {
        {
                .compatible = "rockchip,canfd-1.0",
                .data = (void *)ROCKCHIP_CANFD_MODE
        },
        {
                .compatible = "rockchip,can-2.0",
                .data = (void *)ROCKCHIP_CAN_MODE
        },
        {
                .compatible = "rockchip,rk3568-can-2.0",
                .data = (void *)ROCKCHIP_RK3568_CAN_MODE
        },
        {},
};
MODULE_DEVICE_TABLE(of, rockchip_canfd_of_match);

static int rockchip_canfd_probe(struct platform_device *pdev)
{
        struct net_device *ndev;
        struct rockchip_canfd *rcan;
        struct resource *res;
        void __iomem *addr;
        int err, irq;

        irq = platform_get_irq(pdev, 0);
        if (irq < 0) {
                dev_err(&pdev->dev, "could not get a valid irq\n");
                return -ENODEV;
        }

        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        addr = devm_ioremap_resource(&pdev->dev, res);
        if (IS_ERR(addr))
                return -EBUSY;

        ndev = alloc_candev(sizeof(struct rockchip_canfd), 1);
        if (!ndev) {
                dev_err(&pdev->dev, "could not allocate memory for CANFD device\n");
                return -ENOMEM;
        }
        rcan = netdev_priv(ndev);

        /* register interrupt handler */
        err = devm_request_irq(&pdev->dev, irq, rockchip_canfd_interrupt,
                               0, ndev->name, ndev);
        if (err) {
                dev_err(&pdev->dev, "request_irq err: %d\n", err);
                return err;
        }

        rcan->reset = devm_reset_control_array_get(&pdev->dev, false, false);
        if (IS_ERR(rcan->reset)) {
                if (PTR_ERR(rcan->reset) != -EPROBE_DEFER)
                        dev_err(&pdev->dev, "failed to get canfd reset lines\n");
                return PTR_ERR(rcan->reset);
        }
        rcan->num_clks = devm_clk_bulk_get_all(&pdev->dev, &rcan->clks);
        if (rcan->num_clks < 1)
                return -ENODEV;

        rcan->mode = (unsigned long)of_device_get_match_data(&pdev->dev);

        rcan->base = addr;
        rcan->can.clock.freq = clk_get_rate(rcan->clks[0].clk);
        rcan->dev = &pdev->dev;
        rcan->can.state = CAN_STATE_STOPPED;
        switch (rcan->mode) {
        case ROCKCHIP_CANFD_MODE:
                rcan->can.bittiming_const = &rockchip_canfd_bittiming_const;
                rcan->can.data_bittiming_const = &rockchip_canfd_data_bittiming_const;
                rcan->can.do_set_mode = rockchip_canfd_set_mode;
                rcan->can.do_get_berr_counter = rockchip_canfd_get_berr_counter;
                rcan->can.do_set_bittiming = rockchip_canfd_set_bittiming;
                rcan->can.do_set_data_bittiming = rockchip_canfd_set_bittiming;
                rcan->can.ctrlmode = CAN_CTRLMODE_FD;
                /* IFI CANFD can do both Bosch FD and ISO FD */
                rcan->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK |
                                               CAN_CTRLMODE_FD;
                rcan->rx_fifo_shift = RX_FIFO_CNT0_SHIFT;
                rcan->rx_fifo_mask = RX_FIFO_CNT0_MASK;
                break;
        case ROCKCHIP_CAN_MODE:
        case ROCKCHIP_RK3568_CAN_MODE:
                rcan->can.bittiming_const = &rockchip_canfd_bittiming_const;
                rcan->can.do_set_mode = rockchip_canfd_set_mode;
                rcan->can.do_get_berr_counter = rockchip_canfd_get_berr_counter;
                rcan->can.ctrlmode_supported = CAN_CTRLMODE_BERR_REPORTING |
                                               CAN_CTRLMODE_LISTENONLY |
                                               CAN_CTRLMODE_LOOPBACK |
                                               CAN_CTRLMODE_3_SAMPLES;
                rcan->rx_fifo_shift = RX_FIFO_CNT0_SHIFT;
                rcan->rx_fifo_mask = RX_FIFO_CNT0_MASK;
                break;
        default:
                return -EINVAL;
        }

        if (rcan->mode == ROCKCHIP_CAN_MODE) {
                rcan->rx_fifo_shift = RX_FIFO_CNT1_SHIFT;
                rcan->rx_fifo_mask = RX_FIFO_CNT1_MASK;
        }

        if (device_property_read_u32_array(&pdev->dev,
                                           "rockchip,tx-invalid-info",
                                           rcan->tx_invalid, 4))
                rcan->txtorx = 1;

        ndev->netdev_ops = &rockchip_canfd_netdev_ops;
        ndev->irq = irq;
        ndev->flags |= IFF_ECHO;
        rcan->can.restart_ms = 1;

        INIT_DELAYED_WORK(&rcan->tx_err_work, rockchip_canfd_tx_err_delay_work);

        platform_set_drvdata(pdev, ndev);
        SET_NETDEV_DEV(ndev, &pdev->dev);

        pm_runtime_enable(&pdev->dev);
        err = pm_runtime_get_sync(&pdev->dev);
        if (err < 0) {
                dev_err(&pdev->dev, "%s: pm_runtime_get failed(%d)\n",
                        __func__, err);
                goto err_pmdisable;
        }

        err = register_candev(ndev);
        if (err) {
                dev_err(&pdev->dev, "registering %s failed (err=%d)\n",
                        DRV_NAME, err);
                goto err_disableclks;
        }

        devm_can_led_init(ndev);

        return 0;

err_disableclks:
        pm_runtime_put(&pdev->dev);
err_pmdisable:
        pm_runtime_disable(&pdev->dev);
        free_candev(ndev);

        return err;
}

static int rockchip_canfd_remove(struct platform_device *pdev)
{
        struct net_device *ndev = platform_get_drvdata(pdev);

        unregister_netdev(ndev);
        pm_runtime_disable(&pdev->dev);
        free_candev(ndev);

        return 0;
}

static struct platform_driver rockchip_canfd_driver = {
        .driver = {
                .name = DRV_NAME,
                .pm = &rockchip_canfd_dev_pm_ops,
                .of_match_table = rockchip_canfd_of_match,
        },
        .probe = rockchip_canfd_probe,
        .remove = rockchip_canfd_remove,
};
module_platform_driver(rockchip_canfd_driver);

MODULE_AUTHOR("Elaine Zhang <zhangqing@rock-chips.com>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Rockchip CANFD Drivers");

【iopoll.h】
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Copyright (c) 2012-2014 The Linux Foundation. All rights reserved.
 */

#ifndef _LINUX_IOPOLL_H
#define _LINUX_IOPOLL_H

#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/ktime.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/io.h>

/**
 * read_poll_timeout - Periodically poll an address until a condition is
 *                      met or a timeout occurs
 * @op: accessor function (takes @args as its arguments)
 * @val: Variable to read the value into
 * @cond: Break condition (usually involving @val)
 * @sleep_us: Maximum time to sleep between reads in us (0
 *            tight-loops).  Should be less than ~20ms since usleep_range
 *            is used (see Documentation/timers/timers-howto.rst).
 * @timeout_us: Timeout in us, 0 means never timeout
 * @sleep_before_read: if it is true, sleep @sleep_us before read.
 * @args: arguments for @op poll
 *
 * Returns 0 on success and -ETIMEDOUT upon a timeout. In either
 * case, the last read value at @args is stored in @val. Must not
 * be called from atomic context if sleep_us or timeout_us are used.
 *
 * When available, you'll probably want to use one of the specialized
 * macros defined below rather than this macro directly.
 */
#define read_poll_timeout(op, val, cond, sleep_us, timeout_us, \
                                sleep_before_read, args...) \
({ \
        u64 __timeout_us = (timeout_us); \
        unsigned long __sleep_us = (sleep_us); \
        ktime_t __timeout = ktime_add_us(ktime_get(), __timeout_us); \
        might_sleep_if((__sleep_us) != 0); \
        if (sleep_before_read && __sleep_us) \
                usleep_range((__sleep_us >> 2) + 1, __sleep_us); \
        for (;;) { \
                (val) = op(args); \
                if (cond) \
                        break; \
                if (__timeout_us && \
                    ktime_compare(ktime_get(), __timeout) > 0) { \
                        (val) = op(args); \
                        break; \
                } \
                if (__sleep_us) \
                        usleep_range((__sleep_us >> 2) + 1, __sleep_us); \
        } \
        (cond) ? 0 : -ETIMEDOUT; \
})

/**
 * read_poll_timeout_atomic - Periodically poll an address until a condition is
 *                              met or a timeout occurs
 * @op: accessor function (takes @args as its arguments)
 * @val: Variable to read the value into
 * @cond: Break condition (usually involving @val)
 * @delay_us: Time to udelay between reads in us (0 tight-loops).  Should
 *            be less than ~10us since udelay is used (see
 *            Documentation/timers/timers-howto.rst).
 * @timeout_us: Timeout in us, 0 means never timeout
 * @delay_before_read: if it is true, delay @delay_us before read.
 * @args: arguments for @op poll
 *
 * Returns 0 on success and -ETIMEDOUT upon a timeout. In either
 * case, the last read value at @args is stored in @val.
 *
 * When available, you'll probably want to use one of the specialized
 * macros defined below rather than this macro directly.
 */
#define read_poll_timeout_atomic(op, val, cond, delay_us, timeout_us, \
                                        delay_before_read, args...) \
({ \
        u64 __timeout_us = (timeout_us); \
        unsigned long __delay_us = (delay_us); \
        ktime_t __timeout = ktime_add_us(ktime_get(), __timeout_us); \
        if (delay_before_read && __delay_us) \
                udelay(__delay_us); \
        for (;;) { \
                (val) = op(args); \
                if (cond) \
                        break; \
                if (__timeout_us && \
                    ktime_compare(ktime_get(), __timeout) > 0) { \
                        (val) = op(args); \
                        break; \
                } \
                if (__delay_us) \
                        udelay(__delay_us); \
        } \
        (cond) ? 0 : -ETIMEDOUT; \
})

/**
 * readx_poll_timeout - Periodically poll an address until a condition is met or a timeout occurs
 * @op: accessor function (takes @addr as its only argument)
 * @addr: Address to poll
 * @val: Variable to read the value into
 * @cond: Break condition (usually involving @val)
 * @sleep_us: Maximum time to sleep between reads in us (0
 *            tight-loops).  Should be less than ~20ms since usleep_range
 *            is used (see Documentation/timers/timers-howto.rst).
 * @timeout_us: Timeout in us, 0 means never timeout
 *
 * Returns 0 on success and -ETIMEDOUT upon a timeout. In either
 * case, the last read value at @addr is stored in @val. Must not
 * be called from atomic context if sleep_us or timeout_us are used.
 *
 * When available, you'll probably want to use one of the specialized
 * macros defined below rather than this macro directly.
 */
#define readx_poll_timeout(op, addr, val, cond, sleep_us, timeout_us)   \
        read_poll_timeout(op, val, cond, sleep_us, timeout_us, false, addr)

/**
 * readx_poll_timeout_atomic - Periodically poll an address until a condition is met or a timeout occurs
 * @op: accessor function (takes @addr as its only argument)
 * @addr: Address to poll
 * @val: Variable to read the value into
 * @cond: Break condition (usually involving @val)
 * @delay_us: Time to udelay between reads in us (0 tight-loops).  Should
 *            be less than ~10us since udelay is used (see
 *            Documentation/timers/timers-howto.rst).
 * @timeout_us: Timeout in us, 0 means never timeout
 *
 * Returns 0 on success and -ETIMEDOUT upon a timeout. In either
 * case, the last read value at @addr is stored in @val.
 *
 * When available, you'll probably want to use one of the specialized
 * macros defined below rather than this macro directly.
 */
#define readx_poll_timeout_atomic(op, addr, val, cond, delay_us, timeout_us) \
        read_poll_timeout_atomic(op, val, cond, delay_us, timeout_us, false, addr)

#define readb_poll_timeout(addr, val, cond, delay_us, timeout_us) \
        readx_poll_timeout(readb, addr, val, cond, delay_us, timeout_us)

#define readb_poll_timeout_atomic(addr, val, cond, delay_us, timeout_us) \
        readx_poll_timeout_atomic(readb, addr, val, cond, delay_us, timeout_us)

#define readw_poll_timeout(addr, val, cond, delay_us, timeout_us) \
        readx_poll_timeout(readw, addr, val, cond, delay_us, timeout_us)

#define readw_poll_timeout_atomic(addr, val, cond, delay_us, timeout_us) \
        readx_poll_timeout_atomic(readw, addr, val, cond, delay_us, timeout_us)

#define readl_poll_timeout(addr, val, cond, delay_us, timeout_us) \
        readx_poll_timeout(readl, addr, val, cond, delay_us, timeout_us)

#define readl_poll_timeout_atomic(addr, val, cond, delay_us, timeout_us) \
        readx_poll_timeout_atomic(readl, addr, val, cond, delay_us, timeout_us)

#define readq_poll_timeout(addr, val, cond, delay_us, timeout_us) \
        readx_poll_timeout(readq, addr, val, cond, delay_us, timeout_us)

#define readq_poll_timeout_atomic(addr, val, cond, delay_us, timeout_us) \
        readx_poll_timeout_atomic(readq, addr, val, cond, delay_us, timeout_us)

#define readb_relaxed_poll_timeout(addr, val, cond, delay_us, timeout_us) \
        readx_poll_timeout(readb_relaxed, addr, val, cond, delay_us, timeout_us)

#define readb_relaxed_poll_timeout_atomic(addr, val, cond, delay_us, timeout_us) \
        readx_poll_timeout_atomic(readb_relaxed, addr, val, cond, delay_us, timeout_us)

#define readw_relaxed_poll_timeout(addr, val, cond, delay_us, timeout_us) \
        readx_poll_timeout(readw_relaxed, addr, val, cond, delay_us, timeout_us)

#define readw_relaxed_poll_timeout_atomic(addr, val, cond, delay_us, timeout_us) \
        readx_poll_timeout_atomic(readw_relaxed, addr, val, cond, delay_us, timeout_us)

#define readl_relaxed_poll_timeout(addr, val, cond, delay_us, timeout_us) \
        readx_poll_timeout(readl_relaxed, addr, val, cond, delay_us, timeout_us)

#define readl_relaxed_poll_timeout_atomic(addr, val, cond, delay_us, timeout_us) \
        readx_poll_timeout_atomic(readl_relaxed, addr, val, cond, delay_us, timeout_us)

#define readq_relaxed_poll_timeout(addr, val, cond, delay_us, timeout_us) \
        readx_poll_timeout(readq_relaxed, addr, val, cond, delay_us, timeout_us)

#define readq_relaxed_poll_timeout_atomic(addr, val, cond, delay_us, timeout_us) \
        readx_poll_timeout_atomic(readq_relaxed, addr, val, cond, delay_us, timeout_us)

#endif /* _LINUX_IOPOLL_H */

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

RK3568 CAN驱动更新说明 的相关文章

  • 远程 ssh 命令:第一个回显输出丢失

    我试图通过 ssh 1 liner 调用在远程机器上运行多个命令 方法是将它们指定为传递给 bash c 的分号分隔字符串 它适用于某些情况 但不适用于其他情况 看一下这个 Note the echo 1 output is lost ba
  • bash 别名中允许使用哪些字符[关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我最近添加了 alias cd alias cd alias cd 到我的 bash aliases 文件 玩弄这个 我注意到在别名时 被
  • Linux 的 gcc __attribute__((selectany)) 替代方案?

    我想知道是否有替代方案 attribute selectany 在Linux中 我想定义这样的东西 char a qwe zxc 并将其包含在许多链接在一起的 c 文件中 因此链接器将看到 a 的多个定义 因此不会链接 我读过这个属性 se
  • 除了 iptables 之外还有数据包管理实用程序吗? [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我正在寻找一个 Linux 实用程序 它可以根据一组规则更改网络数据包的有效负载 理想情况下 我会使用
  • 编译器 libstdc++ 版本与系统版本

    我试图了解 g 如何选择它链接的 libstdc 版本 以及当库的 系统 版本不同时它意味着什么 我正在使用 gcc g 4 1 2 根据ABI 指南 http gcc gnu org onlinedocs libstdc manual a
  • libusb 和轮询/选择

    我正在使用 Linux 操作系统 想知道是否有任何文件描述符可以轮询 选择 当数据等待从 USB 设备读取时会触发这些文件描述符 我也在使用 libusb 库 但尚未找到可以使用的文件描述符 Use libusb 的轮询函数 http li
  • 命令行参数中的“-”(破折号)有什么魔力?

    例子 创建 ISO 映像并将其直接刻录到 CD mkisofs V Photos r home vivek photos cdrecord v dev dev dvdrw 更改到上一个目录 cd 侦听端口 12345 并解压发送到该端口的数
  • Linux:通过网络进行屏幕桌面视频捕获和 VNC 帧速率

    抱歉 文字墙很长 TL DR VNC 连接的帧速率是多少 以帧 秒为单位 或者更确切地说 由谁决定 客户端还是服务器 对于桌面屏幕捕获的任何其他建议 但 正确的时间编码 具有不抖动的帧速率 具有稳定的周期 并有可能将其作为未压缩 或无损 图
  • 当存在点和下划线时,使用 sed 搜索并替换

    我该如何更换foo with foo sed 只需运行 sed s foo foo g file php 不起作用 逃离 sed s foo foo g file php Example cat test txt foo bar sed s
  • 操作系统崩溃的常见原因[关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我有兴趣了解 操作系统崩溃 不限于Windows崩溃 最常见的技术原因 从操作系统编程的角度 有哪些 我正在寻找一个不像 打开太多应用
  • sed 仅最后一个匹配模式

    我想sed仅文本文件的最后一个匹配模式 输入文件 boy boy girl boy 输出文件 boy boy girl boys 一种方法是反转文件 仅替换第一个匹配项 然后再次反转 tac
  • 如何使用sprof?

    请举例说明 从邮件中找到here http sources redhat com ml libc alpha 2003 07 msg00029 html and here http sourceware org ml binutils 20
  • 在Linux伪终端中执行从一个终端发送到另一个终端的字符串

    假设我有一个终端 其中 tty 的输出是 dev pts 2 我想从另一个终端向第一个终端发送命令并执行它 使用 echo ls gt dev pts 2 仅在第一个终端中打印 ls 有没有办法执行字符串 不 终端不执行命令 它们只是数据的
  • 在单个命令中使用前缀重命名文件夹中的所有文件

    重命名带有前缀的文件夹中的所有文件 Unix 假设一个文件夹有两个文件 a txt b pdf 那么它们都应该从一个命令重命名为 Unix a txt Unix b pdf 如果您的文件名包含没有空格并且你没有任何子目录 你可以使用一个简单
  • 在键盘热插拔上加载模块

    我正在尝试学习如何为 Linux 系统编写模块和驱动程序 类似于this https unix stackexchange com questions 120839 usb kernel module does not load on de
  • 有关 Linux 内存类型的问题

    关于Linux内存我有以下问题 我知道活动内存是最常访问的内存部分 但是有人可以解释一下 linux 如何考虑将内存位置用于活动内存或非活动内存 主动存储器由哪些部分组成 磁盘 文件缓存是否被视为活动内存的一部分 有什么区别Buffers
  • 如何使用libaudit?

    我试图了解如何使用 libaudit 我想接收有关使用 C C 的用户操作的事件 我不明白如何设置规则 以及如何获取有关用户操作的信息 例如 我想获取用户创建目录时的信息 int audit fd audit open struct aud
  • 使用 MongoDB docker 镜像停止虚拟机而不丢失数据

    我已经在 AWS EC2 上的虚拟机中安装了官方的 MongoDB docker 映像 并且数据库上已经有数据 如果我停止虚拟机 以节省过夜费用 我会丢失数据库中包含的所有数据吗 在这些情况下我怎样才能让它持久 有多种选择可以实现此目的 但
  • 测试linux下磁盘空间不足

    我有一个程序 当写入某个文件的磁盘空间不足时 该程序可能会死掉 我不确定是否是这种情况 我想运行它并查看 但我的测试服务器不会很快耗尽空间 有什么办法可以嘲笑这种行为吗 看起来没有任何方法可以在 Ubuntu 中设置文件夹 文件大小限制 并
  • 退出 bash 脚本但保持进程运行

    我正在运行服务器 需要使用参数执行以下命令 这些脚本目前工作得很好 但问题是当我运行脚本时我无法返回到控制台 它在控制台中保持运行 如果我强行停止它 那么该过程也会停止 我想继续运行该进程并返回到控制台 bin sh php home st

随机推荐

  • uniapp之uni.navigateBack返回携带参数

    代码 let pages getCurrentPages 获取所有页面栈实例列表 let nowPage pages pages length 1 当前页页面实例 let prevPage pages pages length 2 上一页页
  • k8s挂载目录_k8s学习之存储卷volume详解

    1 案例准备 存储卷以MariaDb来演示 其中每个节点需要准备如下镜像 docker pull mariadb 10 5 2 编写部署mariadb的资源文件 apiVersion apps v1kind Deploymentmetada
  • 【C++】运算符重载实现分数类的四则运算

    题目 定义一个分数类 Fraction 该类具有分子 分母两个成员属性 编写程序完成以下功能 定义合适的构造函数 定义前自增 后自增运算符重载 完成分子 1操作 定义分数加减乘除四则运算的运算符重载函数 Fraction h头文件代码 pr
  • SpringSecurity进阶:OAuth2.0详解

    OAuth2是什么 OAuth是一个为了方便用户登入而使用的授权流程 他的优点是不需要向第三方平台暴露我们的用户名和密码 而是使用授权服务器颁发短期的token和效验token的方式开放部分资源给第三方平台 OAuth是一个授权协议不是认证
  • css解决浏览器记住密码后input框的背景色为淡黄色的代码

    input webkit autofill textarea webkit autofill select webkit autofill webkit text fill color ededed important webkit box
  • 100-The 3n+1 problem

    100 The 3n 1 problem Time limit 3 000 seconds Problems in Computer Science are often classified as belonging to a certai
  • SpringBoot引入本地jar包,引用sdk

    1 引入本地jar包并通过maven打包成jar包 第一步 创建lib包 将所需的本地jar包导入 第二步 在pom文件中引导路径
  • 【开发工具】Python解释器的下载和安装(windows系统)

    Python解释器的下载和安装目录 一 Python解释器种类 二 CPython解释器的下载 三 CPython解释器的安装 四 验证解释器是否安装成功 一 Python解释器种类 CPython C语 开发的解释器 官 应 泛的解释器
  • HTML常用标签--整理篇

    HTML常用标签 文章目录 HTML常用标签 文本标签 HTML格式化标签 HTML图像标签 HTML表格标签 HTML表单标签 文本标签 常 文本标签如下
  • JaveWeb-12JDBC

    目录 一 JDBC 1 什么是JDBC 2 JDBC编程步骤 1 装载相应数据库的JDBC驱动并进行初始化 2 建立JDBC和数据库之间的Connection连接 3 创建Statement或者PreparedStatement接口 执行S
  • 二叉排序树(二叉搜索树)的时间复杂度&空间复杂度

    二叉排序树又称二叉查找树 二叉搜索树 它或是一棵空的二叉树 或是具有下列性质的二叉树 若它的左子树不空 则左子树上所有节点的值均小于根节点的值 若它的右子树不空 则右子树上所有节点的值均大于根节点的值 它的左右子树也都是二叉排序树 如果二叉
  • Redis为什么快

    Redis有多快 Redis采用的是基于内存的采用的是单进程单线程模型的 KV 数据库 由C语言编写 官方提供的数据是可以达到100000 的QPS 每秒内查询次数 这个数据不比采用单进程多线程的同样基于内存的 KV 数据库 Memcach
  • [数据库] MySQL基础知识之日期判断及添加排序序号

    这篇文章主要记录MySQL中遇到的几个基础问题 希望文章对你有所帮助 包括 1 日期类型的判断 2 decode函数的替代方法 3 查询语句中添加一个排序的序号 4 子函数查询select a 1 日期类型判断 日期类型主要是 DATE 显
  • Python中if __name__ == ‘__main__‘:

    也看了一些别人的总结 这里就结合其他文章谈谈自己的理解 Python中 if name main 我刚开始看的时候就直接把他当成了一个项目运行的开始 至于为什么也没有仔细研究 后来看得多了 就研究了一下 name 是每一个python文件的
  • win10安装Ubuntu20.04虚拟机

    打开VMware Workstation的界面为下方的画面 注意第一次安装的时候我的计算机中是没有系统的 点击创建新的虚拟机会出现下方界面 选择自定义然后点击下一步 继续点击下一步选择稍后安装操作系统 继续点击下一步 继续下一步选择安装位置
  • 二级下拉菜单布局(纵向、横向)

    一级菜单 在ul列表内建立li元素并清除默认样式 让所有li元素左浮动并清除浮动 DOM中文档结构如下 ul class clearfix li a href 1 a li li a href 2 a li li a href 3 a li
  • Jupyter远程配置

    安装Jupyter pip install jupyter 生成默认配置文件 jupyter notebook generate config 生成密钥 python gt gt gt from notebook auth import p
  • 【React】单页面应用限制多开登录

    react 单页面应用限制多开登录 情景 测试小姐姐提了一个 BUG 在同一浏览器中打开两个页面 两个页面分别登录不同的账号 A 页面先登录A B 页面再登录B 此时回到 A 页面 交互时账号数据应该刷新为 B 登录的账号 分析 这个问题
  • 怎么设置权限?后台管理系统中的功能权限和数据权限设置

    一 功能级 页面级 权限 不同的用户 角色 登录到管理系统后 看到的功能不一样 思路 前端进入登录页面 前端发送请求给后端 后端验证用户名和密码是否正确 如果验证通过 需要根据用户所属的角色查他对应功能 path 响应给前端 前端接收到后端
  • RK3568 CAN驱动更新说明

    RK3568 CAN问题 同时收发数据一段时间 几秒钟 can出现错误收发功能异常 必须重新down up恢复正常 内核更新rockchip canfd c iopoll h 配置Networking support gt CAN bus