On Fri, Jul 6, 2018 at 7:50 PM Anssi Hannula <anssi.hannula@xxxxxxxxxx> wrote: > > Add support for Xilinx CAN FD core. > > The major difference from the previously supported cores is that there > are TX mailboxes instead of a TX FIFO and the RX FIFO access method is > different. > > We only transmit one frame at a time to prevent the HW from reordering > frames (it uses CAN ID priority order). > > Support for CAN FD protocol is not added yet. > > v2: Removed unnecessary "rx-mode" DT property and wrapped some long > lines. > > Signed-off-by: Anssi Hannula <anssi.hannula@xxxxxxxxxx> > Cc: Michal Simek <michal.simek@xxxxxxxxxx> > --- > drivers/net/can/xilinx_can.c | 303 ++++++++++++++++++++++++++++++++++++------- > 1 file changed, 259 insertions(+), 44 deletions(-) > > diff --git a/drivers/net/can/xilinx_can.c b/drivers/net/can/xilinx_can.c > index b2b55eaea872..7f8c9262c210 100644 > --- a/drivers/net/can/xilinx_can.c > +++ b/drivers/net/can/xilinx_can.c > @@ -2,7 +2,7 @@ > * > * Copyright (C) 2012 - 2014 Xilinx, Inc. > * Copyright (C) 2009 PetaLogix. All rights reserved. > - * Copyright (C) 2017 Sandvik Mining and Construction Oy > + * Copyright (C) 2017 - 2018 Sandvik Mining and Construction Oy > * > * Description: > * This driver is developed for Axi CAN IP and for Zynq CANPS Controller. > @@ -51,8 +51,18 @@ enum xcan_reg { > XCAN_ISR_OFFSET = 0x1C, /* Interrupt status */ > XCAN_IER_OFFSET = 0x20, /* Interrupt enable */ > XCAN_ICR_OFFSET = 0x24, /* Interrupt clear */ > + > + /* not on CAN FD cores */ > XCAN_TXFIFO_OFFSET = 0x30, /* TX FIFO base */ > XCAN_RXFIFO_OFFSET = 0x50, /* RX FIFO base */ > + XCAN_AFR_OFFSET = 0x60, /* Acceptance Filter */ > + > + /* only on CAN FD cores */ > + XCAN_TRR_OFFSET = 0x0090, /* TX Buffer Ready Request */ > + XCAN_AFR_EXT_OFFSET = 0x00E0, /* Acceptance Filter */ > + XCAN_FSR_OFFSET = 0x00E8, /* RX FIFO Status */ > + XCAN_TXMSG_BASE_OFFSET = 0x0100, /* TX Message Space */ > + XCAN_RXMSG_BASE_OFFSET = 0x1100, /* RX Message Space */ > }; > > #define XCAN_FRAME_ID_OFFSET(frame_base) ((frame_base) + 0x00) > @@ -60,6 +70,15 @@ enum xcan_reg { > #define XCAN_FRAME_DW1_OFFSET(frame_base) ((frame_base) + 0x08) > #define XCAN_FRAME_DW2_OFFSET(frame_base) ((frame_base) + 0x0C) > > +#define XCAN_CANFD_FRAME_SIZE 0x48 > +#define XCAN_TXMSG_FRAME_OFFSET(n) (XCAN_TXMSG_BASE_OFFSET + \ > + XCAN_CANFD_FRAME_SIZE * (n)) > +#define XCAN_RXMSG_FRAME_OFFSET(n) (XCAN_RXMSG_BASE_OFFSET + \ > + XCAN_CANFD_FRAME_SIZE * (n)) > + > +/* the single TX mailbox used by this driver on CAN FD HW */ > +#define XCAN_TX_MAILBOX_IDX 0 > + > /* CAN register bit masks - XCAN_<REG>_<BIT>_MASK */ > #define XCAN_SRR_CEN_MASK 0x00000002 /* CAN enable */ > #define XCAN_SRR_RESET_MASK 0x00000001 /* Soft Reset the CAN core */ > @@ -69,6 +88,9 @@ enum xcan_reg { > #define XCAN_BTR_SJW_MASK 0x00000180 /* Synchronous jump width */ > #define XCAN_BTR_TS2_MASK 0x00000070 /* Time segment 2 */ > #define XCAN_BTR_TS1_MASK 0x0000000F /* Time segment 1 */ > +#define XCAN_BTR_SJW_MASK_CANFD 0x000F0000 /* Synchronous jump width */ > +#define XCAN_BTR_TS2_MASK_CANFD 0x00000F00 /* Time segment 2 */ > +#define XCAN_BTR_TS1_MASK_CANFD 0x0000003F /* Time segment 1 */ > #define XCAN_ECR_REC_MASK 0x0000FF00 /* Receive error counter */ > #define XCAN_ECR_TEC_MASK 0x000000FF /* Transmit error counter */ > #define XCAN_ESR_ACKER_MASK 0x00000010 /* ACK error */ > @@ -82,6 +104,7 @@ enum xcan_reg { > #define XCAN_SR_NORMAL_MASK 0x00000008 /* Normal mode */ > #define XCAN_SR_LBACK_MASK 0x00000002 /* Loop back mode */ > #define XCAN_SR_CONFIG_MASK 0x00000001 /* Configuration mode */ > +#define XCAN_IXR_RXMNF_MASK 0x00020000 /* RX match not finished */ > #define XCAN_IXR_TXFEMP_MASK 0x00004000 /* TX FIFO Empty */ > #define XCAN_IXR_WKUP_MASK 0x00000800 /* Wake up interrupt */ > #define XCAN_IXR_SLP_MASK 0x00000400 /* Sleep interrupt */ > @@ -99,10 +122,15 @@ enum xcan_reg { > #define XCAN_IDR_ID2_MASK 0x0007FFFE /* Extended message ident */ > #define XCAN_IDR_RTR_MASK 0x00000001 /* Remote TX request */ > #define XCAN_DLCR_DLC_MASK 0xF0000000 /* Data length code */ > +#define XCAN_FSR_FL_MASK 0x00003F00 /* RX Fill Level */ > +#define XCAN_FSR_IRI_MASK 0x00000080 /* RX Increment Read Index */ > +#define XCAN_FSR_RI_MASK 0x0000001F /* RX Read Index */ > > /* CAN register bit shift - XCAN_<REG>_<BIT>_SHIFT */ > #define XCAN_BTR_SJW_SHIFT 7 /* Synchronous jump width */ > #define XCAN_BTR_TS2_SHIFT 4 /* Time segment 2 */ > +#define XCAN_BTR_SJW_SHIFT_CANFD 16 /* Synchronous jump width */ > +#define XCAN_BTR_TS2_SHIFT_CANFD 8 /* Time segment 2 */ > #define XCAN_IDR_ID1_SHIFT 21 /* Standard Messg Identifier */ > #define XCAN_IDR_ID2_SHIFT 1 /* Extended Message Identifier */ > #define XCAN_DLCR_DLC_SHIFT 28 /* Data length code */ > @@ -114,11 +142,23 @@ enum xcan_reg { > > /* TX-FIFO-empty interrupt available */ > #define XCAN_FLAG_TXFEMP 0x0001 > +/* RX Match Not Finished interrupt available */ > +#define XCAN_FLAG_RXMNF 0x0002 > +/* Extended acceptance filters with control at 0xE0 */ > +#define XCAN_FLAG_EXT_FILTERS 0x0004 > +/* TX mailboxes instead of TX FIFO */ > +#define XCAN_FLAG_TX_MAILBOXES 0x0008 > +/* RX FIFO with each buffer in separate registers at 0x1100 > + * instead of the regular FIFO at 0x50 > + */ > +#define XCAN_FLAG_RX_FIFO_MULTI 0x0010 > > struct xcan_devtype_data { > unsigned int flags; > const struct can_bittiming_const *bittiming_const; > const char *bus_clk_name; > + unsigned int btr_ts2_shift; > + unsigned int btr_sjw_shift; > }; > > /** > @@ -169,6 +209,18 @@ static const struct can_bittiming_const xcan_bittiming_const = { > .brp_inc = 1, > }; > > +static const struct can_bittiming_const xcan_bittiming_const_canfd = { > + .name = DRIVER_NAME, > + .tseg1_min = 1, > + .tseg1_max = 64, > + .tseg2_min = 1, > + .tseg2_max = 16, > + .sjw_max = 16, > + .brp_min = 1, > + .brp_max = 256, > + .brp_inc = 1, > +}; Where is this used? > + > /** > * xcan_write_reg_le - Write a value to the device register little endian > * @priv: Driver private data structure > @@ -223,6 +275,23 @@ static u32 xcan_read_reg_be(const struct xcan_priv *priv, enum xcan_reg reg) > return ioread32be(priv->reg_base + reg); > } > > +/** > + * xcan_rx_int_mask - Get the mask for the receive interrupt > + * @priv: Driver private data structure > + * > + * Return: The receive interrupt mask used by the driver on this HW > + */ > +static u32 xcan_rx_int_mask(const struct xcan_priv *priv) > +{ > + /* RXNEMP is better suited for our use case as it cannot be cleared > + * while the FIFO is non-empty, but CAN FD HW does not have it > + */ > + if (priv->devtype.flags & XCAN_FLAG_RX_FIFO_MULTI) > + return XCAN_IXR_RXOK_MASK; > + else > + return XCAN_IXR_RXNEMP_MASK; > +} > + > /** > * set_reset_mode - Resets the CAN device mode > * @ndev: Pointer to net_device structure > @@ -287,10 +356,10 @@ static int xcan_set_bittiming(struct net_device *ndev) > btr1 = (bt->prop_seg + bt->phase_seg1 - 1); > > /* Setting Time Segment 2 in BTR Register */ > - btr1 |= (bt->phase_seg2 - 1) << XCAN_BTR_TS2_SHIFT; > + btr1 |= (bt->phase_seg2 - 1) << priv->devtype.btr_ts2_shift; > > /* Setting Synchronous jump width in BTR Register */ > - btr1 |= (bt->sjw - 1) << XCAN_BTR_SJW_SHIFT; > + btr1 |= (bt->sjw - 1) << priv->devtype.btr_sjw_shift; > > priv->write_reg(priv, XCAN_BRPR_OFFSET, btr0); > priv->write_reg(priv, XCAN_BTR_OFFSET, btr1); > @@ -318,6 +387,7 @@ static int xcan_chip_start(struct net_device *ndev) > u32 reg_msr, reg_sr_mask; > int err; > unsigned long timeout; > + u32 ier; > > /* Check if it is in reset mode */ > err = set_reset_mode(ndev); > @@ -329,11 +399,15 @@ static int xcan_chip_start(struct net_device *ndev) > return err; > > /* Enable interrupts */ > - priv->write_reg(priv, XCAN_IER_OFFSET, > - XCAN_IXR_TXOK_MASK | XCAN_IXR_BSOFF_MASK | > - XCAN_IXR_WKUP_MASK | XCAN_IXR_SLP_MASK | > - XCAN_IXR_RXNEMP_MASK | XCAN_IXR_ERROR_MASK | > - XCAN_IXR_RXOFLW_MASK | XCAN_IXR_ARBLST_MASK); > + ier = XCAN_IXR_TXOK_MASK | XCAN_IXR_BSOFF_MASK | > + XCAN_IXR_WKUP_MASK | XCAN_IXR_SLP_MASK | > + XCAN_IXR_ERROR_MASK | XCAN_IXR_RXOFLW_MASK | > + XCAN_IXR_ARBLST_MASK | xcan_rx_int_mask(priv); > + > + if (priv->devtype.flags & XCAN_FLAG_RXMNF) > + ier |= XCAN_IXR_RXMNF_MASK; > + > + priv->write_reg(priv, XCAN_IER_OFFSET, ier); > > /* Check whether it is loopback mode or normal mode */ > if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) { > @@ -344,6 +418,12 @@ static int xcan_chip_start(struct net_device *ndev) > reg_sr_mask = XCAN_SR_NORMAL_MASK; > } > > + /* enable the first extended filter, if any, as cores with extended > + * filtering default to non-receipt if all filters are disabled > + */ > + if (priv->devtype.flags & XCAN_FLAG_EXT_FILTERS) > + priv->write_reg(priv, XCAN_AFR_EXT_OFFSET, 0x00000001); > + > priv->write_reg(priv, XCAN_MSR_OFFSET, reg_msr); > priv->write_reg(priv, XCAN_SRR_OFFSET, XCAN_SRR_CEN_MASK); > > @@ -439,13 +519,15 @@ static void xcan_write_frame(struct xcan_priv *priv, struct sk_buff *skb, > data[1] = be32_to_cpup((__be32 *)(cf->data + 4)); > > priv->write_reg(priv, XCAN_FRAME_ID_OFFSET(frame_offset), id); > - /* If the CAN frame is RTR frame this write triggers transmission */ > + /* If the CAN frame is RTR frame this write triggers transmission > + * (not on CAN FD) > + */ > priv->write_reg(priv, XCAN_FRAME_DLC_OFFSET(frame_offset), dlc); > if (!(cf->can_id & CAN_RTR_FLAG)) { > priv->write_reg(priv, XCAN_FRAME_DW1_OFFSET(frame_offset), > data[0]); > /* If the CAN frame is Standard/Extended frame this > - * write triggers transmission > + * write triggers transmission (not on CAN FD) > */ > priv->write_reg(priv, XCAN_FRAME_DW2_OFFSET(frame_offset), > data[1]); > @@ -488,25 +570,60 @@ static int xcan_start_xmit_fifo(struct sk_buff *skb, struct net_device *ndev) > return 0; > } > > +/** > + * xcan_start_xmit_mailbox - Starts the transmission (mailbox mode) > + * > + * Return: 0 on success, -ENOSPC if there is no space > + */ > +static int xcan_start_xmit_mailbox(struct sk_buff *skb, struct net_device *ndev) > +{ > + struct xcan_priv *priv = netdev_priv(ndev); > + unsigned long flags; > + > + if (unlikely(priv->read_reg(priv, XCAN_TRR_OFFSET) & > + BIT(XCAN_TX_MAILBOX_IDX))) > + return -ENOSPC; > + > + can_put_echo_skb(skb, ndev, 0); > + > + spin_lock_irqsave(&priv->tx_lock, flags); > + > + priv->tx_head++; > + > + xcan_write_frame(priv, skb, > + XCAN_TXMSG_FRAME_OFFSET(XCAN_TX_MAILBOX_IDX)); > + > + /* Mark buffer as ready for transmit */ > + priv->write_reg(priv, XCAN_TRR_OFFSET, BIT(XCAN_TX_MAILBOX_IDX)); > + > + netif_stop_queue(ndev); > + > + spin_unlock_irqrestore(&priv->tx_lock, flags); > + > + return 0; > +} > + > /** > * xcan_start_xmit - Starts the transmission > * @skb: sk_buff pointer that contains data to be Txed > * @ndev: Pointer to net_device structure > * > - * This function is invoked from upper layers to initiate transmission. This > - * function uses the next available free txbuff and populates their fields to > - * start the transmission. > + * This function is invoked from upper layers to initiate transmission. > * > * Return: 0 on success and failure value on error > */ > static int xcan_start_xmit(struct sk_buff *skb, struct net_device *ndev) > { > + struct xcan_priv *priv = netdev_priv(ndev); > int ret; > > if (can_dropped_invalid_skb(ndev, skb)) > return NETDEV_TX_OK; > > - ret = xcan_start_xmit_fifo(skb, ndev); > + if (priv->devtype.flags & XCAN_FLAG_TX_MAILBOXES) > + ret = xcan_start_xmit_mailbox(skb, ndev); > + else > + ret = xcan_start_xmit_fifo(skb, ndev); > > if (ret < 0) { > netdev_err(ndev, "BUG!, TX full when queue awake!\n"); > @@ -739,6 +856,17 @@ static void xcan_err_interrupt(struct net_device *ndev, u32 isr) > } > } > > + /* Check for RX Match Not Finished interrupt */ > + if (isr & XCAN_IXR_RXMNF_MASK) { > + stats->rx_dropped++; > + stats->rx_errors++; > + netdev_err(ndev, "RX match not finished, frame discarded\n"); > + if (skb) { > + cf->can_id |= CAN_ERR_CRTL; > + cf->data[1] |= CAN_ERR_CRTL_UNSPEC; > + } > + } > + > /* Check for error interrupt */ > if (isr & XCAN_IXR_ERROR_MASK) { > if (skb) > @@ -822,6 +950,44 @@ static void xcan_state_interrupt(struct net_device *ndev, u32 isr) > priv->can.state = CAN_STATE_ERROR_ACTIVE; > } > > +/** > + * xcan_rx_fifo_get_next_frame - Get register offset of next RX frame > + * > + * Return: Register offset of the next frame in RX FIFO. > + */ > +static int xcan_rx_fifo_get_next_frame(struct xcan_priv *priv) > +{ > + int offset; > + > + if (priv->devtype.flags & XCAN_FLAG_RX_FIFO_MULTI) { > + u32 fsr; > + > + /* clear RXOK before the is-empty check so that any newly > + * received frame will reassert it without a race > + */ > + priv->write_reg(priv, XCAN_ICR_OFFSET, XCAN_IXR_RXOK_MASK); > + > + fsr = priv->read_reg(priv, XCAN_FSR_OFFSET); > + > + /* check if RX FIFO is empty */ > + if (!(fsr & XCAN_FSR_FL_MASK)) > + return -ENOENT; > + > + offset = XCAN_RXMSG_FRAME_OFFSET(fsr & XCAN_FSR_RI_MASK); > + > + } else { > + /* check if RX FIFO is empty */ > + if (!(priv->read_reg(priv, XCAN_ISR_OFFSET) & > + XCAN_IXR_RXNEMP_MASK)) > + return -ENOENT; > + > + /* frames are read from a static offset */ > + offset = XCAN_RXFIFO_OFFSET; > + } > + > + return offset; > +} > + > /** > * xcan_rx_poll - Poll routine for rx packets (NAPI) > * @napi: napi structure pointer > @@ -836,14 +1002,24 @@ static int xcan_rx_poll(struct napi_struct *napi, int quota) > { > struct net_device *ndev = napi->dev; > struct xcan_priv *priv = netdev_priv(ndev); > - u32 isr, ier; > + u32 ier; > int work_done = 0; > - > - isr = priv->read_reg(priv, XCAN_ISR_OFFSET); > - while ((isr & XCAN_IXR_RXNEMP_MASK) && (work_done < quota)) { > - work_done += xcan_rx(ndev, XCAN_RXFIFO_OFFSET); > - priv->write_reg(priv, XCAN_ICR_OFFSET, XCAN_IXR_RXNEMP_MASK); > - isr = priv->read_reg(priv, XCAN_ISR_OFFSET); > + int frame_offset; > + > + while ((frame_offset = xcan_rx_fifo_get_next_frame(priv)) >= 0 && > + (work_done < quota)) { > + work_done += xcan_rx(ndev, frame_offset); > + > + if (priv->devtype.flags & XCAN_FLAG_RX_FIFO_MULTI) > + /* increment read index */ > + priv->write_reg(priv, XCAN_FSR_OFFSET, > + XCAN_FSR_IRI_MASK); > + else > + /* clear rx-not-empty (will actually clear only if > + * empty) > + */ > + priv->write_reg(priv, XCAN_ICR_OFFSET, > + XCAN_IXR_RXNEMP_MASK); > } > > if (work_done) { > @@ -854,7 +1030,7 @@ static int xcan_rx_poll(struct napi_struct *napi, int quota) > if (work_done < quota) { > napi_complete_done(napi, work_done); > ier = priv->read_reg(priv, XCAN_IER_OFFSET); > - ier |= XCAN_IXR_RXNEMP_MASK; > + ier |= xcan_rx_int_mask(priv); > priv->write_reg(priv, XCAN_IER_OFFSET, ier); > } > return work_done; > @@ -953,6 +1129,7 @@ static irqreturn_t xcan_interrupt(int irq, void *dev_id) > struct xcan_priv *priv = netdev_priv(ndev); > u32 isr, ier; > u32 isr_errors; > + u32 rx_int_mask = xcan_rx_int_mask(priv); > > /* Get the interrupt status from Xilinx CAN */ > isr = priv->read_reg(priv, XCAN_ISR_OFFSET); > @@ -972,16 +1149,17 @@ static irqreturn_t xcan_interrupt(int irq, void *dev_id) > > /* Check for the type of error interrupt and Processing it */ > isr_errors = isr & (XCAN_IXR_ERROR_MASK | XCAN_IXR_RXOFLW_MASK | > - XCAN_IXR_BSOFF_MASK | XCAN_IXR_ARBLST_MASK); > + XCAN_IXR_BSOFF_MASK | XCAN_IXR_ARBLST_MASK | > + XCAN_IXR_RXMNF_MASK); > if (isr_errors) { > priv->write_reg(priv, XCAN_ICR_OFFSET, isr_errors); > xcan_err_interrupt(ndev, isr); > } > > /* Check for the type of receive interrupt and Processing it */ > - if (isr & XCAN_IXR_RXNEMP_MASK) { > + if (isr & rx_int_mask) { > ier = priv->read_reg(priv, XCAN_IER_OFFSET); > - ier &= ~XCAN_IXR_RXNEMP_MASK; > + ier &= ~rx_int_mask; > priv->write_reg(priv, XCAN_IER_OFFSET, ier); > napi_schedule(&priv->napi); > } > @@ -1228,14 +1406,27 @@ static const struct dev_pm_ops xcan_dev_pm_ops = { > }; > > static const struct xcan_devtype_data xcan_zynq_data = { > - .flags = XCAN_FLAG_TXFEMP, > .bittiming_const = &xcan_bittiming_const, > + .btr_ts2_shift = XCAN_BTR_TS2_SHIFT, > + .btr_sjw_shift = XCAN_BTR_SJW_SHIFT, > .bus_clk_name = "pclk", > }; > > static const struct xcan_devtype_data xcan_axi_data = { > - .flags = 0, > .bittiming_const = &xcan_bittiming_const, > + .btr_ts2_shift = XCAN_BTR_TS2_SHIFT, > + .btr_sjw_shift = XCAN_BTR_SJW_SHIFT, > + .bus_clk_name = "s_axi_aclk", > +}; > + > +static const struct xcan_devtype_data xcan_canfd_data = { > + .flags = XCAN_FLAG_EXT_FILTERS | > + XCAN_FLAG_RXMNF | > + XCAN_FLAG_TX_MAILBOXES | > + XCAN_FLAG_RX_FIFO_MULTI, > + .bittiming_const = &xcan_bittiming_const, Did you intend to use xcan_bittiming_const_canfd here ? > + .btr_ts2_shift = XCAN_BTR_TS2_SHIFT_CANFD, > + .btr_sjw_shift = XCAN_BTR_SJW_SHIFT_CANFD, > .bus_clk_name = "s_axi_aclk", > }; > > @@ -1243,6 +1434,7 @@ static const struct xcan_devtype_data xcan_axi_data = { > static const struct of_device_id xcan_of_match[] = { > { .compatible = "xlnx,zynq-can-1.0", .data = &xcan_zynq_data }, > { .compatible = "xlnx,axi-can-1.00.a", .data = &xcan_axi_data }, > + { .compatible = "xlnx,canfd-1.0", .data = &xcan_canfd_data }, > { /* end of list */ }, > }; > MODULE_DEVICE_TABLE(of, xcan_of_match); > @@ -1264,7 +1456,10 @@ static int xcan_probe(struct platform_device *pdev) > const struct of_device_id *of_id; > const struct xcan_devtype_data *devtype = &xcan_axi_data; > void __iomem *addr; > - int ret, rx_max, tx_max, tx_fifo_depth; > + int ret; > + int rx_max, tx_max; > + int hw_tx_max, hw_rx_max; > + const char *hw_tx_max_property; > > /* Get the virtual base address for the device */ > res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > @@ -1274,21 +1469,33 @@ static int xcan_probe(struct platform_device *pdev) > goto err; > } > > - ret = of_property_read_u32(pdev->dev.of_node, "tx-fifo-depth", > - &tx_fifo_depth); > - if (ret < 0) > - goto err; > - > - ret = of_property_read_u32(pdev->dev.of_node, "rx-fifo-depth", &rx_max); > - if (ret < 0) > - goto err; > - > of_id = of_match_device(xcan_of_match, &pdev->dev); > if (of_id && of_id->data) > devtype = of_id->data; > > - /* There is no way to directly figure out how many frames have been > - * sent when the TXOK interrupt is processed. If watermark programming > + hw_tx_max_property = devtype->flags & XCAN_FLAG_TX_MAILBOXES ? > + "tx-mailbox-count" : "tx-fifo-depth"; > + > + ret = of_property_read_u32(pdev->dev.of_node, hw_tx_max_property, > + &hw_tx_max); > + if (ret < 0) { > + dev_err(&pdev->dev, "missing %s property\n", > + hw_tx_max_property); > + goto err; > + } > + > + ret = of_property_read_u32(pdev->dev.of_node, "rx-fifo-depth", > + &hw_rx_max); > + if (ret < 0) { > + dev_err(&pdev->dev, > + "missing rx-fifo-depth property (mailbox mode is not supported)\n"); > + goto err; > + } > + > + /* With TX FIFO: > + * > + * There is no way to directly figure out how many frames have been > + * sent when the TXOK interrupt is processed. If TXFEMP > * is supported, we can have 2 frames in the FIFO and use TXFEMP > * to determine if 1 or 2 frames have been sent. > * Theoretically we should be able to use TXFWMEMP to determine up > @@ -1297,12 +1504,20 @@ static int xcan_probe(struct platform_device *pdev) > * than 2 frames in FIFO) is set anyway with no TXOK (a frame was > * sent), which is not a sensible state - possibly TXFWMEMP is not > * completely synchronized with the rest of the bits? > + * > + * With TX mailboxes: > + * > + * HW sends frames in CAN ID priority order. To preserve FIFO ordering > + * we submit frames one at a time. > */ > - if (devtype->flags & XCAN_FLAG_TXFEMP) > - tx_max = min(tx_fifo_depth, 2); > + if (!(devtype->flags & XCAN_FLAG_TX_MAILBOXES) && > + (devtype->flags & XCAN_FLAG_TXFEMP)) > + tx_max = min(hw_tx_max, 2); > else > tx_max = 1; > > + rx_max = hw_rx_max; > + > /* Create a CAN device instance */ > ndev = alloc_candev(sizeof(struct xcan_priv), tx_max); > if (!ndev) > @@ -1373,9 +1588,9 @@ static int xcan_probe(struct platform_device *pdev) > > pm_runtime_put(&pdev->dev); > > - netdev_dbg(ndev, "reg_base=0x%p irq=%d clock=%d, tx fifo depth: actual %d, using %d\n", > - priv->reg_base, ndev->irq, priv->can.clock.freq, > - tx_fifo_depth, priv->tx_max); > + netdev_dbg(ndev, "reg_base=0x%p irq=%d clock=%d, tx buffers: actual %d, using %d\n", > + priv->reg_base, ndev->irq, priv->can.clock.freq, > + hw_tx_max, priv->tx_max); > > return 0; > > -- > 2.16.3 > > -- > To unsubscribe from this list: send the line "unsubscribe linux-can" in > the body of a message to majordomo@xxxxxxxxxxxxxxx > More majordomo info at http://vger.kernel.org/majordomo-info.html