Netdev List
 help / color / mirror / Atom feed
* [PATCH net-next 10/10] net: hns3: Fix loss of coal configuration while doing reset
From: Salil Mehta @ 2018-09-26 18:28 UTC (permalink / raw)
  To: davem
  Cc: salil.mehta, yisen.zhuang, lipeng321, mehta.salil, netdev,
	linux-kernel, linuxarm, Huazhong Tan, Yunsheng Lin
In-Reply-To: <20180926182840.28392-1-salil.mehta@huawei.com>

From: Huazhong Tan <tanhuazhong@huawei.com>

The user's coal configuration will be lost after reset, so the tx_coal
and rx_coal fields are added to the struct hns_nic_priv to save the coal
configuration and used to restore the user's configuration after the reset
is complete.

Fixes: bb6b94a896d4 ("net: hns3: Add reset interface implementation in client")
Signed-off-by: Huazhong Tan <tanhuazhong@huawei.com>
Signed-off-by: Yunsheng Lin <linyunsheng@huawei.com>
Signed-off-by: Salil Mehta <salil.mehta@huawei.com>
---
 drivers/net/ethernet/hisilicon/hns3/hns3_enet.c | 71 ++++++++++++-------------
 drivers/net/ethernet/hisilicon/hns3/hns3_enet.h |  2 +
 2 files changed, 36 insertions(+), 37 deletions(-)

diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c
index 8273c03..10506522 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c
@@ -225,8 +225,6 @@ void hns3_set_vector_coalesce_tx_gl(struct hns3_enet_tqp_vector *tqp_vector,
 static void hns3_vector_gl_rl_init(struct hns3_enet_tqp_vector *tqp_vector,
 				   struct hns3_nic_priv *priv)
 {
-	struct hnae3_handle *h = priv->ae_handle;
-
 	/* initialize the configuration for interrupt coalescing.
 	 * 1. GL (Interrupt Gap Limiter)
 	 * 2. RL (Interrupt Rate Limiter)
@@ -239,9 +237,6 @@ static void hns3_vector_gl_rl_init(struct hns3_enet_tqp_vector *tqp_vector,
 	tqp_vector->tx_group.coal.int_gl = HNS3_INT_GL_50K;
 	tqp_vector->rx_group.coal.int_gl = HNS3_INT_GL_50K;
 
-	/* Default: disable RL */
-	h->kinfo.int_rl_setting = 0;
-
 	tqp_vector->int_adapt_down = HNS3_INT_ADAPT_DOWN_START;
 	tqp_vector->rx_group.coal.flow_level = HNS3_FLOW_LOW;
 	tqp_vector->tx_group.coal.flow_level = HNS3_FLOW_LOW;
@@ -3490,6 +3485,31 @@ int hns3_nic_reset_all_ring(struct hnae3_handle *h)
 	return 0;
 }
 
+static void hns3_store_coal(struct hns3_nic_priv *priv)
+{
+	/* ethtool only support setting and querying one coal
+	 * configuation for now, so save the vector 0' coal
+	 * configuation here in order to restore it.
+	 */
+	memcpy(&priv->tx_coal, &priv->tqp_vector[0].tx_group.coal,
+	       sizeof(struct hns3_enet_coalesce));
+	memcpy(&priv->rx_coal, &priv->tqp_vector[0].rx_group.coal,
+	       sizeof(struct hns3_enet_coalesce));
+}
+
+static void hns3_restore_coal(struct hns3_nic_priv *priv)
+{
+	u16 vector_num = priv->vector_num;
+	int i;
+
+	for (i = 0; i < vector_num; i++) {
+		memcpy(&priv->tqp_vector[i].tx_group.coal, &priv->tx_coal,
+		       sizeof(struct hns3_enet_coalesce));
+		memcpy(&priv->tqp_vector[i].rx_group.coal, &priv->rx_coal,
+		       sizeof(struct hns3_enet_coalesce));
+	}
+}
+
 static int hns3_reset_notify_down_enet(struct hnae3_handle *handle)
 {
 	struct hnae3_knic_private_info *kinfo = &handle->kinfo;
@@ -3536,6 +3556,8 @@ static int hns3_reset_notify_init_enet(struct hnae3_handle *handle)
 	/* Carrier off reporting is important to ethtool even BEFORE open */
 	netif_carrier_off(netdev);
 
+	hns3_restore_coal(priv);
+
 	ret = hns3_nic_init_vector_data(priv);
 	if (ret)
 		return ret;
@@ -3563,6 +3585,8 @@ static int hns3_reset_notify_uninit_enet(struct hnae3_handle *handle)
 		return ret;
 	}
 
+	hns3_store_coal(priv);
+
 	ret = hns3_uninit_all_ring(priv);
 	if (ret)
 		netdev_err(netdev, "uninit ring error\n");
@@ -3597,24 +3621,7 @@ static int hns3_reset_notify(struct hnae3_handle *handle,
 	return ret;
 }
 
-static void hns3_restore_coal(struct hns3_nic_priv *priv,
-			      struct hns3_enet_coalesce *tx,
-			      struct hns3_enet_coalesce *rx)
-{
-	u16 vector_num = priv->vector_num;
-	int i;
-
-	for (i = 0; i < vector_num; i++) {
-		memcpy(&priv->tqp_vector[i].tx_group.coal, tx,
-		       sizeof(struct hns3_enet_coalesce));
-		memcpy(&priv->tqp_vector[i].rx_group.coal, rx,
-		       sizeof(struct hns3_enet_coalesce));
-	}
-}
-
-static int hns3_modify_tqp_num(struct net_device *netdev, u16 new_tqp_num,
-			       struct hns3_enet_coalesce *tx,
-			       struct hns3_enet_coalesce *rx)
+static int hns3_modify_tqp_num(struct net_device *netdev, u16 new_tqp_num)
 {
 	struct hns3_nic_priv *priv = netdev_priv(netdev);
 	struct hnae3_handle *h = hns3_get_handle(netdev);
@@ -3632,7 +3639,7 @@ static int hns3_modify_tqp_num(struct net_device *netdev, u16 new_tqp_num,
 	if (ret)
 		goto err_alloc_vector;
 
-	hns3_restore_coal(priv, tx, rx);
+	hns3_restore_coal(priv);
 
 	ret = hns3_nic_init_vector_data(priv);
 	if (ret)
@@ -3664,7 +3671,6 @@ int hns3_set_channels(struct net_device *netdev,
 	struct hns3_nic_priv *priv = netdev_priv(netdev);
 	struct hnae3_handle *h = hns3_get_handle(netdev);
 	struct hnae3_knic_private_info *kinfo = &h->kinfo;
-	struct hns3_enet_coalesce tx_coal, rx_coal;
 	bool if_running = netif_running(netdev);
 	u32 new_tqp_num = ch->combined_count;
 	u16 org_tqp_num;
@@ -3696,15 +3702,7 @@ int hns3_set_channels(struct net_device *netdev,
 		goto open_netdev;
 	}
 
-	/* Changing the tqp num may also change the vector num,
-	 * ethtool only support setting and querying one coal
-	 * configuation for now, so save the vector 0' coal
-	 * configuation here in order to restore it.
-	 */
-	memcpy(&tx_coal, &priv->tqp_vector[0].tx_group.coal,
-	       sizeof(struct hns3_enet_coalesce));
-	memcpy(&rx_coal, &priv->tqp_vector[0].rx_group.coal,
-	       sizeof(struct hns3_enet_coalesce));
+	hns3_store_coal(priv);
 
 	hns3_nic_dealloc_vector_data(priv);
 
@@ -3712,10 +3710,9 @@ int hns3_set_channels(struct net_device *netdev,
 	hns3_put_ring_config(priv);
 
 	org_tqp_num = h->kinfo.num_tqps;
-	ret = hns3_modify_tqp_num(netdev, new_tqp_num, &tx_coal, &rx_coal);
+	ret = hns3_modify_tqp_num(netdev, new_tqp_num);
 	if (ret) {
-		ret = hns3_modify_tqp_num(netdev, org_tqp_num,
-					  &tx_coal, &rx_coal);
+		ret = hns3_modify_tqp_num(netdev, org_tqp_num);
 		if (ret) {
 			/* If revert to old tqp failed, fatal error occurred */
 			dev_err(&netdev->dev,
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h
index 4a56c3d..27d704f 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h
@@ -543,6 +543,8 @@ struct hns3_nic_priv {
 	/* Vxlan/Geneve information */
 	struct hns3_udp_tunnel udp_tnl[HNS3_UDP_TNL_MAX];
 	unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)];
+	struct hns3_enet_coalesce tx_coal;
+	struct hns3_enet_coalesce rx_coal;
 };
 
 union l3_hdr_info {
-- 
2.7.4

^ permalink raw reply related

* [PATCH net-next 09/10] net: hns3: Modify hns3_get_max_available_channels
From: Salil Mehta @ 2018-09-26 18:28 UTC (permalink / raw)
  To: davem
  Cc: salil.mehta, yisen.zhuang, lipeng321, mehta.salil, netdev,
	linux-kernel, linuxarm, Huazhong Tan, Yunsheng Lin
In-Reply-To: <20180926182840.28392-1-salil.mehta@huawei.com>

From: Huazhong Tan <tanhuazhong@huawei.com>

The current hns3_get_max_available_channels returns the total number
of queues for the device, which makes ethtool -L set the number of queues
per channel queues incorrectly, so hns3_get_max_available_channels should
return the maximum available number of queues per channel, depending on
the total number of queues allocated and the hardware configurations.

Signed-off-by: Huazhong Tan <tanhuazhong@huawei.com>
Signed-off-by: Yunsheng Lin <linyunsheng@huawei.com>
Signed-off-by: Peng Li <lipeng321@huawei.com>
Signed-off-by: Salil Mehta <salil.mehta@huawei.com>
---
 drivers/net/ethernet/hisilicon/hns3/hnae3.h               |  2 +-
 drivers/net/ethernet/hisilicon/hns3/hns3_enet.c           | 14 ++++++++------
 drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c   | 10 ++--------
 drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c |  4 ++--
 4 files changed, 13 insertions(+), 17 deletions(-)

diff --git a/drivers/net/ethernet/hisilicon/hns3/hnae3.h b/drivers/net/ethernet/hisilicon/hns3/hnae3.h
index 8581e16..dea422c 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hnae3.h
+++ b/drivers/net/ethernet/hisilicon/hns3/hnae3.h
@@ -403,7 +403,7 @@ struct hnae3_ae_ops {
 	void (*get_channels)(struct hnae3_handle *handle,
 			     struct ethtool_channels *ch);
 	void (*get_tqps_and_rss_info)(struct hnae3_handle *h,
-				      u16 *free_tqps, u16 *max_rss_size);
+				      u16 *alloc_tqps, u16 *max_rss_size);
 	int (*set_channels)(struct hnae3_handle *handle, u32 new_tqps_num);
 	void (*get_flowctrl_adv)(struct hnae3_handle *handle,
 				 u32 *flowctrl_adv);
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c
index 545777a..8273c03 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c
@@ -307,12 +307,12 @@ static int hns3_nic_set_real_num_queue(struct net_device *netdev)
 
 static u16 hns3_get_max_available_channels(struct hnae3_handle *h)
 {
-	u16 free_tqps, max_rss_size, max_tqps;
+	u16 alloc_tqps, max_rss_size, rss_size;
 
-	h->ae_algo->ops->get_tqps_and_rss_info(h, &free_tqps, &max_rss_size);
-	max_tqps = h->kinfo.num_tc * max_rss_size;
+	h->ae_algo->ops->get_tqps_and_rss_info(h, &alloc_tqps, &max_rss_size);
+	rss_size = alloc_tqps / h->kinfo.num_tc;
 
-	return min_t(u16, max_tqps, (free_tqps + h->kinfo.num_tqps));
+	return min_t(u16, rss_size, max_rss_size);
 }
 
 static int hns3_nic_net_up(struct net_device *netdev)
@@ -3164,12 +3164,14 @@ static void hns3_nic_set_priv_ops(struct net_device *netdev)
 static int hns3_client_init(struct hnae3_handle *handle)
 {
 	struct pci_dev *pdev = handle->pdev;
+	u16 alloc_tqps, max_rss_size;
 	struct hns3_nic_priv *priv;
 	struct net_device *netdev;
 	int ret;
 
-	netdev = alloc_etherdev_mq(sizeof(struct hns3_nic_priv),
-				   hns3_get_max_available_channels(handle));
+	handle->ae_algo->ops->get_tqps_and_rss_info(handle, &alloc_tqps,
+						    &max_rss_size);
+	netdev = alloc_etherdev_mq(sizeof(struct hns3_nic_priv), alloc_tqps);
 	if (!netdev)
 		return -ENOMEM;
 
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
index d8b0d16..67b131d 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
@@ -5659,18 +5659,12 @@ static void hclge_get_channels(struct hnae3_handle *handle,
 }
 
 static void hclge_get_tqps_and_rss_info(struct hnae3_handle *handle,
-					u16 *free_tqps, u16 *max_rss_size)
+					u16 *alloc_tqps, u16 *max_rss_size)
 {
 	struct hclge_vport *vport = hclge_get_vport(handle);
 	struct hclge_dev *hdev = vport->back;
-	u16 temp_tqps = 0;
-	int i;
 
-	for (i = 0; i < hdev->num_tqps; i++) {
-		if (!hdev->htqp[i].alloced)
-			temp_tqps++;
-	}
-	*free_tqps = temp_tqps;
+	*alloc_tqps = vport->alloc_tqps;
 	*max_rss_size = hdev->rss_size_max;
 }
 
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c
index 9781931..8f858cb 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c
@@ -1975,11 +1975,11 @@ static void hclgevf_get_channels(struct hnae3_handle *handle,
 }
 
 static void hclgevf_get_tqps_and_rss_info(struct hnae3_handle *handle,
-					  u16 *free_tqps, u16 *max_rss_size)
+					  u16 *alloc_tqps, u16 *max_rss_size)
 {
 	struct hclgevf_dev *hdev = hclgevf_ae_get_hdev(handle);
 
-	*free_tqps = 0;
+	*alloc_tqps = hdev->num_tqps;
 	*max_rss_size = hdev->rss_size_max;
 }
 
-- 
2.7.4

^ permalink raw reply related

* [PATCH net-next 08/10] net: hns3: Change return type of hclge_tm_schd_info_update()
From: Salil Mehta @ 2018-09-26 18:28 UTC (permalink / raw)
  To: davem
  Cc: salil.mehta, yisen.zhuang, lipeng321, mehta.salil, netdev,
	linux-kernel, linuxarm, Huazhong Tan, Yunsheng Lin
In-Reply-To: <20180926182840.28392-1-salil.mehta@huawei.com>

From: Huazhong Tan <tanhuazhong@huawei.com>

hclge_tm_schd_info_update should return an error when num_tc is greater
than alloc_tqps.

This patch changes the return type of hnae3_register_ae_algo from void
to int.

Signed-off-by: Huazhong Tan <tanhuazhong@huawei.com>
Signed-off-by: Yunsheng Lin <linyunsheng@huawei.com>
Signed-off-by: Peng Li <lipeng321@huawei.com>
Signed-off-by: Salil Mehta <salil.mehta@huawei.com>
---
 drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_dcb.c | 8 ++++++--
 drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c  | 9 ++++++++-
 drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.h  | 2 +-
 3 files changed, 15 insertions(+), 4 deletions(-)

diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_dcb.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_dcb.c
index 92f1938..e72f724 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_dcb.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_dcb.c
@@ -184,7 +184,9 @@ static int hclge_ieee_setets(struct hnae3_handle *h, struct ieee_ets *ets)
 	if (ret)
 		return ret;
 
-	hclge_tm_schd_info_update(hdev, num_tc);
+	ret = hclge_tm_schd_info_update(hdev, num_tc);
+	if (ret)
+		return ret;
 
 	ret = hclge_ieee_ets_to_tm_info(hdev, ets);
 	if (ret)
@@ -310,7 +312,9 @@ static int hclge_setup_tc(struct hnae3_handle *h, u8 tc, u8 *prio_tc)
 		return -EINVAL;
 	}
 
-	hclge_tm_schd_info_update(hdev, tc);
+	ret = hclge_tm_schd_info_update(hdev, tc);
+	if (ret)
+		return ret;
 
 	ret = hclge_tm_prio_tc_info_update(hdev, prio_tc);
 	if (ret)
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c
index ab7280d..aa5cb98 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c
@@ -1280,10 +1280,15 @@ int hclge_tm_prio_tc_info_update(struct hclge_dev *hdev, u8 *prio_tc)
 	return 0;
 }
 
-void hclge_tm_schd_info_update(struct hclge_dev *hdev, u8 num_tc)
+int hclge_tm_schd_info_update(struct hclge_dev *hdev, u8 num_tc)
 {
 	u8 i, bit_map = 0;
 
+	for (i = 0; i < hdev->num_alloc_vport; i++) {
+		if (num_tc > hdev->vport[i].alloc_tqps)
+			return -EINVAL;
+	}
+
 	hdev->tm_info.num_tc = num_tc;
 
 	for (i = 0; i < hdev->tm_info.num_tc; i++)
@@ -1297,6 +1302,8 @@ void hclge_tm_schd_info_update(struct hclge_dev *hdev, u8 num_tc)
 	hdev->hw_tc_map = bit_map;
 
 	hclge_tm_schd_info_init(hdev);
+
+	return 0;
 }
 
 int hclge_tm_init_hw(struct hclge_dev *hdev)
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.h
index e1568b8..25eef13 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.h
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.h
@@ -132,7 +132,7 @@ int hclge_tm_schd_init(struct hclge_dev *hdev);
 int hclge_pause_setup_hw(struct hclge_dev *hdev);
 int hclge_tm_schd_mode_hw(struct hclge_dev *hdev);
 int hclge_tm_prio_tc_info_update(struct hclge_dev *hdev, u8 *prio_tc);
-void hclge_tm_schd_info_update(struct hclge_dev *hdev, u8 num_tc);
+int hclge_tm_schd_info_update(struct hclge_dev *hdev, u8 num_tc);
 int hclge_tm_dwrr_cfg(struct hclge_dev *hdev);
 int hclge_tm_map_cfg(struct hclge_dev *hdev);
 int hclge_tm_init_hw(struct hclge_dev *hdev);
-- 
2.7.4

^ permalink raw reply related

* [PATCH net-next 07/10] net: hns3: Fix for netdev not up problem when setting mtu
From: Salil Mehta @ 2018-09-26 18:28 UTC (permalink / raw)
  To: davem
  Cc: salil.mehta, yisen.zhuang, lipeng321, mehta.salil, netdev,
	linux-kernel, linuxarm, Yunsheng Lin
In-Reply-To: <20180926182840.28392-1-salil.mehta@huawei.com>

From: Yunsheng Lin <linyunsheng@huawei.com>

Currently hns3_nic_change_mtu will try to down the netdev before
setting mtu, and it does not up the netdev when the setting fails,
which causes netdev not up problem.

This patch fixes it by not returning when the setting fails.

Fixes: a8e8b7ff3517 ("net: hns3: Add support to change MTU in HNS3 hardware")
Signed-off-by: Yunsheng Lin <linyunsheng@huawei.com>
Signed-off-by: Peng Li <lipeng321@huawei.com>
Signed-off-by: Salil Mehta <salil.mehta@huawei.com>
---
 drivers/net/ethernet/hisilicon/hns3/hns3_enet.c | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c
index 3fc3822..545777a 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c
@@ -1491,13 +1491,11 @@ static int hns3_nic_change_mtu(struct net_device *netdev, int new_mtu)
 	}
 
 	ret = h->ae_algo->ops->set_mtu(h, new_mtu);
-	if (ret) {
+	if (ret)
 		netdev_err(netdev, "failed to change MTU in hardware %d\n",
 			   ret);
-		return ret;
-	}
-
-	netdev->mtu = new_mtu;
+	else
+		netdev->mtu = new_mtu;
 
 	/* if the netdev was running earlier, bring it up again */
 	if (if_running && hns3_nic_net_open(netdev))
-- 
2.7.4

^ permalink raw reply related

* [PATCH net-next 06/10] net: hns3: Fix for packet buffer setting bug
From: Salil Mehta @ 2018-09-26 18:28 UTC (permalink / raw)
  To: davem
  Cc: salil.mehta, yisen.zhuang, lipeng321, mehta.salil, netdev,
	linux-kernel, linuxarm, Yunsheng Lin
In-Reply-To: <20180926182840.28392-1-salil.mehta@huawei.com>

From: Yunsheng Lin <linyunsheng@huawei.com>

The hardware expects a unit of 128 bytes when setting
packet buffer. When calculating the packet buffer size,
hclge_rx_buffer_calc does not round up the size as a unit
of 128 byte, which may casue packet lost problem when stress
testing.

This patch fixes it by rounding up packet size when calculating.

Fixes: 46a3df9f9718 ("net: hns3: Add HNS3 Acceleration Engine & Compatibility Layer Support")
Signed-off-by: Yunsheng Lin <linyunsheng@huawei.com>
Signed-off-by: Peng Li <lipeng321@huawei.com>
Signed-off-by: Salil Mehta <salil.mehta@huawei.com>
---
 drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
index 72bc8c3..d8b0d16 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
@@ -1356,11 +1356,13 @@ static int hclge_tx_buffer_calc(struct hclge_dev *hdev,
 static int hclge_rx_buffer_calc(struct hclge_dev *hdev,
 				struct hclge_pkt_buf_alloc *buf_alloc)
 {
-	u32 rx_all = hdev->pkt_buf_size;
+#define HCLGE_BUF_SIZE_UNIT	128
+	u32 rx_all = hdev->pkt_buf_size, aligned_mps;
 	int no_pfc_priv_num, pfc_priv_num;
 	struct hclge_priv_buf *priv;
 	int i;
 
+	aligned_mps = round_up(hdev->mps, HCLGE_BUF_SIZE_UNIT);
 	rx_all -= hclge_get_tx_buff_alloced(buf_alloc);
 
 	/* When DCB is not supported, rx private
@@ -1379,13 +1381,13 @@ static int hclge_rx_buffer_calc(struct hclge_dev *hdev,
 		if (hdev->hw_tc_map & BIT(i)) {
 			priv->enable = 1;
 			if (hdev->tm_info.hw_pfc_map & BIT(i)) {
-				priv->wl.low = hdev->mps;
-				priv->wl.high = priv->wl.low + hdev->mps;
+				priv->wl.low = aligned_mps;
+				priv->wl.high = priv->wl.low + aligned_mps;
 				priv->buf_size = priv->wl.high +
 						HCLGE_DEFAULT_DV;
 			} else {
 				priv->wl.low = 0;
-				priv->wl.high = 2 * hdev->mps;
+				priv->wl.high = 2 * aligned_mps;
 				priv->buf_size = priv->wl.high;
 			}
 		} else {
@@ -1417,11 +1419,11 @@ static int hclge_rx_buffer_calc(struct hclge_dev *hdev,
 
 		if (hdev->tm_info.hw_pfc_map & BIT(i)) {
 			priv->wl.low = 128;
-			priv->wl.high = priv->wl.low + hdev->mps;
+			priv->wl.high = priv->wl.low + aligned_mps;
 			priv->buf_size = priv->wl.high + HCLGE_DEFAULT_DV;
 		} else {
 			priv->wl.low = 0;
-			priv->wl.high = hdev->mps;
+			priv->wl.high = aligned_mps;
 			priv->buf_size = priv->wl.high;
 		}
 	}
-- 
2.7.4

^ permalink raw reply related

* [PATCH net-next 05/10] net: hns3: Add serdes parallel inner loopback support
From: Salil Mehta @ 2018-09-26 18:28 UTC (permalink / raw)
  To: davem
  Cc: salil.mehta, yisen.zhuang, lipeng321, mehta.salil, netdev,
	linux-kernel, linuxarm, Fuyun Liang
In-Reply-To: <20180926182840.28392-1-salil.mehta@huawei.com>

From: Fuyun Liang <liangfuyun1@huawei.com>

This patch adds serdes parallel inner loopback support for self test.

Signed-off-by: Fuyun Liang <liangfuyun1@huawei.com>
Signed-off-by: Peng Li <lipeng321@huawei.com>
Signed-off-by: Salil Mehta <salil.mehta@huawei.com>
---
 drivers/net/ethernet/hisilicon/hns3/hnae3.h        |  6 ++-
 drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c | 16 +++++--
 .../net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h |  1 +
 .../ethernet/hisilicon/hns3/hns3pf/hclge_main.c    | 54 ++++++++++++++++------
 4 files changed, 57 insertions(+), 20 deletions(-)

diff --git a/drivers/net/ethernet/hisilicon/hns3/hnae3.h b/drivers/net/ethernet/hisilicon/hns3/hnae3.h
index 27155ea..8581e16 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hnae3.h
+++ b/drivers/net/ethernet/hisilicon/hns3/hnae3.h
@@ -85,7 +85,8 @@ struct hnae3_queue {
 /*hnae3 loop mode*/
 enum hnae3_loop {
 	HNAE3_LOOP_APP,
-	HNAE3_LOOP_SERDES,
+	HNAE3_LOOP_SERIAL_SERDES,
+	HNAE3_LOOP_PARALLEL_SERDES,
 	HNAE3_LOOP_PHY,
 	HNAE3_LOOP_NONE,
 };
@@ -480,8 +481,9 @@ struct hnae3_unic_private_info {
 
 #define HNAE3_SUPPORT_APP_LOOPBACK    BIT(0)
 #define HNAE3_SUPPORT_PHY_LOOPBACK    BIT(1)
-#define HNAE3_SUPPORT_SERDES_LOOPBACK BIT(2)
+#define HNAE3_SUPPORT_SERDES_SERIAL_LOOPBACK	BIT(2)
 #define HNAE3_SUPPORT_VF	      BIT(3)
+#define HNAE3_SUPPORT_SERDES_PARALLEL_LOOPBACK	BIT(4)
 
 struct hnae3_handle {
 	struct hnae3_client *client;
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c b/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c
index 75ebd2e..e959b7b 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c
@@ -53,7 +53,7 @@ static const struct hns3_stats hns3_rxq_stats[] = {
 
 #define HNS3_TQP_STATS_COUNT (HNS3_TXQ_STATS_COUNT + HNS3_RXQ_STATS_COUNT)
 
-#define HNS3_SELF_TEST_TYPE_NUM		2
+#define HNS3_SELF_TEST_TPYE_NUM         3
 #define HNS3_NIC_LB_TEST_PKT_NUM	1
 #define HNS3_NIC_LB_TEST_RING_ID	0
 #define HNS3_NIC_LB_TEST_PACKET_SIZE	128
@@ -78,7 +78,8 @@ static int hns3_lp_setup(struct net_device *ndev, enum hnae3_loop loop, bool en)
 		return -EOPNOTSUPP;
 
 	switch (loop) {
-	case HNAE3_LOOP_SERDES:
+	case HNAE3_LOOP_SERIAL_SERDES:
+	case HNAE3_LOOP_PARALLEL_SERDES:
 	case HNAE3_LOOP_APP:
 		ret = h->ae_algo->ops->set_loopback(h, loop, en);
 		break;
@@ -290,9 +291,14 @@ static void hns3_self_test(struct net_device *ndev,
 	st_param[HNAE3_LOOP_APP][1] =
 			h->flags & HNAE3_SUPPORT_APP_LOOPBACK;
 
-	st_param[HNAE3_LOOP_SERDES][0] = HNAE3_LOOP_SERDES;
-	st_param[HNAE3_LOOP_SERDES][1] =
-			h->flags & HNAE3_SUPPORT_SERDES_LOOPBACK;
+	st_param[HNAE3_LOOP_SERIAL_SERDES][0] = HNAE3_LOOP_SERIAL_SERDES;
+	st_param[HNAE3_LOOP_SERIAL_SERDES][1] =
+			h->flags & HNAE3_SUPPORT_SERDES_SERIAL_LOOPBACK;
+
+	st_param[HNAE3_LOOP_PARALLEL_SERDES][0] =
+			HNAE3_LOOP_PARALLEL_SERDES;
+	st_param[HNAE3_LOOP_PARALLEL_SERDES][1] =
+			h->flags & HNAE3_SUPPORT_SERDES_PARALLEL_LOOPBACK;
 
 	if (if_running)
 		ndev->netdev_ops->ndo_stop(ndev);
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h
index 821d4c2..842dc3f 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h
@@ -778,6 +778,7 @@ struct hclge_reset_cmd {
 };
 
 #define HCLGE_CMD_SERDES_SERIAL_INNER_LOOP_B	BIT(0)
+#define HCLGE_CMD_SERDES_PARALLEL_INNER_LOOP_B	BIT(2)
 #define HCLGE_CMD_SERDES_DONE_B			BIT(0)
 #define HCLGE_CMD_SERDES_SUCCESS_B		BIT(1)
 struct hclge_serdes_lb_cmd {
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
index a0f6329..72bc8c3 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
@@ -50,7 +50,8 @@ MODULE_DEVICE_TABLE(pci, ae_algo_pci_tbl);
 
 static const char hns3_nic_test_strs[][ETH_GSTRING_LEN] = {
 	"App    Loopback test",
-	"Serdes Loopback test",
+	"Serdes serial Loopback test",
+	"Serdes parallel Loopback test",
 	"Phy    Loopback test"
 };
 
@@ -475,7 +476,10 @@ static void hclge_update_stats(struct hnae3_handle *handle,
 
 static int hclge_get_sset_count(struct hnae3_handle *handle, int stringset)
 {
-#define HCLGE_LOOPBACK_TEST_FLAGS 0x7
+#define HCLGE_LOOPBACK_TEST_FLAGS (HNAE3_SUPPORT_APP_LOOPBACK |\
+		HNAE3_SUPPORT_PHY_LOOPBACK |\
+		HNAE3_SUPPORT_SERDES_SERIAL_LOOPBACK |\
+		HNAE3_SUPPORT_SERDES_PARALLEL_LOOPBACK)
 
 	struct hclge_vport *vport = hclge_get_vport(handle);
 	struct hclge_dev *hdev = vport->back;
@@ -489,15 +493,17 @@ static int hclge_get_sset_count(struct hnae3_handle *handle, int stringset)
 	if (stringset == ETH_SS_TEST) {
 		/* clear loopback bit flags at first */
 		handle->flags = (handle->flags & (~HCLGE_LOOPBACK_TEST_FLAGS));
-		if (hdev->hw.mac.speed == HCLGE_MAC_SPEED_10M ||
+		if (hdev->pdev->revision >= HNAE3_REVISION_ID_21 ||
+		    hdev->hw.mac.speed == HCLGE_MAC_SPEED_10M ||
 		    hdev->hw.mac.speed == HCLGE_MAC_SPEED_100M ||
 		    hdev->hw.mac.speed == HCLGE_MAC_SPEED_1G) {
 			count += 1;
 			handle->flags |= HNAE3_SUPPORT_APP_LOOPBACK;
 		}
 
-		count++;
-		handle->flags |= HNAE3_SUPPORT_SERDES_LOOPBACK;
+		count += 2;
+		handle->flags |= HNAE3_SUPPORT_SERDES_SERIAL_LOOPBACK;
+		handle->flags |= HNAE3_SUPPORT_SERDES_PARALLEL_LOOPBACK;
 	} else if (stringset == ETH_SS_STATS) {
 		count = ARRAY_SIZE(g_mac_stats_string) +
 			hclge_tqps_get_sset_count(handle, stringset);
@@ -527,9 +533,15 @@ static void hclge_get_strings(struct hnae3_handle *handle,
 			       ETH_GSTRING_LEN);
 			p += ETH_GSTRING_LEN;
 		}
-		if (handle->flags & HNAE3_SUPPORT_SERDES_LOOPBACK) {
+		if (handle->flags & HNAE3_SUPPORT_SERDES_SERIAL_LOOPBACK) {
 			memcpy(p,
-			       hns3_nic_test_strs[HNAE3_LOOP_SERDES],
+			       hns3_nic_test_strs[HNAE3_LOOP_SERIAL_SERDES],
+			       ETH_GSTRING_LEN);
+			p += ETH_GSTRING_LEN;
+		}
+		if (handle->flags & HNAE3_SUPPORT_SERDES_PARALLEL_LOOPBACK) {
+			memcpy(p,
+			       hns3_nic_test_strs[HNAE3_LOOP_PARALLEL_SERDES],
 			       ETH_GSTRING_LEN);
 			p += ETH_GSTRING_LEN;
 		}
@@ -3381,22 +3393,37 @@ static int hclge_set_app_loopback(struct hclge_dev *hdev, bool en)
 	return ret;
 }
 
-static int hclge_set_serdes_loopback(struct hclge_dev *hdev, bool en)
+static int hclge_set_serdes_loopback(struct hclge_dev *hdev, bool en,
+				     enum hnae3_loop loop_mode)
 {
 #define HCLGE_SERDES_RETRY_MS	10
 #define HCLGE_SERDES_RETRY_NUM	100
 	struct hclge_serdes_lb_cmd *req;
 	struct hclge_desc desc;
 	int ret, i = 0;
+	u8 loop_mode_b;
 
 	req = (struct hclge_serdes_lb_cmd *)desc.data;
 	hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_SERDES_LOOPBACK, false);
 
+	switch (loop_mode) {
+	case HNAE3_LOOP_SERIAL_SERDES:
+		loop_mode_b = HCLGE_CMD_SERDES_SERIAL_INNER_LOOP_B;
+		break;
+	case HNAE3_LOOP_PARALLEL_SERDES:
+		loop_mode_b = HCLGE_CMD_SERDES_PARALLEL_INNER_LOOP_B;
+		break;
+	default:
+		dev_err(&hdev->pdev->dev,
+			"unsupported serdes loopback mode %d\n", loop_mode);
+		return -ENOTSUPP;
+	}
+
 	if (en) {
-		req->enable = HCLGE_CMD_SERDES_SERIAL_INNER_LOOP_B;
-		req->mask = HCLGE_CMD_SERDES_SERIAL_INNER_LOOP_B;
+		req->enable = loop_mode_b;
+		req->mask = loop_mode_b;
 	} else {
-		req->mask = HCLGE_CMD_SERDES_SERIAL_INNER_LOOP_B;
+		req->mask = loop_mode_b;
 	}
 
 	ret = hclge_cmd_send(&hdev->hw, &desc, 1);
@@ -3462,8 +3489,9 @@ static int hclge_set_loopback(struct hnae3_handle *handle,
 	case HNAE3_LOOP_APP:
 		ret = hclge_set_app_loopback(hdev, en);
 		break;
-	case HNAE3_LOOP_SERDES:
-		ret = hclge_set_serdes_loopback(hdev, en);
+	case HNAE3_LOOP_SERIAL_SERDES:
+	case HNAE3_LOOP_PARALLEL_SERDES:
+		ret = hclge_set_serdes_loopback(hdev, en, loop_mode);
 		break;
 	default:
 		ret = -ENOTSUPP;
-- 
2.7.4

^ permalink raw reply related

* [PATCH net-next 04/10] net: hns3: Rename mac loopback to app loopback
From: Salil Mehta @ 2018-09-26 18:28 UTC (permalink / raw)
  To: davem
  Cc: salil.mehta, yisen.zhuang, lipeng321, mehta.salil, netdev,
	linux-kernel, linuxarm, Fuyun Liang
In-Reply-To: <20180926182840.28392-1-salil.mehta@huawei.com>

From: Fuyun Liang <liangfuyun1@huawei.com>

In fact, our implementation of mac loopback is the implementation of app
loopback now. Current name is wrong. This patch renames mac loopback to
app loopback.

Signed-off-by: Fuyun Liang <liangfuyun1@huawei.com>
Signed-off-by: Peng Li <lipeng321@huawei.com>
Signed-off-by: Salil Mehta <salil.mehta@huawei.com>
---
 drivers/net/ethernet/hisilicon/hns3/hnae3.h             |  4 ++--
 drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c      |  8 ++++----
 drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c | 14 +++++++-------
 3 files changed, 13 insertions(+), 13 deletions(-)

diff --git a/drivers/net/ethernet/hisilicon/hns3/hnae3.h b/drivers/net/ethernet/hisilicon/hns3/hnae3.h
index 03d7878..27155ea 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hnae3.h
+++ b/drivers/net/ethernet/hisilicon/hns3/hnae3.h
@@ -84,7 +84,7 @@ struct hnae3_queue {
 
 /*hnae3 loop mode*/
 enum hnae3_loop {
-	HNAE3_LOOP_MAC,
+	HNAE3_LOOP_APP,
 	HNAE3_LOOP_SERDES,
 	HNAE3_LOOP_PHY,
 	HNAE3_LOOP_NONE,
@@ -478,7 +478,7 @@ struct hnae3_unic_private_info {
 	struct hnae3_queue **tqp;  /* array base of all TQPs of this instance */
 };
 
-#define HNAE3_SUPPORT_MAC_LOOPBACK    BIT(0)
+#define HNAE3_SUPPORT_APP_LOOPBACK    BIT(0)
 #define HNAE3_SUPPORT_PHY_LOOPBACK    BIT(1)
 #define HNAE3_SUPPORT_SERDES_LOOPBACK BIT(2)
 #define HNAE3_SUPPORT_VF	      BIT(3)
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c b/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c
index 86587cf..75ebd2e 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c
@@ -79,7 +79,7 @@ static int hns3_lp_setup(struct net_device *ndev, enum hnae3_loop loop, bool en)
 
 	switch (loop) {
 	case HNAE3_LOOP_SERDES:
-	case HNAE3_LOOP_MAC:
+	case HNAE3_LOOP_APP:
 		ret = h->ae_algo->ops->set_loopback(h, loop, en);
 		break;
 	default:
@@ -286,9 +286,9 @@ static void hns3_self_test(struct net_device *ndev,
 	if (eth_test->flags != ETH_TEST_FL_OFFLINE)
 		return;
 
-	st_param[HNAE3_LOOP_MAC][0] = HNAE3_LOOP_MAC;
-	st_param[HNAE3_LOOP_MAC][1] =
-			h->flags & HNAE3_SUPPORT_MAC_LOOPBACK;
+	st_param[HNAE3_LOOP_APP][0] = HNAE3_LOOP_APP;
+	st_param[HNAE3_LOOP_APP][1] =
+			h->flags & HNAE3_SUPPORT_APP_LOOPBACK;
 
 	st_param[HNAE3_LOOP_SERDES][0] = HNAE3_LOOP_SERDES;
 	st_param[HNAE3_LOOP_SERDES][1] =
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
index 33e97fc..a0f6329 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
@@ -49,7 +49,7 @@ static const struct pci_device_id ae_algo_pci_tbl[] = {
 MODULE_DEVICE_TABLE(pci, ae_algo_pci_tbl);
 
 static const char hns3_nic_test_strs[][ETH_GSTRING_LEN] = {
-	"Mac    Loopback test",
+	"App    Loopback test",
 	"Serdes Loopback test",
 	"Phy    Loopback test"
 };
@@ -493,7 +493,7 @@ static int hclge_get_sset_count(struct hnae3_handle *handle, int stringset)
 		    hdev->hw.mac.speed == HCLGE_MAC_SPEED_100M ||
 		    hdev->hw.mac.speed == HCLGE_MAC_SPEED_1G) {
 			count += 1;
-			handle->flags |= HNAE3_SUPPORT_MAC_LOOPBACK;
+			handle->flags |= HNAE3_SUPPORT_APP_LOOPBACK;
 		}
 
 		count++;
@@ -521,9 +521,9 @@ static void hclge_get_strings(struct hnae3_handle *handle,
 					   p);
 		p = hclge_tqps_get_strings(handle, p);
 	} else if (stringset == ETH_SS_TEST) {
-		if (handle->flags & HNAE3_SUPPORT_MAC_LOOPBACK) {
+		if (handle->flags & HNAE3_SUPPORT_APP_LOOPBACK) {
 			memcpy(p,
-			       hns3_nic_test_strs[HNAE3_LOOP_MAC],
+			       hns3_nic_test_strs[HNAE3_LOOP_APP],
 			       ETH_GSTRING_LEN);
 			p += ETH_GSTRING_LEN;
 		}
@@ -3345,7 +3345,7 @@ static void hclge_cfg_mac_mode(struct hclge_dev *hdev, bool enable)
 			"mac enable fail, ret =%d.\n", ret);
 }
 
-static int hclge_set_mac_loopback(struct hclge_dev *hdev, bool en)
+static int hclge_set_app_loopback(struct hclge_dev *hdev, bool en)
 {
 	struct hclge_config_mac_mode_cmd *req;
 	struct hclge_desc desc;
@@ -3459,8 +3459,8 @@ static int hclge_set_loopback(struct hnae3_handle *handle,
 	int i, ret;
 
 	switch (loop_mode) {
-	case HNAE3_LOOP_MAC:
-		ret = hclge_set_mac_loopback(hdev, en);
+	case HNAE3_LOOP_APP:
+		ret = hclge_set_app_loopback(hdev, en);
 		break;
 	case HNAE3_LOOP_SERDES:
 		ret = hclge_set_serdes_loopback(hdev, en);
-- 
2.7.4

^ permalink raw reply related

* [PATCH net-next 03/10] net: hns3: Rename loop mode
From: Salil Mehta @ 2018-09-26 18:28 UTC (permalink / raw)
  To: davem
  Cc: salil.mehta, yisen.zhuang, lipeng321, mehta.salil, netdev,
	linux-kernel, linuxarm, Fuyun Liang
In-Reply-To: <20180926182840.28392-1-salil.mehta@huawei.com>

From: Fuyun Liang <liangfuyun1@huawei.com>

Our loop mode includes mac loop, serdes loop and phy loop. Not all of them
are related with mac. This patch corrects their names.

Signed-off-by: Fuyun Liang <liangfuyun1@huawei.com>
Signed-off-by: Peng Li <lipeng321@huawei.com>
Signed-off-by: Salil Mehta <salil.mehta@huawei.com>
---
 drivers/net/ethernet/hisilicon/hns3/hnae3.h             |  8 ++++----
 drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c      | 12 ++++++------
 drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c | 10 +++++-----
 3 files changed, 15 insertions(+), 15 deletions(-)

diff --git a/drivers/net/ethernet/hisilicon/hns3/hnae3.h b/drivers/net/ethernet/hisilicon/hns3/hnae3.h
index 564afd4..03d7878 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hnae3.h
+++ b/drivers/net/ethernet/hisilicon/hns3/hnae3.h
@@ -84,10 +84,10 @@ struct hnae3_queue {
 
 /*hnae3 loop mode*/
 enum hnae3_loop {
-	HNAE3_MAC_INTER_LOOP_MAC,
-	HNAE3_MAC_INTER_LOOP_SERDES,
-	HNAE3_MAC_INTER_LOOP_PHY,
-	HNAE3_MAC_LOOP_NONE,
+	HNAE3_LOOP_MAC,
+	HNAE3_LOOP_SERDES,
+	HNAE3_LOOP_PHY,
+	HNAE3_LOOP_NONE,
 };
 
 enum hnae3_client_type {
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c b/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c
index 8803a87..86587cf 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c
@@ -78,8 +78,8 @@ static int hns3_lp_setup(struct net_device *ndev, enum hnae3_loop loop, bool en)
 		return -EOPNOTSUPP;
 
 	switch (loop) {
-	case HNAE3_MAC_INTER_LOOP_SERDES:
-	case HNAE3_MAC_INTER_LOOP_MAC:
+	case HNAE3_LOOP_SERDES:
+	case HNAE3_LOOP_MAC:
 		ret = h->ae_algo->ops->set_loopback(h, loop, en);
 		break;
 	default:
@@ -286,12 +286,12 @@ static void hns3_self_test(struct net_device *ndev,
 	if (eth_test->flags != ETH_TEST_FL_OFFLINE)
 		return;
 
-	st_param[HNAE3_MAC_INTER_LOOP_MAC][0] = HNAE3_MAC_INTER_LOOP_MAC;
-	st_param[HNAE3_MAC_INTER_LOOP_MAC][1] =
+	st_param[HNAE3_LOOP_MAC][0] = HNAE3_LOOP_MAC;
+	st_param[HNAE3_LOOP_MAC][1] =
 			h->flags & HNAE3_SUPPORT_MAC_LOOPBACK;
 
-	st_param[HNAE3_MAC_INTER_LOOP_SERDES][0] = HNAE3_MAC_INTER_LOOP_SERDES;
-	st_param[HNAE3_MAC_INTER_LOOP_SERDES][1] =
+	st_param[HNAE3_LOOP_SERDES][0] = HNAE3_LOOP_SERDES;
+	st_param[HNAE3_LOOP_SERDES][1] =
 			h->flags & HNAE3_SUPPORT_SERDES_LOOPBACK;
 
 	if (if_running)
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
index 2de5a00..33e97fc 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
@@ -523,19 +523,19 @@ static void hclge_get_strings(struct hnae3_handle *handle,
 	} else if (stringset == ETH_SS_TEST) {
 		if (handle->flags & HNAE3_SUPPORT_MAC_LOOPBACK) {
 			memcpy(p,
-			       hns3_nic_test_strs[HNAE3_MAC_INTER_LOOP_MAC],
+			       hns3_nic_test_strs[HNAE3_LOOP_MAC],
 			       ETH_GSTRING_LEN);
 			p += ETH_GSTRING_LEN;
 		}
 		if (handle->flags & HNAE3_SUPPORT_SERDES_LOOPBACK) {
 			memcpy(p,
-			       hns3_nic_test_strs[HNAE3_MAC_INTER_LOOP_SERDES],
+			       hns3_nic_test_strs[HNAE3_LOOP_SERDES],
 			       ETH_GSTRING_LEN);
 			p += ETH_GSTRING_LEN;
 		}
 		if (handle->flags & HNAE3_SUPPORT_PHY_LOOPBACK) {
 			memcpy(p,
-			       hns3_nic_test_strs[HNAE3_MAC_INTER_LOOP_PHY],
+			       hns3_nic_test_strs[HNAE3_LOOP_PHY],
 			       ETH_GSTRING_LEN);
 			p += ETH_GSTRING_LEN;
 		}
@@ -3459,10 +3459,10 @@ static int hclge_set_loopback(struct hnae3_handle *handle,
 	int i, ret;
 
 	switch (loop_mode) {
-	case HNAE3_MAC_INTER_LOOP_MAC:
+	case HNAE3_LOOP_MAC:
 		ret = hclge_set_mac_loopback(hdev, en);
 		break;
-	case HNAE3_MAC_INTER_LOOP_SERDES:
+	case HNAE3_LOOP_SERDES:
 		ret = hclge_set_serdes_loopback(hdev, en);
 		break;
 	default:
-- 
2.7.4

^ permalink raw reply related

* [PATCH net-next 02/10] net: hns3: Set extra mac address of pause param for HW
From: Salil Mehta @ 2018-09-26 18:28 UTC (permalink / raw)
  To: davem
  Cc: salil.mehta, yisen.zhuang, lipeng321, mehta.salil, netdev,
	linux-kernel, linuxarm, Fuyun Liang
In-Reply-To: <20180926182840.28392-1-salil.mehta@huawei.com>

From: Fuyun Liang <liangfuyun1@huawei.com>

The extra mac address of pause param is used to do double check
for pause frame. This patch set it to HW. If we do not do that,
pfc pause frame will be transferred protocol stack when normal
flow control mode is enabled.

Signed-off-by: Fuyun Liang <liangfuyun1@huawei.com>
Signed-off-by: Peng Li <lipeng321@huawei.com>
Signed-off-by: Salil Mehta <salil.mehta@huawei.com>
---
 drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c | 1 +
 drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.h | 4 ++++
 2 files changed, 5 insertions(+)

diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c
index 00bb394..ab7280d 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c
@@ -193,6 +193,7 @@ static int hclge_pause_param_cfg(struct hclge_dev *hdev, const u8 *addr,
 	hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_CFG_MAC_PARA, false);
 
 	ether_addr_copy(pause_param->mac_addr, addr);
+	ether_addr_copy(pause_param->mac_addr_extra, addr);
 	pause_param->pause_trans_gap = pause_trans_gap;
 	pause_param->pause_trans_time = cpu_to_le16(pause_trans_time);
 
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.h
index dd4c194..e1568b8 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.h
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.h
@@ -106,6 +106,10 @@ struct hclge_cfg_pause_param_cmd {
 	u8 pause_trans_gap;
 	u8 rsvd;
 	__le16 pause_trans_time;
+	u8 rsvd1[6];
+	/* extra mac address to do double check for pause frame */
+	u8 mac_addr_extra[ETH_ALEN];
+	u16 rsvd2;
 };
 
 struct hclge_pfc_stats_cmd {
-- 
2.7.4

^ permalink raw reply related

* [PATCH net-next 01/10] net: hns3: Add support for sctp checksum offload
From: Salil Mehta @ 2018-09-26 18:28 UTC (permalink / raw)
  To: davem
  Cc: salil.mehta, yisen.zhuang, lipeng321, mehta.salil, netdev,
	linux-kernel, linuxarm, Fuyun Liang
In-Reply-To: <20180926182840.28392-1-salil.mehta@huawei.com>

From: Peng Li <lipeng321@huawei.com>

This patch adds support for sctp checksum offload.

Signed-off-by: Fuyun Liang <liangfuyun1@huawei.com>
Signed-off-by: Peng Li <lipeng321@huawei.com>
Signed-off-by: Salil Mehta <salil.mehta@huawei.com>
---
 drivers/net/ethernet/hisilicon/hns3/hns3_enet.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c
index 5a4773a..3fc3822 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c
@@ -1740,7 +1740,7 @@ static void hns3_set_default_feature(struct net_device *netdev)
 		NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_GSO |
 		NETIF_F_GRO | NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_GSO_GRE |
 		NETIF_F_GSO_GRE_CSUM | NETIF_F_GSO_UDP_TUNNEL |
-		NETIF_F_GSO_UDP_TUNNEL_CSUM;
+		NETIF_F_GSO_UDP_TUNNEL_CSUM | NETIF_F_SCTP_CRC;
 
 	netdev->hw_enc_features |= NETIF_F_TSO_MANGLEID;
 
@@ -1752,21 +1752,21 @@ static void hns3_set_default_feature(struct net_device *netdev)
 		NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_GSO |
 		NETIF_F_GRO | NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_GSO_GRE |
 		NETIF_F_GSO_GRE_CSUM | NETIF_F_GSO_UDP_TUNNEL |
-		NETIF_F_GSO_UDP_TUNNEL_CSUM;
+		NETIF_F_GSO_UDP_TUNNEL_CSUM | NETIF_F_SCTP_CRC;
 
 	netdev->vlan_features |=
 		NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM |
 		NETIF_F_SG | NETIF_F_GSO | NETIF_F_GRO |
 		NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_GSO_GRE |
 		NETIF_F_GSO_GRE_CSUM | NETIF_F_GSO_UDP_TUNNEL |
-		NETIF_F_GSO_UDP_TUNNEL_CSUM;
+		NETIF_F_GSO_UDP_TUNNEL_CSUM | NETIF_F_SCTP_CRC;
 
 	netdev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
 		NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX |
 		NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_GSO |
 		NETIF_F_GRO | NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_GSO_GRE |
 		NETIF_F_GSO_GRE_CSUM | NETIF_F_GSO_UDP_TUNNEL |
-		NETIF_F_GSO_UDP_TUNNEL_CSUM;
+		NETIF_F_GSO_UDP_TUNNEL_CSUM | NETIF_F_SCTP_CRC;
 
 	if (pdev->revision != 0x20)
 		netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER;
-- 
2.7.4

^ permalink raw reply related

* [PATCH net-next 00/10] Cleanups, minor additions & fixes for HNS3 driver
From: Salil Mehta @ 2018-09-26 18:28 UTC (permalink / raw)
  To: davem
  Cc: salil.mehta, yisen.zhuang, lipeng321, mehta.salil, netdev,
	linux-kernel, linuxarm

This patch-set contains cleans-ups, minor changes and fixes to the HNS3 driver.

Fuyun Liang (4):
  net: hns3: Set extra mac address of pause param for HW
  net: hns3: Rename loop mode
  net: hns3: Rename mac loopback to app loopback
  net: hns3: Add serdes parallel inner loopback support

Huazhong Tan (3):
  net: hns3: Change return type of hclge_tm_schd_info_update()
  net: hns3: Modify hns3_get_max_available_channels
  net: hns3: Fix loss of coal configuration while doing reset

Peng Li (1):
  net: hns3: Add support for sctp checksum offload

Yunsheng Lin (2):
  net: hns3: Fix for packet buffer setting bug
  net: hns3: Fix for netdev not up problem when setting mtu

 drivers/net/ethernet/hisilicon/hns3/hnae3.h        |  16 ++--
 drivers/net/ethernet/hisilicon/hns3/hns3_enet.c    | 101 ++++++++++-----------
 drivers/net/ethernet/hisilicon/hns3/hns3_enet.h    |   2 +
 drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c |  24 +++--
 .../net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h |   1 +
 .../net/ethernet/hisilicon/hns3/hns3pf/hclge_dcb.c |   8 +-
 .../ethernet/hisilicon/hns3/hns3pf/hclge_main.c    |  94 ++++++++++++-------
 .../net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c  |  10 +-
 .../net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.h  |   6 +-
 .../ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c  |   4 +-
 10 files changed, 157 insertions(+), 109 deletions(-)

-- 
2.7.4

^ permalink raw reply

* [PATCH net-next] net: aquantia: Make function aq_fw1x_set_power() static
From: Wei Yongjun @ 2018-09-26 12:20 UTC (permalink / raw)
  To: Igor Russkikh, Nikita Danilov, Yana Esina, Andrew Lunn
  Cc: Wei Yongjun, netdev, kernel-janitors

Fixes the following sparse warning:

drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.c:873:5: warning:
 symbol 'aq_fw1x_set_power' was not declared. Should it be static?

Fixes: a0da96c08cfa ("net: aquantia: implement WOL support")
Signed-off-by: Wei Yongjun <weiyongjun1@huawei.com>
---
 drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.c b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.c
index 0dd59b09..7def1cb 100644
--- a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.c
+++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.c
@@ -870,8 +870,8 @@ static int aq_fw1x_set_wol(struct aq_hw_s *self, bool wol_enabled, u8 *mac)
 	return err;
 }
 
-int aq_fw1x_set_power(struct aq_hw_s *self, unsigned int power_state,
-		      u8 *mac)
+static int aq_fw1x_set_power(struct aq_hw_s *self, unsigned int power_state,
+			     u8 *mac)
 {
 	struct hw_atl_utils_fw_rpc *prpc = NULL;
 	unsigned int rpc_size = 0U;

^ permalink raw reply related

* [PATCH net] net: phy: micrel: add Microchip KSZ9131 inital driver
From: Yuiko Oshino @ 2018-09-26 18:22 UTC (permalink / raw)
  To: andrew, davem, netdev, linux-kernel; +Cc: UNGLinuxDriver

Add support for Microchip Technology KSZ9131 10/100/1000 Ethernet phy

Signed-off-by: Yuiko Oshino <yuiko.oshino@microchip.com>
---
 drivers/net/phy/micrel.c   | 32 +++++++++++++++++++++++++++++---
 include/linux/micrel_phy.h |  1 +
 2 files changed, 30 insertions(+), 3 deletions(-)

diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c
index 3db06b4..a4473cb 100644
--- a/drivers/net/phy/micrel.c
+++ b/drivers/net/phy/micrel.c
@@ -14,7 +14,7 @@
  * option) any later version.
  *
  * Support : Micrel Phys:
- *		Giga phys: ksz9021, ksz9031
+ *		Giga phys: ksz9021, ksz9031, ksz9131
  *		100/10 Phys : ksz8001, ksz8721, ksz8737, ksz8041
  *			   ksz8021, ksz8031, ksz8051,
  *			   ksz8081, ksz8091,
@@ -425,6 +425,7 @@ static int ksz9021_config_init(struct phy_device *phydev)
 #define MII_KSZ9031RN_MMD_REGDATA_REG	0x0e
 #define OP_DATA				1
 #define KSZ9031_PS_TO_REG		60
+#define KSZ9131_PS_TO_REG		100
 
 /* Extended registers */
 /* MMD Address 0x0 */
@@ -470,6 +471,10 @@ static int ksz9031_of_load_skew_values(struct phy_device *phydev,
 	u16 maxval;
 	u16 newval;
 	int i;
+	int pstoreg = KSZ9031_PS_TO_REG;
+
+	if (phydev->drv->phy_id == PHY_ID_KSZ9131)
+		pstoreg = KSZ9131_PS_TO_REG;
 
 	for (i = 0; i < numfields; i++)
 		if (!of_property_read_u32(of_node, field[i], val + i))
@@ -489,7 +494,7 @@ static int ksz9031_of_load_skew_values(struct phy_device *phydev,
 			mask = 0xffff;
 			mask ^= maxval << (field_sz * i);
 			newval = (newval & mask) |
-				(((val[i] / KSZ9031_PS_TO_REG) & maxval)
+				(((val[i] / pstoreg) & maxval)
 					<< (field_sz * i));
 		}
 
@@ -602,7 +607,10 @@ static int ksz9031_config_init(struct phy_device *phydev)
 		}
 	}
 
-	return ksz9031_center_flp_timing(phydev);
+	if (phydev->drv->phy_id == PHY_ID_KSZ9031)
+		return ksz9031_center_flp_timing(phydev);
+	else
+		return 0;
 
 err_force_master:
 	phydev_err(phydev, "failed to force the phy to master mode\n");
@@ -975,6 +983,23 @@ static struct phy_driver ksphy_driver[] = {
 	.suspend	= genphy_suspend,
 	.resume		= kszphy_resume,
 }, {
+	.phy_id		= PHY_ID_KSZ9131,
+	.phy_id_mask	= MICREL_PHY_ID_MASK,
+	.name		= "Microchip KSZ9131 Gigabit PHY",
+	.features	= PHY_GBIT_FEATURES,
+	.flags		= PHY_HAS_INTERRUPT,
+	.driver_data	= &ksz9021_type,
+	.probe		= kszphy_probe,
+	.config_init	= ksz9031_config_init,
+	.read_status	= ksz9031_read_status,
+	.ack_interrupt	= kszphy_ack_interrupt,
+	.config_intr	= kszphy_config_intr,
+	.get_sset_count = kszphy_get_sset_count,
+	.get_strings	= kszphy_get_strings,
+	.get_stats	= kszphy_get_stats,
+	.suspend	= genphy_suspend,
+	.resume		= kszphy_resume,
+}, {
 	.phy_id		= PHY_ID_KSZ8873MLL,
 	.phy_id_mask	= MICREL_PHY_ID_MASK,
 	.name		= "Micrel KSZ8873MLL Switch",
@@ -1022,6 +1047,7 @@ MODULE_LICENSE("GPL");
 static struct mdio_device_id __maybe_unused micrel_tbl[] = {
 	{ PHY_ID_KSZ9021, 0x000ffffe },
 	{ PHY_ID_KSZ9031, MICREL_PHY_ID_MASK },
+	{ PHY_ID_KSZ9131, MICREL_PHY_ID_MASK },
 	{ PHY_ID_KSZ8001, 0x00fffffc },
 	{ PHY_ID_KS8737, MICREL_PHY_ID_MASK },
 	{ PHY_ID_KSZ8021, 0x00ffffff },
diff --git a/include/linux/micrel_phy.h b/include/linux/micrel_phy.h
index 472fa4d..7361cd3 100644
--- a/include/linux/micrel_phy.h
+++ b/include/linux/micrel_phy.h
@@ -31,6 +31,7 @@
 #define PHY_ID_KSZ8081		0x00221560
 #define PHY_ID_KSZ8061		0x00221570
 #define PHY_ID_KSZ9031		0x00221620
+#define PHY_ID_KSZ9131		0x00221640
 
 #define PHY_ID_KSZ886X		0x00221430
 #define PHY_ID_KSZ8863		0x00221435
-- 
2.7.4

^ permalink raw reply related

* [PATCH net] dt-bindings: net: add support for Microchip KSZ9131 Ethernet PHY
From: Yuiko Oshino @ 2018-09-26 18:16 UTC (permalink / raw)
  To: davem, robh+dt, devicetree
  Cc: linux-kernel, mark.rutland, m.felsch, Markus.Niebel, andrew,
	netdev, UNGLinuxDriver

Add support for Microchip Technology KSZ9131 10/100/1000 Ethernet phy

Signed-off-by: Yuiko Oshino <yuiko.oshino@microchip.com>
---
 .../devicetree/bindings/net/micrel-ksz90x1.txt     | 29 +++++++++++++++++++++-
 1 file changed, 28 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/net/micrel-ksz90x1.txt b/Documentation/devicetree/bindings/net/micrel-ksz90x1.txt
index e22d8cf..d23d14a 100644
--- a/Documentation/devicetree/bindings/net/micrel-ksz90x1.txt
+++ b/Documentation/devicetree/bindings/net/micrel-ksz90x1.txt
@@ -1,4 +1,4 @@
-Micrel KSZ9021/KSZ9031 Gigabit Ethernet PHY
+Micrel/Microchip KSZ9021/KSZ9031/KSZ9131 Gigabit Ethernet PHY
 
 Some boards require special tuning values, particularly when it comes
 to clock delays. You can specify clock delay values in the PHY OF
@@ -64,6 +64,33 @@ KSZ9031:
         Attention: The link partner must be configurable as slave otherwise
         no link will be established.
 
+KSZ9131:
+
+  All skew control options are specified in picoseconds. The minimum
+  value is 0, and the maximum is property-dependent. The increment
+  step is 100ps. The default value is the neutral setting, so setting
+  rxc-skew-ps=<0> actually results in -700 picoseconds adjustment.
+
+  Optional properties:
+
+    Maximum value of 2400, default value 700:
+
+      - rxc-skew-ps : Skew control of RX clock pad
+      - txc-skew-ps : Skew control of TX clock pad
+
+    Maximum value of 1500, default value 700:
+
+      - rxdv-skew-ps : Skew control of RX CTL pad
+      - txen-skew-ps : Skew control of TX CTL pad
+      - rxd0-skew-ps : Skew control of RX data 0 pad
+      - rxd1-skew-ps : Skew control of RX data 1 pad
+      - rxd2-skew-ps : Skew control of RX data 2 pad
+      - rxd3-skew-ps : Skew control of RX data 3 pad
+      - txd0-skew-ps : Skew control of TX data 0 pad
+      - txd1-skew-ps : Skew control of TX data 1 pad
+      - txd2-skew-ps : Skew control of TX data 2 pad
+      - txd3-skew-ps : Skew control of TX data 3 pad
+
 Examples:
 
 	mdio {
-- 
2.7.4

^ permalink raw reply related

* [PATCH net-next] net/tls: Make function get_rec() static
From: Wei Yongjun @ 2018-09-26 12:10 UTC (permalink / raw)
  To: Boris Pismenny, Aviad Yehezkel, Dave Watson, Vakul Garg
  Cc: Wei Yongjun, netdev, kernel-janitors

Fixes the following sparse warning:

net/tls/tls_sw.c:655:16: warning:
 symbol 'get_rec' was not declared. Should it be static?

Fixes: a42055e8d2c3 ("net/tls: Add support for async encryption of records for performance")
Signed-off-by: Wei Yongjun <weiyongjun1@huawei.com>
---
 net/tls/tls_sw.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c
index bcb24c4..207e3ca 100644
--- a/net/tls/tls_sw.c
+++ b/net/tls/tls_sw.c
@@ -652,7 +652,7 @@ static int memcopy_from_iter(struct sock *sk, struct iov_iter *from,
 	return rc;
 }
 
-struct tls_rec *get_rec(struct sock *sk)
+static struct tls_rec *get_rec(struct sock *sk)
 {
 	struct tls_context *tls_ctx = tls_get_ctx(sk);
 	struct tls_sw_context_tx *ctx = tls_sw_ctx_tx(tls_ctx);

^ permalink raw reply related

* [PATCH net-next] net/core: make function ___gnet_stats_copy_basic() static
From: Wei Yongjun @ 2018-09-26 12:09 UTC (permalink / raw)
  To: john.fastabend, toke, echaudro; +Cc: Wei Yongjun, netdev, kernel-janitors

Fixes the following sparse warning:

net/core/gen_stats.c:166:1: warning:
 symbol '___gnet_stats_copy_basic' was not declared. Should it be static?

Fixes: 5e111210a443 ("net/core: Add new basic hardware counter")
Signed-off-by: Wei Yongjun <weiyongjun1@huawei.com>
---
 net/core/gen_stats.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/net/core/gen_stats.c b/net/core/gen_stats.c
index 65a2e82..9bf1b9a 100644
--- a/net/core/gen_stats.c
+++ b/net/core/gen_stats.c
@@ -162,7 +162,7 @@
 }
 EXPORT_SYMBOL(__gnet_stats_copy_basic);
 
-int
+static int
 ___gnet_stats_copy_basic(const seqcount_t *running,
 			 struct gnet_dump *d,
 			 struct gnet_stats_basic_cpu __percpu *cpu,

^ permalink raw reply related

* [RFC PATCH iproute2-next V2] man: Add devlink exception man page
From: Eran Ben Elisha @ 2018-09-26 11:52 UTC (permalink / raw)
  To: netdev, Jakub Kicinski, Jiri Pirko, Stephen Hemminger,
	Andrew Lunn, Tobin C. Harding
  Cc: Ariel Almog, Tal Alon, Eran Ben Elisha
In-Reply-To: <1537962779-8910-1-git-send-email-eranbe@mellanox.com>

Add devlink-exception man page. Devlink-exception tool will control device
exception attributes, conditions, actions and logging.

Signed-off-by: Eran Ben Elisha <eranbe@mellanox.com>

-------------------------------------------------------
Copy paste man output to here for easier review process of the RFC.

DEVLINK-EXCEPTION(8)                                                                                            Linux                                                                                           DEVLINK-EXCEPTION(8)

NAME
       devlink-exception - devlink exception configuration

SYNOPSIS
       devlink [ OPTIONS ] exception  { COMMAND | help }

       OPTIONS := { -V[ersion] | -n[no-nice-names] }

       devlink exception show [ DEV ] [ condition NAME ] [ action NAME ]

       devlink exception condition set DEV name NAME [ action NAME { active | inactive } ]

       devlink exception action set DEV name NAME period PERIOD count COUNT fail { ignore | down }

       devlink exception help

DESCRIPTION
       devlink-exception tool allows user to configure the way driver treats unexpected status. The tool allows configuration of the conditions that can trigger exception activity. Set for each condition the follow up opera‐
       tions, such as, reset and dump of info. In addition, set the exception activity termination action.

   devlink exception show - Display devlink exception conditions and actions attributes
       DEV    Specifies the devlink device to show.

       condition NAME
              Specifies the devlink condition to show.

       action NAME
              Specifies the devlink action to show.

   devlink exception condition set - sets devlink exception condition attributes
       DEV    Specifies the devlink device to set.

       name NAME
              Name of the condition to set.

       action NAME { active | inactive }
                  Specify which actions to activate and which to deactivate once a condition was triggered. Actions can be dump, reset, etc.

   devlink exception action set - sets devlink action attributes.
       Once this command is launched, period and count measurement will be reset.

       DEV    Specifies the devlink device to set.

       name NAME
              Specifies the devlink action to set.

       period PERIOD
              The period on which we limit the amount of performed actions, measured in seconds.

       count COUNT
              The maximum number of actions performed in a limited time frame.

       fail   { ignore | down }
                  Specify the behavior once count limit was reached.

                  ignore - Skip triggering this action.

                  down - Driver will remain in nonoperational state.

EXAMPLES
       devlink exception show
           Shows the exception state of all devlink devices on the system.

       devlink exception show pci/0000:01:00.0
           Shows the exception state of specified devlink device.

       devlink exception condition set pci/0000:01:00.0 name TX_COMP_ERROR action reset off action dump on
           Sets TX_COMP_ERROR condition parameters for a specific device.

       devlink exception action set pci/0000:01:00.0 name reset period 3600 count 5 fail ignore
           Sets exception attributes for reset action. Period timer and counter are being reset.

SEE ALSO
       devlink(8), devlink-port(8), devlink-sb(8), devlink-monitor(8), devlink-dev(8),

AUTHOR
       Eran ben Elisha <eranbe@mellanox.com>

iproute2                                                                                                     15 Aug 2018                                                                                        DEVLINK-EXCEPTION(8)

---
 man/man8/devlink-exception.8 | 158 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 158 insertions(+)
 create mode 100644 man/man8/devlink-exception.8

diff --git a/man/man8/devlink-exception.8 b/man/man8/devlink-exception.8
new file mode 100644
index 000000000000..03f24b32cc98
--- /dev/null
+++ b/man/man8/devlink-exception.8
@@ -0,0 +1,158 @@
+.TH DEVLINK\-EXCEPTION 8 "15 Aug 2018" "iproute2" "Linux"
+.SH NAME
+devlink-exception \- devlink exception configuration
+.SH SYNOPSIS
+.sp
+.ad l
+.in +8
+.ti -8
+.B devlink
+.RI "[ " OPTIONS " ]"
+.BR exception
+.RI  " { " COMMAND " | "
+.BR help " }"
+.sp
+
+.ti -8
+.IR OPTIONS " := { "
+\fB\-V\fR[\fIersion\fR] |
+\fB\-n\fR[\fIno-nice-names\fR] }
+
+.ti -8
+.B devlink exception show
+.RI "[ " DEV " ]"
+.RI "[ "
+.B condition
+.IR NAME
+.RI "]"
+.RI "[ "
+.B action
+.IR NAME
+.RI "]"
+
+.ti -8
+.B devlink exception condition set
+.IR DEV
+.B name
+.IR NAME
+.RI "[ "
+.BR action
+.IR NAME
+.R "{" active "|" inactive "}" ]
+
+.ti -8
+.B devlink exception action set
+.IR DEV
+.B name
+.IR NAME
+.BR period
+.IR PERIOD
+.BR count
+.IR COUNT
+.BR fail " { "
+.IR ignore
+.BR "| "
+.IR down
+.R "} "
+
+.ti -8
+.B devlink exception help
+
+.SH "DESCRIPTION"
+.B devlink-exception
+tool allows user to configure the way driver treats unexpected status. The tool allows configuration of the conditions that can trigger exception activity. Set for each condition the follow up operations, such as, reset and dump of info. In addition, set the exception activity termination action.
+
+.SS devlink exception show - Display devlink exception conditions and actions attributes
+.TP
+.BI "DEV"
+Specifies the devlink device to show.
+
+.PP
+.TP
+.BI condition " NAME"
+Specifies the devlink condition to show.
+
+.TP
+.BI action " NAME"
+Specifies the devlink action to show.
+
+.SS devlink exception condition set - sets devlink exception condition attributes
+
+.TP
+.B "DEV"
+Specifies the devlink device to set.
+
+.TP
+.BI name " NAME"
+Name of the condition to set.
+
+.TP
+.BR action
+.IR NAME
+.R "{" active "|" inactive "} "
+.in +4
+Specify which actions to activate and which to deactivate once a condition was triggered. Actions can be dump, reset, etc.
+
+.SS devlink exception action set - sets devlink action attributes.
+Once this command is launched, period and count measurement will be reset.
+
+.TP
+.B "DEV"
+Specifies the devlink device to set.
+
+.TP
+.BI name " NAME"
+Specifies the devlink action to set.
+
+.TP
+.BI period " PERIOD"
+The period on which we limit the amount of performed actions, measured in seconds.
+
+.TP
+.BI count " COUNT"
+The maximum number of actions performed in a limited time frame.
+
+.TP
+.BR fail
+.R "{" ignore "|" down "}"
+.in +4
+Specify the behavior once count limit was reached.
+
+.I ignore
+- Skip triggering this action.
+
+.I down
+- Driver will remain in nonoperational state.
+
+.SH "EXAMPLES"
+.PP
+devlink exception show
+.RS 4
+Shows the exception state of all devlink devices on the system.
+.RE
+.PP
+devlink exception show pci/0000:01:00.0
+.RS 4
+Shows the exception state of specified devlink device.
+.RE
+.PP
+devlink exception condition set pci/0000:01:00.0 name TX_COMP_ERROR action reset off action dump on
+.RS 4
+Sets TX_COMP_ERROR condition parameters for a specific device.
+.RE
+.PP
+devlink exception action set pci/0000:01:00.0 name reset period 3600 count 5 fail ignore
+.RS 4
+Sets exception attributes for reset action. Period timer and counter are being reset.
+.RE
+
+.SH SEE ALSO
+.BR devlink (8),
+.BR devlink-port (8),
+.BR devlink-sb (8),
+.BR devlink-monitor (8),
+.BR devlink-dev (8),
+.br
+
+.SH AUTHOR
+Eran ben Elisha <eranbe@mellanox.com>
-- 
1.8.3.1

^ permalink raw reply related

* [RFC PATCH iproute2-next V2] System specification exception API
From: Eran Ben Elisha @ 2018-09-26 11:52 UTC (permalink / raw)
  To: netdev, Jakub Kicinski, Jiri Pirko, Stephen Hemminger,
	Andrew Lunn, Tobin C. Harding
  Cc: Ariel Almog, Tal Alon, Eran Ben Elisha

The exception spec is targeted for Real Time Alerting, in order to know when
something bad had happened to a PCI device
- Provide alert debug information
- Self healing
- If problem needs vendor support, provide a way to gather all needed debugging
  information.

The exception mechanism contains condition checkers which sense for malfunction. Upon a condition hit,
actions such as logs and correction can be taken.

The condition checkers are divided into the following groups
- Hardware - a checker which is triggered by the device due to
  malfunction.
- Software - a checker which is triggered by the software due to
  malfunction.
Both groups of condition checkers can be triggered due to error event or due to a periodic check.

Actions are the way to handle those events. Action can be in one of the
following groups:
- Dump -  SW trace, SW dump, HW trace, HW dump
- Reset - Surgical correction (e.g. modify Q, flush Q, reset of device, etc)
Actions can be performed by SW or HW.

User is allowed to enable or disable condition checkers and its action mapping.

This RFC man page patch describes the suggested API of devlink-exception in order
to control conditions and actions.

V2:
* Renaming terms:
	health -> exception
	sensor -> condition
* Remove reinit command and merge with action command.
* Consmetics in grammer.

Eran Ben Elisha (1):
  man: Add devlink exception man page

 man/man8/devlink-exception.8 | 158 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 158 insertions(+)
 create mode 100644 man/man8/devlink-exception.8

-- 
1.8.3.1

^ permalink raw reply

* Re: [PATCH 2/2] can: tcan4x5x: Add tcan4x5x driver to the kernel
From: Dan Murphy @ 2018-09-26 18:00 UTC (permalink / raw)
  To: Wolfgang Grandegger, mkl, davem; +Cc: linux-can, netdev, linux-kernel
In-Reply-To: <45621ef7-bc87-3d1e-efca-e6387fdebf9a@grandegger.com>

Wolfgang

On 09/26/2018 12:54 PM, Wolfgang Grandegger wrote:
> Hello,
> 
> I wonder why you do not extend the existing MCAN driver by implementing
> an interface to access the hardware. Would that be feasible?
> 

That did cross my mind. The issue is I have no way of testing the existing
driver to make sure I did not break anything.

My thought was to create a basic MCAN framework and attach the devices to it.
So, like in this case, if there is a special way to talk to the device it can 
be handled in the device driver.

If thats what is needed then I will have to re-write the driver.

Dan

> Wolfgang.
> 
> Am 26.09.2018 um 19:40 schrieb Dan Murphy:
>> bump
>>
>> On 09/10/2018 03:12 PM, Dan Murphy wrote:
>>> Add the TCAN4x5x SPI CAN driver.  This device
>>> uses the Bosch MCAN IP core along with a SPI
>>> interface map.  The register and data are
>>> 32 bits wide.
>>>
>>> Signed-off-by: Dan Murphy <dmurphy@ti.com>
>>> ---
>>>  drivers/net/can/spi/Kconfig    |    5 +
>>>  drivers/net/can/spi/Makefile   |    1 +
>>>  drivers/net/can/spi/tcan4x5x.c | 1206 ++++++++++++++++++++++++++++++++
>>>  drivers/net/can/spi/tcan4x5x.h |  109 +++
>>>  4 files changed, 1321 insertions(+)
>>>  create mode 100644 drivers/net/can/spi/tcan4x5x.c
>>>  create mode 100644 drivers/net/can/spi/tcan4x5x.h
>>>
>>> diff --git a/drivers/net/can/spi/Kconfig b/drivers/net/can/spi/Kconfig
>>> index 8f2e0dd7b756..8cac6ce37506 100644
>>> --- a/drivers/net/can/spi/Kconfig
>>> +++ b/drivers/net/can/spi/Kconfig
>>> @@ -13,4 +13,9 @@ config CAN_MCP251X
>>>  	---help---
>>>  	  Driver for the Microchip MCP251x SPI CAN controllers.
>>>  
>>> +config CAN_TCAN4X5X
>>> +	tristate "Texas Instruments TCAN4X5X SPI CAN controllers"
>>> +	depends on HAS_DMA
>>> +	---help---
>>> +	  Driver for the Texas Instruments TCAN4X5X SPI CAN controllers.
>>>  endmenu
>>> diff --git a/drivers/net/can/spi/Makefile b/drivers/net/can/spi/Makefile
>>> index f59fa3731073..8ecaace7a920 100644
>>> --- a/drivers/net/can/spi/Makefile
>>> +++ b/drivers/net/can/spi/Makefile
>>> @@ -5,3 +5,4 @@
>>>  
>>>  obj-$(CONFIG_CAN_HI311X)	+= hi311x.o
>>>  obj-$(CONFIG_CAN_MCP251X)	+= mcp251x.o
>>> +obj-$(CONFIG_CAN_TCAN4X5X)	+= tcan4x5x.o
>>> diff --git a/drivers/net/can/spi/tcan4x5x.c b/drivers/net/can/spi/tcan4x5x.c
>>> new file mode 100644
>>> index 000000000000..ca3753efe35a
>>> --- /dev/null
>>> +++ b/drivers/net/can/spi/tcan4x5x.c
>>> @@ -0,0 +1,1206 @@
>>> +// SPDX-License-Identifier: GPL-2.0
>>> +// SPI to CAN driver for the Texas Instruments TCAN4x5x
>>> +// Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
>>> +
>>> +#include <linux/can/core.h>
>>> +#include <linux/can/dev.h>
>>> +#include <linux/can/led.h>
>>> +#include <linux/clk.h>
>>> +#include <linux/completion.h>
>>> +#include <linux/delay.h>
>>> +#include <linux/device.h>
>>> +#include <linux/dma-mapping.h>
>>> +#include <linux/freezer.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/regmap.h>
>>> +#include <linux/slab.h>
>>> +#include <linux/spi/spi.h>
>>> +#include <linux/uaccess.h>
>>> +
>>> +#include <linux/regulator/consumer.h>
>>> +#include <linux/gpio/consumer.h>
>>> +
>>> +#include "tcan4x5x.h"
>>> +
>>> +#define DEVICE_NAME "tcan4x5x"
>>> +#define TCAN4X5X_EXT_CLK_DEF	40000000
>>> +
>>> +#define TCAN4X5X_CLEAR_ALL_INT	0xffffffff
>>> +#define TCAN4X5X_SET_ALL_INT	0xffffffff
>>> +
>>> +#define TCAN4X5X_TX_ECHO_SKB_MAX 1
>>> +#define TCAN4X5X_DATA_PKT_OFF	2
>>> +#define TCAN4X5X_WRITE_CMD	(0x61 << 24)
>>> +#define TCAN4X5X_READ_CMD	(0x41 << 24)
>>> +
>>> +#define TCAN4X5X_SID_SHIFT	18
>>> +#define TCAN4X5X_DLC_SHIFT	16
>>> +
>>> +#define TCAN4X5X_ESI_SHIFT	31
>>> +#define TCAN4X5X_XTD_SHIFT	30
>>> +#define TCAN4X5X_RTR_SHIFT	29
>>> +#define TCAN4X5X_FDF_SHIFT	21
>>> +#define TCAN4X5X_BRS_SHIFT	20
>>> +#define TCAN4X5X_DLC_SHIFT	16
>>> +
>>> +#define TCAN4X5X_ESI_MASK	BIT(31)
>>> +#define TCAN4X5X_XTD_MASK	BIT(30)
>>> +#define TCAN4X5X_RTR_MASK	BIT(29)
>>> +
>>> +#define TCAN4X5X_DLC_MASK	0xf0000
>>> +#define TCAN4X5X_SW_RESET	BIT(2)
>>> +
>>> +#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_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))
>>> +
>>> +/* Nominal Bit Timing & Prescaler Register */
>>> +#define TCAN4X5X_NSJW_SHIFT	25
>>> +#define TCAN4X5X_NBRP_SHIFT	16
>>> +#define TCAN4X5X_NTSEG1_SHIFT	8
>>> +
>>> +#define TCAN4X5X_TDCR_TDCO_SHIFT	8
>>> +
>>> +/* Data Bit Timing & Prescaler Register (DBTP) */
>>> +#define DBTP_TDC		BIT(23)
>>> +#define DBTP_DBRP_SHIFT		16
>>> +#define DBTP_DBRP_MASK		(0x1f << DBTP_DBRP_SHIFT)
>>> +#define DBTP_DTSEG1_SHIFT	8
>>> +#define DBTP_DTSEG1_MASK	(0x1f << DBTP_DTSEG1_SHIFT)
>>> +#define DBTP_DTSEG2_SHIFT	4
>>> +#define DBTP_DTSEG2_MASK	(0xf << DBTP_DTSEG2_SHIFT)
>>> +#define DBTP_DSJW_SHIFT		0
>>> +#define DBTP_DSJW_MASK		(0xf << DBTP_DSJW_SHIFT)
>>> +
>>> +#define TCAN4x5x_QUEUE_LVL_MASK		0x1f
>>> +#define TCAN4x5x_QUEUE_IDX_SHIFT	16
>>> +#define TCAN4x5x_QUEUE_IDX_MASK		0x1f00
>>> +
>>> +#define TCAN4X5X_CANBUSNOM_INT_EN	BIT(14)
>>> +
>>> +#define TCAN4X5X_NUM_TX_BUF	5
>>> +#define TCAN4X5X_TX_QUEUE_SHIFT	24
>>> +#define TCAN4X5X_TX_NDTB_SHIFT	16
>>> +#define TCAN4X5X_TX_BUF_START	0x324
>>> +
>>> +#define TCAN4X5X_NUM_RX_BUF		3
>>> +#define TCAN4X5X_RX_WATER_MARK		2
>>> +#define TCAN4X5X_RX_WATER_MARK_SHIFT	24
>>> +#define TCAN4X5X_RX_FIFO_SZ_SHIFT	16
>>> +#define TCAN4X5X_RX_BUF_START		0x4
>>> +
>>> +#define TCAN4X5X_RX_F1DS_SHIFT	4
>>> +#define TCAN4X5X_RX_RBDS_SHIFT	8
>>> +
>>> +#define TCAN4X5X_RX_FIFO0_MESSAGE	BIT(0)
>>> +#define TCAN4X5X_RX_FIFO1_MESSAGE	BIT(4)
>>> +#define TCAN4X5X_RX_BUFFER_MESSAGE	BIT(19)
>>> +#define TCAN4X5X_RX_INDEX_MASK		0x3f00
>>> +#define TCAN4X5X_RX_INDEX_SHIFT		8
>>> +
>>> +#define TCAN4X5X_RX_ADDR_OFFSET		0x8000
>>> +#define TCAN4X5X_RX_BUF_ADDR_OFFSET	0x8100
>>> +#define TCAN4X5X_RX_ADDR_MASK		0xffff
>>> +
>>> +#define TCAN4X5X_ERR_PROTOCOL_MASK	0x7
>>> +#define TCAN4X5X_ERR_STUFERR		0x1
>>> +#define TCAN4X5X_ERR_FRMERR		0x2
>>> +#define TCAN4X5X_ERR_ACKERR		0x3
>>> +#define TCAN4X5X_ERR_BIT1ERR		0x4
>>> +#define TCAN4X5X_ERR_BIT0ERR		0x5
>>> +#define TCAN4X5X_ERR_CRCERR		0x6
>>> +
>>> +/* 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_ALL_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)
>>> +
>>> +/* CCR bits */
>>> +#define TCAN4X5X_CCCR_NISO_BOSCH	BIT(15)
>>> +#define TCAN4X5X_CCCR_TXP		BIT(15)
>>> +#define TCAN4X5X_CCCR_EFBI		BIT(13)
>>> +#define TCAN4X5X_CCCR_PXHD_DIS		BIT(12)
>>> +#define TCAN4X5X_CCCR_BRSE		BIT(9)
>>> +#define TCAN4X5X_CCCR_FDOE		BIT(8)
>>> +#define TCAN4X5X_CCCR_TEST		BIT(7)
>>> +#define TCAN4X5X_CCCR_DAR_DIS		BIT(6)
>>> +#define TCAN4X5X_CCCR_MON		BIT(5)
>>> +#define TCAN4X5X_CCCR_CSR		BIT(4)
>>> +#define TCAN4X5X_CCCR_CSA		BIT(3)
>>> +#define TCAN4X5X_CCCR_ASM		BIT(2)
>>> +#define TCAN4X5X_CCCR_CCE		BIT(1)
>>> +#define TCAN4X5X_CCCR_INIT		BIT(0)
>>> +
>>> +#define TCAN4X5X_EINT0			BIT(0)
>>> +#define TCAN4X5X_EINT1			BIT(1)
>>> +
>>> +struct tcan4x5x_rx_regs {
>>> +	u32 fifo_status_reg;
>>> +	u32 fifo_config_reg;
>>> +	u32 fifo_ack_reg;
>>> +	u32 rx_buf_shift;
>>> +};
>>> +
>>> +struct tcan4x5x_rx_regs tcan4x5x_fifo_regs[] = {
>>> +	{ TCAN4X5X_MCAN_RXF0S, TCAN4X5X_MCAN_RXF0C, TCAN4X5X_MCAN_RXF0A, 0},
>>> +	{ TCAN4X5X_MCAN_RXF1S, TCAN4X5X_MCAN_RXF1C, TCAN4X5X_MCAN_RXF1A, 4},
>>> +	{ TCAN4X5X_MCAN_NDAT1, TCAN4X5X_MCAN_RXBC, TCAN4X5X_MCAN_NDAT1, 8},
>>> +};
>>> +
>>> +enum tcan4x5x_data_size {
>>> +	TCAN4X5X_8_BYTE = 0,
>>> +	TCAN4X5X_12_BYTE,
>>> +	TCAN4X5X_16_BYTE,
>>> +	TCAN4X5X_20_BYTE,
>>> +	TCAN4X5X_24_BYTE,
>>> +	TCAN4X5X_32_BYTE,
>>> +	TCAN4X5X_48_BYTE,
>>> +	TCAN4X5X_64_BYTE,
>>> +};
>>> +
>>> +static const 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 const 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_clean(struct net_device *net)
>>> +{
>>> +	struct tcan4x5x_priv *priv = netdev_priv(net);
>>> +
>>> +	if (priv->tx_skb || priv->tx_len)
>>> +		net->stats.tx_errors++;
>>> +	if (priv->tx_skb)
>>> +		dev_kfree_skb(priv->tx_skb);
>>> +	if (priv->tx_len)
>>> +		can_free_echo_skb(priv->net, 0);
>>> +
>>> +	priv->tx_skb = NULL;
>>> +	priv->tx_len = 0;
>>> +}
>>> +
>>> +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);
>>> +	u32 addr;
>>> +	struct spi_message m;
>>> +	struct spi_transfer t[2] = {{ .tx_buf = &addr, .len = 4, .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 + 2;
>>> +
>>> +	return regmap_spi_gather_write(context, reg, 2, val, count - 2);
>>> +}
>>> +
>>> +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, 4, 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 uint8_t tcan4x5x_dlc_conv(uint8_t input)
>>> +{
>>> +	const static u8 lookup[7] = {12, 16, 20, 24, 32, 48, 64};
>>> +
>>> +	if (input < 9)
>>> +		return input;
>>> +
>>> +	if (input < 16)
>>> +		return lookup[(unsigned int)(input - 9)];
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static uint8_t tcan4x5x_txrxesc_value(uint8_t input)
>>> +{
>>> +	const u8 lookup[8] = {8, 12, 16, 20, 24, 32, 48, 64};
>>> +	return lookup[(unsigned int)(input & 0x07)];
>>> +}
>>> +
>>> +static void tcan4x5x_hw_tx(struct tcan4x5x_priv *tcan4x5x)
>>> +{
>>> +	u32 sid, eid, exide, rtr, brs, esi, fdf, xtd, data_len;
>>> +	u32 mcan_address, mcan_tx_element_sz;
>>> +	int queue_stat, queue_lvl, queue_idx;
>>> +	struct canfd_frame *fd_frame;
>>> +	struct can_frame *frame;
>>> +	int tx_element_sz, i, temp;
>>> +	canid_t frame_id;
>>> +	u8 dlc_len;
>>> +
>>> +	regmap_read(tcan4x5x->regmap, TCAN4X5X_MCAN_TXFQS, &queue_stat);
>>> +	queue_lvl = queue_stat & TCAN4x5x_QUEUE_LVL_MASK;
>>> +	queue_idx = (queue_stat & TCAN4x5x_QUEUE_IDX_MASK) >> TCAN4x5x_QUEUE_IDX_SHIFT;
>>> +
>>> +	if (tcan4x5x->tx_skb->len == CAN_MTU) {
>>> +		fd_frame = NULL;
>>> +		frame = (struct can_frame *)tcan4x5x->tx_skb->data;
>>> +		frame_id = frame->can_id;
>>> +		dlc_len = frame->can_dlc;
>>> +		data_len = ((dlc_len % 4) + dlc_len) / 4;
>>> +		brs = 0;
>>> +	} else if (tcan4x5x->tx_skb->len == CANFD_MTU) {
>>> +		frame = NULL;
>>> +		fd_frame = (struct canfd_frame *)tcan4x5x->tx_skb->data;
>>> +		frame_id = fd_frame->can_id;
>>> +		dlc_len = fd_frame->len;
>>> +		data_len = ((dlc_len % 4) + dlc_len) / 4;
>>> +		brs = fd_frame->flags & CANFD_BRS;
>>> +		esi = fd_frame->flags & CANFD_ESI;
>>> +		fdf = 1;
>>> +	} else {
>>> +		return;
>>> +	}
>>> +
>>> +	eid = frame_id & CAN_EFF_MASK;
>>> +	rtr = (frame_id & CAN_RTR_FLAG) ? 1 : 0;
>>> +
>>> +	exide = (frame_id & CAN_EFF_FLAG) ? 1 : 0;
>>> +	if (exide) {
>>> +		sid = frame_id & CAN_EFF_MASK;
>>> +		xtd = 1;
>>> +	} else {
>>> +		sid = (frame_id & CAN_SFF_MASK) << TCAN4X5X_SID_SHIFT;
>>> +		xtd = 0;
>>> +	}
>>> +
>>> +	regmap_read(tcan4x5x->regmap, TCAN4X5X_MCAN_TXBC, &mcan_address);
>>> +
>>> +	mcan_address = (mcan_address & 0xffff) + TCAN4X5X_MRAM_START;
>>> +	temp = (uint8_t)((mcan_address >> 24) & 0x3F);
>>> +
>>> +	tx_element_sz = temp > 32 ? 32 : temp;
>>> +	temp = (uint8_t)((mcan_address >> 16) & 0x3F);
>>> +
>>> +	tx_element_sz += temp > 32 ? 32 : temp;
>>> +	mcan_address += ((uint32_t)tx_element_sz * queue_idx);
>>> +	regmap_read(tcan4x5x->regmap, TCAN4X5X_MCAN_TXESC, &mcan_tx_element_sz);
>>> +	tx_element_sz = tcan4x5x_txrxesc_value(mcan_tx_element_sz & 0x07) + 8;
>>> +	mcan_address += ((uint32_t)tx_element_sz * 0);
>>> +
>>> +	tx_element_sz = (tcan4x5x_dlc_conv(dlc_len & 0x0F) + 8) >> 2;
>>> +	if (tcan4x5x_dlc_conv(dlc_len & 0x0F) % 4)
>>> +		tx_element_sz += 1;
>>> +
>>> +	tcan4x5x->spi_tx_buf[0] = esi << TCAN4X5X_ESI_SHIFT |
>>> +				  xtd << TCAN4X5X_XTD_SHIFT |
>>> +				  rtr << TCAN4X5X_RTR_SHIFT | sid;
>>> +
>>> +	tcan4x5x->spi_tx_buf[1] = fdf << TCAN4X5X_FDF_SHIFT |
>>> +		 brs << TCAN4X5X_BRS_SHIFT | dlc_len << TCAN4X5X_DLC_SHIFT;
>>> +
>>> +	if (tcan4x5x->tx_skb->len == CAN_MTU)
>>> +		memcpy(tcan4x5x->spi_tx_buf + TCAN4X5X_DATA_PKT_OFF,
>>> +		       frame->data, dlc_len);
>>> +	else
>>> +		memcpy(tcan4x5x->spi_tx_buf + TCAN4X5X_DATA_PKT_OFF,
>>> +		       fd_frame->data, dlc_len);
>>> +
>>> +	for (i = dlc_len + 1; i < TCAN4X5X_BUF_LEN / 4; i++)
>>> +		tcan4x5x->spi_tx_buf[i] = 0;
>>> +
>>> +	regmap_bulk_write(tcan4x5x->regmap, mcan_address, tcan4x5x->spi_tx_buf,
>>> +			  TCAN4X5X_BUF_LEN);
>>> +
>>> +	regmap_write(tcan4x5x->regmap, TCAN4X5X_MCAN_TXBAR, (1 << (queue_idx)));
>>> +}
>>> +
>>> +int tcan4x5x_hw_rx(struct tcan4x5x_priv *tcan4x5x)
>>> +{
>>> +	u32 queue_idx, fifo_idx, fifo_start_addr, rx_buf_size, msg_type;
>>> +	u32 data_buffer[TCAN4X5X_BUF_LEN] = {0x0};
>>> +	u32 rx_header[2] = {0x0};
>>> +	struct tcan4x5x_rx_regs *buffer_regs;
>>> +	struct canfd_frame *fd_frame;
>>> +	int dlc_len, data_len;
>>> +	struct sk_buff *skb;
>>> +
>>> +	skb = alloc_canfd_skb(tcan4x5x->net, &fd_frame);
>>> +	if (!skb) {
>>> +		dev_err(&tcan4x5x->spi->dev, "cannot allocate RX skb\n");
>>> +		tcan4x5x->net->stats.rx_dropped++;
>>> +		return -ENOMEM;
>>> +	}
>>> +
>>> +	regmap_read(tcan4x5x->regmap, TCAN4X5X_MCAN_INT_FLAG, &msg_type);
>>> +	if (msg_type & TCAN4X5X_RX_FIFO0_MESSAGE) {
>>> +		buffer_regs = &tcan4x5x_fifo_regs[0];
>>> +	} else if (msg_type & TCAN4X5X_RX_FIFO1_MESSAGE) {
>>> +		buffer_regs = &tcan4x5x_fifo_regs[1];
>>> +	} else if (msg_type & TCAN4X5X_RX_BUFFER_MESSAGE) {
>>> +		buffer_regs = &tcan4x5x_fifo_regs[2];
>>> +	} else {
>>> +		buffer_regs = NULL;
>>> +		return -EINVAL;
>>> +	}
>>> +
>>> +	rx_buf_size = TCAN4X5X_BUF_LEN;
>>> +
>>> +	/* Determine which FIFO needs service */
>>> +	regmap_read(tcan4x5x->regmap, buffer_regs->fifo_status_reg, &fifo_idx);
>>> +	if (msg_type & TCAN4X5X_RX_BUFFER_MESSAGE)
>>> +		queue_idx = fifo_idx - 1;
>>> +	else
>>> +		queue_idx = (TCAN4X5X_RX_INDEX_MASK & fifo_idx) >> TCAN4X5X_RX_INDEX_SHIFT;
>>> +
>>> +	/* Calculate the FIFO start address to service */
>>> +	regmap_read(tcan4x5x->regmap, buffer_regs->fifo_config_reg, &fifo_start_addr);
>>> +	fifo_start_addr = (TCAN4X5X_RX_ADDR_MASK & fifo_start_addr);
>>> +	if (msg_type & TCAN4X5X_RX_BUFFER_MESSAGE)
>>> +		fifo_start_addr = fifo_start_addr + TCAN4X5X_RX_BUF_ADDR_OFFSET +
>>> +				  (rx_buf_size * queue_idx);
>>> +	else
>>> +		fifo_start_addr = fifo_start_addr + TCAN4X5X_RX_ADDR_OFFSET +
>>> +				  (rx_buf_size * queue_idx);
>>> +
>>> +	regmap_bulk_read(tcan4x5x->regmap, fifo_start_addr, rx_header, 2);
>>> +
>>> +	dlc_len = (rx_header[1] & TCAN4X5X_DLC_MASK) >> TCAN4X5X_DLC_SHIFT;
>>> +	if (dlc_len <= 8)
>>> +		data_len = dlc_len;
>>> +	else
>>> +		data_len = tcan4x5x_txrxesc_value(dlc_len);
>>> +
>>> +	regmap_bulk_read(tcan4x5x->regmap, fifo_start_addr + 8,
>>> +			 data_buffer, data_len / 4);
>>> +
>>> +	/* Acknowledge receipt of the data */
>>> +	regmap_write(tcan4x5x->regmap, buffer_regs->fifo_ack_reg, queue_idx);
>>> +
>>> +	if (rx_header[0] &  TCAN4X5X_XTD_MASK) {
>>> +		fd_frame->can_id = CAN_EFF_FLAG;
>>> +		fd_frame->can_id |= (rx_header[0] & CAN_EFF_MASK);
>>> +	} else {
>>> +		fd_frame->can_id |= ((rx_header[0] >> TCAN4X5X_SID_SHIFT) &
>>> +				    CAN_SFF_MASK);
>>> +	}
>>> +
>>> +	if (rx_header[0] & TCAN4X5X_RTR_MASK)
>>> +		fd_frame->can_id |= CAN_RTR_FLAG;
>>> +
>>> +	if (rx_header[0] & TCAN4X5X_ESI_MASK) {
>>> +		fd_frame->can_id |= CAN_ERR_FLAG;
>>> +		fd_frame->flags |= CANFD_ESI;
>>> +		netdev_dbg(tcan4x5x->net, "ESI Error\n");
>>> +	}
>>> +
>>> +	fd_frame->len = data_len;
>>> +	memcpy(fd_frame->data, data_buffer, fd_frame->len);
>>> +
>>> +	tcan4x5x->net->stats.rx_packets++;
>>> +	tcan4x5x->net->stats.rx_bytes += fd_frame->len;
>>> +
>>> +	can_led_event(tcan4x5x->net, CAN_LED_EVENT_RX);
>>> +	netif_rx_ni(skb);
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static void tcan4x5x_sleep(struct spi_device *spi)
>>> +{
>>> +	struct tcan4x5x_priv *tcan4x5x = spi_get_drvdata(spi);
>>> +
>>> +	regmap_update_bits(tcan4x5x->regmap, TCAN4X5X_CONFIG,
>>> +			   TCAN4X5X_MODE_SEL_MASK, TCAN4X5X_MODE_STANDBY);
>>> +}
>>> +
>>> +static int tcan4x5x_reset(struct net_device *net)
>>> +{
>>> +	struct tcan4x5x_priv *tcan4x5x = netdev_priv(net);
>>> +
>>> +	if (tcan4x5x->reset_gpio) {
>>> +		gpiod_set_value_cansleep(tcan4x5x->reset_gpio, 1);
>>> +		udelay(10);
>>> +		gpiod_set_value_cansleep(tcan4x5x->reset_gpio, 0);
>>> +	} else {
>>> +		regmap_write(tcan4x5x->regmap, TCAN4X5X_CONFIG,
>>> +			     TCAN4X5X_SW_RESET);
>>> +	}
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +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 irqreturn_t tcan4x5x_can_ist(int irq, void *dev_id)
>>> +{
>>> +	struct tcan4x5x_priv *tcan4x5x = dev_id;
>>> +	struct spi_device *spi = tcan4x5x->spi;
>>> +	struct net_device *net = tcan4x5x->net;
>>> +	enum can_state new_state;
>>> +	int intf, eflag, mcan_intf;
>>> +
>>> +	mutex_lock(&tcan4x5x->tcan4x5x_lock);
>>> +
>>> +	regmap_read(tcan4x5x->regmap, TCAN4X5X_INT_FLAGS, &intf);
>>> +	if (intf & TCAN4X5X_MCAN_INT)
>>> +		tcan4x5x_hw_rx(tcan4x5x);
>>> +
>>> +	regmap_read(tcan4x5x->regmap, TCAN4X5X_MCAN_INT_FLAG, &mcan_intf);
>>> +
>>> +	regmap_read(tcan4x5x->regmap, TCAN4X5X_STATUS, &eflag);
>>> +	/* Update can state */
>>> +	if (eflag & TCAN4X5X_MCAN_IR_BO)
>>> +		new_state = CAN_STATE_BUS_OFF;
>>> +	else if (eflag & TCAN4X5X_MCAN_IR_EP)
>>> +		new_state = CAN_STATE_ERROR_PASSIVE;
>>> +	else if (eflag & TCAN4X5X_MCAN_IR_EW)
>>> +		new_state = CAN_STATE_ERROR_WARNING;
>>> +	else
>>> +		new_state = CAN_STATE_ERROR_ACTIVE;
>>> +
>>> +	if (new_state != tcan4x5x->can.state) {
>>> +		struct can_frame *cf;
>>> +		struct sk_buff *skb;
>>> +		enum can_state rx_state, tx_state;
>>> +		u32 error_count;
>>> +
>>> +		skb = alloc_can_err_skb(net, &cf);
>>> +		if (!skb)
>>> +			goto ist_out;
>>> +
>>> +		regmap_read(tcan4x5x->regmap, TCAN4X5X_MCAN_ECR, &error_count);
>>> +		cf->data[6] = error_count & 0xff;
>>> +		cf->data[7] = error_count & 0x7f00 >> 8;
>>> +		tx_state = cf->data[6] >= cf->data[7] ? new_state : 0;
>>> +		rx_state = cf->data[6] <= cf->data[7] ? new_state : 0;
>>> +		can_change_state(net, cf, tx_state, rx_state);
>>> +		netif_rx_ni(skb);
>>> +
>>> +		if (new_state == CAN_STATE_BUS_OFF) {
>>> +			can_bus_off(net);
>>> +			if (tcan4x5x->can.restart_ms == 0) {
>>> +				tcan4x5x->force_quit = 1;
>>> +				tcan4x5x_sleep(spi);
>>> +				goto ist_out;
>>> +			}
>>> +		}
>>> +	}
>>> +
>>> +	/* Update bus errors */
>>> +	if ((intf & TCAN4X5X_BUS_FAULT) &&
>>> +	    (tcan4x5x->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)) {
>>> +		struct can_frame *cf;
>>> +		struct sk_buff *skb;
>>> +		u32 psr_err, error_count;
>>> +
>>> +		/* Check for protocol errors */
>>> +		regmap_read(tcan4x5x->regmap, TCAN4X5X_MCAN_PSR, &psr_err);
>>> +		if (psr_err & TCAN4X5X_ERR_PROTOCOL_MASK) {
>>> +			skb = alloc_can_err_skb(net, &cf);
>>> +			if (!skb)
>>> +				goto ist_out;
>>> +
>>> +			cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
>>> +			tcan4x5x->can.can_stats.bus_error++;
>>> +			tcan4x5x->net->stats.rx_errors++;
>>> +			if (psr_err & TCAN4X5X_ERR_BIT0ERR)
>>> +				cf->data[2] |= CAN_ERR_PROT_BIT0;
>>> +			else if (psr_err & TCAN4X5X_ERR_BIT1ERR)
>>> +				cf->data[2] |= CAN_ERR_PROT_BIT1;
>>> +			else if (psr_err & TCAN4X5X_ERR_FRMERR)
>>> +				cf->data[2] |= CAN_ERR_PROT_FORM;
>>> +			else if (psr_err & TCAN4X5X_ERR_STUFERR)
>>> +				cf->data[2] |= CAN_ERR_PROT_STUFF;
>>> +			else if (psr_err & TCAN4X5X_ERR_CRCERR)
>>> +				cf->data[3] |= CAN_ERR_PROT_LOC_CRC_SEQ;
>>> +			else if (psr_err & TCAN4X5X_ERR_ACKERR)
>>> +				cf->data[3] |= CAN_ERR_PROT_LOC_ACK;
>>> +
>>> +			regmap_read(tcan4x5x->regmap, TCAN4X5X_MCAN_ECR,
>>> +				    &error_count);
>>> +			cf->data[6] = error_count & 0xff;
>>> +			cf->data[7] = error_count & 0x7f00 >> 8;
>>> +			netdev_dbg(tcan4x5x->net, "Bus Error\n");
>>> +			netif_rx_ni(skb);
>>> +		}
>>> +	}
>>> +
>>> +	if (mcan_intf & TCAN4X5X_MCAN_IR_TC) {
>>> +		net->stats.tx_packets++;
>>> +		net->stats.tx_bytes += tcan4x5x->tx_len - 1;
>>> +		can_led_event(net, CAN_LED_EVENT_TX);
>>> +		if (tcan4x5x->tx_len) {
>>> +			can_get_echo_skb(net, 0);
>>> +			tcan4x5x->tx_len = 0;
>>> +		}
>>> +		netif_wake_queue(net);
>>> +	}
>>> +
>>> +ist_out:
>>> +	regmap_write(tcan4x5x->regmap, TCAN4X5X_INT_FLAGS, TCAN4X5X_CLEAR_ALL_INT);
>>> +	regmap_write(tcan4x5x->regmap, TCAN4X5X_STATUS, TCAN4X5X_CLEAR_ALL_INT);
>>> +	regmap_write(tcan4x5x->regmap, TCAN4X5X_MCAN_INT_FLAG,
>>> +		     TCAN4X5X_CLEAR_ALL_INT);
>>> +
>>> +	mutex_unlock(&tcan4x5x->tcan4x5x_lock);
>>> +	return IRQ_HANDLED;
>>> +}
>>> +
>>> +static int tcan4x5x_do_set_bittiming(struct net_device *net)
>>> +{
>>> +	struct tcan4x5x_priv *priv = netdev_priv(net);
>>> +	struct can_bittiming *bt = &priv->can.bittiming;
>>> +	struct can_bittiming *dbt = &priv->can.data_bittiming;
>>> +	u16 brp, sjw, tseg1, tseg2;
>>> +	int ret;
>>> +	u32 val;
>>> +
>>> +	brp = bt->brp - 1;
>>> +	sjw = bt->sjw - 1;
>>> +	tseg1 = bt->prop_seg + bt->phase_seg1 - 1;
>>> +	tseg2 = bt->phase_seg2 - 1;
>>> +	val = (brp << TCAN4X5X_NBRP_SHIFT) | (sjw << TCAN4X5X_NSJW_SHIFT) |
>>> +		(tseg1 << TCAN4X5X_NTSEG1_SHIFT) | tseg2;
>>> +
>>> +	ret = regmap_write(priv->regmap, TCAN4X5X_MCAN_NBTP, val);
>>> +	if (ret)
>>> +		return -EIO;
>>> +
>>> +	if (priv->can.ctrlmode & CAN_CTRLMODE_FD) {
>>> +		val = 0;
>>> +		brp = dbt->brp - 1;
>>> +		sjw = dbt->sjw - 1;
>>> +		tseg1 = dbt->prop_seg + dbt->phase_seg1 - 1;
>>> +		tseg2 = dbt->phase_seg2 - 1;
>>> +
>>> +		/* 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(net, "TDCO value of %u is beyond maximum. Using maximum possible value\n",
>>> +					    tdco);
>>> +				tdco = 127;
>>> +			}
>>> +
>>> +			val |= DBTP_TDC;
>>> +			ret = regmap_write(priv->regmap, TCAN4X5X_MCAN_TDCR,
>>> +					   tdco << TCAN4X5X_TDCR_TDCO_SHIFT);
>>> +			if (ret)
>>> +				return -EIO;
>>> +		}
>>> +
>>> +		val |= (brp << DBTP_DBRP_SHIFT) |
>>> +			   (sjw << DBTP_DSJW_SHIFT) |
>>> +			   (tseg1 << DBTP_DTSEG1_SHIFT) |
>>> +			   (tseg2 << DBTP_DTSEG2_SHIFT);
>>> +
>>> +		ret = regmap_write(priv->regmap, TCAN4X5X_MCAN_DBTP, val);
>>> +	}
>>> +
>>> +	return ret;
>>> +}
>>> +
>>> +static int tcan4x5x_setup(struct spi_device *spi)
>>> +{
>>> +	struct tcan4x5x_priv *tcan4x5x = spi_get_drvdata(spi);
>>> +	int start_reg = TCAN4X5X_MRAM_START;
>>> +	int end_reg = start_reg + TCAN4X5X_MRAM_SIZE;
>>> +	int ret;
>>> +
>>> +	ret = regmap_write(tcan4x5x->regmap, TCAN4X5X_MCAN_INT_REG,
>>> +			   TCAN4X5X_CLEAR_ALL_INT);
>>> +	if (ret)
>>> +		return -EIO;
>>> +
>>> +	ret = regmap_write(tcan4x5x->regmap, TCAN4X5X_MCAN_INT_EN,
>>> +			   TCAN4X5X_ENABLE_MCAN_INT);
>>> +	if (ret)
>>> +		return -EIO;
>>> +
>>> +	ret = regmap_write(tcan4x5x->regmap, TCAN4X5X_MCAN_CCCR,
>>> +			   TCAN4X5X_CCCR_INIT | TCAN4X5X_CCCR_CCE);
>>> +	if (ret)
>>> +		return -EIO;
>>> +
>>> +	ret = regmap_write(tcan4x5x->regmap, TCAN4X5X_MCAN_CCCR,
>>> +			   TCAN4X5X_CCCR_INIT | TCAN4X5X_CCCR_CCE |
>>> +			   TCAN4X5X_CCCR_FDOE | TCAN4X5X_CCCR_BRSE);
>>> +	if (ret)
>>> +		return -EIO;
>>> +
>>> +	ret = tcan4x5x_do_set_bittiming(tcan4x5x->net);
>>> +	if (ret)
>>> +		return -EIO;
>>> +
>>> +	ret = regmap_write(tcan4x5x->regmap, TCAN4X5X_MCAN_TXESC,
>>> +			   TCAN4X5X_64_BYTE);
>>> +	if (ret)
>>> +		return -EIO;
>>> +
>>> +	ret = regmap_write(tcan4x5x->regmap, TCAN4X5X_MCAN_TXBC,
>>> +			   (TCAN4X5X_NUM_TX_BUF << TCAN4X5X_TX_QUEUE_SHIFT |
>>> +			   TCAN4X5X_TX_BUF_START));
>>> +	if (ret)
>>> +		return -EIO;
>>> +
>>> +	ret = regmap_write(tcan4x5x->regmap, TCAN4X5X_MCAN_RXF0C,
>>> +			   (TCAN4X5X_RX_WATER_MARK << TCAN4X5X_RX_WATER_MARK_SHIFT |
>>> +			   TCAN4X5X_NUM_RX_BUF << TCAN4X5X_RX_FIFO_SZ_SHIFT |
>>> +			   TCAN4X5X_RX_BUF_START));
>>> +	if (ret)
>>> +		return -EIO;
>>> +
>>> +	ret = regmap_write(tcan4x5x->regmap, TCAN4X5X_MCAN_RXESC,
>>> +			   (TCAN4X5X_64_BYTE << TCAN4X5X_RX_RBDS_SHIFT |
>>> +			   TCAN4X5X_64_BYTE << TCAN4X5X_RX_F1DS_SHIFT |
>>> +			   TCAN4X5X_64_BYTE));
>>> +	if (ret)
>>> +		return -EIO;
>>> +
>>> +	ret = regmap_write(tcan4x5x->regmap, TCAN4X5X_MCAN_TXBTIE,
>>> +			   TCAN4X5X_SET_ALL_INT);
>>> +	if (ret)
>>> +		return -EIO;
>>> +
>>> +
>>> +	ret = regmap_update_bits(tcan4x5x->regmap, TCAN4X5X_CONFIG,
>>> +				 TCAN4X5X_MODE_SEL_MASK, TCAN4X5X_MODE_NORMAL);
>>> +	if (ret)
>>> +		return -EIO;
>>> +
>>> +	ret = regmap_write(tcan4x5x->regmap, TCAN4X5X_MCAN_ILE, TCAN4X5X_EINT0);
>>> +	if (ret)
>>> +		return -EIO;
>>> +
>>> +	/* Zero out the MCAN buffers */
>>> +	while (start_reg < end_reg) {
>>> +		regmap_write(tcan4x5x->regmap, start_reg, 0);
>>> +		start_reg += 4;
>>> +	}
>>> +
>>> +	return ret;
>>> +}
>>> +
>>> +static void tcan4x5x_tx_work_handler(struct work_struct *ws)
>>> +{
>>> +	struct tcan4x5x_priv *tcan4x5x = container_of(ws, struct tcan4x5x_priv,
>>> +						tx_work);
>>> +	struct net_device *net = tcan4x5x->net;
>>> +	struct can_frame *frame;
>>> +
>>> +	mutex_lock(&tcan4x5x->tcan4x5x_lock);
>>> +	if (tcan4x5x->tx_skb) {
>>> +		if (tcan4x5x->can.state == CAN_STATE_BUS_OFF) {
>>> +			tcan4x5x_clean(net);
>>> +		} else {
>>> +			frame = (struct can_frame *)tcan4x5x->tx_skb->data;
>>> +			tcan4x5x_hw_tx(tcan4x5x);
>>> +			tcan4x5x->tx_len = 1 + frame->can_dlc;
>>> +			can_put_echo_skb(tcan4x5x->tx_skb, net, 0);
>>> +			tcan4x5x->tx_skb = NULL;
>>> +		}
>>> +	}
>>> +	mutex_unlock(&tcan4x5x->tcan4x5x_lock);
>>> +}
>>> +
>>> +static void tcan4x5x_restart_work_handler(struct work_struct *ws)
>>> +{
>>> +	struct tcan4x5x_priv *tcan4x5x = container_of(ws, struct tcan4x5x_priv,
>>> +						restart_work);
>>> +	struct spi_device *spi = tcan4x5x->spi;
>>> +	struct net_device *net = tcan4x5x->net;
>>> +
>>> +	mutex_lock(&tcan4x5x->tcan4x5x_lock);
>>> +	if (tcan4x5x->after_suspend) {
>>> +		tcan4x5x_reset(net);
>>> +		tcan4x5x_setup(spi);
>>> +		if (tcan4x5x->after_suspend & AFTER_SUSPEND_RESTART) {
>>> +			tcan4x5x_setup(spi);
>>> +		} else if (tcan4x5x->after_suspend & AFTER_SUSPEND_UP) {
>>> +			netif_device_attach(net);
>>> +			tcan4x5x_clean(net);
>>> +			tcan4x5x_setup(spi);
>>> +			netif_wake_queue(net);
>>> +		} else {
>>> +			tcan4x5x_sleep(spi);
>>> +		}
>>> +		tcan4x5x->after_suspend = 0;
>>> +		tcan4x5x->force_quit = 0;
>>> +	}
>>> +
>>> +	if (tcan4x5x->restart_tx) {
>>> +		tcan4x5x->restart_tx = 0;
>>> +		tcan4x5x_reset(net);
>>> +		tcan4x5x_clean(net);
>>> +		tcan4x5x_setup(spi);
>>> +		netif_wake_queue(net);
>>> +	}
>>> +	mutex_unlock(&tcan4x5x->tcan4x5x_lock);
>>> +}
>>> +
>>> +static int tcan4x5x_open(struct net_device *net)
>>> +{
>>> +	struct tcan4x5x_priv *priv = netdev_priv(net);
>>> +	struct spi_device *spi = priv->spi;
>>> +	unsigned long flags = IRQF_ONESHOT | IRQF_TRIGGER_LOW;
>>> +	int ret;
>>> +
>>> +	ret = open_candev(net);
>>> +	if (ret)
>>> +		return ret;
>>> +
>>> +	mutex_lock(&priv->tcan4x5x_lock);
>>> +	tcan4x5x_power_enable(priv->power, 1);
>>> +
>>> +	priv->force_quit = 0;
>>> +	priv->tx_skb = NULL;
>>> +	priv->tx_len = 0;
>>> +
>>> +	ret = request_threaded_irq(priv->irq, NULL, tcan4x5x_can_ist,
>>> +				   flags, DEVICE_NAME, priv);
>>> +	if (ret) {
>>> +		dev_err(&spi->dev, "failed to acquire irq %d %i\n",
>>> +			priv->irq, ret);
>>> +		goto out_close;
>>> +	}
>>> +
>>> +	priv->wq = alloc_workqueue("tcan4x5x_wq", WQ_FREEZABLE | WQ_MEM_RECLAIM,
>>> +				   0);
>>> +	if (!priv->wq) {
>>> +		ret = -ENOMEM;
>>> +		goto out_free_irq;
>>> +	}
>>> +
>>> +	INIT_WORK(&priv->tx_work, tcan4x5x_tx_work_handler);
>>> +	INIT_WORK(&priv->restart_work, tcan4x5x_restart_work_handler);
>>> +
>>> +	priv->spi_tx_buf = devm_kzalloc(&spi->dev, TCAN4X5X_BUF_LEN,
>>> +					GFP_KERNEL);
>>> +	if (!priv->spi_tx_buf) {
>>> +		ret = -ENOMEM;
>>> +		goto  out_free_wq;
>>> +	}
>>> +
>>> +	priv->spi_rx_buf = devm_kzalloc(&spi->dev, TCAN4X5X_BUF_LEN,
>>> +					GFP_KERNEL);
>>> +	if (!priv->spi_rx_buf) {
>>> +		ret = -ENOMEM;
>>> +		goto  out_free_wq;
>>> +	}
>>> +
>>> +	if (priv->wake_gpio)
>>> +		gpiod_set_value_cansleep(priv->wake_gpio, 1);
>>> +
>>> +	ret = tcan4x5x_reset(net);
>>> +	if (ret)
>>> +		goto out_free_wq;
>>> +
>>> +	ret = tcan4x5x_setup(spi);
>>> +	if (ret)
>>> +		goto out_free_wq;
>>> +
>>> +	can_led_event(net, CAN_LED_EVENT_OPEN);
>>> +	netif_wake_queue(net);
>>> +	mutex_unlock(&priv->tcan4x5x_lock);
>>> +
>>> +	return 0;
>>> +
>>> + out_free_wq:
>>> +	destroy_workqueue(priv->wq);
>>> + out_free_irq:
>>> +	free_irq(priv->irq, priv);
>>> +	tcan4x5x_sleep(spi);
>>> + out_close:
>>> +	tcan4x5x_power_enable(priv->power, 0);
>>> +	close_candev(net);
>>> +	mutex_unlock(&priv->tcan4x5x_lock);
>>> +	return ret;
>>> +}
>>> +
>>> +static int tcan4x5x_stop(struct net_device *net)
>>> +{
>>> +	struct tcan4x5x_priv *priv = netdev_priv(net);
>>> +	struct spi_device *spi = priv->spi;
>>> +
>>> +	close_candev(net);
>>> +
>>> +	priv->force_quit = 1;
>>> +	free_irq(priv->irq, priv);
>>> +	destroy_workqueue(priv->wq);
>>> +	priv->wq = NULL;
>>> +
>>> +	mutex_lock(&priv->tcan4x5x_lock);
>>> +
>>> +	priv->can.state = CAN_STATE_STOPPED;
>>> +	tcan4x5x_sleep(spi);
>>> +	tcan4x5x_power_enable(priv->power, 0);
>>> +
>>> +	mutex_unlock(&priv->tcan4x5x_lock);
>>> +
>>> +	can_led_event(net, CAN_LED_EVENT_STOP);
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static netdev_tx_t tcan4x5x_hard_start_xmit(struct sk_buff *skb,
>>> +					    struct net_device *net)
>>> +{
>>> +	struct tcan4x5x_priv *priv = netdev_priv(net);
>>> +	struct spi_device *spi = priv->spi;
>>> +
>>> +	if (priv->tx_skb || priv->tx_len) {
>>> +		dev_warn(&spi->dev, "hard_xmit called while tx busy\n");
>>> +		return NETDEV_TX_BUSY;
>>> +	}
>>> +
>>> +	if (can_dropped_invalid_skb(net, skb))
>>> +		return NETDEV_TX_OK;
>>> +
>>> +	netif_stop_queue(net);
>>> +	priv->tx_skb = skb;
>>> +	queue_work(priv->wq, &priv->tx_work);
>>> +
>>> +	return NETDEV_TX_OK;
>>> +}
>>> +
>>> +static int tcan4x5x_do_set_mode(struct net_device *net, enum can_mode mode)
>>> +{
>>> +	struct tcan4x5x_priv *priv = netdev_priv(net);
>>> +
>>> +	switch (mode) {
>>> +	case CAN_MODE_START:
>>> +		tcan4x5x_clean(net);
>>> +		priv->can.state = CAN_STATE_ERROR_ACTIVE;
>>> +		priv->restart_tx = 1;
>>> +		queue_work(priv->wq, &priv->restart_work);
>>> +		break;
>>> +	default:
>>> +		return -EOPNOTSUPP;
>>> +	}
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static const struct net_device_ops tcan4x5x_netdev_ops = {
>>> +	.ndo_open = tcan4x5x_open,
>>> +	.ndo_stop = tcan4x5x_stop,
>>> +	.ndo_start_xmit = tcan4x5x_hard_start_xmit,
>>> +	.ndo_change_mtu = can_change_mtu,
>>> +};
>>> +
>>> +static int tcan4x5x_parse_config(struct tcan4x5x_priv *tcan4x5x)
>>> +{
>>> +	tcan4x5x->reset_gpio = devm_gpiod_get_optional(&tcan4x5x->spi->dev,
>>> +						       "reset", GPIOD_OUT_LOW);
>>> +	if (IS_ERR(tcan4x5x->reset_gpio))
>>> +		tcan4x5x->reset_gpio = NULL;
>>> +
>>> +	tcan4x5x->wake_gpio = devm_gpiod_get_optional(&tcan4x5x->spi->dev,
>>> +						      "wake-up", GPIOD_OUT_LOW);
>>> +	if (IS_ERR(tcan4x5x->wake_gpio))
>>> +		tcan4x5x->wake_gpio = NULL;
>>> +
>>> +	tcan4x5x->interrupt_gpio = devm_gpiod_get(&tcan4x5x->spi->dev,
>>> +						  "data-ready", GPIOD_IN);
>>> +	if (IS_ERR(tcan4x5x->interrupt_gpio)) {
>>> +		dev_err(&tcan4x5x->spi->dev, "data-ready gpio not defined\n");
>>> +		return -EINVAL;
>>> +	}
>>> +
>>> +	tcan4x5x->irq = gpiod_to_irq(tcan4x5x->interrupt_gpio);
>>> +
>>> +	tcan4x5x->power = devm_regulator_get_optional(&tcan4x5x->spi->dev,
>>> +						      "vsup");
>>> +	if (PTR_ERR(tcan4x5x->power) == -EPROBE_DEFER)
>>> +		return -EPROBE_DEFER;
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static const struct regmap_config tcan4x5x_regmap = {
>>> +	.reg_bits = 16,
>>> +	.val_bits = 32,
>>> +	.cache_type = REGCACHE_NONE,
>>> +	.max_register = TCAN4X5X_MAX_REGISTER,
>>> +};
>>> +
>>> +static int tcan4x5x_can_probe(struct spi_device *spi)
>>> +{
>>> +	struct net_device *net;
>>> +	struct tcan4x5x_priv *priv;
>>> +	struct clk *clk;
>>> +	int freq, ret;
>>> +
>>> +	clk = devm_clk_get(&spi->dev, NULL);
>>> +	if (IS_ERR(clk)) {
>>> +		dev_err(&spi->dev, "no CAN clock source defined\n");
>>> +		freq = TCAN4X5X_EXT_CLK_DEF;
>>> +	} else {
>>> +		freq = clk_get_rate(clk);
>>> +	}
>>> +
>>> +	/* Sanity check */
>>> +	if (freq < 20000000 || freq > TCAN4X5X_EXT_CLK_DEF)
>>> +		return -ERANGE;
>>> +
>>> +	/* Allocate can/net device */
>>> +	net = alloc_candev(sizeof(*priv), TCAN4X5X_TX_ECHO_SKB_MAX);
>>> +	if (!net)
>>> +		return -ENOMEM;
>>> +
>>> +	if (!IS_ERR(clk)) {
>>> +		ret = clk_prepare_enable(clk);
>>> +		if (ret)
>>> +			goto out_free;
>>> +	}
>>> +
>>> +	net->netdev_ops = &tcan4x5x_netdev_ops;
>>> +	net->flags |= IFF_ECHO;
>>> +	net->mtu = CANFD_MTU;
>>> +
>>> +	priv = netdev_priv(net);
>>> +	priv->can.bittiming_const = &tcan4x5x_bittiming_const;
>>> +	priv->can.data_bittiming_const = &tcan4x5x_data_bittiming_const;
>>> +	priv->can.do_set_mode = tcan4x5x_do_set_mode;
>>> +	priv->can.clock.freq = freq;
>>> +	priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK |
>>> +				       CAN_CTRLMODE_LISTENONLY |
>>> +				       CAN_CTRLMODE_BERR_REPORTING |
>>> +				       CAN_CTRLMODE_FD |
>>> +				       CAN_CTRLMODE_FD_NON_ISO;
>>> +	priv->net = net;
>>> +	priv->spi = spi;
>>> +	priv->clk = clk;
>>> +	spi_set_drvdata(spi, priv);
>>> +
>>> +	ret = tcan4x5x_parse_config(priv);
>>> +	if (ret)
>>> +		goto out_clk;
>>> +
>>> +	/* Configure the SPI bus */
>>> +	spi->bits_per_word = 32;
>>> +	ret = spi_setup(spi);
>>> +	if (ret)
>>> +		goto out_clk;
>>> +
>>> +	mutex_init(&priv->tcan4x5x_lock);
>>> +
>>> +	priv->regmap = devm_regmap_init(&spi->dev, &tcan4x5x_bus,
>>> +					&spi->dev, &tcan4x5x_regmap);
>>> +
>>> +	SET_NETDEV_DEV(net, &spi->dev);
>>> +	ret = register_candev(net);
>>> +	if (ret)
>>> +		goto error_probe;
>>> +
>>> +	devm_can_led_init(net);
>>> +
>>> +	netdev_info(net, "TCAN4X5X successfully initialized.\n");
>>> +	return 0;
>>> +
>>> +error_probe:
>>> +	tcan4x5x_power_enable(priv->power, 0);
>>> +out_clk:
>>> +	if (!IS_ERR(clk))
>>> +		clk_disable_unprepare(clk);
>>> +out_free:
>>> +	free_candev(net);
>>> +	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);
>>> +	struct net_device *net = priv->net;
>>> +
>>> +	unregister_candev(net);
>>> +
>>> +	tcan4x5x_power_enable(priv->power, 0);
>>> +
>>> +	if (!IS_ERR(priv->clk))
>>> +		clk_disable_unprepare(priv->clk);
>>> +
>>> +	free_candev(net);
>>> +
>>> +	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");
>>> diff --git a/drivers/net/can/spi/tcan4x5x.h b/drivers/net/can/spi/tcan4x5x.h
>>> new file mode 100644
>>> index 000000000000..5e14ba571d49
>>> --- /dev/null
>>> +++ b/drivers/net/can/spi/tcan4x5x.h
>>> @@ -0,0 +1,109 @@
>>> +// SPDX-License-Identifier: GPL-2.0
>>> +// SPI to CAN driver for the Texas Instruments TCA4x5x
>>> +// Flash driver chip family
>>> +// Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
>>> +
>>> +#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
>>> +
>>> +#define TCAN4X5X_MCAN_CREL	0x1000
>>> +#define TCAN4X5X_MCAN_ENDN	0x1004
>>> +#define TCAN4X5X_MCAN_CUST	0x1008
>>> +#define TCAN4X5X_MCAN_DBTP	0x100C
>>> +#define TCAN4X5X_MCAN_TEST	0x1010
>>> +#define TCAN4X5X_MCAN_RWD	0x1014
>>> +#define TCAN4X5X_MCAN_CCCR	0x1018
>>> +#define TCAN4X5X_MCAN_NBTP	0x101C
>>> +#define TCAN4X5X_MCAN_TSCC	0x1020
>>> +#define TCAN4X5X_MCAN_TSCV	0x1024
>>> +#define TCAN4X5X_MCAN_TOCC	0x1028
>>> +#define TCAN4X5X_MCAN_TOCV	0x102C
>>> +#define TCAN4X5X_MCAN_ECR	0x1040
>>> +#define TCAN4X5X_MCAN_PSR	0x1044
>>> +#define TCAN4X5X_MCAN_TDCR	0x1048
>>> +#define TCAN4X5X_MCAN_INT_FLAG	0x1050
>>> +#define TCAN4X5X_MCAN_INT_EN	0x1054
>>> +#define TCAN4X5X_MCAN_ILS	0x1058
>>> +#define TCAN4X5X_MCAN_ILE	0x105C
>>> +#define TCAN4X5X_MCAN_GFC	0x1080
>>> +#define TCAN4X5X_MCAN_SIDFC	0x1084
>>> +#define TCAN4X5X_MCAN_XIDFC	0x1088
>>> +#define TCAN4X5X_MCAN_XIDAM	0x1090
>>> +#define TCAN4X5X_MCAN_HPMS	0x1094
>>> +#define TCAN4X5X_MCAN_NDAT1	0x1098
>>> +#define TCAN4X5X_MCAN_NDAT2	0x109C
>>> +#define TCAN4X5X_MCAN_RXF0C	0x10A0
>>> +#define TCAN4X5X_MCAN_RXF0S	0x10A4
>>> +#define TCAN4X5X_MCAN_RXF0A	0x10A8
>>> +#define TCAN4X5X_MCAN_RXBC	0x10AC
>>> +#define TCAN4X5X_MCAN_RXF1C	0x10B0
>>> +#define TCAN4X5X_MCAN_RXF1S	0x10B4
>>> +#define TCAN4X5X_MCAN_RXF1A	0x10B8
>>> +#define TCAN4X5X_MCAN_RXESC	0x10BC
>>> +#define TCAN4X5X_MCAN_TXBC	0x10C0
>>> +#define TCAN4X5X_MCAN_TXFQS	0x10C4
>>> +#define TCAN4X5X_MCAN_TXESC	0x10C8
>>> +#define TCAN4X5X_MCAN_TXBRP	0x10CC
>>> +#define TCAN4X5X_MCAN_TXBAR	0x10D0
>>> +#define TCAN4X5X_MCAN_TXBCR	0x10D4
>>> +#define TCAN4X5X_MCAN_TXBTO	0x10D8
>>> +#define TCAN4X5X_MCAN_TXBCF	0x10DC
>>> +#define TCAN4X5X_MCAN_TXBTIE	0x10E0
>>> +#define TCAN4X5X_MCAN_TXBCIE	0x10E4
>>> +#define TCAN4X5X_MCAN_TXEFC	0x10F0
>>> +#define TCAN4X5X_MCAN_TXEFS	0x10F4
>>> +#define TCAN4X5X_MCAN_TXEFA	0x10F8
>>> +
>>> +#define TCAN4X5X_MRAM_START	0x8000
>>> +#define TCAN4X5X_MRAM_SIZE	2048
>>> +
>>> +#define TCAN4X5X_MAX_REGISTER	0x8fff
>>> +
>>> +/* 64 byte buffer + 8 byte MCAN header */
>>> +#define TCAN4X5X_BUF_LEN 	72
>>> +
>>> +struct tcan4x5x_priv {
>>> +	struct can_priv can;
>>> +	struct net_device *net;
>>> +	struct regmap *regmap;
>>> +	struct spi_device *spi;
>>> +
>>> +	struct mutex tcan4x5x_lock; /* SPI device lock */
>>> +
>>> +	struct gpio_desc *reset_gpio;
>>> +	struct gpio_desc *interrupt_gpio;
>>> +	struct gpio_desc *wake_gpio;
>>> +	struct regulator *power;
>>> +	struct clk *clk;
>>> +
>>> +	struct sk_buff *tx_skb;
>>> +	int tx_len;
>>> +
>>> +	struct workqueue_struct *wq;
>>> +	struct work_struct tx_work;
>>> +	struct work_struct restart_work;
>>> +
>>> +	u32 *spi_tx_buf;
>>> +	u32 *spi_rx_buf;
>>> +
>>> +	int force_quit;
>>> +	int after_suspend;
>>> +#define AFTER_SUSPEND_UP 1
>>> +#define AFTER_SUSPEND_DOWN 2
>>> +#define AFTER_SUSPEND_POWER 4
>>> +#define AFTER_SUSPEND_RESTART 8
>>> +	int restart_tx;
>>> +
>>> +	int irq;
>>> +};
>>>
>>
>>


-- 
------------------
Dan Murphy

^ permalink raw reply

* Re: [PATCH 2/2] can: tcan4x5x: Add tcan4x5x driver to the kernel
From: Wolfgang Grandegger @ 2018-09-26 17:54 UTC (permalink / raw)
  To: Dan Murphy, mkl, davem; +Cc: linux-can, netdev, linux-kernel
In-Reply-To: <631340c2-78a6-eec3-0ab2-713dddd8e84f@ti.com>

Hello,

I wonder why you do not extend the existing MCAN driver by implementing
an interface to access the hardware. Would that be feasible?

Wolfgang.

Am 26.09.2018 um 19:40 schrieb Dan Murphy:
> bump
> 
> On 09/10/2018 03:12 PM, Dan Murphy wrote:
>> Add the TCAN4x5x SPI CAN driver.  This device
>> uses the Bosch MCAN IP core along with a SPI
>> interface map.  The register and data are
>> 32 bits wide.
>>
>> Signed-off-by: Dan Murphy <dmurphy@ti.com>
>> ---
>>  drivers/net/can/spi/Kconfig    |    5 +
>>  drivers/net/can/spi/Makefile   |    1 +
>>  drivers/net/can/spi/tcan4x5x.c | 1206 ++++++++++++++++++++++++++++++++
>>  drivers/net/can/spi/tcan4x5x.h |  109 +++
>>  4 files changed, 1321 insertions(+)
>>  create mode 100644 drivers/net/can/spi/tcan4x5x.c
>>  create mode 100644 drivers/net/can/spi/tcan4x5x.h
>>
>> diff --git a/drivers/net/can/spi/Kconfig b/drivers/net/can/spi/Kconfig
>> index 8f2e0dd7b756..8cac6ce37506 100644
>> --- a/drivers/net/can/spi/Kconfig
>> +++ b/drivers/net/can/spi/Kconfig
>> @@ -13,4 +13,9 @@ config CAN_MCP251X
>>  	---help---
>>  	  Driver for the Microchip MCP251x SPI CAN controllers.
>>  
>> +config CAN_TCAN4X5X
>> +	tristate "Texas Instruments TCAN4X5X SPI CAN controllers"
>> +	depends on HAS_DMA
>> +	---help---
>> +	  Driver for the Texas Instruments TCAN4X5X SPI CAN controllers.
>>  endmenu
>> diff --git a/drivers/net/can/spi/Makefile b/drivers/net/can/spi/Makefile
>> index f59fa3731073..8ecaace7a920 100644
>> --- a/drivers/net/can/spi/Makefile
>> +++ b/drivers/net/can/spi/Makefile
>> @@ -5,3 +5,4 @@
>>  
>>  obj-$(CONFIG_CAN_HI311X)	+= hi311x.o
>>  obj-$(CONFIG_CAN_MCP251X)	+= mcp251x.o
>> +obj-$(CONFIG_CAN_TCAN4X5X)	+= tcan4x5x.o
>> diff --git a/drivers/net/can/spi/tcan4x5x.c b/drivers/net/can/spi/tcan4x5x.c
>> new file mode 100644
>> index 000000000000..ca3753efe35a
>> --- /dev/null
>> +++ b/drivers/net/can/spi/tcan4x5x.c
>> @@ -0,0 +1,1206 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +// SPI to CAN driver for the Texas Instruments TCAN4x5x
>> +// Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
>> +
>> +#include <linux/can/core.h>
>> +#include <linux/can/dev.h>
>> +#include <linux/can/led.h>
>> +#include <linux/clk.h>
>> +#include <linux/completion.h>
>> +#include <linux/delay.h>
>> +#include <linux/device.h>
>> +#include <linux/dma-mapping.h>
>> +#include <linux/freezer.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/regmap.h>
>> +#include <linux/slab.h>
>> +#include <linux/spi/spi.h>
>> +#include <linux/uaccess.h>
>> +
>> +#include <linux/regulator/consumer.h>
>> +#include <linux/gpio/consumer.h>
>> +
>> +#include "tcan4x5x.h"
>> +
>> +#define DEVICE_NAME "tcan4x5x"
>> +#define TCAN4X5X_EXT_CLK_DEF	40000000
>> +
>> +#define TCAN4X5X_CLEAR_ALL_INT	0xffffffff
>> +#define TCAN4X5X_SET_ALL_INT	0xffffffff
>> +
>> +#define TCAN4X5X_TX_ECHO_SKB_MAX 1
>> +#define TCAN4X5X_DATA_PKT_OFF	2
>> +#define TCAN4X5X_WRITE_CMD	(0x61 << 24)
>> +#define TCAN4X5X_READ_CMD	(0x41 << 24)
>> +
>> +#define TCAN4X5X_SID_SHIFT	18
>> +#define TCAN4X5X_DLC_SHIFT	16
>> +
>> +#define TCAN4X5X_ESI_SHIFT	31
>> +#define TCAN4X5X_XTD_SHIFT	30
>> +#define TCAN4X5X_RTR_SHIFT	29
>> +#define TCAN4X5X_FDF_SHIFT	21
>> +#define TCAN4X5X_BRS_SHIFT	20
>> +#define TCAN4X5X_DLC_SHIFT	16
>> +
>> +#define TCAN4X5X_ESI_MASK	BIT(31)
>> +#define TCAN4X5X_XTD_MASK	BIT(30)
>> +#define TCAN4X5X_RTR_MASK	BIT(29)
>> +
>> +#define TCAN4X5X_DLC_MASK	0xf0000
>> +#define TCAN4X5X_SW_RESET	BIT(2)
>> +
>> +#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_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))
>> +
>> +/* Nominal Bit Timing & Prescaler Register */
>> +#define TCAN4X5X_NSJW_SHIFT	25
>> +#define TCAN4X5X_NBRP_SHIFT	16
>> +#define TCAN4X5X_NTSEG1_SHIFT	8
>> +
>> +#define TCAN4X5X_TDCR_TDCO_SHIFT	8
>> +
>> +/* Data Bit Timing & Prescaler Register (DBTP) */
>> +#define DBTP_TDC		BIT(23)
>> +#define DBTP_DBRP_SHIFT		16
>> +#define DBTP_DBRP_MASK		(0x1f << DBTP_DBRP_SHIFT)
>> +#define DBTP_DTSEG1_SHIFT	8
>> +#define DBTP_DTSEG1_MASK	(0x1f << DBTP_DTSEG1_SHIFT)
>> +#define DBTP_DTSEG2_SHIFT	4
>> +#define DBTP_DTSEG2_MASK	(0xf << DBTP_DTSEG2_SHIFT)
>> +#define DBTP_DSJW_SHIFT		0
>> +#define DBTP_DSJW_MASK		(0xf << DBTP_DSJW_SHIFT)
>> +
>> +#define TCAN4x5x_QUEUE_LVL_MASK		0x1f
>> +#define TCAN4x5x_QUEUE_IDX_SHIFT	16
>> +#define TCAN4x5x_QUEUE_IDX_MASK		0x1f00
>> +
>> +#define TCAN4X5X_CANBUSNOM_INT_EN	BIT(14)
>> +
>> +#define TCAN4X5X_NUM_TX_BUF	5
>> +#define TCAN4X5X_TX_QUEUE_SHIFT	24
>> +#define TCAN4X5X_TX_NDTB_SHIFT	16
>> +#define TCAN4X5X_TX_BUF_START	0x324
>> +
>> +#define TCAN4X5X_NUM_RX_BUF		3
>> +#define TCAN4X5X_RX_WATER_MARK		2
>> +#define TCAN4X5X_RX_WATER_MARK_SHIFT	24
>> +#define TCAN4X5X_RX_FIFO_SZ_SHIFT	16
>> +#define TCAN4X5X_RX_BUF_START		0x4
>> +
>> +#define TCAN4X5X_RX_F1DS_SHIFT	4
>> +#define TCAN4X5X_RX_RBDS_SHIFT	8
>> +
>> +#define TCAN4X5X_RX_FIFO0_MESSAGE	BIT(0)
>> +#define TCAN4X5X_RX_FIFO1_MESSAGE	BIT(4)
>> +#define TCAN4X5X_RX_BUFFER_MESSAGE	BIT(19)
>> +#define TCAN4X5X_RX_INDEX_MASK		0x3f00
>> +#define TCAN4X5X_RX_INDEX_SHIFT		8
>> +
>> +#define TCAN4X5X_RX_ADDR_OFFSET		0x8000
>> +#define TCAN4X5X_RX_BUF_ADDR_OFFSET	0x8100
>> +#define TCAN4X5X_RX_ADDR_MASK		0xffff
>> +
>> +#define TCAN4X5X_ERR_PROTOCOL_MASK	0x7
>> +#define TCAN4X5X_ERR_STUFERR		0x1
>> +#define TCAN4X5X_ERR_FRMERR		0x2
>> +#define TCAN4X5X_ERR_ACKERR		0x3
>> +#define TCAN4X5X_ERR_BIT1ERR		0x4
>> +#define TCAN4X5X_ERR_BIT0ERR		0x5
>> +#define TCAN4X5X_ERR_CRCERR		0x6
>> +
>> +/* 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_ALL_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)
>> +
>> +/* CCR bits */
>> +#define TCAN4X5X_CCCR_NISO_BOSCH	BIT(15)
>> +#define TCAN4X5X_CCCR_TXP		BIT(15)
>> +#define TCAN4X5X_CCCR_EFBI		BIT(13)
>> +#define TCAN4X5X_CCCR_PXHD_DIS		BIT(12)
>> +#define TCAN4X5X_CCCR_BRSE		BIT(9)
>> +#define TCAN4X5X_CCCR_FDOE		BIT(8)
>> +#define TCAN4X5X_CCCR_TEST		BIT(7)
>> +#define TCAN4X5X_CCCR_DAR_DIS		BIT(6)
>> +#define TCAN4X5X_CCCR_MON		BIT(5)
>> +#define TCAN4X5X_CCCR_CSR		BIT(4)
>> +#define TCAN4X5X_CCCR_CSA		BIT(3)
>> +#define TCAN4X5X_CCCR_ASM		BIT(2)
>> +#define TCAN4X5X_CCCR_CCE		BIT(1)
>> +#define TCAN4X5X_CCCR_INIT		BIT(0)
>> +
>> +#define TCAN4X5X_EINT0			BIT(0)
>> +#define TCAN4X5X_EINT1			BIT(1)
>> +
>> +struct tcan4x5x_rx_regs {
>> +	u32 fifo_status_reg;
>> +	u32 fifo_config_reg;
>> +	u32 fifo_ack_reg;
>> +	u32 rx_buf_shift;
>> +};
>> +
>> +struct tcan4x5x_rx_regs tcan4x5x_fifo_regs[] = {
>> +	{ TCAN4X5X_MCAN_RXF0S, TCAN4X5X_MCAN_RXF0C, TCAN4X5X_MCAN_RXF0A, 0},
>> +	{ TCAN4X5X_MCAN_RXF1S, TCAN4X5X_MCAN_RXF1C, TCAN4X5X_MCAN_RXF1A, 4},
>> +	{ TCAN4X5X_MCAN_NDAT1, TCAN4X5X_MCAN_RXBC, TCAN4X5X_MCAN_NDAT1, 8},
>> +};
>> +
>> +enum tcan4x5x_data_size {
>> +	TCAN4X5X_8_BYTE = 0,
>> +	TCAN4X5X_12_BYTE,
>> +	TCAN4X5X_16_BYTE,
>> +	TCAN4X5X_20_BYTE,
>> +	TCAN4X5X_24_BYTE,
>> +	TCAN4X5X_32_BYTE,
>> +	TCAN4X5X_48_BYTE,
>> +	TCAN4X5X_64_BYTE,
>> +};
>> +
>> +static const 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 const 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_clean(struct net_device *net)
>> +{
>> +	struct tcan4x5x_priv *priv = netdev_priv(net);
>> +
>> +	if (priv->tx_skb || priv->tx_len)
>> +		net->stats.tx_errors++;
>> +	if (priv->tx_skb)
>> +		dev_kfree_skb(priv->tx_skb);
>> +	if (priv->tx_len)
>> +		can_free_echo_skb(priv->net, 0);
>> +
>> +	priv->tx_skb = NULL;
>> +	priv->tx_len = 0;
>> +}
>> +
>> +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);
>> +	u32 addr;
>> +	struct spi_message m;
>> +	struct spi_transfer t[2] = {{ .tx_buf = &addr, .len = 4, .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 + 2;
>> +
>> +	return regmap_spi_gather_write(context, reg, 2, val, count - 2);
>> +}
>> +
>> +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, 4, 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 uint8_t tcan4x5x_dlc_conv(uint8_t input)
>> +{
>> +	const static u8 lookup[7] = {12, 16, 20, 24, 32, 48, 64};
>> +
>> +	if (input < 9)
>> +		return input;
>> +
>> +	if (input < 16)
>> +		return lookup[(unsigned int)(input - 9)];
>> +
>> +	return 0;
>> +}
>> +
>> +static uint8_t tcan4x5x_txrxesc_value(uint8_t input)
>> +{
>> +	const u8 lookup[8] = {8, 12, 16, 20, 24, 32, 48, 64};
>> +	return lookup[(unsigned int)(input & 0x07)];
>> +}
>> +
>> +static void tcan4x5x_hw_tx(struct tcan4x5x_priv *tcan4x5x)
>> +{
>> +	u32 sid, eid, exide, rtr, brs, esi, fdf, xtd, data_len;
>> +	u32 mcan_address, mcan_tx_element_sz;
>> +	int queue_stat, queue_lvl, queue_idx;
>> +	struct canfd_frame *fd_frame;
>> +	struct can_frame *frame;
>> +	int tx_element_sz, i, temp;
>> +	canid_t frame_id;
>> +	u8 dlc_len;
>> +
>> +	regmap_read(tcan4x5x->regmap, TCAN4X5X_MCAN_TXFQS, &queue_stat);
>> +	queue_lvl = queue_stat & TCAN4x5x_QUEUE_LVL_MASK;
>> +	queue_idx = (queue_stat & TCAN4x5x_QUEUE_IDX_MASK) >> TCAN4x5x_QUEUE_IDX_SHIFT;
>> +
>> +	if (tcan4x5x->tx_skb->len == CAN_MTU) {
>> +		fd_frame = NULL;
>> +		frame = (struct can_frame *)tcan4x5x->tx_skb->data;
>> +		frame_id = frame->can_id;
>> +		dlc_len = frame->can_dlc;
>> +		data_len = ((dlc_len % 4) + dlc_len) / 4;
>> +		brs = 0;
>> +	} else if (tcan4x5x->tx_skb->len == CANFD_MTU) {
>> +		frame = NULL;
>> +		fd_frame = (struct canfd_frame *)tcan4x5x->tx_skb->data;
>> +		frame_id = fd_frame->can_id;
>> +		dlc_len = fd_frame->len;
>> +		data_len = ((dlc_len % 4) + dlc_len) / 4;
>> +		brs = fd_frame->flags & CANFD_BRS;
>> +		esi = fd_frame->flags & CANFD_ESI;
>> +		fdf = 1;
>> +	} else {
>> +		return;
>> +	}
>> +
>> +	eid = frame_id & CAN_EFF_MASK;
>> +	rtr = (frame_id & CAN_RTR_FLAG) ? 1 : 0;
>> +
>> +	exide = (frame_id & CAN_EFF_FLAG) ? 1 : 0;
>> +	if (exide) {
>> +		sid = frame_id & CAN_EFF_MASK;
>> +		xtd = 1;
>> +	} else {
>> +		sid = (frame_id & CAN_SFF_MASK) << TCAN4X5X_SID_SHIFT;
>> +		xtd = 0;
>> +	}
>> +
>> +	regmap_read(tcan4x5x->regmap, TCAN4X5X_MCAN_TXBC, &mcan_address);
>> +
>> +	mcan_address = (mcan_address & 0xffff) + TCAN4X5X_MRAM_START;
>> +	temp = (uint8_t)((mcan_address >> 24) & 0x3F);
>> +
>> +	tx_element_sz = temp > 32 ? 32 : temp;
>> +	temp = (uint8_t)((mcan_address >> 16) & 0x3F);
>> +
>> +	tx_element_sz += temp > 32 ? 32 : temp;
>> +	mcan_address += ((uint32_t)tx_element_sz * queue_idx);
>> +	regmap_read(tcan4x5x->regmap, TCAN4X5X_MCAN_TXESC, &mcan_tx_element_sz);
>> +	tx_element_sz = tcan4x5x_txrxesc_value(mcan_tx_element_sz & 0x07) + 8;
>> +	mcan_address += ((uint32_t)tx_element_sz * 0);
>> +
>> +	tx_element_sz = (tcan4x5x_dlc_conv(dlc_len & 0x0F) + 8) >> 2;
>> +	if (tcan4x5x_dlc_conv(dlc_len & 0x0F) % 4)
>> +		tx_element_sz += 1;
>> +
>> +	tcan4x5x->spi_tx_buf[0] = esi << TCAN4X5X_ESI_SHIFT |
>> +				  xtd << TCAN4X5X_XTD_SHIFT |
>> +				  rtr << TCAN4X5X_RTR_SHIFT | sid;
>> +
>> +	tcan4x5x->spi_tx_buf[1] = fdf << TCAN4X5X_FDF_SHIFT |
>> +		 brs << TCAN4X5X_BRS_SHIFT | dlc_len << TCAN4X5X_DLC_SHIFT;
>> +
>> +	if (tcan4x5x->tx_skb->len == CAN_MTU)
>> +		memcpy(tcan4x5x->spi_tx_buf + TCAN4X5X_DATA_PKT_OFF,
>> +		       frame->data, dlc_len);
>> +	else
>> +		memcpy(tcan4x5x->spi_tx_buf + TCAN4X5X_DATA_PKT_OFF,
>> +		       fd_frame->data, dlc_len);
>> +
>> +	for (i = dlc_len + 1; i < TCAN4X5X_BUF_LEN / 4; i++)
>> +		tcan4x5x->spi_tx_buf[i] = 0;
>> +
>> +	regmap_bulk_write(tcan4x5x->regmap, mcan_address, tcan4x5x->spi_tx_buf,
>> +			  TCAN4X5X_BUF_LEN);
>> +
>> +	regmap_write(tcan4x5x->regmap, TCAN4X5X_MCAN_TXBAR, (1 << (queue_idx)));
>> +}
>> +
>> +int tcan4x5x_hw_rx(struct tcan4x5x_priv *tcan4x5x)
>> +{
>> +	u32 queue_idx, fifo_idx, fifo_start_addr, rx_buf_size, msg_type;
>> +	u32 data_buffer[TCAN4X5X_BUF_LEN] = {0x0};
>> +	u32 rx_header[2] = {0x0};
>> +	struct tcan4x5x_rx_regs *buffer_regs;
>> +	struct canfd_frame *fd_frame;
>> +	int dlc_len, data_len;
>> +	struct sk_buff *skb;
>> +
>> +	skb = alloc_canfd_skb(tcan4x5x->net, &fd_frame);
>> +	if (!skb) {
>> +		dev_err(&tcan4x5x->spi->dev, "cannot allocate RX skb\n");
>> +		tcan4x5x->net->stats.rx_dropped++;
>> +		return -ENOMEM;
>> +	}
>> +
>> +	regmap_read(tcan4x5x->regmap, TCAN4X5X_MCAN_INT_FLAG, &msg_type);
>> +	if (msg_type & TCAN4X5X_RX_FIFO0_MESSAGE) {
>> +		buffer_regs = &tcan4x5x_fifo_regs[0];
>> +	} else if (msg_type & TCAN4X5X_RX_FIFO1_MESSAGE) {
>> +		buffer_regs = &tcan4x5x_fifo_regs[1];
>> +	} else if (msg_type & TCAN4X5X_RX_BUFFER_MESSAGE) {
>> +		buffer_regs = &tcan4x5x_fifo_regs[2];
>> +	} else {
>> +		buffer_regs = NULL;
>> +		return -EINVAL;
>> +	}
>> +
>> +	rx_buf_size = TCAN4X5X_BUF_LEN;
>> +
>> +	/* Determine which FIFO needs service */
>> +	regmap_read(tcan4x5x->regmap, buffer_regs->fifo_status_reg, &fifo_idx);
>> +	if (msg_type & TCAN4X5X_RX_BUFFER_MESSAGE)
>> +		queue_idx = fifo_idx - 1;
>> +	else
>> +		queue_idx = (TCAN4X5X_RX_INDEX_MASK & fifo_idx) >> TCAN4X5X_RX_INDEX_SHIFT;
>> +
>> +	/* Calculate the FIFO start address to service */
>> +	regmap_read(tcan4x5x->regmap, buffer_regs->fifo_config_reg, &fifo_start_addr);
>> +	fifo_start_addr = (TCAN4X5X_RX_ADDR_MASK & fifo_start_addr);
>> +	if (msg_type & TCAN4X5X_RX_BUFFER_MESSAGE)
>> +		fifo_start_addr = fifo_start_addr + TCAN4X5X_RX_BUF_ADDR_OFFSET +
>> +				  (rx_buf_size * queue_idx);
>> +	else
>> +		fifo_start_addr = fifo_start_addr + TCAN4X5X_RX_ADDR_OFFSET +
>> +				  (rx_buf_size * queue_idx);
>> +
>> +	regmap_bulk_read(tcan4x5x->regmap, fifo_start_addr, rx_header, 2);
>> +
>> +	dlc_len = (rx_header[1] & TCAN4X5X_DLC_MASK) >> TCAN4X5X_DLC_SHIFT;
>> +	if (dlc_len <= 8)
>> +		data_len = dlc_len;
>> +	else
>> +		data_len = tcan4x5x_txrxesc_value(dlc_len);
>> +
>> +	regmap_bulk_read(tcan4x5x->regmap, fifo_start_addr + 8,
>> +			 data_buffer, data_len / 4);
>> +
>> +	/* Acknowledge receipt of the data */
>> +	regmap_write(tcan4x5x->regmap, buffer_regs->fifo_ack_reg, queue_idx);
>> +
>> +	if (rx_header[0] &  TCAN4X5X_XTD_MASK) {
>> +		fd_frame->can_id = CAN_EFF_FLAG;
>> +		fd_frame->can_id |= (rx_header[0] & CAN_EFF_MASK);
>> +	} else {
>> +		fd_frame->can_id |= ((rx_header[0] >> TCAN4X5X_SID_SHIFT) &
>> +				    CAN_SFF_MASK);
>> +	}
>> +
>> +	if (rx_header[0] & TCAN4X5X_RTR_MASK)
>> +		fd_frame->can_id |= CAN_RTR_FLAG;
>> +
>> +	if (rx_header[0] & TCAN4X5X_ESI_MASK) {
>> +		fd_frame->can_id |= CAN_ERR_FLAG;
>> +		fd_frame->flags |= CANFD_ESI;
>> +		netdev_dbg(tcan4x5x->net, "ESI Error\n");
>> +	}
>> +
>> +	fd_frame->len = data_len;
>> +	memcpy(fd_frame->data, data_buffer, fd_frame->len);
>> +
>> +	tcan4x5x->net->stats.rx_packets++;
>> +	tcan4x5x->net->stats.rx_bytes += fd_frame->len;
>> +
>> +	can_led_event(tcan4x5x->net, CAN_LED_EVENT_RX);
>> +	netif_rx_ni(skb);
>> +
>> +	return 0;
>> +}
>> +
>> +static void tcan4x5x_sleep(struct spi_device *spi)
>> +{
>> +	struct tcan4x5x_priv *tcan4x5x = spi_get_drvdata(spi);
>> +
>> +	regmap_update_bits(tcan4x5x->regmap, TCAN4X5X_CONFIG,
>> +			   TCAN4X5X_MODE_SEL_MASK, TCAN4X5X_MODE_STANDBY);
>> +}
>> +
>> +static int tcan4x5x_reset(struct net_device *net)
>> +{
>> +	struct tcan4x5x_priv *tcan4x5x = netdev_priv(net);
>> +
>> +	if (tcan4x5x->reset_gpio) {
>> +		gpiod_set_value_cansleep(tcan4x5x->reset_gpio, 1);
>> +		udelay(10);
>> +		gpiod_set_value_cansleep(tcan4x5x->reset_gpio, 0);
>> +	} else {
>> +		regmap_write(tcan4x5x->regmap, TCAN4X5X_CONFIG,
>> +			     TCAN4X5X_SW_RESET);
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +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 irqreturn_t tcan4x5x_can_ist(int irq, void *dev_id)
>> +{
>> +	struct tcan4x5x_priv *tcan4x5x = dev_id;
>> +	struct spi_device *spi = tcan4x5x->spi;
>> +	struct net_device *net = tcan4x5x->net;
>> +	enum can_state new_state;
>> +	int intf, eflag, mcan_intf;
>> +
>> +	mutex_lock(&tcan4x5x->tcan4x5x_lock);
>> +
>> +	regmap_read(tcan4x5x->regmap, TCAN4X5X_INT_FLAGS, &intf);
>> +	if (intf & TCAN4X5X_MCAN_INT)
>> +		tcan4x5x_hw_rx(tcan4x5x);
>> +
>> +	regmap_read(tcan4x5x->regmap, TCAN4X5X_MCAN_INT_FLAG, &mcan_intf);
>> +
>> +	regmap_read(tcan4x5x->regmap, TCAN4X5X_STATUS, &eflag);
>> +	/* Update can state */
>> +	if (eflag & TCAN4X5X_MCAN_IR_BO)
>> +		new_state = CAN_STATE_BUS_OFF;
>> +	else if (eflag & TCAN4X5X_MCAN_IR_EP)
>> +		new_state = CAN_STATE_ERROR_PASSIVE;
>> +	else if (eflag & TCAN4X5X_MCAN_IR_EW)
>> +		new_state = CAN_STATE_ERROR_WARNING;
>> +	else
>> +		new_state = CAN_STATE_ERROR_ACTIVE;
>> +
>> +	if (new_state != tcan4x5x->can.state) {
>> +		struct can_frame *cf;
>> +		struct sk_buff *skb;
>> +		enum can_state rx_state, tx_state;
>> +		u32 error_count;
>> +
>> +		skb = alloc_can_err_skb(net, &cf);
>> +		if (!skb)
>> +			goto ist_out;
>> +
>> +		regmap_read(tcan4x5x->regmap, TCAN4X5X_MCAN_ECR, &error_count);
>> +		cf->data[6] = error_count & 0xff;
>> +		cf->data[7] = error_count & 0x7f00 >> 8;
>> +		tx_state = cf->data[6] >= cf->data[7] ? new_state : 0;
>> +		rx_state = cf->data[6] <= cf->data[7] ? new_state : 0;
>> +		can_change_state(net, cf, tx_state, rx_state);
>> +		netif_rx_ni(skb);
>> +
>> +		if (new_state == CAN_STATE_BUS_OFF) {
>> +			can_bus_off(net);
>> +			if (tcan4x5x->can.restart_ms == 0) {
>> +				tcan4x5x->force_quit = 1;
>> +				tcan4x5x_sleep(spi);
>> +				goto ist_out;
>> +			}
>> +		}
>> +	}
>> +
>> +	/* Update bus errors */
>> +	if ((intf & TCAN4X5X_BUS_FAULT) &&
>> +	    (tcan4x5x->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)) {
>> +		struct can_frame *cf;
>> +		struct sk_buff *skb;
>> +		u32 psr_err, error_count;
>> +
>> +		/* Check for protocol errors */
>> +		regmap_read(tcan4x5x->regmap, TCAN4X5X_MCAN_PSR, &psr_err);
>> +		if (psr_err & TCAN4X5X_ERR_PROTOCOL_MASK) {
>> +			skb = alloc_can_err_skb(net, &cf);
>> +			if (!skb)
>> +				goto ist_out;
>> +
>> +			cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
>> +			tcan4x5x->can.can_stats.bus_error++;
>> +			tcan4x5x->net->stats.rx_errors++;
>> +			if (psr_err & TCAN4X5X_ERR_BIT0ERR)
>> +				cf->data[2] |= CAN_ERR_PROT_BIT0;
>> +			else if (psr_err & TCAN4X5X_ERR_BIT1ERR)
>> +				cf->data[2] |= CAN_ERR_PROT_BIT1;
>> +			else if (psr_err & TCAN4X5X_ERR_FRMERR)
>> +				cf->data[2] |= CAN_ERR_PROT_FORM;
>> +			else if (psr_err & TCAN4X5X_ERR_STUFERR)
>> +				cf->data[2] |= CAN_ERR_PROT_STUFF;
>> +			else if (psr_err & TCAN4X5X_ERR_CRCERR)
>> +				cf->data[3] |= CAN_ERR_PROT_LOC_CRC_SEQ;
>> +			else if (psr_err & TCAN4X5X_ERR_ACKERR)
>> +				cf->data[3] |= CAN_ERR_PROT_LOC_ACK;
>> +
>> +			regmap_read(tcan4x5x->regmap, TCAN4X5X_MCAN_ECR,
>> +				    &error_count);
>> +			cf->data[6] = error_count & 0xff;
>> +			cf->data[7] = error_count & 0x7f00 >> 8;
>> +			netdev_dbg(tcan4x5x->net, "Bus Error\n");
>> +			netif_rx_ni(skb);
>> +		}
>> +	}
>> +
>> +	if (mcan_intf & TCAN4X5X_MCAN_IR_TC) {
>> +		net->stats.tx_packets++;
>> +		net->stats.tx_bytes += tcan4x5x->tx_len - 1;
>> +		can_led_event(net, CAN_LED_EVENT_TX);
>> +		if (tcan4x5x->tx_len) {
>> +			can_get_echo_skb(net, 0);
>> +			tcan4x5x->tx_len = 0;
>> +		}
>> +		netif_wake_queue(net);
>> +	}
>> +
>> +ist_out:
>> +	regmap_write(tcan4x5x->regmap, TCAN4X5X_INT_FLAGS, TCAN4X5X_CLEAR_ALL_INT);
>> +	regmap_write(tcan4x5x->regmap, TCAN4X5X_STATUS, TCAN4X5X_CLEAR_ALL_INT);
>> +	regmap_write(tcan4x5x->regmap, TCAN4X5X_MCAN_INT_FLAG,
>> +		     TCAN4X5X_CLEAR_ALL_INT);
>> +
>> +	mutex_unlock(&tcan4x5x->tcan4x5x_lock);
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +static int tcan4x5x_do_set_bittiming(struct net_device *net)
>> +{
>> +	struct tcan4x5x_priv *priv = netdev_priv(net);
>> +	struct can_bittiming *bt = &priv->can.bittiming;
>> +	struct can_bittiming *dbt = &priv->can.data_bittiming;
>> +	u16 brp, sjw, tseg1, tseg2;
>> +	int ret;
>> +	u32 val;
>> +
>> +	brp = bt->brp - 1;
>> +	sjw = bt->sjw - 1;
>> +	tseg1 = bt->prop_seg + bt->phase_seg1 - 1;
>> +	tseg2 = bt->phase_seg2 - 1;
>> +	val = (brp << TCAN4X5X_NBRP_SHIFT) | (sjw << TCAN4X5X_NSJW_SHIFT) |
>> +		(tseg1 << TCAN4X5X_NTSEG1_SHIFT) | tseg2;
>> +
>> +	ret = regmap_write(priv->regmap, TCAN4X5X_MCAN_NBTP, val);
>> +	if (ret)
>> +		return -EIO;
>> +
>> +	if (priv->can.ctrlmode & CAN_CTRLMODE_FD) {
>> +		val = 0;
>> +		brp = dbt->brp - 1;
>> +		sjw = dbt->sjw - 1;
>> +		tseg1 = dbt->prop_seg + dbt->phase_seg1 - 1;
>> +		tseg2 = dbt->phase_seg2 - 1;
>> +
>> +		/* 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(net, "TDCO value of %u is beyond maximum. Using maximum possible value\n",
>> +					    tdco);
>> +				tdco = 127;
>> +			}
>> +
>> +			val |= DBTP_TDC;
>> +			ret = regmap_write(priv->regmap, TCAN4X5X_MCAN_TDCR,
>> +					   tdco << TCAN4X5X_TDCR_TDCO_SHIFT);
>> +			if (ret)
>> +				return -EIO;
>> +		}
>> +
>> +		val |= (brp << DBTP_DBRP_SHIFT) |
>> +			   (sjw << DBTP_DSJW_SHIFT) |
>> +			   (tseg1 << DBTP_DTSEG1_SHIFT) |
>> +			   (tseg2 << DBTP_DTSEG2_SHIFT);
>> +
>> +		ret = regmap_write(priv->regmap, TCAN4X5X_MCAN_DBTP, val);
>> +	}
>> +
>> +	return ret;
>> +}
>> +
>> +static int tcan4x5x_setup(struct spi_device *spi)
>> +{
>> +	struct tcan4x5x_priv *tcan4x5x = spi_get_drvdata(spi);
>> +	int start_reg = TCAN4X5X_MRAM_START;
>> +	int end_reg = start_reg + TCAN4X5X_MRAM_SIZE;
>> +	int ret;
>> +
>> +	ret = regmap_write(tcan4x5x->regmap, TCAN4X5X_MCAN_INT_REG,
>> +			   TCAN4X5X_CLEAR_ALL_INT);
>> +	if (ret)
>> +		return -EIO;
>> +
>> +	ret = regmap_write(tcan4x5x->regmap, TCAN4X5X_MCAN_INT_EN,
>> +			   TCAN4X5X_ENABLE_MCAN_INT);
>> +	if (ret)
>> +		return -EIO;
>> +
>> +	ret = regmap_write(tcan4x5x->regmap, TCAN4X5X_MCAN_CCCR,
>> +			   TCAN4X5X_CCCR_INIT | TCAN4X5X_CCCR_CCE);
>> +	if (ret)
>> +		return -EIO;
>> +
>> +	ret = regmap_write(tcan4x5x->regmap, TCAN4X5X_MCAN_CCCR,
>> +			   TCAN4X5X_CCCR_INIT | TCAN4X5X_CCCR_CCE |
>> +			   TCAN4X5X_CCCR_FDOE | TCAN4X5X_CCCR_BRSE);
>> +	if (ret)
>> +		return -EIO;
>> +
>> +	ret = tcan4x5x_do_set_bittiming(tcan4x5x->net);
>> +	if (ret)
>> +		return -EIO;
>> +
>> +	ret = regmap_write(tcan4x5x->regmap, TCAN4X5X_MCAN_TXESC,
>> +			   TCAN4X5X_64_BYTE);
>> +	if (ret)
>> +		return -EIO;
>> +
>> +	ret = regmap_write(tcan4x5x->regmap, TCAN4X5X_MCAN_TXBC,
>> +			   (TCAN4X5X_NUM_TX_BUF << TCAN4X5X_TX_QUEUE_SHIFT |
>> +			   TCAN4X5X_TX_BUF_START));
>> +	if (ret)
>> +		return -EIO;
>> +
>> +	ret = regmap_write(tcan4x5x->regmap, TCAN4X5X_MCAN_RXF0C,
>> +			   (TCAN4X5X_RX_WATER_MARK << TCAN4X5X_RX_WATER_MARK_SHIFT |
>> +			   TCAN4X5X_NUM_RX_BUF << TCAN4X5X_RX_FIFO_SZ_SHIFT |
>> +			   TCAN4X5X_RX_BUF_START));
>> +	if (ret)
>> +		return -EIO;
>> +
>> +	ret = regmap_write(tcan4x5x->regmap, TCAN4X5X_MCAN_RXESC,
>> +			   (TCAN4X5X_64_BYTE << TCAN4X5X_RX_RBDS_SHIFT |
>> +			   TCAN4X5X_64_BYTE << TCAN4X5X_RX_F1DS_SHIFT |
>> +			   TCAN4X5X_64_BYTE));
>> +	if (ret)
>> +		return -EIO;
>> +
>> +	ret = regmap_write(tcan4x5x->regmap, TCAN4X5X_MCAN_TXBTIE,
>> +			   TCAN4X5X_SET_ALL_INT);
>> +	if (ret)
>> +		return -EIO;
>> +
>> +
>> +	ret = regmap_update_bits(tcan4x5x->regmap, TCAN4X5X_CONFIG,
>> +				 TCAN4X5X_MODE_SEL_MASK, TCAN4X5X_MODE_NORMAL);
>> +	if (ret)
>> +		return -EIO;
>> +
>> +	ret = regmap_write(tcan4x5x->regmap, TCAN4X5X_MCAN_ILE, TCAN4X5X_EINT0);
>> +	if (ret)
>> +		return -EIO;
>> +
>> +	/* Zero out the MCAN buffers */
>> +	while (start_reg < end_reg) {
>> +		regmap_write(tcan4x5x->regmap, start_reg, 0);
>> +		start_reg += 4;
>> +	}
>> +
>> +	return ret;
>> +}
>> +
>> +static void tcan4x5x_tx_work_handler(struct work_struct *ws)
>> +{
>> +	struct tcan4x5x_priv *tcan4x5x = container_of(ws, struct tcan4x5x_priv,
>> +						tx_work);
>> +	struct net_device *net = tcan4x5x->net;
>> +	struct can_frame *frame;
>> +
>> +	mutex_lock(&tcan4x5x->tcan4x5x_lock);
>> +	if (tcan4x5x->tx_skb) {
>> +		if (tcan4x5x->can.state == CAN_STATE_BUS_OFF) {
>> +			tcan4x5x_clean(net);
>> +		} else {
>> +			frame = (struct can_frame *)tcan4x5x->tx_skb->data;
>> +			tcan4x5x_hw_tx(tcan4x5x);
>> +			tcan4x5x->tx_len = 1 + frame->can_dlc;
>> +			can_put_echo_skb(tcan4x5x->tx_skb, net, 0);
>> +			tcan4x5x->tx_skb = NULL;
>> +		}
>> +	}
>> +	mutex_unlock(&tcan4x5x->tcan4x5x_lock);
>> +}
>> +
>> +static void tcan4x5x_restart_work_handler(struct work_struct *ws)
>> +{
>> +	struct tcan4x5x_priv *tcan4x5x = container_of(ws, struct tcan4x5x_priv,
>> +						restart_work);
>> +	struct spi_device *spi = tcan4x5x->spi;
>> +	struct net_device *net = tcan4x5x->net;
>> +
>> +	mutex_lock(&tcan4x5x->tcan4x5x_lock);
>> +	if (tcan4x5x->after_suspend) {
>> +		tcan4x5x_reset(net);
>> +		tcan4x5x_setup(spi);
>> +		if (tcan4x5x->after_suspend & AFTER_SUSPEND_RESTART) {
>> +			tcan4x5x_setup(spi);
>> +		} else if (tcan4x5x->after_suspend & AFTER_SUSPEND_UP) {
>> +			netif_device_attach(net);
>> +			tcan4x5x_clean(net);
>> +			tcan4x5x_setup(spi);
>> +			netif_wake_queue(net);
>> +		} else {
>> +			tcan4x5x_sleep(spi);
>> +		}
>> +		tcan4x5x->after_suspend = 0;
>> +		tcan4x5x->force_quit = 0;
>> +	}
>> +
>> +	if (tcan4x5x->restart_tx) {
>> +		tcan4x5x->restart_tx = 0;
>> +		tcan4x5x_reset(net);
>> +		tcan4x5x_clean(net);
>> +		tcan4x5x_setup(spi);
>> +		netif_wake_queue(net);
>> +	}
>> +	mutex_unlock(&tcan4x5x->tcan4x5x_lock);
>> +}
>> +
>> +static int tcan4x5x_open(struct net_device *net)
>> +{
>> +	struct tcan4x5x_priv *priv = netdev_priv(net);
>> +	struct spi_device *spi = priv->spi;
>> +	unsigned long flags = IRQF_ONESHOT | IRQF_TRIGGER_LOW;
>> +	int ret;
>> +
>> +	ret = open_candev(net);
>> +	if (ret)
>> +		return ret;
>> +
>> +	mutex_lock(&priv->tcan4x5x_lock);
>> +	tcan4x5x_power_enable(priv->power, 1);
>> +
>> +	priv->force_quit = 0;
>> +	priv->tx_skb = NULL;
>> +	priv->tx_len = 0;
>> +
>> +	ret = request_threaded_irq(priv->irq, NULL, tcan4x5x_can_ist,
>> +				   flags, DEVICE_NAME, priv);
>> +	if (ret) {
>> +		dev_err(&spi->dev, "failed to acquire irq %d %i\n",
>> +			priv->irq, ret);
>> +		goto out_close;
>> +	}
>> +
>> +	priv->wq = alloc_workqueue("tcan4x5x_wq", WQ_FREEZABLE | WQ_MEM_RECLAIM,
>> +				   0);
>> +	if (!priv->wq) {
>> +		ret = -ENOMEM;
>> +		goto out_free_irq;
>> +	}
>> +
>> +	INIT_WORK(&priv->tx_work, tcan4x5x_tx_work_handler);
>> +	INIT_WORK(&priv->restart_work, tcan4x5x_restart_work_handler);
>> +
>> +	priv->spi_tx_buf = devm_kzalloc(&spi->dev, TCAN4X5X_BUF_LEN,
>> +					GFP_KERNEL);
>> +	if (!priv->spi_tx_buf) {
>> +		ret = -ENOMEM;
>> +		goto  out_free_wq;
>> +	}
>> +
>> +	priv->spi_rx_buf = devm_kzalloc(&spi->dev, TCAN4X5X_BUF_LEN,
>> +					GFP_KERNEL);
>> +	if (!priv->spi_rx_buf) {
>> +		ret = -ENOMEM;
>> +		goto  out_free_wq;
>> +	}
>> +
>> +	if (priv->wake_gpio)
>> +		gpiod_set_value_cansleep(priv->wake_gpio, 1);
>> +
>> +	ret = tcan4x5x_reset(net);
>> +	if (ret)
>> +		goto out_free_wq;
>> +
>> +	ret = tcan4x5x_setup(spi);
>> +	if (ret)
>> +		goto out_free_wq;
>> +
>> +	can_led_event(net, CAN_LED_EVENT_OPEN);
>> +	netif_wake_queue(net);
>> +	mutex_unlock(&priv->tcan4x5x_lock);
>> +
>> +	return 0;
>> +
>> + out_free_wq:
>> +	destroy_workqueue(priv->wq);
>> + out_free_irq:
>> +	free_irq(priv->irq, priv);
>> +	tcan4x5x_sleep(spi);
>> + out_close:
>> +	tcan4x5x_power_enable(priv->power, 0);
>> +	close_candev(net);
>> +	mutex_unlock(&priv->tcan4x5x_lock);
>> +	return ret;
>> +}
>> +
>> +static int tcan4x5x_stop(struct net_device *net)
>> +{
>> +	struct tcan4x5x_priv *priv = netdev_priv(net);
>> +	struct spi_device *spi = priv->spi;
>> +
>> +	close_candev(net);
>> +
>> +	priv->force_quit = 1;
>> +	free_irq(priv->irq, priv);
>> +	destroy_workqueue(priv->wq);
>> +	priv->wq = NULL;
>> +
>> +	mutex_lock(&priv->tcan4x5x_lock);
>> +
>> +	priv->can.state = CAN_STATE_STOPPED;
>> +	tcan4x5x_sleep(spi);
>> +	tcan4x5x_power_enable(priv->power, 0);
>> +
>> +	mutex_unlock(&priv->tcan4x5x_lock);
>> +
>> +	can_led_event(net, CAN_LED_EVENT_STOP);
>> +
>> +	return 0;
>> +}
>> +
>> +static netdev_tx_t tcan4x5x_hard_start_xmit(struct sk_buff *skb,
>> +					    struct net_device *net)
>> +{
>> +	struct tcan4x5x_priv *priv = netdev_priv(net);
>> +	struct spi_device *spi = priv->spi;
>> +
>> +	if (priv->tx_skb || priv->tx_len) {
>> +		dev_warn(&spi->dev, "hard_xmit called while tx busy\n");
>> +		return NETDEV_TX_BUSY;
>> +	}
>> +
>> +	if (can_dropped_invalid_skb(net, skb))
>> +		return NETDEV_TX_OK;
>> +
>> +	netif_stop_queue(net);
>> +	priv->tx_skb = skb;
>> +	queue_work(priv->wq, &priv->tx_work);
>> +
>> +	return NETDEV_TX_OK;
>> +}
>> +
>> +static int tcan4x5x_do_set_mode(struct net_device *net, enum can_mode mode)
>> +{
>> +	struct tcan4x5x_priv *priv = netdev_priv(net);
>> +
>> +	switch (mode) {
>> +	case CAN_MODE_START:
>> +		tcan4x5x_clean(net);
>> +		priv->can.state = CAN_STATE_ERROR_ACTIVE;
>> +		priv->restart_tx = 1;
>> +		queue_work(priv->wq, &priv->restart_work);
>> +		break;
>> +	default:
>> +		return -EOPNOTSUPP;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct net_device_ops tcan4x5x_netdev_ops = {
>> +	.ndo_open = tcan4x5x_open,
>> +	.ndo_stop = tcan4x5x_stop,
>> +	.ndo_start_xmit = tcan4x5x_hard_start_xmit,
>> +	.ndo_change_mtu = can_change_mtu,
>> +};
>> +
>> +static int tcan4x5x_parse_config(struct tcan4x5x_priv *tcan4x5x)
>> +{
>> +	tcan4x5x->reset_gpio = devm_gpiod_get_optional(&tcan4x5x->spi->dev,
>> +						       "reset", GPIOD_OUT_LOW);
>> +	if (IS_ERR(tcan4x5x->reset_gpio))
>> +		tcan4x5x->reset_gpio = NULL;
>> +
>> +	tcan4x5x->wake_gpio = devm_gpiod_get_optional(&tcan4x5x->spi->dev,
>> +						      "wake-up", GPIOD_OUT_LOW);
>> +	if (IS_ERR(tcan4x5x->wake_gpio))
>> +		tcan4x5x->wake_gpio = NULL;
>> +
>> +	tcan4x5x->interrupt_gpio = devm_gpiod_get(&tcan4x5x->spi->dev,
>> +						  "data-ready", GPIOD_IN);
>> +	if (IS_ERR(tcan4x5x->interrupt_gpio)) {
>> +		dev_err(&tcan4x5x->spi->dev, "data-ready gpio not defined\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	tcan4x5x->irq = gpiod_to_irq(tcan4x5x->interrupt_gpio);
>> +
>> +	tcan4x5x->power = devm_regulator_get_optional(&tcan4x5x->spi->dev,
>> +						      "vsup");
>> +	if (PTR_ERR(tcan4x5x->power) == -EPROBE_DEFER)
>> +		return -EPROBE_DEFER;
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct regmap_config tcan4x5x_regmap = {
>> +	.reg_bits = 16,
>> +	.val_bits = 32,
>> +	.cache_type = REGCACHE_NONE,
>> +	.max_register = TCAN4X5X_MAX_REGISTER,
>> +};
>> +
>> +static int tcan4x5x_can_probe(struct spi_device *spi)
>> +{
>> +	struct net_device *net;
>> +	struct tcan4x5x_priv *priv;
>> +	struct clk *clk;
>> +	int freq, ret;
>> +
>> +	clk = devm_clk_get(&spi->dev, NULL);
>> +	if (IS_ERR(clk)) {
>> +		dev_err(&spi->dev, "no CAN clock source defined\n");
>> +		freq = TCAN4X5X_EXT_CLK_DEF;
>> +	} else {
>> +		freq = clk_get_rate(clk);
>> +	}
>> +
>> +	/* Sanity check */
>> +	if (freq < 20000000 || freq > TCAN4X5X_EXT_CLK_DEF)
>> +		return -ERANGE;
>> +
>> +	/* Allocate can/net device */
>> +	net = alloc_candev(sizeof(*priv), TCAN4X5X_TX_ECHO_SKB_MAX);
>> +	if (!net)
>> +		return -ENOMEM;
>> +
>> +	if (!IS_ERR(clk)) {
>> +		ret = clk_prepare_enable(clk);
>> +		if (ret)
>> +			goto out_free;
>> +	}
>> +
>> +	net->netdev_ops = &tcan4x5x_netdev_ops;
>> +	net->flags |= IFF_ECHO;
>> +	net->mtu = CANFD_MTU;
>> +
>> +	priv = netdev_priv(net);
>> +	priv->can.bittiming_const = &tcan4x5x_bittiming_const;
>> +	priv->can.data_bittiming_const = &tcan4x5x_data_bittiming_const;
>> +	priv->can.do_set_mode = tcan4x5x_do_set_mode;
>> +	priv->can.clock.freq = freq;
>> +	priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK |
>> +				       CAN_CTRLMODE_LISTENONLY |
>> +				       CAN_CTRLMODE_BERR_REPORTING |
>> +				       CAN_CTRLMODE_FD |
>> +				       CAN_CTRLMODE_FD_NON_ISO;
>> +	priv->net = net;
>> +	priv->spi = spi;
>> +	priv->clk = clk;
>> +	spi_set_drvdata(spi, priv);
>> +
>> +	ret = tcan4x5x_parse_config(priv);
>> +	if (ret)
>> +		goto out_clk;
>> +
>> +	/* Configure the SPI bus */
>> +	spi->bits_per_word = 32;
>> +	ret = spi_setup(spi);
>> +	if (ret)
>> +		goto out_clk;
>> +
>> +	mutex_init(&priv->tcan4x5x_lock);
>> +
>> +	priv->regmap = devm_regmap_init(&spi->dev, &tcan4x5x_bus,
>> +					&spi->dev, &tcan4x5x_regmap);
>> +
>> +	SET_NETDEV_DEV(net, &spi->dev);
>> +	ret = register_candev(net);
>> +	if (ret)
>> +		goto error_probe;
>> +
>> +	devm_can_led_init(net);
>> +
>> +	netdev_info(net, "TCAN4X5X successfully initialized.\n");
>> +	return 0;
>> +
>> +error_probe:
>> +	tcan4x5x_power_enable(priv->power, 0);
>> +out_clk:
>> +	if (!IS_ERR(clk))
>> +		clk_disable_unprepare(clk);
>> +out_free:
>> +	free_candev(net);
>> +	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);
>> +	struct net_device *net = priv->net;
>> +
>> +	unregister_candev(net);
>> +
>> +	tcan4x5x_power_enable(priv->power, 0);
>> +
>> +	if (!IS_ERR(priv->clk))
>> +		clk_disable_unprepare(priv->clk);
>> +
>> +	free_candev(net);
>> +
>> +	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");
>> diff --git a/drivers/net/can/spi/tcan4x5x.h b/drivers/net/can/spi/tcan4x5x.h
>> new file mode 100644
>> index 000000000000..5e14ba571d49
>> --- /dev/null
>> +++ b/drivers/net/can/spi/tcan4x5x.h
>> @@ -0,0 +1,109 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +// SPI to CAN driver for the Texas Instruments TCA4x5x
>> +// Flash driver chip family
>> +// Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
>> +
>> +#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
>> +
>> +#define TCAN4X5X_MCAN_CREL	0x1000
>> +#define TCAN4X5X_MCAN_ENDN	0x1004
>> +#define TCAN4X5X_MCAN_CUST	0x1008
>> +#define TCAN4X5X_MCAN_DBTP	0x100C
>> +#define TCAN4X5X_MCAN_TEST	0x1010
>> +#define TCAN4X5X_MCAN_RWD	0x1014
>> +#define TCAN4X5X_MCAN_CCCR	0x1018
>> +#define TCAN4X5X_MCAN_NBTP	0x101C
>> +#define TCAN4X5X_MCAN_TSCC	0x1020
>> +#define TCAN4X5X_MCAN_TSCV	0x1024
>> +#define TCAN4X5X_MCAN_TOCC	0x1028
>> +#define TCAN4X5X_MCAN_TOCV	0x102C
>> +#define TCAN4X5X_MCAN_ECR	0x1040
>> +#define TCAN4X5X_MCAN_PSR	0x1044
>> +#define TCAN4X5X_MCAN_TDCR	0x1048
>> +#define TCAN4X5X_MCAN_INT_FLAG	0x1050
>> +#define TCAN4X5X_MCAN_INT_EN	0x1054
>> +#define TCAN4X5X_MCAN_ILS	0x1058
>> +#define TCAN4X5X_MCAN_ILE	0x105C
>> +#define TCAN4X5X_MCAN_GFC	0x1080
>> +#define TCAN4X5X_MCAN_SIDFC	0x1084
>> +#define TCAN4X5X_MCAN_XIDFC	0x1088
>> +#define TCAN4X5X_MCAN_XIDAM	0x1090
>> +#define TCAN4X5X_MCAN_HPMS	0x1094
>> +#define TCAN4X5X_MCAN_NDAT1	0x1098
>> +#define TCAN4X5X_MCAN_NDAT2	0x109C
>> +#define TCAN4X5X_MCAN_RXF0C	0x10A0
>> +#define TCAN4X5X_MCAN_RXF0S	0x10A4
>> +#define TCAN4X5X_MCAN_RXF0A	0x10A8
>> +#define TCAN4X5X_MCAN_RXBC	0x10AC
>> +#define TCAN4X5X_MCAN_RXF1C	0x10B0
>> +#define TCAN4X5X_MCAN_RXF1S	0x10B4
>> +#define TCAN4X5X_MCAN_RXF1A	0x10B8
>> +#define TCAN4X5X_MCAN_RXESC	0x10BC
>> +#define TCAN4X5X_MCAN_TXBC	0x10C0
>> +#define TCAN4X5X_MCAN_TXFQS	0x10C4
>> +#define TCAN4X5X_MCAN_TXESC	0x10C8
>> +#define TCAN4X5X_MCAN_TXBRP	0x10CC
>> +#define TCAN4X5X_MCAN_TXBAR	0x10D0
>> +#define TCAN4X5X_MCAN_TXBCR	0x10D4
>> +#define TCAN4X5X_MCAN_TXBTO	0x10D8
>> +#define TCAN4X5X_MCAN_TXBCF	0x10DC
>> +#define TCAN4X5X_MCAN_TXBTIE	0x10E0
>> +#define TCAN4X5X_MCAN_TXBCIE	0x10E4
>> +#define TCAN4X5X_MCAN_TXEFC	0x10F0
>> +#define TCAN4X5X_MCAN_TXEFS	0x10F4
>> +#define TCAN4X5X_MCAN_TXEFA	0x10F8
>> +
>> +#define TCAN4X5X_MRAM_START	0x8000
>> +#define TCAN4X5X_MRAM_SIZE	2048
>> +
>> +#define TCAN4X5X_MAX_REGISTER	0x8fff
>> +
>> +/* 64 byte buffer + 8 byte MCAN header */
>> +#define TCAN4X5X_BUF_LEN 	72
>> +
>> +struct tcan4x5x_priv {
>> +	struct can_priv can;
>> +	struct net_device *net;
>> +	struct regmap *regmap;
>> +	struct spi_device *spi;
>> +
>> +	struct mutex tcan4x5x_lock; /* SPI device lock */
>> +
>> +	struct gpio_desc *reset_gpio;
>> +	struct gpio_desc *interrupt_gpio;
>> +	struct gpio_desc *wake_gpio;
>> +	struct regulator *power;
>> +	struct clk *clk;
>> +
>> +	struct sk_buff *tx_skb;
>> +	int tx_len;
>> +
>> +	struct workqueue_struct *wq;
>> +	struct work_struct tx_work;
>> +	struct work_struct restart_work;
>> +
>> +	u32 *spi_tx_buf;
>> +	u32 *spi_rx_buf;
>> +
>> +	int force_quit;
>> +	int after_suspend;
>> +#define AFTER_SUSPEND_UP 1
>> +#define AFTER_SUSPEND_DOWN 2
>> +#define AFTER_SUSPEND_POWER 4
>> +#define AFTER_SUSPEND_RESTART 8
>> +	int restart_tx;
>> +
>> +	int irq;
>> +};
>>
> 
> 

^ permalink raw reply

* [PATCH v3 bpf-next 09/10] samples/bpf: extend test_cgrp2_attach2 test to use per-cpu cgroup storage
From: Roman Gushchin @ 2018-09-26 11:33 UTC (permalink / raw)
  To: netdev
  Cc: Song Liu, linux-kernel, kernel-team, Roman Gushchin,
	Daniel Borkmann, Alexei Starovoitov
In-Reply-To: <20180926113326.29069-1-guro@fb.com>

This commit extends the test_cgrp2_attach2 test to cover per-cpu
cgroup storage. Bpf program will use shared and per-cpu cgroup
storages simultaneously, so a better coverage of corresponding
core code will be achieved.

Expected output:
  $ ./test_cgrp2_attach2
  Attached DROP prog. This ping in cgroup /foo should fail...
  ping: sendmsg: Operation not permitted
  Attached DROP prog. This ping in cgroup /foo/bar should fail...
  ping: sendmsg: Operation not permitted
  Attached PASS prog. This ping in cgroup /foo/bar should pass...
  Detached PASS from /foo/bar while DROP is attached to /foo.
  This ping in cgroup /foo/bar should fail...
  ping: sendmsg: Operation not permitted
  Attached PASS from /foo/bar and detached DROP from /foo.
  This ping in cgroup /foo/bar should pass...
  ### override:PASS
  ### multi:PASS

Signed-off-by: Roman Gushchin <guro@fb.com>
Acked-by: Song Liu <songliubraving@fb.com>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: Alexei Starovoitov <ast@kernel.org>
---
 samples/bpf/test_cgrp2_attach2.c | 19 ++++++++++++++++++-
 1 file changed, 18 insertions(+), 1 deletion(-)

diff --git a/samples/bpf/test_cgrp2_attach2.c b/samples/bpf/test_cgrp2_attach2.c
index 180f9d813bca..d7b68ef5ba79 100644
--- a/samples/bpf/test_cgrp2_attach2.c
+++ b/samples/bpf/test_cgrp2_attach2.c
@@ -209,7 +209,7 @@ static int map_fd = -1;
 
 static int prog_load_cnt(int verdict, int val)
 {
-	int cgroup_storage_fd;
+	int cgroup_storage_fd, percpu_cgroup_storage_fd;
 
 	if (map_fd < 0)
 		map_fd = bpf_create_map(BPF_MAP_TYPE_ARRAY, 4, 8, 1, 0);
@@ -225,6 +225,14 @@ static int prog_load_cnt(int verdict, int val)
 		return -1;
 	}
 
+	percpu_cgroup_storage_fd = bpf_create_map(
+		BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE,
+		sizeof(struct bpf_cgroup_storage_key), 8, 0, 0);
+	if (percpu_cgroup_storage_fd < 0) {
+		printf("failed to create map '%s'\n", strerror(errno));
+		return -1;
+	}
+
 	struct bpf_insn prog[] = {
 		BPF_MOV32_IMM(BPF_REG_0, 0),
 		BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_0, -4), /* *(u32 *)(fp - 4) = r0 */
@@ -235,11 +243,20 @@ static int prog_load_cnt(int verdict, int val)
 		BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2),
 		BPF_MOV64_IMM(BPF_REG_1, val), /* r1 = 1 */
 		BPF_RAW_INSN(BPF_STX | BPF_XADD | BPF_DW, BPF_REG_0, BPF_REG_1, 0, 0), /* xadd r0 += r1 */
+
 		BPF_LD_MAP_FD(BPF_REG_1, cgroup_storage_fd),
 		BPF_MOV64_IMM(BPF_REG_2, 0),
 		BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_local_storage),
 		BPF_MOV64_IMM(BPF_REG_1, val),
 		BPF_RAW_INSN(BPF_STX | BPF_XADD | BPF_W, BPF_REG_0, BPF_REG_1, 0, 0),
+
+		BPF_LD_MAP_FD(BPF_REG_1, percpu_cgroup_storage_fd),
+		BPF_MOV64_IMM(BPF_REG_2, 0),
+		BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_local_storage),
+		BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_0, 0),
+		BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, 0x1),
+		BPF_STX_MEM(BPF_W, BPF_REG_0, BPF_REG_3, 0),
+
 		BPF_MOV64_IMM(BPF_REG_0, verdict), /* r0 = verdict */
 		BPF_EXIT_INSN(),
 	};
-- 
2.17.1

^ permalink raw reply related

* [PATCH v3 bpf-next 05/10] bpf: sync include/uapi/linux/bpf.h to tools/include/uapi/linux/bpf.h
From: Roman Gushchin @ 2018-09-26 11:33 UTC (permalink / raw)
  To: netdev
  Cc: Song Liu, linux-kernel, kernel-team, Roman Gushchin,
	Daniel Borkmann, Alexei Starovoitov
In-Reply-To: <20180926113326.29069-1-guro@fb.com>

The sync is required due to the appearance of a new map type:
BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE, which implements per-cpu
cgroup local storage.

Signed-off-by: Roman Gushchin <guro@fb.com>
Acked-by: Song Liu <songliubraving@fb.com>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: Alexei Starovoitov <ast@kernel.org>
---
 tools/include/uapi/linux/bpf.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
index aa5ccd2385ed..e2070d819e04 100644
--- a/tools/include/uapi/linux/bpf.h
+++ b/tools/include/uapi/linux/bpf.h
@@ -127,6 +127,7 @@ enum bpf_map_type {
 	BPF_MAP_TYPE_SOCKHASH,
 	BPF_MAP_TYPE_CGROUP_STORAGE,
 	BPF_MAP_TYPE_REUSEPORT_SOCKARRAY,
+	BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE,
 };
 
 enum bpf_prog_type {
-- 
2.17.1

^ permalink raw reply related

* [PATCH v3 bpf-next 04/10] bpf: don't allow create maps of per-cpu cgroup local storages
From: Roman Gushchin @ 2018-09-26 11:33 UTC (permalink / raw)
  To: netdev
  Cc: Song Liu, linux-kernel, kernel-team, Roman Gushchin,
	Daniel Borkmann, Alexei Starovoitov
In-Reply-To: <20180926113326.29069-1-guro@fb.com>

Explicitly forbid creating map of per-cpu cgroup local storages.
This behavior matches the behavior of shared cgroup storages.

Signed-off-by: Roman Gushchin <guro@fb.com>
Acked-by: Song Liu <songliubraving@fb.com>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: Alexei Starovoitov <ast@kernel.org>
---
 kernel/bpf/map_in_map.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/kernel/bpf/map_in_map.c b/kernel/bpf/map_in_map.c
index 3bfbf4464416..99d243e1ad6e 100644
--- a/kernel/bpf/map_in_map.c
+++ b/kernel/bpf/map_in_map.c
@@ -24,7 +24,8 @@ struct bpf_map *bpf_map_meta_alloc(int inner_map_ufd)
 	 * in the verifier is not enough.
 	 */
 	if (inner_map->map_type == BPF_MAP_TYPE_PROG_ARRAY ||
-	    inner_map->map_type == BPF_MAP_TYPE_CGROUP_STORAGE) {
+	    inner_map->map_type == BPF_MAP_TYPE_CGROUP_STORAGE ||
+	    inner_map->map_type == BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE) {
 		fdput(f);
 		return ERR_PTR(-ENOTSUPP);
 	}
-- 
2.17.1

^ permalink raw reply related

* Re: [PATCH net-next v6 07/23] zinc: ChaCha20 ARM and ARM64 implementations
From: Jason A. Donenfeld @ 2018-09-26 17:46 UTC (permalink / raw)
  To: Eric Biggers
  Cc: Ard Biesheuvel, Herbert Xu, Thomas Gleixner, LKML, Netdev,
	Linux Crypto Mailing List, David Miller, Greg Kroah-Hartman,
	Samuel Neves, Andrew Lutomirski, Jean-Philippe Aumasson,
	Russell King - ARM Linux, linux-arm-kernel
In-Reply-To: <20180926173720.GA248906@gmail.com>

On Wed, Sep 26, 2018 at 7:37 PM Eric Biggers <ebiggers@kernel.org> wrote:
> Can you please stop accusing Ard of "filibustering" your patchset?  Spending too
> long in non-preemptible code is a real problem even on non-RT systems.
> syzkaller has been reporting bugs where the kernel spins too long without any
> preemption points, both in crypto-related code and elsewhere in the kernel.  So
> we've had to add explicit preemption points to address those, as otherwise users
> can lock up all CPUs for tens of seconds.  The issue being discussed here is
> basically the same except here preemption is being explicitly disabled via
> kernel_neon_begin(), so it becomes a problem even on non-CONFIG_PREEMPT kernels.

The async distraction (re:filibustering) and the preempt concern are
two totally different things. I've already posted some code elsewhere
in this thread that addresses the preempt issue that looked good to
Ard. This will be part of v7.

^ permalink raw reply

* Re: [PATCH 2/2] can: tcan4x5x: Add tcan4x5x driver to the kernel
From: Dan Murphy @ 2018-09-26 17:40 UTC (permalink / raw)
  To: wg, mkl, davem; +Cc: linux-can, netdev, linux-kernel
In-Reply-To: <20180910201241.24092-2-dmurphy@ti.com>

bump

On 09/10/2018 03:12 PM, Dan Murphy wrote:
> Add the TCAN4x5x SPI CAN driver.  This device
> uses the Bosch MCAN IP core along with a SPI
> interface map.  The register and data are
> 32 bits wide.
> 
> Signed-off-by: Dan Murphy <dmurphy@ti.com>
> ---
>  drivers/net/can/spi/Kconfig    |    5 +
>  drivers/net/can/spi/Makefile   |    1 +
>  drivers/net/can/spi/tcan4x5x.c | 1206 ++++++++++++++++++++++++++++++++
>  drivers/net/can/spi/tcan4x5x.h |  109 +++
>  4 files changed, 1321 insertions(+)
>  create mode 100644 drivers/net/can/spi/tcan4x5x.c
>  create mode 100644 drivers/net/can/spi/tcan4x5x.h
> 
> diff --git a/drivers/net/can/spi/Kconfig b/drivers/net/can/spi/Kconfig
> index 8f2e0dd7b756..8cac6ce37506 100644
> --- a/drivers/net/can/spi/Kconfig
> +++ b/drivers/net/can/spi/Kconfig
> @@ -13,4 +13,9 @@ config CAN_MCP251X
>  	---help---
>  	  Driver for the Microchip MCP251x SPI CAN controllers.
>  
> +config CAN_TCAN4X5X
> +	tristate "Texas Instruments TCAN4X5X SPI CAN controllers"
> +	depends on HAS_DMA
> +	---help---
> +	  Driver for the Texas Instruments TCAN4X5X SPI CAN controllers.
>  endmenu
> diff --git a/drivers/net/can/spi/Makefile b/drivers/net/can/spi/Makefile
> index f59fa3731073..8ecaace7a920 100644
> --- a/drivers/net/can/spi/Makefile
> +++ b/drivers/net/can/spi/Makefile
> @@ -5,3 +5,4 @@
>  
>  obj-$(CONFIG_CAN_HI311X)	+= hi311x.o
>  obj-$(CONFIG_CAN_MCP251X)	+= mcp251x.o
> +obj-$(CONFIG_CAN_TCAN4X5X)	+= tcan4x5x.o
> diff --git a/drivers/net/can/spi/tcan4x5x.c b/drivers/net/can/spi/tcan4x5x.c
> new file mode 100644
> index 000000000000..ca3753efe35a
> --- /dev/null
> +++ b/drivers/net/can/spi/tcan4x5x.c
> @@ -0,0 +1,1206 @@
> +// SPDX-License-Identifier: GPL-2.0
> +// SPI to CAN driver for the Texas Instruments TCAN4x5x
> +// Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
> +
> +#include <linux/can/core.h>
> +#include <linux/can/dev.h>
> +#include <linux/can/led.h>
> +#include <linux/clk.h>
> +#include <linux/completion.h>
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/freezer.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/regmap.h>
> +#include <linux/slab.h>
> +#include <linux/spi/spi.h>
> +#include <linux/uaccess.h>
> +
> +#include <linux/regulator/consumer.h>
> +#include <linux/gpio/consumer.h>
> +
> +#include "tcan4x5x.h"
> +
> +#define DEVICE_NAME "tcan4x5x"
> +#define TCAN4X5X_EXT_CLK_DEF	40000000
> +
> +#define TCAN4X5X_CLEAR_ALL_INT	0xffffffff
> +#define TCAN4X5X_SET_ALL_INT	0xffffffff
> +
> +#define TCAN4X5X_TX_ECHO_SKB_MAX 1
> +#define TCAN4X5X_DATA_PKT_OFF	2
> +#define TCAN4X5X_WRITE_CMD	(0x61 << 24)
> +#define TCAN4X5X_READ_CMD	(0x41 << 24)
> +
> +#define TCAN4X5X_SID_SHIFT	18
> +#define TCAN4X5X_DLC_SHIFT	16
> +
> +#define TCAN4X5X_ESI_SHIFT	31
> +#define TCAN4X5X_XTD_SHIFT	30
> +#define TCAN4X5X_RTR_SHIFT	29
> +#define TCAN4X5X_FDF_SHIFT	21
> +#define TCAN4X5X_BRS_SHIFT	20
> +#define TCAN4X5X_DLC_SHIFT	16
> +
> +#define TCAN4X5X_ESI_MASK	BIT(31)
> +#define TCAN4X5X_XTD_MASK	BIT(30)
> +#define TCAN4X5X_RTR_MASK	BIT(29)
> +
> +#define TCAN4X5X_DLC_MASK	0xf0000
> +#define TCAN4X5X_SW_RESET	BIT(2)
> +
> +#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_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))
> +
> +/* Nominal Bit Timing & Prescaler Register */
> +#define TCAN4X5X_NSJW_SHIFT	25
> +#define TCAN4X5X_NBRP_SHIFT	16
> +#define TCAN4X5X_NTSEG1_SHIFT	8
> +
> +#define TCAN4X5X_TDCR_TDCO_SHIFT	8
> +
> +/* Data Bit Timing & Prescaler Register (DBTP) */
> +#define DBTP_TDC		BIT(23)
> +#define DBTP_DBRP_SHIFT		16
> +#define DBTP_DBRP_MASK		(0x1f << DBTP_DBRP_SHIFT)
> +#define DBTP_DTSEG1_SHIFT	8
> +#define DBTP_DTSEG1_MASK	(0x1f << DBTP_DTSEG1_SHIFT)
> +#define DBTP_DTSEG2_SHIFT	4
> +#define DBTP_DTSEG2_MASK	(0xf << DBTP_DTSEG2_SHIFT)
> +#define DBTP_DSJW_SHIFT		0
> +#define DBTP_DSJW_MASK		(0xf << DBTP_DSJW_SHIFT)
> +
> +#define TCAN4x5x_QUEUE_LVL_MASK		0x1f
> +#define TCAN4x5x_QUEUE_IDX_SHIFT	16
> +#define TCAN4x5x_QUEUE_IDX_MASK		0x1f00
> +
> +#define TCAN4X5X_CANBUSNOM_INT_EN	BIT(14)
> +
> +#define TCAN4X5X_NUM_TX_BUF	5
> +#define TCAN4X5X_TX_QUEUE_SHIFT	24
> +#define TCAN4X5X_TX_NDTB_SHIFT	16
> +#define TCAN4X5X_TX_BUF_START	0x324
> +
> +#define TCAN4X5X_NUM_RX_BUF		3
> +#define TCAN4X5X_RX_WATER_MARK		2
> +#define TCAN4X5X_RX_WATER_MARK_SHIFT	24
> +#define TCAN4X5X_RX_FIFO_SZ_SHIFT	16
> +#define TCAN4X5X_RX_BUF_START		0x4
> +
> +#define TCAN4X5X_RX_F1DS_SHIFT	4
> +#define TCAN4X5X_RX_RBDS_SHIFT	8
> +
> +#define TCAN4X5X_RX_FIFO0_MESSAGE	BIT(0)
> +#define TCAN4X5X_RX_FIFO1_MESSAGE	BIT(4)
> +#define TCAN4X5X_RX_BUFFER_MESSAGE	BIT(19)
> +#define TCAN4X5X_RX_INDEX_MASK		0x3f00
> +#define TCAN4X5X_RX_INDEX_SHIFT		8
> +
> +#define TCAN4X5X_RX_ADDR_OFFSET		0x8000
> +#define TCAN4X5X_RX_BUF_ADDR_OFFSET	0x8100
> +#define TCAN4X5X_RX_ADDR_MASK		0xffff
> +
> +#define TCAN4X5X_ERR_PROTOCOL_MASK	0x7
> +#define TCAN4X5X_ERR_STUFERR		0x1
> +#define TCAN4X5X_ERR_FRMERR		0x2
> +#define TCAN4X5X_ERR_ACKERR		0x3
> +#define TCAN4X5X_ERR_BIT1ERR		0x4
> +#define TCAN4X5X_ERR_BIT0ERR		0x5
> +#define TCAN4X5X_ERR_CRCERR		0x6
> +
> +/* 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_ALL_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)
> +
> +/* CCR bits */
> +#define TCAN4X5X_CCCR_NISO_BOSCH	BIT(15)
> +#define TCAN4X5X_CCCR_TXP		BIT(15)
> +#define TCAN4X5X_CCCR_EFBI		BIT(13)
> +#define TCAN4X5X_CCCR_PXHD_DIS		BIT(12)
> +#define TCAN4X5X_CCCR_BRSE		BIT(9)
> +#define TCAN4X5X_CCCR_FDOE		BIT(8)
> +#define TCAN4X5X_CCCR_TEST		BIT(7)
> +#define TCAN4X5X_CCCR_DAR_DIS		BIT(6)
> +#define TCAN4X5X_CCCR_MON		BIT(5)
> +#define TCAN4X5X_CCCR_CSR		BIT(4)
> +#define TCAN4X5X_CCCR_CSA		BIT(3)
> +#define TCAN4X5X_CCCR_ASM		BIT(2)
> +#define TCAN4X5X_CCCR_CCE		BIT(1)
> +#define TCAN4X5X_CCCR_INIT		BIT(0)
> +
> +#define TCAN4X5X_EINT0			BIT(0)
> +#define TCAN4X5X_EINT1			BIT(1)
> +
> +struct tcan4x5x_rx_regs {
> +	u32 fifo_status_reg;
> +	u32 fifo_config_reg;
> +	u32 fifo_ack_reg;
> +	u32 rx_buf_shift;
> +};
> +
> +struct tcan4x5x_rx_regs tcan4x5x_fifo_regs[] = {
> +	{ TCAN4X5X_MCAN_RXF0S, TCAN4X5X_MCAN_RXF0C, TCAN4X5X_MCAN_RXF0A, 0},
> +	{ TCAN4X5X_MCAN_RXF1S, TCAN4X5X_MCAN_RXF1C, TCAN4X5X_MCAN_RXF1A, 4},
> +	{ TCAN4X5X_MCAN_NDAT1, TCAN4X5X_MCAN_RXBC, TCAN4X5X_MCAN_NDAT1, 8},
> +};
> +
> +enum tcan4x5x_data_size {
> +	TCAN4X5X_8_BYTE = 0,
> +	TCAN4X5X_12_BYTE,
> +	TCAN4X5X_16_BYTE,
> +	TCAN4X5X_20_BYTE,
> +	TCAN4X5X_24_BYTE,
> +	TCAN4X5X_32_BYTE,
> +	TCAN4X5X_48_BYTE,
> +	TCAN4X5X_64_BYTE,
> +};
> +
> +static const 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 const 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_clean(struct net_device *net)
> +{
> +	struct tcan4x5x_priv *priv = netdev_priv(net);
> +
> +	if (priv->tx_skb || priv->tx_len)
> +		net->stats.tx_errors++;
> +	if (priv->tx_skb)
> +		dev_kfree_skb(priv->tx_skb);
> +	if (priv->tx_len)
> +		can_free_echo_skb(priv->net, 0);
> +
> +	priv->tx_skb = NULL;
> +	priv->tx_len = 0;
> +}
> +
> +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);
> +	u32 addr;
> +	struct spi_message m;
> +	struct spi_transfer t[2] = {{ .tx_buf = &addr, .len = 4, .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 + 2;
> +
> +	return regmap_spi_gather_write(context, reg, 2, val, count - 2);
> +}
> +
> +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, 4, 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 uint8_t tcan4x5x_dlc_conv(uint8_t input)
> +{
> +	const static u8 lookup[7] = {12, 16, 20, 24, 32, 48, 64};
> +
> +	if (input < 9)
> +		return input;
> +
> +	if (input < 16)
> +		return lookup[(unsigned int)(input - 9)];
> +
> +	return 0;
> +}
> +
> +static uint8_t tcan4x5x_txrxesc_value(uint8_t input)
> +{
> +	const u8 lookup[8] = {8, 12, 16, 20, 24, 32, 48, 64};
> +	return lookup[(unsigned int)(input & 0x07)];
> +}
> +
> +static void tcan4x5x_hw_tx(struct tcan4x5x_priv *tcan4x5x)
> +{
> +	u32 sid, eid, exide, rtr, brs, esi, fdf, xtd, data_len;
> +	u32 mcan_address, mcan_tx_element_sz;
> +	int queue_stat, queue_lvl, queue_idx;
> +	struct canfd_frame *fd_frame;
> +	struct can_frame *frame;
> +	int tx_element_sz, i, temp;
> +	canid_t frame_id;
> +	u8 dlc_len;
> +
> +	regmap_read(tcan4x5x->regmap, TCAN4X5X_MCAN_TXFQS, &queue_stat);
> +	queue_lvl = queue_stat & TCAN4x5x_QUEUE_LVL_MASK;
> +	queue_idx = (queue_stat & TCAN4x5x_QUEUE_IDX_MASK) >> TCAN4x5x_QUEUE_IDX_SHIFT;
> +
> +	if (tcan4x5x->tx_skb->len == CAN_MTU) {
> +		fd_frame = NULL;
> +		frame = (struct can_frame *)tcan4x5x->tx_skb->data;
> +		frame_id = frame->can_id;
> +		dlc_len = frame->can_dlc;
> +		data_len = ((dlc_len % 4) + dlc_len) / 4;
> +		brs = 0;
> +	} else if (tcan4x5x->tx_skb->len == CANFD_MTU) {
> +		frame = NULL;
> +		fd_frame = (struct canfd_frame *)tcan4x5x->tx_skb->data;
> +		frame_id = fd_frame->can_id;
> +		dlc_len = fd_frame->len;
> +		data_len = ((dlc_len % 4) + dlc_len) / 4;
> +		brs = fd_frame->flags & CANFD_BRS;
> +		esi = fd_frame->flags & CANFD_ESI;
> +		fdf = 1;
> +	} else {
> +		return;
> +	}
> +
> +	eid = frame_id & CAN_EFF_MASK;
> +	rtr = (frame_id & CAN_RTR_FLAG) ? 1 : 0;
> +
> +	exide = (frame_id & CAN_EFF_FLAG) ? 1 : 0;
> +	if (exide) {
> +		sid = frame_id & CAN_EFF_MASK;
> +		xtd = 1;
> +	} else {
> +		sid = (frame_id & CAN_SFF_MASK) << TCAN4X5X_SID_SHIFT;
> +		xtd = 0;
> +	}
> +
> +	regmap_read(tcan4x5x->regmap, TCAN4X5X_MCAN_TXBC, &mcan_address);
> +
> +	mcan_address = (mcan_address & 0xffff) + TCAN4X5X_MRAM_START;
> +	temp = (uint8_t)((mcan_address >> 24) & 0x3F);
> +
> +	tx_element_sz = temp > 32 ? 32 : temp;
> +	temp = (uint8_t)((mcan_address >> 16) & 0x3F);
> +
> +	tx_element_sz += temp > 32 ? 32 : temp;
> +	mcan_address += ((uint32_t)tx_element_sz * queue_idx);
> +	regmap_read(tcan4x5x->regmap, TCAN4X5X_MCAN_TXESC, &mcan_tx_element_sz);
> +	tx_element_sz = tcan4x5x_txrxesc_value(mcan_tx_element_sz & 0x07) + 8;
> +	mcan_address += ((uint32_t)tx_element_sz * 0);
> +
> +	tx_element_sz = (tcan4x5x_dlc_conv(dlc_len & 0x0F) + 8) >> 2;
> +	if (tcan4x5x_dlc_conv(dlc_len & 0x0F) % 4)
> +		tx_element_sz += 1;
> +
> +	tcan4x5x->spi_tx_buf[0] = esi << TCAN4X5X_ESI_SHIFT |
> +				  xtd << TCAN4X5X_XTD_SHIFT |
> +				  rtr << TCAN4X5X_RTR_SHIFT | sid;
> +
> +	tcan4x5x->spi_tx_buf[1] = fdf << TCAN4X5X_FDF_SHIFT |
> +		 brs << TCAN4X5X_BRS_SHIFT | dlc_len << TCAN4X5X_DLC_SHIFT;
> +
> +	if (tcan4x5x->tx_skb->len == CAN_MTU)
> +		memcpy(tcan4x5x->spi_tx_buf + TCAN4X5X_DATA_PKT_OFF,
> +		       frame->data, dlc_len);
> +	else
> +		memcpy(tcan4x5x->spi_tx_buf + TCAN4X5X_DATA_PKT_OFF,
> +		       fd_frame->data, dlc_len);
> +
> +	for (i = dlc_len + 1; i < TCAN4X5X_BUF_LEN / 4; i++)
> +		tcan4x5x->spi_tx_buf[i] = 0;
> +
> +	regmap_bulk_write(tcan4x5x->regmap, mcan_address, tcan4x5x->spi_tx_buf,
> +			  TCAN4X5X_BUF_LEN);
> +
> +	regmap_write(tcan4x5x->regmap, TCAN4X5X_MCAN_TXBAR, (1 << (queue_idx)));
> +}
> +
> +int tcan4x5x_hw_rx(struct tcan4x5x_priv *tcan4x5x)
> +{
> +	u32 queue_idx, fifo_idx, fifo_start_addr, rx_buf_size, msg_type;
> +	u32 data_buffer[TCAN4X5X_BUF_LEN] = {0x0};
> +	u32 rx_header[2] = {0x0};
> +	struct tcan4x5x_rx_regs *buffer_regs;
> +	struct canfd_frame *fd_frame;
> +	int dlc_len, data_len;
> +	struct sk_buff *skb;
> +
> +	skb = alloc_canfd_skb(tcan4x5x->net, &fd_frame);
> +	if (!skb) {
> +		dev_err(&tcan4x5x->spi->dev, "cannot allocate RX skb\n");
> +		tcan4x5x->net->stats.rx_dropped++;
> +		return -ENOMEM;
> +	}
> +
> +	regmap_read(tcan4x5x->regmap, TCAN4X5X_MCAN_INT_FLAG, &msg_type);
> +	if (msg_type & TCAN4X5X_RX_FIFO0_MESSAGE) {
> +		buffer_regs = &tcan4x5x_fifo_regs[0];
> +	} else if (msg_type & TCAN4X5X_RX_FIFO1_MESSAGE) {
> +		buffer_regs = &tcan4x5x_fifo_regs[1];
> +	} else if (msg_type & TCAN4X5X_RX_BUFFER_MESSAGE) {
> +		buffer_regs = &tcan4x5x_fifo_regs[2];
> +	} else {
> +		buffer_regs = NULL;
> +		return -EINVAL;
> +	}
> +
> +	rx_buf_size = TCAN4X5X_BUF_LEN;
> +
> +	/* Determine which FIFO needs service */
> +	regmap_read(tcan4x5x->regmap, buffer_regs->fifo_status_reg, &fifo_idx);
> +	if (msg_type & TCAN4X5X_RX_BUFFER_MESSAGE)
> +		queue_idx = fifo_idx - 1;
> +	else
> +		queue_idx = (TCAN4X5X_RX_INDEX_MASK & fifo_idx) >> TCAN4X5X_RX_INDEX_SHIFT;
> +
> +	/* Calculate the FIFO start address to service */
> +	regmap_read(tcan4x5x->regmap, buffer_regs->fifo_config_reg, &fifo_start_addr);
> +	fifo_start_addr = (TCAN4X5X_RX_ADDR_MASK & fifo_start_addr);
> +	if (msg_type & TCAN4X5X_RX_BUFFER_MESSAGE)
> +		fifo_start_addr = fifo_start_addr + TCAN4X5X_RX_BUF_ADDR_OFFSET +
> +				  (rx_buf_size * queue_idx);
> +	else
> +		fifo_start_addr = fifo_start_addr + TCAN4X5X_RX_ADDR_OFFSET +
> +				  (rx_buf_size * queue_idx);
> +
> +	regmap_bulk_read(tcan4x5x->regmap, fifo_start_addr, rx_header, 2);
> +
> +	dlc_len = (rx_header[1] & TCAN4X5X_DLC_MASK) >> TCAN4X5X_DLC_SHIFT;
> +	if (dlc_len <= 8)
> +		data_len = dlc_len;
> +	else
> +		data_len = tcan4x5x_txrxesc_value(dlc_len);
> +
> +	regmap_bulk_read(tcan4x5x->regmap, fifo_start_addr + 8,
> +			 data_buffer, data_len / 4);
> +
> +	/* Acknowledge receipt of the data */
> +	regmap_write(tcan4x5x->regmap, buffer_regs->fifo_ack_reg, queue_idx);
> +
> +	if (rx_header[0] &  TCAN4X5X_XTD_MASK) {
> +		fd_frame->can_id = CAN_EFF_FLAG;
> +		fd_frame->can_id |= (rx_header[0] & CAN_EFF_MASK);
> +	} else {
> +		fd_frame->can_id |= ((rx_header[0] >> TCAN4X5X_SID_SHIFT) &
> +				    CAN_SFF_MASK);
> +	}
> +
> +	if (rx_header[0] & TCAN4X5X_RTR_MASK)
> +		fd_frame->can_id |= CAN_RTR_FLAG;
> +
> +	if (rx_header[0] & TCAN4X5X_ESI_MASK) {
> +		fd_frame->can_id |= CAN_ERR_FLAG;
> +		fd_frame->flags |= CANFD_ESI;
> +		netdev_dbg(tcan4x5x->net, "ESI Error\n");
> +	}
> +
> +	fd_frame->len = data_len;
> +	memcpy(fd_frame->data, data_buffer, fd_frame->len);
> +
> +	tcan4x5x->net->stats.rx_packets++;
> +	tcan4x5x->net->stats.rx_bytes += fd_frame->len;
> +
> +	can_led_event(tcan4x5x->net, CAN_LED_EVENT_RX);
> +	netif_rx_ni(skb);
> +
> +	return 0;
> +}
> +
> +static void tcan4x5x_sleep(struct spi_device *spi)
> +{
> +	struct tcan4x5x_priv *tcan4x5x = spi_get_drvdata(spi);
> +
> +	regmap_update_bits(tcan4x5x->regmap, TCAN4X5X_CONFIG,
> +			   TCAN4X5X_MODE_SEL_MASK, TCAN4X5X_MODE_STANDBY);
> +}
> +
> +static int tcan4x5x_reset(struct net_device *net)
> +{
> +	struct tcan4x5x_priv *tcan4x5x = netdev_priv(net);
> +
> +	if (tcan4x5x->reset_gpio) {
> +		gpiod_set_value_cansleep(tcan4x5x->reset_gpio, 1);
> +		udelay(10);
> +		gpiod_set_value_cansleep(tcan4x5x->reset_gpio, 0);
> +	} else {
> +		regmap_write(tcan4x5x->regmap, TCAN4X5X_CONFIG,
> +			     TCAN4X5X_SW_RESET);
> +	}
> +
> +	return 0;
> +}
> +
> +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 irqreturn_t tcan4x5x_can_ist(int irq, void *dev_id)
> +{
> +	struct tcan4x5x_priv *tcan4x5x = dev_id;
> +	struct spi_device *spi = tcan4x5x->spi;
> +	struct net_device *net = tcan4x5x->net;
> +	enum can_state new_state;
> +	int intf, eflag, mcan_intf;
> +
> +	mutex_lock(&tcan4x5x->tcan4x5x_lock);
> +
> +	regmap_read(tcan4x5x->regmap, TCAN4X5X_INT_FLAGS, &intf);
> +	if (intf & TCAN4X5X_MCAN_INT)
> +		tcan4x5x_hw_rx(tcan4x5x);
> +
> +	regmap_read(tcan4x5x->regmap, TCAN4X5X_MCAN_INT_FLAG, &mcan_intf);
> +
> +	regmap_read(tcan4x5x->regmap, TCAN4X5X_STATUS, &eflag);
> +	/* Update can state */
> +	if (eflag & TCAN4X5X_MCAN_IR_BO)
> +		new_state = CAN_STATE_BUS_OFF;
> +	else if (eflag & TCAN4X5X_MCAN_IR_EP)
> +		new_state = CAN_STATE_ERROR_PASSIVE;
> +	else if (eflag & TCAN4X5X_MCAN_IR_EW)
> +		new_state = CAN_STATE_ERROR_WARNING;
> +	else
> +		new_state = CAN_STATE_ERROR_ACTIVE;
> +
> +	if (new_state != tcan4x5x->can.state) {
> +		struct can_frame *cf;
> +		struct sk_buff *skb;
> +		enum can_state rx_state, tx_state;
> +		u32 error_count;
> +
> +		skb = alloc_can_err_skb(net, &cf);
> +		if (!skb)
> +			goto ist_out;
> +
> +		regmap_read(tcan4x5x->regmap, TCAN4X5X_MCAN_ECR, &error_count);
> +		cf->data[6] = error_count & 0xff;
> +		cf->data[7] = error_count & 0x7f00 >> 8;
> +		tx_state = cf->data[6] >= cf->data[7] ? new_state : 0;
> +		rx_state = cf->data[6] <= cf->data[7] ? new_state : 0;
> +		can_change_state(net, cf, tx_state, rx_state);
> +		netif_rx_ni(skb);
> +
> +		if (new_state == CAN_STATE_BUS_OFF) {
> +			can_bus_off(net);
> +			if (tcan4x5x->can.restart_ms == 0) {
> +				tcan4x5x->force_quit = 1;
> +				tcan4x5x_sleep(spi);
> +				goto ist_out;
> +			}
> +		}
> +	}
> +
> +	/* Update bus errors */
> +	if ((intf & TCAN4X5X_BUS_FAULT) &&
> +	    (tcan4x5x->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)) {
> +		struct can_frame *cf;
> +		struct sk_buff *skb;
> +		u32 psr_err, error_count;
> +
> +		/* Check for protocol errors */
> +		regmap_read(tcan4x5x->regmap, TCAN4X5X_MCAN_PSR, &psr_err);
> +		if (psr_err & TCAN4X5X_ERR_PROTOCOL_MASK) {
> +			skb = alloc_can_err_skb(net, &cf);
> +			if (!skb)
> +				goto ist_out;
> +
> +			cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
> +			tcan4x5x->can.can_stats.bus_error++;
> +			tcan4x5x->net->stats.rx_errors++;
> +			if (psr_err & TCAN4X5X_ERR_BIT0ERR)
> +				cf->data[2] |= CAN_ERR_PROT_BIT0;
> +			else if (psr_err & TCAN4X5X_ERR_BIT1ERR)
> +				cf->data[2] |= CAN_ERR_PROT_BIT1;
> +			else if (psr_err & TCAN4X5X_ERR_FRMERR)
> +				cf->data[2] |= CAN_ERR_PROT_FORM;
> +			else if (psr_err & TCAN4X5X_ERR_STUFERR)
> +				cf->data[2] |= CAN_ERR_PROT_STUFF;
> +			else if (psr_err & TCAN4X5X_ERR_CRCERR)
> +				cf->data[3] |= CAN_ERR_PROT_LOC_CRC_SEQ;
> +			else if (psr_err & TCAN4X5X_ERR_ACKERR)
> +				cf->data[3] |= CAN_ERR_PROT_LOC_ACK;
> +
> +			regmap_read(tcan4x5x->regmap, TCAN4X5X_MCAN_ECR,
> +				    &error_count);
> +			cf->data[6] = error_count & 0xff;
> +			cf->data[7] = error_count & 0x7f00 >> 8;
> +			netdev_dbg(tcan4x5x->net, "Bus Error\n");
> +			netif_rx_ni(skb);
> +		}
> +	}
> +
> +	if (mcan_intf & TCAN4X5X_MCAN_IR_TC) {
> +		net->stats.tx_packets++;
> +		net->stats.tx_bytes += tcan4x5x->tx_len - 1;
> +		can_led_event(net, CAN_LED_EVENT_TX);
> +		if (tcan4x5x->tx_len) {
> +			can_get_echo_skb(net, 0);
> +			tcan4x5x->tx_len = 0;
> +		}
> +		netif_wake_queue(net);
> +	}
> +
> +ist_out:
> +	regmap_write(tcan4x5x->regmap, TCAN4X5X_INT_FLAGS, TCAN4X5X_CLEAR_ALL_INT);
> +	regmap_write(tcan4x5x->regmap, TCAN4X5X_STATUS, TCAN4X5X_CLEAR_ALL_INT);
> +	regmap_write(tcan4x5x->regmap, TCAN4X5X_MCAN_INT_FLAG,
> +		     TCAN4X5X_CLEAR_ALL_INT);
> +
> +	mutex_unlock(&tcan4x5x->tcan4x5x_lock);
> +	return IRQ_HANDLED;
> +}
> +
> +static int tcan4x5x_do_set_bittiming(struct net_device *net)
> +{
> +	struct tcan4x5x_priv *priv = netdev_priv(net);
> +	struct can_bittiming *bt = &priv->can.bittiming;
> +	struct can_bittiming *dbt = &priv->can.data_bittiming;
> +	u16 brp, sjw, tseg1, tseg2;
> +	int ret;
> +	u32 val;
> +
> +	brp = bt->brp - 1;
> +	sjw = bt->sjw - 1;
> +	tseg1 = bt->prop_seg + bt->phase_seg1 - 1;
> +	tseg2 = bt->phase_seg2 - 1;
> +	val = (brp << TCAN4X5X_NBRP_SHIFT) | (sjw << TCAN4X5X_NSJW_SHIFT) |
> +		(tseg1 << TCAN4X5X_NTSEG1_SHIFT) | tseg2;
> +
> +	ret = regmap_write(priv->regmap, TCAN4X5X_MCAN_NBTP, val);
> +	if (ret)
> +		return -EIO;
> +
> +	if (priv->can.ctrlmode & CAN_CTRLMODE_FD) {
> +		val = 0;
> +		brp = dbt->brp - 1;
> +		sjw = dbt->sjw - 1;
> +		tseg1 = dbt->prop_seg + dbt->phase_seg1 - 1;
> +		tseg2 = dbt->phase_seg2 - 1;
> +
> +		/* 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(net, "TDCO value of %u is beyond maximum. Using maximum possible value\n",
> +					    tdco);
> +				tdco = 127;
> +			}
> +
> +			val |= DBTP_TDC;
> +			ret = regmap_write(priv->regmap, TCAN4X5X_MCAN_TDCR,
> +					   tdco << TCAN4X5X_TDCR_TDCO_SHIFT);
> +			if (ret)
> +				return -EIO;
> +		}
> +
> +		val |= (brp << DBTP_DBRP_SHIFT) |
> +			   (sjw << DBTP_DSJW_SHIFT) |
> +			   (tseg1 << DBTP_DTSEG1_SHIFT) |
> +			   (tseg2 << DBTP_DTSEG2_SHIFT);
> +
> +		ret = regmap_write(priv->regmap, TCAN4X5X_MCAN_DBTP, val);
> +	}
> +
> +	return ret;
> +}
> +
> +static int tcan4x5x_setup(struct spi_device *spi)
> +{
> +	struct tcan4x5x_priv *tcan4x5x = spi_get_drvdata(spi);
> +	int start_reg = TCAN4X5X_MRAM_START;
> +	int end_reg = start_reg + TCAN4X5X_MRAM_SIZE;
> +	int ret;
> +
> +	ret = regmap_write(tcan4x5x->regmap, TCAN4X5X_MCAN_INT_REG,
> +			   TCAN4X5X_CLEAR_ALL_INT);
> +	if (ret)
> +		return -EIO;
> +
> +	ret = regmap_write(tcan4x5x->regmap, TCAN4X5X_MCAN_INT_EN,
> +			   TCAN4X5X_ENABLE_MCAN_INT);
> +	if (ret)
> +		return -EIO;
> +
> +	ret = regmap_write(tcan4x5x->regmap, TCAN4X5X_MCAN_CCCR,
> +			   TCAN4X5X_CCCR_INIT | TCAN4X5X_CCCR_CCE);
> +	if (ret)
> +		return -EIO;
> +
> +	ret = regmap_write(tcan4x5x->regmap, TCAN4X5X_MCAN_CCCR,
> +			   TCAN4X5X_CCCR_INIT | TCAN4X5X_CCCR_CCE |
> +			   TCAN4X5X_CCCR_FDOE | TCAN4X5X_CCCR_BRSE);
> +	if (ret)
> +		return -EIO;
> +
> +	ret = tcan4x5x_do_set_bittiming(tcan4x5x->net);
> +	if (ret)
> +		return -EIO;
> +
> +	ret = regmap_write(tcan4x5x->regmap, TCAN4X5X_MCAN_TXESC,
> +			   TCAN4X5X_64_BYTE);
> +	if (ret)
> +		return -EIO;
> +
> +	ret = regmap_write(tcan4x5x->regmap, TCAN4X5X_MCAN_TXBC,
> +			   (TCAN4X5X_NUM_TX_BUF << TCAN4X5X_TX_QUEUE_SHIFT |
> +			   TCAN4X5X_TX_BUF_START));
> +	if (ret)
> +		return -EIO;
> +
> +	ret = regmap_write(tcan4x5x->regmap, TCAN4X5X_MCAN_RXF0C,
> +			   (TCAN4X5X_RX_WATER_MARK << TCAN4X5X_RX_WATER_MARK_SHIFT |
> +			   TCAN4X5X_NUM_RX_BUF << TCAN4X5X_RX_FIFO_SZ_SHIFT |
> +			   TCAN4X5X_RX_BUF_START));
> +	if (ret)
> +		return -EIO;
> +
> +	ret = regmap_write(tcan4x5x->regmap, TCAN4X5X_MCAN_RXESC,
> +			   (TCAN4X5X_64_BYTE << TCAN4X5X_RX_RBDS_SHIFT |
> +			   TCAN4X5X_64_BYTE << TCAN4X5X_RX_F1DS_SHIFT |
> +			   TCAN4X5X_64_BYTE));
> +	if (ret)
> +		return -EIO;
> +
> +	ret = regmap_write(tcan4x5x->regmap, TCAN4X5X_MCAN_TXBTIE,
> +			   TCAN4X5X_SET_ALL_INT);
> +	if (ret)
> +		return -EIO;
> +
> +
> +	ret = regmap_update_bits(tcan4x5x->regmap, TCAN4X5X_CONFIG,
> +				 TCAN4X5X_MODE_SEL_MASK, TCAN4X5X_MODE_NORMAL);
> +	if (ret)
> +		return -EIO;
> +
> +	ret = regmap_write(tcan4x5x->regmap, TCAN4X5X_MCAN_ILE, TCAN4X5X_EINT0);
> +	if (ret)
> +		return -EIO;
> +
> +	/* Zero out the MCAN buffers */
> +	while (start_reg < end_reg) {
> +		regmap_write(tcan4x5x->regmap, start_reg, 0);
> +		start_reg += 4;
> +	}
> +
> +	return ret;
> +}
> +
> +static void tcan4x5x_tx_work_handler(struct work_struct *ws)
> +{
> +	struct tcan4x5x_priv *tcan4x5x = container_of(ws, struct tcan4x5x_priv,
> +						tx_work);
> +	struct net_device *net = tcan4x5x->net;
> +	struct can_frame *frame;
> +
> +	mutex_lock(&tcan4x5x->tcan4x5x_lock);
> +	if (tcan4x5x->tx_skb) {
> +		if (tcan4x5x->can.state == CAN_STATE_BUS_OFF) {
> +			tcan4x5x_clean(net);
> +		} else {
> +			frame = (struct can_frame *)tcan4x5x->tx_skb->data;
> +			tcan4x5x_hw_tx(tcan4x5x);
> +			tcan4x5x->tx_len = 1 + frame->can_dlc;
> +			can_put_echo_skb(tcan4x5x->tx_skb, net, 0);
> +			tcan4x5x->tx_skb = NULL;
> +		}
> +	}
> +	mutex_unlock(&tcan4x5x->tcan4x5x_lock);
> +}
> +
> +static void tcan4x5x_restart_work_handler(struct work_struct *ws)
> +{
> +	struct tcan4x5x_priv *tcan4x5x = container_of(ws, struct tcan4x5x_priv,
> +						restart_work);
> +	struct spi_device *spi = tcan4x5x->spi;
> +	struct net_device *net = tcan4x5x->net;
> +
> +	mutex_lock(&tcan4x5x->tcan4x5x_lock);
> +	if (tcan4x5x->after_suspend) {
> +		tcan4x5x_reset(net);
> +		tcan4x5x_setup(spi);
> +		if (tcan4x5x->after_suspend & AFTER_SUSPEND_RESTART) {
> +			tcan4x5x_setup(spi);
> +		} else if (tcan4x5x->after_suspend & AFTER_SUSPEND_UP) {
> +			netif_device_attach(net);
> +			tcan4x5x_clean(net);
> +			tcan4x5x_setup(spi);
> +			netif_wake_queue(net);
> +		} else {
> +			tcan4x5x_sleep(spi);
> +		}
> +		tcan4x5x->after_suspend = 0;
> +		tcan4x5x->force_quit = 0;
> +	}
> +
> +	if (tcan4x5x->restart_tx) {
> +		tcan4x5x->restart_tx = 0;
> +		tcan4x5x_reset(net);
> +		tcan4x5x_clean(net);
> +		tcan4x5x_setup(spi);
> +		netif_wake_queue(net);
> +	}
> +	mutex_unlock(&tcan4x5x->tcan4x5x_lock);
> +}
> +
> +static int tcan4x5x_open(struct net_device *net)
> +{
> +	struct tcan4x5x_priv *priv = netdev_priv(net);
> +	struct spi_device *spi = priv->spi;
> +	unsigned long flags = IRQF_ONESHOT | IRQF_TRIGGER_LOW;
> +	int ret;
> +
> +	ret = open_candev(net);
> +	if (ret)
> +		return ret;
> +
> +	mutex_lock(&priv->tcan4x5x_lock);
> +	tcan4x5x_power_enable(priv->power, 1);
> +
> +	priv->force_quit = 0;
> +	priv->tx_skb = NULL;
> +	priv->tx_len = 0;
> +
> +	ret = request_threaded_irq(priv->irq, NULL, tcan4x5x_can_ist,
> +				   flags, DEVICE_NAME, priv);
> +	if (ret) {
> +		dev_err(&spi->dev, "failed to acquire irq %d %i\n",
> +			priv->irq, ret);
> +		goto out_close;
> +	}
> +
> +	priv->wq = alloc_workqueue("tcan4x5x_wq", WQ_FREEZABLE | WQ_MEM_RECLAIM,
> +				   0);
> +	if (!priv->wq) {
> +		ret = -ENOMEM;
> +		goto out_free_irq;
> +	}
> +
> +	INIT_WORK(&priv->tx_work, tcan4x5x_tx_work_handler);
> +	INIT_WORK(&priv->restart_work, tcan4x5x_restart_work_handler);
> +
> +	priv->spi_tx_buf = devm_kzalloc(&spi->dev, TCAN4X5X_BUF_LEN,
> +					GFP_KERNEL);
> +	if (!priv->spi_tx_buf) {
> +		ret = -ENOMEM;
> +		goto  out_free_wq;
> +	}
> +
> +	priv->spi_rx_buf = devm_kzalloc(&spi->dev, TCAN4X5X_BUF_LEN,
> +					GFP_KERNEL);
> +	if (!priv->spi_rx_buf) {
> +		ret = -ENOMEM;
> +		goto  out_free_wq;
> +	}
> +
> +	if (priv->wake_gpio)
> +		gpiod_set_value_cansleep(priv->wake_gpio, 1);
> +
> +	ret = tcan4x5x_reset(net);
> +	if (ret)
> +		goto out_free_wq;
> +
> +	ret = tcan4x5x_setup(spi);
> +	if (ret)
> +		goto out_free_wq;
> +
> +	can_led_event(net, CAN_LED_EVENT_OPEN);
> +	netif_wake_queue(net);
> +	mutex_unlock(&priv->tcan4x5x_lock);
> +
> +	return 0;
> +
> + out_free_wq:
> +	destroy_workqueue(priv->wq);
> + out_free_irq:
> +	free_irq(priv->irq, priv);
> +	tcan4x5x_sleep(spi);
> + out_close:
> +	tcan4x5x_power_enable(priv->power, 0);
> +	close_candev(net);
> +	mutex_unlock(&priv->tcan4x5x_lock);
> +	return ret;
> +}
> +
> +static int tcan4x5x_stop(struct net_device *net)
> +{
> +	struct tcan4x5x_priv *priv = netdev_priv(net);
> +	struct spi_device *spi = priv->spi;
> +
> +	close_candev(net);
> +
> +	priv->force_quit = 1;
> +	free_irq(priv->irq, priv);
> +	destroy_workqueue(priv->wq);
> +	priv->wq = NULL;
> +
> +	mutex_lock(&priv->tcan4x5x_lock);
> +
> +	priv->can.state = CAN_STATE_STOPPED;
> +	tcan4x5x_sleep(spi);
> +	tcan4x5x_power_enable(priv->power, 0);
> +
> +	mutex_unlock(&priv->tcan4x5x_lock);
> +
> +	can_led_event(net, CAN_LED_EVENT_STOP);
> +
> +	return 0;
> +}
> +
> +static netdev_tx_t tcan4x5x_hard_start_xmit(struct sk_buff *skb,
> +					    struct net_device *net)
> +{
> +	struct tcan4x5x_priv *priv = netdev_priv(net);
> +	struct spi_device *spi = priv->spi;
> +
> +	if (priv->tx_skb || priv->tx_len) {
> +		dev_warn(&spi->dev, "hard_xmit called while tx busy\n");
> +		return NETDEV_TX_BUSY;
> +	}
> +
> +	if (can_dropped_invalid_skb(net, skb))
> +		return NETDEV_TX_OK;
> +
> +	netif_stop_queue(net);
> +	priv->tx_skb = skb;
> +	queue_work(priv->wq, &priv->tx_work);
> +
> +	return NETDEV_TX_OK;
> +}
> +
> +static int tcan4x5x_do_set_mode(struct net_device *net, enum can_mode mode)
> +{
> +	struct tcan4x5x_priv *priv = netdev_priv(net);
> +
> +	switch (mode) {
> +	case CAN_MODE_START:
> +		tcan4x5x_clean(net);
> +		priv->can.state = CAN_STATE_ERROR_ACTIVE;
> +		priv->restart_tx = 1;
> +		queue_work(priv->wq, &priv->restart_work);
> +		break;
> +	default:
> +		return -EOPNOTSUPP;
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct net_device_ops tcan4x5x_netdev_ops = {
> +	.ndo_open = tcan4x5x_open,
> +	.ndo_stop = tcan4x5x_stop,
> +	.ndo_start_xmit = tcan4x5x_hard_start_xmit,
> +	.ndo_change_mtu = can_change_mtu,
> +};
> +
> +static int tcan4x5x_parse_config(struct tcan4x5x_priv *tcan4x5x)
> +{
> +	tcan4x5x->reset_gpio = devm_gpiod_get_optional(&tcan4x5x->spi->dev,
> +						       "reset", GPIOD_OUT_LOW);
> +	if (IS_ERR(tcan4x5x->reset_gpio))
> +		tcan4x5x->reset_gpio = NULL;
> +
> +	tcan4x5x->wake_gpio = devm_gpiod_get_optional(&tcan4x5x->spi->dev,
> +						      "wake-up", GPIOD_OUT_LOW);
> +	if (IS_ERR(tcan4x5x->wake_gpio))
> +		tcan4x5x->wake_gpio = NULL;
> +
> +	tcan4x5x->interrupt_gpio = devm_gpiod_get(&tcan4x5x->spi->dev,
> +						  "data-ready", GPIOD_IN);
> +	if (IS_ERR(tcan4x5x->interrupt_gpio)) {
> +		dev_err(&tcan4x5x->spi->dev, "data-ready gpio not defined\n");
> +		return -EINVAL;
> +	}
> +
> +	tcan4x5x->irq = gpiod_to_irq(tcan4x5x->interrupt_gpio);
> +
> +	tcan4x5x->power = devm_regulator_get_optional(&tcan4x5x->spi->dev,
> +						      "vsup");
> +	if (PTR_ERR(tcan4x5x->power) == -EPROBE_DEFER)
> +		return -EPROBE_DEFER;
> +
> +	return 0;
> +}
> +
> +static const struct regmap_config tcan4x5x_regmap = {
> +	.reg_bits = 16,
> +	.val_bits = 32,
> +	.cache_type = REGCACHE_NONE,
> +	.max_register = TCAN4X5X_MAX_REGISTER,
> +};
> +
> +static int tcan4x5x_can_probe(struct spi_device *spi)
> +{
> +	struct net_device *net;
> +	struct tcan4x5x_priv *priv;
> +	struct clk *clk;
> +	int freq, ret;
> +
> +	clk = devm_clk_get(&spi->dev, NULL);
> +	if (IS_ERR(clk)) {
> +		dev_err(&spi->dev, "no CAN clock source defined\n");
> +		freq = TCAN4X5X_EXT_CLK_DEF;
> +	} else {
> +		freq = clk_get_rate(clk);
> +	}
> +
> +	/* Sanity check */
> +	if (freq < 20000000 || freq > TCAN4X5X_EXT_CLK_DEF)
> +		return -ERANGE;
> +
> +	/* Allocate can/net device */
> +	net = alloc_candev(sizeof(*priv), TCAN4X5X_TX_ECHO_SKB_MAX);
> +	if (!net)
> +		return -ENOMEM;
> +
> +	if (!IS_ERR(clk)) {
> +		ret = clk_prepare_enable(clk);
> +		if (ret)
> +			goto out_free;
> +	}
> +
> +	net->netdev_ops = &tcan4x5x_netdev_ops;
> +	net->flags |= IFF_ECHO;
> +	net->mtu = CANFD_MTU;
> +
> +	priv = netdev_priv(net);
> +	priv->can.bittiming_const = &tcan4x5x_bittiming_const;
> +	priv->can.data_bittiming_const = &tcan4x5x_data_bittiming_const;
> +	priv->can.do_set_mode = tcan4x5x_do_set_mode;
> +	priv->can.clock.freq = freq;
> +	priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK |
> +				       CAN_CTRLMODE_LISTENONLY |
> +				       CAN_CTRLMODE_BERR_REPORTING |
> +				       CAN_CTRLMODE_FD |
> +				       CAN_CTRLMODE_FD_NON_ISO;
> +	priv->net = net;
> +	priv->spi = spi;
> +	priv->clk = clk;
> +	spi_set_drvdata(spi, priv);
> +
> +	ret = tcan4x5x_parse_config(priv);
> +	if (ret)
> +		goto out_clk;
> +
> +	/* Configure the SPI bus */
> +	spi->bits_per_word = 32;
> +	ret = spi_setup(spi);
> +	if (ret)
> +		goto out_clk;
> +
> +	mutex_init(&priv->tcan4x5x_lock);
> +
> +	priv->regmap = devm_regmap_init(&spi->dev, &tcan4x5x_bus,
> +					&spi->dev, &tcan4x5x_regmap);
> +
> +	SET_NETDEV_DEV(net, &spi->dev);
> +	ret = register_candev(net);
> +	if (ret)
> +		goto error_probe;
> +
> +	devm_can_led_init(net);
> +
> +	netdev_info(net, "TCAN4X5X successfully initialized.\n");
> +	return 0;
> +
> +error_probe:
> +	tcan4x5x_power_enable(priv->power, 0);
> +out_clk:
> +	if (!IS_ERR(clk))
> +		clk_disable_unprepare(clk);
> +out_free:
> +	free_candev(net);
> +	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);
> +	struct net_device *net = priv->net;
> +
> +	unregister_candev(net);
> +
> +	tcan4x5x_power_enable(priv->power, 0);
> +
> +	if (!IS_ERR(priv->clk))
> +		clk_disable_unprepare(priv->clk);
> +
> +	free_candev(net);
> +
> +	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");
> diff --git a/drivers/net/can/spi/tcan4x5x.h b/drivers/net/can/spi/tcan4x5x.h
> new file mode 100644
> index 000000000000..5e14ba571d49
> --- /dev/null
> +++ b/drivers/net/can/spi/tcan4x5x.h
> @@ -0,0 +1,109 @@
> +// SPDX-License-Identifier: GPL-2.0
> +// SPI to CAN driver for the Texas Instruments TCA4x5x
> +// Flash driver chip family
> +// Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
> +
> +#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
> +
> +#define TCAN4X5X_MCAN_CREL	0x1000
> +#define TCAN4X5X_MCAN_ENDN	0x1004
> +#define TCAN4X5X_MCAN_CUST	0x1008
> +#define TCAN4X5X_MCAN_DBTP	0x100C
> +#define TCAN4X5X_MCAN_TEST	0x1010
> +#define TCAN4X5X_MCAN_RWD	0x1014
> +#define TCAN4X5X_MCAN_CCCR	0x1018
> +#define TCAN4X5X_MCAN_NBTP	0x101C
> +#define TCAN4X5X_MCAN_TSCC	0x1020
> +#define TCAN4X5X_MCAN_TSCV	0x1024
> +#define TCAN4X5X_MCAN_TOCC	0x1028
> +#define TCAN4X5X_MCAN_TOCV	0x102C
> +#define TCAN4X5X_MCAN_ECR	0x1040
> +#define TCAN4X5X_MCAN_PSR	0x1044
> +#define TCAN4X5X_MCAN_TDCR	0x1048
> +#define TCAN4X5X_MCAN_INT_FLAG	0x1050
> +#define TCAN4X5X_MCAN_INT_EN	0x1054
> +#define TCAN4X5X_MCAN_ILS	0x1058
> +#define TCAN4X5X_MCAN_ILE	0x105C
> +#define TCAN4X5X_MCAN_GFC	0x1080
> +#define TCAN4X5X_MCAN_SIDFC	0x1084
> +#define TCAN4X5X_MCAN_XIDFC	0x1088
> +#define TCAN4X5X_MCAN_XIDAM	0x1090
> +#define TCAN4X5X_MCAN_HPMS	0x1094
> +#define TCAN4X5X_MCAN_NDAT1	0x1098
> +#define TCAN4X5X_MCAN_NDAT2	0x109C
> +#define TCAN4X5X_MCAN_RXF0C	0x10A0
> +#define TCAN4X5X_MCAN_RXF0S	0x10A4
> +#define TCAN4X5X_MCAN_RXF0A	0x10A8
> +#define TCAN4X5X_MCAN_RXBC	0x10AC
> +#define TCAN4X5X_MCAN_RXF1C	0x10B0
> +#define TCAN4X5X_MCAN_RXF1S	0x10B4
> +#define TCAN4X5X_MCAN_RXF1A	0x10B8
> +#define TCAN4X5X_MCAN_RXESC	0x10BC
> +#define TCAN4X5X_MCAN_TXBC	0x10C0
> +#define TCAN4X5X_MCAN_TXFQS	0x10C4
> +#define TCAN4X5X_MCAN_TXESC	0x10C8
> +#define TCAN4X5X_MCAN_TXBRP	0x10CC
> +#define TCAN4X5X_MCAN_TXBAR	0x10D0
> +#define TCAN4X5X_MCAN_TXBCR	0x10D4
> +#define TCAN4X5X_MCAN_TXBTO	0x10D8
> +#define TCAN4X5X_MCAN_TXBCF	0x10DC
> +#define TCAN4X5X_MCAN_TXBTIE	0x10E0
> +#define TCAN4X5X_MCAN_TXBCIE	0x10E4
> +#define TCAN4X5X_MCAN_TXEFC	0x10F0
> +#define TCAN4X5X_MCAN_TXEFS	0x10F4
> +#define TCAN4X5X_MCAN_TXEFA	0x10F8
> +
> +#define TCAN4X5X_MRAM_START	0x8000
> +#define TCAN4X5X_MRAM_SIZE	2048
> +
> +#define TCAN4X5X_MAX_REGISTER	0x8fff
> +
> +/* 64 byte buffer + 8 byte MCAN header */
> +#define TCAN4X5X_BUF_LEN 	72
> +
> +struct tcan4x5x_priv {
> +	struct can_priv can;
> +	struct net_device *net;
> +	struct regmap *regmap;
> +	struct spi_device *spi;
> +
> +	struct mutex tcan4x5x_lock; /* SPI device lock */
> +
> +	struct gpio_desc *reset_gpio;
> +	struct gpio_desc *interrupt_gpio;
> +	struct gpio_desc *wake_gpio;
> +	struct regulator *power;
> +	struct clk *clk;
> +
> +	struct sk_buff *tx_skb;
> +	int tx_len;
> +
> +	struct workqueue_struct *wq;
> +	struct work_struct tx_work;
> +	struct work_struct restart_work;
> +
> +	u32 *spi_tx_buf;
> +	u32 *spi_rx_buf;
> +
> +	int force_quit;
> +	int after_suspend;
> +#define AFTER_SUSPEND_UP 1
> +#define AFTER_SUSPEND_DOWN 2
> +#define AFTER_SUSPEND_POWER 4
> +#define AFTER_SUSPEND_RESTART 8
> +	int restart_tx;
> +
> +	int irq;
> +};
> 


-- 
------------------
Dan Murphy

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox