brintos

brintos / linux-shallow public Read only

0
0
Text · 7.9 KiB · fe897f3 Raw
334 lines · c
1// SPDX-License-Identifier: GPL-2.02//3// mcp251xfd - Microchip MCP251xFD Family CAN controller driver4//5// Copyright (c) 2019, 2020, 2021, 2023 Pengutronix,6//               Marc Kleine-Budde <kernel@pengutronix.de>7//8// Based on:9//10// CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface11//12// Copyright (c) 2019 Martin Sperl <kernel@martin.sperl.org>13//14 15#include <linux/bitfield.h>16 17#include "mcp251xfd.h"18 19static inline bool mcp251xfd_rx_fifo_sta_empty(const u32 fifo_sta)20{21	return !(fifo_sta & MCP251XFD_REG_FIFOSTA_TFNRFNIF);22}23 24static inline bool mcp251xfd_rx_fifo_sta_full(const u32 fifo_sta)25{26	return fifo_sta & MCP251XFD_REG_FIFOSTA_TFERFFIF;27}28 29static inline int30mcp251xfd_rx_tail_get_from_chip(const struct mcp251xfd_priv *priv,31				const struct mcp251xfd_rx_ring *ring,32				u8 *rx_tail)33{34	u32 fifo_ua;35	int err;36 37	err = regmap_read(priv->map_reg, MCP251XFD_REG_FIFOUA(ring->fifo_nr),38			  &fifo_ua);39	if (err)40		return err;41 42	fifo_ua -= ring->base - MCP251XFD_RAM_START;43	*rx_tail = fifo_ua / ring->obj_size;44 45	return 0;46}47 48static int49mcp251xfd_check_rx_tail(const struct mcp251xfd_priv *priv,50			const struct mcp251xfd_rx_ring *ring)51{52	u8 rx_tail_chip, rx_tail;53	int err;54 55	if (!IS_ENABLED(CONFIG_CAN_MCP251XFD_SANITY))56		return 0;57 58	err = mcp251xfd_rx_tail_get_from_chip(priv, ring, &rx_tail_chip);59	if (err)60		return err;61 62	rx_tail = mcp251xfd_get_rx_tail(ring);63	if (rx_tail_chip != rx_tail) {64		netdev_err(priv->ndev,65			   "RX tail of chip (%d) and ours (%d) inconsistent.\n",66			   rx_tail_chip, rx_tail);67		return -EILSEQ;68	}69 70	return 0;71}72 73static int74mcp251xfd_get_rx_len(const struct mcp251xfd_priv *priv,75		     const struct mcp251xfd_rx_ring *ring,76		     u8 *len_p)77{78	const u8 shift = ring->obj_num_shift_to_u8;79	u8 chip_head, tail, len;80	u32 fifo_sta;81	int err;82 83	err = regmap_read(priv->map_reg, MCP251XFD_REG_FIFOSTA(ring->fifo_nr),84			  &fifo_sta);85	if (err)86		return err;87 88	if (mcp251xfd_rx_fifo_sta_empty(fifo_sta)) {89		*len_p = 0;90		return 0;91	}92 93	if (mcp251xfd_rx_fifo_sta_full(fifo_sta)) {94		*len_p = ring->obj_num;95		return 0;96	}97 98	chip_head = FIELD_GET(MCP251XFD_REG_FIFOSTA_FIFOCI_MASK, fifo_sta);99 100	err =  mcp251xfd_check_rx_tail(priv, ring);101	if (err)102		return err;103	tail = mcp251xfd_get_rx_tail(ring);104 105	/* First shift to full u8. The subtraction works on signed106	 * values, that keeps the difference steady around the u8107	 * overflow. The right shift acts on len, which is an u8.108	 */109	BUILD_BUG_ON(sizeof(ring->obj_num) != sizeof(chip_head));110	BUILD_BUG_ON(sizeof(ring->obj_num) != sizeof(tail));111	BUILD_BUG_ON(sizeof(ring->obj_num) != sizeof(len));112 113	len = (chip_head << shift) - (tail << shift);114	*len_p = len >> shift;115 116	return 0;117}118 119static void120mcp251xfd_hw_rx_obj_to_skb(const struct mcp251xfd_priv *priv,121			   const struct mcp251xfd_hw_rx_obj_canfd *hw_rx_obj,122			   struct sk_buff *skb)123{124	struct canfd_frame *cfd = (struct canfd_frame *)skb->data;125	u8 dlc;126 127	if (hw_rx_obj->flags & MCP251XFD_OBJ_FLAGS_IDE) {128		u32 sid, eid;129 130		eid = FIELD_GET(MCP251XFD_OBJ_ID_EID_MASK, hw_rx_obj->id);131		sid = FIELD_GET(MCP251XFD_OBJ_ID_SID_MASK, hw_rx_obj->id);132 133		cfd->can_id = CAN_EFF_FLAG |134			FIELD_PREP(MCP251XFD_REG_FRAME_EFF_EID_MASK, eid) |135			FIELD_PREP(MCP251XFD_REG_FRAME_EFF_SID_MASK, sid);136	} else {137		cfd->can_id = FIELD_GET(MCP251XFD_OBJ_ID_SID_MASK,138					hw_rx_obj->id);139	}140 141	dlc = FIELD_GET(MCP251XFD_OBJ_FLAGS_DLC_MASK, hw_rx_obj->flags);142 143	/* CANFD */144	if (hw_rx_obj->flags & MCP251XFD_OBJ_FLAGS_FDF) {145		if (hw_rx_obj->flags & MCP251XFD_OBJ_FLAGS_ESI)146			cfd->flags |= CANFD_ESI;147 148		if (hw_rx_obj->flags & MCP251XFD_OBJ_FLAGS_BRS)149			cfd->flags |= CANFD_BRS;150 151		cfd->len = can_fd_dlc2len(dlc);152	} else {153		if (hw_rx_obj->flags & MCP251XFD_OBJ_FLAGS_RTR)154			cfd->can_id |= CAN_RTR_FLAG;155 156		can_frame_set_cc_len((struct can_frame *)cfd, dlc,157				     priv->can.ctrlmode);158	}159 160	if (!(hw_rx_obj->flags & MCP251XFD_OBJ_FLAGS_RTR))161		memcpy(cfd->data, hw_rx_obj->data, cfd->len);162}163 164static int165mcp251xfd_handle_rxif_one(struct mcp251xfd_priv *priv,166			  struct mcp251xfd_rx_ring *ring,167			  const struct mcp251xfd_hw_rx_obj_canfd *hw_rx_obj)168{169	struct net_device_stats *stats = &priv->ndev->stats;170	struct sk_buff *skb;171	struct canfd_frame *cfd;172	u64 timestamp;173	int err;174 175	/* According to mcp2518fd erratum DS80000789E 6. the FIFOCI176	 * bits of a FIFOSTA register, here the RX FIFO head index177	 * might be corrupted and we might process past the RX FIFO's178	 * head into old CAN frames.179	 *180	 * Compare the timestamp of currently processed CAN frame with181	 * last valid frame received. Abort with -EBADMSG if an old182	 * CAN frame is detected.183	 */184	timestamp = timecounter_cyc2time(&priv->tc, hw_rx_obj->ts);185	if (timestamp <= ring->last_valid) {186		stats->rx_fifo_errors++;187 188		return -EBADMSG;189	}190	ring->last_valid = timestamp;191 192	if (hw_rx_obj->flags & MCP251XFD_OBJ_FLAGS_FDF)193		skb = alloc_canfd_skb(priv->ndev, &cfd);194	else195		skb = alloc_can_skb(priv->ndev, (struct can_frame **)&cfd);196 197	if (!skb) {198		stats->rx_dropped++;199		return 0;200	}201 202	mcp251xfd_skb_set_timestamp(skb, timestamp);203	mcp251xfd_hw_rx_obj_to_skb(priv, hw_rx_obj, skb);204	err = can_rx_offload_queue_timestamp(&priv->offload, skb, hw_rx_obj->ts);205	if (err)206		stats->rx_fifo_errors++;207 208	return 0;209}210 211static inline int212mcp251xfd_rx_obj_read(const struct mcp251xfd_priv *priv,213		      const struct mcp251xfd_rx_ring *ring,214		      struct mcp251xfd_hw_rx_obj_canfd *hw_rx_obj,215		      const u8 offset, const u8 len)216{217	const int val_bytes = regmap_get_val_bytes(priv->map_rx);218	int err;219 220	err = regmap_bulk_read(priv->map_rx,221			       mcp251xfd_get_rx_obj_addr(ring, offset),222			       hw_rx_obj,223			       len * ring->obj_size / val_bytes);224 225	return err;226}227 228static int229mcp251xfd_handle_rxif_ring_uinc(const struct mcp251xfd_priv *priv,230				struct mcp251xfd_rx_ring *ring,231				u8 len)232{233	int offset;234	int err;235 236	if (!len)237		return 0;238 239	ring->head += len;240 241	/* Increment the RX FIFO tail pointer 'len' times in a242	 * single SPI message.243	 *244	 * Note:245	 * Calculate offset, so that the SPI transfer ends on246	 * the last message of the uinc_xfer array, which has247	 * "cs_change == 0", to properly deactivate the chip248	 * select.249	 */250	offset = ARRAY_SIZE(ring->uinc_xfer) - len;251	err = spi_sync_transfer(priv->spi,252				ring->uinc_xfer + offset, len);253	if (err)254		return err;255 256	ring->tail += len;257 258	return 0;259}260 261static int262mcp251xfd_handle_rxif_ring(struct mcp251xfd_priv *priv,263			   struct mcp251xfd_rx_ring *ring)264{265	struct mcp251xfd_hw_rx_obj_canfd *hw_rx_obj = ring->obj;266	u8 rx_tail, len, l;267	int err, i;268 269	err = mcp251xfd_get_rx_len(priv, ring, &len);270	if (err)271		return err;272 273	while ((l = mcp251xfd_get_rx_linear_len(ring, len))) {274		rx_tail = mcp251xfd_get_rx_tail(ring);275 276		err = mcp251xfd_rx_obj_read(priv, ring, hw_rx_obj,277					    rx_tail, l);278		if (err)279			return err;280 281		for (i = 0; i < l; i++) {282			err = mcp251xfd_handle_rxif_one(priv, ring,283							(void *)hw_rx_obj +284							i * ring->obj_size);285 286			/* -EBADMSG means we're affected by mcp2518fd287			 * erratum DS80000789E 6., i.e. the timestamp288			 * in the RX object is older that the last289			 * valid received CAN frame. Don't process any290			 * further and mark processed frames as good.291			 */292			if (err == -EBADMSG)293				return mcp251xfd_handle_rxif_ring_uinc(priv, ring, i);294			else if (err)295				return err;296		}297 298		err = mcp251xfd_handle_rxif_ring_uinc(priv, ring, l);299		if (err)300			return err;301 302		len -= l;303	}304 305	return 0;306}307 308int mcp251xfd_handle_rxif(struct mcp251xfd_priv *priv)309{310	struct mcp251xfd_rx_ring *ring;311	int err, n;312 313	mcp251xfd_for_each_rx_ring(priv, ring, n) {314		/* - if RX IRQ coalescing is active always handle ring 0315		 * - only handle rings if RX IRQ is active316		 */317		if ((ring->nr > 0 || !priv->rx_obj_num_coalesce_irq) &&318		    !(priv->regs_status.rxif & BIT(ring->fifo_nr)))319			continue;320 321		err = mcp251xfd_handle_rxif_ring(priv, ring);322		if (err)323			return err;324	}325 326	if (priv->rx_coalesce_usecs_irq)327		hrtimer_start(&priv->rx_irq_timer,328			      ns_to_ktime(priv->rx_coalesce_usecs_irq *329					  NSEC_PER_USEC),330			      HRTIMER_MODE_REL);331 332	return 0;333}334