linux-can-next-for-5.15-20210819

-----BEGIN PGP SIGNATURE-----
 
 iQFHBAABCgAxFiEEK3kIWJt9yTYMP3ehqclaivrt76kFAmEeV4ATHG1rbEBwZW5n
 dXRyb25peC5kZQAKCRCpyVqK+u3vqcnkB/0RSBHy8w48DqTvdTd4ldMiNROc47bb
 q/HiTHq9dkNRyNzg1unSYLMEyEmdjmek8PmeZug1UV2kdTRFFawuDmk5lNDI6pFy
 GNSEcX8VdN6bqTROXtVxjaHPAm5M5Dx91WmytK810aV6b3asMezEPCNsrerfRt+a
 xvVTOAbPGvbb2Fb4e0e4ijDgiBpVD+nqhWcJb9953EayxpU80bfE94TSmRiC5yHo
 dEZN73tBr08rjSK69bEHKHg0T92Omig5+kn5sLj9TgXhfdtIUCMPnPwEh+8RY1l8
 cUY8zUCoyGGWStbhP+O6lq1yLcjQuliQwCAf6oHPb9DM+TmsUozWt2H5
 =xTjF
 -----END PGP SIGNATURE-----

Merge tag 'linux-can-next-for-5.15-20210819' of git://git.kernel.org/pub/scm/linux/kernel/git/mkl/linux-can-next

Marc Kleine-Budde says:

====================
linux-can-next-for-5.15-20210819

The first patch is by me, for the mailmap file and maps the email
address of two former ESD employees to a newly created role account.

The next 3 patches are by Oleksij Rempel and add support for GPIO
based switchable CAN bus termination.

The next 3 patches are by Vincent Mailhol. The first one changes the
CAN netlink interface to not bail out if the user switched off
unsupported features. The next one adds Vincent as the maintainer of
the etas_es58x driver and the last one cleans up the documentation of
struct es58x_fd_tx_conf_msg.

The next patch is by me, for the mcp251xfd driver and marks some
instances of struct mcp251xfd_priv as const. Lad Prabhakar contributes
2 patches for the rcar_canfd driver, that add support for RZ/G2L
family.

The next 5 patches target the m_can/tcan45x5 driver. 2 are by me an
fix trivial checkpatch warnings. The remaining 3 patches are by Matt
Kline and improve the performance on the SPI based tcan4x5x chip by
batching FIFO reads and writes.

The last 7 patches are for the c_can driver. Dario Binacchi's patch
converts the DT bindings to yaml, 2 patches by me fix a typo and
rename a macro to properly represent the usage. The last 4 patches are
again by Dario Binacchi and provide a performance improvement for the
TX path by operating the TX mailboxes as a true FIFO.

* tag 'linux-can-next-for-5.15-20210819' of git://git.kernel.org/pub/scm/linux/kernel/git/mkl/linux-can-next: (22 commits)
  can: c_can: cache frames to operate as a true FIFO
  can: c_can: support tx ring algorithm
  can: c_can: exit c_can_do_tx() early if no frames have been sent
  can: c_can: remove struct c_can_priv::priv field
  can: c_can: rename IF_RX -> IF_NAPI
  can: c_can: c_can_do_tx(): fix typo in comment
  dt-bindings: net: can: c_can: convert to json-schema
  can: m_can: Batch FIFO writes during CAN transmit
  can: m_can: Batch FIFO reads during CAN receive
  can: m_can: Disable IRQs on FIFO bus errors
  can: m_can: fix block comment style
  can: tcan4x5x: cdev_to_priv(): remove stray empty line
  can: rcar_canfd: Add support for RZ/G2L family
  dt-bindings: net: can: renesas,rcar-canfd: Document RZ/G2L SoC
  can: mcp251xfd: mark some instances of struct mcp251xfd_priv as const
  can: etas_es58x: clean-up documentation of struct es58x_fd_tx_conf_msg
  MAINTAINERS: add Vincent MAILHOL as maintainer for the ETAS ES58X CAN/USB driver
  can: netlink: allow user to turn off unsupported features
  can: dev: provide optional GPIO based termination support
  dt-bindings: can: fsl,flexcan: enable termination-* bindings
  ...
====================

Link: https://lore.kernel.org/r/20210819133913.657715-1-mkl@pengutronix.de
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2021-08-19 11:51:13 -07:00
commit 185f690f29
23 changed files with 852 additions and 306 deletions

View file

@ -229,6 +229,7 @@ Matthew Wilcox <willy@infradead.org> <mawilcox@microsoft.com>
Matthew Wilcox <willy@infradead.org> <willy@debian.org>
Matthew Wilcox <willy@infradead.org> <willy@linux.intel.com>
Matthew Wilcox <willy@infradead.org> <willy@parisc-linux.org>
Matthias Fuchs <socketcan@esd.eu> <matthias.fuchs@esd.eu>
Matthieu CASTET <castet.matthieu@free.fr>
Matt Ranostay <matt.ranostay@konsulko.com> <matt@ranostay.consulting>
Matt Ranostay <mranostay@gmail.com> Matthew Ranostay <mranostay@embeddedalley.com>
@ -341,6 +342,7 @@ Sumit Semwal <sumit.semwal@ti.com>
Takashi YOSHII <takashi.yoshii.zj@renesas.com>
Tejun Heo <htejun@gmail.com>
Thomas Graf <tgraf@suug.ch>
Thomas Körper <socketcan@esd.eu> <thomas.koerper@esd.eu>
Thomas Pedersen <twp@codeaurora.org>
Tiezhu Yang <yangtiezhu@loongson.cn> <kernelpatch@126.com>
Todor Tomov <todor.too@gmail.com> <todor.tomov@linaro.org>

View file

@ -0,0 +1,119 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/net/can/bosch,c_can.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Bosch C_CAN/D_CAN controller Device Tree Bindings
description: Bosch C_CAN/D_CAN controller for CAN bus
maintainers:
- Dario Binacchi <dariobin@libero.it>
allOf:
- $ref: can-controller.yaml#
properties:
compatible:
oneOf:
- enum:
- bosch,c_can
- bosch,d_can
- ti,dra7-d_can
- ti,am3352-d_can
- items:
- enum:
- ti,am4372-d_can
- const: ti,am3352-d_can
reg:
maxItems: 1
interrupts:
minItems: 1
maxItems: 4
power-domains:
description: |
Should contain a phandle to a PM domain provider node and an args
specifier containing the DCAN device id value. It's mandatory for
Keystone 2 66AK2G SoCs only.
maxItems: 1
clocks:
description: |
CAN functional clock phandle.
maxItems: 1
clock-names:
maxItems: 1
syscon-raminit:
description: |
Handle to system control region that contains the RAMINIT register,
register offset to the RAMINIT register and the CAN instance number (0
offset).
$ref: /schemas/types.yaml#/definitions/phandle-array
items:
items:
- description: The phandle to the system control region.
- description: The register offset.
- description: The CAN instance number.
resets:
maxItems: 1
required:
- compatible
- reg
- interrupts
- clocks
if:
properties:
compatible:
contains:
enum:
- bosch,d_can
then:
properties:
interrupts:
minItems: 4
maxItems: 4
items:
- description: Error and status IRQ
- description: Message object IRQ
- description: RAM ECC correctable error IRQ
- description: RAM ECC non-correctable error IRQ
else:
properties:
interrupts:
maxItems: 1
items:
- description: Error and status IRQ
additionalProperties: false
examples:
- |
#include <dt-bindings/reset/altr,rst-mgr.h>
can@ffc00000 {
compatible = "bosch,d_can";
reg = <0xffc00000 0x1000>;
interrupts = <0 131 4>, <0 132 4>, <0 133 4>, <0 134 4>;
clocks = <&can0_clk>;
resets = <&rst CAN0_RESET>;
};
- |
can@0 {
compatible = "ti,am3352-d_can";
reg = <0x0 0x2000>;
clocks = <&dcan1_fck>;
clock-names = "fck";
syscon-raminit = <&scm_conf 0x644 1>;
interrupts = <55>;
};

View file

@ -1,65 +0,0 @@
Bosch C_CAN/D_CAN controller Device Tree Bindings
-------------------------------------------------
Required properties:
- compatible : Should be "bosch,c_can" for C_CAN controllers and
"bosch,d_can" for D_CAN controllers.
Can be "ti,dra7-d_can", "ti,am3352-d_can" or
"ti,am4372-d_can".
- reg : physical base address and size of the C_CAN/D_CAN
registers map
- interrupts : property with a value describing the interrupt
number
The following are mandatory properties for DRA7x, AM33xx and AM43xx SoCs only:
- ti,hwmods : Must be "d_can<n>" or "c_can<n>", n being the
instance number
The following are mandatory properties for Keystone 2 66AK2G SoCs only:
- power-domains : Should contain a phandle to a PM domain provider node
and an args specifier containing the DCAN device id
value. This property is as per the binding,
Documentation/devicetree/bindings/soc/ti/sci-pm-domain.yaml
- clocks : CAN functional clock phandle. This property is as per the
binding,
Documentation/devicetree/bindings/clock/ti,sci-clk.yaml
Optional properties:
- syscon-raminit : Handle to system control region that contains the
RAMINIT register, register offset to the RAMINIT
register and the CAN instance number (0 offset).
Note: "ti,hwmods" field is used to fetch the base address and irq
resources from TI, omap hwmod data base during device registration.
Future plan is to migrate hwmod data base contents into device tree
blob so that, all the required data will be used from device tree dts
file.
Example:
Step1: SoC common .dtsi file
dcan1: d_can@481d0000 {
compatible = "bosch,d_can";
reg = <0x481d0000 0x2000>;
interrupts = <55>;
interrupt-parent = <&intc>;
status = "disabled";
};
(or)
dcan1: d_can@481d0000 {
compatible = "bosch,d_can";
ti,hwmods = "d_can1";
reg = <0x481d0000 0x2000>;
interrupts = <55>;
interrupt-parent = <&intc>;
status = "disabled";
};
Step 2: board specific .dts file
&dcan1 {
status = "okay";
};

View file

@ -13,6 +13,15 @@ properties:
$nodename:
pattern: "^can(@.*)?$"
termination-gpios:
description: GPIO pin to enable CAN bus termination.
maxItems: 1
termination-ohms:
description: The resistance value of the CAN bus termination resistor.
minimum: 1
maximum: 65535
additionalProperties: true
...

View file

@ -119,6 +119,9 @@ properties:
minimum: 0
maximum: 2
termination-gpios: true
termination-ohms: true
required:
- compatible
- reg
@ -148,3 +151,17 @@ examples:
fsl,stop-mode = <&gpr 0x34 28>;
fsl,scu-index = /bits/ 8 <1>;
};
- |
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/gpio/gpio.h>
can@2090000 {
compatible = "fsl,imx6q-flexcan";
reg = <0x02090000 0x4000>;
interrupts = <0 110 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks 1>, <&clks 2>;
clock-names = "ipg", "per";
fsl,stop-mode = <&gpr 0x34 28>;
termination-gpios = <&gpio1 0 GPIO_ACTIVE_LOW>;
termination-ohms = <120>;
};

View file

@ -30,13 +30,15 @@ properties:
- renesas,r8a77995-canfd # R-Car D3
- const: renesas,rcar-gen3-canfd # R-Car Gen3 and RZ/G2
- items:
- enum:
- renesas,r9a07g044-canfd # RZ/G2{L,LC}
- const: renesas,rzg2l-canfd # RZ/G2L family
reg:
maxItems: 1
interrupts:
items:
- description: Channel interrupt
- description: Global interrupt
interrupts: true
clocks:
maxItems: 3
@ -50,8 +52,7 @@ properties:
power-domains:
maxItems: 1
resets:
maxItems: 1
resets: true
renesas,no-can-fd:
$ref: /schemas/types.yaml#/definitions/flag
@ -91,6 +92,62 @@ required:
- channel0
- channel1
if:
properties:
compatible:
contains:
enum:
- renesas,rzg2l-canfd
then:
properties:
interrupts:
items:
- description: CAN global error interrupt
- description: CAN receive FIFO interrupt
- description: CAN0 error interrupt
- description: CAN0 transmit interrupt
- description: CAN0 transmit/receive FIFO receive completion interrupt
- description: CAN1 error interrupt
- description: CAN1 transmit interrupt
- description: CAN1 transmit/receive FIFO receive completion interrupt
interrupt-names:
items:
- const: g_err
- const: g_recc
- const: ch0_err
- const: ch0_rec
- const: ch0_trx
- const: ch1_err
- const: ch1_rec
- const: ch1_trx
resets:
maxItems: 2
reset-names:
items:
- const: rstp_n
- const: rstc_n
required:
- interrupt-names
- reset-names
else:
properties:
interrupts:
items:
- description: Channel interrupt
- description: Global interrupt
interrupt-names:
items:
- const: ch_int
- const: g_int
resets:
maxItems: 1
unevaluatedProperties: false
examples:

View file

@ -6905,6 +6905,12 @@ M: Mark Einon <mark.einon@gmail.com>
S: Odd Fixes
F: drivers/net/ethernet/agere/
ETAS ES58X CAN/USB DRIVER
M: Vincent Mailhol <mailhol.vincent@wanadoo.fr>
L: linux-can@vger.kernel.org
S: Maintained
F: drivers/net/can/usb/etas_es58x/
ETHERNET BRIDGE
M: Roopa Prabhu <roopa@nvidia.com>
M: Nikolay Aleksandrov <nikolay@nvidia.com>

View file

