mirror of https://gitee.com/openkylin/linux.git
linux-can-next-for-5.10-20200921
-----BEGIN PGP SIGNATURE----- iQFHBAABCgAxFiEEK3kIWJt9yTYMP3ehqclaivrt76kFAl9onIcTHG1rbEBwZW5n dXRyb25peC5kZQAKCRCpyVqK+u3vqf2aCACpwHz9PLKTX4zNDnDRsA7x4c+Gk2nU FOgwodyk9ayax1lLYkgq3ynlb5zM4tRNKLJguoJX69pVvw87uPwBhEbQWQkFFZoW ErkAmYgzGeoA2bu/iJnbsktm7uyEv5a5SntZ+3DSfdnJhuZoU2WfgGa0Z6yDYlWG W6/Dy66XGKzCrK1iFOyvvaNDOhS+Jkul1JWcyhrztQeCbIPTiqVztaE/QA+5cDoT ScO4LdHx7U49XQMiNUjrHFv8qso7fVPicqnefWb19w22wixPzUSGNjCOk284qLn9 eldxYwN77sC8VzPIiyKv7sW8z1o+lq1uTeO5Aa+ATqhCdwTmeNIVfJsp =C8ET -----END PGP SIGNATURE----- Merge tag 'linux-can-next-for-5.10-20200921' of git://git.kernel.org/pub/scm/linux/kernel/git/mkl/linux-can-next Marc Kleine-Budde says: ==================== pull-request: can-next 2020-09-21 this is a pull request of 38 patches for net-next. the first 5 patches are by Colin Ian King, Alexandre Belloni and me and they fix various spelling mistakes. The next patch is by me and fixes the indention in the CAN raw protocol according to the kernel coding style. Diego Elio Pettenò contributes two patches to fix dead links in CAN's Kconfig. Masahiro Yamada's patch removes the "WITH Linux-syscall-note" from SPDX tag of C files. AThe next 4 patches are by me and target the CAN device infrastructure and add error propagation and improve the output of various messages to ease driver development and debugging. YueHaibing's patch for the c_can driver removes an unused inline function. Next follows another patch by Colin Ian King, which removes the unneeded initialization of a variable in the mcba_usb driver. A patch by me annotates a fallthrough in the mscan driver. The ti_hecc driver is converted to use devm_platform_ioremap_resource_byname() in a patch by Dejin Zheng. Liu Shixin's patch converts the pcan_usb_pro driver to make use of le32_add_cpu() instead of open coding it. Wang Hai's patch for the peak_pciefd_main driver removes an unused makro. Vaibhav Gupta's patch converts the pch_can driver to generic power management. Stephane Grosjean improves the pcan_usb usb driver by first documenting the commands sent to the device and by adding support of rxerr/txerr counters. The next patch is by me and cleans up the Kconfig of the CAN SPI drivers. The next 6 patches all target the mcp251x driver, they are by Timo Schlüßler, Andy Shevchenko, Tim Harvey and me. They update the DT bindings documentation, sort the include files alphabetically, add GPIO support, make use of the readx_poll_timeout() helper, and add support for half duplex SPI-controllers. Wolfram Sang contributes a patch to update the contact email address in the mscan driver, while Zhang Changzhong updates the clock handling. The next patch is by and updates the rx-offload infrastructure to support callback less usage. The last 6 patches add support for the mcp25xxfd CAN SPI driver. First the dt-bindings are added by Oleksij Rempel, the regmap infrastructure and the main driver is contributed by me. Kurt Van Dijck adds listen-only support, Manivannan Sadhasivam adds himself as maintainer, and Thomas Kopp himself as a reviewer. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
c5a2a132a3
|
@ -12,6 +12,9 @@ Required properties:
|
|||
Optional properties:
|
||||
- vdd-supply: Regulator that powers the CAN controller.
|
||||
- xceiver-supply: Regulator that powers the CAN transceiver.
|
||||
- gpio-controller: Indicates this device is a GPIO controller.
|
||||
- #gpio-cells: Should be two. The first cell is the pin number and
|
||||
the second cell is used to specify the gpio polarity.
|
||||
|
||||
Example:
|
||||
can0: can@1 {
|
||||
|
@ -19,7 +22,9 @@ Example:
|
|||
reg = <1>;
|
||||
clocks = <&clk24m>;
|
||||
interrupt-parent = <&gpio4>;
|
||||
interrupts = <13 0x2>;
|
||||
interrupts = <13 IRQ_TYPE_LEVEL_LOW>;
|
||||
vdd-supply = <®5v0>;
|
||||
xceiver-supply = <®5v0>;
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/net/can/microchip,mcp25xxfd.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title:
|
||||
Microchip MCP2517FD and MCP2518FD stand-alone CAN controller device tree
|
||||
bindings
|
||||
|
||||
maintainers:
|
||||
- Marc Kleine-Budde <mkl@pengutronix.de>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- const: microchip,mcp2517fd
|
||||
description: for MCP2517FD
|
||||
- const: microchip,mcp2518fd
|
||||
description: for MCP2518FD
|
||||
- const: microchip,mcp25xxfd
|
||||
description: to autodetect chip variant
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts-extended:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
vdd-supply:
|
||||
description: Regulator that powers the CAN controller.
|
||||
maxItems: 1
|
||||
|
||||
xceiver-supply:
|
||||
description: Regulator that powers the CAN transceiver.
|
||||
maxItems: 1
|
||||
|
||||
microchip,rx-int-gpios:
|
||||
description:
|
||||
GPIO phandle of GPIO connected to to INT1 pin of the MCP25XXFD, which
|
||||
signals a pending RX interrupt.
|
||||
maxItems: 1
|
||||
|
||||
spi-max-frequency:
|
||||
description:
|
||||
Must be half or less of "clocks" frequency.
|
||||
maximum: 20000000
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts-extended
|
||||
- clocks
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
spi0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
can@0 {
|
||||
compatible = "microchip,mcp25xxfd";
|
||||
reg = <0>;
|
||||
clocks = <&can0_osc>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&can0_pins>;
|
||||
spi-max-frequency = <20000000>;
|
||||
interrupts-extended = <&gpio 13 IRQ_TYPE_LEVEL_LOW>;
|
||||
microchip,rx-int-gpios = <&gpio 27 GPIO_ACTIVE_LOW>;
|
||||
vdd-supply = <®5v0>;
|
||||
xceiver-supply = <®5v0>;
|
||||
};
|
||||
};
|
|
@ -10671,6 +10671,15 @@ L: linux-input@vger.kernel.org
|
|||
S: Maintained
|
||||
F: drivers/hid/hid-mcp2221.c
|
||||
|
||||
MCP25XXFD SPI-CAN NETWORK DRIVER
|
||||
M: Marc Kleine-Budde <mkl@pengutronix.de>
|
||||
M: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
|
||||
R: Thomas Kopp <thomas.kopp@microchip.com>
|
||||
L: linux-can@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/net/can/microchip,mcp25xxfd.yaml
|
||||
F: drivers/net/can/spi/mcp25xxfd/
|
||||
|
||||
MCP4018 AND MCP4531 MICROCHIP DIGITAL POTENTIOMETER DRIVERS
|
||||
M: Peter Rosin <peda@axentia.se>
|
||||
L: linux-iio@vger.kernel.org
|
||||
|
|
|
@ -41,8 +41,8 @@ config CAN_SLCAN
|
|||
www.canusb.com / www.can232.com / www.mictronics.de / www.canhack.de
|
||||
|
||||
Userspace tools to attach the SLCAN line discipline (slcan_attach,
|
||||
slcand) can be found in the can-utils at the SocketCAN SVN, see
|
||||
http://developer.berlios.de/projects/socketcan for details.
|
||||
slcand) can be found in the can-utils at the linux-can project, see
|
||||
https://github.com/linux-can/can-utils for details.
|
||||
|
||||
The slcan driver supports up to 10 CAN netdevices by default which
|
||||
can be changed by the 'maxdev=xx' module option. This driver can
|
||||
|
|
|
@ -643,7 +643,7 @@ static void at91_read_msg(struct net_device *dev, unsigned int mb)
|
|||
*
|
||||
* The first message goes into mb nr. 1 and issues an interrupt. All
|
||||
* rx ints are disabled in the interrupt handler and a napi poll is
|
||||
* scheduled. We read the mailbox, but do _not_ reenable the mb (to
|
||||
* scheduled. We read the mailbox, but do _not_ re-enable the mb (to
|
||||
* receive another message).
|
||||
*
|
||||
* lower mbxs upper
|
||||
|
@ -661,13 +661,13 @@ static void at91_read_msg(struct net_device *dev, unsigned int mb)
|
|||
*
|
||||
* The variable priv->rx_next points to the next mailbox to read a
|
||||
* message from. As long we're in the lower mailboxes we just read the
|
||||
* mailbox but not reenable it.
|
||||
* mailbox but not re-enable it.
|
||||
*
|
||||
* With completion of the last of the lower mailboxes, we reenable the
|
||||
* With completion of the last of the lower mailboxes, we re-enable the
|
||||
* whole first group, but continue to look for filled mailboxes in the
|
||||
* upper mailboxes. Imagine the second group like overflow mailboxes,
|
||||
* which takes CAN messages if the lower goup is full. While in the
|
||||
* upper group we reenable the mailbox right after reading it. Giving
|
||||
* upper group we re-enable the mailbox right after reading it. Giving
|
||||
* the chip more room to store messages.
|
||||
*
|
||||
* After finishing we look again in the lower group if we've still
|
||||
|
|
|
@ -356,15 +356,6 @@ static void c_can_setup_tx_object(struct net_device *dev, int iface,
|
|||
}
|
||||
}
|
||||
|
||||
static inline void c_can_activate_all_lower_rx_msg_obj(struct net_device *dev,
|
||||
int iface)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = C_CAN_MSG_OBJ_RX_FIRST; i <= C_CAN_MSG_RX_LOW_LAST; i++)
|
||||
c_can_object_get(dev, iface, i, IF_COMM_CLR_NEWDAT);
|
||||
}
|
||||
|
||||
static int c_can_handle_lost_msg_obj(struct net_device *dev,
|
||||
int iface, int objno, u32 ctrl)
|
||||
{
|
||||
|
|
|
@ -538,7 +538,7 @@ static int cc770_err(struct net_device *dev, u8 status)
|
|||
priv->can.can_stats.error_warning++;
|
||||
}
|
||||
} else {
|
||||
/* Back to error avtive */
|
||||
/* Back to error active */
|
||||
cf->can_id |= CAN_ERR_PROT;
|
||||
cf->data[2] = CAN_ERR_PROT_ACTIVE;
|
||||
priv->can.state = CAN_STATE_ERROR_ACTIVE;
|
||||
|
|
|
@ -184,7 +184,7 @@ struct cc770_priv {
|
|||
u8 control_normal_mode; /* Control register for normal mode */
|
||||
u8 cpu_interface; /* CPU interface register */
|
||||
u8 clkout; /* Clock out register */
|
||||
u8 bus_config; /* Bus conffiguration register */
|
||||
u8 bus_config; /* Bus configuration register */
|
||||
|
||||
struct sk_buff *tx_skb;
|
||||
};
|
||||
|
|
|
@ -371,6 +371,28 @@ static int can_rx_state_to_frame(struct net_device *dev, enum can_state state)
|
|||
}
|
||||
}
|
||||
|
||||
static const char *can_get_state_str(const enum can_state state)
|
||||
{
|
||||
switch (state) {
|
||||
case CAN_STATE_ERROR_ACTIVE:
|
||||
return "Error Active";
|
||||
case CAN_STATE_ERROR_WARNING:
|
||||
return "Error Warning";
|
||||
case CAN_STATE_ERROR_PASSIVE:
|
||||
return "Error Passive";
|
||||
case CAN_STATE_BUS_OFF:
|
||||
return "Bus Off";
|
||||
case CAN_STATE_STOPPED:
|
||||
return "Stopped";
|
||||
case CAN_STATE_SLEEPING:
|
||||
return "Sleeping";
|
||||
default:
|
||||
return "<unknown>";
|
||||
}
|
||||
|
||||
return "<unknown>";
|
||||
}
|
||||
|
||||
void can_change_state(struct net_device *dev, struct can_frame *cf,
|
||||
enum can_state tx_state, enum can_state rx_state)
|
||||
{
|
||||
|
@ -382,7 +404,9 @@ void can_change_state(struct net_device *dev, struct can_frame *cf,
|
|||
return;
|
||||
}
|
||||
|
||||
netdev_dbg(dev, "New error state: %d\n", new_state);
|
||||
netdev_dbg(dev, "Controller changed from %s State (%d) into %s State (%d).\n",
|
||||
can_get_state_str(priv->state), priv->state,
|
||||
can_get_state_str(new_state), new_state);
|
||||
|
||||
can_update_state_error_stats(dev, new_state);
|
||||
priv->state = new_state;
|
||||
|
@ -434,7 +458,7 @@ static void can_flush_echo_skb(struct net_device *dev)
|
|||
* of the device driver. The driver must protect access to
|
||||
* priv->echo_skb, if necessary.
|
||||
*/
|
||||
void can_put_echo_skb(struct sk_buff *skb, struct net_device *dev,
|
||||
int can_put_echo_skb(struct sk_buff *skb, struct net_device *dev,
|
||||
unsigned int idx)
|
||||
{
|
||||
struct can_priv *priv = netdev_priv(dev);
|
||||
|
@ -446,13 +470,13 @@ void can_put_echo_skb(struct sk_buff *skb, struct net_device *dev,
|
|||
(skb->protocol != htons(ETH_P_CAN) &&
|
||||
skb->protocol != htons(ETH_P_CANFD))) {
|
||||
kfree_skb(skb);
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!priv->echo_skb[idx]) {
|
||||
skb = can_create_echo_skb(skb);
|
||||
if (!skb)
|
||||
return;
|
||||
return -ENOMEM;
|
||||
|
||||
/* make settings for echo to reduce code in irq context */
|
||||
skb->pkt_type = PACKET_BROADCAST;
|
||||
|
@ -463,9 +487,12 @@ void can_put_echo_skb(struct sk_buff *skb, struct net_device *dev,
|
|||
priv->echo_skb[idx] = skb;
|
||||
} else {
|
||||
/* locking problem with netif_stop_queue() ?? */
|
||||
netdev_err(dev, "%s: BUG! echo_skb is occupied!\n", __func__);
|
||||
netdev_err(dev, "%s: BUG! echo_skb %d is occupied!\n", __func__, idx);
|
||||
kfree_skb(skb);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(can_put_echo_skb);
|
||||
|
||||
|
@ -612,6 +639,10 @@ void can_bus_off(struct net_device *dev)
|
|||
{
|
||||
struct can_priv *priv = netdev_priv(dev);
|
||||
|
||||
if (priv->restart_ms)
|
||||
netdev_info(dev, "bus-off, scheduling restart in %d ms\n",
|
||||
priv->restart_ms);
|
||||
else
|
||||
netdev_info(dev, "bus-off\n");
|
||||
|
||||
netif_carrier_off(dev);
|
||||
|
|
|
@ -230,7 +230,7 @@ struct flexcan_regs {
|
|||
/* FIFO-mode:
|
||||
* MB
|
||||
* 0x080...0x08f 0 RX message buffer
|
||||
* 0x090...0x0df 1-5 reserverd
|
||||
* 0x090...0x0df 1-5 reserved
|
||||
* 0x0e0...0x0ff 6-7 8 entry ID table
|
||||
* (mx25, mx28, mx35, mx53)
|
||||
* 0x0e0...0x2df 6-7..37 8..128 entry ID table
|
||||
|
|
|
@ -726,7 +726,7 @@ static void grcan_err(struct net_device *dev, u32 sources, u32 status)
|
|||
txrx = "on rx ";
|
||||
stats->rx_errors++;
|
||||
}
|
||||
netdev_err(dev, "Fatal AHB buss error %s- halting device\n",
|
||||
netdev_err(dev, "Fatal AHB bus error %s- halting device\n",
|
||||
txrx);
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
|
@ -1243,7 +1243,7 @@ static int grcan_poll(struct napi_struct *napi, int budget)
|
|||
int rx_budget = budget / 2;
|
||||
int tx_budget = budget - rx_budget;
|
||||
|
||||
/* Half of the budget for receiveing messages */
|
||||
/* Half of the budget for receiving messages */
|
||||
rx_work_done = grcan_receive(dev, rx_budget);
|
||||
|
||||
/* Half of the budget for transmitting messages as that can trigger echo
|
||||
|
|
|
@ -20,5 +20,5 @@ config CAN_M_CAN_TCAN4X5X
|
|||
tristate "TCAN4X5X M_CAN device"
|
||||
help
|
||||
Say Y here if you want support for Texas Instruments TCAN4x5x
|
||||
M_CAN controller. This device is a peripherial device that uses the
|
||||
M_CAN controller. This device is a peripheral device that uses the
|
||||
SPI bus for communication.
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* Copyright (C) 2004-2005 Andrey Volkov <avolkov@varma-el.com>,
|
||||
* Varma Electronics Oy
|
||||
* Copyright (C) 2008-2009 Wolfgang Grandegger <wg@grandegger.com>
|
||||
* Copyright (C) 2009 Wolfram Sang, Pengutronix <w.sang@pengutronix.de>
|
||||
* Copyright (C) 2009 Wolfram Sang, Pengutronix <kernel@pengutronix.de>
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
|
|
|
@ -209,6 +209,7 @@ static netdev_tx_t mscan_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
|||
* since buffer with lower id have higher priority (hell..)
|
||||
*/
|
||||
netif_stop_queue(dev);
|
||||
fallthrough;
|
||||
case 2:
|
||||
if (buf_id < priv->prev_buf_id) {
|
||||
priv->cur_pri++;
|
||||
|
@ -540,16 +541,12 @@ static int mscan_open(struct net_device *dev)
|
|||
struct mscan_priv *priv = netdev_priv(dev);
|
||||
struct mscan_regs __iomem *regs = priv->reg_base;
|
||||
|
||||
if (priv->clk_ipg) {
|
||||
ret = clk_prepare_enable(priv->clk_ipg);
|
||||
if (ret)
|
||||
goto exit_retcode;
|
||||
}
|
||||
if (priv->clk_can) {
|
||||
ret = clk_prepare_enable(priv->clk_can);
|
||||
if (ret)
|
||||
goto exit_dis_ipg_clock;
|
||||
}
|
||||
|
||||
/* common open */
|
||||
ret = open_candev(dev);
|
||||
|
@ -583,10 +580,8 @@ static int mscan_open(struct net_device *dev)
|
|||
napi_disable(&priv->napi);
|
||||
close_candev(dev);
|
||||
exit_dis_can_clock:
|
||||
if (priv->clk_can)
|
||||
clk_disable_unprepare(priv->clk_can);
|
||||
exit_dis_ipg_clock:
|
||||
if (priv->clk_ipg)
|
||||
clk_disable_unprepare(priv->clk_ipg);
|
||||
exit_retcode:
|
||||
return ret;
|
||||
|
@ -606,9 +601,7 @@ static int mscan_close(struct net_device *dev)
|
|||
close_candev(dev);
|
||||
free_irq(dev->irq, dev);
|
||||
|
||||
if (priv->clk_can)
|
||||
clk_disable_unprepare(priv->clk_can);
|
||||
if (priv->clk_ipg)
|
||||
clk_disable_unprepare(priv->clk_ipg);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -461,7 +461,7 @@ static void pch_can_int_clr(struct pch_can_priv *priv, u32 mask)
|
|||
PCH_ID2_DIR | (0x7ff << 2));
|
||||
iowrite32(0x0, &priv->regs->ifregs[1].id1);
|
||||
|
||||
/* Claring NewDat, TxRqst & IntPnd */
|
||||
/* Clearing NewDat, TxRqst & IntPnd */
|
||||
pch_can_bit_clear(&priv->regs->ifregs[1].mcont,
|
||||
PCH_IF_MCONT_NEWDAT | PCH_IF_MCONT_INTPND |
|
||||
PCH_IF_MCONT_TXRQXT);
|
||||
|
@ -834,7 +834,7 @@ static int pch_can_open(struct net_device *ndev)
|
|||
struct pch_can_priv *priv = netdev_priv(ndev);
|
||||
int retval;
|
||||
|
||||
/* Regstering the interrupt. */
|
||||
/* Registering the interrupt. */
|
||||
retval = request_irq(priv->dev->irq, pch_can_interrupt, IRQF_SHARED,
|
||||
ndev->name, ndev);
|
||||
if (retval) {
|
||||
|
@ -957,8 +957,7 @@ static void pch_can_remove(struct pci_dev *pdev)
|
|||
free_candev(priv->ndev);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static void pch_can_set_int_custom(struct pch_can_priv *priv)
|
||||
static void __maybe_unused pch_can_set_int_custom(struct pch_can_priv *priv)
|
||||
{
|
||||
/* Clearing the IE, SIE and EIE bits of Can control register. */
|
||||
pch_can_bit_clear(&priv->regs->cont, PCH_CTRL_IE_SIE_EIE);
|
||||
|
@ -969,14 +968,14 @@ static void pch_can_set_int_custom(struct pch_can_priv *priv)
|
|||
}
|
||||
|
||||
/* This function retrieves interrupt enabled for the CAN device. */
|
||||
static u32 pch_can_get_int_enables(struct pch_can_priv *priv)
|
||||
static u32 __maybe_unused pch_can_get_int_enables(struct pch_can_priv *priv)
|
||||
{
|
||||
/* Obtaining the status of IE, SIE and EIE interrupt bits. */
|
||||
return (ioread32(&priv->regs->cont) & PCH_CTRL_IE_SIE_EIE) >> 1;
|
||||
}
|
||||
|
||||
static u32 pch_can_get_rxtx_ir(struct pch_can_priv *priv, u32 buff_num,
|
||||
enum pch_ifreg dir)
|
||||
static u32 __maybe_unused pch_can_get_rxtx_ir(struct pch_can_priv *priv,
|
||||
u32 buff_num, enum pch_ifreg dir)
|
||||
{
|
||||
u32 ie, enable;
|
||||
|
||||
|
@ -997,7 +996,7 @@ static u32 pch_can_get_rxtx_ir(struct pch_can_priv *priv, u32 buff_num,
|
|||
return enable;
|
||||
}
|
||||
|
||||
static void pch_can_set_rx_buffer_link(struct pch_can_priv *priv,
|
||||
static void __maybe_unused pch_can_set_rx_buffer_link(struct pch_can_priv *priv,
|
||||
u32 buffer_num, int set)
|
||||
{
|
||||
iowrite32(PCH_CMASK_RX_TX_GET, &priv->regs->ifregs[0].cmask);
|
||||
|
@ -1013,7 +1012,8 @@ static void pch_can_set_rx_buffer_link(struct pch_can_priv *priv,
|
|||
pch_can_rw_msg_obj(&priv->regs->ifregs[0].creq, buffer_num);
|
||||
}
|
||||
|
||||
static u32 pch_can_get_rx_buffer_link(struct pch_can_priv *priv, u32 buffer_num)
|
||||
static u32 __maybe_unused pch_can_get_rx_buffer_link(struct pch_can_priv *priv,
|
||||
u32 buffer_num)
|
||||
{
|
||||
u32 link;
|
||||
|
||||
|
@ -1027,20 +1027,19 @@ static u32 pch_can_get_rx_buffer_link(struct pch_can_priv *priv, u32 buffer_num)
|
|||
return link;
|
||||
}
|
||||
|
||||
static int pch_can_get_buffer_status(struct pch_can_priv *priv)
|
||||
static int __maybe_unused pch_can_get_buffer_status(struct pch_can_priv *priv)
|
||||
{
|
||||
return (ioread32(&priv->regs->treq1) & 0xffff) |
|
||||
(ioread32(&priv->regs->treq2) << 16);
|
||||
}
|
||||
|
||||
static int pch_can_suspend(struct pci_dev *pdev, pm_message_t state)
|
||||
static int __maybe_unused pch_can_suspend(struct device *dev_d)
|
||||
{
|
||||
int i;
|
||||
int retval;
|
||||
u32 buf_stat; /* Variable for reading the transmit buffer status. */
|
||||
int counter = PCH_COUNTER_LIMIT;
|
||||
|
||||
struct net_device *dev = pci_get_drvdata(pdev);
|
||||
struct net_device *dev = dev_get_drvdata(dev_d);
|
||||
struct pch_can_priv *priv = netdev_priv(dev);
|
||||
|
||||
/* Stop the CAN controller */
|
||||
|
@ -1058,7 +1057,7 @@ static int pch_can_suspend(struct pci_dev *pdev, pm_message_t state)
|
|||
udelay(1);
|
||||
}
|
||||
if (!counter)
|
||||
dev_err(&pdev->dev, "%s -> Transmission time out.\n", __func__);
|
||||
dev_err(dev_d, "%s -> Transmission time out.\n", __func__);
|
||||
|
||||
/* Save interrupt configuration and then disable them */
|
||||
priv->int_enables = pch_can_get_int_enables(priv);
|
||||
|
@ -1081,35 +1080,16 @@ static int pch_can_suspend(struct pci_dev *pdev, pm_message_t state)
|
|||
|
||||
/* Disable all Receive buffers */
|
||||
pch_can_set_rx_all(priv, 0);
|
||||
retval = pci_save_state(pdev);
|
||||
if (retval) {
|
||||
dev_err(&pdev->dev, "pci_save_state failed.\n");
|
||||
} else {
|
||||
pci_enable_wake(pdev, PCI_D3hot, 0);
|
||||
pci_disable_device(pdev);
|
||||
pci_set_power_state(pdev, pci_choose_state(pdev, state));
|
||||
}
|
||||
|
||||
return retval;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pch_can_resume(struct pci_dev *pdev)
|
||||
static int __maybe_unused pch_can_resume(struct device *dev_d)
|
||||
{
|
||||
int i;
|
||||
int retval;
|
||||
struct net_device *dev = pci_get_drvdata(pdev);
|
||||
struct net_device *dev = dev_get_drvdata(dev_d);
|
||||
struct pch_can_priv *priv = netdev_priv(dev);
|
||||
|
||||
pci_set_power_state(pdev, PCI_D0);
|
||||
pci_restore_state(pdev);
|
||||
retval = pci_enable_device(pdev);
|
||||
if (retval) {
|
||||
dev_err(&pdev->dev, "pci_enable_device failed.\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
pci_enable_wake(pdev, PCI_D3hot, 0);
|
||||
|
||||
priv->can.state = CAN_STATE_ERROR_ACTIVE;
|
||||
|
||||
/* Disabling all interrupts. */
|
||||
|
@ -1146,12 +1126,8 @@ static int pch_can_resume(struct pci_dev *pdev)
|
|||
/* Restore Run Mode */
|
||||
pch_can_set_run_mode(priv, PCH_CAN_RUN);
|
||||
|
||||
return retval;
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define pch_can_suspend NULL
|
||||
#define pch_can_resume NULL
|
||||
#endif
|
||||
|
||||
static int pch_can_get_berr_counter(const struct net_device *dev,
|
||||
struct can_berr_counter *bec)
|
||||
|
@ -1252,13 +1228,16 @@ static int pch_can_probe(struct pci_dev *pdev,
|
|||
return rc;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(pch_can_pm_ops,
|
||||
pch_can_suspend,
|
||||
pch_can_resume);
|
||||
|
||||
static struct pci_driver pch_can_pci_driver = {
|
||||
.name = "pch_can",
|
||||
.id_table = pch_pci_tbl,
|
||||
.probe = pch_can_probe,
|
||||
.remove = pch_can_remove,
|
||||
.suspend = pch_can_suspend,
|
||||
.resume = pch_can_resume,
|
||||
.driver.pm = &pch_can_pm_ops,
|
||||
};
|
||||
|
||||
module_pci_driver(pch_can_pci_driver);
|
||||
|
|
|
@ -116,8 +116,6 @@ MODULE_LICENSE("GPL v2");
|
|||
#define CANFD_CTL_IRQ_CL_DEF 16 /* Rx msg max nb per IRQ in Rx DMA */
|
||||
#define CANFD_CTL_IRQ_TL_DEF 10 /* Time before IRQ if < CL (x100 µs) */
|
||||
|
||||
#define CANFD_OPTIONS_SET (CANFD_OPTION_ERROR | CANFD_OPTION_BUSLOAD)
|
||||
|
||||
/* Tx anticipation window (link logical address should be aligned on 2K
|
||||
* boundary)
|
||||
*/
|
||||
|
|
|
@ -351,6 +351,17 @@ int can_rx_offload_add_fifo(struct net_device *dev,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(can_rx_offload_add_fifo);
|
||||
|
||||
int can_rx_offload_add_manual(struct net_device *dev,
|
||||
struct can_rx_offload *offload,
|
||||
unsigned int weight)
|
||||
{
|
||||
if (offload->mailbox_read)
|
||||
return -EINVAL;
|
||||
|
||||
return can_rx_offload_init_queue(dev, offload, weight);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(can_rx_offload_add_manual);
|
||||
|
||||
void can_rx_offload_enable(struct can_rx_offload *offload)
|
||||
{
|
||||
napi_enable(&offload->napi);
|
||||
|
|
|
@ -97,7 +97,7 @@ MODULE_DEVICE_TABLE(pci, peak_pci_tbl);
|
|||
/* GPIOICR byte access offsets */
|
||||
#define PITA_GPOUT 0x18 /* GPx output value */
|
||||
#define PITA_GPIN 0x19 /* GPx input value */
|
||||
#define PITA_GPOEN 0x1A /* configure GPx as ouput pin */
|
||||
#define PITA_GPOEN 0x1A /* configure GPx as output pin */
|
||||
|
||||
/* I2C GP bits */
|
||||
#define PITA_GPIN_SCL 0x01 /* Serial Clock Line */
|
||||
|
|
|
@ -671,7 +671,7 @@ static int pcan_probe(struct pcmcia_device *pdev)
|
|||
card->fw_major = pcan_read_reg(card, PCC_FW_MAJOR);
|
||||
card->fw_minor = pcan_read_reg(card, PCC_FW_MINOR);
|
||||
|
||||
/* display board name and firware version */
|
||||
/* display board name and firmware version */
|
||||
dev_info(&pdev->dev, "PEAK-System pcmcia card %s fw %d.%d\n",
|
||||
pdev->prod_id[1] ? pdev->prod_id[1] : "PCAN-PC Card",
|
||||
card->fw_major, card->fw_minor);
|
||||
|
|
|
@ -5,14 +5,14 @@ config CAN_SOFTING
|
|||
help
|
||||
Support for CAN cards from Softing Gmbh & some cards
|
||||
from Vector Gmbh.
|
||||
Softing Gmbh CAN cards come with 1 or 2 physical busses.
|
||||
Softing Gmbh CAN cards come with 1 or 2 physical buses.
|
||||
Those cards typically use Dual Port RAM to communicate
|
||||
with the host CPU. The interface is then identical for PCI
|
||||
and PCMCIA cards. This driver operates on a platform device,
|
||||
which has been created by softing_cs or softing_pci driver.
|
||||
Warning:
|
||||
The API of the card does not allow fine control per bus, but
|
||||
controls the 2 busses on the card together.
|
||||
controls the 2 buses on the card together.
|
||||
As such, some actions (start/stop/busoff recovery) on 1 bus
|
||||
must bring down the other bus too temporarily.
|
||||
|
||||
|
@ -24,7 +24,7 @@ config CAN_SOFTING_CS
|
|||
Support for PCMCIA cards from Softing Gmbh & some cards
|
||||
from Vector Gmbh.
|
||||
You need firmware for these, which you can get at
|
||||
http://developer.berlios.de/projects/socketcan/
|
||||
https://github.com/linux-can/can-firmware
|
||||
This version of the driver is written against
|
||||
firmware version 4.6 (softing-fw-4.6-binaries.tar.gz)
|
||||
In order to use the card as CAN device, you need the Softing generic
|
||||
|
|
|
@ -273,7 +273,7 @@ int softing_load_app_fw(const char *file, struct softing *card)
|
|||
goto failed;
|
||||
}
|
||||
|
||||
/* regualar data */
|
||||
/* regular data */
|
||||
for (sum = 0, j = 0; j < len; ++j)
|
||||
sum += dat[j];
|
||||
/* work in 16bit (target) */
|
||||
|
@ -474,14 +474,14 @@ int softing_startstop(struct net_device *dev, int up)
|
|||
if (ret)
|
||||
goto failed;
|
||||
if (!bus_bitmask_start)
|
||||
/* no busses to be brought up */
|
||||
/* no buses to be brought up */
|
||||
goto card_done;
|
||||
|
||||
if ((bus_bitmask_start & 1) && (bus_bitmask_start & 2)
|
||||
&& (softing_error_reporting(card->net[0])
|
||||
!= softing_error_reporting(card->net[1]))) {
|
||||
dev_alert(&card->pdev->dev,
|
||||
"err_reporting flag differs for busses\n");
|
||||
"err_reporting flag differs for buses\n");
|
||||
goto invalid;
|
||||
}
|
||||
error_reporting = 0;
|
||||
|
@ -635,7 +635,7 @@ int softing_startstop(struct net_device *dev, int up)
|
|||
priv->can.state = CAN_STATE_ERROR_ACTIVE;
|
||||
open_candev(netdev);
|
||||
if (dev != netdev) {
|
||||
/* notify other busses on the restart */
|
||||
/* notify other buses on the restart */
|
||||
softing_netdev_rx(netdev, &msg, 0);
|
||||
++priv->can.can_stats.restarts;
|
||||
}
|
||||
|
|
|
@ -170,8 +170,8 @@ static int softing_handle_1(struct softing *card)
|
|||
msg.can_dlc = CAN_ERR_DLC;
|
||||
msg.data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
|
||||
/*
|
||||
* service to all busses, we don't know which it was applicable
|
||||
* but only service busses that are online
|
||||
* service to all buses, we don't know which it was applicable
|
||||
* but only service buses that are online
|
||||
*/
|
||||
for (j = 0; j < ARRAY_SIZE(card->net); ++j) {
|
||||
netdev = card->net[j];
|
||||
|
@ -339,7 +339,7 @@ static irqreturn_t softing_irq_thread(int irq, void *dev_id)
|
|||
continue;
|
||||
priv = netdev_priv(netdev);
|
||||
if (!canif_is_active(netdev))
|
||||
/* it makes no sense to wake dead busses */
|
||||
/* it makes no sense to wake dead buses */
|
||||
continue;
|
||||
if (priv->tx.pending >= TX_ECHO_SKB_MAX)
|
||||
continue;
|
||||
|
@ -374,7 +374,7 @@ static irqreturn_t softing_irq_v1(int irq, void *dev_id)
|
|||
}
|
||||
|
||||
/*
|
||||
* netdev/candev inter-operability
|
||||
* netdev/candev interoperability
|
||||
*/
|
||||
static int softing_netdev_open(struct net_device *ndev)
|
||||
{
|
||||
|
|
|
@ -19,7 +19,7 @@ struct softing_platform_data {
|
|||
* 16bit, shared interrupt
|
||||
*/
|
||||
int generation;
|
||||
int nbus; /* # busses on device */
|
||||
int nbus; /* # buses on device */
|
||||
unsigned int freq; /* operating frequency in Hz */
|
||||
unsigned int max_brp;
|
||||
unsigned int max_sjw;
|
||||
|
|
|
@ -4,15 +4,15 @@ menu "CAN SPI interfaces"
|
|||
|
||||
config CAN_HI311X
|
||||
tristate "Holt HI311x SPI CAN controllers"
|
||||
depends on CAN_DEV && SPI && HAS_DMA
|
||||
help
|
||||
Driver for the Holt HI311x SPI CAN controllers.
|
||||
|
||||
config CAN_MCP251X
|
||||
tristate "Microchip MCP251x and MCP25625 SPI CAN controllers"
|
||||
depends on HAS_DMA
|
||||
help
|
||||
Driver for the Microchip MCP251x and MCP25625 SPI CAN
|
||||
controllers.
|
||||
|
||||
source "drivers/net/can/spi/mcp25xxfd/Kconfig"
|
||||
|
||||
endmenu
|
||||
|
|
|
@ -6,3 +6,4 @@
|
|||
|
||||
obj-$(CONFIG_CAN_HI311X) += hi311x.o
|
||||
obj-$(CONFIG_CAN_MCP251X) += mcp251x.o
|
||||
obj-y += mcp25xxfd/
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
* Copyright 2007
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/can/core.h>
|
||||
#include <linux/can/dev.h>
|
||||
#include <linux/can/led.h>
|
||||
|
@ -27,17 +28,20 @@
|
|||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/freezer.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
/* SPI interface instruction set */
|
||||
#define INSTRUCTION_WRITE 0x02
|
||||
|
@ -52,6 +56,30 @@
|
|||
#define INSTRUCTION_RTS(n) (0x80 | ((n) & 0x07))
|
||||
|
||||
/* MPC251x registers */
|
||||
#define BFPCTRL 0x0c
|
||||
# define BFPCTRL_B0BFM BIT(0)
|
||||
# define BFPCTRL_B1BFM BIT(1)
|
||||
# define BFPCTRL_BFM(n) (BFPCTRL_B0BFM << (n))
|
||||
# define BFPCTRL_BFM_MASK GENMASK(1, 0)
|
||||
# define BFPCTRL_B0BFE BIT(2)
|
||||
# define BFPCTRL_B1BFE BIT(3)
|
||||
# define BFPCTRL_BFE(n) (BFPCTRL_B0BFE << (n))
|
||||
# define BFPCTRL_BFE_MASK GENMASK(3, 2)
|
||||
# define BFPCTRL_B0BFS BIT(4)
|
||||
# define BFPCTRL_B1BFS BIT(5)
|
||||
# define BFPCTRL_BFS(n) (BFPCTRL_B0BFS << (n))
|
||||
# define BFPCTRL_BFS_MASK GENMASK(5, 4)
|
||||
#define TXRTSCTRL 0x0d
|
||||
# define TXRTSCTRL_B0RTSM BIT(0)
|
||||
# define TXRTSCTRL_B1RTSM BIT(1)
|
||||
# define TXRTSCTRL_B2RTSM BIT(2)
|
||||
# define TXRTSCTRL_RTSM(n) (TXRTSCTRL_B0RTSM << (n))
|
||||
# define TXRTSCTRL_RTSM_MASK GENMASK(2, 0)
|
||||
# define TXRTSCTRL_B0RTS BIT(3)
|
||||
# define TXRTSCTRL_B1RTS BIT(4)
|
||||
# define TXRTSCTRL_B2RTS BIT(5)
|
||||
# define TXRTSCTRL_RTS(n) (TXRTSCTRL_B0RTS << (n))
|
||||
# define TXRTSCTRL_RTS_MASK GENMASK(5, 3)
|
||||
#define CANSTAT 0x0e
|
||||
#define CANCTRL 0x0f
|
||||
# define CANCTRL_REQOP_MASK 0xe0
|
||||
|
@ -225,6 +253,10 @@ struct mcp251x_priv {
|
|||
struct regulator *power;
|
||||
struct regulator *transceiver;
|
||||
struct clk *clk;
|
||||
#ifdef CONFIG_GPIOLIB
|
||||
struct gpio_chip gpio;
|
||||
u8 reg_bfpctrl;
|
||||
#endif
|
||||
};
|
||||
|
||||
#define MCP251X_IS(_model) \
|
||||
|
@ -290,8 +322,12 @@ static u8 mcp251x_read_reg(struct spi_device *spi, u8 reg)
|
|||
priv->spi_tx_buf[0] = INSTRUCTION_READ;
|
||||
priv->spi_tx_buf[1] = reg;
|
||||
|
||||
if (spi->controller->flags & SPI_CONTROLLER_HALF_DUPLEX) {
|
||||
spi_write_then_read(spi, priv->spi_tx_buf, 2, &val, 1);
|
||||
} else {
|
||||
mcp251x_spi_trans(spi, 3);
|
||||
val = priv->spi_rx_buf[2];
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
@ -303,10 +339,18 @@ static void mcp251x_read_2regs(struct spi_device *spi, u8 reg, u8 *v1, u8 *v2)
|
|||
priv->spi_tx_buf[0] = INSTRUCTION_READ;
|
||||
priv->spi_tx_buf[1] = reg;
|
||||
|
||||
if (spi->controller->flags & SPI_CONTROLLER_HALF_DUPLEX) {
|
||||
u8 val[2] = { 0 };
|
||||
|
||||
spi_write_then_read(spi, priv->spi_tx_buf, 2, val, 2);
|
||||
*v1 = val[0];
|
||||
*v2 = val[1];
|
||||
} else {
|
||||
mcp251x_spi_trans(spi, 4);
|
||||
|
||||
*v1 = priv->spi_rx_buf[2];
|
||||
*v2 = priv->spi_rx_buf[3];
|
||||
}
|
||||
}
|
||||
|
||||
static void mcp251x_write_reg(struct spi_device *spi, u8 reg, u8 val)
|
||||
|
@ -345,6 +389,222 @@ static void mcp251x_write_bits(struct spi_device *spi, u8 reg,
|
|||
mcp251x_spi_trans(spi, 4);
|
||||
}
|
||||
|
||||
static u8 mcp251x_read_stat(struct spi_device *spi)
|
||||
{
|
||||
return mcp251x_read_reg(spi, CANSTAT) & CANCTRL_REQOP_MASK;
|
||||
}
|
||||
|
||||
#define mcp251x_read_stat_poll_timeout(addr, val, cond, delay_us, timeout_us) \
|
||||
readx_poll_timeout(mcp251x_read_stat, addr, val, cond, \
|
||||
delay_us, timeout_us)
|
||||
|
||||
#ifdef CONFIG_GPIOLIB
|
||||
enum {
|
||||
MCP251X_GPIO_TX0RTS = 0, /* inputs */
|
||||
MCP251X_GPIO_TX1RTS,
|
||||
MCP251X_GPIO_TX2RTS,
|
||||
MCP251X_GPIO_RX0BF, /* outputs */
|
||||
MCP251X_GPIO_RX1BF,
|
||||
};
|
||||
|
||||
#define MCP251X_GPIO_INPUT_MASK \
|
||||
GENMASK(MCP251X_GPIO_TX2RTS, MCP251X_GPIO_TX0RTS)
|
||||
#define MCP251X_GPIO_OUTPUT_MASK \
|
||||
GENMASK(MCP251X_GPIO_RX1BF, MCP251X_GPIO_RX0BF)
|
||||
|
||||
static const char * const mcp251x_gpio_names[] = {
|
||||
[MCP251X_GPIO_TX0RTS] = "TX0RTS", /* inputs */
|
||||
[MCP251X_GPIO_TX1RTS] = "TX1RTS",
|
||||
[MCP251X_GPIO_TX2RTS] = "TX2RTS",
|
||||
[MCP251X_GPIO_RX0BF] = "RX0BF", /* outputs */
|
||||
[MCP251X_GPIO_RX1BF] = "RX1BF",
|
||||
};
|
||||
|
||||
static inline bool mcp251x_gpio_is_input(unsigned int offset)
|
||||
{
|
||||
return offset <= MCP251X_GPIO_TX2RTS;
|
||||
}
|
||||
|
||||
static int mcp251x_gpio_request(struct gpio_chip *chip,
|
||||
unsigned int offset)
|
||||
{
|
||||
struct mcp251x_priv *priv = gpiochip_get_data(chip);
|
||||
u8 val;
|
||||
|
||||
/* nothing to be done for inputs */
|
||||
if (mcp251x_gpio_is_input(offset))
|
||||
return 0;
|
||||
|
||||
val = BFPCTRL_BFE(offset - MCP251X_GPIO_RX0BF);
|
||||
|
||||
mutex_lock(&priv->mcp_lock);
|
||||
mcp251x_write_bits(priv->spi, BFPCTRL, val, val);
|
||||
mutex_unlock(&priv->mcp_lock);
|
||||
|
||||
priv->reg_bfpctrl |= val;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mcp251x_gpio_free(struct gpio_chip *chip,
|
||||
unsigned int offset)
|
||||
{
|
||||
struct mcp251x_priv *priv = gpiochip_get_data(chip);
|
||||
u8 val;
|
||||
|
||||
/* nothing to be done for inputs */
|
||||
if (mcp251x_gpio_is_input(offset))
|
||||
return;
|
||||
|
||||
val = BFPCTRL_BFE(offset - MCP251X_GPIO_RX0BF);
|
||||
|
||||
mutex_lock(&priv->mcp_lock);
|
||||
mcp251x_write_bits(priv->spi, BFPCTRL, val, 0);
|
||||
mutex_unlock(&priv->mcp_lock);
|
||||
|
||||
priv->reg_bfpctrl &= ~val;
|
||||
}
|
||||
|
||||
static int mcp251x_gpio_get_direction(struct gpio_chip *chip,
|
||||
unsigned int offset)
|
||||
{
|
||||
if (mcp251x_gpio_is_input(offset))
|
||||
return GPIOF_DIR_IN;
|
||||
|
||||
return GPIOF_DIR_OUT;
|
||||
}
|
||||
|
||||
static int mcp251x_gpio_get(struct gpio_chip *chip, unsigned int offset)
|
||||
{
|
||||
struct mcp251x_priv *priv = gpiochip_get_data(chip);
|
||||
u8 reg, mask, val;
|
||||
|
||||
if (mcp251x_gpio_is_input(offset)) {
|
||||
reg = TXRTSCTRL;
|
||||
mask = TXRTSCTRL_RTS(offset);
|
||||
} else {
|
||||
reg = BFPCTRL;
|
||||
mask = BFPCTRL_BFS(offset - MCP251X_GPIO_RX0BF);
|
||||
}
|
||||
|
||||
mutex_lock(&priv->mcp_lock);
|
||||
val = mcp251x_read_reg(priv->spi, reg);
|
||||
mutex_unlock(&priv->mcp_lock);
|
||||
|
||||
return !!(val & mask);
|
||||
}
|
||||
|
||||
static int mcp251x_gpio_get_multiple(struct gpio_chip *chip,
|
||||
unsigned long *maskp, unsigned long *bitsp)
|
||||
{
|
||||
struct mcp251x_priv *priv = gpiochip_get_data(chip);
|
||||
unsigned long bits = 0;
|
||||
u8 val;
|
||||
|
||||
mutex_lock(&priv->mcp_lock);
|
||||
if (maskp[0] & MCP251X_GPIO_INPUT_MASK) {
|
||||
val = mcp251x_read_reg(priv->spi, TXRTSCTRL);
|
||||
val = FIELD_GET(TXRTSCTRL_RTS_MASK, val);
|
||||
bits |= FIELD_PREP(MCP251X_GPIO_INPUT_MASK, val);
|
||||
}
|
||||
if (maskp[0] & MCP251X_GPIO_OUTPUT_MASK) {
|
||||
val = mcp251x_read_reg(priv->spi, BFPCTRL);
|
||||
val = FIELD_GET(BFPCTRL_BFS_MASK, val);
|
||||
bits |= FIELD_PREP(MCP251X_GPIO_OUTPUT_MASK, val);
|
||||
}
|
||||
mutex_unlock(&priv->mcp_lock);
|
||||
|
||||
bitsp[0] = bits;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mcp251x_gpio_set(struct gpio_chip *chip, unsigned int offset,
|
||||
int value)
|
||||
{
|
||||
struct mcp251x_priv *priv = gpiochip_get_data(chip);
|
||||
u8 mask, val;
|
||||
|
||||
mask = BFPCTRL_BFS(offset - MCP251X_GPIO_RX0BF);
|
||||
val = value ? mask : 0;
|
||||
|
||||
mutex_lock(&priv->mcp_lock);
|
||||
mcp251x_write_bits(priv->spi, BFPCTRL, mask, val);
|
||||
mutex_unlock(&priv->mcp_lock);
|
||||
|
||||
priv->reg_bfpctrl &= ~mask;
|
||||
priv->reg_bfpctrl |= val;
|
||||
}
|
||||
|
||||
static void
|
||||
mcp251x_gpio_set_multiple(struct gpio_chip *chip,
|
||||
unsigned long *maskp, unsigned long *bitsp)
|
||||
{
|
||||
struct mcp251x_priv *priv = gpiochip_get_data(chip);
|
||||
u8 mask, val;
|
||||
|
||||
mask = FIELD_GET(MCP251X_GPIO_OUTPUT_MASK, maskp[0]);
|
||||
mask = FIELD_PREP(BFPCTRL_BFS_MASK, mask);
|
||||
|
||||
val = FIELD_GET(MCP251X_GPIO_OUTPUT_MASK, bitsp[0]);
|
||||
val = FIELD_PREP(BFPCTRL_BFS_MASK, val);
|
||||
|
||||
if (!mask)
|
||||
return;
|
||||
|
||||
mutex_lock(&priv->mcp_lock);
|
||||
mcp251x_write_bits(priv->spi, BFPCTRL, mask, val);
|
||||
mutex_unlock(&priv->mcp_lock);
|
||||
|
||||
priv->reg_bfpctrl &= ~mask;
|
||||
priv->reg_bfpctrl |= val;
|
||||
}
|
||||
|
||||
static void mcp251x_gpio_restore(struct spi_device *spi)
|
||||
{
|
||||
struct mcp251x_priv *priv = spi_get_drvdata(spi);
|
||||
|
||||
mcp251x_write_reg(spi, BFPCTRL, priv->reg_bfpctrl);
|
||||
}
|
||||
|
||||
static int mcp251x_gpio_setup(struct mcp251x_priv *priv)
|
||||
{
|
||||
struct gpio_chip *gpio = &priv->gpio;
|
||||
|
||||
if (!device_property_present(&priv->spi->dev, "gpio-controller"))
|
||||
return 0;
|
||||
|
||||
/* gpiochip handles TX[0..2]RTS and RX[0..1]BF */
|
||||
gpio->label = priv->spi->modalias;
|
||||
gpio->parent = &priv->spi->dev;
|
||||
gpio->owner = THIS_MODULE;
|
||||
gpio->request = mcp251x_gpio_request;
|
||||
gpio->free = mcp251x_gpio_free;
|
||||
gpio->get_direction = mcp251x_gpio_get_direction;
|
||||
gpio->get = mcp251x_gpio_get;
|
||||
gpio->get_multiple = mcp251x_gpio_get_multiple;
|
||||
gpio->set = mcp251x_gpio_set;
|
||||
gpio->set_multiple = mcp251x_gpio_set_multiple;
|
||||
gpio->base = -1;
|
||||
gpio->ngpio = ARRAY_SIZE(mcp251x_gpio_names);
|
||||
gpio->names = mcp251x_gpio_names;
|
||||
gpio->can_sleep = true;
|
||||
#ifdef CONFIG_OF_GPIO
|
||||
gpio->of_node = priv->spi->dev.of_node;
|
||||
#endif
|
||||
|
||||
return devm_gpiochip_add_data(&priv->spi->dev, gpio, priv);
|
||||
}
|
||||
#else
|
||||
static inline void mcp251x_gpio_restore(struct spi_device *spi)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int mcp251x_gpio_setup(struct mcp251x_priv *priv)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void mcp251x_hw_tx_frame(struct spi_device *spi, u8 *buf,
|
||||
int len, int tx_buf_idx)
|
||||
{
|
||||
|
@ -409,9 +669,17 @@ static void mcp251x_hw_rx_frame(struct spi_device *spi, u8 *buf,
|
|||
buf[i] = mcp251x_read_reg(spi, RXBCTRL(buf_idx) + i);
|
||||
} else {
|
||||
priv->spi_tx_buf[RXBCTRL_OFF] = INSTRUCTION_READ_RXB(buf_idx);
|
||||
if (spi->controller->flags & SPI_CONTROLLER_HALF_DUPLEX) {
|
||||
spi_write_then_read(spi, priv->spi_tx_buf, 1,
|
||||
priv->spi_rx_buf,
|
||||
SPI_TRANSFER_BUF_LEN);
|
||||
memcpy(buf + 1, priv->spi_rx_buf,
|
||||
SPI_TRANSFER_BUF_LEN - 1);
|
||||
} else {
|
||||
mcp251x_spi_trans(spi, SPI_TRANSFER_BUF_LEN);
|
||||
memcpy(buf, priv->spi_rx_buf, SPI_TRANSFER_BUF_LEN);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void mcp251x_hw_rx(struct spi_device *spi, int buf_idx)
|
||||
|
@ -471,7 +739,8 @@ static void mcp251x_hw_sleep(struct spi_device *spi)
|
|||
/* May only be called when device is sleeping! */
|
||||
static int mcp251x_hw_wake(struct spi_device *spi)
|
||||
{
|
||||
unsigned long timeout;
|
||||
u8 value;
|
||||
int ret;
|
||||
|
||||
/* Force wakeup interrupt to wake device, but don't execute IST */
|
||||
disable_irq(spi->irq);
|
||||
|
@ -484,14 +753,12 @@ static int mcp251x_hw_wake(struct spi_device *spi)
|
|||
mcp251x_write_reg(spi, CANCTRL, CANCTRL_REQOP_CONF);
|
||||
|
||||
/* Wait for the device to enter config mode */
|
||||
timeout = jiffies + HZ;
|
||||
while ((mcp251x_read_reg(spi, CANSTAT) & CANCTRL_REQOP_MASK) !=
|
||||
CANCTRL_REQOP_CONF) {
|
||||
schedule();
|
||||
if (time_after(jiffies, timeout)) {
|
||||
ret = mcp251x_read_stat_poll_timeout(spi, value, value == CANCTRL_REQOP_CONF,
|
||||
MCP251X_OST_DELAY_MS * 1000,
|
||||
USEC_PER_SEC);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "MCP251x didn't enter in config mode\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Disable and clear pending interrupts */
|
||||
|
@ -546,7 +813,8 @@ static int mcp251x_do_set_mode(struct net_device *net, enum can_mode mode)
|
|||
static int mcp251x_set_normal_mode(struct spi_device *spi)
|
||||
{
|
||||
struct mcp251x_priv *priv = spi_get_drvdata(spi);
|
||||
unsigned long timeout;
|
||||
u8 value;
|
||||
int ret;
|
||||
|
||||
/* Enable interrupts */
|
||||
mcp251x_write_reg(spi, CANINTE,
|
||||
|
@ -564,13 +832,12 @@ static int mcp251x_set_normal_mode(struct spi_device *spi)
|
|||
mcp251x_write_reg(spi, CANCTRL, CANCTRL_REQOP_NORMAL);
|
||||
|
||||
/* Wait for the device to enter normal mode */
|
||||
timeout = jiffies + HZ;
|
||||
while (mcp251x_read_reg(spi, CANSTAT) & CANCTRL_REQOP_MASK) {
|
||||
schedule();
|
||||
if (time_after(jiffies, timeout)) {
|
||||
ret = mcp251x_read_stat_poll_timeout(spi, value, value == 0,
|
||||
MCP251X_OST_DELAY_MS * 1000,
|
||||
USEC_PER_SEC);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "MCP251x didn't enter in normal mode\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
priv->can.state = CAN_STATE_ERROR_ACTIVE;
|
||||
|
@ -614,7 +881,7 @@ static int mcp251x_setup(struct net_device *net, struct spi_device *spi)
|
|||
static int mcp251x_hw_reset(struct spi_device *spi)
|
||||
{
|
||||
struct mcp251x_priv *priv = spi_get_drvdata(spi);
|
||||
unsigned long timeout;
|
||||
u8 value;
|
||||
int ret;
|
||||
|
||||
/* Wait for oscillator startup timer after power up */
|
||||
|
@ -629,19 +896,12 @@ static int mcp251x_hw_reset(struct spi_device *spi)
|
|||
mdelay(MCP251X_OST_DELAY_MS);
|
||||
|
||||
/* Wait for reset to finish */
|
||||
timeout = jiffies + HZ;
|
||||
while ((mcp251x_read_reg(spi, CANSTAT) & CANCTRL_REQOP_MASK) !=
|
||||
CANCTRL_REQOP_CONF) {
|
||||
usleep_range(MCP251X_OST_DELAY_MS * 1000,
|
||||
MCP251X_OST_DELAY_MS * 1000 * 2);
|
||||
|
||||
if (time_after(jiffies, timeout)) {
|
||||
dev_err(&spi->dev,
|
||||
"MCP251x didn't enter in conf mode after reset\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
ret = mcp251x_read_stat_poll_timeout(spi, value, value == CANCTRL_REQOP_CONF,
|
||||
MCP251X_OST_DELAY_MS * 1000,
|
||||
USEC_PER_SEC);
|
||||
if (ret)
|
||||
dev_err(&spi->dev, "MCP251x didn't enter in conf mode after reset\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mcp251x_hw_probe(struct spi_device *spi)
|
||||
|
@ -761,6 +1021,7 @@ static void mcp251x_restart_work_handler(struct work_struct *ws)
|
|||
if (priv->after_suspend & AFTER_SUSPEND_POWER) {
|
||||
mcp251x_hw_reset(spi);
|
||||
mcp251x_setup(net, spi);
|
||||
mcp251x_gpio_restore(spi);
|
||||
} else {
|
||||
mcp251x_hw_wake(spi);
|
||||
}
|
||||
|
@ -1136,6 +1397,10 @@ static int mcp251x_can_probe(struct spi_device *spi)
|
|||
|
||||
devm_can_led_init(net);
|
||||
|
||||
ret = mcp251x_gpio_setup(priv);
|
||||
if (ret)
|
||||
goto error_probe;
|
||||
|
||||
netdev_info(net, "MCP%x successfully initialized.\n", priv->model);
|
||||
return 0;
|
||||
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
config CAN_MCP25XXFD
|
||||
tristate "Microchip MCP25xxFD SPI CAN controllers"
|
||||
select REGMAP
|
||||
help
|
||||
Driver for the Microchip MCP25XXFD SPI FD-CAN controller
|
||||
family.
|
||||
|
||||
config CAN_MCP25XXFD_SANITY
|
||||
depends on CAN_MCP25XXFD
|
||||
bool "Additional Sanity Checks"
|
||||
help
|
||||
This option enables additional sanity checks in the driver,
|
||||
that compares various internal counters with the in chip
|
||||
variants. This comes with a runtime overhead.
|
||||
Disable if unsure.
|
|
@ -0,0 +1,8 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
obj-$(CONFIG_CAN_MCP25XXFD) += mcp25xxfd.o
|
||||
|
||||
mcp25xxfd-objs :=
|
||||
mcp25xxfd-objs += mcp25xxfd-core.o
|
||||
mcp25xxfd-objs += mcp25xxfd-crc16.o
|
||||
mcp25xxfd-objs += mcp25xxfd-regmap.o
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,89 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// mcp25xxfd - Microchip MCP25xxFD Family CAN controller driver
|
||||
//
|
||||
// Copyright (c) 2020 Pengutronix,
|
||||
// Marc Kleine-Budde <kernel@pengutronix.de>
|
||||
//
|
||||
// Based on:
|
||||
//
|
||||
// CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface
|
||||
//
|
||||
// Copyright (c) 2019 Martin Sperl <kernel@martin.sperl.org>
|
||||
//
|
||||
|
||||
#include "mcp25xxfd.h"
|
||||
|
||||
/* The standard crc16 in linux/crc16.h is unfortunately not computing
|
||||
* the correct results (left shift vs. right shift). So here an
|
||||
* implementation with a table generated with the help of:
|
||||
*
|
||||
* http://lkml.iu.edu/hypermail/linux/kernel/0508.1/1085.html
|
||||
*/
|
||||
static const u16 mcp25xxfd_crc16_table[] = {
|
||||
0x0000, 0x8005, 0x800f, 0x000a, 0x801b, 0x001e, 0x0014, 0x8011,
|
||||
0x8033, 0x0036, 0x003c, 0x8039, 0x0028, 0x802d, 0x8027, 0x0022,
|
||||
0x8063, 0x0066, 0x006c, 0x8069, 0x0078, 0x807d, 0x8077, 0x0072,
|
||||
0x0050, 0x8055, 0x805f, 0x005a, 0x804b, 0x004e, 0x0044, 0x8041,
|
||||
0x80c3, 0x00c6, 0x00cc, 0x80c9, 0x00d8, 0x80dd, 0x80d7, 0x00d2,
|
||||
0x00f0, 0x80f5, 0x80ff, 0x00fa, 0x80eb, 0x00ee, 0x00e4, 0x80e1,
|
||||
0x00a0, 0x80a5, 0x80af, 0x00aa, 0x80bb, 0x00be, 0x00b4, 0x80b1,
|
||||
0x8093, 0x0096, 0x009c, 0x8099, 0x0088, 0x808d, 0x8087, 0x0082,
|
||||
0x8183, 0x0186, 0x018c, 0x8189, 0x0198, 0x819d, 0x8197, 0x0192,
|
||||
0x01b0, 0x81b5, 0x81bf, 0x01ba, 0x81ab, 0x01ae, 0x01a4, 0x81a1,
|
||||
0x01e0, 0x81e5, 0x81ef, 0x01ea, 0x81fb, 0x01fe, 0x01f4, 0x81f1,
|
||||
0x81d3, 0x01d6, 0x01dc, 0x81d9, 0x01c8, 0x81cd, 0x81c7, 0x01c2,
|
||||
0x0140, 0x8145, 0x814f, 0x014a, 0x815b, 0x015e, 0x0154, 0x8151,
|
||||
0x8173, 0x0176, 0x017c, 0x8179, 0x0168, 0x816d, 0x8167, 0x0162,
|
||||
0x8123, 0x0126, 0x012c, 0x8129, 0x0138, 0x813d, 0x8137, 0x0132,
|
||||
0x0110, 0x8115, 0x811f, 0x011a, 0x810b, 0x010e, 0x0104, 0x8101,
|
||||
0x8303, 0x0306, 0x030c, 0x8309, 0x0318, 0x831d, 0x8317, 0x0312,
|
||||
0x0330, 0x8335, 0x833f, 0x033a, 0x832b, 0x032e, 0x0324, 0x8321,
|
||||
0x0360, 0x8365, 0x836f, 0x036a, 0x837b, 0x037e, 0x0374, 0x8371,
|
||||
0x8353, 0x0356, 0x035c, 0x8359, 0x0348, 0x834d, 0x8347, 0x0342,
|
||||
0x03c0, 0x83c5, 0x83cf, 0x03ca, 0x83db, 0x03de, 0x03d4, 0x83d1,
|
||||
0x83f3, 0x03f6, 0x03fc, 0x83f9, 0x03e8, 0x83ed, 0x83e7, 0x03e2,
|
||||
0x83a3, 0x03a6, 0x03ac, 0x83a9, 0x03b8, 0x83bd, 0x83b7, 0x03b2,
|
||||
0x0390, 0x8395, 0x839f, 0x039a, 0x838b, 0x038e, 0x0384, 0x8381,
|
||||
0x0280, 0x8285, 0x828f, 0x028a, 0x829b, 0x029e, 0x0294, 0x8291,
|
||||
0x82b3, 0x02b6, 0x02bc, 0x82b9, 0x02a8, 0x82ad, 0x82a7, 0x02a2,
|
||||
0x82e3, 0x02e6, 0x02ec, 0x82e9, 0x02f8, 0x82fd, 0x82f7, 0x02f2,
|
||||
0x02d0, 0x82d5, 0x82df, 0x02da, 0x82cb, 0x02ce, 0x02c4, 0x82c1,
|
||||
0x8243, 0x0246, 0x024c, 0x8249, 0x0258, 0x825d, 0x8257, 0x0252,
|
||||
0x0270, 0x8275, 0x827f, 0x027a, 0x826b, 0x026e, 0x0264, 0x8261,
|
||||
0x0220, 0x8225, 0x822f, 0x022a, 0x823b, 0x023e, 0x0234, 0x8231,
|
||||
0x8213, 0x0216, 0x021c, 0x8219, 0x0208, 0x820d, 0x8207, 0x0202
|
||||
};
|
||||
|
||||
static inline u16 mcp25xxfd_crc16_byte(u16 crc, const u8 data)
|
||||
{
|
||||
u8 index = (crc >> 8) ^ data;
|
||||
|
||||
return (crc << 8) ^ mcp25xxfd_crc16_table[index];
|
||||
}
|
||||
|
||||
static u16 mcp25xxfd_crc16(u16 crc, u8 const *buffer, size_t len)
|
||||
{
|
||||
while (len--)
|
||||
crc = mcp25xxfd_crc16_byte(crc, *buffer++);
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
u16 mcp25xxfd_crc16_compute(const void *data, size_t data_size)
|
||||
{
|
||||
u16 crc = 0xffff;
|
||||
|
||||
return mcp25xxfd_crc16(crc, data, data_size);
|
||||
}
|
||||
|
||||
u16 mcp25xxfd_crc16_compute2(const void *cmd, size_t cmd_size,
|
||||
const void *data, size_t data_size)
|
||||
{
|
||||
u16 crc;
|
||||
|
||||
crc = mcp25xxfd_crc16_compute(cmd, cmd_size);
|
||||
crc = mcp25xxfd_crc16(crc, data, data_size);
|
||||
|
||||
return crc;
|
||||
}
|
|
@ -0,0 +1,556 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// mcp25xxfd - Microchip MCP25xxFD Family CAN controller driver
|
||||
//
|
||||
// Copyright (c) 2019, 2020 Pengutronix,
|
||||
// Marc Kleine-Budde <kernel@pengutronix.de>
|
||||
//
|
||||
|
||||
#include "mcp25xxfd.h"
|
||||
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
static const struct regmap_config mcp25xxfd_regmap_crc;
|
||||
|
||||
static int
|
||||
mcp25xxfd_regmap_nocrc_write(void *context, const void *data, size_t count)
|
||||
{
|
||||
struct spi_device *spi = context;
|
||||
|
||||
return spi_write(spi, data, count);
|
||||
}
|
||||
|
||||
static int
|
||||
mcp25xxfd_regmap_nocrc_gather_write(void *context,
|
||||
const void *reg, size_t reg_len,
|
||||
const void *val, size_t val_len)
|
||||
{
|
||||
struct spi_device *spi = context;
|
||||
struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
|
||||
struct mcp25xxfd_map_buf_nocrc *buf_tx = priv->map_buf_nocrc_tx;
|
||||
struct spi_transfer xfer[] = {
|
||||
{
|
||||
.tx_buf = buf_tx,
|
||||
.len = sizeof(buf_tx->cmd) + val_len,
|
||||
},
|
||||
};
|
||||
|
||||
BUILD_BUG_ON(sizeof(buf_tx->cmd) != sizeof(__be16));
|
||||
|
||||
if (IS_ENABLED(CONFIG_CAN_MCP25XXFD_SANITY) &&
|
||||
reg_len != sizeof(buf_tx->cmd.cmd))
|
||||
return -EINVAL;
|
||||
|
||||
memcpy(&buf_tx->cmd, reg, sizeof(buf_tx->cmd));
|
||||
memcpy(buf_tx->data, val, val_len);
|
||||
|
||||
return spi_sync_transfer(spi, xfer, ARRAY_SIZE(xfer));
|
||||
}
|
||||
|
||||
static inline bool mcp25xxfd_update_bits_read_reg(unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case MCP25XXFD_REG_INT:
|
||||
case MCP25XXFD_REG_TEFCON:
|
||||
case MCP25XXFD_REG_FIFOCON(MCP25XXFD_RX_FIFO(0)):
|
||||
case MCP25XXFD_REG_FLTCON(0):
|
||||
case MCP25XXFD_REG_ECCSTAT:
|
||||
case MCP25XXFD_REG_CRC:
|
||||
return false;
|
||||
case MCP25XXFD_REG_CON:
|
||||
case MCP25XXFD_REG_FIFOSTA(MCP25XXFD_RX_FIFO(0)):
|
||||
case MCP25XXFD_REG_OSC:
|
||||
case MCP25XXFD_REG_ECCCON:
|
||||
return true;
|
||||
default:
|
||||
WARN(1, "Status of reg 0x%04x unknown.\n", reg);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int
|
||||
mcp25xxfd_regmap_nocrc_update_bits(void *context, unsigned int reg,
|
||||
unsigned int mask, unsigned int val)
|
||||
{
|
||||
struct spi_device *spi = context;
|
||||
struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
|
||||
struct mcp25xxfd_map_buf_nocrc *buf_rx = priv->map_buf_nocrc_rx;
|
||||
struct mcp25xxfd_map_buf_nocrc *buf_tx = priv->map_buf_nocrc_tx;
|
||||
__le32 orig_le32 = 0, mask_le32, val_le32, tmp_le32;
|
||||
u8 first_byte, last_byte, len;
|
||||
int err;
|
||||
|
||||
BUILD_BUG_ON(sizeof(buf_rx->cmd) != sizeof(__be16));
|
||||
BUILD_BUG_ON(sizeof(buf_tx->cmd) != sizeof(__be16));
|
||||
|
||||
if (IS_ENABLED(CONFIG_CAN_MCP25XXFD_SANITY) &&
|
||||
mask == 0)
|
||||
return -EINVAL;
|
||||
|
||||
first_byte = mcp25xxfd_first_byte_set(mask);
|
||||
last_byte = mcp25xxfd_last_byte_set(mask);
|
||||
len = last_byte - first_byte + 1;
|
||||
|
||||
if (mcp25xxfd_update_bits_read_reg(reg)) {
|
||||
struct spi_transfer xfer[2] = { };
|
||||
struct spi_message msg;
|
||||
|
||||
spi_message_init(&msg);
|
||||
spi_message_add_tail(&xfer[0], &msg);
|
||||
|
||||
if (priv->devtype_data.quirks & MCP25XXFD_QUIRK_HALF_DUPLEX) {
|
||||
xfer[0].tx_buf = buf_tx;
|
||||
xfer[0].len = sizeof(buf_tx->cmd);
|
||||
|
||||
xfer[1].rx_buf = buf_rx->data;
|
||||
xfer[1].len = len;
|
||||
spi_message_add_tail(&xfer[1], &msg);
|
||||
} else {
|
||||
xfer[0].tx_buf = buf_tx;
|
||||
xfer[0].rx_buf = buf_rx;
|
||||
xfer[0].len = sizeof(buf_tx->cmd) + len;
|
||||
|
||||
if (MCP25XXFD_SANITIZE_SPI)
|
||||
memset(buf_tx->data, 0x0, len);
|
||||
}
|
||||
|
||||
mcp25xxfd_spi_cmd_read_nocrc(&buf_tx->cmd, reg + first_byte);
|
||||
err = spi_sync(spi, &msg);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
memcpy(&orig_le32, buf_rx->data, len);
|
||||
}
|
||||
|
||||
mask_le32 = cpu_to_le32(mask >> BITS_PER_BYTE * first_byte);
|
||||
val_le32 = cpu_to_le32(val >> BITS_PER_BYTE * first_byte);
|
||||
|
||||
tmp_le32 = orig_le32 & ~mask_le32;
|
||||
tmp_le32 |= val_le32 & mask_le32;
|
||||
|
||||
mcp25xxfd_spi_cmd_write_nocrc(&buf_tx->cmd, reg + first_byte);
|
||||
memcpy(buf_tx->data, &tmp_le32, len);
|
||||
|
||||
return spi_write(spi, buf_tx, sizeof(buf_tx->cmd) + len);
|
||||
}
|
||||
|
||||
static int
|
||||
mcp25xxfd_regmap_nocrc_read(void *context,
|
||||
const void *reg, size_t reg_len,
|
||||
void *val_buf, size_t val_len)
|
||||
{
|
||||
struct spi_device *spi = context;
|
||||
struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
|
||||
struct mcp25xxfd_map_buf_nocrc *buf_rx = priv->map_buf_nocrc_rx;
|
||||
struct mcp25xxfd_map_buf_nocrc *buf_tx = priv->map_buf_nocrc_tx;
|
||||
struct spi_transfer xfer[2] = { };
|
||||
struct spi_message msg;
|
||||
int err;
|
||||
|
||||
BUILD_BUG_ON(sizeof(buf_rx->cmd) != sizeof(__be16));
|
||||
BUILD_BUG_ON(sizeof(buf_tx->cmd) != sizeof(__be16));
|
||||
|
||||
if (IS_ENABLED(CONFIG_CAN_MCP25XXFD_SANITY) &&
|
||||
reg_len != sizeof(buf_tx->cmd.cmd))
|
||||
return -EINVAL;
|
||||
|
||||
spi_message_init(&msg);
|
||||
spi_message_add_tail(&xfer[0], &msg);
|
||||
|
||||
if (priv->devtype_data.quirks & MCP25XXFD_QUIRK_HALF_DUPLEX) {
|
||||
xfer[0].tx_buf = reg;
|
||||
xfer[0].len = sizeof(buf_tx->cmd);
|
||||
|
||||
xfer[1].rx_buf = val_buf;
|
||||
xfer[1].len = val_len;
|
||||
spi_message_add_tail(&xfer[1], &msg);
|
||||
} else {
|
||||
xfer[0].tx_buf = buf_tx;
|
||||
xfer[0].rx_buf = buf_rx;
|
||||
xfer[0].len = sizeof(buf_tx->cmd) + val_len;
|
||||
|
||||
memcpy(&buf_tx->cmd, reg, sizeof(buf_tx->cmd));
|
||||
if (MCP25XXFD_SANITIZE_SPI)
|
||||
memset(buf_tx->data, 0x0, val_len);
|
||||
};
|
||||
|
||||
err = spi_sync(spi, &msg);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!(priv->devtype_data.quirks & MCP25XXFD_QUIRK_HALF_DUPLEX))
|
||||
memcpy(val_buf, buf_rx->data, val_len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mcp25xxfd_regmap_crc_gather_write(void *context,
|
||||
const void *reg_p, size_t reg_len,
|
||||
const void *val, size_t val_len)
|
||||
{
|
||||
struct spi_device *spi = context;
|
||||
struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
|
||||
struct mcp25xxfd_map_buf_crc *buf_tx = priv->map_buf_crc_tx;
|
||||
struct spi_transfer xfer[] = {
|
||||
{
|
||||
.tx_buf = buf_tx,
|
||||
.len = sizeof(buf_tx->cmd) + val_len +
|
||||
sizeof(buf_tx->crc),
|
||||
},
|
||||
};
|
||||
u16 reg = *(u16 *)reg_p;
|
||||
u16 crc;
|
||||
|
||||
BUILD_BUG_ON(sizeof(buf_tx->cmd) != sizeof(__be16) + sizeof(u8));
|
||||
|
||||
if (IS_ENABLED(CONFIG_CAN_MCP25XXFD_SANITY) &&
|
||||
reg_len != sizeof(buf_tx->cmd.cmd) +
|
||||
mcp25xxfd_regmap_crc.pad_bits / BITS_PER_BYTE)
|
||||
return -EINVAL;
|
||||
|
||||
mcp25xxfd_spi_cmd_write_crc(&buf_tx->cmd, reg, val_len);
|
||||
memcpy(buf_tx->data, val, val_len);
|
||||
|
||||
crc = mcp25xxfd_crc16_compute(buf_tx, sizeof(buf_tx->cmd) + val_len);
|
||||
put_unaligned_be16(crc, buf_tx->data + val_len);
|
||||
|
||||
return spi_sync_transfer(spi, xfer, ARRAY_SIZE(xfer));
|
||||
}
|
||||
|
||||
static int
|
||||
mcp25xxfd_regmap_crc_write(void *context,
|
||||
const void *data, size_t count)
|
||||
{
|
||||
const size_t data_offset = sizeof(__be16) +
|
||||
mcp25xxfd_regmap_crc.pad_bits / BITS_PER_BYTE;
|
||||
|
||||
return mcp25xxfd_regmap_crc_gather_write(context,
|
||||
data, data_offset,
|
||||
data + data_offset,
|
||||
count - data_offset);
|
||||
}
|
||||
|
||||
static int
|
||||
mcp25xxfd_regmap_crc_read_one(struct mcp25xxfd_priv *priv,
|
||||
struct spi_message *msg, unsigned int data_len)
|
||||
{
|
||||
const struct mcp25xxfd_map_buf_crc *buf_rx = priv->map_buf_crc_rx;
|
||||
const struct mcp25xxfd_map_buf_crc *buf_tx = priv->map_buf_crc_tx;
|
||||
u16 crc_received, crc_calculated;
|
||||
int err;
|
||||
|
||||
BUILD_BUG_ON(sizeof(buf_rx->cmd) != sizeof(__be16) + sizeof(u8));
|
||||
BUILD_BUG_ON(sizeof(buf_tx->cmd) != sizeof(__be16) + sizeof(u8));
|
||||
|
||||
err = spi_sync(priv->spi, msg);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
crc_received = get_unaligned_be16(buf_rx->data + data_len);
|
||||
crc_calculated = mcp25xxfd_crc16_compute2(&buf_tx->cmd,
|
||||
sizeof(buf_tx->cmd),
|
||||
buf_rx->data,
|
||||
data_len);
|
||||
if (crc_received != crc_calculated)
|
||||
return -EBADMSG;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mcp25xxfd_regmap_crc_read(void *context,
|
||||
const void *reg_p, size_t reg_len,
|
||||
void *val_buf, size_t val_len)
|
||||
{
|
||||
struct spi_device *spi = context;
|
||||
struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
|
||||
struct mcp25xxfd_map_buf_crc *buf_rx = priv->map_buf_crc_rx;
|
||||
struct mcp25xxfd_map_buf_crc *buf_tx = priv->map_buf_crc_tx;
|
||||
struct spi_transfer xfer[2] = { };
|
||||
struct spi_message msg;
|
||||
u16 reg = *(u16 *)reg_p;
|
||||
int i, err;
|
||||
|
||||
BUILD_BUG_ON(sizeof(buf_rx->cmd) != sizeof(__be16) + sizeof(u8));
|
||||
BUILD_BUG_ON(sizeof(buf_tx->cmd) != sizeof(__be16) + sizeof(u8));
|
||||
|
||||
if (IS_ENABLED(CONFIG_CAN_MCP25XXFD_SANITY) &&
|
||||
reg_len != sizeof(buf_tx->cmd.cmd) +
|
||||
mcp25xxfd_regmap_crc.pad_bits / BITS_PER_BYTE)
|
||||
return -EINVAL;
|
||||
|
||||
spi_message_init(&msg);
|
||||
spi_message_add_tail(&xfer[0], &msg);
|
||||
|
||||
if (priv->devtype_data.quirks & MCP25XXFD_QUIRK_HALF_DUPLEX) {
|
||||
xfer[0].tx_buf = buf_tx;
|
||||
xfer[0].len = sizeof(buf_tx->cmd);
|
||||
|
||||
xfer[1].rx_buf = buf_rx->data;
|
||||
xfer[1].len = val_len + sizeof(buf_tx->crc);
|
||||
spi_message_add_tail(&xfer[1], &msg);
|
||||
} else {
|
||||
xfer[0].tx_buf = buf_tx;
|
||||
xfer[0].rx_buf = buf_rx;
|
||||
xfer[0].len = sizeof(buf_tx->cmd) + val_len +
|
||||
sizeof(buf_tx->crc);
|
||||
|
||||
if (MCP25XXFD_SANITIZE_SPI)
|
||||
memset(buf_tx->data, 0x0, val_len +
|
||||
sizeof(buf_tx->crc));
|
||||
}
|
||||
|
||||
mcp25xxfd_spi_cmd_read_crc(&buf_tx->cmd, reg, val_len);
|
||||
|
||||
for (i = 0; i < MCP25XXFD_READ_CRC_RETRIES_MAX; i++) {
|
||||
err = mcp25xxfd_regmap_crc_read_one(priv, &msg, val_len);
|
||||
if (!err)
|
||||
goto out;
|
||||
if (err != -EBADMSG)
|
||||
return err;
|
||||
|
||||
/* MCP25XXFD_REG_OSC is the first ever reg we read from.
|
||||
*
|
||||
* The chip may be in deep sleep and this SPI transfer
|
||||
* (i.e. the assertion of the CS) will wake the chip
|
||||
* up. This takes about 3ms. The CRC of this transfer
|
||||
* is wrong.
|
||||
*
|
||||
* Or there isn't a chip at all, in this case the CRC
|
||||
* will be wrong, too.
|
||||
*
|
||||
* In both cases ignore the CRC and copy the read data
|
||||
* to the caller. It will take care of both cases.
|
||||
*
|
||||
*/
|
||||
if (reg == MCP25XXFD_REG_OSC) {
|
||||
err = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
netdev_dbg(priv->ndev,
|
||||
"CRC read error at address 0x%04x (length=%zd, data=%*ph, CRC=0x%04x) retrying.\n",
|
||||
reg, val_len, (int)val_len, buf_rx->data,
|
||||
get_unaligned_be16(buf_rx->data + val_len));
|
||||
}
|
||||
|
||||
if (err) {
|
||||
netdev_info(priv->ndev,
|
||||
"CRC read error at address 0x%04x (length=%zd, data=%*ph, CRC=0x%04x).\n",
|
||||
reg, val_len, (int)val_len, buf_rx->data,
|
||||
get_unaligned_be16(buf_rx->data + val_len));
|
||||
|
||||
return err;
|
||||
}
|
||||
out:
|
||||
memcpy(val_buf, buf_rx->data, val_len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct regmap_range mcp25xxfd_reg_table_yes_range[] = {
|
||||
regmap_reg_range(0x000, 0x2ec), /* CAN FD Controller Module SFR */
|
||||
regmap_reg_range(0x400, 0xbfc), /* RAM */
|
||||
regmap_reg_range(0xe00, 0xe14), /* MCP2517/18FD SFR */
|
||||
};
|
||||
|
||||
static const struct regmap_access_table mcp25xxfd_reg_table = {
|
||||
.yes_ranges = mcp25xxfd_reg_table_yes_range,
|
||||
.n_yes_ranges = ARRAY_SIZE(mcp25xxfd_reg_table_yes_range),
|
||||
};
|
||||
|
||||
static const struct regmap_config mcp25xxfd_regmap_nocrc = {
|
||||
.name = "nocrc",
|
||||
.reg_bits = 16,
|
||||
.reg_stride = 4,
|
||||
.pad_bits = 0,
|
||||
.val_bits = 32,
|
||||
.max_register = 0xffc,
|
||||
.wr_table = &mcp25xxfd_reg_table,
|
||||
.rd_table = &mcp25xxfd_reg_table,
|
||||
.cache_type = REGCACHE_NONE,
|
||||
.read_flag_mask = (__force unsigned long)
|
||||
cpu_to_be16(MCP25XXFD_SPI_INSTRUCTION_READ),
|
||||
.write_flag_mask = (__force unsigned long)
|
||||
cpu_to_be16(MCP25XXFD_SPI_INSTRUCTION_WRITE),
|
||||
};
|
||||
|
||||
static const struct regmap_bus mcp25xxfd_bus_nocrc = {
|
||||
.write = mcp25xxfd_regmap_nocrc_write,
|
||||
.gather_write = mcp25xxfd_regmap_nocrc_gather_write,
|
||||
.reg_update_bits = mcp25xxfd_regmap_nocrc_update_bits,
|
||||
.read = mcp25xxfd_regmap_nocrc_read,
|
||||
.reg_format_endian_default = REGMAP_ENDIAN_BIG,
|
||||
.val_format_endian_default = REGMAP_ENDIAN_LITTLE,
|
||||
.max_raw_read = sizeof_field(struct mcp25xxfd_map_buf_nocrc, data),
|
||||
.max_raw_write = sizeof_field(struct mcp25xxfd_map_buf_nocrc, data),
|
||||
};
|
||||
|
||||
static const struct regmap_config mcp25xxfd_regmap_crc = {
|
||||
.name = "crc",
|
||||
.reg_bits = 16,
|
||||
.reg_stride = 4,
|
||||
.pad_bits = 16, /* keep data bits aligned */
|
||||
.val_bits = 32,
|
||||
.max_register = 0xffc,
|
||||
.wr_table = &mcp25xxfd_reg_table,
|
||||
.rd_table = &mcp25xxfd_reg_table,
|
||||
.cache_type = REGCACHE_NONE,
|
||||
};
|
||||
|
||||
static const struct regmap_bus mcp25xxfd_bus_crc = {
|
||||
.write = mcp25xxfd_regmap_crc_write,
|
||||
.gather_write = mcp25xxfd_regmap_crc_gather_write,
|
||||
.read = mcp25xxfd_regmap_crc_read,
|
||||
.reg_format_endian_default = REGMAP_ENDIAN_NATIVE,
|
||||
.val_format_endian_default = REGMAP_ENDIAN_LITTLE,
|
||||
.max_raw_read = sizeof_field(struct mcp25xxfd_map_buf_crc, data),
|
||||
.max_raw_write = sizeof_field(struct mcp25xxfd_map_buf_crc, data),
|
||||
};
|
||||
|
||||
static inline bool
|
||||
mcp25xxfd_regmap_use_nocrc(struct mcp25xxfd_priv *priv)
|
||||
{
|
||||
return (!(priv->devtype_data.quirks & MCP25XXFD_QUIRK_CRC_REG)) ||
|
||||
(!(priv->devtype_data.quirks & MCP25XXFD_QUIRK_CRC_RX));
|
||||
}
|
||||
|
||||
static inline bool
|
||||
mcp25xxfd_regmap_use_crc(struct mcp25xxfd_priv *priv)
|
||||
{
|
||||
return (priv->devtype_data.quirks & MCP25XXFD_QUIRK_CRC_REG) ||
|
||||
(priv->devtype_data.quirks & MCP25XXFD_QUIRK_CRC_RX);
|
||||
}
|
||||
|
||||
static int
|
||||
mcp25xxfd_regmap_init_nocrc(struct mcp25xxfd_priv *priv)
|
||||
{
|
||||
if (!priv->map_nocrc) {
|
||||
struct regmap *map;
|
||||
|
||||
map = devm_regmap_init(&priv->spi->dev, &mcp25xxfd_bus_nocrc,
|
||||
priv->spi, &mcp25xxfd_regmap_nocrc);
|
||||
if (IS_ERR(map))
|
||||
return PTR_ERR(map);
|
||||
|
||||
priv->map_nocrc = map;
|
||||
}
|
||||
|
||||
if (!priv->map_buf_nocrc_rx) {
|
||||
priv->map_buf_nocrc_rx =
|
||||
devm_kzalloc(&priv->spi->dev,
|
||||
sizeof(*priv->map_buf_nocrc_rx),
|
||||
GFP_KERNEL);
|
||||
if (!priv->map_buf_nocrc_rx)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (!priv->map_buf_nocrc_tx) {
|
||||
priv->map_buf_nocrc_tx =
|
||||
devm_kzalloc(&priv->spi->dev,
|
||||
sizeof(*priv->map_buf_nocrc_tx),
|
||||
GFP_KERNEL);
|
||||
if (!priv->map_buf_nocrc_tx)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (!(priv->devtype_data.quirks & MCP25XXFD_QUIRK_CRC_REG))
|
||||
priv->map_reg = priv->map_nocrc;
|
||||
|
||||
if (!(priv->devtype_data.quirks & MCP25XXFD_QUIRK_CRC_RX))
|
||||
priv->map_rx = priv->map_nocrc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mcp25xxfd_regmap_destroy_nocrc(struct mcp25xxfd_priv *priv)
|
||||
{
|
||||
if (priv->map_buf_nocrc_rx) {
|
||||
devm_kfree(&priv->spi->dev, priv->map_buf_nocrc_rx);
|
||||
priv->map_buf_nocrc_rx = NULL;
|
||||
}
|
||||
if (priv->map_buf_nocrc_tx) {
|
||||
devm_kfree(&priv->spi->dev, priv->map_buf_nocrc_tx);
|
||||
priv->map_buf_nocrc_tx = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
mcp25xxfd_regmap_init_crc(struct mcp25xxfd_priv *priv)
|
||||
{
|
||||
if (!priv->map_crc) {
|
||||
struct regmap *map;
|
||||
|
||||
map = devm_regmap_init(&priv->spi->dev, &mcp25xxfd_bus_crc,
|
||||
priv->spi, &mcp25xxfd_regmap_crc);
|
||||
if (IS_ERR(map))
|
||||
return PTR_ERR(map);
|
||||
|
||||
priv->map_crc = map;
|
||||
}
|
||||
|
||||
if (!priv->map_buf_crc_rx) {
|
||||
priv->map_buf_crc_rx =
|
||||
devm_kzalloc(&priv->spi->dev,
|
||||
sizeof(*priv->map_buf_crc_rx),
|
||||
GFP_KERNEL);
|
||||
if (!priv->map_buf_crc_rx)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (!priv->map_buf_crc_tx) {
|
||||
priv->map_buf_crc_tx =
|
||||
devm_kzalloc(&priv->spi->dev,
|
||||
sizeof(*priv->map_buf_crc_tx),
|
||||
GFP_KERNEL);
|
||||
if (!priv->map_buf_crc_tx)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (priv->devtype_data.quirks & MCP25XXFD_QUIRK_CRC_REG)
|
||||
priv->map_reg = priv->map_crc;
|
||||
|
||||
if (priv->devtype_data.quirks & MCP25XXFD_QUIRK_CRC_RX)
|
||||
priv->map_rx = priv->map_crc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mcp25xxfd_regmap_destroy_crc(struct mcp25xxfd_priv *priv)
|
||||
{
|
||||
if (priv->map_buf_crc_rx) {
|
||||
devm_kfree(&priv->spi->dev, priv->map_buf_crc_rx);
|
||||
priv->map_buf_crc_rx = NULL;
|
||||
}
|
||||
if (priv->map_buf_crc_tx) {
|
||||
devm_kfree(&priv->spi->dev, priv->map_buf_crc_tx);
|
||||
priv->map_buf_crc_tx = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int mcp25xxfd_regmap_init(struct mcp25xxfd_priv *priv)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (mcp25xxfd_regmap_use_nocrc(priv)) {
|
||||
err = mcp25xxfd_regmap_init_nocrc(priv);
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
} else {
|
||||
mcp25xxfd_regmap_destroy_nocrc(priv);
|
||||
}
|
||||
|
||||
if (mcp25xxfd_regmap_use_crc(priv)) {
|
||||
err = mcp25xxfd_regmap_init_crc(priv);
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
} else {
|
||||
mcp25xxfd_regmap_destroy_crc(priv);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,835 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
* mcp25xxfd - Microchip MCP25xxFD Family CAN controller driver
|
||||
*
|
||||
* Copyright (c) 2019 Pengutronix,
|
||||
* Marc Kleine-Budde <kernel@pengutronix.de>
|
||||
* Copyright (c) 2019 Martin Sperl <kernel@martin.sperl.org>
|
||||
*/
|
||||
|
||||
#ifndef _MCP25XXFD_H
|
||||
#define _MCP25XXFD_H
|
||||
|
||||
#include <linux/can/core.h>
|
||||
#include <linux/can/dev.h>
|
||||
#include <linux/can/rx-offload.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
/* MPC25xx registers */
|
||||
|
||||
/* CAN FD Controller Module SFR */
|
||||
#define MCP25XXFD_REG_CON 0x00
|
||||
#define MCP25XXFD_REG_CON_TXBWS_MASK GENMASK(31, 28)
|
||||
#define MCP25XXFD_REG_CON_ABAT BIT(27)
|
||||
#define MCP25XXFD_REG_CON_REQOP_MASK GENMASK(26, 24)
|
||||
#define MCP25XXFD_REG_CON_MODE_MIXED 0
|
||||
#define MCP25XXFD_REG_CON_MODE_SLEEP 1
|
||||
#define MCP25XXFD_REG_CON_MODE_INT_LOOPBACK 2
|
||||
#define MCP25XXFD_REG_CON_MODE_LISTENONLY 3
|
||||
#define MCP25XXFD_REG_CON_MODE_CONFIG 4
|
||||
#define MCP25XXFD_REG_CON_MODE_EXT_LOOPBACK 5
|
||||
#define MCP25XXFD_REG_CON_MODE_CAN2_0 6
|
||||
#define MCP25XXFD_REG_CON_MODE_RESTRICTED 7
|
||||
#define MCP25XXFD_REG_CON_OPMOD_MASK GENMASK(23, 21)
|
||||
#define MCP25XXFD_REG_CON_TXQEN BIT(20)
|
||||
#define MCP25XXFD_REG_CON_STEF BIT(19)
|
||||
#define MCP25XXFD_REG_CON_SERR2LOM BIT(18)
|
||||
#define MCP25XXFD_REG_CON_ESIGM BIT(17)
|
||||
#define MCP25XXFD_REG_CON_RTXAT BIT(16)
|
||||
#define MCP25XXFD_REG_CON_BRSDIS BIT(12)
|
||||
#define MCP25XXFD_REG_CON_BUSY BIT(11)
|
||||
#define MCP25XXFD_REG_CON_WFT_MASK GENMASK(10, 9)
|
||||
#define MCP25XXFD_REG_CON_WFT_T00FILTER 0x0
|
||||
#define MCP25XXFD_REG_CON_WFT_T01FILTER 0x1
|
||||
#define MCP25XXFD_REG_CON_WFT_T10FILTER 0x2
|
||||
#define MCP25XXFD_REG_CON_WFT_T11FILTER 0x3
|
||||
#define MCP25XXFD_REG_CON_WAKFIL BIT(8)
|
||||
#define MCP25XXFD_REG_CON_PXEDIS BIT(6)
|
||||
#define MCP25XXFD_REG_CON_ISOCRCEN BIT(5)
|
||||
#define MCP25XXFD_REG_CON_DNCNT_MASK GENMASK(4, 0)
|
||||
|
||||
#define MCP25XXFD_REG_NBTCFG 0x04
|
||||
#define MCP25XXFD_REG_NBTCFG_BRP_MASK GENMASK(31, 24)
|
||||
#define MCP25XXFD_REG_NBTCFG_TSEG1_MASK GENMASK(23, 16)
|
||||
#define MCP25XXFD_REG_NBTCFG_TSEG2_MASK GENMASK(14, 8)
|
||||
#define MCP25XXFD_REG_NBTCFG_SJW_MASK GENMASK(6, 0)
|
||||
|
||||
#define MCP25XXFD_REG_DBTCFG 0x08
|
||||
#define MCP25XXFD_REG_DBTCFG_BRP_MASK GENMASK(31, 24)
|
||||
#define MCP25XXFD_REG_DBTCFG_TSEG1_MASK GENMASK(20, 16)
|
||||
#define MCP25XXFD_REG_DBTCFG_TSEG2_MASK GENMASK(11, 8)
|
||||
#define MCP25XXFD_REG_DBTCFG_SJW_MASK GENMASK(3, 0)
|
||||
|
||||
#define MCP25XXFD_REG_TDC 0x0c
|
||||
#define MCP25XXFD_REG_TDC_EDGFLTEN BIT(25)
|
||||
#define MCP25XXFD_REG_TDC_SID11EN BIT(24)
|
||||
#define MCP25XXFD_REG_TDC_TDCMOD_MASK GENMASK(17, 16)
|
||||
#define MCP25XXFD_REG_TDC_TDCMOD_AUTO 2
|
||||
#define MCP25XXFD_REG_TDC_TDCMOD_MANUAL 1
|
||||
#define MCP25XXFD_REG_TDC_TDCMOD_DISABLED 0
|
||||
#define MCP25XXFD_REG_TDC_TDCO_MASK GENMASK(14, 8)
|
||||
#define MCP25XXFD_REG_TDC_TDCV_MASK GENMASK(5, 0)
|
||||
|
||||
#define MCP25XXFD_REG_TBC 0x10
|
||||
|
||||
#define MCP25XXFD_REG_TSCON 0x14
|
||||
#define MCP25XXFD_REG_TSCON_TSRES BIT(18)
|
||||
#define MCP25XXFD_REG_TSCON_TSEOF BIT(17)
|
||||
#define MCP25XXFD_REG_TSCON_TBCEN BIT(16)
|
||||
#define MCP25XXFD_REG_TSCON_TBCPRE_MASK GENMASK(9, 0)
|
||||
|
||||
#define MCP25XXFD_REG_VEC 0x18
|
||||
#define MCP25XXFD_REG_VEC_RXCODE_MASK GENMASK(30, 24)
|
||||
#define MCP25XXFD_REG_VEC_TXCODE_MASK GENMASK(22, 16)
|
||||
#define MCP25XXFD_REG_VEC_FILHIT_MASK GENMASK(12, 8)
|
||||
#define MCP25XXFD_REG_VEC_ICODE_MASK GENMASK(6, 0)
|
||||
|
||||
#define MCP25XXFD_REG_INT 0x1c
|
||||
#define MCP25XXFD_REG_INT_IF_MASK GENMASK(15, 0)
|
||||
#define MCP25XXFD_REG_INT_IE_MASK GENMASK(31, 16)
|
||||
#define MCP25XXFD_REG_INT_IVMIE BIT(31)
|
||||
#define MCP25XXFD_REG_INT_WAKIE BIT(30)
|
||||
#define MCP25XXFD_REG_INT_CERRIE BIT(29)
|
||||
#define MCP25XXFD_REG_INT_SERRIE BIT(28)
|
||||
#define MCP25XXFD_REG_INT_RXOVIE BIT(27)
|
||||
#define MCP25XXFD_REG_INT_TXATIE BIT(26)
|
||||
#define MCP25XXFD_REG_INT_SPICRCIE BIT(25)
|
||||
#define MCP25XXFD_REG_INT_ECCIE BIT(24)
|
||||
#define MCP25XXFD_REG_INT_TEFIE BIT(20)
|
||||
#define MCP25XXFD_REG_INT_MODIE BIT(19)
|
||||
#define MCP25XXFD_REG_INT_TBCIE BIT(18)
|
||||
#define MCP25XXFD_REG_INT_RXIE BIT(17)
|
||||
#define MCP25XXFD_REG_INT_TXIE BIT(16)
|
||||
#define MCP25XXFD_REG_INT_IVMIF BIT(15)
|
||||
#define MCP25XXFD_REG_INT_WAKIF BIT(14)
|
||||
#define MCP25XXFD_REG_INT_CERRIF BIT(13)
|
||||
#define MCP25XXFD_REG_INT_SERRIF BIT(12)
|
||||
#define MCP25XXFD_REG_INT_RXOVIF BIT(11)
|
||||
#define MCP25XXFD_REG_INT_TXATIF BIT(10)
|
||||
#define MCP25XXFD_REG_INT_SPICRCIF BIT(9)
|
||||
#define MCP25XXFD_REG_INT_ECCIF BIT(8)
|
||||
#define MCP25XXFD_REG_INT_TEFIF BIT(4)
|
||||
#define MCP25XXFD_REG_INT_MODIF BIT(3)
|
||||
#define MCP25XXFD_REG_INT_TBCIF BIT(2)
|
||||
#define MCP25XXFD_REG_INT_RXIF BIT(1)
|
||||
#define MCP25XXFD_REG_INT_TXIF BIT(0)
|
||||
/* These IRQ flags must be cleared by SW in the CAN_INT register */
|
||||
#define MCP25XXFD_REG_INT_IF_CLEARABLE_MASK \
|
||||
(MCP25XXFD_REG_INT_IVMIF | MCP25XXFD_REG_INT_WAKIF | \
|
||||
MCP25XXFD_REG_INT_CERRIF | MCP25XXFD_REG_INT_SERRIF | \
|
||||
MCP25XXFD_REG_INT_MODIF)
|
||||
|
||||
#define MCP25XXFD_REG_RXIF 0x20
|
||||
#define MCP25XXFD_REG_TXIF 0x24
|
||||
#define MCP25XXFD_REG_RXOVIF 0x28
|
||||
#define MCP25XXFD_REG_TXATIF 0x2c
|
||||
#define MCP25XXFD_REG_TXREQ 0x30
|
||||
|
||||
#define MCP25XXFD_REG_TREC 0x34
|
||||
#define MCP25XXFD_REG_TREC_TXBO BIT(21)
|
||||
#define MCP25XXFD_REG_TREC_TXBP BIT(20)
|
||||
#define MCP25XXFD_REG_TREC_RXBP BIT(19)
|
||||
#define MCP25XXFD_REG_TREC_TXWARN BIT(18)
|
||||
#define MCP25XXFD_REG_TREC_RXWARN BIT(17)
|
||||
#define MCP25XXFD_REG_TREC_EWARN BIT(16)
|
||||
#define MCP25XXFD_REG_TREC_TEC_MASK GENMASK(15, 8)
|
||||
#define MCP25XXFD_REG_TREC_REC_MASK GENMASK(7, 0)
|
||||
|
||||
#define MCP25XXFD_REG_BDIAG0 0x38
|
||||
#define MCP25XXFD_REG_BDIAG0_DTERRCNT_MASK GENMASK(31, 24)
|
||||
#define MCP25XXFD_REG_BDIAG0_DRERRCNT_MASK GENMASK(23, 16)
|
||||
#define MCP25XXFD_REG_BDIAG0_NTERRCNT_MASK GENMASK(15, 8)
|
||||
#define MCP25XXFD_REG_BDIAG0_NRERRCNT_MASK GENMASK(7, 0)
|
||||
|
||||
#define MCP25XXFD_REG_BDIAG1 0x3c
|
||||
#define MCP25XXFD_REG_BDIAG1_DLCMM BIT(31)
|
||||
#define MCP25XXFD_REG_BDIAG1_ESI BIT(30)
|
||||
#define MCP25XXFD_REG_BDIAG1_DCRCERR BIT(29)
|
||||
#define MCP25XXFD_REG_BDIAG1_DSTUFERR BIT(28)
|
||||
#define MCP25XXFD_REG_BDIAG1_DFORMERR BIT(27)
|
||||
#define MCP25XXFD_REG_BDIAG1_DBIT1ERR BIT(25)
|
||||
#define MCP25XXFD_REG_BDIAG1_DBIT0ERR BIT(24)
|
||||
#define MCP25XXFD_REG_BDIAG1_TXBOERR BIT(23)
|
||||
#define MCP25XXFD_REG_BDIAG1_NCRCERR BIT(21)
|
||||
#define MCP25XXFD_REG_BDIAG1_NSTUFERR BIT(20)
|
||||
#define MCP25XXFD_REG_BDIAG1_NFORMERR BIT(19)
|
||||
#define MCP25XXFD_REG_BDIAG1_NACKERR BIT(18)
|
||||
#define MCP25XXFD_REG_BDIAG1_NBIT1ERR BIT(17)
|
||||
#define MCP25XXFD_REG_BDIAG1_NBIT0ERR BIT(16)
|
||||
#define MCP25XXFD_REG_BDIAG1_BERR_MASK \
|
||||
(MCP25XXFD_REG_BDIAG1_DLCMM | MCP25XXFD_REG_BDIAG1_ESI | \
|
||||
MCP25XXFD_REG_BDIAG1_DCRCERR | MCP25XXFD_REG_BDIAG1_DSTUFERR | \
|
||||
MCP25XXFD_REG_BDIAG1_DFORMERR | MCP25XXFD_REG_BDIAG1_DBIT1ERR | \
|
||||
MCP25XXFD_REG_BDIAG1_DBIT0ERR | MCP25XXFD_REG_BDIAG1_TXBOERR | \
|
||||
MCP25XXFD_REG_BDIAG1_NCRCERR | MCP25XXFD_REG_BDIAG1_NSTUFERR | \
|
||||
MCP25XXFD_REG_BDIAG1_NFORMERR | MCP25XXFD_REG_BDIAG1_NACKERR | \
|
||||
MCP25XXFD_REG_BDIAG1_NBIT1ERR | MCP25XXFD_REG_BDIAG1_NBIT0ERR)
|
||||
#define MCP25XXFD_REG_BDIAG1_EFMSGCNT_MASK GENMASK(15, 0)
|
||||
|
||||
#define MCP25XXFD_REG_TEFCON 0x40
|
||||
#define MCP25XXFD_REG_TEFCON_FSIZE_MASK GENMASK(28, 24)
|
||||
#define MCP25XXFD_REG_TEFCON_FRESET BIT(10)
|
||||
#define MCP25XXFD_REG_TEFCON_UINC BIT(8)
|
||||
#define MCP25XXFD_REG_TEFCON_TEFTSEN BIT(5)
|
||||
#define MCP25XXFD_REG_TEFCON_TEFOVIE BIT(3)
|
||||
#define MCP25XXFD_REG_TEFCON_TEFFIE BIT(2)
|
||||
#define MCP25XXFD_REG_TEFCON_TEFHIE BIT(1)
|
||||
#define MCP25XXFD_REG_TEFCON_TEFNEIE BIT(0)
|
||||
|
||||
#define MCP25XXFD_REG_TEFSTA 0x44
|
||||
#define MCP25XXFD_REG_TEFSTA_TEFOVIF BIT(3)
|
||||
#define MCP25XXFD_REG_TEFSTA_TEFFIF BIT(2)
|
||||
#define MCP25XXFD_REG_TEFSTA_TEFHIF BIT(1)
|
||||
#define MCP25XXFD_REG_TEFSTA_TEFNEIF BIT(0)
|
||||
|
||||
#define MCP25XXFD_REG_TEFUA 0x48
|
||||
|
||||
#define MCP25XXFD_REG_TXQCON 0x50
|
||||
#define MCP25XXFD_REG_TXQCON_PLSIZE_MASK GENMASK(31, 29)
|
||||
#define MCP25XXFD_REG_TXQCON_PLSIZE_8 0
|
||||
#define MCP25XXFD_REG_TXQCON_PLSIZE_12 1
|
||||
#define MCP25XXFD_REG_TXQCON_PLSIZE_16 2
|
||||
#define MCP25XXFD_REG_TXQCON_PLSIZE_20 3
|
||||
#define MCP25XXFD_REG_TXQCON_PLSIZE_24 4
|
||||
#define MCP25XXFD_REG_TXQCON_PLSIZE_32 5
|
||||
#define MCP25XXFD_REG_TXQCON_PLSIZE_48 6
|
||||
#define MCP25XXFD_REG_TXQCON_PLSIZE_64 7
|
||||
#define MCP25XXFD_REG_TXQCON_FSIZE_MASK GENMASK(28, 24)
|
||||
#define MCP25XXFD_REG_TXQCON_TXAT_UNLIMITED 3
|
||||
#define MCP25XXFD_REG_TXQCON_TXAT_THREE_SHOT 1
|
||||
#define MCP25XXFD_REG_TXQCON_TXAT_ONE_SHOT 0
|
||||
#define MCP25XXFD_REG_TXQCON_TXAT_MASK GENMASK(22, 21)
|
||||
#define MCP25XXFD_REG_TXQCON_TXPRI_MASK GENMASK(20, 16)
|
||||
#define MCP25XXFD_REG_TXQCON_FRESET BIT(10)
|
||||
#define MCP25XXFD_REG_TXQCON_TXREQ BIT(9)
|
||||
#define MCP25XXFD_REG_TXQCON_UINC BIT(8)
|
||||
#define MCP25XXFD_REG_TXQCON_TXEN BIT(7)
|
||||
#define MCP25XXFD_REG_TXQCON_TXATIE BIT(4)
|
||||
#define MCP25XXFD_REG_TXQCON_TXQEIE BIT(2)
|
||||
#define MCP25XXFD_REG_TXQCON_TXQNIE BIT(0)
|
||||
|
||||
#define MCP25XXFD_REG_TXQSTA 0x54
|
||||
#define MCP25XXFD_REG_TXQSTA_TXQCI_MASK GENMASK(12, 8)
|
||||
#define MCP25XXFD_REG_TXQSTA_TXABT BIT(7)
|
||||
#define MCP25XXFD_REG_TXQSTA_TXLARB BIT(6)
|
||||
#define MCP25XXFD_REG_TXQSTA_TXERR BIT(5)
|
||||
#define MCP25XXFD_REG_TXQSTA_TXATIF BIT(4)
|
||||
#define MCP25XXFD_REG_TXQSTA_TXQEIF BIT(2)
|
||||
#define MCP25XXFD_REG_TXQSTA_TXQNIF BIT(0)
|
||||
|
||||
#define MCP25XXFD_REG_TXQUA 0x58
|
||||
|
||||
#define MCP25XXFD_REG_FIFOCON(x) (0x50 + 0xc * (x))
|
||||
#define MCP25XXFD_REG_FIFOCON_PLSIZE_MASK GENMASK(31, 29)
|
||||
#define MCP25XXFD_REG_FIFOCON_PLSIZE_8 0
|
||||
#define MCP25XXFD_REG_FIFOCON_PLSIZE_12 1
|
||||
#define MCP25XXFD_REG_FIFOCON_PLSIZE_16 2
|
||||
#define MCP25XXFD_REG_FIFOCON_PLSIZE_20 3
|
||||
#define MCP25XXFD_REG_FIFOCON_PLSIZE_24 4
|
||||
#define MCP25XXFD_REG_FIFOCON_PLSIZE_32 5
|
||||
#define MCP25XXFD_REG_FIFOCON_PLSIZE_48 6
|
||||
#define MCP25XXFD_REG_FIFOCON_PLSIZE_64 7
|
||||
#define MCP25XXFD_REG_FIFOCON_FSIZE_MASK GENMASK(28, 24)
|
||||
#define MCP25XXFD_REG_FIFOCON_TXAT_MASK GENMASK(22, 21)
|
||||
#define MCP25XXFD_REG_FIFOCON_TXAT_ONE_SHOT 0
|
||||
#define MCP25XXFD_REG_FIFOCON_TXAT_THREE_SHOT 1
|
||||
#define MCP25XXFD_REG_FIFOCON_TXAT_UNLIMITED 3
|
||||
#define MCP25XXFD_REG_FIFOCON_TXPRI_MASK GENMASK(20, 16)
|
||||
#define MCP25XXFD_REG_FIFOCON_FRESET BIT(10)
|
||||
#define MCP25XXFD_REG_FIFOCON_TXREQ BIT(9)
|
||||
#define MCP25XXFD_REG_FIFOCON_UINC BIT(8)
|
||||
#define MCP25XXFD_REG_FIFOCON_TXEN BIT(7)
|
||||
#define MCP25XXFD_REG_FIFOCON_RTREN BIT(6)
|
||||
#define MCP25XXFD_REG_FIFOCON_RXTSEN BIT(5)
|
||||
#define MCP25XXFD_REG_FIFOCON_TXATIE BIT(4)
|
||||
#define MCP25XXFD_REG_FIFOCON_RXOVIE BIT(3)
|
||||
#define MCP25XXFD_REG_FIFOCON_TFERFFIE BIT(2)
|
||||
#define MCP25XXFD_REG_FIFOCON_TFHRFHIE BIT(1)
|
||||
#define MCP25XXFD_REG_FIFOCON_TFNRFNIE BIT(0)
|
||||
|
||||
#define MCP25XXFD_REG_FIFOSTA(x) (0x54 + 0xc * (x))
|
||||
#define MCP25XXFD_REG_FIFOSTA_FIFOCI_MASK GENMASK(12, 8)
|
||||
#define MCP25XXFD_REG_FIFOSTA_TXABT BIT(7)
|
||||
#define MCP25XXFD_REG_FIFOSTA_TXLARB BIT(6)
|
||||
#define MCP25XXFD_REG_FIFOSTA_TXERR BIT(5)
|
||||
#define MCP25XXFD_REG_FIFOSTA_TXATIF BIT(4)
|
||||
#define MCP25XXFD_REG_FIFOSTA_RXOVIF BIT(3)
|
||||
#define MCP25XXFD_REG_FIFOSTA_TFERFFIF BIT(2)
|
||||
#define MCP25XXFD_REG_FIFOSTA_TFHRFHIF BIT(1)
|
||||
#define MCP25XXFD_REG_FIFOSTA_TFNRFNIF BIT(0)
|
||||
|
||||
#define MCP25XXFD_REG_FIFOUA(x) (0x58 + 0xc * (x))
|
||||
|
||||
#define MCP25XXFD_REG_FLTCON(x) (0x1d0 + 0x4 * (x))
|
||||
#define MCP25XXFD_REG_FLTCON_FLTEN3 BIT(31)
|
||||
#define MCP25XXFD_REG_FLTCON_F3BP_MASK GENMASK(28, 24)
|
||||
#define MCP25XXFD_REG_FLTCON_FLTEN2 BIT(23)
|
||||
#define MCP25XXFD_REG_FLTCON_F2BP_MASK GENMASK(20, 16)
|
||||
#define MCP25XXFD_REG_FLTCON_FLTEN1 BIT(15)
|
||||
#define MCP25XXFD_REG_FLTCON_F1BP_MASK GENMASK(12, 8)
|
||||
#define MCP25XXFD_REG_FLTCON_FLTEN0 BIT(7)
|
||||
#define MCP25XXFD_REG_FLTCON_F0BP_MASK GENMASK(4, 0)
|
||||
#define MCP25XXFD_REG_FLTCON_FLTEN(x) (BIT(7) << 8 * ((x) & 0x3))
|
||||
#define MCP25XXFD_REG_FLTCON_FLT_MASK(x) (GENMASK(7, 0) << (8 * ((x) & 0x3)))
|
||||
#define MCP25XXFD_REG_FLTCON_FBP(x, fifo) ((fifo) << 8 * ((x) & 0x3))
|
||||
|
||||
#define MCP25XXFD_REG_FLTOBJ(x) (0x1f0 + 0x8 * (x))
|
||||
#define MCP25XXFD_REG_FLTOBJ_EXIDE BIT(30)
|
||||
#define MCP25XXFD_REG_FLTOBJ_SID11 BIT(29)
|
||||
#define MCP25XXFD_REG_FLTOBJ_EID_MASK GENMASK(28, 11)
|
||||
#define MCP25XXFD_REG_FLTOBJ_SID_MASK GENMASK(10, 0)
|
||||
|
||||
#define MCP25XXFD_REG_FLTMASK(x) (0x1f4 + 0x8 * (x))
|
||||
#define MCP25XXFD_REG_MASK_MIDE BIT(30)
|
||||
#define MCP25XXFD_REG_MASK_MSID11 BIT(29)
|
||||
#define MCP25XXFD_REG_MASK_MEID_MASK GENMASK(28, 11)
|
||||
#define MCP25XXFD_REG_MASK_MSID_MASK GENMASK(10, 0)
|
||||
|
||||
/* RAM */
|
||||
#define MCP25XXFD_RAM_START 0x400
|
||||
#define MCP25XXFD_RAM_SIZE SZ_2K
|
||||
|
||||
/* Message Object */
|
||||
#define MCP25XXFD_OBJ_ID_SID11 BIT(29)
|
||||
#define MCP25XXFD_OBJ_ID_EID_MASK GENMASK(28, 11)
|
||||
#define MCP25XXFD_OBJ_ID_SID_MASK GENMASK(10, 0)
|
||||
#define MCP25XXFD_OBJ_FLAGS_SEQ_MCP2518FD_MASK GENMASK(31, 9)
|
||||
#define MCP25XXFD_OBJ_FLAGS_SEQ_MCP2517FD_MASK GENMASK(15, 9)
|
||||
#define MCP25XXFD_OBJ_FLAGS_SEQ_MASK MCP25XXFD_OBJ_FLAGS_SEQ_MCP2518FD_MASK
|
||||
#define MCP25XXFD_OBJ_FLAGS_ESI BIT(8)
|
||||
#define MCP25XXFD_OBJ_FLAGS_FDF BIT(7)
|
||||
#define MCP25XXFD_OBJ_FLAGS_BRS BIT(6)
|
||||
#define MCP25XXFD_OBJ_FLAGS_RTR BIT(5)
|
||||
#define MCP25XXFD_OBJ_FLAGS_IDE BIT(4)
|
||||
#define MCP25XXFD_OBJ_FLAGS_DLC GENMASK(3, 0)
|
||||
|
||||
#define MCP25XXFD_REG_FRAME_EFF_SID_MASK GENMASK(28, 18)
|
||||
#define MCP25XXFD_REG_FRAME_EFF_EID_MASK GENMASK(17, 0)
|
||||
|
||||
/* MCP2517/18FD SFR */
|
||||
#define MCP25XXFD_REG_OSC 0xe00
|
||||
#define MCP25XXFD_REG_OSC_SCLKRDY BIT(12)
|
||||
#define MCP25XXFD_REG_OSC_OSCRDY BIT(10)
|
||||
#define MCP25XXFD_REG_OSC_PLLRDY BIT(8)
|
||||
#define MCP25XXFD_REG_OSC_CLKODIV_10 3
|
||||
#define MCP25XXFD_REG_OSC_CLKODIV_4 2
|
||||
#define MCP25XXFD_REG_OSC_CLKODIV_2 1
|
||||
#define MCP25XXFD_REG_OSC_CLKODIV_1 0
|
||||
#define MCP25XXFD_REG_OSC_CLKODIV_MASK GENMASK(6, 5)
|
||||
#define MCP25XXFD_REG_OSC_SCLKDIV BIT(4)
|
||||
#define MCP25XXFD_REG_OSC_LPMEN BIT(3) /* MCP2518FD only */
|
||||
#define MCP25XXFD_REG_OSC_OSCDIS BIT(2)
|
||||
#define MCP25XXFD_REG_OSC_PLLEN BIT(0)
|
||||
|
||||
#define MCP25XXFD_REG_IOCON 0xe04
|
||||
#define MCP25XXFD_REG_IOCON_INTOD BIT(30)
|
||||
#define MCP25XXFD_REG_IOCON_SOF BIT(29)
|
||||
#define MCP25XXFD_REG_IOCON_TXCANOD BIT(28)
|
||||
#define MCP25XXFD_REG_IOCON_PM1 BIT(25)
|
||||
#define MCP25XXFD_REG_IOCON_PM0 BIT(24)
|
||||
#define MCP25XXFD_REG_IOCON_GPIO1 BIT(17)
|
||||
#define MCP25XXFD_REG_IOCON_GPIO0 BIT(16)
|
||||
#define MCP25XXFD_REG_IOCON_LAT1 BIT(9)
|
||||
#define MCP25XXFD_REG_IOCON_LAT0 BIT(8)
|
||||
#define MCP25XXFD_REG_IOCON_XSTBYEN BIT(6)
|
||||
#define MCP25XXFD_REG_IOCON_TRIS1 BIT(1)
|
||||
#define MCP25XXFD_REG_IOCON_TRIS0 BIT(0)
|
||||
|
||||
#define MCP25XXFD_REG_CRC 0xe08
|
||||
#define MCP25XXFD_REG_CRC_FERRIE BIT(25)
|
||||
#define MCP25XXFD_REG_CRC_CRCERRIE BIT(24)
|
||||
#define MCP25XXFD_REG_CRC_FERRIF BIT(17)
|
||||
#define MCP25XXFD_REG_CRC_CRCERRIF BIT(16)
|
||||
#define MCP25XXFD_REG_CRC_IF_MASK GENMASK(17, 16)
|
||||
#define MCP25XXFD_REG_CRC_MASK GENMASK(15, 0)
|
||||
|
||||
#define MCP25XXFD_REG_ECCCON 0xe0c
|
||||
#define MCP25XXFD_REG_ECCCON_PARITY_MASK GENMASK(14, 8)
|
||||
#define MCP25XXFD_REG_ECCCON_DEDIE BIT(2)
|
||||
#define MCP25XXFD_REG_ECCCON_SECIE BIT(1)
|
||||
#define MCP25XXFD_REG_ECCCON_ECCEN BIT(0)
|
||||
|
||||
#define MCP25XXFD_REG_ECCSTAT 0xe10
|
||||
#define MCP25XXFD_REG_ECCSTAT_ERRADDR_MASK GENMASK(27, 16)
|
||||
#define MCP25XXFD_REG_ECCSTAT_IF_MASK GENMASK(2, 1)
|
||||
#define MCP25XXFD_REG_ECCSTAT_DEDIF BIT(2)
|
||||
#define MCP25XXFD_REG_ECCSTAT_SECIF BIT(1)
|
||||
|
||||
#define MCP25XXFD_REG_DEVID 0xe14 /* MCP2518FD only */
|
||||
#define MCP25XXFD_REG_DEVID_ID_MASK GENMASK(7, 4)
|
||||
#define MCP25XXFD_REG_DEVID_REV_MASK GENMASK(3, 0)
|
||||
|
||||
/* number of TX FIFO objects, depending on CAN mode
|
||||
*
|
||||
* FIFO setup: tef: 8*12 bytes = 96 bytes, tx: 8*16 bytes = 128 bytes
|
||||
* FIFO setup: tef: 4*12 bytes = 48 bytes, tx: 4*72 bytes = 288 bytes
|
||||
*/
|
||||
#define MCP25XXFD_TX_OBJ_NUM_CAN 8
|
||||
#define MCP25XXFD_TX_OBJ_NUM_CANFD 4
|
||||
|
||||
#if MCP25XXFD_TX_OBJ_NUM_CAN > MCP25XXFD_TX_OBJ_NUM_CANFD
|
||||
#define MCP25XXFD_TX_OBJ_NUM_MAX MCP25XXFD_TX_OBJ_NUM_CAN
|
||||
#else
|
||||
#define MCP25XXFD_TX_OBJ_NUM_MAX MCP25XXFD_TX_OBJ_NUM_CANFD
|
||||
#endif
|
||||
|
||||
#define MCP25XXFD_NAPI_WEIGHT 32
|
||||
#define MCP25XXFD_TX_FIFO 1
|
||||
#define MCP25XXFD_RX_FIFO(x) (MCP25XXFD_TX_FIFO + 1 + (x))
|
||||
|
||||
/* SPI commands */
|
||||
#define MCP25XXFD_SPI_INSTRUCTION_RESET 0x0000
|
||||
#define MCP25XXFD_SPI_INSTRUCTION_WRITE 0x2000
|
||||
#define MCP25XXFD_SPI_INSTRUCTION_READ 0x3000
|
||||
#define MCP25XXFD_SPI_INSTRUCTION_WRITE_CRC 0xa000
|
||||
#define MCP25XXFD_SPI_INSTRUCTION_READ_CRC 0xb000
|
||||
#define MCP25XXFD_SPI_INSTRUCTION_WRITE_CRC_SAFE 0xc000
|
||||
#define MCP25XXFD_SPI_ADDRESS_MASK GENMASK(11, 0)
|
||||
|
||||
#define MCP25XXFD_SYSCLOCK_HZ_MAX 40000000
|
||||
#define MCP25XXFD_SYSCLOCK_HZ_MIN 1000000
|
||||
#define MCP25XXFD_SPICLOCK_HZ_MAX 20000000
|
||||
#define MCP25XXFD_OSC_PLL_MULTIPLIER 10
|
||||
#define MCP25XXFD_OSC_STAB_SLEEP_US (3 * USEC_PER_MSEC)
|
||||
#define MCP25XXFD_OSC_STAB_TIMEOUT_US (10 * MCP25XXFD_OSC_STAB_SLEEP_US)
|
||||
#define MCP25XXFD_POLL_SLEEP_US (10)
|
||||
#define MCP25XXFD_POLL_TIMEOUT_US (USEC_PER_MSEC)
|
||||
#define MCP25XXFD_SOFTRESET_RETRIES_MAX 3
|
||||
#define MCP25XXFD_READ_CRC_RETRIES_MAX 3
|
||||
#define MCP25XXFD_ECC_CNT_MAX 2
|
||||
#define MCP25XXFD_SANITIZE_SPI 1
|
||||
#define MCP25XXFD_SANITIZE_CAN 1
|
||||
|
||||
/* Silence TX MAB overflow warnings */
|
||||
#define MCP25XXFD_QUIRK_MAB_NO_WARN BIT(0)
|
||||
/* Use CRC to access registers */
|
||||
#define MCP25XXFD_QUIRK_CRC_REG BIT(1)
|
||||
/* Use CRC to access RX/TEF-RAM */
|
||||
#define MCP25XXFD_QUIRK_CRC_RX BIT(2)
|
||||
/* Use CRC to access TX-RAM */
|
||||
#define MCP25XXFD_QUIRK_CRC_TX BIT(3)
|
||||
/* Enable ECC for RAM */
|
||||
#define MCP25XXFD_QUIRK_ECC BIT(4)
|
||||
/* Use Half Duplex SPI transfers */
|
||||
#define MCP25XXFD_QUIRK_HALF_DUPLEX BIT(5)
|
||||
|
||||
struct mcp25xxfd_hw_tef_obj {
|
||||
u32 id;
|
||||
u32 flags;
|
||||
u32 ts;
|
||||
};
|
||||
|
||||
/* The tx_obj_raw version is used in spi async, i.e. without
|
||||
* regmap. We have to take care of endianness ourselves.
|
||||
*/
|
||||
struct mcp25xxfd_hw_tx_obj_raw {
|
||||
__le32 id;
|
||||
__le32 flags;
|
||||
u8 data[sizeof_field(struct canfd_frame, data)];
|
||||
};
|
||||
|
||||
struct mcp25xxfd_hw_tx_obj_can {
|
||||
u32 id;
|
||||
u32 flags;
|
||||
u8 data[sizeof_field(struct can_frame, data)];
|
||||
};
|
||||
|
||||
struct mcp25xxfd_hw_tx_obj_canfd {
|
||||
u32 id;
|
||||
u32 flags;
|
||||
u8 data[sizeof_field(struct canfd_frame, data)];
|
||||
};
|
||||
|
||||
struct mcp25xxfd_hw_rx_obj_can {
|
||||
u32 id;
|
||||
u32 flags;
|
||||
u32 ts;
|
||||
u8 data[sizeof_field(struct can_frame, data)];
|
||||
};
|
||||
|
||||
struct mcp25xxfd_hw_rx_obj_canfd {
|
||||
u32 id;
|
||||
u32 flags;
|
||||
u32 ts;
|
||||
u8 data[sizeof_field(struct canfd_frame, data)];
|
||||
};
|
||||
|
||||
struct mcp25xxfd_tef_ring {
|
||||
unsigned int head;
|
||||
unsigned int tail;
|
||||
|
||||
/* u8 obj_num equals tx_ring->obj_num */
|
||||
/* u8 obj_size equals sizeof(struct mcp25xxfd_hw_tef_obj) */
|
||||
};
|
||||
|
||||
struct __packed mcp25xxfd_buf_cmd {
|
||||
__be16 cmd;
|
||||
};
|
||||
|
||||
struct __packed mcp25xxfd_buf_cmd_crc {
|
||||
__be16 cmd;
|
||||
u8 len;
|
||||
};
|
||||
|
||||
union mcp25xxfd_tx_obj_load_buf {
|
||||
struct __packed {
|
||||
struct mcp25xxfd_buf_cmd cmd;
|
||||
struct mcp25xxfd_hw_tx_obj_raw hw_tx_obj;
|
||||
} nocrc;
|
||||
struct __packed {
|
||||
struct mcp25xxfd_buf_cmd_crc cmd;
|
||||
struct mcp25xxfd_hw_tx_obj_raw hw_tx_obj;
|
||||
__be16 crc;
|
||||
} crc;
|
||||
} ____cacheline_aligned;
|
||||
|
||||
union mcp25xxfd_write_reg_buf {
|
||||
struct __packed {
|
||||
struct mcp25xxfd_buf_cmd cmd;
|
||||
u8 data[4];
|
||||
} nocrc;
|
||||
struct __packed {
|
||||
struct mcp25xxfd_buf_cmd_crc cmd;
|
||||
u8 data[4];
|
||||
__be16 crc;
|
||||
} crc;
|
||||
} ____cacheline_aligned;
|
||||
|
||||
struct mcp25xxfd_tx_obj {
|
||||
struct spi_message msg;
|
||||
struct spi_transfer xfer[2];
|
||||
union mcp25xxfd_tx_obj_load_buf buf;
|
||||
};
|
||||
|
||||
struct mcp25xxfd_tx_ring {
|
||||
unsigned int head;
|
||||
unsigned int tail;
|
||||
|
||||
u16 base;
|
||||
u8 obj_num;
|
||||
u8 obj_size;
|
||||
|
||||
struct mcp25xxfd_tx_obj obj[MCP25XXFD_TX_OBJ_NUM_MAX];
|
||||
union mcp25xxfd_write_reg_buf rts_buf;
|
||||
};
|
||||
|
||||
struct mcp25xxfd_rx_ring {
|
||||
unsigned int head;
|
||||
unsigned int tail;
|
||||
|
||||
u16 base;
|
||||
u8 nr;
|
||||
u8 fifo_nr;
|
||||
u8 obj_num;
|
||||
u8 obj_size;
|
||||
|
||||
struct mcp25xxfd_hw_rx_obj_canfd obj[];
|
||||
};
|
||||
|
||||
struct __packed mcp25xxfd_map_buf_nocrc {
|
||||
struct mcp25xxfd_buf_cmd cmd;
|
||||
u8 data[256];
|
||||
} ____cacheline_aligned;
|
||||
|
||||
struct __packed mcp25xxfd_map_buf_crc {
|
||||
struct mcp25xxfd_buf_cmd_crc cmd;
|
||||
u8 data[256 - 4];
|
||||
__be16 crc;
|
||||
} ____cacheline_aligned;
|
||||
|
||||
struct mcp25xxfd_ecc {
|
||||
u32 ecc_stat;
|
||||
int cnt;
|
||||
};
|
||||
|
||||
struct mcp25xxfd_regs_status {
|
||||
u32 intf;
|
||||
};
|
||||
|
||||
enum mcp25xxfd_model {
|
||||
MCP25XXFD_MODEL_MCP2517FD = 0x2517,
|
||||
MCP25XXFD_MODEL_MCP2518FD = 0x2518,
|
||||
MCP25XXFD_MODEL_MCP25XXFD = 0xffff, /* autodetect model */
|
||||
};
|
||||
|
||||
struct mcp25xxfd_devtype_data {
|
||||
enum mcp25xxfd_model model;
|
||||
u32 quirks;
|
||||
};
|
||||
|
||||
struct mcp25xxfd_priv {
|
||||
struct can_priv can;
|
||||
struct can_rx_offload offload;
|
||||
struct net_device *ndev;
|
||||
|
||||
struct regmap *map_reg; /* register access */
|
||||
struct regmap *map_rx; /* RX/TEF RAM access */
|
||||
|
||||
struct regmap *map_nocrc;
|
||||
struct mcp25xxfd_map_buf_nocrc *map_buf_nocrc_rx;
|
||||
struct mcp25xxfd_map_buf_nocrc *map_buf_nocrc_tx;
|
||||
|
||||
struct regmap *map_crc;
|
||||
struct mcp25xxfd_map_buf_crc *map_buf_crc_rx;
|
||||
struct mcp25xxfd_map_buf_crc *map_buf_crc_tx;
|
||||
|
||||
struct spi_device *spi;
|
||||
u32 spi_max_speed_hz_orig;
|
||||
|
||||
struct mcp25xxfd_tef_ring tef;
|
||||
struct mcp25xxfd_tx_ring tx[1];
|
||||
struct mcp25xxfd_rx_ring *rx[1];
|
||||
|
||||
u8 rx_ring_num;
|
||||
|
||||
struct mcp25xxfd_ecc ecc;
|
||||
struct mcp25xxfd_regs_status regs_status;
|
||||
|
||||
struct gpio_desc *rx_int;
|
||||
struct clk *clk;
|
||||
struct regulator *reg_vdd;
|
||||
struct regulator *reg_xceiver;
|
||||
|
||||
struct mcp25xxfd_devtype_data devtype_data;
|
||||
struct can_berr_counter bec;
|
||||
};
|
||||
|
||||
#define MCP25XXFD_IS(_model) \
|
||||
static inline bool \
|
||||
mcp25xxfd_is_##_model(const struct mcp25xxfd_priv *priv) \
|
||||
{ \
|
||||
return priv->devtype_data.model == MCP25XXFD_MODEL_MCP##_model##FD; \
|
||||
}
|
||||
|
||||
MCP25XXFD_IS(2517);
|
||||
MCP25XXFD_IS(2518);
|
||||
MCP25XXFD_IS(25XX);
|
||||
|
||||
static inline u8 mcp25xxfd_first_byte_set(u32 mask)
|
||||
{
|
||||
return (mask & 0x0000ffff) ?
|
||||
((mask & 0x000000ff) ? 0 : 1) :
|
||||
((mask & 0x00ff0000) ? 2 : 3);
|
||||
}
|
||||
|
||||
static inline u8 mcp25xxfd_last_byte_set(u32 mask)
|
||||
{
|
||||
return (mask & 0xffff0000) ?
|
||||
((mask & 0xff000000) ? 3 : 2) :
|
||||
((mask & 0x0000ff00) ? 1 : 0);
|
||||
}
|
||||
|
||||
static inline __be16 mcp25xxfd_cmd_reset(void)
|
||||
{
|
||||
return cpu_to_be16(MCP25XXFD_SPI_INSTRUCTION_RESET);
|
||||
}
|
||||
|
||||
static inline void
|
||||
mcp25xxfd_spi_cmd_read_nocrc(struct mcp25xxfd_buf_cmd *cmd, u16 addr)
|
||||
{
|
||||
cmd->cmd = cpu_to_be16(MCP25XXFD_SPI_INSTRUCTION_READ | addr);
|
||||
}
|
||||
|
||||
static inline void
|
||||
mcp25xxfd_spi_cmd_write_nocrc(struct mcp25xxfd_buf_cmd *cmd, u16 addr)
|
||||
{
|
||||
cmd->cmd = cpu_to_be16(MCP25XXFD_SPI_INSTRUCTION_WRITE | addr);
|
||||
}
|
||||
|
||||
static inline bool mcp25xxfd_reg_in_ram(unsigned int reg)
|
||||
{
|
||||
static const struct regmap_range range =
|
||||
regmap_reg_range(MCP25XXFD_RAM_START,
|
||||
MCP25XXFD_RAM_START + MCP25XXFD_RAM_SIZE - 4);
|
||||
|
||||
return regmap_reg_in_range(reg, &range);
|
||||
}
|
||||
|
||||
static inline void
|
||||
__mcp25xxfd_spi_cmd_crc_set_len(struct mcp25xxfd_buf_cmd_crc *cmd,
|
||||
u16 len, bool in_ram)
|
||||
{
|
||||
/* Number of u32 for RAM access, number of u8 otherwise. */
|
||||
if (in_ram)
|
||||
cmd->len = len >> 2;
|
||||
else
|
||||
cmd->len = len;
|
||||
}
|
||||
|
||||
static inline void
|
||||
mcp25xxfd_spi_cmd_crc_set_len_in_ram(struct mcp25xxfd_buf_cmd_crc *cmd, u16 len)
|
||||
{
|
||||
__mcp25xxfd_spi_cmd_crc_set_len(cmd, len, true);
|
||||
}
|
||||
|
||||
static inline void
|
||||
mcp25xxfd_spi_cmd_crc_set_len_in_reg(struct mcp25xxfd_buf_cmd_crc *cmd, u16 len)
|
||||
{
|
||||
__mcp25xxfd_spi_cmd_crc_set_len(cmd, len, false);
|
||||
}
|
||||
|
||||
static inline void
|
||||
mcp25xxfd_spi_cmd_read_crc_set_addr(struct mcp25xxfd_buf_cmd_crc *cmd, u16 addr)
|
||||
{
|
||||
cmd->cmd = cpu_to_be16(MCP25XXFD_SPI_INSTRUCTION_READ_CRC | addr);
|
||||
}
|
||||
|
||||
static inline void
|
||||
mcp25xxfd_spi_cmd_read_crc(struct mcp25xxfd_buf_cmd_crc *cmd,
|
||||
u16 addr, u16 len)
|
||||
{
|
||||
mcp25xxfd_spi_cmd_read_crc_set_addr(cmd, addr);
|
||||
__mcp25xxfd_spi_cmd_crc_set_len(cmd, len, mcp25xxfd_reg_in_ram(addr));
|
||||
}
|
||||
|
||||
static inline void
|
||||
mcp25xxfd_spi_cmd_write_crc_set_addr(struct mcp25xxfd_buf_cmd_crc *cmd,
|
||||
u16 addr)
|
||||
{
|
||||
cmd->cmd = cpu_to_be16(MCP25XXFD_SPI_INSTRUCTION_WRITE_CRC | addr);
|
||||
}
|
||||
|
||||
static inline void
|
||||
mcp25xxfd_spi_cmd_write_crc(struct mcp25xxfd_buf_cmd_crc *cmd,
|
||||
u16 addr, u16 len)
|
||||
{
|
||||
mcp25xxfd_spi_cmd_write_crc_set_addr(cmd, addr);
|
||||
__mcp25xxfd_spi_cmd_crc_set_len(cmd, len, mcp25xxfd_reg_in_ram(addr));
|
||||
}
|
||||
|
||||
static inline u8 *
|
||||
mcp25xxfd_spi_cmd_write(const struct mcp25xxfd_priv *priv,
|
||||
union mcp25xxfd_write_reg_buf *write_reg_buf,
|
||||
u16 addr)
|
||||
{
|
||||
u8 *data;
|
||||
|
||||
if (priv->devtype_data.quirks & MCP25XXFD_QUIRK_CRC_REG) {
|
||||
mcp25xxfd_spi_cmd_write_crc_set_addr(&write_reg_buf->crc.cmd,
|
||||
addr);
|
||||
data = write_reg_buf->crc.data;
|
||||
} else {
|
||||
mcp25xxfd_spi_cmd_write_nocrc(&write_reg_buf->nocrc.cmd,
|
||||
addr);
|
||||
data = write_reg_buf->nocrc.data;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static inline u16 mcp25xxfd_get_tef_obj_addr(u8 n)
|
||||
{
|
||||
return MCP25XXFD_RAM_START +
|
||||
sizeof(struct mcp25xxfd_hw_tef_obj) * n;
|
||||
}
|
||||
|
||||
static inline u16
|
||||
mcp25xxfd_get_tx_obj_addr(const struct mcp25xxfd_tx_ring *ring, u8 n)
|
||||
{
|
||||
return ring->base + ring->obj_size * n;
|
||||
}
|
||||
|
||||
static inline u16
|
||||
mcp25xxfd_get_rx_obj_addr(const struct mcp25xxfd_rx_ring *ring, u8 n)
|
||||
{
|
||||
return ring->base + ring->obj_size * n;
|
||||
}
|
||||
|
||||
static inline u8 mcp25xxfd_get_tef_head(const struct mcp25xxfd_priv *priv)
|
||||
{
|
||||
return priv->tef.head & (priv->tx->obj_num - 1);
|
||||
}
|
||||
|
||||
static inline u8 mcp25xxfd_get_tef_tail(const struct mcp25xxfd_priv *priv)
|
||||
{
|
||||
return priv->tef.tail & (priv->tx->obj_num - 1);
|
||||
}
|
||||
|
||||
static inline u8 mcp25xxfd_get_tef_len(const struct mcp25xxfd_priv *priv)
|
||||
{
|
||||
return priv->tef.head - priv->tef.tail;
|
||||
}
|
||||
|
||||
static inline u8 mcp25xxfd_get_tef_linear_len(const struct mcp25xxfd_priv *priv)
|
||||
{
|
||||
u8 len;
|
||||
|
||||
len = mcp25xxfd_get_tef_len(priv);
|
||||
|
||||
return min_t(u8, len, priv->tx->obj_num - mcp25xxfd_get_tef_tail(priv));
|
||||
}
|
||||
|
||||
static inline u8 mcp25xxfd_get_tx_head(const struct mcp25xxfd_tx_ring *ring)
|
||||
{
|
||||
return ring->head & (ring->obj_num - 1);
|
||||
}
|
||||
|
||||
static inline u8 mcp25xxfd_get_tx_tail(const struct mcp25xxfd_tx_ring *ring)
|
||||
{
|
||||
return ring->tail & (ring->obj_num - 1);
|
||||
}
|
||||
|
||||
static inline u8 mcp25xxfd_get_tx_free(const struct mcp25xxfd_tx_ring *ring)
|
||||
{
|
||||
return ring->obj_num - (ring->head - ring->tail);
|
||||
}
|
||||
|
||||
static inline int
|
||||
mcp25xxfd_get_tx_nr_by_addr(const struct mcp25xxfd_tx_ring *tx_ring, u8 *nr,
|
||||
u16 addr)
|
||||
{
|
||||
if (addr < mcp25xxfd_get_tx_obj_addr(tx_ring, 0) ||
|
||||
addr >= mcp25xxfd_get_tx_obj_addr(tx_ring, tx_ring->obj_num))
|
||||
return -ENOENT;
|
||||
|
||||
*nr = (addr - mcp25xxfd_get_tx_obj_addr(tx_ring, 0)) /
|
||||
tx_ring->obj_size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline u8 mcp25xxfd_get_rx_head(const struct mcp25xxfd_rx_ring *ring)
|
||||
{
|
||||
return ring->head & (ring->obj_num - 1);
|
||||
}
|
||||
|
||||
static inline u8 mcp25xxfd_get_rx_tail(const struct mcp25xxfd_rx_ring *ring)
|
||||
{
|
||||
return ring->tail & (ring->obj_num - 1);
|
||||
}
|
||||
|
||||
static inline u8 mcp25xxfd_get_rx_len(const struct mcp25xxfd_rx_ring *ring)
|
||||
{
|
||||
return ring->head - ring->tail;
|
||||
}
|
||||
|
||||
static inline u8
|
||||
mcp25xxfd_get_rx_linear_len(const struct mcp25xxfd_rx_ring *ring)
|
||||
{
|
||||
u8 len;
|
||||
|
||||
len = mcp25xxfd_get_rx_len(ring);
|
||||
|
||||
return min_t(u8, len, ring->obj_num - mcp25xxfd_get_rx_tail(ring));
|
||||
}
|
||||
|
||||
#define mcp25xxfd_for_each_tx_obj(ring, _obj, n) \
|
||||
for ((n) = 0, (_obj) = &(ring)->obj[(n)]; \
|
||||
(n) < (ring)->obj_num; \
|
||||
(n)++, (_obj) = &(ring)->obj[(n)])
|
||||
|
||||
#define mcp25xxfd_for_each_rx_ring(priv, ring, n) \
|
||||
for ((n) = 0, (ring) = *((priv)->rx + (n)); \
|
||||
(n) < (priv)->rx_ring_num; \
|
||||
(n)++, (ring) = *((priv)->rx + (n)))
|
||||
|
||||
int mcp25xxfd_regmap_init(struct mcp25xxfd_priv *priv);
|
||||
u16 mcp25xxfd_crc16_compute2(const void *cmd, size_t cmd_size,
|
||||
const void *data, size_t data_size);
|
||||
u16 mcp25xxfd_crc16_compute(const void *data, size_t data_size);
|
||||
|
||||
#endif
|
|
@ -454,7 +454,7 @@ static int ti_hecc_get_berr_counter(const struct net_device *ndev,
|
|||
/* ti_hecc_xmit: HECC Transmit
|
||||
*
|
||||
* The transmit mailboxes start from 0 to HECC_MAX_TX_MBOX. In HECC the
|
||||
* priority of the mailbox for tranmission is dependent upon priority setting
|
||||
* priority of the mailbox for transmission is dependent upon priority setting
|
||||
* field in mailbox registers. The mailbox with highest value in priority field
|
||||
* is transmitted first. Only when two mailboxes have the same value in
|
||||
* priority field the highest numbered mailbox is transmitted first.
|
||||
|
@ -857,7 +857,7 @@ static int ti_hecc_probe(struct platform_device *pdev)
|
|||
struct net_device *ndev = (struct net_device *)0;
|
||||
struct ti_hecc_priv *priv;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct resource *res, *irq;
|
||||
struct resource *irq;
|
||||
struct regulator *reg_xceiver;
|
||||
int err = -ENODEV;
|
||||
|
||||
|
@ -878,39 +878,22 @@ static int ti_hecc_probe(struct platform_device *pdev)
|
|||
priv = netdev_priv(ndev);
|
||||
|
||||
/* handle hecc memory */
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hecc");
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "can't get IORESOURCE_MEM hecc\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
priv->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
priv->base = devm_platform_ioremap_resource_byname(pdev, "hecc");
|
||||
if (IS_ERR(priv->base)) {
|
||||
dev_err(&pdev->dev, "hecc ioremap failed\n");
|
||||
return PTR_ERR(priv->base);
|
||||
}
|
||||
|
||||
/* handle hecc-ram memory */
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hecc-ram");
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "can't get IORESOURCE_MEM hecc-ram\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
priv->hecc_ram = devm_ioremap_resource(&pdev->dev, res);
|
||||
priv->hecc_ram = devm_platform_ioremap_resource_byname(pdev,
|
||||
"hecc-ram");
|
||||
if (IS_ERR(priv->hecc_ram)) {
|
||||
dev_err(&pdev->dev, "hecc-ram ioremap failed\n");
|
||||
return PTR_ERR(priv->hecc_ram);
|
||||
}
|
||||
|
||||
/* handle mbx memory */
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mbx");
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "can't get IORESOURCE_MEM mbx\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
priv->mbx = devm_ioremap_resource(&pdev->dev, res);
|
||||
priv->mbx = devm_platform_ioremap_resource_byname(pdev, "mbx");
|
||||
if (IS_ERR(priv->mbx)) {
|
||||
dev_err(&pdev->dev, "mbx ioremap failed\n");
|
||||
return PTR_ERR(priv->mbx);
|
||||
|
|
|
@ -90,7 +90,7 @@ config CAN_PEAK_USB
|
|||
tristate "PEAK PCAN-USB/USB Pro interfaces for CAN 2.0b/CAN-FD"
|
||||
help
|
||||
This driver supports the PEAK-System Technik USB adapters that enable
|
||||
access to the CAN bus, with repect to the CAN 2.0b and/or CAN-FD
|
||||
access to the CAN bus, with respect to the CAN 2.0b and/or CAN-FD
|
||||
standards, that is:
|
||||
|
||||
PCAN-USB single CAN 2.0b channel USB adapter
|
||||
|
|
|
@ -828,7 +828,7 @@ static struct gs_can *gs_make_candev(unsigned int channel,
|
|||
|
||||
netdev->flags |= IFF_ECHO; /* we support full roundtrip echo */
|
||||
|
||||
/* dev settup */
|
||||
/* dev setup */
|
||||
strcpy(dev->bt_const.name, "gs_usb");
|
||||
dev->bt_const.tseg1_min = bt_const->tseg1_min;
|
||||
dev->bt_const.tseg1_max = bt_const->tseg1_max;
|
||||
|
@ -852,7 +852,7 @@ static struct gs_can *gs_make_candev(unsigned int channel,
|
|||
dev->tx_context[rc].echo_id = GS_MAX_TX_URBS;
|
||||
}
|
||||
|
||||
/* can settup */
|
||||
/* can setup */
|
||||
dev->can.state = CAN_STATE_STOPPED;
|
||||
dev->can.clock.freq = bt_const->fclk_can;
|
||||
dev->can.bittiming_const = &dev->bt_const;
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
#define MCBA_CTX_FREE MCBA_MAX_TX_URBS
|
||||
|
||||
/* RX buffer must be bigger than msg size since at the
|
||||
* beggining USB messages are stacked.
|
||||
* beginning USB messages are stacked.
|
||||
*/
|
||||
#define MCBA_USB_RX_BUFF_SIZE 64
|
||||
#define MCBA_USB_TX_BUFF_SIZE (sizeof(struct mcba_usb_msg))
|
||||
|
@ -793,7 +793,7 @@ static int mcba_usb_probe(struct usb_interface *intf,
|
|||
{
|
||||
struct net_device *netdev;
|
||||
struct mcba_priv *priv;
|
||||
int err = -ENOMEM;
|
||||
int err;
|
||||
struct usb_device *usbdev = interface_to_usbdev(intf);
|
||||
|
||||
netdev = alloc_candev(sizeof(struct mcba_priv), MCBA_MAX_TX_URBS);
|
||||
|
|
|
@ -34,6 +34,23 @@ MODULE_SUPPORTED_DEVICE("PEAK-System PCAN-USB adapter");
|
|||
#define PCAN_USB_CMD_LEN (PCAN_USB_CMD_ARGS + \
|
||||
PCAN_USB_CMD_ARGS_LEN)
|
||||
|
||||
/* PCAN-USB commands */
|
||||
#define PCAN_USB_CMD_BITRATE 1
|
||||
#define PCAN_USB_CMD_SET_BUS 3
|
||||
#define PCAN_USB_CMD_DEVID 4
|
||||
#define PCAN_USB_CMD_SN 6
|
||||
#define PCAN_USB_CMD_REGISTER 9
|
||||
#define PCAN_USB_CMD_EXT_VCC 10
|
||||
#define PCAN_USB_CMD_ERR_FR 11
|
||||
|
||||
/* PCAN_USB_CMD_SET_BUS number arg */
|
||||
#define PCAN_USB_BUS_XCVER 2
|
||||
#define PCAN_USB_BUS_SILENT_MODE 3
|
||||
|
||||
/* PCAN_USB_CMD_xxx functions */
|
||||
#define PCAN_USB_GET 1
|
||||
#define PCAN_USB_SET 2
|
||||
|
||||
/* PCAN-USB command timeout (ms.) */
|
||||
#define PCAN_USB_COMMAND_TIMEOUT 1000
|
||||
|
||||
|
@ -66,6 +83,10 @@ MODULE_SUPPORTED_DEVICE("PEAK-System PCAN-USB adapter");
|
|||
#define PCAN_USB_ERROR_QOVR 0x40
|
||||
#define PCAN_USB_ERROR_TXQFULL 0x80
|
||||
|
||||
#define PCAN_USB_ERROR_BUS (PCAN_USB_ERROR_BUS_LIGHT | \
|
||||
PCAN_USB_ERROR_BUS_HEAVY | \
|
||||
PCAN_USB_ERROR_BUS_OFF)
|
||||
|
||||
/* SJA1000 modes */
|
||||
#define SJA1000_MODE_NORMAL 0x00
|
||||
#define SJA1000_MODE_INIT 0x01
|
||||
|
@ -85,11 +106,25 @@ MODULE_SUPPORTED_DEVICE("PEAK-System PCAN-USB adapter");
|
|||
#define PCAN_USB_REC_TS 4
|
||||
#define PCAN_USB_REC_BUSEVT 5
|
||||
|
||||
/* CAN bus events notifications selection mask */
|
||||
#define PCAN_USB_ERR_RXERR 0x02 /* ask for rxerr counter */
|
||||
#define PCAN_USB_ERR_TXERR 0x04 /* ask for txerr counter */
|
||||
|
||||
/* This mask generates an usb packet each time the state of the bus changes.
|
||||
* In other words, its interest is to know which side among rx and tx is
|
||||
* responsible of the change of the bus state.
|
||||
*/
|
||||
#define PCAN_USB_BERR_MASK (PCAN_USB_ERR_RXERR | PCAN_USB_ERR_TXERR)
|
||||
|
||||
/* identify bus event packets with rx/tx error counters */
|
||||
#define PCAN_USB_ERR_CNT 0x80
|
||||
|
||||
/* private to PCAN-USB adapter */
|
||||
struct pcan_usb {
|
||||
struct peak_usb_device dev;
|
||||
struct peak_time_ref time_ref;
|
||||
struct timer_list restart_timer;
|
||||
struct can_berr_counter bec;
|
||||
};
|
||||
|
||||
/* incoming message context for decoding */
|
||||
|
@ -172,7 +207,8 @@ static int pcan_usb_set_sja1000(struct peak_usb_device *dev, u8 mode)
|
|||
[1] = mode,
|
||||
};
|
||||
|
||||
return pcan_usb_send_cmd(dev, 9, 2, args);
|
||||
return pcan_usb_send_cmd(dev, PCAN_USB_CMD_REGISTER, PCAN_USB_SET,
|
||||
args);
|
||||
}
|
||||
|
||||
static int pcan_usb_set_bus(struct peak_usb_device *dev, u8 onoff)
|
||||
|
@ -181,7 +217,8 @@ static int pcan_usb_set_bus(struct peak_usb_device *dev, u8 onoff)
|
|||
[0] = !!onoff,
|
||||
};
|
||||
|
||||
return pcan_usb_send_cmd(dev, 3, 2, args);
|
||||
return pcan_usb_send_cmd(dev, PCAN_USB_CMD_SET_BUS, PCAN_USB_BUS_XCVER,
|
||||
args);
|
||||
}
|
||||
|
||||
static int pcan_usb_set_silent(struct peak_usb_device *dev, u8 onoff)
|
||||
|
@ -190,7 +227,18 @@ static int pcan_usb_set_silent(struct peak_usb_device *dev, u8 onoff)
|
|||
[0] = !!onoff,
|
||||
};
|
||||
|
||||
return pcan_usb_send_cmd(dev, 3, 3, args);
|
||||
return pcan_usb_send_cmd(dev, PCAN_USB_CMD_SET_BUS,
|
||||
PCAN_USB_BUS_SILENT_MODE, args);
|
||||
}
|
||||
|
||||
/* send the cmd to be notified from bus errors */
|
||||
static int pcan_usb_set_err_frame(struct peak_usb_device *dev, u8 err_mask)
|
||||
{
|
||||
u8 args[PCAN_USB_CMD_ARGS_LEN] = {
|
||||
[0] = err_mask,
|
||||
};
|
||||
|
||||
return pcan_usb_send_cmd(dev, PCAN_USB_CMD_ERR_FR, PCAN_USB_SET, args);
|
||||
}
|
||||
|
||||
static int pcan_usb_set_ext_vcc(struct peak_usb_device *dev, u8 onoff)
|
||||
|
@ -199,7 +247,7 @@ static int pcan_usb_set_ext_vcc(struct peak_usb_device *dev, u8 onoff)
|
|||
[0] = !!onoff,
|
||||
};
|
||||
|
||||
return pcan_usb_send_cmd(dev, 10, 2, args);
|
||||
return pcan_usb_send_cmd(dev, PCAN_USB_CMD_EXT_VCC, PCAN_USB_SET, args);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -223,7 +271,7 @@ static int pcan_usb_set_bittiming(struct peak_usb_device *dev,
|
|||
args[0] = btr1;
|
||||
args[1] = btr0;
|
||||
|
||||
return pcan_usb_send_cmd(dev, 1, 2, args);
|
||||
return pcan_usb_send_cmd(dev, PCAN_USB_CMD_BITRATE, PCAN_USB_SET, args);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -307,7 +355,7 @@ static int pcan_usb_get_serial(struct peak_usb_device *dev, u32 *serial_number)
|
|||
u8 args[PCAN_USB_CMD_ARGS_LEN];
|
||||
int err;
|
||||
|
||||
err = pcan_usb_wait_rsp(dev, 6, 1, args);
|
||||
err = pcan_usb_wait_rsp(dev, PCAN_USB_CMD_SN, PCAN_USB_GET, args);
|
||||
if (err) {
|
||||
netdev_err(dev->netdev, "getting serial failure: %d\n", err);
|
||||
} else if (serial_number) {
|
||||
|
@ -328,7 +376,7 @@ static int pcan_usb_get_device_id(struct peak_usb_device *dev, u32 *device_id)
|
|||
u8 args[PCAN_USB_CMD_ARGS_LEN];
|
||||
int err;
|
||||
|
||||
err = pcan_usb_wait_rsp(dev, 4, 1, args);
|
||||
err = pcan_usb_wait_rsp(dev, PCAN_USB_CMD_DEVID, PCAN_USB_GET, args);
|
||||
if (err)
|
||||
netdev_err(dev->netdev, "getting device id failure: %d\n", err);
|
||||
else if (device_id)
|
||||
|
@ -426,7 +474,7 @@ static int pcan_usb_decode_error(struct pcan_usb_msg_context *mc, u8 n,
|
|||
new_state = CAN_STATE_BUS_OFF;
|
||||
break;
|
||||
}
|
||||
if (n & (PCAN_USB_ERROR_RXQOVR | PCAN_USB_ERROR_QOVR)) {
|
||||
if (n & ~PCAN_USB_ERROR_BUS) {
|
||||
/*
|
||||
* trick to bypass next comparison and process other
|
||||
* errors
|
||||
|
@ -450,7 +498,7 @@ static int pcan_usb_decode_error(struct pcan_usb_msg_context *mc, u8 n,
|
|||
new_state = CAN_STATE_ERROR_WARNING;
|
||||
break;
|
||||
}
|
||||
if (n & (PCAN_USB_ERROR_RXQOVR | PCAN_USB_ERROR_QOVR)) {
|
||||
if (n & ~PCAN_USB_ERROR_BUS) {
|
||||
/*
|
||||
* trick to bypass next comparison and process other
|
||||
* errors
|
||||
|
@ -489,29 +537,50 @@ static int pcan_usb_decode_error(struct pcan_usb_msg_context *mc, u8 n,
|
|||
|
||||
case CAN_STATE_ERROR_PASSIVE:
|
||||
cf->can_id |= CAN_ERR_CRTL;
|
||||
cf->data[1] |= CAN_ERR_CRTL_TX_PASSIVE |
|
||||
cf->data[1] = (mc->pdev->bec.txerr > mc->pdev->bec.rxerr) ?
|
||||
CAN_ERR_CRTL_TX_PASSIVE :
|
||||
CAN_ERR_CRTL_RX_PASSIVE;
|
||||
cf->data[6] = mc->pdev->bec.txerr;
|
||||
cf->data[7] = mc->pdev->bec.rxerr;
|
||||
|
||||
mc->pdev->dev.can.can_stats.error_passive++;
|
||||
break;
|
||||
|
||||
case CAN_STATE_ERROR_WARNING:
|
||||
cf->can_id |= CAN_ERR_CRTL;
|
||||
cf->data[1] |= CAN_ERR_CRTL_TX_WARNING |
|
||||
cf->data[1] = (mc->pdev->bec.txerr > mc->pdev->bec.rxerr) ?
|
||||
CAN_ERR_CRTL_TX_WARNING :
|
||||
CAN_ERR_CRTL_RX_WARNING;
|
||||
cf->data[6] = mc->pdev->bec.txerr;
|
||||
cf->data[7] = mc->pdev->bec.rxerr;
|
||||
|
||||
mc->pdev->dev.can.can_stats.error_warning++;
|
||||
break;
|
||||
|
||||
case CAN_STATE_ERROR_ACTIVE:
|
||||
cf->can_id |= CAN_ERR_CRTL;
|
||||
cf->data[1] = CAN_ERR_CRTL_ACTIVE;
|
||||
|
||||
/* sync local copies of rxerr/txerr counters */
|
||||
mc->pdev->bec.txerr = 0;
|
||||
mc->pdev->bec.rxerr = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
/* CAN_STATE_MAX (trick to handle other errors) */
|
||||
if (n & PCAN_USB_ERROR_TXQFULL)
|
||||
netdev_dbg(mc->netdev, "device Tx queue full)\n");
|
||||
|
||||
if (n & PCAN_USB_ERROR_RXQOVR) {
|
||||
netdev_dbg(mc->netdev, "data overrun interrupt\n");
|
||||
cf->can_id |= CAN_ERR_CRTL;
|
||||
cf->data[1] |= CAN_ERR_CRTL_RX_OVERFLOW;
|
||||
mc->netdev->stats.rx_over_errors++;
|
||||
mc->netdev->stats.rx_errors++;
|
||||
}
|
||||
|
||||
cf->data[6] = mc->pdev->bec.txerr;
|
||||
cf->data[7] = mc->pdev->bec.rxerr;
|
||||
|
||||
new_state = mc->pdev->dev.can.state;
|
||||
break;
|
||||
|
@ -533,6 +602,30 @@ static int pcan_usb_decode_error(struct pcan_usb_msg_context *mc, u8 n,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* decode bus event usb packet: first byte contains rxerr while 2nd one contains
|
||||
* txerr.
|
||||
*/
|
||||
static int pcan_usb_handle_bus_evt(struct pcan_usb_msg_context *mc, u8 ir)
|
||||
{
|
||||
struct pcan_usb *pdev = mc->pdev;
|
||||
|
||||
/* acccording to the content of the packet */
|
||||
switch (ir) {
|
||||
case PCAN_USB_ERR_CNT:
|
||||
|
||||
/* save rx/tx error counters from in the device context */
|
||||
pdev->bec.rxerr = mc->ptr[0];
|
||||
pdev->bec.txerr = mc->ptr[1];
|
||||
break;
|
||||
|
||||
default:
|
||||
/* reserved */
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* decode non-data usb message
|
||||
*/
|
||||
|
@ -587,9 +680,10 @@ static int pcan_usb_decode_status(struct pcan_usb_msg_context *mc,
|
|||
break;
|
||||
|
||||
case PCAN_USB_REC_BUSEVT:
|
||||
/* error frame/bus event */
|
||||
if (n & PCAN_USB_ERROR_TXQFULL)
|
||||
netdev_dbg(mc->netdev, "device Tx queue full)\n");
|
||||
/* bus event notifications (get rxerr/txerr) */
|
||||
err = pcan_usb_handle_bus_evt(mc, n);
|
||||
if (err)
|
||||
return err;
|
||||
break;
|
||||
default:
|
||||
netdev_err(mc->netdev, "unexpected function %u\n", f);
|
||||
|
@ -773,20 +867,44 @@ static int pcan_usb_encode_msg(struct peak_usb_device *dev, struct sk_buff *skb,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* socket callback used to copy berr counters values received through USB */
|
||||
static int pcan_usb_get_berr_counter(const struct net_device *netdev,
|
||||
struct can_berr_counter *bec)
|
||||
{
|
||||
struct peak_usb_device *dev = netdev_priv(netdev);
|
||||
struct pcan_usb *pdev = container_of(dev, struct pcan_usb, dev);
|
||||
|
||||
*bec = pdev->bec;
|
||||
|
||||
/* must return 0 */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* start interface
|
||||
*/
|
||||
static int pcan_usb_start(struct peak_usb_device *dev)
|
||||
{
|
||||
struct pcan_usb *pdev = container_of(dev, struct pcan_usb, dev);
|
||||
int err;
|
||||
|
||||
/* number of bits used in timestamps read from adapter struct */
|
||||
peak_usb_init_time_ref(&pdev->time_ref, &pcan_usb);
|
||||
|
||||
pdev->bec.rxerr = 0;
|
||||
pdev->bec.txerr = 0;
|
||||
|
||||
/* be notified on error counter changes (if requested by user) */
|
||||
if (dev->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) {
|
||||
err = pcan_usb_set_err_frame(dev, PCAN_USB_BERR_MASK);
|
||||
if (err)
|
||||
netdev_warn(dev->netdev,
|
||||
"Asking for BERR reporting error %u\n",
|
||||
err);
|
||||
}
|
||||
|
||||
/* if revision greater than 3, can put silent mode on/off */
|
||||
if (dev->device_rev > 3) {
|
||||
int err;
|
||||
|
||||
err = pcan_usb_set_silent(dev,
|
||||
dev->can.ctrlmode & CAN_CTRLMODE_LISTENONLY);
|
||||
if (err)
|
||||
|
@ -873,7 +991,8 @@ const struct peak_usb_adapter pcan_usb = {
|
|||
.name = "PCAN-USB",
|
||||
.device_id = PCAN_USB_PRODUCT_ID,
|
||||
.ctrl_count = 1,
|
||||
.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES | CAN_CTRLMODE_LISTENONLY,
|
||||
.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES | CAN_CTRLMODE_LISTENONLY |
|
||||
CAN_CTRLMODE_BERR_REPORTING,
|
||||
.clock = {
|
||||
.freq = PCAN_USB_CRYSTAL_HZ / 2 ,
|
||||
},
|
||||
|
@ -906,4 +1025,5 @@ const struct peak_usb_adapter pcan_usb = {
|
|||
.dev_encode_msg = pcan_usb_encode_msg,
|
||||
.dev_start = pcan_usb_start,
|
||||
.dev_restart_async = pcan_usb_restart_async,
|
||||
.do_get_berr_counter = pcan_usb_get_berr_counter,
|
||||
};
|
||||
|
|
|
@ -35,7 +35,7 @@ MODULE_SUPPORTED_DEVICE("PEAK-System PCAN-USB Pro FD adapter");
|
|||
#define PCAN_UFD_RX_BUFFER_SIZE 2048
|
||||
#define PCAN_UFD_TX_BUFFER_SIZE 512
|
||||
|
||||
/* read some versions info from the hw devcie */
|
||||
/* read some versions info from the hw device */
|
||||
struct __packed pcan_ufd_fw_info {
|
||||
__le16 size_of; /* sizeof this */
|
||||
__le16 type; /* type of this structure */
|
||||
|
@ -796,7 +796,7 @@ static int pcan_usb_fd_start(struct peak_usb_device *dev)
|
|||
return err;
|
||||
}
|
||||
|
||||
/* socket callback used to copy berr counters values receieved through USB */
|
||||
/* socket callback used to copy berr counters values received through USB */
|
||||
static int pcan_usb_fd_get_berr_counter(const struct net_device *netdev,
|
||||
struct can_berr_counter *bec)
|
||||
{
|
||||
|
|
|
@ -186,7 +186,7 @@ static int pcan_msg_add_rec(struct pcan_usb_pro_msg *pm, int id, ...)
|
|||
|
||||
len = pc - pm->rec_ptr;
|
||||
if (len > 0) {
|
||||
*pm->u.rec_cnt = cpu_to_le32(le32_to_cpu(*pm->u.rec_cnt) + 1);
|
||||
le32_add_cpu(pm->u.rec_cnt, 1);
|
||||
*pm->rec_ptr = id;
|
||||
|
||||
pm->rec_ptr = pc;
|
||||
|
@ -973,7 +973,7 @@ int pcan_usb_pro_probe(struct usb_interface *intf)
|
|||
struct usb_endpoint_descriptor *ep = &if_desc->endpoint[i].desc;
|
||||
|
||||
/*
|
||||
* below is the list of valid ep addreses. Any other ep address
|
||||
* below is the list of valid ep addresses. Any other ep address
|
||||
* is considered as not-CAN interface address => no dev created
|
||||
*/
|
||||
switch (ep->bEndpointAddress) {
|
||||
|
|
|
@ -1445,7 +1445,7 @@ static int ucan_probe(struct usb_interface *intf,
|
|||
|
||||
/* request the device information and store it in ctl_msg_buffer
|
||||
*
|
||||
* note: ucan_ctrl_command_* wrappers connot be used yet
|
||||
* note: ucan_ctrl_command_* wrappers cannot be used yet
|
||||
* because `up` is initialised in Stage 3
|
||||
*/
|
||||
ret = usb_control_msg(udev,
|
||||
|
@ -1494,7 +1494,7 @@ static int ucan_probe(struct usb_interface *intf,
|
|||
|
||||
up = netdev_priv(netdev);
|
||||
|
||||
/* initialze data */
|
||||
/* initialize data */
|
||||
up->udev = udev;
|
||||
up->intf = intf;
|
||||
up->netdev = netdev;
|
||||
|
|
|
@ -88,7 +88,7 @@ enum usb_8dev_cmd {
|
|||
|
||||
/* status */
|
||||
#define USB_8DEV_STATUSMSG_OK 0x00 /* Normal condition. */
|
||||
#define USB_8DEV_STATUSMSG_OVERRUN 0x01 /* Overrun occured when sending */
|
||||
#define USB_8DEV_STATUSMSG_OVERRUN 0x01 /* Overrun occurred when sending */
|
||||
#define USB_8DEV_STATUSMSG_BUSLIGHT 0x02 /* Error counter has reached 96 */
|
||||
#define USB_8DEV_STATUSMSG_BUSHEAVY 0x03 /* Error count. has reached 128 */
|
||||
#define USB_8DEV_STATUSMSG_BUSOFF 0x04 /* Device is in BUSOFF */
|
||||
|
@ -165,7 +165,7 @@ struct __packed usb_8dev_rx_msg {
|
|||
/* command frame */
|
||||
struct __packed usb_8dev_cmd_msg {
|
||||
u8 begin;
|
||||
u8 channel; /* unkown - always 0 */
|
||||
u8 channel; /* unknown - always 0 */
|
||||
u8 command; /* command to execute */
|
||||
u8 opt1; /* optional parameter / return value */
|
||||
u8 opt2; /* optional parameter 2 */
|
||||
|
|
|
@ -1308,7 +1308,7 @@ static void xcan_tx_interrupt(struct net_device *ndev, u32 isr)
|
|||
/**
|
||||
* xcan_interrupt - CAN Isr
|
||||
* @irq: irq number
|
||||
* @dev_id: device id poniter
|
||||
* @dev_id: device id pointer
|
||||
*
|
||||
* This is the xilinx CAN Isr. It checks for the type of interrupt
|
||||
* and invokes the corresponding ISR.
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
/*
|
||||
* linux/can/core.h
|
||||
*
|
||||
* Protoypes and definitions for CAN protocol modules using the PF_CAN core
|
||||
* Prototypes and definitions for CAN protocol modules using the PF_CAN core
|
||||
*
|
||||
* Authors: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
|
||||
* Urs Thuermann <urs.thuermann@volkswagen.de>
|
||||
|
|
|
@ -108,7 +108,7 @@ static inline bool can_skb_headroom_valid(struct net_device *dev,
|
|||
|
||||
skb->ip_summed = CHECKSUM_UNNECESSARY;
|
||||
|
||||
/* preform proper loopback on capable devices */
|
||||
/* perform proper loopback on capable devices */
|
||||
if (dev->flags & IFF_ECHO)
|
||||
skb->pkt_type = PACKET_LOOPBACK;
|
||||
else
|
||||
|
@ -201,7 +201,7 @@ void can_bus_off(struct net_device *dev);
|
|||
void can_change_state(struct net_device *dev, struct can_frame *cf,
|
||||
enum can_state tx_state, enum can_state rx_state);
|
||||
|
||||
void can_put_echo_skb(struct sk_buff *skb, struct net_device *dev,
|
||||
int can_put_echo_skb(struct sk_buff *skb, struct net_device *dev,
|
||||
unsigned int idx);
|
||||
struct sk_buff *__can_get_echo_skb(struct net_device *dev, unsigned int idx,
|
||||
u8 *len_ptr);
|
||||
|
|
|
@ -35,6 +35,9 @@ int can_rx_offload_add_timestamp(struct net_device *dev,
|
|||
int can_rx_offload_add_fifo(struct net_device *dev,
|
||||
struct can_rx_offload *offload,
|
||||
unsigned int weight);
|
||||
int can_rx_offload_add_manual(struct net_device *dev,
|
||||
struct can_rx_offload *offload,
|
||||
unsigned int weight);
|
||||
int can_rx_offload_irq_offload_timestamp(struct can_rx_offload *offload,
|
||||
u64 reg);
|
||||
int can_rx_offload_irq_offload_fifo(struct can_rx_offload *offload);
|
||||
|
|
|
@ -358,7 +358,7 @@ static unsigned int effhash(canid_t can_id)
|
|||
*
|
||||
* Return:
|
||||
* Pointer to optimal filterlist for the given can_id/mask pair.
|
||||
* Constistency checked mask.
|
||||
* Consistency checked mask.
|
||||
* Reduced can_id to have a preprocessed filter compare value.
|
||||
*/
|
||||
static struct hlist_head *can_rcv_list_find(canid_t *can_id, canid_t *mask,
|
||||
|
@ -411,7 +411,7 @@ static struct hlist_head *can_rcv_list_find(canid_t *can_id, canid_t *mask,
|
|||
/**
|
||||
* can_rx_register - subscribe CAN frames from a specific interface
|
||||
* @net: the applicable net namespace
|
||||
* @dev: pointer to netdevice (NULL => subcribe from 'all' CAN devices list)
|
||||
* @dev: pointer to netdevice (NULL => subscribe from 'all' CAN devices list)
|
||||
* @can_id: CAN identifier (see description)
|
||||
* @mask: CAN mask (see description)
|
||||
* @func: callback function on filter match
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
|
||||
// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
|
||||
/*
|
||||
* bcm.c - Broadcast Manager to filter/send (cyclic) CAN content
|
||||
*
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
|
||||
// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
|
||||
/* gw.c - CAN frame Gateway/Router/Bridge with netlink interface
|
||||
*
|
||||
* Copyright (c) 2019 Volkswagen Group Electronic Research
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
|
||||
// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
|
||||
/*
|
||||
* proc.c - procfs support for Protocol family CAN core module
|
||||
*
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
|
||||
// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
|
||||
/* raw.c - Raw sockets for protocol family CAN
|
||||
*
|
||||
* Copyright (c) 2002-2007 Volkswagen Group Electronic Research
|
||||
|
@ -154,10 +154,10 @@ static void raw_rcv(struct sk_buff *oskb, void *data)
|
|||
if (!skb)
|
||||
return;
|
||||
|
||||
/* Put the datagram to the queue so that raw_recvmsg() can
|
||||
* get it from there. We need to pass the interface index to
|
||||
* raw_recvmsg(). We pass a whole struct sockaddr_can in skb->cb
|
||||
* containing the interface index.
|
||||
/* Put the datagram to the queue so that raw_recvmsg() can get
|
||||
* it from there. We need to pass the interface index to
|
||||
* raw_recvmsg(). We pass a whole struct sockaddr_can in
|
||||
* skb->cb containing the interface index.
|
||||
*/
|
||||
|
||||
sock_skb_cb_check_size(sizeof(struct sockaddr_can));
|
||||
|
|
Loading…
Reference in New Issue