[PATCH v4 2/2] can: m_can: add Bosch M_CAN controller support

Marc Kleine-Budde mkl at pengutronix.de
Tue Jul 15 00:29:54 PDT 2014


On 07/15/2014 05:33 AM, Dong Aisheng wrote:
> On Mon, Jul 14, 2014 at 02:13:46PM +0200, Marc Kleine-Budde wrote:
>> On 07/14/2014 01:40 PM, Dong Aisheng wrote:
>>> The patch adds the basic CAN TX/RX function support for Bosch M_CAN controller.
>>> For TX, only one dedicated tx buffer is used for sending data.
>>> For RX, RXFIFO 0 is used for receiving data to avoid overflow.
>>> Rx FIFO 1 and Rx Buffers are not used currently, as well as Tx Event FIFO.
>>>
>>> Due to the message ram can be shared by multi m_can instances
>>> and the fifo element is configurable which is SoC dependant,
>>> the design is to parse the message ram related configuration data from device
>>> tree rather than hardcode define it in driver which can make the message
>>> ram sharing fully transparent to M_CAN controller driver,
>>> then we can gain better driver maintainability and future features upgrade.
>>>
>>> M_CAN also supports CANFD protocol features like data payload up to 64 bytes
>>> and bitrate switch at runtime, however, this patch still does not add the
>>> support for these features.
>>>
>>> Cc: Wolfgang Grandegger <wg at grandegger.com>
>>> Cc: Marc Kleine-Budde <mkl at pengutronix.de>
>>> Cc: Mark Rutland <mark.rutland at arm.com>
>>> Cc: Oliver Hartkopp <socketcan at hartkopp.net>
>>> Cc: Varka Bhadram <varkabhadram at gmail.com>
>>> Signed-off-by: Dong Aisheng <b29396 at freescale.com>
>>
>> [...]
>>
>>> +static inline u32 m_can_fifo_read(const struct m_can_priv *priv,
>>> +				  u32 fgi, unsigned int offset)
>>> +{
>>> +	return readl(priv->mram_base + priv->mcfg[MRAM_RXF0].off +
>>> +				fgi * RXF0_ELEMENT_SIZE + offset);
>>> +}
>>
>> Can you add a similar function for fifo_write, please?
> 
> Unlike fifo_read, we only use one tx buffer for tx function,
> fpi, correspding to fgi, is always 0.
> So i planned to add it later when adding using multi tx buffers before.
> If you like it, i could add it now.
> It could be:
> +static inline void m_can_fifo_write(const struct m_can_priv *priv,
> +                                 u32 fpi, unsigned int offset, u32 val)
> +{
> +       return writel(val, priv->mram_base + priv->mcfg[MRAM_TXB].off +
> +                               fpi * TXB_ELEMENT_SIZE + offset);
> +}
> +

Yes, looks good.

