summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/net/can/can-transceiver.txt24
-rw-r--r--Documentation/devicetree/bindings/net/can/m_can.txt9
-rw-r--r--drivers/net/can/dev.c45
-rw-r--r--drivers/net/can/m_can/m_can.c183
-rw-r--r--include/linux/can/dev.h7
-rw-r--r--include/uapi/linux/can/netlink.h1
6 files changed, 202 insertions, 67 deletions
diff --git a/Documentation/devicetree/bindings/net/can/can-transceiver.txt b/Documentation/devicetree/bindings/net/can/can-transceiver.txt
new file mode 100644
index 000000000000..0011f53ff159
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/can/can-transceiver.txt
@@ -0,0 +1,24 @@
+Generic CAN transceiver Device Tree binding
+------------------------------
+
+CAN transceiver typically limits the max speed in standard CAN and CAN FD
+modes. Typically these limitations are static and the transceivers themselves
+provide no way to detect this limitation at runtime. For this situation,
+the "can-transceiver" node can be used.
+
+Required Properties:
+ max-bitrate: a positive non 0 value that determines the max
+ speed that CAN/CAN-FD can run. Any other value
+ will be ignored.
+
+Examples:
+
+Based on Texas Instrument's TCAN1042HGV CAN Transceiver
+
+m_can0 {
+ ....
+ can-transceiver {
+ max-bitrate = <5000000>;
+ };
+ ...
+};
diff --git a/Documentation/devicetree/bindings/net/can/m_can.txt b/Documentation/devicetree/bindings/net/can/m_can.txt
index 63e90421d029..ed614383af9c 100644
--- a/Documentation/devicetree/bindings/net/can/m_can.txt
+++ b/Documentation/devicetree/bindings/net/can/m_can.txt
@@ -43,6 +43,11 @@ Required properties:
Please refer to 2.4.1 Message RAM Configuration in
Bosch M_CAN user manual for details.
+Optional Subnode:
+- can-transceiver : Can-transceiver subnode describing maximum speed
+ that can be used for CAN/CAN-FD modes. See
+ Documentation/devicetree/bindings/net/can/can-transceiver.txt
+ for details.
Example:
SoC dtsi:
m_can1: can@20e8000 {
@@ -63,4 +68,8 @@ Board dts:
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_m_can1>;
status = "enabled";
+
+ can-transceiver {
+ max-bitrate = <5000000>;
+ };
};
diff --git a/drivers/net/can/dev.c b/drivers/net/can/dev.c
index 365a8cc62405..cc94604b23e0 100644
--- a/drivers/net/can/dev.c
+++ b/drivers/net/can/dev.c
@@ -27,6 +27,7 @@
#include <linux/can/skb.h>
#include <linux/can/netlink.h>
#include <linux/can/led.h>
+#include <linux/of.h>
#include <net/rtnetlink.h>
#define MOD_DESC "CAN device driver interface"
@@ -814,6 +815,29 @@ int open_candev(struct net_device *dev)
}
EXPORT_SYMBOL_GPL(open_candev);
+#ifdef CONFIG_OF
+/* Common function that can be used to understand the limitation of
+ * a transceiver when it provides no means to determine these limitations
+ * at runtime.
+ */
+void of_can_transceiver(struct net_device *dev)
+{
+ struct device_node *dn;
+ struct can_priv *priv = netdev_priv(dev);
+ struct device_node *np = dev->dev.parent->of_node;
+ int ret;
+
+ dn = of_get_child_by_name(np, "can-transceiver");
+ if (!dn)
+ return;
+
+ ret = of_property_read_u32(dn, "max-bitrate", &priv->bitrate_max);
+ if ((ret && ret != -EINVAL) || (!ret && !priv->bitrate_max))
+ netdev_warn(dev, "Invalid value for transceiver max bitrate. Ignoring bitrate limit.\n");
+}
+EXPORT_SYMBOL_GPL(of_can_transceiver);
+#endif
+
/*
* Common close function for cleanup before the device gets closed.
*
@@ -913,6 +937,13 @@ static int can_changelink(struct net_device *dev, struct nlattr *tb[],
priv->bitrate_const_cnt);
if (err)
return err;
+
+ if (priv->bitrate_max && bt.bitrate > priv->bitrate_max) {
+ netdev_err(dev, "arbitration bitrate surpasses transceiver capabilities of %d bps\n",
+ priv->bitrate_max);
+ return -EINVAL;
+ }
+
memcpy(&priv->bittiming, &bt, sizeof(bt));
if (priv->do_set_bittiming) {
@@ -997,6 +1028,13 @@ static int can_changelink(struct net_device *dev, struct nlattr *tb[],
priv->data_bitrate_const_cnt);
if (err)
return err;
+
+ if (priv->bitrate_max && dbt.bitrate > priv->bitrate_max) {
+ netdev_err(dev, "canfd data bitrate surpasses transceiver capabilities of %d bps\n",
+ priv->bitrate_max);
+ return -EINVAL;
+ }
+
memcpy(&priv->data_bittiming, &dbt, sizeof(dbt));
if (priv->do_set_data_bittiming) {
@@ -1064,6 +1102,7 @@ static size_t can_get_size(const struct net_device *dev)
if (priv->data_bitrate_const) /* IFLA_CAN_DATA_BITRATE_CONST */
size += nla_total_size(sizeof(*priv->data_bitrate_const) *
priv->data_bitrate_const_cnt);
+ size += sizeof(priv->bitrate_max); /* IFLA_CAN_BITRATE_MAX */
return size;
}
@@ -1121,7 +1160,11 @@ static int can_fill_info(struct sk_buff *skb, const struct net_device *dev)
nla_put(skb, IFLA_CAN_DATA_BITRATE_CONST,
sizeof(*priv->data_bitrate_const) *
priv->data_bitrate_const_cnt,
- priv->data_bitrate_const))
+ priv->data_bitrate_const)) ||
+
+ (nla_put(skb, IFLA_CAN_BITRATE_MAX,
+ sizeof(priv->bitrate_max),
+ &priv->bitrate_max))
)
return -EMSGSIZE;
diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c
index f4947a74b65f..49ed8737871f 100644
--- a/drivers/net/can/m_can/m_can.c
+++ b/drivers/net/can/m_can/m_can.c
@@ -23,6 +23,7 @@
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <linux/iopoll.h>
#include <linux/can/dev.h>
@@ -126,6 +127,12 @@ enum m_can_mram_cfg {
#define DBTP_DSJW_SHIFT 0
#define DBTP_DSJW_MASK (0xf << DBTP_DSJW_SHIFT)
+/* Transmitter Delay Compensation Register (TDCR) */
+#define TDCR_TDCO_SHIFT 8
+#define TDCR_TDCO_MASK (0x7F << TDCR_TDCO_SHIFT)
+#define TDCR_TDCF_SHIFT 0
+#define TDCR_TDCF_MASK (0x7F << TDCR_TDCF_SHIFT)
+
/* Test Register (TEST) */
#define TEST_LBCK BIT(4)
@@ -625,21 +632,16 @@ static int m_can_clk_start(struct m_can_priv *priv)
{
int err;
- err = clk_prepare_enable(priv->hclk);
+ err = pm_runtime_get_sync(priv->device);
if (err)
- return err;
-
- err = clk_prepare_enable(priv->cclk);
- if (err)
- clk_disable_unprepare(priv->hclk);
+ pm_runtime_put_noidle(priv->device);
return err;
}
static void m_can_clk_stop(struct m_can_priv *priv)
{
- clk_disable_unprepare(priv->cclk);
- clk_disable_unprepare(priv->hclk);
+ pm_runtime_put_sync(priv->device);
}
static int m_can_get_berr_counter(const struct net_device *dev,
@@ -987,13 +989,47 @@ static int m_can_set_bittiming(struct net_device *dev)
m_can_write(priv, M_CAN_NBTP, reg_btp);
if (priv->can.ctrlmode & CAN_CTRLMODE_FD) {
+ reg_btp = 0;
brp = dbt->brp - 1;
sjw = dbt->sjw - 1;
tseg1 = dbt->prop_seg + dbt->phase_seg1 - 1;
tseg2 = dbt->phase_seg2 - 1;
- reg_btp = (brp << DBTP_DBRP_SHIFT) | (sjw << DBTP_DSJW_SHIFT) |
- (tseg1 << DBTP_DTSEG1_SHIFT) |
- (tseg2 << DBTP_DTSEG2_SHIFT);
+
+ /* TDC is only needed for bitrates beyond 2.5 MBit/s.
+ * This is mentioned in the "Bit Time Requirements for CAN FD"
+ * paper presented at the International CAN Conference 2013
+ */
+ if (dbt->bitrate > 2500000) {
+ u32 tdco, ssp;
+
+ /* Use the same value of secondary sampling point
+ * as the data sampling point
+ */
+ ssp = dbt->sample_point;
+
+ /* Equation based on Bosch's M_CAN User Manual's
+ * Transmitter Delay Compensation Section
+ */
+ tdco = (priv->can.clock.freq / 1000) *
+ ssp / dbt->bitrate;
+
+ /* Max valid TDCO value is 127 */
+ if (tdco > 127) {
+ netdev_warn(dev, "TDCO value of %u is beyond maximum. Using maximum possible value\n",
+ tdco);
+ tdco = 127;
+ }
+
+ reg_btp |= DBTP_TDC;
+ m_can_write(priv, M_CAN_TDCR,
+ tdco << TDCR_TDCO_SHIFT);
+ }
+
+ reg_btp |= (brp << DBTP_DBRP_SHIFT) |
+ (sjw << DBTP_DSJW_SHIFT) |
+ (tseg1 << DBTP_DTSEG1_SHIFT) |
+ (tseg2 << DBTP_DTSEG2_SHIFT);
+
m_can_write(priv, M_CAN_DBTP, reg_btp);
}
@@ -1143,11 +1179,6 @@ static int m_can_set_mode(struct net_device *dev, enum can_mode mode)
return 0;
}
-static void free_m_can_dev(struct net_device *dev)
-{
- free_candev(dev);
-}
-
/* Checks core release number of M_CAN
* returns 0 if an unsupported device is detected
* else it returns the release and step coded as:
@@ -1207,31 +1238,20 @@ static bool m_can_niso_supported(const struct m_can_priv *priv)
return !niso_timeout;
}
-static struct net_device *alloc_m_can_dev(struct platform_device *pdev,
- void __iomem *addr, u32 tx_fifo_size)
+static int m_can_dev_setup(struct platform_device *pdev, struct net_device *dev,
+ void __iomem *addr)
{
- struct net_device *dev;
struct m_can_priv *priv;
int m_can_version;
- unsigned int echo_buffer_count;
m_can_version = m_can_check_core_release(addr);
/* return if unsupported version */
if (!m_can_version) {
- dev = NULL;
- goto return_dev;
+ dev_err(&pdev->dev, "Unsupported version number: %2d",
+ m_can_version);
+ return -EINVAL;
}
- /* If version < 3.1.x, then only one echo buffer is used */
- echo_buffer_count = ((m_can_version == 30)
- ? 1U
- : (unsigned int)tx_fifo_size);
-
- dev = alloc_candev(sizeof(*priv), echo_buffer_count);
- if (!dev) {
- dev = NULL;
- goto return_dev;
- }
priv = netdev_priv(dev);
netif_napi_add(dev, &priv->napi, m_can_poll, M_CAN_NAPI_WEIGHT);
@@ -1273,16 +1293,12 @@ static struct net_device *alloc_m_can_dev(struct platform_device *pdev,
: 0);
break;
default:
- /* Unsupported device: free candev */
- free_m_can_dev(dev);
dev_err(&pdev->dev, "Unsupported version number: %2d",
priv->version);
- dev = NULL;
- break;
+ return -EINVAL;
}
-return_dev:
- return dev;
+ return 0;
}
static int m_can_open(struct net_device *dev)
@@ -1574,37 +1590,26 @@ static int m_can_plat_probe(struct platform_device *pdev)
goto failed_ret;
}
- /* Enable clocks. Necessary to read Core Release in order to determine
- * M_CAN version
- */
- ret = clk_prepare_enable(hclk);
- if (ret)
- goto disable_hclk_ret;
-
- ret = clk_prepare_enable(cclk);
- if (ret)
- goto disable_cclk_ret;
-
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 disable_cclk_ret;
+ goto failed_ret;
}
/* message ram could be shared */
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "message_ram");
if (!res) {
ret = -ENODEV;
- goto disable_cclk_ret;
+ goto failed_ret;
}
mram_addr = devm_ioremap(&pdev->dev, res->start, resource_size(res));
if (!mram_addr) {
ret = -ENOMEM;
- goto disable_cclk_ret;
+ goto failed_ret;
}
/* get message ram configuration */
@@ -1613,7 +1618,7 @@ static int m_can_plat_probe(struct platform_device *pdev)
sizeof(mram_config_vals) / 4);
if (ret) {
dev_err(&pdev->dev, "Could not get Message RAM configuration.");
- goto disable_cclk_ret;
+ goto failed_ret;
}
/* Get TX FIFO size
@@ -1622,11 +1627,12 @@ static int m_can_plat_probe(struct platform_device *pdev)
tx_fifo_size = mram_config_vals[7];
/* allocate the m_can device */
- dev = alloc_m_can_dev(pdev, addr, tx_fifo_size);
+ dev = alloc_candev(sizeof(*priv), tx_fifo_size);
if (!dev) {
ret = -ENOMEM;
- goto disable_cclk_ret;
+ goto failed_ret;
}
+
priv = netdev_priv(dev);
dev->irq = irq;
priv->device = &pdev->dev;
@@ -1640,30 +1646,42 @@ static int m_can_plat_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, dev);
SET_NETDEV_DEV(dev, &pdev->dev);
+ /* Enable clocks. Necessary to read Core Release in order to determine
+ * M_CAN version
+ */
+ pm_runtime_enable(&pdev->dev);
+ ret = m_can_clk_start(priv);
+ if (ret)
+ goto pm_runtime_fail;
+
+ ret = m_can_dev_setup(pdev, dev, addr);
+ if (ret)
+ goto clk_disable;
+
ret = register_m_can_dev(dev);
if (ret) {
dev_err(&pdev->dev, "registering %s failed (err=%d)\n",
KBUILD_MODNAME, ret);
- goto failed_free_dev;
+ goto clk_disable;
}
devm_can_led_init(dev);
+ of_can_transceiver(dev);
+
dev_info(&pdev->dev, "%s device registered (irq=%d, version=%d)\n",
KBUILD_MODNAME, dev->irq, priv->version);
/* Probe finished
* Stop clocks. They will be reactivated once the M_CAN device is opened
*/
-
- goto disable_cclk_ret;
-
-failed_free_dev:
- free_m_can_dev(dev);
-disable_cclk_ret:
- clk_disable_unprepare(cclk);
-disable_hclk_ret:
- clk_disable_unprepare(hclk);
+clk_disable:
+ m_can_clk_stop(priv);
+pm_runtime_fail:
+ if (ret) {
+ pm_runtime_disable(&pdev->dev);
+ free_candev(dev);
+ }
failed_ret:
return ret;
}
@@ -1721,14 +1739,47 @@ static int m_can_plat_remove(struct platform_device *pdev)
struct net_device *dev = platform_get_drvdata(pdev);
unregister_m_can_dev(dev);
+
+ pm_runtime_disable(&pdev->dev);
+
platform_set_drvdata(pdev, NULL);
- free_m_can_dev(dev);
+ free_candev(dev);
return 0;
}
+static int m_can_runtime_suspend(struct device *dev)
+{
+ struct net_device *ndev = dev_get_drvdata(dev);
+ struct m_can_priv *priv = netdev_priv(ndev);
+
+ clk_disable_unprepare(priv->cclk);
+ clk_disable_unprepare(priv->hclk);
+
+ return 0;
+}
+
+static int m_can_runtime_resume(struct device *dev)
+{
+ struct net_device *ndev = dev_get_drvdata(dev);
+ struct m_can_priv *priv = netdev_priv(ndev);
+ int err;
+
+ err = clk_prepare_enable(priv->hclk);
+ if (err)
+ return err;
+
+ err = clk_prepare_enable(priv->cclk);
+ if (err)
+ clk_disable_unprepare(priv->hclk);
+
+ 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)
};
diff --git a/include/linux/can/dev.h b/include/linux/can/dev.h
index 61f1cf2d9f44..055aaf5ed9af 100644
--- a/include/linux/can/dev.h
+++ b/include/linux/can/dev.h
@@ -46,6 +46,7 @@ struct can_priv {
unsigned int bitrate_const_cnt;
const u32 *data_bitrate_const;
unsigned int data_bitrate_const_cnt;
+ u32 bitrate_max;
struct can_clock clock;
enum can_state state;
@@ -166,6 +167,12 @@ void can_put_echo_skb(struct sk_buff *skb, struct net_device *dev,
unsigned int can_get_echo_skb(struct net_device *dev, unsigned int idx);
void can_free_echo_skb(struct net_device *dev, unsigned int idx);
+#ifdef CONFIG_OF
+void of_can_transceiver(struct net_device *dev);
+#else
+static inline void of_can_transceiver(struct net_device *dev) { }
+#endif
+
struct sk_buff *alloc_can_skb(struct net_device *dev, struct can_frame **cf);
struct sk_buff *alloc_canfd_skb(struct net_device *dev,
struct canfd_frame **cfd);
diff --git a/include/uapi/linux/can/netlink.h b/include/uapi/linux/can/netlink.h
index 96710e76d5ce..9f56fad4785b 100644
--- a/include/uapi/linux/can/netlink.h
+++ b/include/uapi/linux/can/netlink.h
@@ -132,6 +132,7 @@ enum {
IFLA_CAN_TERMINATION_CONST,
IFLA_CAN_BITRATE_CONST,
IFLA_CAN_DATA_BITRATE_CONST,
+ IFLA_CAN_BITRATE_MAX,
__IFLA_CAN_MAX
};
OpenPOWER on IntegriCloud