@ -176,6 +176,13 @@ struct c_can_raminit {
bool needs_pulse;
};
/* c_can tx ring structure */
struct c_can_tx_ring {
unsigned int head;
unsigned int tail;
unsigned int obj_num;
};
/* c_can private data structure */
struct c_can_priv {
struct can_priv can; /* must be the first member */
@ -190,17 +197,16 @@ struct c_can_priv {
unsigned int msg_obj_tx_first;
unsigned int msg_obj_tx_last;
u32 msg_obj_rx_mask;
atomic_t tx_active;
atomic_t sie_pending;
unsigned long tx_dir;
int last_status;
struct c_can_tx_ring tx;
u16 (*read_reg)(const struct c_can_priv *priv, enum reg index);
void (*write_reg)(const struct c_can_priv *priv, enum reg index, u16 val);
u32 (*read_reg32)(const struct c_can_priv *priv, enum reg index);
void (*write_reg32)(const struct c_can_priv *priv, enum reg index, u32 val);
void __iomem *base;
const u16 *regs;
void *priv; /* for board-specific data */
enum c_can_dev_id type;
struct c_can_raminit raminit_sys; /* RAMINIT via syscon regmap */
void (*raminit)(const struct c_can_priv *priv, bool enable);
@ -220,4 +226,19 @@ int c_can_power_down(struct net_device *dev);
void c_can_set_ethtool_ops(struct net_device *dev);
static inline u8 c_can_get_tx_head(const struct c_can_tx_ring *ring)
{
return ring->head & (ring->obj_num - 1);
}
static inline u8 c_can_get_tx_tail(const struct c_can_tx_ring *ring)
{
return ring->tail & (ring->obj_num - 1);
}
static inline u8 c_can_get_tx_free(const struct c_can_tx_ring *ring)
{
return ring->obj_num - (ring->head - ring->tail);
}
#endif /* C_CAN_H */

View file

@ -160,8 +160,8 @@
#define IF_MCONT_TX (IF_MCONT_TXIE | IF_MCONT_EOB)
/* Use IF1 for RX and IF2 for TX */
#define IF_RX 0
/* Use IF1 in NAPI path and IF2 in TX path */
#define IF_NAPI 0
#define IF_TX 1
/* minimum timeout for checking BUSY status */
@ -427,24 +427,51 @@ static void c_can_setup_receive_object(struct net_device *dev, int iface,
c_can_object_put(dev, iface, obj, IF_COMM_RCV_SETUP);
}
static bool c_can_tx_busy(const struct c_can_priv *priv,
const struct c_can_tx_ring *tx_ring)
{
if (c_can_get_tx_free(tx_ring) > 0)
return false;
netif_stop_queue(priv->dev);
/* Memory barrier before checking tx_free (head and tail) */
smp_mb();
if (c_can_get_tx_free(tx_ring) == 0) {
netdev_dbg(priv->dev,
"Stopping tx-queue (tx_head=0x%08x, tx_tail=0x%08x, len=%d).\n",
tx_ring->head, tx_ring->tail,
tx_ring->head - tx_ring->tail);
return true;
}
netif_start_queue(priv->dev);
return false;
}
static netdev_tx_t c_can_start_xmit(struct sk_buff *skb,
struct net_device *dev)
{
struct can_frame *frame = (struct can_frame *)skb->data;
struct c_can_priv *priv = netdev_priv(dev);
u32 idx, obj;
struct c_can_tx_ring *tx_ring = &priv->tx;
u32 idx, obj, cmd = IF_COMM_TX;
if (can_dropped_invalid_skb(dev, skb))
return NETDEV_TX_OK;
/* This is not a FIFO. C/D_CAN sends out the buffers
* prioritized. The lowest buffer number wins.
*/
idx = fls(atomic_read(&priv->tx_active));
obj = idx + priv->msg_obj_tx_first;
/* If this is the last buffer, stop the xmit queue */
if (idx == priv->msg_obj_tx_num - 1)
if (c_can_tx_busy(priv, tx_ring))
return NETDEV_TX_BUSY;
idx = c_can_get_tx_head(tx_ring);
tx_ring->head++;
if (c_can_get_tx_free(tx_ring) == 0)
netif_stop_queue(dev);
if (idx < c_can_get_tx_tail(tx_ring))
cmd &= ~IF_COMM_TXRQST; /* Cache the message */
/* Store the message in the interface so we can call
* can_put_echo_skb(). We must do this before we enable
* transmit as we might race against do_tx().
@ -452,11 +479,8 @@ static netdev_tx_t c_can_start_xmit(struct sk_buff *skb,
c_can_setup_tx_object(dev, IF_TX, frame, idx);
priv->dlc[idx] = frame->len;
can_put_echo_skb(skb, dev, idx, 0);
/* Update the active bits */
atomic_add(BIT(idx), &priv->tx_active);
/* Start transmission */
c_can_object_put(dev, IF_TX, obj, IF_COMM_TX);
obj = idx + priv->msg_obj_tx_first;
c_can_object_put(dev, IF_TX, obj, cmd);
return NETDEV_TX_OK;
}
@ -529,13 +553,13 @@ static void c_can_configure_msg_objects(struct net_device *dev)
/* first invalidate all message objects */
for (i = priv->msg_obj_rx_first; i <= priv->msg_obj_num; i++)
c_can_inval_msg_object(dev, IF_RX, i);
c_can_inval_msg_object(dev, IF_NAPI, i);
/* setup receive message objects */
for (i = priv->msg_obj_rx_first; i < priv->msg_obj_rx_last; i++)
c_can_setup_receive_object(dev, IF_RX, i, 0, 0, IF_MCONT_RCV);
c_can_setup_receive_object(dev, IF_NAPI, i, 0, 0, IF_MCONT_RCV);
c_can_setup_receive_object(dev, IF_RX, priv->msg_obj_rx_last, 0, 0,
c_can_setup_receive_object(dev, IF_NAPI, priv->msg_obj_rx_last, 0, 0,
IF_MCONT_RCV_EOB);
}
@ -567,6 +591,7 @@ static int c_can_software_reset(struct net_device *dev)
static int c_can_chip_config(struct net_device *dev)
{
struct c_can_priv *priv = netdev_priv(dev);
struct c_can_tx_ring *tx_ring = &priv->tx;
int err;
err = c_can_software_reset(dev);
@ -598,7 +623,8 @@ static int c_can_chip_config(struct net_device *dev)
priv->write_reg(priv, C_CAN_STS_REG, LEC_UNUSED);
/* Clear all internal status */
atomic_set(&priv->tx_active, 0);
tx_ring->head = 0;
tx_ring->tail = 0;
priv->tx_dir = 0;
/* set bittiming params */
@ -696,40 +722,57 @@ static int c_can_get_berr_counter(const struct net_device *dev,
static void c_can_do_tx(struct net_device *dev)
{
struct c_can_priv *priv = netdev_priv(dev);
struct c_can_tx_ring *tx_ring = &priv->tx;
struct net_device_stats *stats = &dev->stats;
u32 idx, obj, pkts = 0, bytes = 0, pend, clr;
u32 idx, obj, pkts = 0, bytes = 0, pend;
u8 tail;
if (priv->msg_obj_tx_last > 32)
pend = priv->read_reg32(priv, C_CAN_INTPND3_REG);
else
pend = priv->read_reg(priv, C_CAN_INTPND2_REG);
clr = pend;
while ((idx = ffs(pend))) {
idx--;
pend &= ~BIT(idx);
obj = idx + priv->msg_obj_tx_first;
/* We use IF_RX interface instead of IF_TX because we
/* We use IF_NAPI interface instead of IF_TX because we
* are called from c_can_poll(), which runs inside
* NAPI. We are not trasmitting.
* NAPI. We are not transmitting.
*/
c_can_inval_tx_object(dev, IF_RX, obj);
c_can_inval_tx_object(dev, IF_NAPI, obj);
can_get_echo_skb(dev, idx, NULL);
bytes += priv->dlc[idx];
pkts++;
}
/* Clear the bits in the tx_active mask */
atomic_sub(clr, &priv->tx_active);
if (!pkts)
return;
if (clr & BIT(priv->msg_obj_tx_num - 1))
netif_wake_queue(dev);
tx_ring->tail += pkts;
if (c_can_get_tx_free(tx_ring)) {
/* Make sure that anybody stopping the queue after
* this sees the new tx_ring->tail.
*/
smp_mb();
netif_wake_queue(priv->dev);
}
if (pkts) {
stats->tx_bytes += bytes;
stats->tx_packets += pkts;
can_led_event(dev, CAN_LED_EVENT_TX);
stats->tx_bytes += bytes;
stats->tx_packets += pkts;
can_led_event(dev, CAN_LED_EVENT_TX);
tail = c_can_get_tx_tail(tx_ring);
if (tail == 0) {
u8 head = c_can_get_tx_head(tx_ring);
/* Start transmission for all cached messages */
for (idx = tail; idx < head; idx++) {
obj = idx + priv->msg_obj_tx_first;
c_can_object_put(dev, IF_NAPI, obj, IF_COMM_TXRQST);
}
}
}
@ -766,14 +809,14 @@ static u32 c_can_adjust_pending(u32 pend, u32 rx_mask)
static inline void c_can_rx_object_get(struct net_device *dev,
struct c_can_priv *priv, u32 obj)
{
c_can_object_get(dev, IF_RX, obj, priv->comm_rcv_high);
c_can_object_get(dev, IF_NAPI, obj, priv->comm_rcv_high);
}
static inline void c_can_rx_finalize(struct net_device *dev,
struct c_can_priv *priv, u32 obj)
{
if (priv->type != BOSCH_D_CAN)
c_can_object_get(dev, IF_RX, obj, IF_COMM_CLR_NEWDAT);
c_can_object_get(dev, IF_NAPI, obj, IF_COMM_CLR_NEWDAT);
}
static int c_can_read_objects(struct net_device *dev, struct c_can_priv *priv,
@ -785,10 +828,12 @@ static int c_can_read_objects(struct net_device *dev, struct c_can_priv *priv,
pend &= ~BIT(obj - 1);
c_can_rx_object_get(dev, priv, obj);
ctrl = priv->read_reg(priv, C_CAN_IFACE(MSGCTRL_REG, IF_RX));
ctrl = priv->read_reg(priv, C_CAN_IFACE(MSGCTRL_REG, IF_NAPI));
if (ctrl & IF_MCONT_MSGLST) {
int n = c_can_handle_lost_msg_obj(dev, IF_RX, obj, ctrl);
int n;
n = c_can_handle_lost_msg_obj(dev, IF_NAPI, obj, ctrl);
pkts += n;
quota -= n;
@ -803,7 +848,7 @@ static int c_can_read_objects(struct net_device *dev, struct c_can_priv *priv,
continue;
/* read the data from the message object */
c_can_read_msg_object(dev, IF_RX, ctrl);
c_can_read_msg_object(dev, IF_NAPI, ctrl);
c_can_rx_finalize(dev, priv, obj);
@ -1205,6 +1250,10 @@ struct net_device *alloc_c_can_dev(int msg_obj_num)
priv->msg_obj_tx_last =
priv->msg_obj_tx_first + priv->msg_obj_tx_num - 1;
priv->tx.head = 0;
priv->tx.tail = 0;
priv->tx.obj_num = msg_obj_tx_num;
netif_napi_add(dev, &priv->napi, c_can_poll, priv->msg_obj_rx_num);
priv->dev = dev;

View file

@ -385,7 +385,6 @@ static int c_can_plat_probe(struct platform_device *pdev)
priv->base = addr;
priv->device = &pdev->dev;
priv->can.clock.freq = clk_get_rate(clk);
priv->priv = clk;
priv->type = drvdata->id;
platform_set_drvdata(pdev, dev);

View file

@ -15,6 +15,7 @@
#include <linux/can/dev.h>
#include <linux/can/skb.h>
#include <linux/can/led.h>
#include <linux/gpio/consumer.h>
#include <linux/of.h>
#define MOD_DESC "CAN device driver interface"
@ -400,10 +401,69 @@ void close_candev(struct net_device *dev)
}
EXPORT_SYMBOL_GPL(close_candev);
static int can_set_termination(struct net_device *ndev, u16 term)
{
struct can_priv *priv = netdev_priv(ndev);
int set;
if (term == priv->termination_gpio_ohms[CAN_TERMINATION_GPIO_ENABLED])
set = 1;
else
set = 0;
gpiod_set_value(priv->termination_gpio, set);
return 0;
}
static int can_get_termination(struct net_device *ndev)
{
struct can_priv *priv = netdev_priv(ndev);
struct device *dev = ndev->dev.parent;
struct gpio_desc *gpio;
u32 term;
int ret;
/* Disabling termination by default is the safe choice: Else if many
* bus participants enable it, no communication is possible at all.
*/
gpio = devm_gpiod_get_optional(dev, "termination", GPIOD_OUT_LOW);
if (IS_ERR(gpio))
return dev_err_probe(dev, PTR_ERR(gpio),
"Cannot get termination-gpios\n");
if (!gpio)
return 0;
ret = device_property_read_u32(dev, "termination-ohms", &term);
if (ret) {
netdev_err(ndev, "Cannot get termination-ohms: %pe\n",
ERR_PTR(ret));
return ret;
}
if (term > U16_MAX) {
netdev_err(ndev, "Invalid termination-ohms value (%u > %u)\n",
term, U16_MAX);
return -EINVAL;
}
priv->termination_const_cnt = ARRAY_SIZE(priv->termination_gpio_ohms);
priv->termination_const = priv->termination_gpio_ohms;
priv->termination_gpio = gpio;
priv->termination_gpio_ohms[CAN_TERMINATION_GPIO_DISABLED] =
CAN_TERMINATION_DISABLED;
priv->termination_gpio_ohms[CAN_TERMINATION_GPIO_ENABLED] = term;
priv->do_set_termination = can_set_termination;
return 0;
}
/* Register the CAN network device */
int register_candev(struct net_device *dev)
{
struct can_priv *priv = netdev_priv(dev);
int err;
/* Ensure termination_const, termination_const_cnt and
* do_set_termination consistency. All must be either set or
@ -419,6 +479,12 @@ int register_candev(struct net_device *dev)
if (!priv->data_bitrate_const != !priv->data_bitrate_const_cnt)
return -EINVAL;
if (!priv->termination_const) {
err = can_get_termination(dev);
if (err)
return err;
}
dev->rtnl_link_ops = &can_link_ops;
netif_carrier_off(dev);

View file

@ -116,7 +116,7 @@ static int can_changelink(struct net_device *dev, struct nlattr *tb[],
maskedflags = cm->flags & cm->mask;
/* check whether provided bits are allowed to be passed */
if (cm->mask & ~(priv->ctrlmode_supported | ctrlstatic))
if (maskedflags & ~(priv->ctrlmode_supported | ctrlstatic))
return -EOPNOTSUPP;
/* do not check for static fd-non-iso if 'fd' is disabled */

View file

@ -279,7 +279,7 @@ enum m_can_reg {
/* Message RAM Elements */
#define M_CAN_FIFO_ID 0x0
#define M_CAN_FIFO_DLC 0x4
#define M_CAN_FIFO_DATA(n) (0x8 + ((n) << 2))
#define M_CAN_FIFO_DATA 0x8
/* Rx Buffer Element */
/* R0 */
@ -309,6 +309,15 @@ enum m_can_reg {
#define TX_EVENT_MM_MASK GENMASK(31, 24)
#define TX_EVENT_TXTS_MASK GENMASK(15, 0)
/* The ID and DLC registers are adjacent in M_CAN FIFO memory,
* and we can save a (potentially slow) bus round trip by combining
* reads and writes to them.
*/
struct id_and_dlc {
u32 id;
u32 dlc;
};
static inline u32 m_can_read(struct m_can_classdev *cdev, enum m_can_reg reg)
{
return cdev->ops->read_reg(cdev, reg);
@ -320,36 +329,39 @@ static inline void m_can_write(struct m_can_classdev *cdev, enum m_can_reg reg,
cdev->ops->write_reg(cdev, reg, val);
}
static u32 m_can_fifo_read(struct m_can_classdev *cdev,
u32 fgi, unsigned int offset)
static int
m_can_fifo_read(struct m_can_classdev *cdev,
u32 fgi, unsigned int offset, void *val, size_t val_count)
{
u32 addr_offset = cdev->mcfg[MRAM_RXF0].off + fgi * RXF0_ELEMENT_SIZE +
offset;
return cdev->ops->read_fifo(cdev, addr_offset);
return cdev->ops->read_fifo(cdev, addr_offset, val, val_count);
}
static void m_can_fifo_write(struct m_can_classdev *cdev,
u32 fpi, unsigned int offset, u32 val)
static int
m_can_fifo_write(struct m_can_classdev *cdev,
u32 fpi, unsigned int offset, const void *val, size_t val_count)
{
u32 addr_offset = cdev->mcfg[MRAM_TXB].off + fpi * TXB_ELEMENT_SIZE +
offset;
cdev->ops->write_fifo(cdev, addr_offset, val);
return cdev->ops->write_fifo(cdev, addr_offset, val, val_count);
}
static inline void m_can_fifo_write_no_off(struct m_can_classdev *cdev,
u32 fpi, u32 val)
static inline int m_can_fifo_write_no_off(struct m_can_classdev *cdev,
u32 fpi, u32 val)
{
cdev->ops->write_fifo(cdev, fpi, val);
return cdev->ops->write_fifo(cdev, fpi, &val, 1);
}
static u32 m_can_txe_fifo_read(struct m_can_classdev *cdev, u32 fgi, u32 offset)
static int
m_can_txe_fifo_read(struct m_can_classdev *cdev, u32 fgi, u32 offset, u32 *val)
{
u32 addr_offset = cdev->mcfg[MRAM_TXE].off + fgi * TXE_ELEMENT_SIZE +
offset;
return cdev->ops->read_fifo(cdev, addr_offset);
return cdev->ops->read_fifo(cdev, addr_offset, val, 1);
}
static inline bool m_can_tx_fifo_full(struct m_can_classdev *cdev)
@ -437,7 +449,7 @@ static void m_can_clean(struct net_device *net)
* napi. For non-peripherals, RX is done in napi already, so push
* directly. timestamp is used to ensure good skb ordering in
* rx-offload and is ignored for non-peripherals.
*/
*/
static void m_can_receive_skb(struct m_can_classdev *cdev,
struct sk_buff *skb,
u32 timestamp)
@ -455,54 +467,57 @@ static void m_can_receive_skb(struct m_can_classdev *cdev,
}
}
static void m_can_read_fifo(struct net_device *dev, u32 rxfs)
static int m_can_read_fifo(struct net_device *dev, u32 rxfs)
{
struct net_device_stats *stats = &dev->stats;
struct m_can_classdev *cdev = netdev_priv(dev);
struct canfd_frame *cf;
struct sk_buff *skb;
u32 id, fgi, dlc;
struct id_and_dlc fifo_header;
u32 fgi;
u32 timestamp = 0;
int i;
int err;
/* calculate the fifo get index for where to read data */
fgi = FIELD_GET(RXFS_FGI_MASK, rxfs);
dlc = m_can_fifo_read(cdev, fgi, M_CAN_FIFO_DLC);
if (dlc & RX_BUF_FDF)
err = m_can_fifo_read(cdev, fgi, M_CAN_FIFO_ID, &fifo_header, 2);
if (err)
goto out_fail;
if (fifo_header.dlc & RX_BUF_FDF)
skb = alloc_canfd_skb(dev, &cf);
else
skb = alloc_can_skb(dev, (struct can_frame **)&cf);
if (!skb) {
stats->rx_dropped++;
return;
return 0;
}
if (dlc & RX_BUF_FDF)
cf->len = can_fd_dlc2len((dlc >> 16) & 0x0F);
if (fifo_header.dlc & RX_BUF_FDF)
cf->len = can_fd_dlc2len((fifo_header.dlc >> 16) & 0x0F);
else
cf->len = can_cc_dlc2len((dlc >> 16) & 0x0F);
cf->len = can_cc_dlc2len((fifo_header.dlc >> 16) & 0x0F);
id = m_can_fifo_read(cdev, fgi, M_CAN_FIFO_ID);
if (id & RX_BUF_XTD)
cf->can_id = (id & CAN_EFF_MASK) | CAN_EFF_FLAG;
if (fifo_header.id & RX_BUF_XTD)
cf->can_id = (fifo_header.id & CAN_EFF_MASK) | CAN_EFF_FLAG;
else
cf->can_id = (id >> 18) & CAN_SFF_MASK;
cf->can_id = (fifo_header.id >> 18) & CAN_SFF_MASK;
if (id & RX_BUF_ESI) {
if (fifo_header.id & RX_BUF_ESI) {
cf->flags |= CANFD_ESI;
netdev_dbg(dev, "ESI Error\n");
}
if (!(dlc & RX_BUF_FDF) && (id & RX_BUF_RTR)) {
if (!(fifo_header.dlc & RX_BUF_FDF) && (fifo_header.id & RX_BUF_RTR)) {
cf->can_id |= CAN_RTR_FLAG;
} else {
if (dlc & RX_BUF_BRS)
if (fifo_header.dlc & RX_BUF_BRS)
cf->flags |= CANFD_BRS;
for (i = 0; i < cf->len; i += 4)
*(u32 *)(cf->data + i) =
m_can_fifo_read(cdev, fgi,
M_CAN_FIFO_DATA(i / 4));
err = m_can_fifo_read(cdev, fgi, M_CAN_FIFO_DATA,
cf->data, DIV_ROUND_UP(cf->len, 4));
if (err)
goto out_fail;
}
/* acknowledge rx fifo 0 */
@ -511,9 +526,15 @@ static void m_can_read_fifo(struct net_device *dev, u32 rxfs)
stats->rx_packets++;
stats->rx_bytes += cf->len;
timestamp = FIELD_GET(RX_BUF_RXTS_MASK, dlc);
timestamp = FIELD_GET(RX_BUF_RXTS_MASK, fifo_header.dlc);
m_can_receive_skb(cdev, skb, timestamp);
return 0;
out_fail:
netdev_err(dev, "FIFO read returned %d\n", err);
return err;
}
static int m_can_do_rx_poll(struct net_device *dev, int quota)
@ -521,6 +542,7 @@ static int m_can_do_rx_poll(struct net_device *dev, int quota)
struct m_can_classdev *cdev = netdev_priv(dev);
u32 pkts = 0;
u32 rxfs;
int err;
rxfs = m_can_read(cdev, M_CAN_RXF0S);
if (!(rxfs & RXFS_FFL_MASK)) {
@ -529,7 +551,9 @@ static int m_can_do_rx_poll(struct net_device *dev, int quota)
}
while ((rxfs & RXFS_FFL_MASK) && (quota > 0)) {
m_can_read_fifo(dev, rxfs);
err = m_can_read_fifo(dev, rxfs);
if (err)
return err;
quota--;
pkts++;
@ -875,6 +899,7 @@ static int m_can_handle_bus_errors(struct net_device *dev, u32 irqstatus,
static int m_can_rx_handler(struct net_device *dev, int quota)
{
struct m_can_classdev *cdev = netdev_priv(dev);
int rx_work_or_err;
int work_done = 0;
u32 irqstatus, psr;
@ -911,8 +936,13 @@ static int m_can_rx_handler(struct net_device *dev, int quota)
if (irqstatus & IR_ERR_BUS_30X)
work_done += m_can_handle_bus_errors(dev, irqstatus, psr);
if (irqstatus & IR_RF0N)
work_done += m_can_do_rx_poll(dev, (quota - work_done));
if (irqstatus & IR_RF0N) {
rx_work_or_err = m_can_do_rx_poll(dev, (quota - work_done));
if (rx_work_or_err < 0)
return rx_work_or_err;
work_done += rx_work_or_err;
}
end:
return work_done;
}
@ -920,12 +950,17 @@ static int m_can_rx_handler(struct net_device *dev, int quota)
static int m_can_rx_peripheral(struct net_device *dev)
{
struct m_can_classdev *cdev = netdev_priv(dev);
int work_done;
m_can_rx_handler(dev, M_CAN_NAPI_WEIGHT);
work_done = m_can_rx_handler(dev, M_CAN_NAPI_WEIGHT);
m_can_enable_all_interrupts(cdev);
/* Don't re-enable interrupts if the driver had a fatal error
* (e.g., FIFO read failure).
*/
if (work_done >= 0)
m_can_enable_all_interrupts(cdev);
return 0;
return work_done;
}
static int m_can_poll(struct napi_struct *napi, int quota)
@ -935,7 +970,11 @@ static int m_can_poll(struct napi_struct *napi, int quota)
int work_done;
work_done = m_can_rx_handler(dev, quota);
if (work_done < quota) {
/* Don't re-enable interrupts if the driver had a fatal error
* (e.g., FIFO read failure).
*/
if (work_done >= 0 && work_done < quota) {
napi_complete_done(napi, work_done);
m_can_enable_all_interrupts(cdev);
}
@ -946,7 +985,7 @@ static int m_can_poll(struct napi_struct *napi, int quota)
/* Echo tx skb and update net stats. Peripherals use rx-offload for
* echo. timestamp is used for peripherals to ensure correct ordering
* by rx-offload, and is ignored for non-peripherals.
*/
*/
static void m_can_tx_update_stats(struct m_can_classdev *cdev,
unsigned int msg_mark,
u32 timestamp)
@ -966,7 +1005,7 @@ static void m_can_tx_update_stats(struct m_can_classdev *cdev,
stats->tx_packets++;
}
static void m_can_echo_tx_event(struct net_device *dev)
static int m_can_echo_tx_event(struct net_device *dev)
{
u32 txe_count = 0;
u32 m_can_txefs;
@ -985,12 +1024,18 @@ static void m_can_echo_tx_event(struct net_device *dev)
/* Get and process all sent elements */
for (i = 0; i < txe_count; i++) {
u32 txe, timestamp = 0;
int err;
/* retrieve get index */
fgi = FIELD_GET(TXEFS_EFGI_MASK, m_can_read(cdev, M_CAN_TXEFS));
/* get message marker, timestamp */
txe = m_can_txe_fifo_read(cdev, fgi, 4);
err = m_can_txe_fifo_read(cdev, fgi, 4, &txe);
if (err) {
netdev_err(dev, "TXE FIFO read returned %d\n", err);
return err;
}
msg_mark = FIELD_GET(TX_EVENT_MM_MASK, txe);
timestamp = FIELD_GET(TX_EVENT_TXTS_MASK, txe);
@ -1001,6 +1046,8 @@ static void m_can_echo_tx_event(struct net_device *dev)
/* update stats */
m_can_tx_update_stats(cdev, msg_mark, timestamp);
}
return 0;
}
static irqreturn_t m_can_isr(int irq, void *dev_id)
@ -1032,8 +1079,8 @@ static irqreturn_t m_can_isr(int irq, void *dev_id)
m_can_disable_all_interrupts(cdev);
if (!cdev->is_peripheral)
napi_schedule(&cdev->napi);
else
m_can_rx_peripheral(dev);
else if (m_can_rx_peripheral(dev) < 0)
goto out_fail;
}
if (cdev->version == 30) {
@ -1051,7 +1098,9 @@ static irqreturn_t m_can_isr(int irq, void *dev_id)
} else {
if (ir & IR_TEFN) {
/* New TX FIFO Element arrived */
m_can_echo_tx_event(dev);
if (m_can_echo_tx_event(dev) != 0)
goto out_fail;
can_led_event(dev, CAN_LED_EVENT_TX);
if (netif_queue_stopped(dev) &&
!m_can_tx_fifo_full(cdev))
@ -1063,6 +1112,10 @@ static irqreturn_t m_can_isr(int irq, void *dev_id)
can_rx_offload_threaded_irq_finish(&cdev->offload);
return IRQ_HANDLED;
out_fail:
m_can_disable_all_interrupts(cdev);
return IRQ_HANDLED;
}
static const struct can_bittiming_const m_can_bittiming_const_30X = {
@ -1306,7 +1359,8 @@ static void m_can_chip_config(struct net_device *dev)
m_can_set_bittiming(dev);
/* enable internal timestamp generation, with a prescalar of 16. The
* prescalar is applied to the nominal bit timing */
* prescalar is applied to the nominal bit timing
*/
m_can_write(cdev, M_CAN_TSCC, FIELD_PREP(TSCC_TCP_MASK, 0xf));
m_can_config_endisable(cdev, false);
@ -1534,8 +1588,9 @@ static netdev_tx_t m_can_tx_handler(struct m_can_classdev *cdev)
struct canfd_frame *cf = (struct canfd_frame *)cdev->tx_skb->data;
struct net_device *dev = cdev->net;
struct sk_buff *skb = cdev->tx_skb;
u32 id, cccr, fdflags;
int i;
struct id_and_dlc fifo_header;
u32 cccr, fdflags;
int err;
int putidx;
cdev->tx_skb = NULL;
@ -1543,27 +1598,29 @@ static netdev_tx_t m_can_tx_handler(struct m_can_classdev *cdev)
/* Generate ID field for TX buffer Element */
/* Common to all supported M_CAN versions */
if (cf->can_id & CAN_EFF_FLAG) {
id = cf->can_id & CAN_EFF_MASK;
id |= TX_BUF_XTD;
fifo_header.id = cf->can_id & CAN_EFF_MASK;
fifo_header.id |= TX_BUF_XTD;
} else {
id = ((cf->can_id & CAN_SFF_MASK) << 18);
fifo_header.id = ((cf->can_id & CAN_SFF_MASK) << 18);
}
if (cf->can_id & CAN_RTR_FLAG)
id |= TX_BUF_RTR;
fifo_header.id |= TX_BUF_RTR;
if (cdev->version == 30) {
netif_stop_queue(dev);
/* message ram configuration */
m_can_fifo_write(cdev, 0, M_CAN_FIFO_ID, id);
m_can_fifo_write(cdev, 0, M_CAN_FIFO_DLC,
can_fd_len2dlc(cf->len) << 16);
fifo_header.dlc = can_fd_len2dlc(cf->len) << 16;
for (i = 0; i < cf->len; i += 4)
m_can_fifo_write(cdev, 0,
M_CAN_FIFO_DATA(i / 4),
*(u32 *)(cf->data + i));
/* Write the frame ID, DLC, and payload to the FIFO element. */
err = m_can_fifo_write(cdev, 0, M_CAN_FIFO_ID, &fifo_header, 2);
if (err)
goto out_fail;
err = m_can_fifo_write(cdev, 0, M_CAN_FIFO_DATA,
cf->data, DIV_ROUND_UP(cf->len, 4));
if (err)
goto out_fail;
can_put_echo_skb(skb, dev, 0, 0);
@ -1607,8 +1664,11 @@ static netdev_tx_t m_can_tx_handler(struct m_can_classdev *cdev)
/* get put index for frame */
putidx = FIELD_GET(TXFQS_TFQPI_MASK,
m_can_read(cdev, M_CAN_TXFQS));
/* Write ID Field to FIFO Element */
m_can_fifo_write(cdev, putidx, M_CAN_FIFO_ID, id);
/* Construct DLC Field, with CAN-FD configuration.
* Use the put index of the fifo as the message marker,
* used in the TX interrupt for sending the correct echo frame.
*/
/* get CAN FD configuration of frame */
fdflags = 0;
@ -1618,20 +1678,17 @@ static netdev_tx_t m_can_tx_handler(struct m_can_classdev *cdev)
fdflags |= TX_BUF_BRS;
}
/* Construct DLC Field. Also contains CAN-FD configuration
* use put index of fifo as message marker
* it is used in TX interrupt for
* sending the correct echo frame
*/
m_can_fifo_write(cdev, putidx, M_CAN_FIFO_DLC,
FIELD_PREP(TX_BUF_MM_MASK, putidx) |
FIELD_PREP(TX_BUF_DLC_MASK,
can_fd_len2dlc(cf->len)) |
fdflags | TX_BUF_EFC);
fifo_header.dlc = FIELD_PREP(TX_BUF_MM_MASK, putidx) |
FIELD_PREP(TX_BUF_DLC_MASK, can_fd_len2dlc(cf->len)) |
fdflags | TX_BUF_EFC;
err = m_can_fifo_write(cdev, putidx, M_CAN_FIFO_ID, &fifo_header, 2);
if (err)
goto out_fail;
for (i = 0; i < cf->len; i += 4)
m_can_fifo_write(cdev, putidx, M_CAN_FIFO_DATA(i / 4),
*(u32 *)(cf->data + i));
err = m_can_fifo_write(cdev, putidx, M_CAN_FIFO_DATA,
cf->data, DIV_ROUND_UP(cf->len, 4));
if (err)
goto out_fail;
/* Push loopback echo.
* Will be looped back on TX interrupt based on message marker
@ -1648,6 +1705,11 @@ static netdev_tx_t m_can_tx_handler(struct m_can_classdev *cdev)
}
return NETDEV_TX_OK;
out_fail:
netdev_err(dev, "FIFO write returned %d\n", err);
m_can_disable_all_interrupts(cdev);
return NETDEV_TX_BUSY;
}
static void m_can_tx_work_queue(struct work_struct *ws)
@ -1819,9 +1881,10 @@ static void m_can_of_parse_mram(struct m_can_classdev *cdev,
cdev->mcfg[MRAM_TXB].off, cdev->mcfg[MRAM_TXB].num);
}
void m_can_init_ram(struct m_can_classdev *cdev)
int m_can_init_ram(struct m_can_classdev *cdev)
{
int end, i, start;
int err = 0;
/* initialize the entire Message RAM in use to avoid possible
* ECC/parity checksum errors when reading an uninitialized buffer
@ -1830,8 +1893,13 @@ void m_can_init_ram(struct m_can_classdev *cdev)
end = cdev->mcfg[MRAM_TXB].off +
cdev->mcfg[MRAM_TXB].num * TXB_ELEMENT_SIZE;
for (i = start; i < end; i += 4)
m_can_fifo_write_no_off(cdev, i, 0x0);
for (i = start; i < end; i += 4) {
err = m_can_fifo_write_no_off(cdev, i, 0x0);
if (err)
break;
}
return err;
}
EXPORT_SYMBOL_GPL(m_can_init_ram);

View file

@ -65,9 +65,9 @@ struct m_can_ops {
int (*clear_interrupts)(struct m_can_classdev *cdev);
u32 (*read_reg)(struct m_can_classdev *cdev, int reg);
int (*write_reg)(struct m_can_classdev *cdev, int reg, int val);
u32 (*read_fifo)(struct m_can_classdev *cdev, int addr_offset);
int (*read_fifo)(struct m_can_classdev *cdev, int addr_offset, void *val, size_t val_count);
int (*write_fifo)(struct m_can_classdev *cdev, int addr_offset,
int val);
const void *val, size_t val_count);
int (*init)(struct m_can_classdev *cdev);
};
@ -101,7 +101,7 @@ void m_can_class_free_dev(struct net_device *net);
int m_can_class_register(struct m_can_classdev *cdev);
void m_can_class_unregister(struct m_can_classdev *cdev);
int m_can_class_get_clocks(struct m_can_classdev *cdev);
void m_can_init_ram(struct m_can_classdev *priv);
int m_can_init_ram(struct m_can_classdev *priv);
int m_can_class_suspend(struct device *dev);
int m_can_class_resume(struct device *dev);

View file

@ -39,11 +39,13 @@ static u32 iomap_read_reg(struct m_can_classdev *cdev, int reg)
return readl(priv->base + reg);
}
static u32 iomap_read_fifo(struct m_can_classdev *cdev, int offset)
static int iomap_read_fifo(struct m_can_classdev *cdev, int offset, void *val, size_t val_count)
{
struct m_can_pci_priv *priv = cdev_to_priv(cdev);
return readl(priv->base + offset);
ioread32_rep(priv->base + offset, val, val_count);
return 0;
}
static int iomap_write_reg(struct m_can_classdev *cdev, int reg, int val)
@ -55,11 +57,12 @@ static int iomap_write_reg(struct m_can_classdev *cdev, int reg, int val)
return 0;
}
static int iomap_write_fifo(struct m_can_classdev *cdev, int offset, int val)
static int iomap_write_fifo(struct m_can_classdev *cdev, int offset,
const void *val, size_t val_count)
{
struct m_can_pci_priv *priv = cdev_to_priv(cdev);
writel(val, priv->base + offset);
iowrite32_rep(priv->base + offset, val, val_count);
return 0;
}

View file

@ -29,11 +29,13 @@ static u32 iomap_read_reg(struct m_can_classdev *cdev, int reg)
return readl(priv->base + reg);
}
static u32 iomap_read_fifo(struct m_can_classdev *cdev, int offset)
static int iomap_read_fifo(struct m_can_classdev *cdev, int offset, void *val, size_t val_count)
{
struct m_can_plat_priv *priv = cdev_to_priv(cdev);
return readl(priv->mram_base + offset);
ioread32_rep(priv->mram_base + offset, val, val_count);
return 0;
}
static int iomap_write_reg(struct m_can_classdev *cdev, int reg, int val)
@ -45,11 +47,12 @@ static int iomap_write_reg(struct m_can_classdev *cdev, int reg, int val)
return 0;
}
static int iomap_write_fifo(struct m_can_classdev *cdev, int offset, int val)
static int iomap_write_fifo(struct m_can_classdev *cdev, int offset,
const void *val, size_t val_count)
{
struct m_can_plat_priv *priv = cdev_to_priv(cdev);
writel(val, priv->mram_base + offset);
iowrite32_rep(priv->base + offset, val, val_count);
return 0;
}
@ -127,7 +130,9 @@ static int m_can_plat_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, mcan_class);
m_can_init_ram(mcan_class);
ret = m_can_init_ram(mcan_class);
if (ret)
goto probe_fail;
pm_runtime_enable(mcan_class->dev);
ret = m_can_class_register(mcan_class);

View file

@ -105,7 +105,6 @@
static inline struct tcan4x5x_priv *cdev_to_priv(struct m_can_classdev *cdev)
{
return container_of(cdev, struct tcan4x5x_priv, cdev);
}
static void tcan4x5x_check_wake(struct tcan4x5x_priv *priv)
@ -154,14 +153,12 @@ static u32 tcan4x5x_read_reg(struct m_can_classdev *cdev, int reg)
return val;
}
static u32 tcan4x5x_read_fifo(struct m_can_classdev *cdev, int addr_offset)
static int tcan4x5x_read_fifo(struct m_can_classdev *cdev, int addr_offset,
void *val, size_t val_count)
{
struct tcan4x5x_priv *priv = cdev_to_priv(cdev);
u32 val;
regmap_read(priv->regmap, TCAN4X5X_MRAM_START + addr_offset, &val);
return val;
return regmap_bulk_read(priv->regmap, TCAN4X5X_MRAM_START + addr_offset, val, val_count);
}
static int tcan4x5x_write_reg(struct m_can_classdev *cdev, int reg, int val)
@ -172,11 +169,11 @@ static int tcan4x5x_write_reg(struct m_can_classdev *cdev, int reg, int val)
}
static int tcan4x5x_write_fifo(struct m_can_classdev *cdev,
int addr_offset, int val)
int addr_offset, const void *val, size_t val_count)
{
struct tcan4x5x_priv *priv = cdev_to_priv(cdev);
return regmap_write(priv->regmap, TCAN4X5X_MRAM_START + addr_offset, val);
return regmap_bulk_write(priv->regmap, TCAN4X5X_MRAM_START + addr_offset, val, val_count);
}
static int tcan4x5x_power_enable(struct regulator *reg, int enable)
@ -238,7 +235,9 @@ static int tcan4x5x_init(struct m_can_classdev *cdev)
return ret;
/* Zero out the MCAN buffers */
m_can_init_ram(cdev);
ret = m_can_init_ram(cdev);
if (ret)
return ret;
ret = regmap_update_bits(tcan4x5x->regmap, TCAN4X5X_CONFIG,
TCAN4X5X_MODE_SEL_MASK, TCAN4X5X_MODE_NORMAL);

View file

@ -37,9 +37,15 @@
#include <linux/bitmap.h>
#include <linux/bitops.h>
#include <linux/iopoll.h>
#include <linux/reset.h>
#define RCANFD_DRV_NAME "rcar_canfd"
enum rcanfd_chip_id {
RENESAS_RCAR_GEN3 = 0,
RENESAS_RZG2L,
};
/* Global register bits */
/* RSCFDnCFDGRMCFG */
@ -513,6 +519,9 @@ struct rcar_canfd_global {
enum rcar_canfd_fcanclk fcan; /* CANFD or Ext clock */
unsigned long channels_mask; /* Enabled channels mask */
bool fdmode; /* CAN FD or Classical CAN only mode */
struct reset_control *rstc1;
struct reset_control *rstc2;
enum rcanfd_chip_id chip_id;
};
/* CAN FD mode nominal rate constants */
@ -1070,38 +1079,70 @@ static void rcar_canfd_tx_done(struct net_device *ndev)
can_led_event(ndev, CAN_LED_EVENT_TX);
}
static void rcar_canfd_handle_global_err(struct rcar_canfd_global *gpriv, u32 ch)
{
struct rcar_canfd_channel *priv = gpriv->ch[ch];
struct net_device *ndev = priv->ndev;
u32 gerfl;
/* Handle global error interrupts */
gerfl = rcar_canfd_read(priv->base, RCANFD_GERFL);
if (unlikely(RCANFD_GERFL_ERR(gpriv, gerfl)))
rcar_canfd_global_error(ndev);
}
static irqreturn_t rcar_canfd_global_err_interrupt(int irq, void *dev_id)
{
struct rcar_canfd_global *gpriv = dev_id;
u32 ch;
for_each_set_bit(ch, &gpriv->channels_mask, RCANFD_NUM_CHANNELS)
rcar_canfd_handle_global_err(gpriv, ch);
return IRQ_HANDLED;
}
static void rcar_canfd_handle_global_receive(struct rcar_canfd_global *gpriv, u32 ch)
{
struct rcar_canfd_channel *priv = gpriv->ch[ch];
u32 ridx = ch + RCANFD_RFFIFO_IDX;
u32 sts;
/* Handle Rx interrupts */
sts = rcar_canfd_read(priv->base, RCANFD_RFSTS(ridx));
if (likely(sts & RCANFD_RFSTS_RFIF)) {
if (napi_schedule_prep(&priv->napi)) {
/* Disable Rx FIFO interrupts */
rcar_canfd_clear_bit(priv->base,
RCANFD_RFCC(ridx),
RCANFD_RFCC_RFIE);
__napi_schedule(&priv->napi);
}
}
}
static irqreturn_t rcar_canfd_global_receive_fifo_interrupt(int irq, void *dev_id)
{
struct rcar_canfd_global *gpriv = dev_id;
u32 ch;
for_each_set_bit(ch, &gpriv->channels_mask, RCANFD_NUM_CHANNELS)
rcar_canfd_handle_global_receive(gpriv, ch);
return IRQ_HANDLED;
}
static irqreturn_t rcar_canfd_global_interrupt(int irq, void *dev_id)
{
struct rcar_canfd_global *gpriv = dev_id;
struct net_device *ndev;
struct rcar_canfd_channel *priv;
u32 sts, gerfl;
u32 ch, ridx;
u32 ch;
/* Global error interrupts still indicate a condition specific
* to a channel. RxFIFO interrupt is a global interrupt.
*/
for_each_set_bit(ch, &gpriv->channels_mask, RCANFD_NUM_CHANNELS) {
priv = gpriv->ch[ch];
ndev = priv->ndev;
ridx = ch + RCANFD_RFFIFO_IDX;
/* Global error interrupts */
gerfl = rcar_canfd_read(priv->base, RCANFD_GERFL);
if (unlikely(RCANFD_GERFL_ERR(gpriv, gerfl)))
rcar_canfd_global_error(ndev);
/* Handle Rx interrupts */
sts = rcar_canfd_read(priv->base, RCANFD_RFSTS(ridx));
if (likely(sts & RCANFD_RFSTS_RFIF)) {
if (napi_schedule_prep(&priv->napi)) {
/* Disable Rx FIFO interrupts */
rcar_canfd_clear_bit(priv->base,
RCANFD_RFCC(ridx),
RCANFD_RFCC_RFIE);
__napi_schedule(&priv->napi);
}
}
rcar_canfd_handle_global_err(gpriv, ch);
rcar_canfd_handle_global_receive(gpriv, ch);
}
return IRQ_HANDLED;
}
@ -1139,38 +1180,73 @@ static void rcar_canfd_state_change(struct net_device *ndev,
}
}
static void rcar_canfd_handle_channel_tx(struct rcar_canfd_global *gpriv, u32 ch)
{
struct rcar_canfd_channel *priv = priv = gpriv->ch[ch];
struct net_device *ndev = priv->ndev;
u32 sts;
/* Handle Tx interrupts */
sts = rcar_canfd_read(priv->base,
RCANFD_CFSTS(ch, RCANFD_CFFIFO_IDX));
if (likely(sts & RCANFD_CFSTS_CFTXIF))
rcar_canfd_tx_done(ndev);
}
static irqreturn_t rcar_canfd_channel_tx_interrupt(int irq, void *dev_id)
{
struct rcar_canfd_global *gpriv = dev_id;
u32 ch;
for_each_set_bit(ch, &gpriv->channels_mask, RCANFD_NUM_CHANNELS)
rcar_canfd_handle_channel_tx(gpriv, ch);
return IRQ_HANDLED;
}
static void rcar_canfd_handle_channel_err(struct rcar_canfd_global *gpriv, u32 ch)
{
struct rcar_canfd_channel *priv = gpriv->ch[ch];
struct net_device *ndev = priv->ndev;
u16 txerr, rxerr;
u32 sts, cerfl;
/* Handle channel error interrupts */
cerfl = rcar_canfd_read(priv->base, RCANFD_CERFL(ch));
sts = rcar_canfd_read(priv->base, RCANFD_CSTS(ch));
txerr = RCANFD_CSTS_TECCNT(sts);
rxerr = RCANFD_CSTS_RECCNT(sts);
if (unlikely(RCANFD_CERFL_ERR(cerfl)))
rcar_canfd_error(ndev, cerfl, txerr, rxerr);
/* Handle state change to lower states */
if (unlikely(priv->can.state != CAN_STATE_ERROR_ACTIVE &&
priv->can.state != CAN_STATE_BUS_OFF))
rcar_canfd_state_change(ndev, txerr, rxerr);
}
static irqreturn_t rcar_canfd_channel_err_interrupt(int irq, void *dev_id)
{
struct rcar_canfd_global *gpriv = dev_id;
u32 ch;
for_each_set_bit(ch, &gpriv->channels_mask, RCANFD_NUM_CHANNELS)
rcar_canfd_handle_channel_err(gpriv, ch);
return IRQ_HANDLED;
}
static irqreturn_t rcar_canfd_channel_interrupt(int irq, void *dev_id)
{
struct rcar_canfd_global *gpriv = dev_id;
struct net_device *ndev;
struct rcar_canfd_channel *priv;
u32 sts, ch, cerfl;
u16 txerr, rxerr;
u32 ch;
/* Common FIFO is a per channel resource */
for_each_set_bit(ch, &gpriv->channels_mask, RCANFD_NUM_CHANNELS) {
priv = gpriv->ch[ch];
ndev = priv->ndev;
/* Channel error interrupts */
cerfl = rcar_canfd_read(priv->base, RCANFD_CERFL(ch));
sts = rcar_canfd_read(priv->base, RCANFD_CSTS(ch));
txerr = RCANFD_CSTS_TECCNT(sts);
rxerr = RCANFD_CSTS_RECCNT(sts);
if (unlikely(RCANFD_CERFL_ERR(cerfl)))
rcar_canfd_error(ndev, cerfl, txerr, rxerr);
/* Handle state change to lower states */
if (unlikely((priv->can.state != CAN_STATE_ERROR_ACTIVE) &&
(priv->can.state != CAN_STATE_BUS_OFF)))
rcar_canfd_state_change(ndev, txerr, rxerr);
/* Handle Tx interrupts */
sts = rcar_canfd_read(priv->base,
RCANFD_CFSTS(ch, RCANFD_CFFIFO_IDX));
if (likely(sts & RCANFD_CFSTS_CFTXIF))
rcar_canfd_tx_done(ndev);
rcar_canfd_handle_channel_err(gpriv, ch);
rcar_canfd_handle_channel_tx(gpriv, ch);
}
return IRQ_HANDLED;
}
@ -1577,6 +1653,53 @@ static int rcar_canfd_channel_probe(struct rcar_canfd_global *gpriv, u32 ch,
priv->can.clock.freq = fcan_freq;
dev_info(&pdev->dev, "can_clk rate is %u\n", priv->can.clock.freq);
if (gpriv->chip_id == RENESAS_RZG2L) {
char *irq_name;
int err_irq;
int tx_irq;
err_irq = platform_get_irq_byname(pdev, ch == 0 ? "ch0_err" : "ch1_err");
if (err_irq < 0) {
err = err_irq;
goto fail;
}
tx_irq = platform_get_irq_byname(pdev, ch == 0 ? "ch0_trx" : "ch1_trx");
if (tx_irq < 0) {
err = tx_irq;
goto fail;
}
irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL,
"canfd.ch%d_err", ch);
if (!irq_name) {
err = -ENOMEM;
goto fail;
}
err = devm_request_irq(&pdev->dev, err_irq,
rcar_canfd_channel_err_interrupt, 0,
irq_name, gpriv);
if (err) {
dev_err(&pdev->dev, "devm_request_irq CH Err(%d) failed, error %d\n",
err_irq, err);
goto fail;
}
irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL,
"canfd.ch%d_trx", ch);
if (!irq_name) {
err = -ENOMEM;
goto fail;
}
err = devm_request_irq(&pdev->dev, tx_irq,
rcar_canfd_channel_tx_interrupt, 0,
irq_name, gpriv);
if (err) {
dev_err(&pdev->dev, "devm_request_irq Tx (%d) failed, error %d\n",
tx_irq, err);
goto fail;
}
}
if (gpriv->fdmode) {
priv->can.bittiming_const = &rcar_canfd_nom_bittiming_const;
priv->can.data_bittiming_const =
@ -1636,7 +1759,11 @@ static int rcar_canfd_probe(struct platform_device *pdev)
struct device_node *of_child;
unsigned long channels_mask = 0;
int err, ch_irq, g_irq;
int g_err_irq, g_recc_irq;
bool fdmode = true; /* CAN FD only mode - default */
enum rcanfd_chip_id chip_id;
chip_id = (uintptr_t)of_device_get_match_data(&pdev->dev);
if (of_property_read_bool(pdev->dev.of_node, "renesas,no-can-fd"))
fdmode = false; /* Classical CAN only mode */
@ -1649,16 +1776,30 @@ static int rcar_canfd_probe(struct platform_device *pdev)
if (of_child && of_device_is_available(of_child))
channels_mask |= BIT(1); /* Channel 1 */
ch_irq = platform_get_irq(pdev, 0);
if (ch_irq < 0) {
err = ch_irq;
goto fail_dev;
}
if (chip_id == RENESAS_RCAR_GEN3) {
ch_irq = platform_get_irq_byname_optional(pdev, "ch_int");
if (ch_irq < 0) {
/* For backward compatibility get irq by index */
ch_irq = platform_get_irq(pdev, 0);
if (ch_irq < 0)
return ch_irq;
}
g_irq = platform_get_irq(pdev, 1);
if (g_irq < 0) {
err = g_irq;
goto fail_dev;
g_irq = platform_get_irq_byname_optional(pdev, "g_int");
if (g_irq < 0) {
/* For backward compatibility get irq by index */
g_irq = platform_get_irq(pdev, 1);
if (g_irq < 0)
return g_irq;
}
} else {
g_err_irq = platform_get_irq_byname(pdev, "g_err");
if (g_err_irq < 0)
return g_err_irq;
g_recc_irq = platform_get_irq_byname(pdev, "g_recc");
if (g_recc_irq < 0)
return g_recc_irq;
}
/* Global controller context */
@ -1670,6 +1811,19 @@ static int rcar_canfd_probe(struct platform_device *pdev)
gpriv->pdev = pdev;
gpriv->channels_mask = channels_mask;
gpriv->fdmode = fdmode;
gpriv->chip_id = chip_id;
if (gpriv->chip_id == RENESAS_RZG2L) {
gpriv->rstc1 = devm_reset_control_get_exclusive(&pdev->dev, "rstp_n");
if (IS_ERR(gpriv->rstc1))
return dev_err_probe(&pdev->dev, PTR_ERR(gpriv->rstc1),
"failed to get rstp_n\n");
gpriv->rstc2 = devm_reset_control_get_exclusive(&pdev->dev, "rstc_n");
if (IS_ERR(gpriv->rstc2))
return dev_err_probe(&pdev->dev, PTR_ERR(gpriv->rstc2),
"failed to get rstc_n\n");
}
/* Peripheral clock */
gpriv->clkp = devm_clk_get(&pdev->dev, "fck");
@ -1699,7 +1853,7 @@ static int rcar_canfd_probe(struct platform_device *pdev)
}
fcan_freq = clk_get_rate(gpriv->can_clk);
if (gpriv->fcan == RCANFD_CANFDCLK)
if (gpriv->fcan == RCANFD_CANFDCLK && gpriv->chip_id == RENESAS_RCAR_GEN3)
/* CANFD clock is further divided by (1/2) within the IP */
fcan_freq /= 2;
@ -1711,20 +1865,51 @@ static int rcar_canfd_probe(struct platform_device *pdev)
gpriv->base = addr;
/* Request IRQ that's common for both channels */
err = devm_request_irq(&pdev->dev, ch_irq,
rcar_canfd_channel_interrupt, 0,
"canfd.chn", gpriv);
if (err) {
dev_err(&pdev->dev, "devm_request_irq(%d) failed, error %d\n",
ch_irq, err);
goto fail_dev;
if (gpriv->chip_id == RENESAS_RCAR_GEN3) {
err = devm_request_irq(&pdev->dev, ch_irq,
rcar_canfd_channel_interrupt, 0,
"canfd.ch_int", gpriv);
if (err) {
dev_err(&pdev->dev, "devm_request_irq(%d) failed, error %d\n",
ch_irq, err);
goto fail_dev;
}
err = devm_request_irq(&pdev->dev, g_irq,
rcar_canfd_global_interrupt, 0,
"canfd.g_int", gpriv);
if (err) {
dev_err(&pdev->dev, "devm_request_irq(%d) failed, error %d\n",
g_irq, err);
goto fail_dev;
}
} else {
err = devm_request_irq(&pdev->dev, g_recc_irq,
rcar_canfd_global_receive_fifo_interrupt, 0,
"canfd.g_recc", gpriv);
if (err) {
dev_err(&pdev->dev, "devm_request_irq(%d) failed, error %d\n",
g_recc_irq, err);
goto fail_dev;
}
err = devm_request_irq(&pdev->dev, g_err_irq,
rcar_canfd_global_err_interrupt, 0,
"canfd.g_err", gpriv);
if (err) {
dev_err(&pdev->dev, "devm_request_irq(%d) failed, error %d\n",
g_err_irq, err);
goto fail_dev;
}
}
err = devm_request_irq(&pdev->dev, g_irq,
rcar_canfd_global_interrupt, 0,
"canfd.gbl", gpriv);
err = reset_control_reset(gpriv->rstc1);
if (err)
goto fail_dev;
err = reset_control_reset(gpriv->rstc2);
if (err) {
dev_err(&pdev->dev, "devm_request_irq(%d) failed, error %d\n",
g_irq, err);
reset_control_assert(gpriv->rstc1);
goto fail_dev;
}
@ -1733,7 +1918,7 @@ static int rcar_canfd_probe(struct platform_device *pdev)
if (err) {
dev_err(&pdev->dev,
"failed to enable peripheral clock, error %d\n", err);
goto fail_dev;
goto fail_reset;
}
err = rcar_canfd_reset_controller(gpriv);
@ -1790,6 +1975,9 @@ static int rcar_canfd_probe(struct platform_device *pdev)
rcar_canfd_disable_global_interrupts(gpriv);
fail_clk:
clk_disable_unprepare(gpriv->clkp);
fail_reset:
reset_control_assert(gpriv->rstc1);
reset_control_assert(gpriv->rstc2);
fail_dev:
return err;
}
@ -1810,6 +1998,9 @@ static int rcar_canfd_remove(struct platform_device *pdev)
/* Enter global sleep mode */
rcar_canfd_set_bit(gpriv->base, RCANFD_GCTR, RCANFD_GCTR_GSLPR);
clk_disable_unprepare(gpriv->clkp);
reset_control_assert(gpriv->rstc1);
reset_control_assert(gpriv->rstc2);
return 0;
}
@ -1827,7 +2018,8 @@ static SIMPLE_DEV_PM_OPS(rcar_canfd_pm_ops, rcar_canfd_suspend,
rcar_canfd_resume);
static const struct of_device_id rcar_canfd_of_table[] = {
{ .compatible = "renesas,rcar-gen3-canfd" },
{ .compatible = "renesas,rcar-gen3-canfd", .data = (void *)RENESAS_RCAR_GEN3 },
{ .compatible = "renesas,rzg2l-canfd", .data = (void *)RENESAS_RZG2L },
{ }
};

View file

@ -1456,7 +1456,7 @@ mcp251xfd_rx_ring_update(const struct mcp251xfd_priv *priv,
}
static void
mcp251xfd_hw_rx_obj_to_skb(struct mcp251xfd_priv *priv,
mcp251xfd_hw_rx_obj_to_skb(const struct mcp251xfd_priv *priv,
const struct mcp251xfd_hw_rx_obj_canfd *hw_rx_obj,
struct sk_buff *skb)
{

View file

@ -13,7 +13,7 @@
static u64 mcp251xfd_timestamp_read(const struct cyclecounter *cc)
{
struct mcp251xfd_priv *priv;
const struct mcp251xfd_priv *priv;
u32 timestamp = 0;
int err;
@ -39,7 +39,7 @@ static void mcp251xfd_timestamp_work(struct work_struct *work)
MCP251XFD_TIMESTAMP_WORK_DELAY_SEC * HZ);
}
void mcp251xfd_skb_set_timestamp(struct mcp251xfd_priv *priv,
void mcp251xfd_skb_set_timestamp(const struct mcp251xfd_priv *priv,
struct sk_buff *skb, u32 timestamp)
{
struct skb_shared_hwtstamps *hwtstamps = skb_hwtstamps(skb);

View file

@ -853,7 +853,7 @@ int mcp251xfd_regmap_init(struct mcp251xfd_priv *priv);
u16 mcp251xfd_crc16_compute2(const void *cmd, size_t cmd_size,
const void *data, size_t data_size);
u16 mcp251xfd_crc16_compute(const void *data, size_t data_size);
void mcp251xfd_skb_set_timestamp(struct mcp251xfd_priv *priv,
void mcp251xfd_skb_set_timestamp(const struct mcp251xfd_priv *priv,
struct sk_buff *skb, u32 timestamp);
void mcp251xfd_timestamp_init(struct mcp251xfd_priv *priv);
void mcp251xfd_timestamp_stop(struct mcp251xfd_priv *priv);

View file

@ -96,23 +96,14 @@ struct es58x_fd_bittiming {
* @ctrlmode: type enum es58x_fd_ctrlmode.
* @canfd_enabled: boolean (0: Classical CAN, 1: CAN and/or CANFD).
* @data_bittiming: Bittiming for flexible data-rate transmission.
* @tdc_enabled: Transmitter Delay Compensation switch (0: disabled,
* 1: enabled). On very high bitrates, the delay between when the
* bit is sent and received on the CANTX and CANRX pins of the
* transceiver start to be significant enough for errors to occur
* and thus need to be compensated.
* @tdco: Transmitter Delay Compensation Offset. Offset value, in time
* quanta, defining the delay between the start of the bit
* reception on the CANRX pin of the transceiver and the SSP
* (Secondary Sample Point). Valid values: 0 to 127.
* @tdcf: Transmitter Delay Compensation Filter window. Defines the
* minimum value for the SSP position, in time quanta. The
* feature is enabled when TDCF is configured to a value greater
* than TDCO. Valid values: 0 to 127.
* @tdc_enabled: Transmitter Delay Compensation switch (0: TDC is
* disabled, 1: TDC is enabled).
* @tdco: Transmitter Delay Compensation Offset.
* @tdcf: Transmitter Delay Compensation Filter window.
*
* Please refer to the microcontroller datasheet: "SAM
* E701/S70/V70/V71 Family" section 49 "Controller Area Network
* (MCAN)" for additional information.
* Please refer to the microcontroller datasheet: "SAM E70/S70/V70/V71
* Family" section 49 "Controller Area Network (MCAN)" for additional
* information.
*/
struct es58x_fd_tx_conf_msg {
struct es58x_fd_bittiming nominal_bittiming;

View file

@ -32,6 +32,12 @@ enum can_mode {
CAN_MODE_SLEEP
};
enum can_termination_gpio {
CAN_TERMINATION_GPIO_DISABLED = 0,
CAN_TERMINATION_GPIO_ENABLED,
CAN_TERMINATION_GPIO_MAX,
};
/*
* CAN common private data
*/
@ -55,6 +61,8 @@ struct can_priv {
unsigned int termination_const_cnt;
const u16 *termination_const;
u16 termination;
struct gpio_desc *termination_gpio;
u16 termination_gpio_ohms[CAN_TERMINATION_GPIO_MAX];
enum can_state state;