mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-05 01:54:09 +08:00
linux-can-next-for-5.4-20190724
-----BEGIN PGP SIGNATURE----- iQFHBAABCgAxFiEEmvEkXzgOfc881GuFWsYho5HknSAFAl04GCsTHG1rbEBwZW5n dXRyb25peC5kZQAKCRBaxiGjkeSdIHzCB/kBJYwlm4Z9uDCj6niXSvONVFgcTpfY 8UTyUw7k5urddo9vuziMsOscS+6Jwndf2rgpFbJaVjEv8pNjryV1LUZ+nBPC3UYa +0tlhGzTa/myofwvtnYERqoSVB/WgFl1cn9mOYss+F+Dd9wyRk1HjHQZzmt7DMtD OJDRRE2CdgTJCKw25toOv5FTbw4qj3E0Q4lExXF82my2SeJsHY8y0/eQ80I0gebn OvaxHOgDd3cVPHwgp4evWUZtAevY/bPjEr9ojp2DqJ/EVHLMzfpaGtUza96J17Ku 7O6rsvdAzDo/yA6mLp+DBvVEKJdgtDHQ4wuQ30i5Wewj4k7ENkQVFuLr =0hl1 -----END PGP SIGNATURE----- Merge tag 'linux-can-next-for-5.4-20190724' of git://git.kernel.org/pub/scm/linux/kernel/git/mkl/linux-can-next Marc Kleine-Budde says: ==================== pull-request: can-next 2019-07-24 this is a pull request for net-next/master consisting of 26 patches. The first two patches are by me. One adds missing files of the CAN subsystem to the MAINTAINERS file, while the other sorts the Makefile/Kconfig of the sja1000 drivers sub directory. In the next patch Ji-Ze Hong (Peter Hong) provides a driver for the "Fintek PCIE to 2 CAN" controller, based on the the sja1000 IP core. Gustavo A. R. Silva's patch for the kvaser_usb driver introduces the use of struct_size() instead of open coding it. Henning Colliander's patch adds a driver for the "Kvaser PCIEcan" devices. Another patch by Gustavo A. R. Silva marks expected switch fall-throughs properly. Dan Murphy provides 5 patches for the m_can. After cleanups a framework is introduced so that the driver can be used from memory mapped IO as well as SPI attached devices. Finally he adds a driver for the tcan4x5x which uses this framework. A series of 5 patches by Appana Durga Kedareswara rao for the xilinx_can driver, first clean up,then add support for CANFD. Colin Ian King contributes another cleanup for the xilinx_can driver. Robert P. J. Day's patch corrects the brief history of the CAN protocol given in the Kconfig menu entry. 2 patches by Dong Aisheng for the flexcan driver provide PE clock source select support and dt-bindings description. 2 patches by Sean Nyekjaer for the flexcan driver provide add CAN wakeup-source property and dt-bindings description. Jeroen Hofstee's patch converts the ti_hecc driver to make use of the rx-offload helper fixing a number of outstanding bugs. The first patch of Oliver Hartkopp removes the now obsolete empty ioctl() handler for the CAN protocols. The second patch adds SPDX license identifiers for CAN subsystem. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
f876a78429
@ -32,6 +32,15 @@ Optional properties:
|
||||
ack_gpr is the gpr register offset of CAN stop acknowledge.
|
||||
ack_bit is the bit offset of CAN stop acknowledge.
|
||||
|
||||
- fsl,clk-source: Select the clock source to the CAN Protocol Engine (PE).
|
||||
It's SoC Implementation dependent. Refer to RM for detailed
|
||||
definition. If this property is not set in device tree node
|
||||
then driver selects clock source 1 by default.
|
||||
0: clock source 0 (oscillator clock)
|
||||
1: clock source 1 (peripheral clock)
|
||||
|
||||
- wakeup-source: enable CAN remote wakeup
|
||||
|
||||
Example:
|
||||
|
||||
can@1c000 {
|
||||
@ -40,4 +49,5 @@ Example:
|
||||
interrupts = <48 0x2>;
|
||||
interrupt-parent = <&mpic>;
|
||||
clock-frequency = <200000000>; // filled in by bootloader
|
||||
fsl,clk-source = <0>; // select clock source 0 for PE
|
||||
};
|
||||
|
37
Documentation/devicetree/bindings/net/can/tcan4x5x.txt
Normal file
37
Documentation/devicetree/bindings/net/can/tcan4x5x.txt
Normal file
@ -0,0 +1,37 @@
|
||||
Texas Instruments TCAN4x5x CAN Controller
|
||||
================================================
|
||||
|
||||
This file provides device node information for the TCAN4x5x interface contains.
|
||||
|
||||
Required properties:
|
||||
- compatible: "ti,tcan4x5x"
|
||||
- reg: 0
|
||||
- #address-cells: 1
|
||||
- #size-cells: 0
|
||||
- spi-max-frequency: Maximum frequency of the SPI bus the chip can
|
||||
operate at should be less than or equal to 18 MHz.
|
||||
- data-ready-gpios: Interrupt GPIO for data and error reporting.
|
||||
- device-wake-gpios: Wake up GPIO to wake up the TCAN device.
|
||||
|
||||
See Documentation/devicetree/bindings/net/can/m_can.txt for additional
|
||||
required property details.
|
||||
|
||||
Optional properties:
|
||||
- reset-gpios: Hardwired output GPIO. If not defined then software
|
||||
reset.
|
||||
- device-state-gpios: Input GPIO that indicates if the device is in
|
||||
a sleep state or if the device is active.
|
||||
|
||||
Example:
|
||||
tcan4x5x: tcan4x5x@0 {
|
||||
compatible = "ti,tcan4x5x";
|
||||
reg = <0>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
spi-max-frequency = <10000000>;
|
||||
bosch,mram-cfg = <0x0 0 0 32 0 0 1 1>;
|
||||
data-ready-gpios = <&gpio1 14 GPIO_ACTIVE_LOW>;
|
||||
device-state-gpios = <&gpio3 21 GPIO_ACTIVE_HIGH>;
|
||||
device-wake-gpios = <&gpio1 15 GPIO_ACTIVE_HIGH>;
|
||||
reset-gpios = <&gpio1 27 GPIO_ACTIVE_LOW>;
|
||||
};
|
@ -3631,9 +3631,12 @@ S: Maintained
|
||||
F: Documentation/devicetree/bindings/net/can/
|
||||
F: drivers/net/can/
|
||||
F: include/linux/can/dev.h
|
||||
F: include/linux/can/led.h
|
||||
F: include/linux/can/rx-offload.h
|
||||
F: include/linux/can/platform/
|
||||
F: include/uapi/linux/can/error.h
|
||||
F: include/uapi/linux/can/netlink.h
|
||||
F: include/uapi/linux/can/vxcan.h
|
||||
|
||||
CAN NETWORK LAYER
|
||||
M: Oliver Hartkopp <socketcan@hartkopp.net>
|
||||
@ -3646,6 +3649,8 @@ S: Maintained
|
||||
F: Documentation/networking/can.rst
|
||||
F: net/can/
|
||||
F: include/linux/can/core.h
|
||||
F: include/linux/can/skb.h
|
||||
F: include/net/netns/can.h
|
||||
F: include/uapi/linux/can.h
|
||||
F: include/uapi/linux/can/bcm.h
|
||||
F: include/uapi/linux/can/raw.h
|
||||
|
@ -120,6 +120,19 @@ config CAN_JANZ_ICAN3
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called janz-ican3.ko.
|
||||
|
||||
config CAN_KVASER_PCIEFD
|
||||
depends on PCI
|
||||
tristate "Kvaser PCIe FD cards"
|
||||
help
|
||||
This is a driver for the Kvaser PCI Express CAN FD family.
|
||||
|
||||
Supported devices:
|
||||
Kvaser PCIEcan 4xHS
|
||||
Kvaser PCIEcan 2xHS v2
|
||||
Kvaser PCIEcan HS v2
|
||||
Kvaser Mini PCI Express HS v2
|
||||
Kvaser Mini PCI Express 2xHS v2
|
||||
|
||||
config CAN_SUN4I
|
||||
tristate "Allwinner A10 CAN controller"
|
||||
depends on MACH_SUN4I || MACH_SUN7I || COMPILE_TEST
|
||||
|
@ -25,6 +25,7 @@ obj-$(CONFIG_CAN_FLEXCAN) += flexcan.o
|
||||
obj-$(CONFIG_CAN_GRCAN) += grcan.o
|
||||
obj-$(CONFIG_CAN_IFI_CANFD) += ifi_canfd/
|
||||
obj-$(CONFIG_CAN_JANZ_ICAN3) += janz-ican3.o
|
||||
obj-$(CONFIG_CAN_KVASER_PCIEFD) += kvaser_pciefd.o
|
||||
obj-$(CONFIG_CAN_MSCAN) += mscan/
|
||||
obj-$(CONFIG_CAN_M_CAN) += m_can/
|
||||
obj-$(CONFIG_CAN_PEAK_PCIEFD) += peak_canfd/
|
||||
|
@ -898,7 +898,8 @@ static void at91_irq_err_state(struct net_device *dev,
|
||||
CAN_ERR_CRTL_TX_WARNING :
|
||||
CAN_ERR_CRTL_RX_WARNING;
|
||||
}
|
||||
case CAN_STATE_ERROR_WARNING: /* fallthrough */
|
||||
/* fall through */
|
||||
case CAN_STATE_ERROR_WARNING:
|
||||
/*
|
||||
* from: ERROR_ACTIVE, ERROR_WARNING
|
||||
* to : ERROR_PASSIVE, BUS_OFF
|
||||
@ -947,7 +948,8 @@ static void at91_irq_err_state(struct net_device *dev,
|
||||
netdev_dbg(dev, "Error Active\n");
|
||||
cf->can_id |= CAN_ERR_PROT;
|
||||
cf->data[2] = CAN_ERR_PROT_ACTIVE;
|
||||
case CAN_STATE_ERROR_WARNING: /* fallthrough */
|
||||
/* fall through */
|
||||
case CAN_STATE_ERROR_WARNING:
|
||||
reg_idr = AT91_IRQ_ERRA | AT91_IRQ_WARN | AT91_IRQ_BOFF;
|
||||
reg_ier = AT91_IRQ_ERRP;
|
||||
break;
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
@ -266,6 +267,7 @@ struct flexcan_stop_mode {
|
||||
struct flexcan_priv {
|
||||
struct can_priv can;
|
||||
struct can_rx_offload offload;
|
||||
struct device *dev;
|
||||
|
||||
struct flexcan_regs __iomem *regs;
|
||||
struct flexcan_mb __iomem *tx_mb;
|
||||
@ -273,6 +275,8 @@ struct flexcan_priv {
|
||||
u8 tx_mb_idx;
|
||||
u8 mb_count;
|
||||
u8 mb_size;
|
||||
u8 clk_src; /* clock source of CAN Protocol Engine */
|
||||
|
||||
u32 reg_ctrl_default;
|
||||
u32 reg_imask1_default;
|
||||
u32 reg_imask2_default;
|
||||
@ -444,6 +448,27 @@ static inline void flexcan_error_irq_disable(const struct flexcan_priv *priv)
|
||||
priv->write(reg_ctrl, ®s->ctrl);
|
||||
}
|
||||
|
||||
static int flexcan_clks_enable(const struct flexcan_priv *priv)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = clk_prepare_enable(priv->clk_ipg);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = clk_prepare_enable(priv->clk_per);
|
||||
if (err)
|
||||
clk_disable_unprepare(priv->clk_ipg);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void flexcan_clks_disable(const struct flexcan_priv *priv)
|
||||
{
|
||||
clk_disable_unprepare(priv->clk_per);
|
||||
clk_disable_unprepare(priv->clk_ipg);
|
||||
}
|
||||
|
||||
static inline int flexcan_transceiver_enable(const struct flexcan_priv *priv)
|
||||
{
|
||||
if (!priv->reg_xceiver)
|
||||
@ -570,19 +595,13 @@ static int flexcan_get_berr_counter(const struct net_device *dev,
|
||||
const struct flexcan_priv *priv = netdev_priv(dev);
|
||||
int err;
|
||||
|
||||
err = clk_prepare_enable(priv->clk_ipg);
|
||||
if (err)
|
||||
err = pm_runtime_get_sync(priv->dev);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = clk_prepare_enable(priv->clk_per);
|
||||
if (err)
|
||||
goto out_disable_ipg;
|
||||
|
||||
err = __flexcan_get_berr_counter(dev, bec);
|
||||
|
||||
clk_disable_unprepare(priv->clk_per);
|
||||
out_disable_ipg:
|
||||
clk_disable_unprepare(priv->clk_ipg);
|
||||
pm_runtime_put(priv->dev);
|
||||
|
||||
return err;
|
||||
}
|
||||
@ -1215,17 +1234,13 @@ static int flexcan_open(struct net_device *dev)
|
||||
struct flexcan_priv *priv = netdev_priv(dev);
|
||||
int err;
|
||||
|
||||
err = clk_prepare_enable(priv->clk_ipg);
|
||||
if (err)
|
||||
err = pm_runtime_get_sync(priv->dev);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = clk_prepare_enable(priv->clk_per);
|
||||
if (err)
|
||||
goto out_disable_ipg;
|
||||
|
||||
err = open_candev(dev);
|
||||
if (err)
|
||||
goto out_disable_per;
|
||||
goto out_runtime_put;
|
||||
|
||||
err = request_irq(dev->irq, flexcan_irq, IRQF_SHARED, dev->name, dev);
|
||||
if (err)
|
||||
@ -1288,10 +1303,8 @@ static int flexcan_open(struct net_device *dev)
|
||||
free_irq(dev->irq, dev);
|
||||
out_close:
|
||||
close_candev(dev);
|
||||
out_disable_per:
|
||||
clk_disable_unprepare(priv->clk_per);
|
||||
out_disable_ipg:
|
||||
clk_disable_unprepare(priv->clk_ipg);
|
||||
out_runtime_put:
|
||||
pm_runtime_put(priv->dev);
|
||||
|
||||
return err;
|
||||
}
|
||||
@ -1306,10 +1319,9 @@ static int flexcan_close(struct net_device *dev)
|
||||
|
||||
can_rx_offload_del(&priv->offload);
|
||||
free_irq(dev->irq, dev);
|
||||
clk_disable_unprepare(priv->clk_per);
|
||||
clk_disable_unprepare(priv->clk_ipg);
|
||||
|
||||
close_candev(dev);
|
||||
pm_runtime_put(priv->dev);
|
||||
|
||||
can_led_event(dev, CAN_LED_EVENT_STOP);
|
||||
|
||||
@ -1349,20 +1361,20 @@ static int register_flexcandev(struct net_device *dev)
|
||||
struct flexcan_regs __iomem *regs = priv->regs;
|
||||
u32 reg, err;
|
||||
|
||||
err = clk_prepare_enable(priv->clk_ipg);
|
||||
err = flexcan_clks_enable(priv);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = clk_prepare_enable(priv->clk_per);
|
||||
if (err)
|
||||
goto out_disable_ipg;
|
||||
|
||||
/* select "bus clock", chip must be disabled */
|
||||
err = flexcan_chip_disable(priv);
|
||||
if (err)
|
||||
goto out_disable_per;
|
||||
goto out_clks_disable;
|
||||
|
||||
reg = priv->read(®s->ctrl);
|
||||
reg |= FLEXCAN_CTRL_CLK_SRC;
|
||||
if (priv->clk_src)
|
||||
reg |= FLEXCAN_CTRL_CLK_SRC;
|
||||
else
|
||||
reg &= ~FLEXCAN_CTRL_CLK_SRC;
|
||||
priv->write(reg, ®s->ctrl);
|
||||
|
||||
err = flexcan_chip_enable(priv);
|
||||
@ -1388,15 +1400,21 @@ static int register_flexcandev(struct net_device *dev)
|
||||
}
|
||||
|
||||
err = register_candev(dev);
|
||||
if (err)
|
||||
goto out_chip_disable;
|
||||
|
||||
/* Disable core and let pm_runtime_put() disable the clocks.
|
||||
* If CONFIG_PM is not enabled, the clocks will stay powered.
|
||||
*/
|
||||
flexcan_chip_disable(priv);
|
||||
pm_runtime_put(priv->dev);
|
||||
|
||||
return 0;
|
||||
|
||||
/* disable core and turn off clocks */
|
||||
out_chip_disable:
|
||||
flexcan_chip_disable(priv);
|
||||
out_disable_per:
|
||||
clk_disable_unprepare(priv->clk_per);
|
||||
out_disable_ipg:
|
||||
clk_disable_unprepare(priv->clk_ipg);
|
||||
|
||||
out_clks_disable:
|
||||
flexcan_clks_disable(priv);
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -1455,6 +1473,9 @@ static int flexcan_setup_stop_mode(struct platform_device *pdev)
|
||||
|
||||
device_set_wakeup_capable(&pdev->dev, true);
|
||||
|
||||
if (of_property_read_bool(np, "wakeup-source"))
|
||||
device_set_wakeup_enable(&pdev->dev, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1488,6 +1509,7 @@ static int flexcan_probe(struct platform_device *pdev)
|
||||
struct clk *clk_ipg = NULL, *clk_per = NULL;
|
||||
struct flexcan_regs __iomem *regs;
|
||||
int err, irq;
|
||||
u8 clk_src = 1;
|
||||
u32 clock_freq = 0;
|
||||
|
||||
reg_xceiver = devm_regulator_get(&pdev->dev, "xceiver");
|
||||
@ -1496,9 +1518,12 @@ static int flexcan_probe(struct platform_device *pdev)
|
||||
else if (IS_ERR(reg_xceiver))
|
||||
reg_xceiver = NULL;
|
||||
|
||||
if (pdev->dev.of_node)
|
||||
if (pdev->dev.of_node) {
|
||||
of_property_read_u32(pdev->dev.of_node,
|
||||
"clock-frequency", &clock_freq);
|
||||
of_property_read_u8(pdev->dev.of_node,
|
||||
"fsl,clk-source", &clk_src);
|
||||
}
|
||||
|
||||
if (!clock_freq) {
|
||||
clk_ipg = devm_clk_get(&pdev->dev, "ipg");
|
||||
@ -1556,6 +1581,7 @@ static int flexcan_probe(struct platform_device *pdev)
|
||||
priv->write = flexcan_write_le;
|
||||
}
|
||||
|
||||
priv->dev = &pdev->dev;
|
||||
priv->can.clock.freq = clock_freq;
|
||||
priv->can.bittiming_const = &flexcan_bittiming_const;
|
||||
priv->can.do_set_mode = flexcan_set_mode;
|
||||
@ -1566,9 +1592,14 @@ static int flexcan_probe(struct platform_device *pdev)
|
||||
priv->regs = regs;
|
||||
priv->clk_ipg = clk_ipg;
|
||||
priv->clk_per = clk_per;
|
||||
priv->clk_src = clk_src;
|
||||
priv->devtype_data = devtype_data;
|
||||
priv->reg_xceiver = reg_xceiver;
|
||||
|
||||
pm_runtime_get_noresume(&pdev->dev);
|
||||
pm_runtime_set_active(&pdev->dev);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
err = register_flexcandev(dev);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "registering netdev failed\n");
|
||||
@ -1595,6 +1626,7 @@ static int flexcan_remove(struct platform_device *pdev)
|
||||
struct net_device *dev = platform_get_drvdata(pdev);
|
||||
|
||||
unregister_flexcandev(dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
free_candev(dev);
|
||||
|
||||
return 0;
|
||||
@ -1604,7 +1636,7 @@ static int __maybe_unused flexcan_suspend(struct device *device)
|
||||
{
|
||||
struct net_device *dev = dev_get_drvdata(device);
|
||||
struct flexcan_priv *priv = netdev_priv(dev);
|
||||
int err;
|
||||
int err = 0;
|
||||
|
||||
if (netif_running(dev)) {
|
||||
/* if wakeup is enabled, enter stop mode
|
||||
@ -1617,20 +1649,22 @@ static int __maybe_unused flexcan_suspend(struct device *device)
|
||||
err = flexcan_chip_disable(priv);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = pm_runtime_force_suspend(device);
|
||||
}
|
||||
netif_stop_queue(dev);
|
||||
netif_device_detach(dev);
|
||||
}
|
||||
priv->can.state = CAN_STATE_SLEEPING;
|
||||
|
||||
return 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __maybe_unused flexcan_resume(struct device *device)
|
||||
{
|
||||
struct net_device *dev = dev_get_drvdata(device);
|
||||
struct flexcan_priv *priv = netdev_priv(dev);
|
||||
int err;
|
||||
int err = 0;
|
||||
|
||||
priv->can.state = CAN_STATE_ERROR_ACTIVE;
|
||||
if (netif_running(dev)) {
|
||||
@ -1639,14 +1673,35 @@ static int __maybe_unused flexcan_resume(struct device *device)
|
||||
if (device_may_wakeup(device)) {
|
||||
disable_irq_wake(dev->irq);
|
||||
} else {
|
||||
err = flexcan_chip_enable(priv);
|
||||
err = pm_runtime_force_resume(device);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = flexcan_chip_enable(priv);
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __maybe_unused flexcan_runtime_suspend(struct device *device)
|
||||
{
|
||||
struct net_device *dev = dev_get_drvdata(device);
|
||||
struct flexcan_priv *priv = netdev_priv(dev);
|
||||
|
||||
flexcan_clks_disable(priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused flexcan_runtime_resume(struct device *device)
|
||||
{
|
||||
struct net_device *dev = dev_get_drvdata(device);
|
||||
struct flexcan_priv *priv = netdev_priv(dev);
|
||||
|
||||
return flexcan_clks_enable(priv);
|
||||
}
|
||||
|
||||
static int __maybe_unused flexcan_noirq_suspend(struct device *device)
|
||||
{
|
||||
struct net_device *dev = dev_get_drvdata(device);
|
||||
@ -1673,6 +1728,7 @@ static int __maybe_unused flexcan_noirq_resume(struct device *device)
|
||||
|
||||
static const struct dev_pm_ops flexcan_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(flexcan_suspend, flexcan_resume)
|
||||
SET_RUNTIME_PM_OPS(flexcan_runtime_suspend, flexcan_runtime_resume, NULL)
|
||||
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(flexcan_noirq_suspend, flexcan_noirq_resume)
|
||||
};
|
||||
|
||||
|
1912
drivers/net/can/kvaser_pciefd.c
Normal file
1912
drivers/net/can/kvaser_pciefd.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,24 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
config CAN_M_CAN
|
||||
depends on HAS_IOMEM
|
||||
tristate "Bosch M_CAN devices"
|
||||
tristate "Bosch M_CAN support"
|
||||
---help---
|
||||
Say Y here if you want to support for Bosch M_CAN controller.
|
||||
Say Y here if you want support for Bosch M_CAN controller framework.
|
||||
This is common support for devices that embed the Bosch M_CAN IP.
|
||||
|
||||
config CAN_M_CAN_PLATFORM
|
||||
tristate "Bosch M_CAN support for io-mapped devices"
|
||||
depends on HAS_IOMEM
|
||||
depends on CAN_M_CAN
|
||||
---help---
|
||||
Say Y here if you want support for IO Mapped Bosch M_CAN controller.
|
||||
This support is for devices that have the Bosch M_CAN controller
|
||||
IP embedded into the device and the IP is IO Mapped to the processor.
|
||||
|
||||
config CAN_M_CAN_TCAN4X5X
|
||||
depends on CAN_M_CAN
|
||||
depends on REGMAP_SPI
|
||||
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
|
||||
SPI bus for communication.
|
||||
|
@ -4,3 +4,5 @@
|
||||
#
|
||||
|
||||
obj-$(CONFIG_CAN_M_CAN) += m_can.o
|
||||
obj-$(CONFIG_CAN_M_CAN_PLATFORM) += m_can_platform.o
|
||||
obj-$(CONFIG_CAN_M_CAN_TCAN4X5X) += tcan4x5x.o
|
||||
|
File diff suppressed because it is too large
Load Diff
110
drivers/net/can/m_can/m_can.h
Normal file
110
drivers/net/can/m_can/m_can.h
Normal file
@ -0,0 +1,110 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* CAN bus driver for Bosch M_CAN controller
|
||||
* Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
|
||||
*/
|
||||
|
||||
#ifndef _CAN_M_CAN_H_
|
||||
#define _CAN_M_CAN_H_
|
||||
|
||||
#include <linux/can/core.h>
|
||||
#include <linux/can/led.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/freezer.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/can/dev.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
|
||||
/* m_can lec values */
|
||||
enum m_can_lec_type {
|
||||
LEC_NO_ERROR = 0,
|
||||
LEC_STUFF_ERROR,
|
||||
LEC_FORM_ERROR,
|
||||
LEC_ACK_ERROR,
|
||||
LEC_BIT1_ERROR,
|
||||
LEC_BIT0_ERROR,
|
||||
LEC_CRC_ERROR,
|
||||
LEC_UNUSED,
|
||||
};
|
||||
|
||||
enum m_can_mram_cfg {
|
||||
MRAM_SIDF = 0,
|
||||
MRAM_XIDF,
|
||||
MRAM_RXF0,
|
||||
MRAM_RXF1,
|
||||
MRAM_RXB,
|
||||
MRAM_TXE,
|
||||
MRAM_TXB,
|
||||
MRAM_CFG_NUM,
|
||||
};
|
||||
|
||||
/* address offset and element number for each FIFO/Buffer in the Message RAM */
|
||||
struct mram_cfg {
|
||||
u16 off;
|
||||
u8 num;
|
||||
};
|
||||
|
||||
struct m_can_classdev;
|
||||
struct m_can_ops {
|
||||
/* Device specific call backs */
|
||||
int (*clear_interrupts)(struct m_can_classdev *cdev);
|
||||
u32 (*read_reg)(struct m_can_classdev *cdev, int reg);
|
||||
int (*write_reg)(struct m_can_classdev *cdev, int reg, int val);
|
||||
u32 (*read_fifo)(struct m_can_classdev *cdev, int addr_offset);
|
||||
int (*write_fifo)(struct m_can_classdev *cdev, int addr_offset,
|
||||
int val);
|
||||
int (*init)(struct m_can_classdev *cdev);
|
||||
};
|
||||
|
||||
struct m_can_classdev {
|
||||
struct can_priv can;
|
||||
struct napi_struct napi;
|
||||
struct net_device *net;
|
||||
struct device *dev;
|
||||
struct clk *hclk;
|
||||
struct clk *cclk;
|
||||
|
||||
struct workqueue_struct *tx_wq;
|
||||
struct work_struct tx_work;
|
||||
struct sk_buff *tx_skb;
|
||||
|
||||
struct can_bittiming_const *bit_timing;
|
||||
struct can_bittiming_const *data_timing;
|
||||
|
||||
struct m_can_ops *ops;
|
||||
|
||||
void *device_data;
|
||||
|
||||
int version;
|
||||
int freq;
|
||||
u32 irqstatus;
|
||||
|
||||
int pm_clock_support;
|
||||
int is_peripheral;
|
||||
|
||||
struct mram_cfg mcfg[MRAM_CFG_NUM];
|
||||
};
|
||||
|
||||
struct m_can_classdev *m_can_class_allocate_dev(struct device *dev);
|
||||
int m_can_class_register(struct m_can_classdev *cdev);
|
||||
void m_can_class_unregister(struct m_can_classdev *cdev);
|
||||
int m_can_class_get_clocks(struct m_can_classdev *cdev);
|
||||
void m_can_init_ram(struct m_can_classdev *priv);
|
||||
void m_can_config_endisable(struct m_can_classdev *priv, bool enable);
|
||||
|
||||
int m_can_class_suspend(struct device *dev);
|
||||
int m_can_class_resume(struct device *dev);
|
||||
#endif /* _CAN_M_H_ */
|
202
drivers/net/can/m_can/m_can_platform.c
Normal file
202
drivers/net/can/m_can/m_can_platform.c
Normal file
@ -0,0 +1,202 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// IOMapped CAN bus driver for Bosch M_CAN controller
|
||||
// Copyright (C) 2014 Freescale Semiconductor, Inc.
|
||||
// Dong Aisheng <b29396@freescale.com>
|
||||
//
|
||||
// Copyright (C) 2018-19 Texas Instruments Incorporated - http://www.ti.com/
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include "m_can.h"
|
||||
|
||||
struct m_can_plat_priv {
|
||||
void __iomem *base;
|
||||
void __iomem *mram_base;
|
||||
};
|
||||
|
||||
static u32 iomap_read_reg(struct m_can_classdev *cdev, int reg)
|
||||
{
|
||||
struct m_can_plat_priv *priv =
|
||||
(struct m_can_plat_priv *)cdev->device_data;
|
||||
|
||||
return readl(priv->base + reg);
|
||||
}
|
||||
|
||||
static u32 iomap_read_fifo(struct m_can_classdev *cdev, int offset)
|
||||
{
|
||||
struct m_can_plat_priv *priv =
|
||||
(struct m_can_plat_priv *)cdev->device_data;
|
||||
|
||||
return readl(priv->mram_base + offset);
|
||||
}
|
||||
|
||||
static int iomap_write_reg(struct m_can_classdev *cdev, int reg, int val)
|
||||
{
|
||||
struct m_can_plat_priv *priv =
|
||||
(struct m_can_plat_priv *)cdev->device_data;
|
||||
|
||||
writel(val, priv->base + reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iomap_write_fifo(struct m_can_classdev *cdev, int offset, int val)
|
||||
{
|
||||
struct m_can_plat_priv *priv =
|
||||
(struct m_can_plat_priv *)cdev->device_data;
|
||||
|
||||
writel(val, priv->mram_base + offset);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct m_can_ops m_can_plat_ops = {
|
||||
.read_reg = iomap_read_reg,
|
||||
.write_reg = iomap_write_reg,
|
||||
.write_fifo = iomap_write_fifo,
|
||||
.read_fifo = iomap_read_fifo,
|
||||
};
|
||||
|
||||
static int m_can_plat_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct m_can_classdev *mcan_class;
|
||||
struct m_can_plat_priv *priv;
|
||||
struct resource *res;
|
||||
void __iomem *addr;
|
||||
void __iomem *mram_addr;
|
||||
int irq, ret = 0;
|
||||
|
||||
mcan_class = m_can_class_allocate_dev(&pdev->dev);
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
mcan_class->device_data = priv;
|
||||
|
||||
m_can_class_get_clocks(mcan_class);
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "m_can");
|
||||
addr = devm_ioremap_resource(&pdev->dev, res);
|
||||
irq = platform_get_irq_byname(pdev, "int0");
|
||||
if (IS_ERR(addr) || irq < 0) {
|
||||
ret = -EINVAL;
|
||||
goto failed_ret;
|
||||
}
|
||||
|
||||
/* message ram could be shared */
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "message_ram");
|
||||
if (!res) {
|
||||
ret = -ENODEV;
|
||||
goto failed_ret;
|
||||
}
|
||||
|
||||
mram_addr = devm_ioremap(&pdev->dev, res->start, resource_size(res));
|
||||
if (!mram_addr) {
|
||||
ret = -ENOMEM;
|
||||
goto failed_ret;
|
||||
}
|
||||
|
||||
priv->base = addr;
|
||||
priv->mram_base = mram_addr;
|
||||
|
||||
mcan_class->net->irq = irq;
|
||||
mcan_class->pm_clock_support = 1;
|
||||
mcan_class->can.clock.freq = clk_get_rate(mcan_class->cclk);
|
||||
mcan_class->dev = &pdev->dev;
|
||||
|
||||
mcan_class->ops = &m_can_plat_ops;
|
||||
|
||||
mcan_class->is_peripheral = false;
|
||||
|
||||
platform_set_drvdata(pdev, mcan_class->dev);
|
||||
|
||||
m_can_init_ram(mcan_class);
|
||||
|
||||
ret = m_can_class_register(mcan_class);
|
||||
|
||||
failed_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static __maybe_unused int m_can_suspend(struct device *dev)
|
||||
{
|
||||
return m_can_class_suspend(dev);
|
||||
}
|
||||
|
||||
static __maybe_unused int m_can_resume(struct device *dev)
|
||||
{
|
||||
return m_can_class_resume(dev);
|
||||
}
|
||||
|
||||
static int m_can_plat_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct net_device *dev = platform_get_drvdata(pdev);
|
||||
struct m_can_classdev *mcan_class = netdev_priv(dev);
|
||||
|
||||
m_can_class_unregister(mcan_class);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused m_can_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct net_device *ndev = dev_get_drvdata(dev);
|
||||
struct m_can_classdev *mcan_class = netdev_priv(ndev);
|
||||
|
||||
m_can_class_suspend(dev);
|
||||
|
||||
clk_disable_unprepare(mcan_class->cclk);
|
||||
clk_disable_unprepare(mcan_class->hclk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused m_can_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct net_device *ndev = dev_get_drvdata(dev);
|
||||
struct m_can_classdev *mcan_class = netdev_priv(ndev);
|
||||
int err;
|
||||
|
||||
err = clk_prepare_enable(mcan_class->hclk);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = clk_prepare_enable(mcan_class->cclk);
|
||||
if (err)
|
||||
clk_disable_unprepare(mcan_class->hclk);
|
||||
|
||||
m_can_class_resume(dev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops m_can_pmops = {
|
||||
SET_RUNTIME_PM_OPS(m_can_runtime_suspend,
|
||||
m_can_runtime_resume, NULL)
|
||||
SET_SYSTEM_SLEEP_PM_OPS(m_can_suspend, m_can_resume)
|
||||
};
|
||||
|
||||
static const struct of_device_id m_can_of_table[] = {
|
||||
{ .compatible = "bosch,m_can", .data = NULL },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, m_can_of_table);
|
||||
|
||||
static struct platform_driver m_can_plat_driver = {
|
||||
.driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.of_match_table = m_can_of_table,
|
||||
.pm = &m_can_pmops,
|
||||
},
|
||||
.probe = m_can_plat_probe,
|
||||
.remove = m_can_plat_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(m_can_plat_driver);
|
||||
|
||||
MODULE_AUTHOR("Dong Aisheng <b29396@freescale.com>");
|
||||
MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("M_CAN driver for IO Mapped Bosch controllers");
|
532
drivers/net/can/m_can/tcan4x5x.c
Normal file
532
drivers/net/can/m_can/tcan4x5x.c
Normal file
@ -0,0 +1,532 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// SPI to CAN driver for the Texas Instruments TCAN4x5x
|
||||
// Copyright (C) 2018-19 Texas Instruments Incorporated - http://www.ti.com/
|
||||
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
|
||||
#include "m_can.h"
|
||||
|
||||
#define DEVICE_NAME "tcan4x5x"
|
||||
#define TCAN4X5X_EXT_CLK_DEF 40000000
|
||||
|
||||
#define TCAN4X5X_DEV_ID0 0x00
|
||||
#define TCAN4X5X_DEV_ID1 0x04
|
||||
#define TCAN4X5X_REV 0x08
|
||||
#define TCAN4X5X_STATUS 0x0C
|
||||
#define TCAN4X5X_ERROR_STATUS 0x10
|
||||
#define TCAN4X5X_CONTROL 0x14
|
||||
|
||||
#define TCAN4X5X_CONFIG 0x800
|
||||
#define TCAN4X5X_TS_PRESCALE 0x804
|
||||
#define TCAN4X5X_TEST_REG 0x808
|
||||
#define TCAN4X5X_INT_FLAGS 0x820
|
||||
#define TCAN4X5X_MCAN_INT_REG 0x824
|
||||
#define TCAN4X5X_INT_EN 0x830
|
||||
|
||||
/* Interrupt bits */
|
||||
#define TCAN4X5X_CANBUSTERMOPEN_INT_EN BIT(30)
|
||||
#define TCAN4X5X_CANHCANL_INT_EN BIT(29)
|
||||
#define TCAN4X5X_CANHBAT_INT_EN BIT(28)
|
||||
#define TCAN4X5X_CANLGND_INT_EN BIT(27)
|
||||
#define TCAN4X5X_CANBUSOPEN_INT_EN BIT(26)
|
||||
#define TCAN4X5X_CANBUSGND_INT_EN BIT(25)
|
||||
#define TCAN4X5X_CANBUSBAT_INT_EN BIT(24)
|
||||
#define TCAN4X5X_UVSUP_INT_EN BIT(22)
|
||||
#define TCAN4X5X_UVIO_INT_EN BIT(21)
|
||||
#define TCAN4X5X_TSD_INT_EN BIT(19)
|
||||
#define TCAN4X5X_ECCERR_INT_EN BIT(16)
|
||||
#define TCAN4X5X_CANINT_INT_EN BIT(15)
|
||||
#define TCAN4X5X_LWU_INT_EN BIT(14)
|
||||
#define TCAN4X5X_CANSLNT_INT_EN BIT(10)
|
||||
#define TCAN4X5X_CANDOM_INT_EN BIT(8)
|
||||
#define TCAN4X5X_CANBUS_ERR_INT_EN BIT(5)
|
||||
#define TCAN4X5X_BUS_FAULT BIT(4)
|
||||
#define TCAN4X5X_MCAN_INT BIT(1)
|
||||
#define TCAN4X5X_ENABLE_TCAN_INT \
|
||||
(TCAN4X5X_MCAN_INT | TCAN4X5X_BUS_FAULT | \
|
||||
TCAN4X5X_CANBUS_ERR_INT_EN | TCAN4X5X_CANINT_INT_EN)
|
||||
|
||||
/* MCAN Interrupt bits */
|
||||
#define TCAN4X5X_MCAN_IR_ARA BIT(29)
|
||||
#define TCAN4X5X_MCAN_IR_PED BIT(28)
|
||||
#define TCAN4X5X_MCAN_IR_PEA BIT(27)
|
||||
#define TCAN4X5X_MCAN_IR_WD BIT(26)
|
||||
#define TCAN4X5X_MCAN_IR_BO BIT(25)
|
||||
#define TCAN4X5X_MCAN_IR_EW BIT(24)
|
||||
#define TCAN4X5X_MCAN_IR_EP BIT(23)
|
||||
#define TCAN4X5X_MCAN_IR_ELO BIT(22)
|
||||
#define TCAN4X5X_MCAN_IR_BEU BIT(21)
|
||||
#define TCAN4X5X_MCAN_IR_BEC BIT(20)
|
||||
#define TCAN4X5X_MCAN_IR_DRX BIT(19)
|
||||
#define TCAN4X5X_MCAN_IR_TOO BIT(18)
|
||||
#define TCAN4X5X_MCAN_IR_MRAF BIT(17)
|
||||
#define TCAN4X5X_MCAN_IR_TSW BIT(16)
|
||||
#define TCAN4X5X_MCAN_IR_TEFL BIT(15)
|
||||
#define TCAN4X5X_MCAN_IR_TEFF BIT(14)
|
||||
#define TCAN4X5X_MCAN_IR_TEFW BIT(13)
|
||||
#define TCAN4X5X_MCAN_IR_TEFN BIT(12)
|
||||
#define TCAN4X5X_MCAN_IR_TFE BIT(11)
|
||||
#define TCAN4X5X_MCAN_IR_TCF BIT(10)
|
||||
#define TCAN4X5X_MCAN_IR_TC BIT(9)
|
||||
#define TCAN4X5X_MCAN_IR_HPM BIT(8)
|
||||
#define TCAN4X5X_MCAN_IR_RF1L BIT(7)
|
||||
#define TCAN4X5X_MCAN_IR_RF1F BIT(6)
|
||||
#define TCAN4X5X_MCAN_IR_RF1W BIT(5)
|
||||
#define TCAN4X5X_MCAN_IR_RF1N BIT(4)
|
||||
#define TCAN4X5X_MCAN_IR_RF0L BIT(3)
|
||||
#define TCAN4X5X_MCAN_IR_RF0F BIT(2)
|
||||
#define TCAN4X5X_MCAN_IR_RF0W BIT(1)
|
||||
#define TCAN4X5X_MCAN_IR_RF0N BIT(0)
|
||||
#define TCAN4X5X_ENABLE_MCAN_INT \
|
||||
(TCAN4X5X_MCAN_IR_TC | TCAN4X5X_MCAN_IR_RF0N | \
|
||||
TCAN4X5X_MCAN_IR_RF1N | TCAN4X5X_MCAN_IR_RF0F | \
|
||||
TCAN4X5X_MCAN_IR_RF1F)
|
||||
|
||||
#define TCAN4X5X_MRAM_START 0x8000
|
||||
#define TCAN4X5X_MCAN_OFFSET 0x1000
|
||||
#define TCAN4X5X_MAX_REGISTER 0x8fff
|
||||
|
||||
#define TCAN4X5X_CLEAR_ALL_INT 0xffffffff
|
||||
#define TCAN4X5X_SET_ALL_INT 0xffffffff
|
||||
|
||||
#define TCAN4X5X_WRITE_CMD (0x61 << 24)
|
||||
#define TCAN4X5X_READ_CMD (0x41 << 24)
|
||||
|
||||
#define TCAN4X5X_MODE_SEL_MASK (BIT(7) | BIT(6))
|
||||
#define TCAN4X5X_MODE_SLEEP 0x00
|
||||
#define TCAN4X5X_MODE_STANDBY BIT(6)
|
||||
#define TCAN4X5X_MODE_NORMAL BIT(7)
|
||||
|
||||
#define TCAN4X5X_SW_RESET BIT(2)
|
||||
|
||||
#define TCAN4X5X_MCAN_CONFIGURED BIT(5)
|
||||
#define TCAN4X5X_WATCHDOG_EN BIT(3)
|
||||
#define TCAN4X5X_WD_60_MS_TIMER 0
|
||||
#define TCAN4X5X_WD_600_MS_TIMER BIT(28)
|
||||
#define TCAN4X5X_WD_3_S_TIMER BIT(29)
|
||||
#define TCAN4X5X_WD_6_S_TIMER (BIT(28) | BIT(29))
|
||||
|
||||
struct tcan4x5x_priv {
|
||||
struct regmap *regmap;
|
||||
struct spi_device *spi;
|
||||
struct mutex tcan4x5x_lock; /* SPI device lock */
|
||||
|
||||
struct m_can_classdev *mcan_dev;
|
||||
|
||||
struct gpio_desc *reset_gpio;
|
||||
struct gpio_desc *interrupt_gpio;
|
||||
struct gpio_desc *device_wake_gpio;
|
||||
struct gpio_desc *device_state_gpio;
|
||||
struct regulator *power;
|
||||
|
||||
/* Register based ip */
|
||||
int mram_start;
|
||||
int reg_offset;
|
||||
};
|
||||
|
||||
static struct can_bittiming_const tcan4x5x_bittiming_const = {
|
||||
.name = DEVICE_NAME,
|
||||
.tseg1_min = 2,
|
||||
.tseg1_max = 31,
|
||||
.tseg2_min = 2,
|
||||
.tseg2_max = 16,
|
||||
.sjw_max = 16,
|
||||
.brp_min = 1,
|
||||
.brp_max = 32,
|
||||
.brp_inc = 1,
|
||||
};
|
||||
|
||||
static struct can_bittiming_const tcan4x5x_data_bittiming_const = {
|
||||
.name = DEVICE_NAME,
|
||||
.tseg1_min = 1,
|
||||
.tseg1_max = 32,
|
||||
.tseg2_min = 1,
|
||||
.tseg2_max = 16,
|
||||
.sjw_max = 16,
|
||||
.brp_min = 1,
|
||||
.brp_max = 32,
|
||||
.brp_inc = 1,
|
||||
};
|
||||
|
||||
static void tcan4x5x_check_wake(struct tcan4x5x_priv *priv)
|
||||
{
|
||||
int wake_state = 0;
|
||||
|
||||
if (priv->device_state_gpio)
|
||||
wake_state = gpiod_get_value(priv->device_state_gpio);
|
||||
|
||||
if (priv->device_wake_gpio && wake_state) {
|
||||
gpiod_set_value(priv->device_wake_gpio, 0);
|
||||
usleep_range(5, 50);
|
||||
gpiod_set_value(priv->device_wake_gpio, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static int regmap_spi_gather_write(void *context, const void *reg,
|
||||
size_t reg_len, const void *val,
|
||||
size_t val_len)
|
||||
{
|
||||
struct device *dev = context;
|
||||
struct spi_device *spi = to_spi_device(dev);
|
||||
struct spi_message m;
|
||||
u32 addr;
|
||||
struct spi_transfer t[2] = {
|
||||
{ .tx_buf = &addr, .len = reg_len, .cs_change = 0,},
|
||||
{ .tx_buf = val, .len = val_len, },
|
||||
};
|
||||
|
||||
addr = TCAN4X5X_WRITE_CMD | (*((u16 *)reg) << 8) | val_len >> 3;
|
||||
|
||||
spi_message_init(&m);
|
||||
spi_message_add_tail(&t[0], &m);
|
||||
spi_message_add_tail(&t[1], &m);
|
||||
|
||||
return spi_sync(spi, &m);
|
||||
}
|
||||
|
||||
static int tcan4x5x_regmap_write(void *context, const void *data, size_t count)
|
||||
{
|
||||
u16 *reg = (u16 *)(data);
|
||||
const u32 *val = data + 4;
|
||||
|
||||
return regmap_spi_gather_write(context, reg, 4, val, count);
|
||||
}
|
||||
|
||||
static int regmap_spi_async_write(void *context,
|
||||
const void *reg, size_t reg_len,
|
||||
const void *val, size_t val_len,
|
||||
struct regmap_async *a)
|
||||
{
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
static struct regmap_async *regmap_spi_async_alloc(void)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int tcan4x5x_regmap_read(void *context,
|
||||
const void *reg, size_t reg_size,
|
||||
void *val, size_t val_size)
|
||||
{
|
||||
struct device *dev = context;
|
||||
struct spi_device *spi = to_spi_device(dev);
|
||||
|
||||
u32 addr = TCAN4X5X_READ_CMD | (*((u16 *)reg) << 8) | val_size >> 2;
|
||||
|
||||
return spi_write_then_read(spi, &addr, reg_size, (u32 *)val, val_size);
|
||||
}
|
||||
|
||||
static struct regmap_bus tcan4x5x_bus = {
|
||||
.write = tcan4x5x_regmap_write,
|
||||
.gather_write = regmap_spi_gather_write,
|
||||
.async_write = regmap_spi_async_write,
|
||||
.async_alloc = regmap_spi_async_alloc,
|
||||
.read = tcan4x5x_regmap_read,
|
||||
.read_flag_mask = 0x00,
|
||||
.reg_format_endian_default = REGMAP_ENDIAN_NATIVE,
|
||||
.val_format_endian_default = REGMAP_ENDIAN_NATIVE,
|
||||
};
|
||||
|
||||
static u32 tcan4x5x_read_reg(struct m_can_classdev *cdev, int reg)
|
||||
{
|
||||
struct tcan4x5x_priv *priv = (struct tcan4x5x_priv *)cdev->device_data;
|
||||
u32 val;
|
||||
|
||||
tcan4x5x_check_wake(priv);
|
||||
|
||||
regmap_read(priv->regmap, priv->reg_offset + reg, &val);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static u32 tcan4x5x_read_fifo(struct m_can_classdev *cdev, int addr_offset)
|
||||
{
|
||||
struct tcan4x5x_priv *priv = (struct tcan4x5x_priv *)cdev->device_data;
|
||||
u32 val;
|
||||
|
||||
tcan4x5x_check_wake(priv);
|
||||
|
||||
regmap_read(priv->regmap, priv->mram_start + addr_offset, &val);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static int tcan4x5x_write_reg(struct m_can_classdev *cdev, int reg, int val)
|
||||
{
|
||||
struct tcan4x5x_priv *priv = (struct tcan4x5x_priv *)cdev->device_data;
|
||||
|
||||
tcan4x5x_check_wake(priv);
|
||||
|
||||
return regmap_write(priv->regmap, priv->reg_offset + reg, val);
|
||||
}
|
||||
|
||||
static int tcan4x5x_write_fifo(struct m_can_classdev *cdev,
|
||||
int addr_offset, int val)
|
||||
{
|
||||
struct tcan4x5x_priv *priv =
|
||||
(struct tcan4x5x_priv *)cdev->device_data;
|
||||
|
||||
tcan4x5x_check_wake(priv);
|
||||
|
||||
return regmap_write(priv->regmap, priv->mram_start + addr_offset, val);
|
||||
}
|
||||
|
||||
static int tcan4x5x_power_enable(struct regulator *reg, int enable)
|
||||
{
|
||||
if (IS_ERR_OR_NULL(reg))
|
||||
return 0;
|
||||
|
||||
if (enable)
|
||||
return regulator_enable(reg);
|
||||
else
|
||||
return regulator_disable(reg);
|
||||
}
|
||||
|
||||
static int tcan4x5x_write_tcan_reg(struct m_can_classdev *cdev,
|
||||
int reg, int val)
|
||||
{
|
||||
struct tcan4x5x_priv *priv =
|
||||
(struct tcan4x5x_priv *)cdev->device_data;
|
||||
|
||||
tcan4x5x_check_wake(priv);
|
||||
|
||||
return regmap_write(priv->regmap, reg, val);
|
||||
}
|
||||
|
||||
static int tcan4x5x_clear_interrupts(struct m_can_classdev *cdev)
|
||||
{
|
||||
struct tcan4x5x_priv *tcan4x5x =
|
||||
(struct tcan4x5x_priv *)cdev->device_data;
|
||||
int ret;
|
||||
|
||||
tcan4x5x_check_wake(tcan4x5x);
|
||||
|
||||
ret = tcan4x5x_write_tcan_reg(cdev, TCAN4X5X_STATUS,
|
||||
TCAN4X5X_CLEAR_ALL_INT);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = tcan4x5x_write_tcan_reg(cdev, TCAN4X5X_MCAN_INT_REG,
|
||||
TCAN4X5X_ENABLE_MCAN_INT);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = tcan4x5x_write_tcan_reg(cdev, TCAN4X5X_INT_FLAGS,
|
||||
TCAN4X5X_CLEAR_ALL_INT);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = tcan4x5x_write_tcan_reg(cdev, TCAN4X5X_ERROR_STATUS,
|
||||
TCAN4X5X_CLEAR_ALL_INT);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tcan4x5x_init(struct m_can_classdev *cdev)
|
||||
{
|
||||
struct tcan4x5x_priv *tcan4x5x =
|
||||
(struct tcan4x5x_priv *)cdev->device_data;
|
||||
int ret;
|
||||
|
||||
tcan4x5x_check_wake(tcan4x5x);
|
||||
|
||||
ret = tcan4x5x_clear_interrupts(cdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = tcan4x5x_write_tcan_reg(cdev, TCAN4X5X_INT_EN,
|
||||
TCAN4X5X_ENABLE_TCAN_INT);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_update_bits(tcan4x5x->regmap, TCAN4X5X_CONFIG,
|
||||
TCAN4X5X_MODE_SEL_MASK, TCAN4X5X_MODE_NORMAL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Zero out the MCAN buffers */
|
||||
m_can_init_ram(cdev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tcan4x5x_parse_config(struct m_can_classdev *cdev)
|
||||
{
|
||||
struct tcan4x5x_priv *tcan4x5x =
|
||||
(struct tcan4x5x_priv *)cdev->device_data;
|
||||
|
||||
tcan4x5x->interrupt_gpio = devm_gpiod_get(cdev->dev, "data-ready",
|
||||
GPIOD_IN);
|
||||
if (IS_ERR(tcan4x5x->interrupt_gpio)) {
|
||||
dev_err(cdev->dev, "data-ready gpio not defined\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
tcan4x5x->device_wake_gpio = devm_gpiod_get(cdev->dev, "device-wake",
|
||||
GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(tcan4x5x->device_wake_gpio)) {
|
||||
dev_err(cdev->dev, "device-wake gpio not defined\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
tcan4x5x->reset_gpio = devm_gpiod_get_optional(cdev->dev, "reset",
|
||||
GPIOD_OUT_LOW);
|
||||
if (IS_ERR(tcan4x5x->reset_gpio))
|
||||
tcan4x5x->reset_gpio = NULL;
|
||||
|
||||
tcan4x5x->device_state_gpio = devm_gpiod_get_optional(cdev->dev,
|
||||
"device-state",
|
||||
GPIOD_IN);
|
||||
if (IS_ERR(tcan4x5x->device_state_gpio))
|
||||
tcan4x5x->device_state_gpio = NULL;
|
||||
|
||||
cdev->net->irq = gpiod_to_irq(tcan4x5x->interrupt_gpio);
|
||||
|
||||
tcan4x5x->power = devm_regulator_get_optional(cdev->dev,
|
||||
"vsup");
|
||||
if (PTR_ERR(tcan4x5x->power) == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct regmap_config tcan4x5x_regmap = {
|
||||
.reg_bits = 32,
|
||||
.val_bits = 32,
|
||||
.cache_type = REGCACHE_NONE,
|
||||
.max_register = TCAN4X5X_MAX_REGISTER,
|
||||
};
|
||||
|
||||
static struct m_can_ops tcan4x5x_ops = {
|
||||
.init = tcan4x5x_init,
|
||||
.read_reg = tcan4x5x_read_reg,
|
||||
.write_reg = tcan4x5x_write_reg,
|
||||
.write_fifo = tcan4x5x_write_fifo,
|
||||
.read_fifo = tcan4x5x_read_fifo,
|
||||
.clear_interrupts = tcan4x5x_clear_interrupts,
|
||||
};
|
||||
|
||||
static int tcan4x5x_can_probe(struct spi_device *spi)
|
||||
{
|
||||
struct tcan4x5x_priv *priv;
|
||||
struct m_can_classdev *mcan_class;
|
||||
int freq, ret;
|
||||
|
||||
mcan_class = m_can_class_allocate_dev(&spi->dev);
|
||||
priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
mcan_class->device_data = priv;
|
||||
|
||||
m_can_class_get_clocks(mcan_class);
|
||||
if (IS_ERR(mcan_class->cclk)) {
|
||||
dev_err(&spi->dev, "no CAN clock source defined\n");
|
||||
freq = TCAN4X5X_EXT_CLK_DEF;
|
||||
} else {
|
||||
freq = clk_get_rate(mcan_class->cclk);
|
||||
}
|
||||
|
||||
/* Sanity check */
|
||||
if (freq < 20000000 || freq > TCAN4X5X_EXT_CLK_DEF)
|
||||
return -ERANGE;
|
||||
|
||||
priv->reg_offset = TCAN4X5X_MCAN_OFFSET;
|
||||
priv->mram_start = TCAN4X5X_MRAM_START;
|
||||
priv->spi = spi;
|
||||
priv->mcan_dev = mcan_class;
|
||||
|
||||
mcan_class->pm_clock_support = 0;
|
||||
mcan_class->can.clock.freq = freq;
|
||||
mcan_class->dev = &spi->dev;
|
||||
mcan_class->ops = &tcan4x5x_ops;
|
||||
mcan_class->is_peripheral = true;
|
||||
mcan_class->bit_timing = &tcan4x5x_bittiming_const;
|
||||
mcan_class->data_timing = &tcan4x5x_data_bittiming_const;
|
||||
|
||||
spi_set_drvdata(spi, priv);
|
||||
|
||||
ret = tcan4x5x_parse_config(mcan_class);
|
||||
if (ret)
|
||||
goto out_clk;
|
||||
|
||||
/* Configure the SPI bus */
|
||||
spi->bits_per_word = 32;
|
||||
ret = spi_setup(spi);
|
||||
if (ret)
|
||||
goto out_clk;
|
||||
|
||||
priv->regmap = devm_regmap_init(&spi->dev, &tcan4x5x_bus,
|
||||
&spi->dev, &tcan4x5x_regmap);
|
||||
|
||||
mutex_init(&priv->tcan4x5x_lock);
|
||||
|
||||
tcan4x5x_power_enable(priv->power, 1);
|
||||
|
||||
ret = m_can_class_register(mcan_class);
|
||||
if (ret)
|
||||
goto out_power;
|
||||
|
||||
netdev_info(mcan_class->net, "TCAN4X5X successfully initialized.\n");
|
||||
return 0;
|
||||
|
||||
out_power:
|
||||
tcan4x5x_power_enable(priv->power, 0);
|
||||
out_clk:
|
||||
if (!IS_ERR(mcan_class->cclk)) {
|
||||
clk_disable_unprepare(mcan_class->cclk);
|
||||
clk_disable_unprepare(mcan_class->hclk);
|
||||
}
|
||||
|
||||
dev_err(&spi->dev, "Probe failed, err=%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tcan4x5x_can_remove(struct spi_device *spi)
|
||||
{
|
||||
struct tcan4x5x_priv *priv = spi_get_drvdata(spi);
|
||||
|
||||
tcan4x5x_power_enable(priv->power, 0);
|
||||
|
||||
m_can_class_unregister(priv->mcan_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id tcan4x5x_of_match[] = {
|
||||
{ .compatible = "ti,tcan4x5x", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, tcan4x5x_of_match);
|
||||
|
||||
static const struct spi_device_id tcan4x5x_id_table[] = {
|
||||
{
|
||||
.name = "tcan4x5x",
|
||||
.driver_data = 0,
|
||||
},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, tcan4x5x_id_table);
|
||||
|
||||
static struct spi_driver tcan4x5x_can_driver = {
|
||||
.driver = {
|
||||
.name = DEVICE_NAME,
|
||||
.of_match_table = tcan4x5x_of_match,
|
||||
.pm = NULL,
|
||||
},
|
||||
.id_table = tcan4x5x_id_table,
|
||||
.probe = tcan4x5x_can_probe,
|
||||
.remove = tcan4x5x_can_remove,
|
||||
};
|
||||
module_spi_driver(tcan4x5x_can_driver);
|
||||
|
||||
MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
|
||||
MODULE_DESCRIPTION("Texas Instruments TCAN4x5x CAN driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -660,7 +660,7 @@ static int pciefd_can_probe(struct pciefd_board *pciefd)
|
||||
pciefd_can_writereg(priv, CANFD_CLK_SEL_80MHZ,
|
||||
PCIEFD_REG_CAN_CLK_SEL);
|
||||
|
||||
/* fallthough */
|
||||
/* fall through */
|
||||
case CANFD_CLK_SEL_80MHZ:
|
||||
priv->ucan.can.clock.freq = 80 * 1000 * 1000;
|
||||
break;
|
||||
|
@ -1,34 +1,11 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
menuconfig CAN_SJA1000
|
||||
tristate "Philips/NXP SJA1000 devices"
|
||||
depends on HAS_IOMEM
|
||||
|
||||
if CAN_SJA1000
|
||||
|
||||
config CAN_SJA1000_ISA
|
||||
tristate "ISA Bus based legacy SJA1000 driver"
|
||||
---help---
|
||||
This driver adds legacy support for SJA1000 chips connected to
|
||||
the ISA bus using I/O port, memory mapped or indirect access.
|
||||
|
||||
config CAN_SJA1000_PLATFORM
|
||||
tristate "Generic Platform Bus based SJA1000 driver"
|
||||
---help---
|
||||
This driver adds support for the SJA1000 chips connected to
|
||||
the "platform bus" (Linux abstraction for directly to the
|
||||
processor attached devices). Which can be found on various
|
||||
boards from Phytec (http://www.phytec.de) like the PCM027,
|
||||
PCM038. It also provides the OpenFirmware "platform bus" found
|
||||
on embedded systems with OpenFirmware bindings, e.g. if you
|
||||
have a PowerPC based system you may want to enable this option.
|
||||
|
||||
config CAN_EMS_PCMCIA
|
||||
tristate "EMS CPC-CARD Card"
|
||||
depends on PCMCIA
|
||||
---help---
|
||||
This driver is for the one or two channel CPC-CARD cards from
|
||||
EMS Dr. Thomas Wuensche (http://www.ems-wuensche.de).
|
||||
|
||||
config CAN_EMS_PCI
|
||||
tristate "EMS CPC-PCI, CPC-PCIe and CPC-104P Card"
|
||||
depends on PCI
|
||||
@ -37,15 +14,29 @@ config CAN_EMS_PCI
|
||||
CPC-PCIe and CPC-104P cards from EMS Dr. Thomas Wuensche
|
||||
(http://www.ems-wuensche.de).
|
||||
|
||||
config CAN_PEAK_PCMCIA
|
||||
tristate "PEAK PCAN-PC Card"
|
||||
config CAN_EMS_PCMCIA
|
||||
tristate "EMS CPC-CARD Card"
|
||||
depends on PCMCIA
|
||||
depends on HAS_IOPORT_MAP
|
||||
---help---
|
||||
This driver is for the PCAN-PC Card PCMCIA adapter (1 or 2 channels)
|
||||
from PEAK-System (http://www.peak-system.com). To compile this
|
||||
driver as a module, choose M here: the module will be called
|
||||
peak_pcmcia.
|
||||
This driver is for the one or two channel CPC-CARD cards from
|
||||
EMS Dr. Thomas Wuensche (http://www.ems-wuensche.de).
|
||||
|
||||
config CAN_F81601
|
||||
tristate "Fintek F81601 PCIE to 2 CAN Controller"
|
||||
depends on PCI
|
||||
help
|
||||
This driver adds support for Fintek F81601 PCIE to 2 CAN
|
||||
Controller. It had internal 24MHz clock source, but it can
|
||||
be changed by manufacturer. Use modinfo to get usage for
|
||||
parameters. Visit http://www.fintek.com.tw to get more
|
||||
information.
|
||||
|
||||
config CAN_KVASER_PCI
|
||||
tristate "Kvaser PCIcanx and Kvaser PCIcan PCI Cards"
|
||||
depends on PCI
|
||||
---help---
|
||||
This driver is for the PCIcanx and PCIcan cards (1, 2 or
|
||||
4 channel) from Kvaser (http://www.kvaser.com).
|
||||
|
||||
config CAN_PEAK_PCI
|
||||
tristate "PEAK PCAN-PCI/PCIe/miniPCI Cards"
|
||||
@ -66,12 +57,15 @@ config CAN_PEAK_PCIEC
|
||||
Technik. This will also automatically select I2C and I2C_ALGO
|
||||
configuration options.
|
||||
|
||||
config CAN_KVASER_PCI
|
||||
tristate "Kvaser PCIcanx and Kvaser PCIcan PCI Cards"
|
||||
depends on PCI
|
||||
config CAN_PEAK_PCMCIA
|
||||
tristate "PEAK PCAN-PC Card"
|
||||
depends on PCMCIA
|
||||
depends on HAS_IOPORT_MAP
|
||||
---help---
|
||||
This driver is for the PCIcanx and PCIcan cards (1, 2 or
|
||||
4 channel) from Kvaser (http://www.kvaser.com).
|
||||
This driver is for the PCAN-PC Card PCMCIA adapter (1 or 2 channels)
|
||||
from PEAK-System (http://www.peak-system.com). To compile this
|
||||
driver as a module, choose M here: the module will be called
|
||||
peak_pcmcia.
|
||||
|
||||
config CAN_PLX_PCI
|
||||
tristate "PLX90xx PCI-bridge based Cards"
|
||||
@ -91,6 +85,23 @@ config CAN_PLX_PCI
|
||||
- Connect Tech Inc. CANpro/104-Plus Opto (CRG001) card (http://www.connecttech.com)
|
||||
- ASEM CAN raw - 2 isolated CAN channels (www.asem.it)
|
||||
|
||||
config CAN_SJA1000_ISA
|
||||
tristate "ISA Bus based legacy SJA1000 driver"
|
||||
---help---
|
||||
This driver adds legacy support for SJA1000 chips connected to
|
||||
the ISA bus using I/O port, memory mapped or indirect access.
|
||||
|
||||
config CAN_SJA1000_PLATFORM
|
||||
tristate "Generic Platform Bus based SJA1000 driver"
|
||||
---help---
|
||||
This driver adds support for the SJA1000 chips connected to
|
||||
the "platform bus" (Linux abstraction for directly to the
|
||||
processor attached devices). Which can be found on various
|
||||
boards from Phytec (http://www.phytec.de) like the PCM027,
|
||||
PCM038. It also provides the OpenFirmware "platform bus" found
|
||||
on embedded systems with OpenFirmware bindings, e.g. if you
|
||||
have a PowerPC based system you may want to enable this option.
|
||||
|
||||
config CAN_TSCAN1
|
||||
tristate "TS-CAN1 PC104 boards"
|
||||
depends on ISA
|
||||
|
@ -3,13 +3,14 @@
|
||||
# Makefile for the SJA1000 CAN controller drivers.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_CAN_EMS_PCI) += ems_pci.o
|
||||
obj-$(CONFIG_CAN_EMS_PCMCIA) += ems_pcmcia.o
|
||||
obj-$(CONFIG_CAN_F81601) += f81601.o
|
||||
obj-$(CONFIG_CAN_KVASER_PCI) += kvaser_pci.o
|
||||
obj-$(CONFIG_CAN_PEAK_PCI) += peak_pci.o
|
||||
obj-$(CONFIG_CAN_PEAK_PCMCIA) += peak_pcmcia.o
|
||||
obj-$(CONFIG_CAN_PLX_PCI) += plx_pci.o
|
||||
obj-$(CONFIG_CAN_SJA1000) += sja1000.o
|
||||
obj-$(CONFIG_CAN_SJA1000_ISA) += sja1000_isa.o
|
||||
obj-$(CONFIG_CAN_SJA1000_PLATFORM) += sja1000_platform.o
|
||||
obj-$(CONFIG_CAN_EMS_PCMCIA) += ems_pcmcia.o
|
||||
obj-$(CONFIG_CAN_EMS_PCI) += ems_pci.o
|
||||
obj-$(CONFIG_CAN_KVASER_PCI) += kvaser_pci.o
|
||||
obj-$(CONFIG_CAN_PEAK_PCMCIA) += peak_pcmcia.o
|
||||
obj-$(CONFIG_CAN_PEAK_PCI) += peak_pci.o
|
||||
obj-$(CONFIG_CAN_PLX_PCI) += plx_pci.o
|
||||
obj-$(CONFIG_CAN_TSCAN1) += tscan1.o
|
||||
|
212
drivers/net/can/sja1000/f81601.c
Normal file
212
drivers/net/can/sja1000/f81601.c
Normal file
@ -0,0 +1,212 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Fintek F81601 PCIE to 2 CAN controller driver
|
||||
*
|
||||
* Copyright (C) 2019 Peter Hong <peter_hong@fintek.com.tw>
|
||||
* Copyright (C) 2019 Linux Foundation
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/can/dev.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/version.h>
|
||||
|
||||
#include "sja1000.h"
|
||||
|
||||
#define F81601_PCI_MAX_CHAN 2
|
||||
|
||||
#define F81601_DECODE_REG 0x209
|
||||
#define F81601_IO_MODE BIT(7)
|
||||
#define F81601_MEM_MODE BIT(6)
|
||||
#define F81601_CFG_MODE BIT(5)
|
||||
#define F81601_CAN2_INTERNAL_CLK BIT(3)
|
||||
#define F81601_CAN1_INTERNAL_CLK BIT(2)
|
||||
#define F81601_CAN2_EN BIT(1)
|
||||
#define F81601_CAN1_EN BIT(0)
|
||||
|
||||
#define F81601_TRAP_REG 0x20a
|
||||
#define F81601_CAN2_HAS_EN BIT(4)
|
||||
|
||||
struct f81601_pci_card {
|
||||
void __iomem *addr;
|
||||
spinlock_t lock; /* use this spin lock only for write access */
|
||||
struct pci_dev *dev;
|
||||
struct net_device *net_dev[F81601_PCI_MAX_CHAN];
|
||||
};
|
||||
|
||||
static const struct pci_device_id f81601_pci_tbl[] = {
|
||||
{ PCI_DEVICE(0x1c29, 0x1703) },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(pci, f81601_pci_tbl);
|
||||
|
||||
static bool internal_clk = true;
|
||||
module_param(internal_clk, bool, 0444);
|
||||
MODULE_PARM_DESC(internal_clk, "Use internal clock, default true (24MHz)");
|
||||
|
||||
static unsigned int external_clk;
|
||||
module_param(external_clk, uint, 0444);
|
||||
MODULE_PARM_DESC(external_clk, "External clock when internal_clk disabled");
|
||||
|
||||
static u8 f81601_pci_read_reg(const struct sja1000_priv *priv, int port)
|
||||
{
|
||||
return readb(priv->reg_base + port);
|
||||
}
|
||||
|
||||
static void f81601_pci_write_reg(const struct sja1000_priv *priv, int port,
|
||||
u8 val)
|
||||
{
|
||||
struct f81601_pci_card *card = priv->priv;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&card->lock, flags);
|
||||
writeb(val, priv->reg_base + port);
|
||||
readb(priv->reg_base);
|
||||
spin_unlock_irqrestore(&card->lock, flags);
|
||||
}
|
||||
|
||||
static void f81601_pci_remove(struct pci_dev *pdev)
|
||||
{
|
||||
struct f81601_pci_card *card = pci_get_drvdata(pdev);
|
||||
struct net_device *dev;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(card->net_dev); i++) {
|
||||
dev = card->net_dev[i];
|
||||
if (!dev)
|
||||
continue;
|
||||
|
||||
dev_info(&pdev->dev, "%s: Removing %s\n", __func__, dev->name);
|
||||
|
||||
unregister_sja1000dev(dev);
|
||||
free_sja1000dev(dev);
|
||||
}
|
||||
}
|
||||
|
||||
/* Probe F81601 based device for the SJA1000 chips and register each
|
||||
* available CAN channel to SJA1000 Socket-CAN subsystem.
|
||||
*/
|
||||
static int f81601_pci_probe(struct pci_dev *pdev,
|
||||
const struct pci_device_id *ent)
|
||||
{
|
||||
struct sja1000_priv *priv;
|
||||
struct net_device *dev;
|
||||
struct f81601_pci_card *card;
|
||||
int err, i, count;
|
||||
u8 tmp;
|
||||
|
||||
if (pcim_enable_device(pdev) < 0) {
|
||||
dev_err(&pdev->dev, "Failed to enable PCI device\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
dev_info(&pdev->dev, "Detected card at slot #%i\n",
|
||||
PCI_SLOT(pdev->devfn));
|
||||
|
||||
card = devm_kzalloc(&pdev->dev, sizeof(*card), GFP_KERNEL);
|
||||
if (!card)
|
||||
return -ENOMEM;
|
||||
|
||||
card->dev = pdev;
|
||||
spin_lock_init(&card->lock);
|
||||
|
||||
pci_set_drvdata(pdev, card);
|
||||
|
||||
tmp = F81601_IO_MODE | F81601_MEM_MODE | F81601_CFG_MODE |
|
||||
F81601_CAN2_EN | F81601_CAN1_EN;
|
||||
|
||||
if (internal_clk) {
|
||||
tmp |= F81601_CAN2_INTERNAL_CLK | F81601_CAN1_INTERNAL_CLK;
|
||||
|
||||
dev_info(&pdev->dev,
|
||||
"F81601 running with internal clock: 24Mhz\n");
|
||||
} else {
|
||||
dev_info(&pdev->dev,
|
||||
"F81601 running with external clock: %dMhz\n",
|
||||
external_clk / 1000000);
|
||||
}
|
||||
|
||||
pci_write_config_byte(pdev, F81601_DECODE_REG, tmp);
|
||||
|
||||
card->addr = pcim_iomap(pdev, 0, pci_resource_len(pdev, 0));
|
||||
|
||||
if (!card->addr) {
|
||||
err = -ENOMEM;
|
||||
dev_err(&pdev->dev, "%s: Failed to remap BAR\n", __func__);
|
||||
goto failure_cleanup;
|
||||
}
|
||||
|
||||
/* read CAN2_HW_EN strap pin to detect how many CANBUS do we have */
|
||||
count = ARRAY_SIZE(card->net_dev);
|
||||
pci_read_config_byte(pdev, F81601_TRAP_REG, &tmp);
|
||||
if (!(tmp & F81601_CAN2_HAS_EN))
|
||||
count = 1;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
dev = alloc_sja1000dev(0);
|
||||
if (!dev) {
|
||||
err = -ENOMEM;
|
||||
goto failure_cleanup;
|
||||
}
|
||||
|
||||
priv = netdev_priv(dev);
|
||||
priv->priv = card;
|
||||
priv->irq_flags = IRQF_SHARED;
|
||||
priv->reg_base = card->addr + 0x80 * i;
|
||||
priv->read_reg = f81601_pci_read_reg;
|
||||
priv->write_reg = f81601_pci_write_reg;
|
||||
|
||||
if (internal_clk)
|
||||
priv->can.clock.freq = 24000000 / 2;
|
||||
else
|
||||
priv->can.clock.freq = external_clk / 2;
|
||||
|
||||
priv->ocr = OCR_TX0_PUSHPULL | OCR_TX1_PUSHPULL;
|
||||
priv->cdr = CDR_CBP;
|
||||
|
||||
SET_NETDEV_DEV(dev, &pdev->dev);
|
||||
dev->dev_id = i;
|
||||
dev->irq = pdev->irq;
|
||||
|
||||
/* Register SJA1000 device */
|
||||
err = register_sja1000dev(dev);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev,
|
||||
"%s: Registering device failed: %x\n", __func__,
|
||||
err);
|
||||
free_sja1000dev(dev);
|
||||
goto failure_cleanup;
|
||||
}
|
||||
|
||||
card->net_dev[i] = dev;
|
||||
dev_info(&pdev->dev, "Channel #%d, %s at 0x%p, irq %d\n", i,
|
||||
dev->name, priv->reg_base, dev->irq);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
failure_cleanup:
|
||||
dev_err(&pdev->dev, "%s: failed: %d. Cleaning Up.\n", __func__, err);
|
||||
f81601_pci_remove(pdev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct pci_driver f81601_pci_driver = {
|
||||
.name = "f81601",
|
||||
.id_table = f81601_pci_tbl,
|
||||
.probe = f81601_pci_probe,
|
||||
.remove = f81601_pci_remove,
|
||||
};
|
||||
|
||||
MODULE_DESCRIPTION("Fintek F81601 PCIE to 2 CANBUS adaptor driver");
|
||||
MODULE_AUTHOR("Peter Hong <peter_hong@fintek.com.tw>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
||||
module_pci_driver(f81601_pci_driver);
|
@ -860,7 +860,8 @@ static irqreturn_t mcp251x_can_ist(int irq, void *dev_id)
|
||||
if (new_state >= CAN_STATE_ERROR_WARNING &&
|
||||
new_state <= CAN_STATE_BUS_OFF)
|
||||
priv->can.can_stats.error_warning++;
|
||||
case CAN_STATE_ERROR_WARNING: /* fallthrough */
|
||||
/* fall through */
|
||||
case CAN_STATE_ERROR_WARNING:
|
||||
if (new_state >= CAN_STATE_ERROR_PASSIVE &&
|
||||
new_state <= CAN_STATE_BUS_OFF)
|
||||
priv->can.can_stats.error_passive++;
|
||||
|
@ -5,6 +5,7 @@
|
||||
* specs for the same is available at <http://www.ti.com>
|
||||
*
|
||||
* Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/
|
||||
* Copyright (C) 2019 Jeroen Hofstee <jhofstee@victronenergy.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
@ -34,6 +35,7 @@
|
||||
#include <linux/can/dev.h>
|
||||
#include <linux/can/error.h>
|
||||
#include <linux/can/led.h>
|
||||
#include <linux/can/rx-offload.h>
|
||||
|
||||
#define DRV_NAME "ti_hecc"
|
||||
#define HECC_MODULE_VERSION "0.7"
|
||||
@ -63,29 +65,15 @@ MODULE_VERSION(HECC_MODULE_VERSION);
|
||||
#define HECC_TX_PRIO_MASK (MAX_TX_PRIO << HECC_MB_TX_SHIFT)
|
||||
#define HECC_TX_MB_MASK (HECC_MAX_TX_MBOX - 1)
|
||||
#define HECC_TX_MASK ((HECC_MAX_TX_MBOX - 1) | HECC_TX_PRIO_MASK)
|
||||
#define HECC_TX_MBOX_MASK (~(BIT(HECC_MAX_TX_MBOX) - 1))
|
||||
#define HECC_DEF_NAPI_WEIGHT HECC_MAX_RX_MBOX
|
||||
|
||||
/*
|
||||
* Important Note: RX mailbox configuration
|
||||
* RX mailboxes are further logically split into two - main and buffer
|
||||
* mailboxes. The goal is to get all packets into main mailboxes as
|
||||
* driven by mailbox number and receive priority (higher to lower) and
|
||||
* buffer mailboxes are used to receive pkts while main mailboxes are being
|
||||
* processed. This ensures in-order packet reception.
|
||||
/* RX mailbox configuration
|
||||
*
|
||||
* Here are the recommended values for buffer mailbox. Note that RX mailboxes
|
||||
* start after TX mailboxes:
|
||||
*
|
||||
* HECC_MAX_RX_MBOX HECC_RX_BUFFER_MBOX No of buffer mailboxes
|
||||
* 28 12 8
|
||||
* 16 20 4
|
||||
* The remaining mailboxes are used for reception and are delivered
|
||||
* based on their timestamp, to avoid a hardware race when CANME is
|
||||
* changed while CAN-bus traffic is being received.
|
||||
*/
|
||||
|
||||
#define HECC_MAX_RX_MBOX (HECC_MAX_MAILBOXES - HECC_MAX_TX_MBOX)
|
||||
#define HECC_RX_BUFFER_MBOX 12 /* as per table above */
|
||||
#define HECC_RX_FIRST_MBOX (HECC_MAX_MAILBOXES - 1)
|
||||
#define HECC_RX_HIGH_MBOX_MASK (~(BIT(HECC_RX_BUFFER_MBOX) - 1))
|
||||
|
||||
/* TI HECC module registers */
|
||||
#define HECC_CANME 0x0 /* Mailbox enable */
|
||||
@ -117,6 +105,9 @@ MODULE_VERSION(HECC_MODULE_VERSION);
|
||||
#define HECC_CANTIOCE 0x68 /* SCC only:Enhanced TX I/O control */
|
||||
#define HECC_CANRIOCE 0x6C /* SCC only:Enhanced RX I/O control */
|
||||
|
||||
/* TI HECC RAM registers */
|
||||
#define HECC_CANMOTS 0x80 /* Message object time stamp */
|
||||
|
||||
/* Mailbox registers */
|
||||
#define HECC_CANMID 0x0
|
||||
#define HECC_CANMCF 0x4
|
||||
@ -193,7 +184,7 @@ static const struct can_bittiming_const ti_hecc_bittiming_const = {
|
||||
|
||||
struct ti_hecc_priv {
|
||||
struct can_priv can; /* MUST be first member/field */
|
||||
struct napi_struct napi;
|
||||
struct can_rx_offload offload;
|
||||
struct net_device *ndev;
|
||||
struct clk *clk;
|
||||
void __iomem *base;
|
||||
@ -203,7 +194,6 @@ struct ti_hecc_priv {
|
||||
spinlock_t mbx_lock; /* CANME register needs protection */
|
||||
u32 tx_head;
|
||||
u32 tx_tail;
|
||||
u32 rx_next;
|
||||
struct regulator *reg_xceiver;
|
||||
};
|
||||
|
||||
@ -227,6 +217,11 @@ static inline void hecc_write_lam(struct ti_hecc_priv *priv, u32 mbxno, u32 val)
|
||||
__raw_writel(val, priv->hecc_ram + mbxno * 4);
|
||||
}
|
||||
|
||||
static inline u32 hecc_read_stamp(struct ti_hecc_priv *priv, u32 mbxno)
|
||||
{
|
||||
return __raw_readl(priv->hecc_ram + HECC_CANMOTS + mbxno * 4);
|
||||
}
|
||||
|
||||
static inline void hecc_write_mbx(struct ti_hecc_priv *priv, u32 mbxno,
|
||||
u32 reg, u32 val)
|
||||
{
|
||||
@ -375,7 +370,6 @@ static void ti_hecc_start(struct net_device *ndev)
|
||||
ti_hecc_reset(ndev);
|
||||
|
||||
priv->tx_head = priv->tx_tail = HECC_TX_MASK;
|
||||
priv->rx_next = HECC_RX_FIRST_MBOX;
|
||||
|
||||
/* Enable local and global acceptance mask registers */
|
||||
hecc_write(priv, HECC_CANGAM, HECC_SET_REG);
|
||||
@ -526,21 +520,17 @@ static netdev_tx_t ti_hecc_xmit(struct sk_buff *skb, struct net_device *ndev)
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
static int ti_hecc_rx_pkt(struct ti_hecc_priv *priv, int mbxno)
|
||||
static inline struct ti_hecc_priv *rx_offload_to_priv(struct can_rx_offload *offload)
|
||||
{
|
||||
struct net_device_stats *stats = &priv->ndev->stats;
|
||||
struct can_frame *cf;
|
||||
struct sk_buff *skb;
|
||||
u32 data, mbx_mask;
|
||||
unsigned long flags;
|
||||
return container_of(offload, struct ti_hecc_priv, offload);
|
||||
}
|
||||
|
||||
skb = alloc_can_skb(priv->ndev, &cf);
|
||||
if (!skb) {
|
||||
if (printk_ratelimit())
|
||||
netdev_err(priv->ndev,
|
||||
"ti_hecc_rx_pkt: alloc_can_skb() failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
static unsigned int ti_hecc_mailbox_read(struct can_rx_offload *offload,
|
||||
struct can_frame *cf,
|
||||
u32 *timestamp, unsigned int mbxno)
|
||||
{
|
||||
struct ti_hecc_priv *priv = rx_offload_to_priv(offload);
|
||||
u32 data, mbx_mask;
|
||||
|
||||
mbx_mask = BIT(mbxno);
|
||||
data = hecc_read_mbx(priv, mbxno, HECC_CANMID);
|
||||
@ -558,100 +548,19 @@ static int ti_hecc_rx_pkt(struct ti_hecc_priv *priv, int mbxno)
|
||||
data = hecc_read_mbx(priv, mbxno, HECC_CANMDH);
|
||||
*(__be32 *)(cf->data + 4) = cpu_to_be32(data);
|
||||
}
|
||||
spin_lock_irqsave(&priv->mbx_lock, flags);
|
||||
hecc_clear_bit(priv, HECC_CANME, mbx_mask);
|
||||
hecc_write(priv, HECC_CANRMP, mbx_mask);
|
||||
/* enable mailbox only if it is part of rx buffer mailboxes */
|
||||
if (priv->rx_next < HECC_RX_BUFFER_MBOX)
|
||||
hecc_set_bit(priv, HECC_CANME, mbx_mask);
|
||||
spin_unlock_irqrestore(&priv->mbx_lock, flags);
|
||||
|
||||
stats->rx_bytes += cf->can_dlc;
|
||||
can_led_event(priv->ndev, CAN_LED_EVENT_RX);
|
||||
netif_receive_skb(skb);
|
||||
stats->rx_packets++;
|
||||
*timestamp = hecc_read_stamp(priv, mbxno);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* ti_hecc_rx_poll - HECC receive pkts
|
||||
*
|
||||
* The receive mailboxes start from highest numbered mailbox till last xmit
|
||||
* mailbox. On CAN frame reception the hardware places the data into highest
|
||||
* numbered mailbox that matches the CAN ID filter. Since all receive mailboxes
|
||||
* have same filtering (ALL CAN frames) packets will arrive in the highest
|
||||
* available RX mailbox and we need to ensure in-order packet reception.
|
||||
*
|
||||
* To ensure the packets are received in the right order we logically divide
|
||||
* the RX mailboxes into main and buffer mailboxes. Packets are received as per
|
||||
* mailbox priotity (higher to lower) in the main bank and once it is full we
|
||||
* disable further reception into main mailboxes. While the main mailboxes are
|
||||
* processed in NAPI, further packets are received in buffer mailboxes.
|
||||
*
|
||||
* We maintain a RX next mailbox counter to process packets and once all main
|
||||
* mailboxe packets are passed to the upper stack we enable all of them but
|
||||
* continue to process packets received in buffer mailboxes. With each packet
|
||||
* received from buffer mailbox we enable it immediately so as to handle the
|
||||
* overflow from higher mailboxes.
|
||||
*/
|
||||
static int ti_hecc_rx_poll(struct napi_struct *napi, int quota)
|
||||
{
|
||||
struct net_device *ndev = napi->dev;
|
||||
struct ti_hecc_priv *priv = netdev_priv(ndev);
|
||||
u32 num_pkts = 0;
|
||||
u32 mbx_mask;
|
||||
unsigned long pending_pkts, flags;
|
||||
|
||||
if (!netif_running(ndev))
|
||||
return 0;
|
||||
|
||||
while ((pending_pkts = hecc_read(priv, HECC_CANRMP)) &&
|
||||
num_pkts < quota) {
|
||||
mbx_mask = BIT(priv->rx_next); /* next rx mailbox to process */
|
||||
if (mbx_mask & pending_pkts) {
|
||||
if (ti_hecc_rx_pkt(priv, priv->rx_next) < 0)
|
||||
return num_pkts;
|
||||
++num_pkts;
|
||||
} else if (priv->rx_next > HECC_RX_BUFFER_MBOX) {
|
||||
break; /* pkt not received yet */
|
||||
}
|
||||
--priv->rx_next;
|
||||
if (priv->rx_next == HECC_RX_BUFFER_MBOX) {
|
||||
/* enable high bank mailboxes */
|
||||
spin_lock_irqsave(&priv->mbx_lock, flags);
|
||||
mbx_mask = hecc_read(priv, HECC_CANME);
|
||||
mbx_mask |= HECC_RX_HIGH_MBOX_MASK;
|
||||
hecc_write(priv, HECC_CANME, mbx_mask);
|
||||
spin_unlock_irqrestore(&priv->mbx_lock, flags);
|
||||
} else if (priv->rx_next == HECC_MAX_TX_MBOX - 1) {
|
||||
priv->rx_next = HECC_RX_FIRST_MBOX;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Enable packet interrupt if all pkts are handled */
|
||||
if (hecc_read(priv, HECC_CANRMP) == 0) {
|
||||
napi_complete(napi);
|
||||
/* Re-enable RX mailbox interrupts */
|
||||
mbx_mask = hecc_read(priv, HECC_CANMIM);
|
||||
mbx_mask |= HECC_TX_MBOX_MASK;
|
||||
hecc_write(priv, HECC_CANMIM, mbx_mask);
|
||||
} else {
|
||||
/* repoll is done only if whole budget is used */
|
||||
num_pkts = quota;
|
||||
}
|
||||
|
||||
return num_pkts;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ti_hecc_error(struct net_device *ndev, int int_status,
|
||||
int err_status)
|
||||
{
|
||||
struct ti_hecc_priv *priv = netdev_priv(ndev);
|
||||
struct net_device_stats *stats = &ndev->stats;
|
||||
struct can_frame *cf;
|
||||
struct sk_buff *skb;
|
||||
u32 timestamp;
|
||||
|
||||
/* propagate the error condition to the can stack */
|
||||
skb = alloc_can_err_skb(ndev, &cf);
|
||||
@ -732,9 +641,8 @@ static int ti_hecc_error(struct net_device *ndev, int int_status,
|
||||
}
|
||||
}
|
||||
|
||||
stats->rx_packets++;
|
||||
stats->rx_bytes += cf->can_dlc;
|
||||
netif_rx(skb);
|
||||
timestamp = hecc_read(priv, HECC_CANLNT);
|
||||
can_rx_offload_queue_sorted(&priv->offload, skb, timestamp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -744,8 +652,8 @@ static irqreturn_t ti_hecc_interrupt(int irq, void *dev_id)
|
||||
struct net_device *ndev = (struct net_device *)dev_id;
|
||||
struct ti_hecc_priv *priv = netdev_priv(ndev);
|
||||
struct net_device_stats *stats = &ndev->stats;
|
||||
u32 mbxno, mbx_mask, int_status, err_status;
|
||||
unsigned long ack, flags;
|
||||
u32 mbxno, mbx_mask, int_status, err_status, stamp;
|
||||
unsigned long flags, rx_pending;
|
||||
|
||||
int_status = hecc_read(priv,
|
||||
(priv->use_hecc1int) ? HECC_CANGIF1 : HECC_CANGIF0);
|
||||
@ -769,11 +677,11 @@ static irqreturn_t ti_hecc_interrupt(int irq, void *dev_id)
|
||||
spin_lock_irqsave(&priv->mbx_lock, flags);
|
||||
hecc_clear_bit(priv, HECC_CANME, mbx_mask);
|
||||
spin_unlock_irqrestore(&priv->mbx_lock, flags);
|
||||
stats->tx_bytes += hecc_read_mbx(priv, mbxno,
|
||||
HECC_CANMCF) & 0xF;
|
||||
stamp = hecc_read_stamp(priv, mbxno);
|
||||
stats->tx_bytes += can_rx_offload_get_echo_skb(&priv->offload,
|
||||
mbxno, stamp);
|
||||
stats->tx_packets++;
|
||||
can_led_event(ndev, CAN_LED_EVENT_TX);
|
||||
can_get_echo_skb(ndev, mbxno);
|
||||
--priv->tx_tail;
|
||||
}
|
||||
|
||||
@ -784,12 +692,11 @@ static irqreturn_t ti_hecc_interrupt(int irq, void *dev_id)
|
||||
((priv->tx_head & HECC_TX_MASK) == HECC_TX_MASK)))
|
||||
netif_wake_queue(ndev);
|
||||
|
||||
/* Disable RX mailbox interrupts and let NAPI reenable them */
|
||||
if (hecc_read(priv, HECC_CANRMP)) {
|
||||
ack = hecc_read(priv, HECC_CANMIM);
|
||||
ack &= BIT(HECC_MAX_TX_MBOX) - 1;
|
||||
hecc_write(priv, HECC_CANMIM, ack);
|
||||
napi_schedule(&priv->napi);
|
||||
/* offload RX mailboxes and let NAPI deliver them */
|
||||
while ((rx_pending = hecc_read(priv, HECC_CANRMP))) {
|
||||
can_rx_offload_irq_offload_timestamp(&priv->offload,
|
||||
rx_pending);
|
||||
hecc_write(priv, HECC_CANRMP, rx_pending);
|
||||
}
|
||||
}
|
||||
|
||||
@ -831,7 +738,7 @@ static int ti_hecc_open(struct net_device *ndev)
|
||||
can_led_event(ndev, CAN_LED_EVENT_OPEN);
|
||||
|
||||
ti_hecc_start(ndev);
|
||||
napi_enable(&priv->napi);
|
||||
can_rx_offload_enable(&priv->offload);
|
||||
netif_start_queue(ndev);
|
||||
|
||||
return 0;
|
||||
@ -842,7 +749,7 @@ static int ti_hecc_close(struct net_device *ndev)
|
||||
struct ti_hecc_priv *priv = netdev_priv(ndev);
|
||||
|
||||
netif_stop_queue(ndev);
|
||||
napi_disable(&priv->napi);
|
||||
can_rx_offload_disable(&priv->offload);
|
||||
ti_hecc_stop(ndev);
|
||||
free_irq(ndev->irq, ndev);
|
||||
close_candev(ndev);
|
||||
@ -962,8 +869,6 @@ static int ti_hecc_probe(struct platform_device *pdev)
|
||||
goto probe_exit_candev;
|
||||
}
|
||||
priv->can.clock.freq = clk_get_rate(priv->clk);
|
||||
netif_napi_add(ndev, &priv->napi, ti_hecc_rx_poll,
|
||||
HECC_DEF_NAPI_WEIGHT);
|
||||
|
||||
err = clk_prepare_enable(priv->clk);
|
||||
if (err) {
|
||||
@ -971,10 +876,19 @@ static int ti_hecc_probe(struct platform_device *pdev)
|
||||
goto probe_exit_clk;
|
||||
}
|
||||
|
||||
priv->offload.mailbox_read = ti_hecc_mailbox_read;
|
||||
priv->offload.mb_first = HECC_RX_FIRST_MBOX;
|
||||
priv->offload.mb_last = HECC_MAX_TX_MBOX;
|
||||
err = can_rx_offload_add_timestamp(ndev, &priv->offload);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "can_rx_offload_add_timestamp() failed\n");
|
||||
goto probe_exit_clk;
|
||||
}
|
||||
|
||||
err = register_candev(ndev);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "register_candev() failed\n");
|
||||
goto probe_exit_clk;
|
||||
goto probe_exit_offload;
|
||||
}
|
||||
|
||||
devm_can_led_init(ndev);
|
||||
@ -984,6 +898,8 @@ static int ti_hecc_probe(struct platform_device *pdev)
|
||||
|
||||
return 0;
|
||||
|
||||
probe_exit_offload:
|
||||
can_rx_offload_del(&priv->offload);
|
||||
probe_exit_clk:
|
||||
clk_put(priv->clk);
|
||||
probe_exit_candev:
|
||||
@ -1000,6 +916,7 @@ static int ti_hecc_remove(struct platform_device *pdev)
|
||||
unregister_candev(ndev);
|
||||
clk_disable_unprepare(priv->clk);
|
||||
clk_put(priv->clk);
|
||||
can_rx_offload_del(&priv->offload);
|
||||
free_candev(ndev);
|
||||
|
||||
return 0;
|
||||
|
@ -643,8 +643,7 @@ static int kvaser_usb_init_one(struct kvaser_usb *dev,
|
||||
return err;
|
||||
}
|
||||
|
||||
netdev = alloc_candev(sizeof(*priv) +
|
||||
dev->max_tx_urbs * sizeof(*priv->tx_contexts),
|
||||
netdev = alloc_candev(struct_size(priv, tx_contexts, dev->max_tx_urbs),
|
||||
dev->max_tx_urbs);
|
||||
if (!netdev) {
|
||||
dev_err(&dev->intf->dev, "Cannot alloc candev\n");
|
||||
|
@ -415,7 +415,7 @@ static int pcan_usb_decode_error(struct pcan_usb_msg_context *mc, u8 n,
|
||||
new_state = CAN_STATE_ERROR_WARNING;
|
||||
break;
|
||||
}
|
||||
/* else: fall through */
|
||||
/* fall through */
|
||||
|
||||
case CAN_STATE_ERROR_WARNING:
|
||||
if (n & PCAN_USB_ERROR_BUS_HEAVY) {
|
||||
|
@ -50,6 +50,10 @@ enum xcan_reg {
|
||||
XCAN_AFR_OFFSET = 0x60, /* Acceptance Filter */
|
||||
|
||||
/* only on CAN FD cores */
|
||||
XCAN_F_BRPR_OFFSET = 0x088, /* Data Phase Baud Rate
|
||||
* Prescalar
|
||||
*/
|
||||
XCAN_F_BTR_OFFSET = 0x08C, /* Data Phase Bit Timing */
|
||||
XCAN_TRR_OFFSET = 0x0090, /* TX Buffer Ready Request */
|
||||
XCAN_AFR_EXT_OFFSET = 0x00E0, /* Acceptance Filter */
|
||||
XCAN_FSR_OFFSET = 0x00E8, /* RX FIFO Status */
|
||||
@ -62,6 +66,8 @@ enum xcan_reg {
|
||||
#define XCAN_FRAME_DLC_OFFSET(frame_base) ((frame_base) + 0x04)
|
||||
#define XCAN_FRAME_DW1_OFFSET(frame_base) ((frame_base) + 0x08)
|
||||
#define XCAN_FRAME_DW2_OFFSET(frame_base) ((frame_base) + 0x0C)
|
||||
#define XCANFD_FRAME_DW_OFFSET(frame_base, n) (((frame_base) + 0x08) + \
|
||||
((n) * XCAN_CANFD_FRAME_SIZE))
|
||||
|
||||
#define XCAN_CANFD_FRAME_SIZE 0x48
|
||||
#define XCAN_TXMSG_FRAME_OFFSET(n) (XCAN_TXMSG_BASE_OFFSET + \
|
||||
@ -120,6 +126,8 @@ enum xcan_reg {
|
||||
#define XCAN_FSR_FL_MASK 0x00003F00 /* RX Fill Level */
|
||||
#define XCAN_FSR_IRI_MASK 0x00000080 /* RX Increment Read Index */
|
||||
#define XCAN_FSR_RI_MASK 0x0000001F /* RX Read Index */
|
||||
#define XCAN_DLCR_EDL_MASK 0x08000000 /* EDL Mask in DLC */
|
||||
#define XCAN_DLCR_BRS_MASK 0x04000000 /* BRS Mask in DLC */
|
||||
|
||||
/* CAN register bit shift - XCAN_<REG>_<BIT>_SHIFT */
|
||||
#define XCAN_BTR_SJW_SHIFT 7 /* Synchronous jump width */
|
||||
@ -133,6 +141,7 @@ enum xcan_reg {
|
||||
|
||||
/* CAN frame length constants */
|
||||
#define XCAN_FRAME_MAX_DATA_LEN 8
|
||||
#define XCANFD_DW_BYTES 4
|
||||
#define XCAN_TIMEOUT (1 * HZ)
|
||||
|
||||
/* TX-FIFO-empty interrupt available */
|
||||
@ -149,7 +158,15 @@ enum xcan_reg {
|
||||
#define XCAN_FLAG_RX_FIFO_MULTI 0x0010
|
||||
#define XCAN_FLAG_CANFD_2 0x0020
|
||||
|
||||
enum xcan_ip_type {
|
||||
XAXI_CAN = 0,
|
||||
XZYNQ_CANPS,
|
||||
XAXI_CANFD,
|
||||
XAXI_CANFD_2_0,
|
||||
};
|
||||
|
||||
struct xcan_devtype_data {
|
||||
enum xcan_ip_type cantype;
|
||||
unsigned int flags;
|
||||
const struct can_bittiming_const *bittiming_const;
|
||||
const char *bus_clk_name;
|
||||
@ -183,7 +200,7 @@ struct xcan_priv {
|
||||
struct napi_struct napi;
|
||||
u32 (*read_reg)(const struct xcan_priv *priv, enum xcan_reg reg);
|
||||
void (*write_reg)(const struct xcan_priv *priv, enum xcan_reg reg,
|
||||
u32 val);
|
||||
u32 val);
|
||||
struct device *dev;
|
||||
void __iomem *reg_base;
|
||||
unsigned long irq_flags;
|
||||
@ -205,6 +222,7 @@ static const struct can_bittiming_const xcan_bittiming_const = {
|
||||
.brp_inc = 1,
|
||||
};
|
||||
|
||||
/* AXI CANFD Arbitration Bittiming constants as per AXI CANFD 1.0 spec */
|
||||
static const struct can_bittiming_const xcan_bittiming_const_canfd = {
|
||||
.name = DRIVER_NAME,
|
||||
.tseg1_min = 1,
|
||||
@ -217,6 +235,20 @@ static const struct can_bittiming_const xcan_bittiming_const_canfd = {
|
||||
.brp_inc = 1,
|
||||
};
|
||||
|
||||
/* AXI CANFD Data Bittiming constants as per AXI CANFD 1.0 specs */
|
||||
static struct can_bittiming_const xcan_data_bittiming_const_canfd = {
|
||||
.name = DRIVER_NAME,
|
||||
.tseg1_min = 1,
|
||||
.tseg1_max = 16,
|
||||
.tseg2_min = 1,
|
||||
.tseg2_max = 8,
|
||||
.sjw_max = 8,
|
||||
.brp_min = 1,
|
||||
.brp_max = 256,
|
||||
.brp_inc = 1,
|
||||
};
|
||||
|
||||
/* AXI CANFD 2.0 Arbitration Bittiming constants as per AXI CANFD 2.0 spec */
|
||||
static const struct can_bittiming_const xcan_bittiming_const_canfd2 = {
|
||||
.name = DRIVER_NAME,
|
||||
.tseg1_min = 1,
|
||||
@ -229,6 +261,19 @@ static const struct can_bittiming_const xcan_bittiming_const_canfd2 = {
|
||||
.brp_inc = 1,
|
||||
};
|
||||
|
||||
/* AXI CANFD 2.0 Data Bittiming constants as per AXI CANFD 2.0 spec */
|
||||
static struct can_bittiming_const xcan_data_bittiming_const_canfd2 = {
|
||||
.name = DRIVER_NAME,
|
||||
.tseg1_min = 1,
|
||||
.tseg1_max = 32,
|
||||
.tseg2_min = 1,
|
||||
.tseg2_max = 16,
|
||||
.sjw_max = 16,
|
||||
.brp_min = 1,
|
||||
.brp_max = 256,
|
||||
.brp_inc = 1,
|
||||
};
|
||||
|
||||
/**
|
||||
* xcan_write_reg_le - Write a value to the device register little endian
|
||||
* @priv: Driver private data structure
|
||||
@ -238,7 +283,7 @@ static const struct can_bittiming_const xcan_bittiming_const_canfd2 = {
|
||||
* Write data to the paricular CAN register
|
||||
*/
|
||||
static void xcan_write_reg_le(const struct xcan_priv *priv, enum xcan_reg reg,
|
||||
u32 val)
|
||||
u32 val)
|
||||
{
|
||||
iowrite32(val, priv->reg_base + reg);
|
||||
}
|
||||
@ -265,7 +310,7 @@ static u32 xcan_read_reg_le(const struct xcan_priv *priv, enum xcan_reg reg)
|
||||
* Write data to the paricular CAN register
|
||||
*/
|
||||
static void xcan_write_reg_be(const struct xcan_priv *priv, enum xcan_reg reg,
|
||||
u32 val)
|
||||
u32 val)
|
||||
{
|
||||
iowrite32be(val, priv->reg_base + reg);
|
||||
}
|
||||
@ -343,6 +388,7 @@ static int xcan_set_bittiming(struct net_device *ndev)
|
||||
{
|
||||
struct xcan_priv *priv = netdev_priv(ndev);
|
||||
struct can_bittiming *bt = &priv->can.bittiming;
|
||||
struct can_bittiming *dbt = &priv->can.data_bittiming;
|
||||
u32 btr0, btr1;
|
||||
u32 is_config_mode;
|
||||
|
||||
@ -372,9 +418,27 @@ static int xcan_set_bittiming(struct net_device *ndev)
|
||||
priv->write_reg(priv, XCAN_BRPR_OFFSET, btr0);
|
||||
priv->write_reg(priv, XCAN_BTR_OFFSET, btr1);
|
||||
|
||||
if (priv->devtype.cantype == XAXI_CANFD ||
|
||||
priv->devtype.cantype == XAXI_CANFD_2_0) {
|
||||
/* Setting Baud Rate prescalar value in F_BRPR Register */
|
||||
btr0 = dbt->brp - 1;
|
||||
|
||||
/* Setting Time Segment 1 in BTR Register */
|
||||
btr1 = dbt->prop_seg + bt->phase_seg1 - 1;
|
||||
|
||||
/* Setting Time Segment 2 in BTR Register */
|
||||
btr1 |= (dbt->phase_seg2 - 1) << priv->devtype.btr_ts2_shift;
|
||||
|
||||
/* Setting Synchronous jump width in BTR Register */
|
||||
btr1 |= (dbt->sjw - 1) << priv->devtype.btr_sjw_shift;
|
||||
|
||||
priv->write_reg(priv, XCAN_F_BRPR_OFFSET, btr0);
|
||||
priv->write_reg(priv, XCAN_F_BTR_OFFSET, btr1);
|
||||
}
|
||||
|
||||
netdev_dbg(ndev, "BRPR=0x%08x, BTR=0x%08x\n",
|
||||
priv->read_reg(priv, XCAN_BRPR_OFFSET),
|
||||
priv->read_reg(priv, XCAN_BTR_OFFSET));
|
||||
priv->read_reg(priv, XCAN_BRPR_OFFSET),
|
||||
priv->read_reg(priv, XCAN_BTR_OFFSET));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -439,12 +503,12 @@ static int xcan_chip_start(struct net_device *ndev)
|
||||
while (!(priv->read_reg(priv, XCAN_SR_OFFSET) & reg_sr_mask)) {
|
||||
if (time_after(jiffies, timeout)) {
|
||||
netdev_warn(ndev,
|
||||
"timed out for correct mode\n");
|
||||
"timed out for correct mode\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
}
|
||||
netdev_dbg(ndev, "status:#x%08x\n",
|
||||
priv->read_reg(priv, XCAN_SR_OFFSET));
|
||||
priv->read_reg(priv, XCAN_SR_OFFSET));
|
||||
|
||||
priv->can.state = CAN_STATE_ERROR_ACTIVE;
|
||||
return 0;
|
||||
@ -483,6 +547,7 @@ static int xcan_do_set_mode(struct net_device *ndev, enum can_mode mode)
|
||||
|
||||
/**
|
||||
* xcan_write_frame - Write a frame to HW
|
||||
* @priv: Driver private data structure
|
||||
* @skb: sk_buff pointer that contains data to be Txed
|
||||
* @frame_offset: Register offset to write the frame to
|
||||
*/
|
||||
@ -490,7 +555,8 @@ static void xcan_write_frame(struct xcan_priv *priv, struct sk_buff *skb,
|
||||
int frame_offset)
|
||||
{
|
||||
u32 id, dlc, data[2] = {0, 0};
|
||||
struct can_frame *cf = (struct can_frame *)skb->data;
|
||||
struct canfd_frame *cf = (struct canfd_frame *)skb->data;
|
||||
u32 ramoff, dwindex = 0, i;
|
||||
|
||||
/* Watch carefully on the bit sequence */
|
||||
if (cf->can_id & CAN_EFF_FLAG) {
|
||||
@ -498,7 +564,7 @@ static void xcan_write_frame(struct xcan_priv *priv, struct sk_buff *skb,
|
||||
id = ((cf->can_id & CAN_EFF_MASK) << XCAN_IDR_ID2_SHIFT) &
|
||||
XCAN_IDR_ID2_MASK;
|
||||
id |= (((cf->can_id & CAN_EFF_MASK) >>
|
||||
(CAN_EFF_ID_BITS-CAN_SFF_ID_BITS)) <<
|
||||
(CAN_EFF_ID_BITS - CAN_SFF_ID_BITS)) <<
|
||||
XCAN_IDR_ID1_SHIFT) & XCAN_IDR_ID1_MASK;
|
||||
|
||||
/* The substibute remote TX request bit should be "1"
|
||||
@ -519,31 +585,51 @@ static void xcan_write_frame(struct xcan_priv *priv, struct sk_buff *skb,
|
||||
id |= XCAN_IDR_SRR_MASK;
|
||||
}
|
||||
|
||||
dlc = cf->can_dlc << XCAN_DLCR_DLC_SHIFT;
|
||||
|
||||
if (cf->can_dlc > 0)
|
||||
data[0] = be32_to_cpup((__be32 *)(cf->data + 0));
|
||||
if (cf->can_dlc > 4)
|
||||
data[1] = be32_to_cpup((__be32 *)(cf->data + 4));
|
||||
dlc = can_len2dlc(cf->len) << XCAN_DLCR_DLC_SHIFT;
|
||||
if (can_is_canfd_skb(skb)) {
|
||||
if (cf->flags & CANFD_BRS)
|
||||
dlc |= XCAN_DLCR_BRS_MASK;
|
||||
dlc |= XCAN_DLCR_EDL_MASK;
|
||||
}
|
||||
|
||||
priv->write_reg(priv, XCAN_FRAME_ID_OFFSET(frame_offset), id);
|
||||
/* If the CAN frame is RTR frame this write triggers transmission
|
||||
* (not on CAN FD)
|
||||
*/
|
||||
priv->write_reg(priv, XCAN_FRAME_DLC_OFFSET(frame_offset), dlc);
|
||||
if (!(cf->can_id & CAN_RTR_FLAG)) {
|
||||
priv->write_reg(priv, XCAN_FRAME_DW1_OFFSET(frame_offset),
|
||||
data[0]);
|
||||
/* If the CAN frame is Standard/Extended frame this
|
||||
* write triggers transmission (not on CAN FD)
|
||||
*/
|
||||
priv->write_reg(priv, XCAN_FRAME_DW2_OFFSET(frame_offset),
|
||||
data[1]);
|
||||
if (priv->devtype.cantype == XAXI_CANFD ||
|
||||
priv->devtype.cantype == XAXI_CANFD_2_0) {
|
||||
for (i = 0; i < cf->len; i += 4) {
|
||||
ramoff = XCANFD_FRAME_DW_OFFSET(frame_offset, dwindex) +
|
||||
(dwindex * XCANFD_DW_BYTES);
|
||||
priv->write_reg(priv, ramoff,
|
||||
be32_to_cpup((__be32 *)(cf->data + i)));
|
||||
dwindex++;
|
||||
}
|
||||
} else {
|
||||
if (cf->len > 0)
|
||||
data[0] = be32_to_cpup((__be32 *)(cf->data + 0));
|
||||
if (cf->len > 4)
|
||||
data[1] = be32_to_cpup((__be32 *)(cf->data + 4));
|
||||
|
||||
if (!(cf->can_id & CAN_RTR_FLAG)) {
|
||||
priv->write_reg(priv,
|
||||
XCAN_FRAME_DW1_OFFSET(frame_offset),
|
||||
data[0]);
|
||||
/* If the CAN frame is Standard/Extended frame this
|
||||
* write triggers transmission (not on CAN FD)
|
||||
*/
|
||||
priv->write_reg(priv,
|
||||
XCAN_FRAME_DW2_OFFSET(frame_offset),
|
||||
data[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* xcan_start_xmit_fifo - Starts the transmission (FIFO mode)
|
||||
* @skb: sk_buff pointer that contains data to be Txed
|
||||
* @ndev: Pointer to net_device structure
|
||||
*
|
||||
* Return: 0 on success, -ENOSPC if FIFO is full.
|
||||
*/
|
||||
@ -580,6 +666,8 @@ static int xcan_start_xmit_fifo(struct sk_buff *skb, struct net_device *ndev)
|
||||
|
||||
/**
|
||||
* xcan_start_xmit_mailbox - Starts the transmission (mailbox mode)
|
||||
* @skb: sk_buff pointer that contains data to be Txed
|
||||
* @ndev: Pointer to net_device structure
|
||||
*
|
||||
* Return: 0 on success, -ENOSPC if there is no space
|
||||
*/
|
||||
@ -711,6 +799,113 @@ static int xcan_rx(struct net_device *ndev, int frame_base)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* xcanfd_rx - Is called from CAN isr to complete the received
|
||||
* frame processing
|
||||
* @ndev: Pointer to net_device structure
|
||||
* @frame_base: Register offset to the frame to be read
|
||||
*
|
||||
* This function is invoked from the CAN isr(poll) to process the Rx frames. It
|
||||
* does minimal processing and invokes "netif_receive_skb" to complete further
|
||||
* processing.
|
||||
* Return: 1 on success and 0 on failure.
|
||||
*/
|
||||
static int xcanfd_rx(struct net_device *ndev, int frame_base)
|
||||
{
|
||||
struct xcan_priv *priv = netdev_priv(ndev);
|
||||
struct net_device_stats *stats = &ndev->stats;
|
||||
struct canfd_frame *cf;
|
||||
struct sk_buff *skb;
|
||||
u32 id_xcan, dlc, data[2] = {0, 0}, dwindex = 0, i, fsr, readindex;
|
||||
|
||||
fsr = priv->read_reg(priv, XCAN_FSR_OFFSET);
|
||||
if (fsr & XCAN_FSR_FL_MASK) {
|
||||
readindex = fsr & XCAN_FSR_RI_MASK;
|
||||
id_xcan = priv->read_reg(priv,
|
||||
XCAN_FRAME_ID_OFFSET(frame_base));
|
||||
dlc = priv->read_reg(priv, XCAN_FRAME_DLC_OFFSET(frame_base));
|
||||
if (dlc & XCAN_DLCR_EDL_MASK)
|
||||
skb = alloc_canfd_skb(ndev, &cf);
|
||||
else
|
||||
skb = alloc_can_skb(ndev, (struct can_frame **)&cf);
|
||||
|
||||
if (unlikely(!skb)) {
|
||||
stats->rx_dropped++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Change Xilinx CANFD data length format to socketCAN data
|
||||
* format
|
||||
*/
|
||||
if (dlc & XCAN_DLCR_EDL_MASK)
|
||||
cf->len = can_dlc2len((dlc & XCAN_DLCR_DLC_MASK) >>
|
||||
XCAN_DLCR_DLC_SHIFT);
|
||||
else
|
||||
cf->len = get_can_dlc((dlc & XCAN_DLCR_DLC_MASK) >>
|
||||
XCAN_DLCR_DLC_SHIFT);
|
||||
|
||||
/* Change Xilinx CAN ID format to socketCAN ID format */
|
||||
if (id_xcan & XCAN_IDR_IDE_MASK) {
|
||||
/* The received frame is an Extended format frame */
|
||||
cf->can_id = (id_xcan & XCAN_IDR_ID1_MASK) >> 3;
|
||||
cf->can_id |= (id_xcan & XCAN_IDR_ID2_MASK) >>
|
||||
XCAN_IDR_ID2_SHIFT;
|
||||
cf->can_id |= CAN_EFF_FLAG;
|
||||
if (id_xcan & XCAN_IDR_RTR_MASK)
|
||||
cf->can_id |= CAN_RTR_FLAG;
|
||||
} else {
|
||||
/* The received frame is a standard format frame */
|
||||
cf->can_id = (id_xcan & XCAN_IDR_ID1_MASK) >>
|
||||
XCAN_IDR_ID1_SHIFT;
|
||||
if (!(dlc & XCAN_DLCR_EDL_MASK) && (id_xcan &
|
||||
XCAN_IDR_SRR_MASK))
|
||||
cf->can_id |= CAN_RTR_FLAG;
|
||||
}
|
||||
|
||||
/* Check the frame received is FD or not*/
|
||||
if (dlc & XCAN_DLCR_EDL_MASK) {
|
||||
for (i = 0; i < cf->len; i += 4) {
|
||||
if (priv->devtype.flags & XCAN_FLAG_CANFD_2)
|
||||
data[0] = priv->read_reg(priv,
|
||||
(XCAN_RXMSG_2_FRAME_OFFSET(readindex) +
|
||||
(dwindex * XCANFD_DW_BYTES)));
|
||||
else
|
||||
data[0] = priv->read_reg(priv,
|
||||
(XCAN_RXMSG_FRAME_OFFSET(readindex) +
|
||||
(dwindex * XCANFD_DW_BYTES)));
|
||||
*(__be32 *)(cf->data + i) =
|
||||
cpu_to_be32(data[0]);
|
||||
dwindex++;
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < cf->len; i += 4) {
|
||||
if (priv->devtype.flags & XCAN_FLAG_CANFD_2)
|
||||
data[0] = priv->read_reg(priv,
|
||||
XCAN_RXMSG_2_FRAME_OFFSET(readindex) + i);
|
||||
else
|
||||
data[0] = priv->read_reg(priv,
|
||||
XCAN_RXMSG_FRAME_OFFSET(readindex) + i);
|
||||
*(__be32 *)(cf->data + i) =
|
||||
cpu_to_be32(data[0]);
|
||||
}
|
||||
}
|
||||
/* Update FSR Register so that next packet will save to
|
||||
* buffer
|
||||
*/
|
||||
fsr = priv->read_reg(priv, XCAN_FSR_OFFSET);
|
||||
fsr |= XCAN_FSR_IRI_MASK;
|
||||
priv->write_reg(priv, XCAN_FSR_OFFSET, fsr);
|
||||
fsr = priv->read_reg(priv, XCAN_FSR_OFFSET);
|
||||
stats->rx_bytes += cf->len;
|
||||
stats->rx_packets++;
|
||||
netif_receive_skb(skb);
|
||||
|
||||
return 1;
|
||||
}
|
||||
/* If FSR Register is not updated with fill level */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* xcan_current_error_state - Get current error state from HW
|
||||
* @ndev: Pointer to net_device structure
|
||||
@ -924,7 +1119,7 @@ static void xcan_err_interrupt(struct net_device *ndev, u32 isr)
|
||||
cf->data[3] = CAN_ERR_PROT_LOC_CRC_SEQ;
|
||||
}
|
||||
}
|
||||
priv->can.can_stats.bus_error++;
|
||||
priv->can.can_stats.bus_error++;
|
||||
}
|
||||
|
||||
if (skb) {
|
||||
@ -934,7 +1129,7 @@ static void xcan_err_interrupt(struct net_device *ndev, u32 isr)
|
||||
}
|
||||
|
||||
netdev_dbg(ndev, "%s: error status register:0x%x\n",
|
||||
__func__, priv->read_reg(priv, XCAN_ESR_OFFSET));
|
||||
__func__, priv->read_reg(priv, XCAN_ESR_OFFSET));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -960,6 +1155,7 @@ static void xcan_state_interrupt(struct net_device *ndev, u32 isr)
|
||||
|
||||
/**
|
||||
* xcan_rx_fifo_get_next_frame - Get register offset of next RX frame
|
||||
* @priv: Driver private data structure
|
||||
*
|
||||
* Return: Register offset of the next frame in RX FIFO.
|
||||
*/
|
||||
@ -982,9 +1178,11 @@ static int xcan_rx_fifo_get_next_frame(struct xcan_priv *priv)
|
||||
return -ENOENT;
|
||||
|
||||
if (priv->devtype.flags & XCAN_FLAG_CANFD_2)
|
||||
offset = XCAN_RXMSG_2_FRAME_OFFSET(fsr & XCAN_FSR_RI_MASK);
|
||||
offset =
|
||||
XCAN_RXMSG_2_FRAME_OFFSET(fsr & XCAN_FSR_RI_MASK);
|
||||
else
|
||||
offset = XCAN_RXMSG_FRAME_OFFSET(fsr & XCAN_FSR_RI_MASK);
|
||||
offset =
|
||||
XCAN_RXMSG_FRAME_OFFSET(fsr & XCAN_FSR_RI_MASK);
|
||||
|
||||
} else {
|
||||
/* check if RX FIFO is empty */
|
||||
@ -1019,7 +1217,10 @@ static int xcan_rx_poll(struct napi_struct *napi, int quota)
|
||||
|
||||
while ((frame_offset = xcan_rx_fifo_get_next_frame(priv)) >= 0 &&
|
||||
(work_done < quota)) {
|
||||
work_done += xcan_rx(ndev, frame_offset);
|
||||
if (xcan_rx_int_mask(priv) & XCAN_IXR_RXOK_MASK)
|
||||
work_done += xcanfd_rx(ndev, frame_offset);
|
||||
else
|
||||
work_done += xcan_rx(ndev, frame_offset);
|
||||
|
||||
if (priv->devtype.flags & XCAN_FLAG_RX_FIFO_MULTI)
|
||||
/* increment read index */
|
||||
@ -1094,8 +1295,10 @@ static void xcan_tx_interrupt(struct net_device *ndev, u32 isr)
|
||||
* via TXFEMP handling as we read TXFEMP *after* TXOK
|
||||
* clear to satisfy (1).
|
||||
*/
|
||||
while ((isr & XCAN_IXR_TXOK_MASK) && !WARN_ON(++retries == 100)) {
|
||||
priv->write_reg(priv, XCAN_ICR_OFFSET, XCAN_IXR_TXOK_MASK);
|
||||
while ((isr & XCAN_IXR_TXOK_MASK) &&
|
||||
!WARN_ON(++retries == 100)) {
|
||||
priv->write_reg(priv, XCAN_ICR_OFFSET,
|
||||
XCAN_IXR_TXOK_MASK);
|
||||
isr = priv->read_reg(priv, XCAN_ISR_OFFSET);
|
||||
}
|
||||
|
||||
@ -1208,12 +1411,12 @@ static int xcan_open(struct net_device *ndev)
|
||||
ret = pm_runtime_get_sync(priv->dev);
|
||||
if (ret < 0) {
|
||||
netdev_err(ndev, "%s: pm_runtime_get failed(%d)\n",
|
||||
__func__, ret);
|
||||
__func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = request_irq(ndev->irq, xcan_interrupt, priv->irq_flags,
|
||||
ndev->name, ndev);
|
||||
ndev->name, ndev);
|
||||
if (ret < 0) {
|
||||
netdev_err(ndev, "irq allocation for CAN failed\n");
|
||||
goto err;
|
||||
@ -1284,7 +1487,7 @@ static int xcan_close(struct net_device *ndev)
|
||||
* Return: 0 on success and failure value on error
|
||||
*/
|
||||
static int xcan_get_berr_counter(const struct net_device *ndev,
|
||||
struct can_berr_counter *bec)
|
||||
struct can_berr_counter *bec)
|
||||
{
|
||||
struct xcan_priv *priv = netdev_priv(ndev);
|
||||
int ret;
|
||||
@ -1292,7 +1495,7 @@ static int xcan_get_berr_counter(const struct net_device *ndev,
|
||||
ret = pm_runtime_get_sync(priv->dev);
|
||||
if (ret < 0) {
|
||||
netdev_err(ndev, "%s: pm_runtime_get failed(%d)\n",
|
||||
__func__, ret);
|
||||
__func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1305,7 +1508,6 @@ static int xcan_get_berr_counter(const struct net_device *ndev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static const struct net_device_ops xcan_netdev_ops = {
|
||||
.ndo_open = xcan_open,
|
||||
.ndo_stop = xcan_close,
|
||||
@ -1417,6 +1619,8 @@ static const struct dev_pm_ops xcan_dev_pm_ops = {
|
||||
};
|
||||
|
||||
static const struct xcan_devtype_data xcan_zynq_data = {
|
||||
.cantype = XZYNQ_CANPS,
|
||||
.flags = XCAN_FLAG_TXFEMP,
|
||||
.bittiming_const = &xcan_bittiming_const,
|
||||
.btr_ts2_shift = XCAN_BTR_TS2_SHIFT,
|
||||
.btr_sjw_shift = XCAN_BTR_SJW_SHIFT,
|
||||
@ -1424,6 +1628,8 @@ static const struct xcan_devtype_data xcan_zynq_data = {
|
||||
};
|
||||
|
||||
static const struct xcan_devtype_data xcan_axi_data = {
|
||||
.cantype = XAXI_CAN,
|
||||
.flags = XCAN_FLAG_TXFEMP,
|
||||
.bittiming_const = &xcan_bittiming_const,
|
||||
.btr_ts2_shift = XCAN_BTR_TS2_SHIFT,
|
||||
.btr_sjw_shift = XCAN_BTR_SJW_SHIFT,
|
||||
@ -1431,6 +1637,7 @@ static const struct xcan_devtype_data xcan_axi_data = {
|
||||
};
|
||||
|
||||
static const struct xcan_devtype_data xcan_canfd_data = {
|
||||
.cantype = XAXI_CANFD,
|
||||
.flags = XCAN_FLAG_EXT_FILTERS |
|
||||
XCAN_FLAG_RXMNF |
|
||||
XCAN_FLAG_TX_MAILBOXES |
|
||||
@ -1442,6 +1649,7 @@ static const struct xcan_devtype_data xcan_canfd_data = {
|
||||
};
|
||||
|
||||
static const struct xcan_devtype_data xcan_canfd2_data = {
|
||||
.cantype = XAXI_CANFD_2_0,
|
||||
.flags = XCAN_FLAG_EXT_FILTERS |
|
||||
XCAN_FLAG_RXMNF |
|
||||
XCAN_FLAG_TX_MAILBOXES |
|
||||
@ -1554,6 +1762,19 @@ static int xcan_probe(struct platform_device *pdev)
|
||||
priv->can.do_get_berr_counter = xcan_get_berr_counter;
|
||||
priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK |
|
||||
CAN_CTRLMODE_BERR_REPORTING;
|
||||
|
||||
if (devtype->cantype == XAXI_CANFD)
|
||||
priv->can.data_bittiming_const =
|
||||
&xcan_data_bittiming_const_canfd;
|
||||
|
||||
if (devtype->cantype == XAXI_CANFD_2_0)
|
||||
priv->can.data_bittiming_const =
|
||||
&xcan_data_bittiming_const_canfd2;
|
||||
|
||||
if (devtype->cantype == XAXI_CANFD ||
|
||||
devtype->cantype == XAXI_CANFD_2_0)
|
||||
priv->can.ctrlmode_supported |= CAN_CTRLMODE_FD;
|
||||
|
||||
priv->reg_base = addr;
|
||||
priv->tx_max = tx_max;
|
||||
priv->devtype = *devtype;
|
||||
@ -1589,7 +1810,7 @@ static int xcan_probe(struct platform_device *pdev)
|
||||
ret = pm_runtime_get_sync(&pdev->dev);
|
||||
if (ret < 0) {
|
||||
netdev_err(ndev, "%s: pm_runtime_get failed(%d)\n",
|
||||
__func__, ret);
|
||||
__func__, ret);
|
||||
goto err_pmdisable;
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
|
||||
/*
|
||||
* linux/can/core.h
|
||||
*
|
||||
@ -57,6 +57,5 @@ extern void can_rx_unregister(struct net *net, struct net_device *dev,
|
||||
void *data);
|
||||
|
||||
extern int can_send(struct sk_buff *skb, int loop);
|
||||
extern int can_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg);
|
||||
|
||||
#endif /* !_CAN_CORE_H */
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
|
||||
/*
|
||||
* linux/can/skb.h
|
||||
*
|
||||
|
@ -8,11 +8,12 @@ menuconfig CAN
|
||||
tristate "CAN bus subsystem support"
|
||||
---help---
|
||||
Controller Area Network (CAN) is a slow (up to 1Mbit/s) serial
|
||||
communications protocol which was developed by Bosch in
|
||||
1991, mainly for automotive, but now widely used in marine
|
||||
(NMEA2000), industrial, and medical applications.
|
||||
More information on the CAN network protocol family PF_CAN
|
||||
is contained in <Documentation/networking/can.rst>.
|
||||
communications protocol. Development of the CAN bus started in
|
||||
1983 at Robert Bosch GmbH, and the protocol was officially
|
||||
released in 1986. The CAN bus was originally mainly for automotive,
|
||||
but is now widely used in marine (NMEA2000), industrial, and medical
|
||||
applications. More information on the CAN network protocol family
|
||||
PF_CAN is contained in <Documentation/networking/can.rst>.
|
||||
|
||||
If you want CAN support you should say Y here and also to the
|
||||
specific driver for your controller(s) below.
|
||||
|
@ -1,3 +1,4 @@
|
||||
// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
|
||||
/*
|
||||
* af_can.c - Protocol family CAN core module
|
||||
* (used by different CAN protocol modules)
|
||||
@ -87,15 +88,6 @@ static atomic_t skbcounter = ATOMIC_INIT(0);
|
||||
* af_can socket functions
|
||||
*/
|
||||
|
||||
int can_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
switch (cmd) {
|
||||
default:
|
||||
return -ENOIOCTLCMD;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(can_ioctl);
|
||||
|
||||
static void can_sock_destruct(struct sock *sk)
|
||||
{
|
||||
skb_queue_purge(&sk->sk_receive_queue);
|
||||
|
@ -1,3 +1,4 @@
|
||||
/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
|
||||
/*
|
||||
* Copyright (c) 2002-2007 Volkswagen Group Electronic Research
|
||||
* All rights reserved.
|
||||
|
@ -1,3 +1,4 @@
|
||||
// SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
|
||||
/*
|
||||
* bcm.c - Broadcast Manager to filter/send (cyclic) CAN content
|
||||
*
|
||||
@ -1688,7 +1689,7 @@ static const struct proto_ops bcm_ops = {
|
||||
.accept = sock_no_accept,
|
||||
.getname = sock_no_getname,
|
||||
.poll = datagram_poll,
|
||||
.ioctl = can_ioctl, /* use can_ioctl() from af_can.c */
|
||||
.ioctl = sock_no_ioctl,
|
||||
.gettstamp = sock_gettstamp,
|
||||
.listen = sock_no_listen,
|
||||
.shutdown = sock_no_shutdown,
|
||||
|
@ -1,3 +1,4 @@
|
||||
// SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
|
||||
/*
|
||||
* gw.c - CAN frame Gateway/Router/Bridge with netlink interface
|
||||
*
|
||||
|
@ -1,3 +1,4 @@
|
||||
// SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
|
||||
/*
|
||||
* proc.c - procfs support for Protocol family CAN core module
|
||||
*
|
||||
|
@ -1,3 +1,4 @@
|
||||
// SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
|
||||
/*
|
||||
* raw.c - Raw sockets for protocol family CAN
|
||||
*
|
||||
@ -845,7 +846,7 @@ static const struct proto_ops raw_ops = {
|
||||
.accept = sock_no_accept,
|
||||
.getname = raw_getname,
|
||||
.poll = datagram_poll,
|
||||
.ioctl = can_ioctl, /* use can_ioctl() from af_can.c */
|
||||
.ioctl = sock_no_ioctl,
|
||||
.gettstamp = sock_gettstamp,
|
||||
.listen = sock_no_listen,
|
||||
.shutdown = sock_no_shutdown,
|
||||
|
Loading…
Reference in New Issue
Block a user