>  static inline void m_can_config_endisable(const struct m_can_priv *priv,
>                                           bool enable)
>  {
> @@ -973,12 +980,10 @@ static netdev_tx_t m_can_start_xmit(struct sk_buff *skb,
>         }
> 
>         /* message ram configuration */
> -       fifo_addr = priv->mram_base + priv->mcfg[MRAM_TXB].off;
> -       writel(id | flags, fifo_addr);
> -       writel(cf->can_dlc << 16, fifo_addr + 0x4);
> -       writel(*(u32 *)(cf->data + 0), fifo_addr + 0x8);
> -       writel(*(u32 *)(cf->data + 4), fifo_addr + 0xc);
> -
> +       m_can_fifo_write(priv, 0, 0x0, id | flags);
> +       m_can_fifo_write(priv, 0, 0x4, cf->can_dlc << 16);
> +       m_can_fifo_write(priv, 0, 0x8, *(u32 *)(cf->data + 0));
> +       m_can_fifo_write(priv, 0, 0xc, *(u32 *)(cf->data + 4));
>         can_put_echo_skb(skb, dev, 0);
> 
>         /* enable first TX buffer to start transfer  */
> 
> The '0' parameter may cause a bit misleading now, do you think it's ok?

Yes.

>>> +
>>> +static inline void m_can_config_endisable(const struct m_can_priv *priv,
>>> +					  bool enable)
>>> +{
>>> +	u32 cccr = m_can_read(priv, M_CAN_CCCR);
>>> +	u32 timeout = 10;
>>> +	u32 val = 0;
>>> +
>>> +	if (enable) {
>>> +		/* enable m_can configuration */
>>> +		m_can_write(priv, M_CAN_CCCR, cccr | CCCR_INIT);
>>> +		/* CCCR.CCE can only be set/reset while CCCR.INIT = '1' */
>>> +		m_can_write(priv, M_CAN_CCCR, cccr | CCCR_INIT | CCCR_CCE);
>>> +	} else {
>>> +		m_can_write(priv, M_CAN_CCCR, cccr & ~(CCCR_INIT | CCCR_CCE));
>>> +	}
>>> +
>>> +	/* there's a delay for module initialization */
>>> +	if (enable)
>>> +		val = CCCR_INIT | CCCR_CCE;
>>> +
>>> +	while ((m_can_read(priv, M_CAN_CCCR) & (CCCR_INIT | CCCR_CCE))
>>> +				!= val) {
>>> +		if (timeout == 0) {
>>> +			netdev_warn(priv->dev, "Failed to init module\n");
>>> +			return;
>>> +		}
>>> +		timeout--;
>>> +		udelay(1);
>>> +	}
>>> +}
>>> +
>>> +static inline void m_can_enable_all_interrupts(const struct m_can_priv *priv)
>>> +{
>>> +	m_can_write(priv, M_CAN_ILE, ILE_EINT0 | ILE_EINT1);
>>> +}
>>> +
>>> +static inline void m_can_disable_all_interrupts(const struct m_can_priv *priv)
>>> +{
>>> +	m_can_write(priv, M_CAN_ILE, 0x0);
>>> +}
>>> +
>>> +static void m_can_read_fifo(const struct net_device *dev, struct can_frame *cf,
>>> +			    u32 rxfs)
>>> +{
>>> +	struct m_can_priv *priv = netdev_priv(dev);
>>> +	u32 flags, fgi;
>>> +
>>> +	/* calculate the fifo get index for where to read data */
>>> +	fgi = (rxfs & RXFS_FGI_MASK) >> RXFS_FGI_OFF;
>>> +	flags = m_can_fifo_read(priv, fgi, 0x0);
>>                                            ^^^
>>
>> Can you introduce an enum for the offsets, please adjust the signature
>> of m_can_fifo_read() accordingly.
>>
> 
> I wonder enum may not be suitable.
> The Rx Buffer and FIFO Element is as follows:
>    31 24 23 16 15 8 7 0
> R0 ESI XTD RTR ID[28:0]

M_CAN_FIFO_ID

> R1 ANMF FIDX[6:0] res EDL BRS DLC[3:0] RXTS[15:0]

M_CAN_FIFO_DLC

> R2 DB3[7:0] DB2[7:0] DB1[7:0] DB0[7:0]
> R3 DB7[7:0] DB6[7:0] DB5[7:0] DB4[7:0]

M_CAN_FIFO_DATA0
M_CAN_FIFO_DATA1

> ... ... ... ... ...
> Rn DBm[7:0] DBm-1[7:0] DBm-2[7:0] DBm-3[7:0]
> Maybe #define a macro for it is better, like:
> #define RX_BUFFER_Rn(n)	(n * 0x4)
> #define TX_BUFFER_Tn(n)	(n * 0x4)
> #define TXE_BUFFER_En(n)(n * 0x4)
> But i'm not sure if it's worthy to do that now.
> I also planed to update it later when adding canfd format support before.

IMHO, there's no need for {RX,TX}_BUFER_Rn() yet.

>>> +	if (flags & RX_BUF_XTD)
>>> +		cf->can_id = (flags & CAN_EFF_MASK) | CAN_EFF_FLAG;
>>> +	else
>>> +		cf->can_id = (flags >> 18) & CAN_SFF_MASK;
>>> +
>>> +	if (flags & RX_BUF_RTR) {
>>> +		cf->can_id |= CAN_RTR_FLAG;
>>> +	} else {
>>> +		flags = m_can_fifo_read(priv, fgi, 0x4);
>>> +		cf->can_dlc = get_can_dlc((flags >> 16) & 0x0F);
>>> +		*(u32 *)(cf->data + 0) = m_can_fifo_read(priv, fgi, 0x8);
>>> +		*(u32 *)(cf->data + 4) = m_can_fifo_read(priv, fgi, 0xC);
>>> +	}
>>> +
>>> +	/* acknowledge rx fifo 0 */
>>> +	m_can_write(priv, M_CAN_RXF0A, fgi);
>>> +}
>>> +
>>> +static int m_can_do_rx_poll(struct net_device *dev, int quota)
>>> +{
>>> +	struct m_can_priv *priv = netdev_priv(dev);
>>> +	struct net_device_stats *stats = &dev->stats;
>>> +	struct sk_buff *skb;
>>> +	struct can_frame *frame;
>>> +	u32 pkts = 0;
>>> +	u32 rxfs;
>>> +
>>> +	rxfs = m_can_read(priv, M_CAN_RXF0S);
>>> +	if (!(rxfs & RXFS_FFL_MASK)) {
>>> +		netdev_dbg(dev, "no messages in fifo0\n");
>>> +		return 0;
>>> +	}
>>> +
>>> +	while ((rxfs & RXFS_FFL_MASK) && (quota > 0)) {
>>> +		if (rxfs & RXFS_RFL)
>>> +			netdev_warn(dev, "Rx FIFO 0 Message Lost\n");
>>> +
>>> +		skb = alloc_can_skb(dev, &frame);
>>> +		if (!skb) {
>>> +			stats->rx_dropped++;
>>> +			return 0;
>>
>> return pkts;
>>  - or -
>> goto out;
>>
> 
> Good catch. Will update it.
> 
>>> +		}
>>> +
>>> +		m_can_read_fifo(dev, frame, rxfs);
>>> +
>>> +		stats->rx_packets++;
>>> +		stats->rx_bytes += frame->can_dlc;
>>> +
>>> +		netif_receive_skb(skb);
>>> +
>>> +		quota--;
>>> +		pkts++;
>>> +		rxfs = m_can_read(priv, M_CAN_RXF0S);
>>> +	};
>>> +
>>
>> out:
>>> +	if (pkts)
>>> +		can_led_event(dev, CAN_LED_EVENT_RX);
>>> +
>>> +	return pkts;
>>> +}
>>> +
>>
>> [...]
>>
>>> +static int m_can_handle_lec_err(struct net_device *dev,
>>> +				enum m_can_lec_type lec_type)
>>> +{
>>> +	struct m_can_priv *priv = netdev_priv(dev);
>>> +	struct net_device_stats *stats = &dev->stats;
>>> +	struct can_frame *cf;
>>> +	struct sk_buff *skb;
>>> +
>>> +	/* early exit if no lec update */
>>> +	if (lec_type == LEC_UNUSED)
>>> +		return 0;
>>
>> I think this is not needed, as checked by the only caller.
> 
> You mean move it to caller as follows?
>         /* handle lec errors on the bus */
>         if ((psr & LEC_UNUSED) && ((psr & LEC_UNUSED)!= LEC_UNUSED) &&

yes - or something like this:

static inline bool is_lec(u32 psr)
{
	u32 psr &= LEC_UNUSED

	return psr && (psr != LEC_UNUSED)
}

	if ((priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) &&
		is_lec(psr)) {
	}

>                 (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING))
>                 work_done += m_can_handle_lec_err(dev,
>                                 psr & LEC_UNUSED);
> It seems not look good.
> 
> How about keep it here and move the later one to caller like:
>         /* handle lec errors on the bus */
>         if ((psr & LEC_UNUSED) &&
>                 (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING))
>                 work_done += m_can_handle_lec_err(dev,
>                                 psr & LEC_UNUSED);
> 
>>> +
>>> +	if (!(priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING))
>>> +		return 0;
>>
>> Can you move this to the caller, too?
>>
>>> +
>>> +	priv->can.can_stats.bus_error++;
>>> +	stats->rx_errors++;
>>> +
>>> +	/* propagate the error condition to the CAN stack */
>>> +	skb = alloc_can_err_skb(dev, &cf);
>>> +	if (unlikely(!skb))
>>> +		return 0;
>>> +
>>> +	/* check for 'last error code' which tells us the
>>> +	 * type of the last error to occur on the CAN bus
>>> +	 */
>>> +	cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
>>> +	cf->data[2] |= CAN_ERR_PROT_UNSPEC;
>>> +
>>> +	switch (lec_type) {
>>> +	case LEC_STUFF_ERROR:
>>> +		netdev_dbg(dev, "stuff error\n");
>>> +		cf->data[2] |= CAN_ERR_PROT_STUFF;
>>> +		break;
>>> +	case LEC_FORM_ERROR:
>>> +		netdev_dbg(dev, "form error\n");
>>> +		cf->data[2] |= CAN_ERR_PROT_FORM;
>>> +		break;
>>> +	case LEC_ACK_ERROR:
>>> +		netdev_dbg(dev, "ack error\n");
>>> +		cf->data[3] |= (CAN_ERR_PROT_LOC_ACK |
>>> +				CAN_ERR_PROT_LOC_ACK_DEL);
>>> +		break;
>>> +	case LEC_BIT1_ERROR:
>>> +		netdev_dbg(dev, "bit1 error\n");
>>> +		cf->data[2] |= CAN_ERR_PROT_BIT1;
>>> +		break;
>>> +	case LEC_BIT0_ERROR:
>>> +		netdev_dbg(dev, "bit0 error\n");
>>> +		cf->data[2] |= CAN_ERR_PROT_BIT0;
>>> +		break;
>>> +	case LEC_CRC_ERROR:
>>> +		netdev_dbg(dev, "CRC error\n");
>>> +		cf->data[3] |= (CAN_ERR_PROT_LOC_CRC_SEQ |
>>> +				CAN_ERR_PROT_LOC_CRC_DEL);
>>> +		break;
>>> +	default:
>>> +		break;
>>> +	}
>>> +
>>> +	stats->rx_packets++;
>>> +	stats->rx_bytes += cf->can_dlc;
>>> +	netif_receive_skb(skb);
>>> +
>>> +	return 1;
>>> +}
>>> +
>>
>> [...]
>>
>>> +static void m_can_handle_other_err(struct net_device *dev, u32 irqstatus)
>>> +{
>>> +	if (irqstatus & IR_WDI)
>>> +		netdev_err(dev, "Message RAM Watchdog event due to missing READY\n");
>>> +	if (irqstatus & IR_BEU)
>>> +		netdev_err(dev, "Error Logging Overflow\n");
>>> +	if (irqstatus & IR_BEU)
>>> +		netdev_err(dev, "Bit Error Uncorrected\n");
>>> +	if (irqstatus & IR_BEC)
>>> +		netdev_err(dev, "Bit Error Corrected\n");
>>> +	if (irqstatus & IR_TOO)
>>> +		netdev_err(dev, "Timeout reached\n");
>>> +	if (irqstatus & IR_MRAF)
>>> +		netdev_err(dev, "Message RAM access failure occurred\n");
>>> +}
>>
>> Have you ever seen these error messages? Better rate limit these, though.
>>
> 
> Normally we will not see those errors.
> It just for telling the user if something errors happened.
> Still not sure about the rate.
> Could it be improved later when we meet the real issue?

Meh, there is not netdev_ratelimited_LEVEL(), okay, keep it this way.

Marc

-- 
Pengutronix e.K.                  | Marc Kleine-Budde           |
Industrial Linux Solutions        | Phone: +49-231-2826-924     |
Vertretung West/Dortmund          | Fax:   +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686  | http://www.pengutronix.de   |

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 242 bytes
Desc: OpenPGP digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140715/0a8af45a/attachment-0001.sig>


More information about the linux-arm-kernel mailing list