mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-24 21:24:00 +08:00
linux-can-next-for-5.12-20210106
-----BEGIN PGP SIGNATURE----- iQFHBAABCgAxFiEEK3kIWJt9yTYMP3ehqclaivrt76kFAl/12xATHG1rbEBwZW5n dXRyb25peC5kZQAKCRCpyVqK+u3vqcB6B/4xTRQCOLF1BN59AdixqGf9gdXjIpQs R5h4WJXqh9OPS4XSckSVBVGYniT8KdfBLL0IY4NV8q2s6R6Yo70G3GTiaYXI4cX/ BaKMRcvX6p1i2qZxTtHDGYxucnKCd+yRwfWkL4GLsCNcmTWCkqUL+nGgx6UmS6eB 9fMHcLXiP9vSfJQUfAOLR33Bu2C0QslI5fX5hi8PIdKEz9cHGf3PAuaRvtWEWi3a 1Zi/iXrv/0XDcT+OE5/2tAU6n8slIA2MU/80r7KdgUrWXOfuueH/pqepYgVU0Nf2 eFH+gDYBlZucd4ef37TPIAdPTw3z4F9irOVI1iwRIWdK3aQ3jyinsVWF =0lW0 -----END PGP SIGNATURE----- Merge tag 'linux-can-next-for-5.12-20210106' of git://git.kernel.org/pub/scm/linux/kernel/git/mkl/linux-can-next Marc Kleine-Budde says: ==================== pull-request: can-next 2021-01-06 The first 16 patches are by me and target the tcan4x5x SPI glue driver for the m_can CAN driver. First there are a several cleanup commits, then the SPI regmap part is converted to 8 bits per word, to make it possible to use that driver on SPI controllers that only support the 8 bit per word mode (such as the SPI cores on the raspberry pi). Oliver Hartkopp contributes a patch for the CAN_RAW protocol. The getsockopt() for CAN_RAW_FILTER is changed to return -ERANGE if the filterset does not fit into the provided user space buffer. The last two patches are by Joakim Zhang and add wakeup support to the flexcan driver for the i.MX8QM SoC. The dt-bindings docs are extended to describe the added property. * tag 'linux-can-next-for-5.12-20210106' of git://git.kernel.org/pub/scm/linux/kernel/git/mkl/linux-can-next: can: flexcan: add CAN wakeup function for i.MX8QM dt-bindings: can: fsl,flexcan: add fsl,scu-index property to indicate a resource can: raw: return -ERANGE when filterset does not fit into user space buffer can: tcan4x5x: add support for half-duplex controllers can: tcan4x5x: rework SPI access can: tcan4x5x: add {wr,rd}_table can: tcan4x5x: add max_raw_{read,write} of 256 can: tcan4x5x: tcan4x5x_regmap: set reg_stride to 4 can: tcan4x5x: fix max register value can: tcan4x5x: tcan4x5x_regmap_init(): use spi as context pointer can: tcan4x5x: tcan4x5x_regmap_write(): remove not needed casts and replace 4 by sizeof can: tcan4x5x: rename regmap_spi_gather_write() -> tcan4x5x_regmap_gather_write() can: tcan4x5x: remove regmap async support can: tcan4x5x: tcan4x5x_bus: remove not needed read_flag_mask can: tcan4x5x: mark struct regmap_bus tcan4x5x_bus as constant can: tcan4x5x: move regmap code into seperate file can: tcan4x5x: rename tcan4x5x.c -> tcan4x5x-core.c can: tcan4x5x: beautify indention of tcan4x5x_of_match and tcan4x5x_id_table can: tcan4x5x: replace DEVICE_NAME by KBUILD_MODNAME ==================== Link: https://lore.kernel.org/r/20210107094900.173046-1-mkl@pengutronix.de Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
c10b377ff6
@ -110,6 +110,16 @@ properties:
|
|||||||
description:
|
description:
|
||||||
Enable CAN remote wakeup.
|
Enable CAN remote wakeup.
|
||||||
|
|
||||||
|
fsl,scu-index:
|
||||||
|
description: |
|
||||||
|
The scu index of CAN instance.
|
||||||
|
For SoCs with SCU support, need setup stop mode via SCU firmware, so this
|
||||||
|
property can help indicate a resource. It supports up to 3 CAN instances
|
||||||
|
now.
|
||||||
|
$ref: /schemas/types.yaml#/definitions/uint8
|
||||||
|
minimum: 0
|
||||||
|
maximum: 2
|
||||||
|
|
||||||
required:
|
required:
|
||||||
- compatible
|
- compatible
|
||||||
- reg
|
- reg
|
||||||
@ -137,4 +147,5 @@ examples:
|
|||||||
clocks = <&clks 1>, <&clks 2>;
|
clocks = <&clks 1>, <&clks 2>;
|
||||||
clock-names = "ipg", "per";
|
clock-names = "ipg", "per";
|
||||||
fsl,stop-mode = <&gpr 0x34 28>;
|
fsl,stop-mode = <&gpr 0x34 28>;
|
||||||
|
fsl,scu-index = /bits/ 8 <1>;
|
||||||
};
|
};
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
//
|
//
|
||||||
// Based on code originally by Andrey Volkov <avolkov@varma-el.com>
|
// Based on code originally by Andrey Volkov <avolkov@varma-el.com>
|
||||||
|
|
||||||
|
#include <dt-bindings/firmware/imx/rsrc.h>
|
||||||
#include <linux/bitfield.h>
|
#include <linux/bitfield.h>
|
||||||
#include <linux/can.h>
|
#include <linux/can.h>
|
||||||
#include <linux/can/dev.h>
|
#include <linux/can/dev.h>
|
||||||
@ -17,6 +18,7 @@
|
|||||||
#include <linux/can/rx-offload.h>
|
#include <linux/can/rx-offload.h>
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
|
#include <linux/firmware/imx/sci.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/mfd/syscon.h>
|
#include <linux/mfd/syscon.h>
|
||||||
@ -242,6 +244,8 @@
|
|||||||
#define FLEXCAN_QUIRK_SUPPORT_FD BIT(9)
|
#define FLEXCAN_QUIRK_SUPPORT_FD BIT(9)
|
||||||
/* support memory detection and correction */
|
/* support memory detection and correction */
|
||||||
#define FLEXCAN_QUIRK_SUPPORT_ECC BIT(10)
|
#define FLEXCAN_QUIRK_SUPPORT_ECC BIT(10)
|
||||||
|
/* Setup stop mode with SCU firmware to support wakeup */
|
||||||
|
#define FLEXCAN_QUIRK_SETUP_STOP_MODE_SCFW BIT(11)
|
||||||
|
|
||||||
/* Structure of the message buffer */
|
/* Structure of the message buffer */
|
||||||
struct flexcan_mb {
|
struct flexcan_mb {
|
||||||
@ -347,6 +351,7 @@ struct flexcan_priv {
|
|||||||
u8 mb_count;
|
u8 mb_count;
|
||||||
u8 mb_size;
|
u8 mb_size;
|
||||||
u8 clk_src; /* clock source of CAN Protocol Engine */
|
u8 clk_src; /* clock source of CAN Protocol Engine */
|
||||||
|
u8 scu_idx;
|
||||||
|
|
||||||
u64 rx_mask;
|
u64 rx_mask;
|
||||||
u64 tx_mask;
|
u64 tx_mask;
|
||||||
@ -358,6 +363,9 @@ struct flexcan_priv {
|
|||||||
struct regulator *reg_xceiver;
|
struct regulator *reg_xceiver;
|
||||||
struct flexcan_stop_mode stm;
|
struct flexcan_stop_mode stm;
|
||||||
|
|
||||||
|
/* IPC handle when setup stop mode by System Controller firmware(scfw) */
|
||||||
|
struct imx_sc_ipc *sc_ipc_handle;
|
||||||
|
|
||||||
/* Read and Write APIs */
|
/* Read and Write APIs */
|
||||||
u32 (*read)(void __iomem *addr);
|
u32 (*read)(void __iomem *addr);
|
||||||
void (*write)(u32 val, void __iomem *addr);
|
void (*write)(u32 val, void __iomem *addr);
|
||||||
@ -387,7 +395,7 @@ static const struct flexcan_devtype_data fsl_imx6q_devtype_data = {
|
|||||||
static const struct flexcan_devtype_data fsl_imx8qm_devtype_data = {
|
static const struct flexcan_devtype_data fsl_imx8qm_devtype_data = {
|
||||||
.quirks = FLEXCAN_QUIRK_DISABLE_RXFG | FLEXCAN_QUIRK_ENABLE_EACEN_RRS |
|
.quirks = FLEXCAN_QUIRK_DISABLE_RXFG | FLEXCAN_QUIRK_ENABLE_EACEN_RRS |
|
||||||
FLEXCAN_QUIRK_USE_OFF_TIMESTAMP | FLEXCAN_QUIRK_BROKEN_PERR_STATE |
|
FLEXCAN_QUIRK_USE_OFF_TIMESTAMP | FLEXCAN_QUIRK_BROKEN_PERR_STATE |
|
||||||
FLEXCAN_QUIRK_SUPPORT_FD,
|
FLEXCAN_QUIRK_SUPPORT_FD | FLEXCAN_QUIRK_SETUP_STOP_MODE_SCFW,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct flexcan_devtype_data fsl_imx8mp_devtype_data = {
|
static struct flexcan_devtype_data fsl_imx8mp_devtype_data = {
|
||||||
@ -546,18 +554,42 @@ static void flexcan_enable_wakeup_irq(struct flexcan_priv *priv, bool enable)
|
|||||||
priv->write(reg_mcr, ®s->mcr);
|
priv->write(reg_mcr, ®s->mcr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int flexcan_stop_mode_enable_scfw(struct flexcan_priv *priv, bool enabled)
|
||||||
|
{
|
||||||
|
u8 idx = priv->scu_idx;
|
||||||
|
u32 rsrc_id, val;
|
||||||
|
|
||||||
|
rsrc_id = IMX_SC_R_CAN(idx);
|
||||||
|
|
||||||
|
if (enabled)
|
||||||
|
val = 1;
|
||||||
|
else
|
||||||
|
val = 0;
|
||||||
|
|
||||||
|
/* stop mode request via scu firmware */
|
||||||
|
return imx_sc_misc_set_control(priv->sc_ipc_handle, rsrc_id,
|
||||||
|
IMX_SC_C_IPG_STOP, val);
|
||||||
|
}
|
||||||
|
|
||||||
static inline int flexcan_enter_stop_mode(struct flexcan_priv *priv)
|
static inline int flexcan_enter_stop_mode(struct flexcan_priv *priv)
|
||||||
{
|
{
|
||||||
struct flexcan_regs __iomem *regs = priv->regs;
|
struct flexcan_regs __iomem *regs = priv->regs;
|
||||||
u32 reg_mcr;
|
u32 reg_mcr;
|
||||||
|
int ret;
|
||||||
|
|
||||||
reg_mcr = priv->read(®s->mcr);
|
reg_mcr = priv->read(®s->mcr);
|
||||||
reg_mcr |= FLEXCAN_MCR_SLF_WAK;
|
reg_mcr |= FLEXCAN_MCR_SLF_WAK;
|
||||||
priv->write(reg_mcr, ®s->mcr);
|
priv->write(reg_mcr, ®s->mcr);
|
||||||
|
|
||||||
/* enable stop request */
|
/* enable stop request */
|
||||||
regmap_update_bits(priv->stm.gpr, priv->stm.req_gpr,
|
if (priv->devtype_data->quirks & FLEXCAN_QUIRK_SETUP_STOP_MODE_SCFW) {
|
||||||
1 << priv->stm.req_bit, 1 << priv->stm.req_bit);
|
ret = flexcan_stop_mode_enable_scfw(priv, true);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
} else {
|
||||||
|
regmap_update_bits(priv->stm.gpr, priv->stm.req_gpr,
|
||||||
|
1 << priv->stm.req_bit, 1 << priv->stm.req_bit);
|
||||||
|
}
|
||||||
|
|
||||||
return flexcan_low_power_enter_ack(priv);
|
return flexcan_low_power_enter_ack(priv);
|
||||||
}
|
}
|
||||||
@ -566,10 +598,17 @@ static inline int flexcan_exit_stop_mode(struct flexcan_priv *priv)
|
|||||||
{
|
{
|
||||||
struct flexcan_regs __iomem *regs = priv->regs;
|
struct flexcan_regs __iomem *regs = priv->regs;
|
||||||
u32 reg_mcr;
|
u32 reg_mcr;
|
||||||
|
int ret;
|
||||||
|
|
||||||
/* remove stop request */
|
/* remove stop request */
|
||||||
regmap_update_bits(priv->stm.gpr, priv->stm.req_gpr,
|
if (priv->devtype_data->quirks & FLEXCAN_QUIRK_SETUP_STOP_MODE_SCFW) {
|
||||||
1 << priv->stm.req_bit, 0);
|
ret = flexcan_stop_mode_enable_scfw(priv, false);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
} else {
|
||||||
|
regmap_update_bits(priv->stm.gpr, priv->stm.req_gpr,
|
||||||
|
1 << priv->stm.req_bit, 0);
|
||||||
|
}
|
||||||
|
|
||||||
reg_mcr = priv->read(®s->mcr);
|
reg_mcr = priv->read(®s->mcr);
|
||||||
reg_mcr &= ~FLEXCAN_MCR_SLF_WAK;
|
reg_mcr &= ~FLEXCAN_MCR_SLF_WAK;
|
||||||
@ -1867,7 +1906,7 @@ static void unregister_flexcandev(struct net_device *dev)
|
|||||||
unregister_candev(dev);
|
unregister_candev(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int flexcan_setup_stop_mode(struct platform_device *pdev)
|
static int flexcan_setup_stop_mode_gpr(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct net_device *dev = platform_get_drvdata(pdev);
|
struct net_device *dev = platform_get_drvdata(pdev);
|
||||||
struct device_node *np = pdev->dev.of_node;
|
struct device_node *np = pdev->dev.of_node;
|
||||||
@ -1912,11 +1951,6 @@ static int flexcan_setup_stop_mode(struct platform_device *pdev)
|
|||||||
"gpr %s req_gpr=0x02%x req_bit=%u\n",
|
"gpr %s req_gpr=0x02%x req_bit=%u\n",
|
||||||
gpr_np->full_name, priv->stm.req_gpr, priv->stm.req_bit);
|
gpr_np->full_name, priv->stm.req_gpr, priv->stm.req_bit);
|
||||||
|
|
||||||
device_set_wakeup_capable(&pdev->dev, true);
|
|
||||||
|
|
||||||
if (of_property_read_bool(np, "wakeup-source"))
|
|
||||||
device_set_wakeup_enable(&pdev->dev, true);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
out_put_node:
|
out_put_node:
|
||||||
@ -1924,6 +1958,58 @@ out_put_node:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int flexcan_setup_stop_mode_scfw(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct net_device *dev = platform_get_drvdata(pdev);
|
||||||
|
struct flexcan_priv *priv;
|
||||||
|
u8 scu_idx;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = of_property_read_u8(pdev->dev.of_node, "fsl,scu-index", &scu_idx);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_dbg(&pdev->dev, "failed to get scu index\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
priv = netdev_priv(dev);
|
||||||
|
priv->scu_idx = scu_idx;
|
||||||
|
|
||||||
|
/* this function could be defered probe, return -EPROBE_DEFER */
|
||||||
|
return imx_scu_get_handle(&priv->sc_ipc_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* flexcan_setup_stop_mode - Setup stop mode for wakeup
|
||||||
|
*
|
||||||
|
* Return: = 0 setup stop mode successfully or doesn't support this feature
|
||||||
|
* < 0 fail to setup stop mode (could be defered probe)
|
||||||
|
*/
|
||||||
|
static int flexcan_setup_stop_mode(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct net_device *dev = platform_get_drvdata(pdev);
|
||||||
|
struct flexcan_priv *priv;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
priv = netdev_priv(dev);
|
||||||
|
|
||||||
|
if (priv->devtype_data->quirks & FLEXCAN_QUIRK_SETUP_STOP_MODE_SCFW)
|
||||||
|
ret = flexcan_setup_stop_mode_scfw(pdev);
|
||||||
|
else if (priv->devtype_data->quirks & FLEXCAN_QUIRK_SETUP_STOP_MODE_GPR)
|
||||||
|
ret = flexcan_setup_stop_mode_gpr(pdev);
|
||||||
|
else
|
||||||
|
/* return 0 directly if doesn't support stop mode feature */
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
device_set_wakeup_capable(&pdev->dev, true);
|
||||||
|
|
||||||
|
if (of_property_read_bool(pdev->dev.of_node, "wakeup-source"))
|
||||||
|
device_set_wakeup_enable(&pdev->dev, true);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static const struct of_device_id flexcan_of_match[] = {
|
static const struct of_device_id flexcan_of_match[] = {
|
||||||
{ .compatible = "fsl,imx8qm-flexcan", .data = &fsl_imx8qm_devtype_data, },
|
{ .compatible = "fsl,imx8qm-flexcan", .data = &fsl_imx8qm_devtype_data, },
|
||||||
{ .compatible = "fsl,imx8mp-flexcan", .data = &fsl_imx8mp_devtype_data, },
|
{ .compatible = "fsl,imx8mp-flexcan", .data = &fsl_imx8mp_devtype_data, },
|
||||||
@ -2054,17 +2140,20 @@ static int flexcan_probe(struct platform_device *pdev)
|
|||||||
goto failed_register;
|
goto failed_register;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = flexcan_setup_stop_mode(pdev);
|
||||||
|
if (err < 0) {
|
||||||
|
if (err != -EPROBE_DEFER)
|
||||||
|
dev_err(&pdev->dev, "setup stop mode failed\n");
|
||||||
|
goto failed_setup_stop_mode;
|
||||||
|
}
|
||||||
|
|
||||||
of_can_transceiver(dev);
|
of_can_transceiver(dev);
|
||||||
devm_can_led_init(dev);
|
devm_can_led_init(dev);
|
||||||
|
|
||||||
if (priv->devtype_data->quirks & FLEXCAN_QUIRK_SETUP_STOP_MODE_GPR) {
|
|
||||||
err = flexcan_setup_stop_mode(pdev);
|
|
||||||
if (err)
|
|
||||||
dev_dbg(&pdev->dev, "failed to setup stop-mode\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
failed_setup_stop_mode:
|
||||||
|
unregister_flexcandev(dev);
|
||||||
failed_register:
|
failed_register:
|
||||||
pm_runtime_put_noidle(&pdev->dev);
|
pm_runtime_put_noidle(&pdev->dev);
|
||||||
pm_runtime_disable(&pdev->dev);
|
pm_runtime_disable(&pdev->dev);
|
||||||
|
@ -7,3 +7,7 @@ obj-$(CONFIG_CAN_M_CAN) += m_can.o
|
|||||||
obj-$(CONFIG_CAN_M_CAN_PCI) += m_can_pci.o
|
obj-$(CONFIG_CAN_M_CAN_PCI) += m_can_pci.o
|
||||||
obj-$(CONFIG_CAN_M_CAN_PLATFORM) += m_can_platform.o
|
obj-$(CONFIG_CAN_M_CAN_PLATFORM) += m_can_platform.o
|
||||||
obj-$(CONFIG_CAN_M_CAN_TCAN4X5X) += tcan4x5x.o
|
obj-$(CONFIG_CAN_M_CAN_TCAN4X5X) += tcan4x5x.o
|
||||||
|
|
||||||
|
tcan4x5x-objs :=
|
||||||
|
tcan4x5x-objs += tcan4x5x-core.o
|
||||||
|
tcan4x5x-objs += tcan4x5x-regmap.o
|
||||||
|
@ -2,15 +2,8 @@
|
|||||||
// SPI to CAN driver for the Texas Instruments TCAN4x5x
|
// SPI to CAN driver for the Texas Instruments TCAN4x5x
|
||||||
// Copyright (C) 2018-19 Texas Instruments Incorporated - http://www.ti.com/
|
// Copyright (C) 2018-19 Texas Instruments Incorporated - http://www.ti.com/
|
||||||
|
|
||||||
#include <linux/regmap.h>
|
#include "tcan4x5x.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_EXT_CLK_DEF 40000000
|
||||||
|
|
||||||
#define TCAN4X5X_DEV_ID0 0x00
|
#define TCAN4X5X_DEV_ID0 0x00
|
||||||
@ -88,14 +81,10 @@
|
|||||||
|
|
||||||
#define TCAN4X5X_MRAM_START 0x8000
|
#define TCAN4X5X_MRAM_START 0x8000
|
||||||
#define TCAN4X5X_MCAN_OFFSET 0x1000
|
#define TCAN4X5X_MCAN_OFFSET 0x1000
|
||||||
#define TCAN4X5X_MAX_REGISTER 0x8fff
|
|
||||||
|
|
||||||
#define TCAN4X5X_CLEAR_ALL_INT 0xffffffff
|
#define TCAN4X5X_CLEAR_ALL_INT 0xffffffff
|
||||||
#define TCAN4X5X_SET_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_SEL_MASK (BIT(7) | BIT(6))
|
||||||
#define TCAN4X5X_MODE_SLEEP 0x00
|
#define TCAN4X5X_MODE_SLEEP 0x00
|
||||||
#define TCAN4X5X_MODE_STANDBY BIT(6)
|
#define TCAN4X5X_MODE_STANDBY BIT(6)
|
||||||
@ -113,18 +102,6 @@
|
|||||||
#define TCAN4X5X_WD_3_S_TIMER BIT(29)
|
#define TCAN4X5X_WD_3_S_TIMER BIT(29)
|
||||||
#define TCAN4X5X_WD_6_S_TIMER (BIT(28) | BIT(29))
|
#define TCAN4X5X_WD_6_S_TIMER (BIT(28) | BIT(29))
|
||||||
|
|
||||||
struct tcan4x5x_priv {
|
|
||||||
struct m_can_classdev cdev;
|
|
||||||
|
|
||||||
struct regmap *regmap;
|
|
||||||
struct spi_device *spi;
|
|
||||||
|
|
||||||
struct gpio_desc *reset_gpio;
|
|
||||||
struct gpio_desc *device_wake_gpio;
|
|
||||||
struct gpio_desc *device_state_gpio;
|
|
||||||
struct regulator *power;
|
|
||||||
};
|
|
||||||
|
|
||||||
static inline struct tcan4x5x_priv *cdev_to_priv(struct m_can_classdev *cdev)
|
static inline struct tcan4x5x_priv *cdev_to_priv(struct m_can_classdev *cdev)
|
||||||
{
|
{
|
||||||
return container_of(cdev, struct tcan4x5x_priv, cdev);
|
return container_of(cdev, struct tcan4x5x_priv, cdev);
|
||||||
@ -132,7 +109,7 @@ static inline struct tcan4x5x_priv *cdev_to_priv(struct m_can_classdev *cdev)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static struct can_bittiming_const tcan4x5x_bittiming_const = {
|
static struct can_bittiming_const tcan4x5x_bittiming_const = {
|
||||||
.name = DEVICE_NAME,
|
.name = KBUILD_MODNAME,
|
||||||
.tseg1_min = 2,
|
.tseg1_min = 2,
|
||||||
.tseg1_max = 31,
|
.tseg1_max = 31,
|
||||||
.tseg2_min = 2,
|
.tseg2_min = 2,
|
||||||
@ -144,7 +121,7 @@ static struct can_bittiming_const tcan4x5x_bittiming_const = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static struct can_bittiming_const tcan4x5x_data_bittiming_const = {
|
static struct can_bittiming_const tcan4x5x_data_bittiming_const = {
|
||||||
.name = DEVICE_NAME,
|
.name = KBUILD_MODNAME,
|
||||||
.tseg1_min = 1,
|
.tseg1_min = 1,
|
||||||
.tseg1_max = 32,
|
.tseg1_max = 32,
|
||||||
.tseg2_min = 1,
|
.tseg2_min = 1,
|
||||||
@ -191,72 +168,6 @@ static int tcan4x5x_reset(struct tcan4x5x_priv *priv)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
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 >> 2;
|
|
||||||
|
|
||||||
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 - 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
static u32 tcan4x5x_read_reg(struct m_can_classdev *cdev, int reg)
|
||||||
{
|
{
|
||||||
struct tcan4x5x_priv *priv = cdev_to_priv(cdev);
|
struct tcan4x5x_priv *priv = cdev_to_priv(cdev);
|
||||||
@ -411,13 +322,6 @@ static int tcan4x5x_get_gpios(struct m_can_classdev *cdev)
|
|||||||
return 0;
|
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 = {
|
static struct m_can_ops tcan4x5x_ops = {
|
||||||
.init = tcan4x5x_init,
|
.init = tcan4x5x_init,
|
||||||
.read_reg = tcan4x5x_read_reg,
|
.read_reg = tcan4x5x_read_reg,
|
||||||
@ -476,17 +380,14 @@ static int tcan4x5x_can_probe(struct spi_device *spi)
|
|||||||
spi_set_drvdata(spi, priv);
|
spi_set_drvdata(spi, priv);
|
||||||
|
|
||||||
/* Configure the SPI bus */
|
/* Configure the SPI bus */
|
||||||
spi->bits_per_word = 32;
|
spi->bits_per_word = 8;
|
||||||
ret = spi_setup(spi);
|
ret = spi_setup(spi);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out_m_can_class_free_dev;
|
goto out_m_can_class_free_dev;
|
||||||
|
|
||||||
priv->regmap = devm_regmap_init(&spi->dev, &tcan4x5x_bus,
|
ret = tcan4x5x_regmap_init(priv);
|
||||||
&spi->dev, &tcan4x5x_regmap);
|
if (ret)
|
||||||
if (IS_ERR(priv->regmap)) {
|
|
||||||
ret = PTR_ERR(priv->regmap);
|
|
||||||
goto out_m_can_class_free_dev;
|
goto out_m_can_class_free_dev;
|
||||||
}
|
|
||||||
|
|
||||||
ret = tcan4x5x_power_enable(priv->power, 1);
|
ret = tcan4x5x_power_enable(priv->power, 1);
|
||||||
if (ret)
|
if (ret)
|
||||||
@ -528,23 +429,26 @@ static int tcan4x5x_can_remove(struct spi_device *spi)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static const struct of_device_id tcan4x5x_of_match[] = {
|
static const struct of_device_id tcan4x5x_of_match[] = {
|
||||||
{ .compatible = "ti,tcan4x5x", },
|
{
|
||||||
{ }
|
.compatible = "ti,tcan4x5x",
|
||||||
|
}, {
|
||||||
|
/* sentinel */
|
||||||
|
},
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, tcan4x5x_of_match);
|
MODULE_DEVICE_TABLE(of, tcan4x5x_of_match);
|
||||||
|
|
||||||
static const struct spi_device_id tcan4x5x_id_table[] = {
|
static const struct spi_device_id tcan4x5x_id_table[] = {
|
||||||
{
|
{
|
||||||
.name = "tcan4x5x",
|
.name = "tcan4x5x",
|
||||||
.driver_data = 0,
|
}, {
|
||||||
|
/* sentinel */
|
||||||
},
|
},
|
||||||
{ }
|
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(spi, tcan4x5x_id_table);
|
MODULE_DEVICE_TABLE(spi, tcan4x5x_id_table);
|
||||||
|
|
||||||
static struct spi_driver tcan4x5x_can_driver = {
|
static struct spi_driver tcan4x5x_can_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = DEVICE_NAME,
|
.name = KBUILD_MODNAME,
|
||||||
.of_match_table = tcan4x5x_of_match,
|
.of_match_table = tcan4x5x_of_match,
|
||||||
.pm = NULL,
|
.pm = NULL,
|
||||||
},
|
},
|
135
drivers/net/can/m_can/tcan4x5x-regmap.c
Normal file
135
drivers/net/can/m_can/tcan4x5x-regmap.c
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
//
|
||||||
|
// tcan4x5x - Texas Instruments TCAN4x5x Family CAN controller driver
|
||||||
|
//
|
||||||
|
// Copyright (c) 2020 Pengutronix,
|
||||||
|
// Marc Kleine-Budde <kernel@pengutronix.de>
|
||||||
|
// Copyright (c) 2018-2019 Texas Instruments Incorporated
|
||||||
|
// http://www.ti.com/
|
||||||
|
|
||||||
|
#include "tcan4x5x.h"
|
||||||
|
|
||||||
|
#define TCAN4X5X_SPI_INSTRUCTION_WRITE (0x61 << 24)
|
||||||
|
#define TCAN4X5X_SPI_INSTRUCTION_READ (0x41 << 24)
|
||||||
|
|
||||||
|
#define TCAN4X5X_MAX_REGISTER 0x8ffc
|
||||||
|
|
||||||
|
static int tcan4x5x_regmap_gather_write(void *context,
|
||||||
|
const void *reg, size_t reg_len,
|
||||||
|
const void *val, size_t val_len)
|
||||||
|
{
|
||||||
|
struct spi_device *spi = context;
|
||||||
|
struct tcan4x5x_priv *priv = spi_get_drvdata(spi);
|
||||||
|
struct tcan4x5x_map_buf *buf_tx = &priv->map_buf_tx;
|
||||||
|
struct spi_transfer xfer[] = {
|
||||||
|
{
|
||||||
|
.tx_buf = buf_tx,
|
||||||
|
.len = sizeof(buf_tx->cmd) + val_len,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
memcpy(&buf_tx->cmd, reg, sizeof(buf_tx->cmd.cmd) +
|
||||||
|
sizeof(buf_tx->cmd.addr));
|
||||||
|
tcan4x5x_spi_cmd_set_len(&buf_tx->cmd, val_len);
|
||||||
|
memcpy(buf_tx->data, val, val_len);
|
||||||
|
|
||||||
|
return spi_sync_transfer(spi, xfer, ARRAY_SIZE(xfer));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tcan4x5x_regmap_write(void *context, const void *data, size_t count)
|
||||||
|
{
|
||||||
|
return tcan4x5x_regmap_gather_write(context, data, sizeof(__be32),
|
||||||
|
data + sizeof(__be32),
|
||||||
|
count - sizeof(__be32));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tcan4x5x_regmap_read(void *context,
|
||||||
|
const void *reg_buf, size_t reg_len,
|
||||||
|
void *val_buf, size_t val_len)
|
||||||
|
{
|
||||||
|
struct spi_device *spi = context;
|
||||||
|
struct tcan4x5x_priv *priv = spi_get_drvdata(spi);
|
||||||
|
struct tcan4x5x_map_buf *buf_rx = &priv->map_buf_rx;
|
||||||
|
struct tcan4x5x_map_buf *buf_tx = &priv->map_buf_tx;
|
||||||
|
struct spi_transfer xfer[2] = {
|
||||||
|
{
|
||||||
|
.tx_buf = buf_tx,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
struct spi_message msg;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
spi_message_init(&msg);
|
||||||
|
spi_message_add_tail(&xfer[0], &msg);
|
||||||
|
|
||||||
|
memcpy(&buf_tx->cmd, reg_buf, sizeof(buf_tx->cmd.cmd) +
|
||||||
|
sizeof(buf_tx->cmd.addr));
|
||||||
|
tcan4x5x_spi_cmd_set_len(&buf_tx->cmd, val_len);
|
||||||
|
|
||||||
|
if (spi->controller->flags & SPI_CONTROLLER_HALF_DUPLEX) {
|
||||||
|
xfer[0].len = sizeof(buf_tx->cmd);
|
||||||
|
|
||||||
|
xfer[1].rx_buf = val_buf;
|
||||||
|
xfer[1].len = val_len;
|
||||||
|
spi_message_add_tail(&xfer[1], &msg);
|
||||||
|
} else {
|
||||||
|
xfer[0].rx_buf = buf_rx;
|
||||||
|
xfer[0].len = sizeof(buf_tx->cmd) + val_len;
|
||||||
|
|
||||||
|
if (TCAN4X5X_SANITIZE_SPI)
|
||||||
|
memset(buf_tx->data, 0x0, val_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
err = spi_sync(spi, &msg);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
if (!(spi->controller->flags & SPI_CONTROLLER_HALF_DUPLEX))
|
||||||
|
memcpy(val_buf, buf_rx->data, val_len);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct regmap_range tcan4x5x_reg_table_yes_range[] = {
|
||||||
|
regmap_reg_range(0x0000, 0x002c), /* Device ID and SPI Registers */
|
||||||
|
regmap_reg_range(0x0800, 0x083c), /* Device configuration registers and Interrupt Flags*/
|
||||||
|
regmap_reg_range(0x1000, 0x10fc), /* M_CAN */
|
||||||
|
regmap_reg_range(0x8000, 0x87fc), /* MRAM */
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct regmap_access_table tcan4x5x_reg_table = {
|
||||||
|
.yes_ranges = tcan4x5x_reg_table_yes_range,
|
||||||
|
.n_yes_ranges = ARRAY_SIZE(tcan4x5x_reg_table_yes_range),
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct regmap_config tcan4x5x_regmap = {
|
||||||
|
.reg_bits = 24,
|
||||||
|
.reg_stride = 4,
|
||||||
|
.pad_bits = 8,
|
||||||
|
.val_bits = 32,
|
||||||
|
.wr_table = &tcan4x5x_reg_table,
|
||||||
|
.rd_table = &tcan4x5x_reg_table,
|
||||||
|
.max_register = TCAN4X5X_MAX_REGISTER,
|
||||||
|
.cache_type = REGCACHE_NONE,
|
||||||
|
.read_flag_mask = (__force unsigned long)
|
||||||
|
cpu_to_be32(TCAN4X5X_SPI_INSTRUCTION_READ),
|
||||||
|
.write_flag_mask = (__force unsigned long)
|
||||||
|
cpu_to_be32(TCAN4X5X_SPI_INSTRUCTION_WRITE),
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct regmap_bus tcan4x5x_bus = {
|
||||||
|
.write = tcan4x5x_regmap_write,
|
||||||
|
.gather_write = tcan4x5x_regmap_gather_write,
|
||||||
|
.read = tcan4x5x_regmap_read,
|
||||||
|
.reg_format_endian_default = REGMAP_ENDIAN_BIG,
|
||||||
|
.val_format_endian_default = REGMAP_ENDIAN_BIG,
|
||||||
|
.max_raw_read = 256,
|
||||||
|
.max_raw_write = 256,
|
||||||
|
};
|
||||||
|
|
||||||
|
int tcan4x5x_regmap_init(struct tcan4x5x_priv *priv)
|
||||||
|
{
|
||||||
|
priv->regmap = devm_regmap_init(&priv->spi->dev, &tcan4x5x_bus,
|
||||||
|
priv->spi, &tcan4x5x_regmap);
|
||||||
|
return PTR_ERR_OR_ZERO(priv->regmap);
|
||||||
|
}
|
57
drivers/net/can/m_can/tcan4x5x.h
Normal file
57
drivers/net/can/m_can/tcan4x5x.h
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
/* SPDX-License-Identifier: GPL-2.0
|
||||||
|
*
|
||||||
|
* tcan4x5x - Texas Instruments TCAN4x5x Family CAN controller driver
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020 Pengutronix,
|
||||||
|
* Marc Kleine-Budde <kernel@pengutronix.de>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _TCAN4X5X_H
|
||||||
|
#define _TCAN4X5X_H
|
||||||
|
|
||||||
|
#include <linux/gpio/consumer.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
|
#include <linux/regulator/consumer.h>
|
||||||
|
#include <linux/spi/spi.h>
|
||||||
|
|
||||||
|
#include "m_can.h"
|
||||||
|
|
||||||
|
#define TCAN4X5X_SANITIZE_SPI 1
|
||||||
|
|
||||||
|
struct __packed tcan4x5x_buf_cmd {
|
||||||
|
u8 cmd;
|
||||||
|
__be16 addr;
|
||||||
|
u8 len;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct __packed tcan4x5x_map_buf {
|
||||||
|
struct tcan4x5x_buf_cmd cmd;
|
||||||
|
u8 data[256 * sizeof(u32)];
|
||||||
|
} ____cacheline_aligned;
|
||||||
|
|
||||||
|
struct tcan4x5x_priv {
|
||||||
|
struct m_can_classdev cdev;
|
||||||
|
|
||||||
|
struct regmap *regmap;
|
||||||
|
struct spi_device *spi;
|
||||||
|
|
||||||
|
struct gpio_desc *reset_gpio;
|
||||||
|
struct gpio_desc *device_wake_gpio;
|
||||||
|
struct gpio_desc *device_state_gpio;
|
||||||
|
struct regulator *power;
|
||||||
|
|
||||||
|
struct tcan4x5x_map_buf map_buf_rx;
|
||||||
|
struct tcan4x5x_map_buf map_buf_tx;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
tcan4x5x_spi_cmd_set_len(struct tcan4x5x_buf_cmd *cmd, u8 len)
|
||||||
|
{
|
||||||
|
/* number of u32 */
|
||||||
|
cmd->len = len >> 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
int tcan4x5x_regmap_init(struct tcan4x5x_priv *priv);
|
||||||
|
|
||||||
|
#endif
|
@ -665,10 +665,18 @@ static int raw_getsockopt(struct socket *sock, int level, int optname,
|
|||||||
if (ro->count > 0) {
|
if (ro->count > 0) {
|
||||||
int fsize = ro->count * sizeof(struct can_filter);
|
int fsize = ro->count * sizeof(struct can_filter);
|
||||||
|
|
||||||
if (len > fsize)
|
/* user space buffer to small for filter list? */
|
||||||
len = fsize;
|
if (len < fsize) {
|
||||||
if (copy_to_user(optval, ro->filter, len))
|
/* return -ERANGE and needed space in optlen */
|
||||||
err = -EFAULT;
|
err = -ERANGE;
|
||||||
|
if (put_user(fsize, optlen))
|
||||||
|
err = -EFAULT;
|
||||||
|
} else {
|
||||||
|
if (len > fsize)
|
||||||
|
len = fsize;
|
||||||
|
if (copy_to_user(optval, ro->filter, len))
|
||||||
|
err = -EFAULT;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
len = 0;
|
len = 0;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user