On 7/7/20 5:35 PM, Abhijeet Badurkar wrote: > > This CAN Controller is found on MEN Chameleon FPGAs. > > The driver/device supports the CAN2.0 specification. > There are 255 RX and 255 Tx buffer within the IP. The > pointer for the buffer are handled by HW to make the > access from within the driver as simple as possible. > > The driver also supports parameters to configure the > buffer level interrupt for RX/TX as well as a RX timeout > interrupt. > > With this configuration options, the driver/device > provides flexibility for different types of usecases. > > Changes in version 2: > Since the hardware does not have TX-done notification, > the local loopback mechanism is implemented using ECHO skbs. > The skb is added to echo stack, upon packet reception, > received and echo skb are matched. > > LED trigger support is added. Please remove, as this is obsolete. There's generic CONFIG_LEDS_TRIGGER_NETDEV now. > The other fixes are related to transmission and reception > of remote frame, setting can state on CAN initialization, > some minor fixes and code optimizations. > > Changes in version 3: > Fixed compiler warning. > > Signed-off-by: Abhijeet Badurkar <abhijeet.badurkar@xxxxxxxxxx> > --- > drivers/net/can/Kconfig | 10 + > drivers/net/can/Makefile | 1 + > drivers/net/can/men_z192_can.c | 1055 ++++++++++++++++++++++++++++++++ > 3 files changed, 1066 insertions(+) > create mode 100644 drivers/net/can/men_z192_can.c > > diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig > index f07012a76c0c..df658ecefddb 100644 > --- a/drivers/net/can/Kconfig > +++ b/drivers/net/can/Kconfig > @@ -133,6 +133,16 @@ config CAN_KVASER_PCIEFD > Kvaser Mini PCI Express HS v2 > Kvaser Mini PCI Express 2xHS v2 > > +config CAN_MEN_Z192 > + depends on MCB > + tristate "MEN 16Z192-00 CAN Controller" > + help > + Driver for MEN 16Z192-00 CAN Controller IP-Core, which > + is connected to the MEN Chameleon Bus. > + > + This driver can also be built as a module. If so, the module will be > + called men_z192_can.ko. > + > config CAN_SUN4I > tristate "Allwinner A10 CAN controller" > depends on MACH_SUN4I || MACH_SUN7I || COMPILE_TEST > diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile > index 22164300122d..d874ab1b9f08 100644 > --- a/drivers/net/can/Makefile > +++ b/drivers/net/can/Makefile > @@ -26,6 +26,7 @@ obj-$(CONFIG_CAN_GRCAN) += grcan.o > obj-$(CONFIG_CAN_IFI_CANFD) += ifi_canfd/ > obj-$(CONFIG_CAN_JANZ_ICAN3) += janz-ican3.o > obj-$(CONFIG_CAN_KVASER_PCIEFD) += kvaser_pciefd.o > +obj-$(CONFIG_CAN_MEN_Z192) += men_z192_can.o > obj-$(CONFIG_CAN_MSCAN) += mscan/ > obj-$(CONFIG_CAN_M_CAN) += m_can/ > obj-$(CONFIG_CAN_PEAK_PCIEFD) += peak_canfd/ > diff --git a/drivers/net/can/men_z192_can.c b/drivers/net/can/men_z192_can.c > new file mode 100644 > index 000000000000..a1e8be2dd94c > --- /dev/null > +++ b/drivers/net/can/men_z192_can.c > @@ -0,0 +1,1055 @@ > +// SPDX-License-Identifier: GPL-2.0-only > +/* MEN 16Z192 CAN Controller driver > + * > + * Copyright (C) 2016 MEN Mikroelektronik GmbH (www.men.de) > + * > + * Local loopback implementation in this driver is adapted from Janz MODULbus > + * VMOD-ICAN3 CAN Interface Driver > + * Copyright (c) 2010 Ira W. Snyder <iws@xxxxxxxxxxxxxxxx> > + */ > + > +#include <linux/kernel.h> > +#include <linux/module.h> > +#include <linux/io.h> > +#include <linux/iopoll.h> > +#include <linux/err.h> > +#include <linux/bitops.h> > +#include <linux/netdevice.h> > +#include <linux/can.h> > +#include <linux/can/dev.h> > +#include <linux/can/error.h> > +#include <linux/can/led.h> > +#include <linux/mcb.h> > + > +#define DRV_NAME "z192_can" > + > +#define MEN_Z192_MODE_TOUT_US 40 > + > +/* CTL/BTR Register Bits */ > +#define MEN_Z192_CTL0_INITRQ BIT(0) > +#define MEN_Z192_CTL0_SLPRQ BIT(1) > +#define MEN_Z192_CTL1_INITAK BIT(8) > +#define MEN_Z192_CTL1_SLPAK BIT(9) > +#define MEN_Z192_CTL1_LISTEN BIT(12) > +#define MEN_Z192_CTL1_LOOPB BIT(13) > +#define MEN_Z192_CTL1_CANE BIT(15) > +#define MEN_Z192_BTR0_BRP(x) (((x) & 0x3f) << 16) > +#define MEN_Z192_BTR0_SJW(x) (((x) & 0x03) << 22) > +#define MEN_Z192_BTR1_TSEG1(x) (((x) & 0x0f) << 24) > +#define MEN_Z192_BTR1_TSEG2(x) (((x) & 0x07) << 28) > +#define MEN_Z192_BTR1_SAMP BIT(31) > + > +/* IER Interrupt Enable Register bits */ > +#define MEN_Z192_RXIE BIT(0) > +#define MEN_Z192_OVRIE BIT(1) > +#define MEN_Z192_CSCIE BIT(6) > +#define MEN_Z192_TOUTE BIT(7) > +#define MEN_Z192_TXIE BIT(16) > +#define MEN_Z192_ERRIE BIT(17) > + > +#define MEN_Z192_IRQ_ALL \ > + (MEN_Z192_RXIE | MEN_Z192_OVRIE | \ > + MEN_Z192_CSCIE | MEN_Z192_TOUTE | \ only one space in front of the '|' > + MEN_Z192_TXIE) > + > +/* RX_TX_STAT RX/TX Status status register bits */ > +#define MEN_Z192_RX_BUF_CNT(x) ((x) & 0xff) > +#define MEN_Z192_TX_BUF_CNT(x) (((x) & 0xff00) >> 8) > +#define MEN_Z192_RFLG_RXIF BIT(16) > +#define MEN_Z192_RFLG_OVRF BIT(17) > +#define MEN_Z192_RFLG_TSTATE GENMASK(19, 18) > +#define MEN_Z192_RFLG_RSTATE GENMASK(21, 20) > +#define MEN_Z192_RFLG_CSCIF BIT(22) > +#define MEN_Z192_RFLG_TOUTF BIT(23) > +#define MEN_Z192_TFLG_TXIF BIT(24) > + > +#define MEN_Z192_GET_TSTATE(x) (((x) & MEN_Z192_RFLG_TSTATE) >> 18) > +#define MEN_Z192_GET_RSTATE(x) (((x) & MEN_Z192_RFLG_RSTATE) >> 20) > + > +#define MEN_Z192_IRQ_NAPI (MEN_Z192_RXIE | MEN_Z192_TOUTE) > + > +#define MEN_Z192_IRQ_FLAGS_ALL \ > + (MEN_Z192_RFLG_RXIF | MEN_Z192_RFLG_OVRF | \ > + MEN_Z192_RFLG_TSTATE | MEN_Z192_RFLG_RSTATE | \ > + MEN_Z192_RFLG_CSCIF | MEN_Z192_RFLG_TOUTF | \ > + MEN_Z192_TFLG_TXIF) > + > +/* RX/TX Error counter bits */ > +#define MEN_Z192_GET_RX_ERR_CNT(x) ((x) & 0xff) > +#define MEN_Z192_GET_TX_ERR_CNT(x) (((x) & 0x00ff0000) >> 16) define a MASK using GENMASK and use FIELD_GET() in the code. > + > +/* Buffer level register bits */ > +#define MEN_Z192_RX_BUF_LVL GENMASK(15, 0) > +#define MEN_Z192_TX_BUF_LVL GENMASK(31, 16) > + > +/* RX/TX Buffer register bits */ > +#define MEN_Z192_CFBUF_LEN GENMASK(3, 0) > +#define MEN_Z192_CFBUF_ID1 GENMASK(31, 21) > +#define MEN_Z192_CFBUF_ID2 GENMASK(18, 1) > +#define MEN_Z192_CFBUF_TS GENMASK(31, 8) > +#define MEN_Z192_CFBUF_E_RTR BIT(0) > +#define MEN_Z192_CFBUF_IDE BIT(19) > +#define MEN_Z192_CFBUF_SRR BIT(20) > +#define MEN_Z192_CFBUF_S_RTR BIT(20) > +#define MEN_Z192_CFBUF_ID2_SHIFT 1 > +#define MEN_Z192_CFBUF_ID1_SHIFT 21 > + > +/* Global register offsets */ > +#define MEN_Z192_RX_BUF_START 0x0000 > +#define MEN_Z192_TX_BUF_START 0x1000 > +#define MEN_Z192_REGS_OFFS 0x2000 > + > +/* Buffer level control values */ > +#define MEN_Z192_MIN_BUF_LVL 0 > +#define MEN_Z192_MAX_BUF_LVL 254 > +#define MEN_Z192_RX_BUF_LVL_DEF 5 > +#define MEN_Z192_TX_BUF_LVL_DEF 5 > +#define MEN_Z192_RX_TOUT_MIN 0 > +#define MEN_Z192_RX_TOUT_MAX 65535 > +#define MEN_Z192_RX_TOUT_DEF 1000 Please use a consistent indention scheme, I suggest only one space between name and value/expression. > + > +static int txlvl = MEN_Z192_TX_BUF_LVL_DEF; > +module_param(txlvl, int, 0444); > +MODULE_PARM_DESC(txlvl, "TX IRQ trigger level (in frames) 0-254, default=" > + __MODULE_STRING(MEN_Z192_MIN_BUF_LVL) ")"); Please implement this via ethtool coalesce. See: https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/ethtool.h#L389 https://elixir.bootlin.com/linux/latest/source/include/linux/ethtool.h#L392 Use an existing driver to follow, how to implement ethtool: https://elixir.bootlin.com/linux/latest/source/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c#L815 > +/* Following parameters related to RX interrupt have impact on latency and > + * provide the users full control over them. > + * > + * rxlvl: Receive buffer level or in other words, the number of frames to > + * trigger RX interrupt > + * When number of received valid CAN frames exceeds rxlvl and RX interrupt is > + * enabled, an interrupt will be generated. For example, if rxlvl is 0 and one > + * frame is received, an interrupt will be generated or if rxlvl is 254 and 255 > + * frames are received, an interrupt will be generated. This probably correspondes to ETHTOOL_COALESCE_RX_MAX_FRAMES > + * > + * rx_timeout: Read timeout for receive buffer > + * When receive buffer is not empty and it has not been accessed since > + * rx_timeout then TOUTF flag is set. If RX interrupt was already enabled and > + * TOUTF flag is set, a RX interrupt will be generated. By defining this > + * parameter, time of the interrupt generation can be controlled, if the RX > + * buffer level is not reached within rx_timeout. This probably correspondes to ETHTOOL_COALESCE_RX_USECS > + * > + * When an interrupt is generated for either of these parameters, NAPI poll is > + * scheduled to read the recived CAN frames. > + */ > +static int rxlvl = MEN_Z192_RX_BUF_LVL_DEF; > +module_param(rxlvl, int, 0444); > +MODULE_PARM_DESC(rxlvl, "RX IRQ trigger level (in frames) 0-254, default=" > + __MODULE_STRING(MEN_Z192_MIN_BUF_LVL) ")"); > + > +static int rx_timeout = MEN_Z192_RX_TOUT_DEF; > +module_param(rx_timeout, int, 0444); > +MODULE_PARM_DESC(rx_timeout, "RX IRQ timeout (in 100usec steps), default=" > + __MODULE_STRING(MEN_Z192_RX_TOUT_DEF) ")"); > + > +struct men_z192_regs { > + u32 ctl_btr; /* Control and bus timing register */ > + u32 ier; /* Interrupt enable register */ > + u32 buf_lvl; /* Buffer level register */ > + u32 rxa; /* RX Data acknowledge register */ > + u32 txa; /* TX data acknowledge register */ > + u32 rx_tx_sts; /* RX/TX flags and buffer level */ > + u32 ovr_ecc_sts; /* Overrun/ECC status register */ > + u32 idac_ver; /* ID acceptance control / version */ > + u32 rx_tx_err; /* RX/TX error counter register */ > + u32 idar_0_to_3; /* ID acceptance register 0...3 */ > + u32 idar_4_to_7; /* ID acceptance register 4...7 */ > + u32 idmr_0_to_3; /* ID mask register 0...3 */ > + u32 idmr_4_to_7; /* ID mask register 4...7 */ > + u32 rx_timeout; /* receive timeout */ > + u32 timebase; /* Base frequency for baudrate calculation */ > +}; > + > +struct men_z192 { > + struct can_priv can; > + struct napi_struct napi; > + struct net_device *ndev; > + struct device *dev; > + > + /* Lock for CTL_BTR register access. This register combines bittiming > + * bits and the operation mode bits. It is also used for bit r/m/w > + * access to all registers. > + */ > + spinlock_t lock; > + struct resource *mem; > + struct men_z192_regs __iomem *regs; > + void __iomem *dev_base; > + > + /* queue for echo packets */ > + struct sk_buff_head echoq; > +}; > + > +struct men_z192_cf_buf { > + u32 can_id; > + u32 data[2]; > + u32 length; > +}; > + > +enum men_z192_int_state { > + MEN_Z192_CAN_DIS = 0, > + MEN_Z192_CAN_EN, > + MEN_Z192_CAN_NAPI_DIS, > + MEN_Z192_CAN_NAPI_EN, > +}; > + > +static enum can_state bus_state_map[] = { const > + CAN_STATE_ERROR_ACTIVE, > + CAN_STATE_ERROR_WARNING, > + CAN_STATE_ERROR_PASSIVE, > + CAN_STATE_BUS_OFF > +}; > + > +static const struct can_bittiming_const men_z192_bittiming_const = { > + .name = DRV_NAME, > + .tseg1_min = 4, > + .tseg1_max = 16, > + .tseg2_min = 2, > + .tseg2_max = 8, > + .sjw_max = 4, > + .brp_min = 2, > + .brp_max = 64, > + .brp_inc = 1, > +}; > + > +static inline void men_z192_bit_clr(struct men_z192 *priv, void __iomem *addr, > + u32 mask) > +{ > + unsigned long flags; > + u32 val; > + > + spin_lock_irqsave(&priv->lock, flags); > + > + val = readl(addr); > + val &= ~mask; > + writel(val, addr); > + > + spin_unlock_irqrestore(&priv->lock, flags); > +} > + > +static inline void men_z192_bit_set(struct men_z192 *priv, void __iomem *addr, > + u32 mask) > +{ > + unsigned long flags; > + u32 val; > + > + spin_lock_irqsave(&priv->lock, flags); > + > + val = readl(addr); > + val |= mask; > + writel(val, addr); > + > + spin_unlock_irqrestore(&priv->lock, flags); > +} > + > +static inline void men_z192_ack_rx_pkg(struct men_z192 *priv, > + unsigned int count) > +{ > + writel(count, &priv->regs->rxa); > +} > + > +static inline void men_z192_ack_tx_pkg(struct men_z192 *priv, > + unsigned int count) > +{ > + writel(count, &priv->regs->txa); > +} > + > +static void men_z192_set_int(struct men_z192 *priv, > + enum men_z192_int_state state) > +{ > + struct men_z192_regs __iomem *regs = priv->regs; > + > + switch (state) { > + case MEN_Z192_CAN_DIS: > + men_z192_bit_clr(priv, ®s->ier, MEN_Z192_IRQ_ALL); > + break; > + > + case MEN_Z192_CAN_EN: > + men_z192_bit_set(priv, ®s->ier, MEN_Z192_IRQ_ALL); > + break; > + > + case MEN_Z192_CAN_NAPI_DIS: > + men_z192_bit_clr(priv, ®s->ier, MEN_Z192_IRQ_NAPI); > + break; > + > + case MEN_Z192_CAN_NAPI_EN: > + men_z192_bit_set(priv, ®s->ier, MEN_Z192_IRQ_NAPI); > + break; > + > + default: > + netdev_err(priv->ndev, "invalid interrupt state\n"); > + break; > + } > +} > + > +static int men_z192_get_berr_counter(const struct net_device *ndev, > + struct can_berr_counter *bec) > +{ > + struct men_z192 *priv = netdev_priv(ndev); > + struct men_z192_regs __iomem *regs = priv->regs; > + u32 err_cnt; > + > + err_cnt = readl(®s->rx_tx_err); > + > + bec->txerr = MEN_Z192_GET_TX_ERR_CNT(err_cnt); > + bec->rxerr = MEN_Z192_GET_RX_ERR_CNT(err_cnt); FIELD_GET() > + > + return 0; > +} > + > +static int men_z192_req_run_mode(struct men_z192 *priv) > +{ > + struct men_z192_regs __iomem *regs = priv->regs; > + u32 val; > + > + men_z192_bit_clr(priv, ®s->ctl_btr, MEN_Z192_CTL0_INITRQ); > + > + return read_poll_timeout(readl, val, !(val & MEN_Z192_CTL1_INITAK), 10, please use a define for the 10 > + MEN_Z192_MODE_TOUT_US, 0, ®s->ctl_btr); > +} > + > +static int men_z192_req_init_mode(struct men_z192 *priv) > +{ > + struct men_z192_regs __iomem *regs = priv->regs; > + u32 val; > + > + men_z192_bit_set(priv, ®s->ctl_btr, MEN_Z192_CTL0_INITRQ); > + > + return read_poll_timeout(readl, val, val & MEN_Z192_CTL1_INITAK, 10, same here > + MEN_Z192_MODE_TOUT_US, 0, ®s->ctl_btr); > +} > + > +static void z192_put_echo_skb(struct men_z192 *priv, struct sk_buff *skb) > +{ > + skb = can_create_echo_skb(skb); > + if (!skb) > + return; > + > + /* save this skb for tx interrupt echo handling */ > + skb_queue_tail(&priv->echoq, skb); > +} > + > +static unsigned int z192_get_echo_skb(struct men_z192 *priv) > +{ > + struct sk_buff *skb = skb_dequeue(&priv->echoq); > + struct can_frame *cf; > + u8 dlc; > + > + /* this should never trigger unless there is a driver bug */ > + if (!skb) { > + netdev_err(priv->ndev, "BUG: echo skb not occupied\n"); > + return 0; > + } > + > + cf = (struct can_frame *)skb->data; > + dlc = cf->can_dlc; > + > + /* check flag whether this packet has to be looped back */ > + if (skb->pkt_type != PACKET_LOOPBACK) { > + kfree_skb(skb); > + return dlc; > + } > + > + skb->protocol = htons(ETH_P_CAN); > + skb->pkt_type = PACKET_BROADCAST; > + skb->ip_summed = CHECKSUM_UNNECESSARY; > + skb->dev = priv->ndev; > + netif_receive_skb(skb); > + return dlc; > +} > + > +static bool z192_echo_skb_matches(struct men_z192 *priv, struct sk_buff *skb) > +{ > + struct can_frame *cf = (struct can_frame *)skb->data; > + struct sk_buff *echo_skb = skb_peek(&priv->echoq); > + struct can_frame *echo_cf; > + > + if (!echo_skb) > + return false; > + > + echo_cf = (struct can_frame *)echo_skb->data; > + if (cf->can_id != echo_cf->can_id) > + return false; > + > + if (cf->can_dlc != echo_cf->can_dlc) > + return false; > + > + return memcmp(cf->data, echo_cf->data, cf->can_dlc) == 0; > +} > + > +static int men_z192_read_frame(struct net_device *ndev, unsigned int frame_nr) > +{ > + struct net_device_stats *stats = &ndev->stats; > + struct men_z192 *priv = netdev_priv(ndev); > + struct men_z192_cf_buf __iomem *cf_buf; > + struct can_frame *cf; > + struct sk_buff *skb; > + u32 cf_offset; > + u32 length; > + u32 id; > + > + skb = alloc_can_skb(ndev, &cf); > + if (unlikely(!skb)) { > + stats->rx_dropped++; > + return 0; > + } > + > + cf_offset = sizeof(struct men_z192_cf_buf) * frame_nr; > + > + cf_buf = priv->dev_base + MEN_Z192_RX_BUF_START + cf_offset; > + length = readl(&cf_buf->length) & MEN_Z192_CFBUF_LEN; > + id = readl(&cf_buf->can_id); > + > + if (id & MEN_Z192_CFBUF_IDE) { > + /* Extended frame */ > + cf->can_id |= > + (id & MEN_Z192_CFBUF_ID1) >> 3 | > + ((id & MEN_Z192_CFBUF_ID2) >> > + MEN_Z192_CFBUF_ID2_SHIFT) | use FIELD_GET() here > + CAN_EFF_FLAG; > + > + if (id & MEN_Z192_CFBUF_E_RTR) > + cf->can_id |= CAN_RTR_FLAG; > + } else { > + /* Standard frame */ > + cf->can_id = (id & MEN_Z192_CFBUF_ID1) >> > + MEN_Z192_CFBUF_ID1_SHIFT; same here > + > + if (id & MEN_Z192_CFBUF_S_RTR) > + cf->can_id |= CAN_RTR_FLAG; > + } > + > + cf->can_dlc = get_can_dlc(length); > + > + /* remote transmission request frame contains no data field even if the > + * data length is set to a value > 0 > + */ > + if (!(cf->can_id & CAN_RTR_FLAG) && cf->can_dlc) { > + *(__be32 *)cf->data = cpu_to_be32(readl(&cf_buf->data[0])); > + if (cf->can_dlc > 4) > + *(__be32 *)(cf->data + 4) = > + cpu_to_be32(readl(&cf_buf->data[1])); > + } > + > + if (z192_echo_skb_matches(priv, skb)) { > + stats->tx_packets++; > + stats->tx_bytes += z192_get_echo_skb(priv); > + kfree_skb(skb); > + goto led_event; > + } > + > + stats->rx_bytes += cf->can_dlc; > + stats->rx_packets++; > + netif_receive_skb(skb); > + > +led_event: > + can_led_event(ndev, CAN_LED_EVENT_RX); > + > + return 1; > +} > + > +static int men_z192_poll(struct napi_struct *napi, int quota) > +{ > + struct net_device *ndev = napi->dev; > + struct men_z192 *priv = netdev_priv(ndev); > + struct men_z192_regs __iomem *regs = priv->regs; > + int work_done = 0; > + u32 frame_cnt; > + u32 status; > + > + status = readl(®s->rx_tx_sts); > + > + frame_cnt = MEN_Z192_RX_BUF_CNT(status); > + > + while (frame_cnt-- && (work_done < quota)) { > + work_done += men_z192_read_frame(ndev, 0); > + men_z192_ack_rx_pkg(priv, 1); > + } > + > + if (work_done < quota) { > + napi_complete(napi); > + men_z192_set_int(priv, MEN_Z192_CAN_NAPI_EN); > + } > + > + return work_done; > +} > + > +static int men_z192_xmit(struct sk_buff *skb, struct net_device *ndev) > +{ > + struct can_frame *cf = (struct can_frame *)skb->data; > + struct men_z192 *priv = netdev_priv(ndev); > + struct men_z192_regs __iomem *regs = priv->regs; > + struct men_z192_cf_buf __iomem *cf_buf; > + int status; > + u32 id; > + > + if (can_dropped_invalid_skb(ndev, skb)) > + return NETDEV_TX_OK; > + > + status = readl(®s->rx_tx_sts); > + > + if (MEN_Z192_TX_BUF_CNT(status) == 254) { please use a define for the 254 > + netif_stop_queue(ndev); > + netdev_err(ndev, "not enough space in TX buffer\n"); > + > + return NETDEV_TX_BUSY; > + } > + > + cf_buf = priv->dev_base + MEN_Z192_TX_BUF_START; > + > + if (cf->can_id & CAN_EFF_FLAG) { > + /* Extended frame */ > + id = ((cf->can_id & CAN_EFF_MASK) << > + MEN_Z192_CFBUF_ID2_SHIFT) & MEN_Z192_CFBUF_ID2; > + > + id |= (((cf->can_id & CAN_EFF_MASK) >> > + (CAN_EFF_ID_BITS - CAN_SFF_ID_BITS)) << > + MEN_Z192_CFBUF_ID1_SHIFT) & MEN_Z192_CFBUF_ID1; > + > + id |= MEN_Z192_CFBUF_IDE | MEN_Z192_CFBUF_SRR; use FIELD_GET()/FIELD_PREP() > + > + if (cf->can_id & CAN_RTR_FLAG) > + id |= MEN_Z192_CFBUF_E_RTR; > + } else { > + /* Standard frame */ > + id = ((cf->can_id & CAN_SFF_MASK) << > + MEN_Z192_CFBUF_ID1_SHIFT) & MEN_Z192_CFBUF_ID1; same here > + > + if (cf->can_id & CAN_RTR_FLAG) > + id |= MEN_Z192_CFBUF_S_RTR; > + } > + > + writel(id, &cf_buf->can_id); > + writel(cf->can_dlc, &cf_buf->length); > + > + if (!(cf->can_id & CAN_RTR_FLAG) && cf->can_dlc) { > + writel(be32_to_cpup((__be32 *)(cf->data)), &cf_buf->data[0]); > + if (cf->can_dlc > 4) > + writel(be32_to_cpup((__be32 *)(cf->data + 4)), > + &cf_buf->data[1]); > + } > + > + /* The 16z192 CAN IP doesn't have TX-done interrupts, so local > + * loopback is emulated using ECHO skbs. Add the skb to the ECHO > + * stack. Upon packet reception, check if the ECHO skb and received > + * skb match, and use that to wake the queue. > + */ > + z192_put_echo_skb(priv, skb); > + > + /* be sure everything is written to the > + * device before acknowledge the data. ...triggerring the transmission? You have to stop the queue, if the buffer in the device is full. > + */ > + wmb(); > + > + /* trigger the transmission */ > + men_z192_ack_tx_pkg(priv, 1); > + > + can_led_event(ndev, CAN_LED_EVENT_TX); > + > + return NETDEV_TX_OK; > +} > + > +static void men_z192_err_interrupt(struct net_device *ndev, u32 status) > +{ > + struct net_device_stats *stats = &ndev->stats; > + struct men_z192 *priv = netdev_priv(ndev); > + struct can_berr_counter bec; > + struct can_frame *cf; > + struct sk_buff *skb; > + enum can_state rx_state = 0, tx_state = 0; > + > + skb = alloc_can_err_skb(ndev, &cf); > + if (unlikely(!skb)) > + return; please do the stats handling, regardless if the skb was allocated or not. > + > + /* put the rx/tx error counter to > + * the additional controller specific > + * section of the error frame. > + */ > + men_z192_get_berr_counter(ndev, &bec); > + cf->data[6] = bec.txerr; > + cf->data[7] = bec.rxerr; > + > + /* overrun interrupt */ > + if (status & MEN_Z192_RFLG_OVRF) { > + cf->can_id |= CAN_ERR_CRTL; > + cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; > + stats->rx_over_errors++; > + stats->rx_errors++; > + } > + > + /* bus change interrupt */ > + if (status & MEN_Z192_RFLG_CSCIF) { > + rx_state = bus_state_map[MEN_Z192_GET_RSTATE(status)]; > + tx_state = bus_state_map[MEN_Z192_GET_TSTATE(status)]; > + can_change_state(ndev, cf, tx_state, rx_state); > + > + if (priv->can.state == CAN_STATE_BUS_OFF) > + can_bus_off(ndev); > + } > + > + stats->rx_packets++; > + stats->rx_bytes += cf->can_dlc; > + netif_receive_skb(skb); > +} > + > +static irqreturn_t men_z192_isr(int irq, void *dev_id) > +{ > + struct net_device *ndev = dev_id; > + struct men_z192 *priv = netdev_priv(ndev); > + struct men_z192_regs __iomem *regs = priv->regs; > + bool handled = false; > + u32 irq_flags; > + u32 status; > + > + status = readl(®s->rx_tx_sts); > + > + irq_flags = status & MEN_Z192_IRQ_FLAGS_ALL; > + if (!irq_flags) > + goto out; > + > + /* It is safe to write to RX_TS_STS[15:0] */ > + writel(irq_flags, ®s->rx_tx_sts); > + > + if (irq_flags & MEN_Z192_TFLG_TXIF) { > + netif_wake_queue(ndev); > + handled = true; > + } > + > + /* handle errors */ > + if ((irq_flags & MEN_Z192_RFLG_OVRF) || > + (irq_flags & MEN_Z192_RFLG_CSCIF)) { > + men_z192_err_interrupt(ndev, status); > + handled = true; > + } > + > + /* schedule NAPI if: > + * - rx IRQ > + * - rx timeout IRQ > + */ > + if ((irq_flags & MEN_Z192_RFLG_RXIF) || > + (irq_flags & MEN_Z192_RFLG_TOUTF)) { > + men_z192_set_int(priv, MEN_Z192_CAN_NAPI_DIS); > + napi_schedule(&priv->napi); > + handled = true; > + } > + > +out: > + return IRQ_RETVAL(handled); > +} > + > +static int men_z192_set_bittiming(struct net_device *ndev) > +{ > + struct men_z192 *priv = netdev_priv(ndev); > + const struct can_bittiming *bt = &priv->can.bittiming; > + unsigned long flags; > + u32 ctlbtr; > + int ret = 0; > + > + spin_lock_irqsave(&priv->lock, flags); > + > + ctlbtr = readl(&priv->regs->ctl_btr); > + > + if (!(ctlbtr & MEN_Z192_CTL1_INITAK)) { > + netdev_alert(ndev, > + "cannot set bittiminig while in running mode\n"); > + ret = -EPERM; > + goto out_restore; > + } > + > + ctlbtr &= ~(MEN_Z192_BTR0_BRP(0x3f) | > + MEN_Z192_BTR0_SJW(0x03) | > + MEN_Z192_BTR1_TSEG1(0x0f) | > + MEN_Z192_BTR1_TSEG2(0x07) | > + MEN_Z192_CTL1_LISTEN | > + MEN_Z192_CTL1_LOOPB | > + MEN_Z192_BTR1_SAMP); > + > + ctlbtr |= MEN_Z192_BTR0_BRP(bt->brp - 1) | > + MEN_Z192_BTR0_SJW(bt->sjw - 1) | > + MEN_Z192_BTR1_TSEG1(bt->phase_seg1 + bt->prop_seg - 1) | > + MEN_Z192_BTR1_TSEG2(bt->phase_seg2 - 1); FIELD_PREP() > + > + if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) > + ctlbtr |= MEN_Z192_BTR1_SAMP; > + > + if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) > + ctlbtr |= MEN_Z192_CTL1_LISTEN; > + > + if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) > + ctlbtr |= MEN_Z192_CTL1_LOOPB; > + > + netdev_dbg(ndev, "CTL_BTR=0x%08x\n", ctlbtr); > + > + writel(ctlbtr, &priv->regs->ctl_btr); > + > +out_restore: > + spin_unlock_irqrestore(&priv->lock, flags); > + > + return ret; > +} > + > +static void men_z192_init_idac(struct net_device *ndev) > +{ > + struct men_z192 *priv = netdev_priv(ndev); > + struct men_z192_regs __iomem *regs = priv->regs; > + > + /* hardware filtering (accept everything) */ > + writel(0x00000000, ®s->idar_0_to_3); > + writel(0x00000000, ®s->idar_4_to_7); > + writel(0xffffffff, ®s->idmr_0_to_3); > + writel(0xffffffff, ®s->idmr_4_to_7); > +} > + > +static void men_z192_set_can_state(struct net_device *ndev) > +{ > + struct men_z192 *priv = netdev_priv(ndev); > + struct men_z192_regs __iomem *regs = priv->regs; > + enum can_state rx_state, tx_state; > + u32 status; > + > + status = readl(®s->rx_tx_sts); > + > + rx_state = bus_state_map[MEN_Z192_GET_RSTATE(status)]; > + tx_state = bus_state_map[MEN_Z192_GET_TSTATE(status)]; > + > + can_change_state(ndev, NULL, tx_state, rx_state); > +} > + > +static int men_z192_start(struct net_device *ndev) > +{ > + struct men_z192 *priv = netdev_priv(ndev); > + int ret; > + > + ret = men_z192_req_init_mode(priv); > + if (ret) > + return ret; > + > + ret = men_z192_set_bittiming(ndev); > + if (ret) > + return ret; > + > + ret = men_z192_req_run_mode(priv); > + if (ret) > + return ret; > + > + men_z192_init_idac(ndev); > + > + /* The 16z192 CAN IP does not reset the can bus state if we enter the > + * init mode. There is also no software reset to reset the state > + * machine. We need to read the current state, and inform the upper > + * layer about the current state. > + */ > + men_z192_set_can_state(ndev); > + > + men_z192_set_int(priv, MEN_Z192_CAN_EN); > + > + return 0; > +} > + > +static int men_z192_open(struct net_device *ndev) > +{ > + struct men_z192 *priv = netdev_priv(ndev); > + int ret; > + > + ret = open_candev(ndev); > + if (ret) > + return ret; > + > + ret = request_irq(ndev->irq, men_z192_isr, IRQF_SHARED, > + ndev->name, ndev); > + if (ret) > + goto out_close; > + > + ret = men_z192_start(ndev); > + if (ret) > + goto out_free_irq; > + > + napi_enable(&priv->napi); > + netif_start_queue(ndev); > + > + can_led_event(ndev, CAN_LED_EVENT_OPEN); > + > + return 0; > + > +out_free_irq: > + free_irq(ndev->irq, ndev); > +out_close: > + close_candev(ndev); > + return ret; > +} > + > +static int men_z192_stop(struct net_device *ndev) > +{ > + struct men_z192 *priv = netdev_priv(ndev); > + int ret; > + > + men_z192_set_int(priv, MEN_Z192_CAN_DIS); > + > + ret = men_z192_req_init_mode(priv); > + if (ret) > + return ret; > + > + priv->can.state = CAN_STATE_STOPPED; > + > + can_led_event(ndev, CAN_LED_EVENT_STOP); > + > + return 0; > +} > + > +static int men_z192_close(struct net_device *ndev) > +{ > + struct men_z192 *priv = netdev_priv(ndev); > + int ret; > + > + netif_stop_queue(ndev); > + > + napi_disable(&priv->napi); > + > + ret = men_z192_stop(ndev); > + > + free_irq(ndev->irq, ndev); > + > + /* drop all outstanding echo skbs */ > + skb_queue_purge(&priv->echoq); > + > + close_candev(ndev); > + > + return ret; > +} > + > +static int men_z192_set_mode(struct net_device *ndev, enum can_mode mode) > +{ > + int ret; > + > + switch (mode) { > + case CAN_MODE_START: > + ret = men_z192_start(ndev); > + if (ret) > + return ret; > + > + netif_wake_queue(ndev); > + break; > + default: > + return -EOPNOTSUPP; > + } > + > + return 0; > +} > + > +static const struct net_device_ops men_z192_netdev_ops = { > + .ndo_open = men_z192_open, > + .ndo_stop = men_z192_close, > + .ndo_start_xmit = men_z192_xmit, > + .ndo_change_mtu = can_change_mtu, > +}; > + > +static int men_z192_verify_buf_lvl(int buffer_lvl) > +{ > + if (buffer_lvl < MEN_Z192_MIN_BUF_LVL || > + buffer_lvl > MEN_Z192_MAX_BUF_LVL) > + return -EINVAL; > + > + return 0; > +} > + > +static void men_z192_set_buf_lvl_irq(struct net_device *ndev, int rxlvl, > + int txlvl) > +{ > + struct men_z192 *priv = netdev_priv(ndev); > + struct men_z192_regs __iomem *regs = priv->regs; > + int reg_val; > + > + if (men_z192_verify_buf_lvl(rxlvl)) > + reg_val = MEN_Z192_RX_BUF_LVL_DEF & MEN_Z192_RX_BUF_LVL; > + else > + reg_val = rxlvl & MEN_Z192_RX_BUF_LVL; > + > + if (men_z192_verify_buf_lvl(txlvl)) > + reg_val |= (MEN_Z192_TX_BUF_LVL_DEF << 16) & > + MEN_Z192_TX_BUF_LVL; > + else > + reg_val |= (txlvl << 16) & MEN_Z192_TX_BUF_LVL; > + > + dev_info(priv->dev, "RX IRQ Level: %d TX IRQ Level: %d\n", > + rxlvl, txlvl); > + > + writel(reg_val, ®s->buf_lvl); > +} > + > +static void men_z192_set_rx_tout(struct net_device *ndev, int tout) > +{ > + struct men_z192 *priv = netdev_priv(ndev); > + struct men_z192_regs __iomem *regs = priv->regs; > + int reg_val; > + > + if (tout < MEN_Z192_RX_TOUT_MIN || tout > MEN_Z192_RX_TOUT_MAX) > + reg_val = MEN_Z192_RX_TOUT_MAX; > + else > + reg_val = tout; > + > + dev_info(priv->dev, "RX IRQ timeout set to: %d\n", reg_val); > + > + writel(reg_val, ®s->rx_timeout); > +} > + > +static int men_z192_register(struct net_device *ndev) > +{ > + struct men_z192 *priv = netdev_priv(ndev); > + struct men_z192_regs __iomem *regs = priv->regs; > + u32 ctl_btr; > + int ret; > + > + /* The CAN controller should be always enabled. > + * There is no way to enable it if disabled. > + */ > + ctl_btr = readl(®s->ctl_btr); > + if (!(ctl_btr & MEN_Z192_CTL1_CANE)) > + return -ENODEV; > + > + men_z192_set_buf_lvl_irq(ndev, rxlvl, txlvl); > + men_z192_set_rx_tout(ndev, rx_timeout); > + > + ret = men_z192_req_init_mode(priv); > + if (ret) { > + dev_err(priv->dev, "failed to request init mode\n"); > + return ret; > + } > + > + return register_candev(ndev); > +} > + > +static int men_z192_probe(struct mcb_device *mdev, > + const struct mcb_device_id *id) > +{ > + struct device *dev = &mdev->dev; > + struct men_z192 *priv; > + struct net_device *ndev; > + void __iomem *dev_base; > + struct resource *mem; > + u32 timebase; > + int ret = 0; > + int irq; > + > + mem = mcb_request_mem(mdev, dev_name(dev)); > + if (IS_ERR(mem)) { > + dev_err(dev, "failed to request device memory"); > + return PTR_ERR(mem); > + } > + > + dev_base = ioremap(mem->start, resource_size(mem)); > + if (!dev_base) { > + dev_err(dev, "failed to ioremap device memory"); > + ret = -ENXIO; > + goto out_release; > + } > + > + irq = mcb_get_irq(mdev); > + if (irq <= 0) { > + ret = -ENODEV; > + goto out_unmap; > + } > + > + ndev = alloc_candev(sizeof(struct men_z192), 0); > + if (!ndev) { > + dev_err(dev, "failed to allocat the can device"); > + ret = -ENOMEM; > + goto out_unmap; > + } > + > + ndev->netdev_ops = &men_z192_netdev_ops; > + ndev->irq = irq; > + ndev->flags |= IFF_ECHO; > + > + priv = netdev_priv(ndev); > + priv->ndev = ndev; > + priv->dev = dev; > + > + priv->mem = mem; > + priv->dev_base = dev_base; > + priv->regs = priv->dev_base + MEN_Z192_REGS_OFFS; > + > + timebase = readl(&priv->regs->timebase); > + if (!timebase) { > + dev_err(dev, "invalid timebase configured (timebase=%d)\n", > + timebase); > + ret = -EINVAL; > + goto out_free_candev; > + } > + > + priv->can.clock.freq = timebase; > + priv->can.bittiming_const = &men_z192_bittiming_const; > + priv->can.do_set_mode = men_z192_set_mode; > + priv->can.do_get_berr_counter = men_z192_get_berr_counter; > + priv->can.ctrlmode_supported = CAN_CTRLMODE_LISTENONLY | > + CAN_CTRLMODE_3_SAMPLES | > + CAN_CTRLMODE_LOOPBACK; > + > + spin_lock_init(&priv->lock); > + > + netif_napi_add(ndev, &priv->napi, men_z192_poll, > + NAPI_POLL_WEIGHT); > + > + skb_queue_head_init(&priv->echoq); > + > + mcb_set_drvdata(mdev, ndev); > + SET_NETDEV_DEV(ndev, dev); > + > + ret = men_z192_register(ndev); > + if (ret) { > + dev_err(dev, "failed to register CAN device"); > + goto out_free_candev; > + } > + > + devm_can_led_init(ndev); > + > + dev_info(dev, "MEN 16z192 CAN driver successfully registered\n"); > + > + return 0; > + > +out_free_candev: > + netif_napi_del(&priv->napi); > + free_candev(ndev); > +out_unmap: > + iounmap(dev_base); > +out_release: > + mcb_release_mem(mem); > + return ret; > +} > + > +static void men_z192_remove(struct mcb_device *mdev) > +{ > + struct net_device *ndev = mcb_get_drvdata(mdev); > + struct men_z192 *priv = netdev_priv(ndev); > + > + unregister_candev(ndev); > + netif_napi_del(&priv->napi); > + > + iounmap(priv->dev_base); > + mcb_release_mem(priv->mem); > + > + free_candev(ndev); > +} > + > +static const struct mcb_device_id men_z192_ids[] = { > + { .device = 0xc0 }, > + { } > +}; > +MODULE_DEVICE_TABLE(mcb, men_z192_ids); > + > +static struct mcb_driver men_z192_driver = { > + .driver = { > + .name = DRV_NAME, > + .owner = THIS_MODULE, > + }, > + .probe = men_z192_probe, > + .remove = men_z192_remove, > + .id_table = men_z192_ids, > +}; > +module_mcb_driver(men_z192_driver); > + > +MODULE_AUTHOR("Andreas Werner <andreas.werner@xxxxxx>"); > +MODULE_DESCRIPTION("MEN 16z192 CAN Controller"); > +MODULE_LICENSE("GPL v2"); > +MODULE_ALIAS("mcb:16z192"); > +MODULE_IMPORT_NS(MCB); > > base-commit: dcb7fd82c75ee2d6e6f9d8cc71c52519ed52e258 > regards, Marc -- Pengutronix e.K. | Marc Kleine-Budde | Embedded Linux | https://www.pengutronix.de | Vertretung West/Dortmund | Phone: +49-231-2826-924 | Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
Attachment:
signature.asc
Description: OpenPGP digital signature