mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-20 14:16:02 +00:00
linux-can-next-for-4.16-20180116
-----BEGIN PGP SIGNATURE----- iQFHBAABCgAxFiEE4bay/IylYqM/npjQHv7KIOw4HPYFAlpeCCQTHG1rbEBwZW5n dXRyb25peC5kZQAKCRAe/sog7Dgc9ti9B/4jAoCYTuYXnkRvS34jdgQQV99QyC8M EpcAgzHo2kYNPDf5q8TEVheXxvA9XeGiA+TtjL9cNowxwMMtJev3/dmBtOmU6jek RgOsYlR8guxBHOx8pj1IMl1xPoWTLfg4Kv1qnpXx3zvhOP610G/edBBSZt659MGF SW6pBoNivbl+EYSM5x81QIfqJ4NlD5AKY4PeeSnGrnSthd8EFNp2zKkcY8nMOJ0D Gm2YyxGJXh+lHse965DQRNg+owZxIWyheQplulVrw9v34LOjbFko3Cd+D9KLW5MG LVVRJ3E0jm7W75AvxNcv2WP+lZVcDxXqsxFH0dP8WOJNZiKZeJ5aZfji =ROXk -----END PGP SIGNATURE----- Merge tag 'linux-can-next-for-4.16-20180116' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/mkl/linux-can-next Marc Kleine-Budde says: ==================== pull-request: can-next 2018-01-16 this is a pull request for net-next/master consisting of 9 patches. This is a series of patches, some of them initially by Franklin S Cooper Jr, which was picked up by Faiz Abbas. Faiz Abbas added some patches while working on this series, I contributed one as well. The first two patches add support to CAN device infrastructure to limit the bitrate of a CAN adapter if the used CAN-transceiver has a certain maximum bitrate. The remaining patches improve the m_can driver. They add support for bitrate limiting to the driver, clean up the driver and add support for runtime PM. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
4f7d58517f
6 changed files with 202 additions and 67 deletions
|
@ -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>;
|
||||||
|
};
|
||||||
|
...
|
||||||
|
};
|
|
@ -43,6 +43,11 @@ Required properties:
|
||||||
Please refer to 2.4.1 Message RAM Configuration in
|
Please refer to 2.4.1 Message RAM Configuration in
|
||||||
Bosch M_CAN user manual for details.
|
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:
|
Example:
|
||||||
SoC dtsi:
|
SoC dtsi:
|
||||||
m_can1: can@20e8000 {
|
m_can1: can@20e8000 {
|
||||||
|
@ -63,4 +68,8 @@ Board dts:
|
||||||
pinctrl-names = "default";
|
pinctrl-names = "default";
|
||||||
pinctrl-0 = <&pinctrl_m_can1>;
|
pinctrl-0 = <&pinctrl_m_can1>;
|
||||||
status = "enabled";
|
status = "enabled";
|
||||||
|
|
||||||
|
can-transceiver {
|
||||||
|
max-bitrate = <5000000>;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#include <linux/can/skb.h>
|
#include <linux/can/skb.h>
|
||||||
#include <linux/can/netlink.h>
|
#include <linux/can/netlink.h>
|
||||||
#include <linux/can/led.h>
|
#include <linux/can/led.h>
|
||||||
|
#include <linux/of.h>
|
||||||
#include <net/rtnetlink.h>
|
#include <net/rtnetlink.h>
|
||||||
|
|
||||||
#define MOD_DESC "CAN device driver interface"
|
#define MOD_DESC "CAN device driver interface"
|
||||||
|
@ -814,6 +815,29 @@ int open_candev(struct net_device *dev)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(open_candev);
|
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.
|
* 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);
|
priv->bitrate_const_cnt);
|
||||||
if (err)
|
if (err)
|
||||||
return 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));
|
memcpy(&priv->bittiming, &bt, sizeof(bt));
|
||||||
|
|
||||||
if (priv->do_set_bittiming) {
|
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);
|
priv->data_bitrate_const_cnt);
|
||||||
if (err)
|
if (err)
|
||||||
return 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));
|
memcpy(&priv->data_bittiming, &dbt, sizeof(dbt));
|
||||||
|
|
||||||
if (priv->do_set_data_bittiming) {
|
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 */
|
if (priv->data_bitrate_const) /* IFLA_CAN_DATA_BITRATE_CONST */
|
||||||
size += nla_total_size(sizeof(*priv->data_bitrate_const) *
|
size += nla_total_size(sizeof(*priv->data_bitrate_const) *
|
||||||
priv->data_bitrate_const_cnt);
|
priv->data_bitrate_const_cnt);
|
||||||
|
size += sizeof(priv->bitrate_max); /* IFLA_CAN_BITRATE_MAX */
|
||||||
|
|
||||||
return size;
|
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,
|
nla_put(skb, IFLA_CAN_DATA_BITRATE_CONST,
|
||||||
sizeof(*priv->data_bitrate_const) *
|
sizeof(*priv->data_bitrate_const) *
|
||||||
priv->data_bitrate_const_cnt,
|
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;
|
return -EMSGSIZE;
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_device.h>
|
#include <linux/of_device.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/pm_runtime.h>
|
||||||
#include <linux/iopoll.h>
|
#include <linux/iopoll.h>
|
||||||
#include <linux/can/dev.h>
|
#include <linux/can/dev.h>
|
||||||
|
|
||||||
|
@ -126,6 +127,12 @@ enum m_can_mram_cfg {
|
||||||
#define DBTP_DSJW_SHIFT 0
|
#define DBTP_DSJW_SHIFT 0
|
||||||
#define DBTP_DSJW_MASK (0xf << DBTP_DSJW_SHIFT)
|
#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) */
|
/* Test Register (TEST) */
|
||||||
#define TEST_LBCK BIT(4)
|
#define TEST_LBCK BIT(4)
|
||||||
|
|
||||||
|
@ -625,21 +632,16 @@ static int m_can_clk_start(struct m_can_priv *priv)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
err = clk_prepare_enable(priv->hclk);
|
err = pm_runtime_get_sync(priv->device);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
pm_runtime_put_noidle(priv->device);
|
||||||
|
|
||||||
err = clk_prepare_enable(priv->cclk);
|
|
||||||
if (err)
|
|
||||||
clk_disable_unprepare(priv->hclk);
|
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void m_can_clk_stop(struct m_can_priv *priv)
|
static void m_can_clk_stop(struct m_can_priv *priv)
|
||||||
{
|
{
|
||||||
clk_disable_unprepare(priv->cclk);
|
pm_runtime_put_sync(priv->device);
|
||||||
clk_disable_unprepare(priv->hclk);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int m_can_get_berr_counter(const struct net_device *dev,
|
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);
|
m_can_write(priv, M_CAN_NBTP, reg_btp);
|
||||||
|
|
||||||
if (priv->can.ctrlmode & CAN_CTRLMODE_FD) {
|
if (priv->can.ctrlmode & CAN_CTRLMODE_FD) {
|
||||||
|
reg_btp = 0;
|
||||||
brp = dbt->brp - 1;
|
brp = dbt->brp - 1;
|
||||||
sjw = dbt->sjw - 1;
|
sjw = dbt->sjw - 1;
|
||||||
tseg1 = dbt->prop_seg + dbt->phase_seg1 - 1;
|
tseg1 = dbt->prop_seg + dbt->phase_seg1 - 1;
|
||||||
tseg2 = dbt->phase_seg2 - 1;
|
tseg2 = dbt->phase_seg2 - 1;
|
||||||
reg_btp = (brp << DBTP_DBRP_SHIFT) | (sjw << DBTP_DSJW_SHIFT) |
|
|
||||||
(tseg1 << DBTP_DTSEG1_SHIFT) |
|
/* TDC is only needed for bitrates beyond 2.5 MBit/s.
|
||||||
(tseg2 << DBTP_DTSEG2_SHIFT);
|
* 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);
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void free_m_can_dev(struct net_device *dev)
|
|
||||||
{
|
|
||||||
free_candev(dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Checks core release number of M_CAN
|
/* Checks core release number of M_CAN
|
||||||
* returns 0 if an unsupported device is detected
|
* returns 0 if an unsupported device is detected
|
||||||
* else it returns the release and step coded as:
|
* 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;
|
return !niso_timeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct net_device *alloc_m_can_dev(struct platform_device *pdev,
|
static int m_can_dev_setup(struct platform_device *pdev, struct net_device *dev,
|
||||||
void __iomem *addr, u32 tx_fifo_size)
|
void __iomem *addr)
|
||||||
{
|
{
|
||||||
struct net_device *dev;
|
|
||||||
struct m_can_priv *priv;
|
struct m_can_priv *priv;
|
||||||
int m_can_version;
|
int m_can_version;
|
||||||
unsigned int echo_buffer_count;
|
|
||||||
|
|
||||||
m_can_version = m_can_check_core_release(addr);
|
m_can_version = m_can_check_core_release(addr);
|
||||||
/* return if unsupported version */
|
/* return if unsupported version */
|
||||||
if (!m_can_version) {
|
if (!m_can_version) {
|
||||||
dev = NULL;
|
dev_err(&pdev->dev, "Unsupported version number: %2d",
|
||||||
goto return_dev;
|
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);
|
priv = netdev_priv(dev);
|
||||||
netif_napi_add(dev, &priv->napi, m_can_poll, M_CAN_NAPI_WEIGHT);
|
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);
|
: 0);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
/* Unsupported device: free candev */
|
|
||||||
free_m_can_dev(dev);
|
|
||||||
dev_err(&pdev->dev, "Unsupported version number: %2d",
|
dev_err(&pdev->dev, "Unsupported version number: %2d",
|
||||||
priv->version);
|
priv->version);
|
||||||
dev = NULL;
|
return -EINVAL;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return_dev:
|
return 0;
|
||||||
return dev;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int m_can_open(struct net_device *dev)
|
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;
|
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");
|
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "m_can");
|
||||||
addr = devm_ioremap_resource(&pdev->dev, res);
|
addr = devm_ioremap_resource(&pdev->dev, res);
|
||||||
irq = platform_get_irq_byname(pdev, "int0");
|
irq = platform_get_irq_byname(pdev, "int0");
|
||||||
|
|
||||||
if (IS_ERR(addr) || irq < 0) {
|
if (IS_ERR(addr) || irq < 0) {
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto disable_cclk_ret;
|
goto failed_ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* message ram could be shared */
|
/* message ram could be shared */
|
||||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "message_ram");
|
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "message_ram");
|
||||||
if (!res) {
|
if (!res) {
|
||||||
ret = -ENODEV;
|
ret = -ENODEV;
|
||||||
goto disable_cclk_ret;
|
goto failed_ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
mram_addr = devm_ioremap(&pdev->dev, res->start, resource_size(res));
|
mram_addr = devm_ioremap(&pdev->dev, res->start, resource_size(res));
|
||||||
if (!mram_addr) {
|
if (!mram_addr) {
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto disable_cclk_ret;
|
goto failed_ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* get message ram configuration */
|
/* get message ram configuration */
|
||||||
|
@ -1613,7 +1618,7 @@ static int m_can_plat_probe(struct platform_device *pdev)
|
||||||
sizeof(mram_config_vals) / 4);
|
sizeof(mram_config_vals) / 4);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&pdev->dev, "Could not get Message RAM configuration.");
|
dev_err(&pdev->dev, "Could not get Message RAM configuration.");
|
||||||
goto disable_cclk_ret;
|
goto failed_ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get TX FIFO size
|
/* 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];
|
tx_fifo_size = mram_config_vals[7];
|
||||||
|
|
||||||
/* allocate the m_can device */
|
/* 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) {
|
if (!dev) {
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto disable_cclk_ret;
|
goto failed_ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
priv = netdev_priv(dev);
|
priv = netdev_priv(dev);
|
||||||
dev->irq = irq;
|
dev->irq = irq;
|
||||||
priv->device = &pdev->dev;
|
priv->device = &pdev->dev;
|
||||||
|
@ -1640,30 +1646,42 @@ static int m_can_plat_probe(struct platform_device *pdev)
|
||||||
platform_set_drvdata(pdev, dev);
|
platform_set_drvdata(pdev, dev);
|
||||||
SET_NETDEV_DEV(dev, &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);
|
ret = register_m_can_dev(dev);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&pdev->dev, "registering %s failed (err=%d)\n",
|
dev_err(&pdev->dev, "registering %s failed (err=%d)\n",
|
||||||
KBUILD_MODNAME, ret);
|
KBUILD_MODNAME, ret);
|
||||||
goto failed_free_dev;
|
goto clk_disable;
|
||||||
}
|
}
|
||||||
|
|
||||||
devm_can_led_init(dev);
|
devm_can_led_init(dev);
|
||||||
|
|
||||||
|
of_can_transceiver(dev);
|
||||||
|
|
||||||
dev_info(&pdev->dev, "%s device registered (irq=%d, version=%d)\n",
|
dev_info(&pdev->dev, "%s device registered (irq=%d, version=%d)\n",
|
||||||
KBUILD_MODNAME, dev->irq, priv->version);
|
KBUILD_MODNAME, dev->irq, priv->version);
|
||||||
|
|
||||||
/* Probe finished
|
/* Probe finished
|
||||||
* Stop clocks. They will be reactivated once the M_CAN device is opened
|
* Stop clocks. They will be reactivated once the M_CAN device is opened
|
||||||
*/
|
*/
|
||||||
|
clk_disable:
|
||||||
goto disable_cclk_ret;
|
m_can_clk_stop(priv);
|
||||||
|
pm_runtime_fail:
|
||||||
failed_free_dev:
|
if (ret) {
|
||||||
free_m_can_dev(dev);
|
pm_runtime_disable(&pdev->dev);
|
||||||
disable_cclk_ret:
|
free_candev(dev);
|
||||||
clk_disable_unprepare(cclk);
|
}
|
||||||
disable_hclk_ret:
|
|
||||||
clk_disable_unprepare(hclk);
|
|
||||||
failed_ret:
|
failed_ret:
|
||||||
return 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);
|
struct net_device *dev = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
unregister_m_can_dev(dev);
|
unregister_m_can_dev(dev);
|
||||||
|
|
||||||
|
pm_runtime_disable(&pdev->dev);
|
||||||
|
|
||||||
platform_set_drvdata(pdev, NULL);
|
platform_set_drvdata(pdev, NULL);
|
||||||
|
|
||||||
free_m_can_dev(dev);
|
free_candev(dev);
|
||||||
|
|
||||||
return 0;
|
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 = {
|
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)
|
SET_SYSTEM_SLEEP_PM_OPS(m_can_suspend, m_can_resume)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -46,6 +46,7 @@ struct can_priv {
|
||||||
unsigned int bitrate_const_cnt;
|
unsigned int bitrate_const_cnt;
|
||||||
const u32 *data_bitrate_const;
|
const u32 *data_bitrate_const;
|
||||||
unsigned int data_bitrate_const_cnt;
|
unsigned int data_bitrate_const_cnt;
|
||||||
|
u32 bitrate_max;
|
||||||
struct can_clock clock;
|
struct can_clock clock;
|
||||||
|
|
||||||
enum can_state state;
|
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);
|
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);
|
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_can_skb(struct net_device *dev, struct can_frame **cf);
|
||||||
struct sk_buff *alloc_canfd_skb(struct net_device *dev,
|
struct sk_buff *alloc_canfd_skb(struct net_device *dev,
|
||||||
struct canfd_frame **cfd);
|
struct canfd_frame **cfd);
|
||||||
|
|
|
@ -132,6 +132,7 @@ enum {
|
||||||
IFLA_CAN_TERMINATION_CONST,
|
IFLA_CAN_TERMINATION_CONST,
|
||||||
IFLA_CAN_BITRATE_CONST,
|
IFLA_CAN_BITRATE_CONST,
|
||||||
IFLA_CAN_DATA_BITRATE_CONST,
|
IFLA_CAN_DATA_BITRATE_CONST,
|
||||||
|
IFLA_CAN_BITRATE_MAX,
|
||||||
__IFLA_CAN_MAX
|
__IFLA_CAN_MAX
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue