From patchwork Wed Jun 10 07:44:37 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Manivannan Sadhasivam X-Patchwork-Id: 187731 Delivered-To: patch@linaro.org Received: by 2002:a92:cf06:0:0:0:0:0 with SMTP id c6csp254224ilo; Wed, 10 Jun 2020 00:45:25 -0700 (PDT) X-Google-Smtp-Source: ABdhPJzrYgtVZKA8Sh2OhL3F5y+qTPLzSWobCzhXOtIKe1D24pe3s3PmjXlG9Mip4i/w9wQ9Uk2+ X-Received: by 2002:a50:be8f:: with SMTP id b15mr1358007edk.182.1591775125658; Wed, 10 Jun 2020 00:45:25 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1591775125; cv=none; d=google.com; s=arc-20160816; b=wzT0ku2fUWwNophvkv9029oqvdVPB99drUziSk8gF8wcZtgLG7qsAPnMmnsObXBpbw PRvMyT9ygJ1uMvVOuYZCMAXTEFWS1fxA/v6l0uPfqbrJuvCU6oAiNn5o8UTCY+65SREC 04xeGWbRmIGrwsnVU2zd1bzFckqlV1EX1sUDYNM6U+aMtwYyzRBi7q/fkVoMu3VoVbiH To5xg5XRf7OP1B3/xMDMtIBw0D6hx8hbRaB7gwYupGCrBnWzFKjzDj+8E2UdvqKm+Jcb 2wMjabVGkZeT81E1zubjx9KuwErItBo5iAJvb3O/k9LfG5oojVAW8w/Kkk0rXNFpqsvw EDCg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:dkim-signature; bh=D5wpVa2/XN6m1sQCYfcdRcpjlnTpDyn4pCl/sB+sGr4=; b=ADqY/bt/p8Sp79CFWVyIZum9g5zw6TDcyNTSOhodYJB1Y/7W6uKUEzteiH8HqxeY/X DBKGAvIRpYD5RtFu62V2gXfryIjZlRmP/eH/kMyNHezIHFoKuHt9SnHaD0q8d1UTRskb XkHhGsby0KG1OhBXPcPzbe1IAHIDj3BnD5H1o9Q9zd9UfuUUw40a+0m1lD4radIHgobK PEVWlkpwHcDwTVkk9itvLzmUKOdEKTvLpCnB/1lcyAJW5WOkm4txJUy+hkC3Co3h0V6Y CW9xV1YfUCuXVbWnO1nHFR9OQscXWZmecrCNyPA2W+keeDhQO0MWnWSIyQ7/L+wQ7eea YX3A== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=FaW2IaQV; spf=pass (google.com: domain of netdev-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=netdev-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id n11si11263268ejh.543.2020.06.10.00.45.25; Wed, 10 Jun 2020 00:45:25 -0700 (PDT) Received-SPF: pass (google.com: domain of netdev-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=FaW2IaQV; spf=pass (google.com: domain of netdev-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=netdev-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726570AbgFJHo7 (ORCPT + 9 others); Wed, 10 Jun 2020 03:44:59 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:57272 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726105AbgFJHo6 (ORCPT ); Wed, 10 Jun 2020 03:44:58 -0400 Received: from mail-pg1-x544.google.com (mail-pg1-x544.google.com [IPv6:2607:f8b0:4864:20::544]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 653C3C03E96F for ; Wed, 10 Jun 2020 00:44:58 -0700 (PDT) Received: by mail-pg1-x544.google.com with SMTP id u5so620973pgn.5 for ; Wed, 10 Jun 2020 00:44:58 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=D5wpVa2/XN6m1sQCYfcdRcpjlnTpDyn4pCl/sB+sGr4=; b=FaW2IaQV+r7P93uoAgYFbVSHwrlIAWFar4ukwDRizxpruAAEKLbT9ZGCuAfcFtiqvp 5VXnwr8XZGqv2p3st1VhCev8QV4uIiwctrNGcNbHTEKDApc6JHt/dPdTrtLiU6eSBple zAGfJigjnXkTY2elpzC5cg6jN4Wo+6BTLaDECUjQP9bNfdGCHpfRqEbHCWiomgDRE7o8 if6XsSeKwq3PEy7bH9D8sIKKIFJvLzfNpjwxEnbNcqyerScSyfp0SRMzS9yJFS5Y4ZxQ SsS4PVNdDod5XCVZCPd5DOnkSaAEqPiFyUhznmTz4n1dE5WagBkg4H5rs+tyUR0rJjcN +iOw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=D5wpVa2/XN6m1sQCYfcdRcpjlnTpDyn4pCl/sB+sGr4=; b=W2eePkJIVQUU35UXhNZv5daybkXsxK/yK96NpIR45uab9ORKluKbHTv1c05F6V+kf1 72/wb5vQKdhR+b4QMEd9q2h+8iFRUA6px2Puzw0N1w3zBX0+cqsPQciF3F/cO3SdYwa2 AeewGrtokutdcgmuP3+b4flWKQHUSOhZYZNVlUNCNS90zY76+cwnNv6kHA3GXLBOqSH5 Vn4ZRx30XmJ9DFoq8iQCeaYgZ7kzWdLhdcVMj81DZQtyrT+RnQfyP7D4wiGC0tmbjXxW 3wT+/jsDJH3nDftjtnm1PNxoFZxhrXybEOOlDPgmuPxhwD9m+SuInoS7uF0KcAl2gni2 SyNQ== X-Gm-Message-State: AOAM530mSTj9LNT7v84sprH5bViVsOe0aDYxmpt7ccqeVrdb7VPi10dt EfZDNgMeqMm0Ge2bfGARG/qb X-Received: by 2002:a65:46cc:: with SMTP id n12mr1664976pgr.80.1591775097893; Wed, 10 Jun 2020 00:44:57 -0700 (PDT) Received: from Mani-XPS-13-9360.localdomain ([2409:4072:630f:1dba:c41:a14e:6586:388a]) by smtp.gmail.com with ESMTPSA id a20sm11516795pff.147.2020.06.10.00.44.54 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 10 Jun 2020 00:44:57 -0700 (PDT) From: Manivannan Sadhasivam To: wg@grandegger.com, mkl@pengutronix.de Cc: kernel@martin.sperl.org, linux-can@vger.kernel.org, netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Manivannan Sadhasivam Subject: [PATCH 1/6] dt-bindings: can: Document devicetree bindings for MCP25XXFD Date: Wed, 10 Jun 2020 13:14:37 +0530 Message-Id: <20200610074442.10808-2-manivannan.sadhasivam@linaro.org> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200610074442.10808-1-manivannan.sadhasivam@linaro.org> References: <20200610074442.10808-1-manivannan.sadhasivam@linaro.org> Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org From: Martin Sperl Add devicetree YAML bindings for Microchip MCP25XXFD CAN controller. Signed-off-by: Martin Sperl [mani: converted to YAML binding] Signed-off-by: Manivannan Sadhasivam --- .../bindings/net/can/microchip,mcp25xxfd.yaml | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 Documentation/devicetree/bindings/net/can/microchip,mcp25xxfd.yaml -- 2.17.1 diff --git a/Documentation/devicetree/bindings/net/can/microchip,mcp25xxfd.yaml b/Documentation/devicetree/bindings/net/can/microchip,mcp25xxfd.yaml new file mode 100644 index 000000000000..7b87ec328515 --- /dev/null +++ b/Documentation/devicetree/bindings/net/can/microchip,mcp25xxfd.yaml @@ -0,0 +1,82 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/can/microchip,mcp25xxfd.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Microchip MCP25XXFD stand-alone CAN controller binding + +maintainers: + - Martin Sperl + - Manivannan Sadhasivam + +properties: + compatible: + const: microchip,mcp2517fd + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + interrupts: + maxItems: 1 + + gpio-controller: true + + "#gpio-cells": + const: 2 + + vdd-supply: + description: Regulator that powers the CAN controller + + xceiver-supply: + description: Regulator that powers the CAN transceiver + + microchip,clock-out-div: + description: Clock output pin divider + allOf: + - $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 1, 2, 4, 10] + default: 10 + + microchip,clock-div2: + description: Divide the internal clock by 2 + type: boolean + + microchip,gpio-open-drain: + description: Enable open-drain for all pins + type: boolean + +required: + - compatible + - reg + - clocks + - interrupts + - gpio-controller + - vdd-supply + - xceiver-supply + +additionalProperties: false + +examples: + - | + spi { + #address-cells = <1>; + #size-cells = <0>; + + can0: can@1 { + compatible = "microchip,mcp2517fd"; + reg = <1>; + clocks = <&clk24m>; + interrupt-parent = <&gpio4>; + interrupts = <13 0x8>; + vdd-supply = <®5v0>; + xceiver-supply = <®5v0>; + gpio-controller; + #gpio-cells = <2>; + }; + }; + +... From patchwork Wed Jun 10 07:44:38 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Manivannan Sadhasivam X-Patchwork-Id: 187729 Delivered-To: patch@linaro.org Received: by 2002:a92:cf06:0:0:0:0:0 with SMTP id c6csp254112ilo; Wed, 10 Jun 2020 00:45:15 -0700 (PDT) X-Google-Smtp-Source: ABdhPJxMTkBukCCajITO6fz8zw1pLlC8j0OFXQvDgyLbr2UfbeFpyPb2mA509wwiJoUgnxrWFd06 X-Received: by 2002:a50:f044:: with SMTP id u4mr1401758edl.226.1591775114861; Wed, 10 Jun 2020 00:45:14 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1591775114; cv=none; d=google.com; s=arc-20160816; b=rdB3Le4l+7T0gBJONdKuiz2wIDgUbiGqrdFKdg1aE27707EBSYjEXidI7WAYCKlUmf Jq6FQDHrIY3dpFKaqFN61uyJwV5kT/ApJMvyUvLMWDxvab5lgAQPGrw9Si9OUoan2Pel A/F1sSEF0EGGvtWIeUQT6HdLg5wVryULnXcQ3umRFO9ix9cs76/jjUSE5sluu7SjBcNM ypv34KAAb6ww4IbN3ax3k4zkpm37XMrO3gFDS3l/02o0M1kH2yJMaM984oQxThfCPBHR 7XWPf8KWqDWNHCJQ1V9jHx4o3Y+DsLIxVDyS27REFKdN0kN5gZ7JQjEco1Dgw2PawmH0 2MVw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:dkim-signature; bh=j0l5m+TkhIbHssxUHNdbOXyWREI7WDwMdOHBW8DfxuY=; b=u9s+4ZrQc1slkqtRBpoM4yV83KcicjusD3T017L36I3l2V2LFVcisN86ZFoTDePMD3 Pi9A22opQ78h10vVosX+mSyEeK/Vp9PSqPm7xHyX/L8jXvrPqPVuTNa0r9VYAkacZz1N iRjKCRa+d+NFzj/djGVynHzx9OZYK8gSpd4eXnt7B4MLVmUZ8fFJZA40J7mCurwI/cTp OW5y/ubkQYzqaAjW1v08RAHyPVlZWZTK3xWAJUHdeHI2HQxNuCbyzlqogaXB9JwXfb2J 9Qy7KwaoA4eyp3okBINTv29iAFXKvO13o7vIIKAg60Wasj8uK+gfEGECFn+Di8IH89kL fYDw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=TjsUl72T; spf=pass (google.com: domain of netdev-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=netdev-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id n11si11263268ejh.543.2020.06.10.00.45.14; Wed, 10 Jun 2020 00:45:14 -0700 (PDT) Received-SPF: pass (google.com: domain of netdev-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=TjsUl72T; spf=pass (google.com: domain of netdev-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=netdev-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726577AbgFJHpI (ORCPT + 9 others); Wed, 10 Jun 2020 03:45:08 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:57302 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726578AbgFJHpE (ORCPT ); Wed, 10 Jun 2020 03:45:04 -0400 Received: from mail-pj1-x1041.google.com (mail-pj1-x1041.google.com [IPv6:2607:f8b0:4864:20::1041]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id C3691C08C5C1 for ; Wed, 10 Jun 2020 00:45:04 -0700 (PDT) Received: by mail-pj1-x1041.google.com with SMTP id i12so518421pju.3 for ; Wed, 10 Jun 2020 00:45:04 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=j0l5m+TkhIbHssxUHNdbOXyWREI7WDwMdOHBW8DfxuY=; b=TjsUl72TVnhuza0h8L21YWJppRmPNzgSky+aRzRHB+yG1KcUnC+7kL1RMdQk62d7kZ QCrQwrG6q2dIAnqcdAz6aAKNbH68BJJT2sx4pJbhdV1ctwl67aMWTznUsbvwD1hqmaFK 5/QKricj0Iu/LsJmluvt+OYdCJA6Ngn5W1XvXKtC3CF5rieerfRNEYY1dTvpaZuDipFd K5BpbiboFltzkgj2mKqfqn0/UJKKsb+Pr49GYRsD/25+4fedSp77Wg/ucpFOQ1QRjJir NEMcFR60gfp5Nq6+StY/wFbbHnOep0ja6tDZtLfvvYr8n6zjRtOOBS7zjJD/shblR/ef QgSw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=j0l5m+TkhIbHssxUHNdbOXyWREI7WDwMdOHBW8DfxuY=; b=cgBidp58TLgvr+lsk9i0ZcaXQ6x01aZgYMkQkNCETPx2Buz+3FW1dl/Zr6Tr1ngsNK XO+UV5AdEJz8Yf8BXuEaTBXYV0vH7toM9/DJQ8jhZ0RwvJmKjsOgrPCDFzg9MyYVJ0cN smMd/3H0JMCsqD4Gf7fHMhoUoJF+lidEoAWSIePjuiLfiAx5DJauRE8lU5aWNoZCQgcA zvr06cqmINgLiWWuKcno/jWTMu5DxULalby7MHeWSVJBAgfZ4Tpt8wYoqbAsYy7pd3aO iqn3u5BQrgKHRFJC/W8uZLbWIUXGYDWJOsgKs5AXIbl2mWvTtxq7t0MlocQgzpRBUPRc YbnQ== X-Gm-Message-State: AOAM533skm+h545kg1teso3ij2M8UiDAcOmo3u6PVpVn1Lfp7TSH7h2M oRONyoHhzFaxYykIKULsNMMawEO0bA== X-Received: by 2002:a17:90a:c257:: with SMTP id d23mr1814645pjx.103.1591775103768; Wed, 10 Jun 2020 00:45:03 -0700 (PDT) Received: from Mani-XPS-13-9360.localdomain ([2409:4072:630f:1dba:c41:a14e:6586:388a]) by smtp.gmail.com with ESMTPSA id a20sm11516795pff.147.2020.06.10.00.44.58 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 10 Jun 2020 00:45:03 -0700 (PDT) From: Manivannan Sadhasivam To: wg@grandegger.com, mkl@pengutronix.de Cc: kernel@martin.sperl.org, linux-can@vger.kernel.org, netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Manivannan Sadhasivam Subject: [PATCH 2/6] can: mcp25xxfd: Add Microchip MCP25XXFD CAN-FD driver infrastructure Date: Wed, 10 Jun 2020 13:14:38 +0530 Message-Id: <20200610074442.10808-3-manivannan.sadhasivam@linaro.org> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200610074442.10808-1-manivannan.sadhasivam@linaro.org> References: <20200610074442.10808-1-manivannan.sadhasivam@linaro.org> Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org From: Martin Sperl This commit adds basic driver support for the Microchip MCP25XXFD CAN-FD controller series. This driver currently supports MCP2517FD as the target controller. The MCP2517FD is capable of transmitting and receiving standard data frames, extended data frames, remote frames and Can-FD frames. The MCP2517FD interfaces with the host over SPI. This commit provides basic driver functionality such as setting up clocks, regulators and the infrastructure for the CAN. Datasheet: * http://ww1.microchip.com/downloads/en/DeviceDoc/20005688A.pdf Reference manual: * http://ww1.microchip.com/downloads/en/DeviceDoc/20005678A.pdf Errata: * http://ww1.microchip.com/downloads/en/DeviceDoc/MCP2517FD-Silicon-Errata-and-Data-Sheet-Clarification-DS80000792A.pdf Signed-off-by: Martin Sperl [mani: trimmed the gpio part and done some cleanups] Signed-off-by: Manivannan Sadhasivam --- drivers/net/can/spi/Kconfig | 2 + drivers/net/can/spi/Makefile | 2 + drivers/net/can/spi/mcp25xxfd/Kconfig | 5 + drivers/net/can/spi/mcp25xxfd/Makefile | 7 + .../net/can/spi/mcp25xxfd/mcp25xxfd_base.c | 171 +++++ .../net/can/spi/mcp25xxfd/mcp25xxfd_base.h | 14 + drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.c | 141 ++++ drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.h | 16 + drivers/net/can/spi/mcp25xxfd/mcp25xxfd_cmd.c | 226 ++++++ drivers/net/can/spi/mcp25xxfd/mcp25xxfd_cmd.h | 84 +++ drivers/net/can/spi/mcp25xxfd/mcp25xxfd_crc.c | 31 + drivers/net/can/spi/mcp25xxfd/mcp25xxfd_crc.h | 15 + drivers/net/can/spi/mcp25xxfd/mcp25xxfd_ecc.c | 74 ++ drivers/net/can/spi/mcp25xxfd/mcp25xxfd_ecc.h | 16 + drivers/net/can/spi/mcp25xxfd/mcp25xxfd_int.c | 59 ++ drivers/net/can/spi/mcp25xxfd/mcp25xxfd_int.h | 15 + .../net/can/spi/mcp25xxfd/mcp25xxfd_priv.h | 48 ++ .../net/can/spi/mcp25xxfd/mcp25xxfd_regs.h | 656 ++++++++++++++++++ 18 files changed, 1582 insertions(+) create mode 100644 drivers/net/can/spi/mcp25xxfd/Kconfig create mode 100644 drivers/net/can/spi/mcp25xxfd/Makefile create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_base.c create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_base.h create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.c create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.h create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_cmd.c create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_cmd.h create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_crc.c create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_crc.h create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_ecc.c create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_ecc.h create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_int.c create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_int.h create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_priv.h create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_regs.h -- 2.17.1 diff --git a/drivers/net/can/spi/Kconfig b/drivers/net/can/spi/Kconfig index 1c50788055cb..d4b68eb5d386 100644 --- a/drivers/net/can/spi/Kconfig +++ b/drivers/net/can/spi/Kconfig @@ -15,4 +15,6 @@ config CAN_MCP251X Driver for the Microchip MCP251x and MCP25625 SPI CAN controllers. +source "drivers/net/can/spi/mcp25xxfd/Kconfig" + endmenu diff --git a/drivers/net/can/spi/Makefile b/drivers/net/can/spi/Makefile index f115b2c46623..f56514541ce4 100644 --- a/drivers/net/can/spi/Makefile +++ b/drivers/net/can/spi/Makefile @@ -6,3 +6,5 @@ obj-$(CONFIG_CAN_HI311X) += hi311x.o obj-$(CONFIG_CAN_MCP251X) += mcp251x.o + +obj-y += mcp25xxfd/ diff --git a/drivers/net/can/spi/mcp25xxfd/Kconfig b/drivers/net/can/spi/mcp25xxfd/Kconfig new file mode 100644 index 000000000000..f720f1377612 --- /dev/null +++ b/drivers/net/can/spi/mcp25xxfd/Kconfig @@ -0,0 +1,5 @@ +config CAN_MCP25XXFD + tristate "Microchip MCP25xxFD SPI CAN controllers" + depends on HAS_DMA + help + Driver for the Microchip MCP25XXFD SPI FD-CAN controller family. diff --git a/drivers/net/can/spi/mcp25xxfd/Makefile b/drivers/net/can/spi/mcp25xxfd/Makefile new file mode 100644 index 000000000000..d8fdb76a9578 --- /dev/null +++ b/drivers/net/can/spi/mcp25xxfd/Makefile @@ -0,0 +1,7 @@ +obj-$(CONFIG_CAN_MCP25XXFD) += mcp25xxfd.o +mcp25xxfd-objs := mcp25xxfd_base.o +mcp25xxfd-objs += mcp25xxfd_can.o +mcp25xxfd-objs += mcp25xxfd_cmd.o +mcp25xxfd-objs += mcp25xxfd_crc.o +mcp25xxfd-objs += mcp25xxfd_ecc.o +mcp25xxfd-objs += mcp25xxfd_int.o diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_base.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_base.c new file mode 100644 index 000000000000..4be456df0998 --- /dev/null +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_base.c @@ -0,0 +1,171 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface + * + * Copyright 2019 Martin Sperl + */ + +#include +#include +#include +#include +#include +#include + +#include "mcp25xxfd_base.h" +#include "mcp25xxfd_can.h" +#include "mcp25xxfd_cmd.h" +#include "mcp25xxfd_ecc.h" +#include "mcp25xxfd_int.h" +#include "mcp25xxfd_priv.h" + +int mcp25xxfd_base_power_enable(struct regulator *reg, int enable) +{ + if (enable) + return regulator_enable(reg); + else + return regulator_disable(reg); +} + +static const struct of_device_id mcp25xxfd_of_match[] = { + { + .compatible = "microchip,mcp2517fd", + .data = (void *)CAN_MCP2517FD, + }, + { } +}; +MODULE_DEVICE_TABLE(of, mcp25xxfd_of_match); + +static int mcp25xxfd_base_probe(struct spi_device *spi) +{ + const struct of_device_id *of_id = + of_match_device(mcp25xxfd_of_match, &spi->dev); + struct mcp25xxfd_priv *priv; + struct clk *clk; + enum mcp25xxfd_model model; + u32 freq; + int ret; + + /* Check whether valid IRQ line is defined or not */ + if (spi->irq <= 0) { + dev_err(&spi->dev, "no valid irq line defined: irq = %i\n", + spi->irq); + return -EINVAL; + } + + priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + spi_set_drvdata(spi, priv); + priv->spi = spi; + + /* Assign device name */ + snprintf(priv->device_name, sizeof(priv->device_name), + DEVICE_NAME "-%s", dev_name(&priv->spi->dev)); + + /* assign model from of or driver_data */ + if (of_id) + model = (enum mcp25xxfd_model)of_id->data; + else + model = spi_get_device_id(spi)->driver_data; + + clk = devm_clk_get(&spi->dev, NULL); + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + goto out_free; + } + + freq = clk_get_rate(clk); + if (!(freq == CLOCK_4_MHZ || freq == CLOCK_10_MHZ || + freq == CLOCK_40_MHZ)) { + ret = -ERANGE; + goto out_free; + } + + ret = clk_prepare_enable(clk); + if (ret) + goto out_free; + + priv->clk = clk; + priv->clock_freq = freq; + + /* Configure the SPI bus */ + spi->bits_per_word = 8; + + /* The frequency of SCK has to be less than or equal to half the + * frequency of SYSCLK. + */ + spi->max_speed_hz = freq / 2; + ret = spi_setup(spi); + if (ret) + goto out_clk; + + priv->power = devm_regulator_get(&spi->dev, "vdd"); + if (IS_ERR(priv->power)) { + if (PTR_ERR(priv->power) != -EPROBE_DEFER) + dev_err(&spi->dev, "failed to get vdd\n"); + ret = PTR_ERR(priv->power); + goto out_clk; + } + + ret = mcp25xxfd_base_power_enable(priv->power, 1); + if (ret) + goto out_clk; + + /* disable interrupts */ + ret = mcp25xxfd_int_enable(priv, false); + if (ret) + goto out_power; + + /* setup ECC for SRAM */ + ret = mcp25xxfd_ecc_enable(priv); + if (ret) + goto out_power; + + dev_info(&spi->dev, + "MCP%04x successfully initialized.\n", model); + return 0; + +out_power: + mcp25xxfd_base_power_enable(priv->power, 0); +out_clk: + clk_disable_unprepare(clk); +out_free: + dev_err(&spi->dev, "Probe failed, err=%d\n", -ret); + return ret; +} + +static int mcp25xxfd_base_remove(struct spi_device *spi) +{ + struct mcp25xxfd_priv *priv = spi_get_drvdata(spi); + + mcp25xxfd_base_power_enable(priv->power, 0); + clk_disable_unprepare(priv->clk); + + return 0; +} + +static const struct spi_device_id mcp25xxfd_id_table[] = { + { + .name = "mcp2517fd", + .driver_data = (kernel_ulong_t)CAN_MCP2517FD, + }, + { } +}; +MODULE_DEVICE_TABLE(spi, mcp25xxfd_id_table); + +static struct spi_driver mcp25xxfd_can_driver = { + .driver = { + .name = DEVICE_NAME, + .of_match_table = mcp25xxfd_of_match, + }, + .id_table = mcp25xxfd_id_table, + .probe = mcp25xxfd_base_probe, + .remove = mcp25xxfd_base_remove, +}; +module_spi_driver(mcp25xxfd_can_driver); + +MODULE_AUTHOR("Martin Sperl "); +MODULE_DESCRIPTION("Microchip 25XXFD CAN driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_base.h b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_base.h new file mode 100644 index 000000000000..4559ac60645c --- /dev/null +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_base.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface + * + * Copyright 2019 Martin Sperl + */ +#ifndef __MCP25XXFD_BASE_H +#define __MCP25XXFD_BASE_H + +#include + +int mcp25xxfd_base_power_enable(struct regulator *reg, int enable); + +#endif /* __MCP25XXFD_BASE_H */ diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.c new file mode 100644 index 000000000000..41a5ab508582 --- /dev/null +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.c @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface + * + * Copyright 2019 Martin Sperl + */ + +#include +#include +#include + +#include "mcp25xxfd_can.h" +#include "mcp25xxfd_cmd.h" +#include "mcp25xxfd_priv.h" +#include "mcp25xxfd_regs.h" + +int mcp25xxfd_can_get_mode(struct mcp25xxfd_priv *priv, u32 *reg) +{ + int ret; + + ret = mcp25xxfd_cmd_read(priv->spi, MCP25XXFD_CAN_CON, reg); + if (ret) + return ret; + + return FIELD_GET(MCP25XXFD_CAN_CON_OPMOD_MASK, *reg); +} + +static int mcp25xxfd_can_switch_mode(struct mcp25xxfd_priv *priv, + u32 *reg, int mode) +{ + int ret, i; + + ret = mcp25xxfd_can_get_mode(priv, reg); + if (ret < 0) + return ret; + + /* Compute the effective mode in osc*/ + *reg &= ~(MCP25XXFD_CAN_CON_REQOP_MASK | + MCP25XXFD_CAN_CON_OPMOD_MASK); + *reg |= FIELD_PREP(MCP25XXFD_CAN_CON_REQOP_MASK, mode) | + FIELD_PREP(MCP25XXFD_CAN_CON_OPMOD_MASK, mode); + + /* Request the mode switch */ + ret = mcp25xxfd_cmd_write(priv->spi, MCP25XXFD_CAN_CON, *reg); + if (ret) + return ret; + + /* Wait for 256 rounds to stabilize. This is essentially > 12ms + * at 1MHz + */ + for (i = 0; i < 256; i++) { + /* get the mode */ + ret = mcp25xxfd_can_get_mode(priv, reg); + if (ret < 0) + return ret; + /* check that we have reached our mode */ + if (ret == mode) + return 0; + } + + dev_err(&priv->spi->dev, "Failed to switch to mode %u in time\n", + mode); + + return -ETIMEDOUT; +} + +static int mcp25xxfd_can_probe_modeswitch(struct mcp25xxfd_priv *priv) +{ + u32 mode_data; + int ret; + + /* We should be in config mode now, so move to INT_LOOPBACK */ + ret = mcp25xxfd_can_switch_mode(priv, &mode_data, + MCP25XXFD_CAN_CON_MODE_INT_LOOPBACK); + if (ret) { + dev_err(&priv->spi->dev, + "Failed to switch into loopback mode\n"); + return ret; + } + + /* Switch back into config mode */ + ret = mcp25xxfd_can_switch_mode(priv, &mode_data, + MCP25XXFD_CAN_CON_MODE_CONFIG); + if (ret) { + dev_err(&priv->spi->dev, + "Failed to switch back to config mode\n"); + return ret; + } + + return 0; +} + +int mcp25xxfd_can_probe(struct mcp25xxfd_priv *priv) +{ + struct spi_device *spi = priv->spi; + u32 mode_data; + int mode, ret; + + /* For sanity check read TXQCON register. The TXEN bit should always + * be read as 1. + */ + ret = mcp25xxfd_cmd_read(spi, MCP25XXFD_CAN_TXQCON, &mode_data); + if (ret) + return ret; + + if ((mode_data & MCP25XXFD_CAN_TXQCON_TXEN) == 0) { + dev_err(&spi->dev, "TXQCON does not have TXEN bit set"); + return -EINVAL; + } + + /* Try to get the current mode */ + mode = mcp25xxfd_can_get_mode(priv, &mode_data); + if (mode < 0) + return mode; + + /* SPI-reset should have moved us into config mode. But then the + * documentation says that SPI-reset may only work reliably when already + * in config mode. So if we are in config mode then everything is fine + * and we check that a mode switch works properly. + */ + if (mode == MCP25XXFD_CAN_CON_MODE_CONFIG) + return mcp25xxfd_can_probe_modeswitch(priv); + + /* Any other mode is unexpected */ + dev_err(&spi->dev, + "Found controller in unexpected mode: %d\n", mode); + + /* Once again try to move to config mode. If this fails, we'll + * bail out. + */ + ret = mcp25xxfd_can_switch_mode(priv, &mode_data, + MCP25XXFD_CAN_CON_MODE_CONFIG); + if (ret) { + dev_err(&priv->spi->dev, + "Unable to switch to config mode\n"); + return -EINVAL; + } + + /* Finally check if modeswitch is really working */ + return mcp25xxfd_can_probe_modeswitch(priv); +} diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.h b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.h new file mode 100644 index 000000000000..f54c716735fb --- /dev/null +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface + * + * Copyright 2019 Martin Sperl + */ + +#ifndef __MCP25XXFD_CAN_H +#define __MCP25XXFD_CAN_H + +#include "mcp25xxfd_priv.h" + +/* probe the can controller */ +int mcp25xxfd_can_probe(struct mcp25xxfd_priv *priv); + +#endif /* __MCP25XXFD_CAN_H */ diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_cmd.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_cmd.c new file mode 100644 index 000000000000..24d01e6f59d4 --- /dev/null +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_cmd.c @@ -0,0 +1,226 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface + * + * Copyright 2019 Martin Sperl + */ + +#include +#include + +#include "mcp25xxfd_cmd.h" +#include "mcp25xxfd_priv.h" + +static int mcp25xxfd_cmd_sync_write(struct spi_device *spi, + const void *tx_buf, + unsigned int tx_len) +{ + struct spi_transfer xfer; + + memset(&xfer, 0, sizeof(xfer)); + xfer.tx_buf = tx_buf; + xfer.len = tx_len; + + return spi_sync_transfer(spi, &xfer, 1); +} + +static int mcp25xxfd_cmd_write_then_read(struct spi_device *spi, + const void *tx_buf, + unsigned int tx_len, + void *rx_buf, + unsigned int rx_len) +{ + struct spi_transfer xfer[2]; + u8 *spi_tx, *spi_rx; + int xfers; + int ret; + + spi_tx = kzalloc(tx_len + rx_len, GFP_KERNEL); + if (!spi_tx) + return -ENOMEM; + + spi_rx = spi_tx + tx_len; + memset(xfer, 0, sizeof(xfer)); + + /* Special handling for half-duplex */ + if (spi->master->flags & SPI_MASTER_HALF_DUPLEX) { + xfers = 2; + xfer[0].tx_buf = spi_tx; + xfer[0].len = tx_len; + /* Offset for rx_buf needs to get aligned */ + xfer[1].rx_buf = spi_rx + tx_len; + xfer[1].len = rx_len; + } else { + xfers = 1; + xfer[0].len = tx_len + rx_len; + xfer[0].tx_buf = spi_tx; + xfer[0].rx_buf = spi_rx; + } + + memcpy(spi_tx, tx_buf, tx_len); + ret = spi_sync_transfer(spi, xfer, xfers); + if (ret) + goto out; + + memcpy(rx_buf, xfer[0].rx_buf + tx_len, rx_len); + +out: + kfree(spi_tx); + + return ret; +} + +static int mcp25xxfd_cmd_write_then_write(struct spi_device *spi, + const void *tx_buf, + unsigned int tx_len, + const void *tx2_buf, + unsigned int tx2_len) +{ + struct spi_transfer xfer; + u8 *spi_tx; + int ret; + + spi_tx = kzalloc(tx_len + tx2_len, GFP_KERNEL); + if (!spi_tx) + return -ENOMEM; + + memset(&xfer, 0, sizeof(xfer)); + xfer.len = tx_len + tx2_len; + xfer.tx_buf = spi_tx; + + memcpy(spi_tx, tx_buf, tx_len); + memcpy(spi_tx + tx_len, tx2_buf, tx2_len); + + ret = spi_sync_transfer(spi, &xfer, 1); + kfree(spi_tx); + + return ret; +} + +/* Read multiple bytes from registers */ +int mcp25xxfd_cmd_read_multi(struct spi_device *spi, u32 reg, + void *data, int n) +{ + u8 cmd[2]; + int ret; + + mcp25xxfd_cmd_calc(MCP25XXFD_INSTRUCTION_READ, reg, cmd); + + ret = mcp25xxfd_cmd_write_then_read(spi, &cmd, 2, data, n); + if (ret) + return ret; + + return 0; +} + +int mcp25xxfd_cmd_read_mask(struct spi_device *spi, u32 reg, + u32 *data, u32 mask) +{ + int first_byte, last_byte, len_byte; + int ret; + + /* Make sure at least one bit is set */ + if (!mask) + return -EINVAL; + + /* Calculate first and last byte used */ + first_byte = mcp25xxfd_cmd_first_byte(mask); + last_byte = mcp25xxfd_cmd_last_byte(mask); + len_byte = last_byte - first_byte + 1; + + *data = 0; + ret = mcp25xxfd_cmd_read_multi(spi, reg + first_byte, + ((void *)data + first_byte), len_byte); + if (ret) + return ret; + + mcp25xxfd_cmd_convert_to_cpu(data, 1); + + return 0; +} + +/* Write multiple bytes to registers */ +int mcp25xxfd_cmd_write_multi(struct spi_device *spi, u32 reg, + void *data, int n) +{ + int ret; + u8 cmd[2]; + + mcp25xxfd_cmd_calc(MCP25XXFD_INSTRUCTION_WRITE, reg, cmd); + + ret = mcp25xxfd_cmd_write_then_write(spi, &cmd, 2, data, n); + if (ret) + return ret; + + return 0; +} + +int mcp25xxfd_cmd_write_mask(struct spi_device *spi, u32 reg, + u32 data, u32 mask) +{ + int first_byte, last_byte, len_byte; + u8 cmd[2]; + + /* Make sure at least one bit is set */ + if (!mask) + return -EINVAL; + + /* calculate first and last byte used */ + first_byte = mcp25xxfd_cmd_first_byte(mask); + last_byte = mcp25xxfd_cmd_last_byte(mask); + len_byte = last_byte - first_byte + 1; + + mcp25xxfd_cmd_calc(MCP25XXFD_INSTRUCTION_WRITE, + reg + first_byte, cmd); + + mcp25xxfd_cmd_convert_from_cpu(&data, 1); + + return mcp25xxfd_cmd_write_then_write(spi, + cmd, sizeof(cmd), + ((void *)&data + first_byte), + len_byte); +} + +int mcp25xxfd_cmd_write_regs(struct spi_device *spi, u32 reg, + u32 *data, u32 bytes) +{ + int ret; + + mcp25xxfd_cmd_convert_from_cpu(data, bytes / sizeof(bytes)); + + ret = mcp25xxfd_cmd_write_multi(spi, reg, data, bytes); + + mcp25xxfd_cmd_convert_to_cpu(data, bytes / sizeof(bytes)); + + return ret; +} + +int mcp25xxfd_cmd_read_regs(struct spi_device *spi, u32 reg, + u32 *data, u32 bytes) +{ + int ret; + + ret = mcp25xxfd_cmd_read_multi(spi, reg, data, bytes); + + mcp25xxfd_cmd_convert_to_cpu((u32 *)data, bytes / sizeof(bytes)); + + return ret; +} + +int mcp25xxfd_cmd_reset(struct spi_device *spi) +{ + u8 *cmd; + int ret; + + cmd = kzalloc(2, GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + mcp25xxfd_cmd_calc(MCP25XXFD_INSTRUCTION_RESET, 0, cmd); + + ret = mcp25xxfd_cmd_sync_write(spi, cmd, 2); + + kfree(cmd); + + return ret; +} diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_cmd.h b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_cmd.h new file mode 100644 index 000000000000..a61815f97bf6 --- /dev/null +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_cmd.h @@ -0,0 +1,84 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface + * + * Copyright 2019 Martin Sperl + */ + +#ifndef __MCP25XXFD_CMD_H +#define __MCP25XXFD_CMD_H + +#include +#include + +/* SPI commands */ +#define MCP25XXFD_INSTRUCTION_RESET 0x0000 +#define MCP25XXFD_INSTRUCTION_READ 0x3000 +#define MCP25XXFD_INSTRUCTION_WRITE 0x2000 +#define MCP25XXFD_INSTRUCTION_READ_CRC 0xB000 +#define MCP25XXFD_INSTRUCTION_WRITE_CRC 0xA000 +#define MCP25XXFD_INSTRUCTION_WRITE_SAVE 0xC000 + +#define MCP25XXFD_ADDRESS_MASK 0x0fff + +static inline void mcp25xxfd_cmd_convert_to_cpu(u32 *data, int n) +{ + le32_to_cpu_array(data, n); +} + +static inline void mcp25xxfd_cmd_convert_from_cpu(u32 *data, int n) +{ + cpu_to_le32_array(data, n); +} + +static inline void mcp25xxfd_cmd_calc(u16 cmd, u16 addr, u8 *data) +{ + cmd = cmd | (addr & MCP25XXFD_ADDRESS_MASK); + + data[0] = (cmd >> 8) & 0xff; + data[1] = (cmd >> 0) & 0xff; +} + +static inline int mcp25xxfd_cmd_first_byte(u32 mask) +{ + return (mask & 0x0000ffff) ? + ((mask & 0x000000ff) ? 0 : 1) : + ((mask & 0x00ff0000) ? 2 : 3); +} + +static inline int mcp25xxfd_cmd_last_byte(u32 mask) +{ + return (mask & 0xffff0000) ? + ((mask & 0xff000000) ? 3 : 2) : + ((mask & 0x0000ff00) ? 1 : 0); +} + +int mcp25xxfd_cmd_read_multi(struct spi_device *spi, u32 reg, + void *data, int n); +int mcp25xxfd_cmd_read_mask(struct spi_device *spi, u32 reg, + u32 *data, u32 mask); +static inline int mcp25xxfd_cmd_read(struct spi_device *spi, u32 reg, + u32 *data) +{ + return mcp25xxfd_cmd_read_mask(spi, reg, data, -1); +} + +int mcp25xxfd_cmd_read_regs(struct spi_device *spi, u32 reg, + u32 *data, u32 bytes); + +int mcp25xxfd_cmd_write_multi(struct spi_device *spi, u32 reg, + void *data, int n); +int mcp25xxfd_cmd_write_mask(struct spi_device *spi, u32 reg, + u32 data, u32 mask); +static inline int mcp25xxfd_cmd_write(struct spi_device *spi, u32 reg, + u32 data) +{ + return mcp25xxfd_cmd_write_mask(spi, reg, data, -1); +} + +int mcp25xxfd_cmd_write_regs(struct spi_device *spi, u32 reg, + u32 *data, u32 bytes); + +int mcp25xxfd_cmd_reset(struct spi_device *spi); + +#endif /* __MCP25XXFD_CMD_H */ diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_crc.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_crc.c new file mode 100644 index 000000000000..b893d8009448 --- /dev/null +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_crc.c @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface + * + * Copyright 2019 Martin Sperl + */ + +#include +#include +#include "mcp25xxfd_cmd.h" +#include "mcp25xxfd_crc.h" +#include "mcp25xxfd_regs.h" +#include "mcp25xxfd_priv.h" + +int mcp25xxfd_crc_enable_int(struct mcp25xxfd_priv *priv, bool enable) +{ + u32 mask = MCP25XXFD_CRC_CRCERRIE | MCP25XXFD_CRC_FERRIE; + + priv->regs.crc &= ~mask; + priv->regs.crc |= enable ? mask : 0; + + return mcp25xxfd_cmd_write_mask(priv->spi, MCP25XXFD_CRC, + priv->regs.crc, mask); +} + +int mcp25xxfd_crc_clear_int(struct mcp25xxfd_priv *priv) +{ + return mcp25xxfd_cmd_write_mask(priv->spi, MCP25XXFD_CRC, 0, + MCP25XXFD_CRC_CRCERRIF | + MCP25XXFD_CRC_FERRIF); +} diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_crc.h b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_crc.h new file mode 100644 index 000000000000..6e42fe0fad0f --- /dev/null +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_crc.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface + * + * Copyright 2019 Martin Sperl + */ +#ifndef __MCP25XXFD_CRC_H +#define __MCP25XXFD_CRC_H + +#include "mcp25xxfd_priv.h" + +int mcp25xxfd_crc_enable_int(struct mcp25xxfd_priv *priv, bool enable); +int mcp25xxfd_crc_clear_int(struct mcp25xxfd_priv *priv); + +#endif /* __MCP25XXFD_CRC_H */ diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_ecc.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_ecc.c new file mode 100644 index 000000000000..56e2c4fbf52d --- /dev/null +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_ecc.c @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface + * + * Copyright 2019 Martin Sperl + */ + +#include +#include +#include "mcp25xxfd_cmd.h" +#include "mcp25xxfd_ecc.h" +#include "mcp25xxfd_priv.h" +#include "mcp25xxfd_regs.h" + +int mcp25xxfd_ecc_clear_int(struct mcp25xxfd_priv *priv) +{ + u32 val, addr; + int ret; + + /* First report the error address */ + ret = mcp25xxfd_cmd_read(priv->spi, MCP25XXFD_ECCSTAT, &val); + if (ret) + return ret; + + /* If no flags are set then nothing to do */ + if (!(val & (MCP25XXFD_ECCSTAT_SECIF | MCP25XXFD_ECCSTAT_DEDIF))) + return 0; + + addr = (val & MCP25XXFD_ECCSTAT_ERRADDR_MASK) >> + MCP25XXFD_ECCSTAT_ERRADDR_SHIFT; + + dev_err_ratelimited(&priv->spi->dev, "ECC %s bit error at %03x\n", + (val & MCP25XXFD_ECCSTAT_DEDIF) ? + "double" : "single", + addr); + + /* Clear the error */ + return mcp25xxfd_cmd_write_mask(priv->spi, MCP25XXFD_ECCSTAT, 0, + MCP25XXFD_ECCSTAT_SECIF | + MCP25XXFD_ECCSTAT_DEDIF); +} + +int mcp25xxfd_ecc_enable_int(struct mcp25xxfd_priv *priv, bool enable) +{ + u32 mask = MCP25XXFD_ECCCON_SECIE | MCP25XXFD_ECCCON_DEDIE; + + priv->regs.ecccon &= ~mask; + priv->regs.ecccon |= MCP25XXFD_ECCCON_ECCEN | (enable ? mask : 0); + + return mcp25xxfd_cmd_write_mask(priv->spi, MCP25XXFD_ECCCON, + priv->regs.ecccon, + MCP25XXFD_ECCCON_ECCEN | mask); +} + +int mcp25xxfd_ecc_enable(struct mcp25xxfd_priv *priv) +{ + u8 buffer[256]; + int i, ret; + + ret = mcp25xxfd_ecc_enable_int(priv, false); + if (ret) + return ret; + + memset(buffer, 0, sizeof(buffer)); + for (i = 0; i < MCP25XXFD_SRAM_SIZE; i += sizeof(buffer)) { + ret = mcp25xxfd_cmd_write_multi(priv->spi, + MCP25XXFD_SRAM_ADDR(i), + buffer, sizeof(buffer)); + if (ret) + return ret; + } + + return 0; +} diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_ecc.h b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_ecc.h new file mode 100644 index 000000000000..117f58c65a46 --- /dev/null +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_ecc.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface + * + * Copyright 2019 Martin Sperl + */ +#ifndef __MCP25XXFD_ECC_H +#define __MCP25XXFD_ECC_H + +#include "mcp25xxfd_priv.h" + +int mcp25xxfd_ecc_clear_int(struct mcp25xxfd_priv *priv); +int mcp25xxfd_ecc_enable_int(struct mcp25xxfd_priv *priv, bool enable); +int mcp25xxfd_ecc_enable(struct mcp25xxfd_priv *priv); + +#endif /* __MCP25XXFD_ECC_H */ diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_int.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_int.c new file mode 100644 index 000000000000..5e274d452646 --- /dev/null +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_int.c @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface + * + * Copyright 2019 Martin Sperl + */ + +#include +#include + +#include "mcp25xxfd_crc.h" +#include "mcp25xxfd_ecc.h" +#include "mcp25xxfd_int.h" +#include "mcp25xxfd_priv.h" + +int mcp25xxfd_int_clear(struct mcp25xxfd_priv *priv) +{ + int ret; + + ret = mcp25xxfd_ecc_clear_int(priv); + if (ret) + return ret; + + return mcp25xxfd_crc_clear_int(priv); +} + +int mcp25xxfd_int_enable(struct mcp25xxfd_priv *priv, bool enable) +{ + int ret; + + /* If we enable interrupts, then clear interrupt first */ + if (enable) { + ret = mcp25xxfd_int_clear(priv); + if (ret) + return ret; + } + + ret = mcp25xxfd_crc_enable_int(priv, enable); + if (ret) + return ret; + + ret = mcp25xxfd_ecc_enable(priv); + if (ret) + goto out_crc; + + ret = mcp25xxfd_ecc_enable_int(priv, enable); + if (ret) + goto out_crc; + + /* If we disable interrupts, then clear interrupt flags last */ + if (!enable) + mcp25xxfd_int_clear(priv); + + return 0; + +out_crc: + mcp25xxfd_crc_enable_int(priv, false); + return ret; +} diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_int.h b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_int.h new file mode 100644 index 000000000000..4daf0182d1af --- /dev/null +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_int.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface + * + * Copyright 2019 Martin Sperl + */ +#ifndef __MCP25XXFD_INT_H +#define __MCP25XXFD_INT_H + +#include "mcp25xxfd_priv.h" + +int mcp25xxfd_int_clear(struct mcp25xxfd_priv *priv); +int mcp25xxfd_int_enable(struct mcp25xxfd_priv *priv, bool enable); + +#endif /* __MCP25XXFD_INT_H */ diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_priv.h b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_priv.h new file mode 100644 index 000000000000..8bc7a599224c --- /dev/null +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_priv.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface + * + * Copyright 2019 Martin Sperl + */ + +#ifndef __MCP25XXFD_PRIV_H +#define __MCP25XXFD_PRIV_H + +#include +#include +#include + +#include "mcp25xxfd_regs.h" + +#define DEVICE_NAME "mcp25xxfd" +#define CLOCK_4_MHZ 4000000 +#define CLOCK_10_MHZ 10000000 +#define CLOCK_40_MHZ 40000000 + +enum mcp25xxfd_model { + CAN_MCP2517FD = 0x2517, +}; + +struct mcp25xxfd_priv { + struct spi_device *spi; + struct clk *clk; + + /* actual model of the mcp25xxfd */ + enum mcp25xxfd_model model; + + /* full device name used for interrupts */ + char device_name[32]; + + int clock_freq; + struct regulator *power; + + /* configuration registers */ + struct { + u32 osc; + u32 iocon; + u32 crc; + u32 ecccon; + } regs; +}; + +#endif /* __MCP25XXFD_PRIV_H */ diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_regs.h b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_regs.h new file mode 100644 index 000000000000..b500cb46b9a4 --- /dev/null +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_regs.h @@ -0,0 +1,656 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface + * + * Copyright 2019 Martin Sperl + */ + +#ifndef __MCP25XXFD_REGS_H +#define __MCP25XXFD_REGS_H + +#include + +/* some constants derived from the datasheets */ +#define MCP25XXFD_OST_DELAY_MS 3 +#define MCP25XXFD_MIN_CLOCK_FREQUENCY 1000000 +#define MCP25XXFD_MAX_CLOCK_FREQUENCY 40000000 +#define MCP25XXFD_PLL_MULTIPLIER 10 +#define MCP25XXFD_AUTO_PLL_MAX_CLOCK_FREQUENCY \ + (MCP25XXFD_MAX_CLOCK_FREQUENCY / MCP25XXFD_PLL_MULTIPLIER) +#define MCP25XXFD_SCLK_DIVIDER 2 + +/* GPIO, clock, ecc related register definitions of Controller itself */ +#define MCP25XXFD_SFR_BASE(x) (0xE00 + (x)) +#define MCP25XXFD_OSC MCP25XXFD_SFR_BASE(0x00) +# define MCP25XXFD_OSC_PLLEN BIT(0) +# define MCP25XXFD_OSC_OSCDIS BIT(2) +# define MCP25XXFD_OSC_SCLKDIV BIT(4) +# define MCP25XXFD_OSC_CLKODIV_BITS 2 +# define MCP25XXFD_OSC_CLKODIV_SHIFT 5 +# define MCP25XXFD_OSC_CLKODIV_MASK \ + GENMASK(MCP25XXFD_OSC_CLKODIV_SHIFT \ + + MCP25XXFD_OSC_CLKODIV_BITS - 1, \ + MCP25XXFD_OSC_CLKODIV_SHIFT) +# define MCP25XXFD_OSC_CLKODIV_10 3 +# define MCP25XXFD_OSC_CLKODIV_4 2 +# define MCP25XXFD_OSC_CLKODIV_2 1 +# define MCP25XXFD_OSC_CLKODIV_1 0 +# define MCP25XXFD_OSC_PLLRDY BIT(8) +# define MCP25XXFD_OSC_OSCRDY BIT(10) +# define MCP25XXFD_OSC_SCLKRDY BIT(12) +#define MCP25XXFD_IOCON MCP25XXFD_SFR_BASE(0x04) +# define MCP25XXFD_IOCON_TRIS0 BIT(0) +# define MCP25XXFD_IOCON_TRIS1 BIT(1) +# define MCP25XXFD_IOCON_XSTBYEN BIT(6) +# define MCP25XXFD_IOCON_LAT0 BIT(8) +# define MCP25XXFD_IOCON_LAT1 BIT(9) +# define MCP25XXFD_IOCON_GPIO0 BIT(16) +# define MCP25XXFD_IOCON_GPIO1 BIT(17) +# define MCP25XXFD_IOCON_PM0 BIT(24) +# define MCP25XXFD_IOCON_PM1 BIT(25) +# define MCP25XXFD_IOCON_TXCANOD BIT(28) +# define MCP25XXFD_IOCON_SOF BIT(29) +# define MCP25XXFD_IOCON_INTOD BIT(30) +#define MCP25XXFD_CRC MCP25XXFD_SFR_BASE(0x08) +# define MCP25XXFD_CRC_MASK GENMASK(15, 0) +# define MCP25XXFD_CRC_CRCERRIE BIT(16) +# define MCP25XXFD_CRC_FERRIE BIT(17) +# define MCP25XXFD_CRC_CRCERRIF BIT(24) +# define MCP25XXFD_CRC_FERRIF BIT(25) +#define MCP25XXFD_ECCCON MCP25XXFD_SFR_BASE(0x0C) +# define MCP25XXFD_ECCCON_ECCEN BIT(0) +# define MCP25XXFD_ECCCON_SECIE BIT(1) +# define MCP25XXFD_ECCCON_DEDIE BIT(2) +# define MCP25XXFD_ECCCON_PARITY_BITS 6 +# define MCP25XXFD_ECCCON_PARITY_SHIFT 8 +# define MCP25XXFD_ECCCON_PARITY_MASK \ + GENMASK(MCP25XXFD_ECCCON_PARITY_SHIFT \ + + MCP25XXFD_ECCCON_PARITY_BITS - 1, \ + MCP25XXFD_ECCCON_PARITY_SHIFT) +#define MCP25XXFD_ECCSTAT MCP25XXFD_SFR_BASE(0x10) +# define MCP25XXFD_ECCSTAT_SECIF BIT(1) +# define MCP25XXFD_ECCSTAT_DEDIF BIT(2) +# define MCP25XXFD_ECCSTAT_ERRADDR_SHIFT 8 +# define MCP25XXFD_ECCSTAT_ERRADDR_MASK \ + GENMASK(MCP25XXFD_ECCSTAT_ERRADDR_SHIFT + 11, \ + MCP25XXFD_ECCSTAT_ERRADDR_SHIFT) + +/* CAN related register definitions of Controller CAN block */ +#define MCP25XXFD_CAN_SFR_BASE(x) (0x000 + (x)) +#define MCP25XXFD_CAN_CON \ + MCP25XXFD_CAN_SFR_BASE(0x00) +# define MCP25XXFD_CAN_CON_DNCNT_BITS 5 +# define MCP25XXFD_CAN_CON_DNCNT_SHIFT 0 +# define MCP25XXFD_CAN_CON_DNCNT_MASK \ + GENMASK(MCP25XXFD_CAN_CON_DNCNT_SHIFT + \ + MCP25XXFD_CAN_CON_DNCNT_BITS - 1, \ + MCP25XXFD_CAN_CON_DNCNT_SHIFT) +# define MCP25XXFD_CAN_CON_ISOCRCEN BIT(5) +# define MCP25XXFD_CAN_CON_PXEDIS BIT(6) +# define MCP25XXFD_CAN_CON_WAKFIL BIT(8) +# define MCP25XXFD_CAN_CON_WFT_BITS 2 +# define MCP25XXFD_CAN_CON_WFT_SHIFT 9 +# define MCP25XXFD_CAN_CON_WFT_MASK \ + GENMASK(MCP25XXFD_CAN_CON_WFT_SHIFT + \ + MCP25XXFD_CAN_CON_WFT_BITS - 1, \ + MCP25XXFD_CAN_CON_WFT_SHIFT) +# define MCP25XXFD_CAN_CON_BUSY BIT(11) +# define MCP25XXFD_CAN_CON_BRSDIS BIT(12) +# define MCP25XXFD_CAN_CON_RTXAT BIT(16) +# define MCP25XXFD_CAN_CON_ESIGM BIT(17) +# define MCP25XXFD_CAN_CON_SERR2LOM BIT(18) +# define MCP25XXFD_CAN_CON_STEF BIT(19) +# define MCP25XXFD_CAN_CON_TXQEN BIT(20) +# define MCP25XXFD_CAN_CON_OPMODE_BITS 3 +# define MCP25XXFD_CAN_CON_OPMOD_SHIFT 21 +# define MCP25XXFD_CAN_CON_OPMOD_MASK \ + GENMASK(MCP25XXFD_CAN_CON_OPMOD_SHIFT + \ + MCP25XXFD_CAN_CON_OPMODE_BITS - 1, \ + MCP25XXFD_CAN_CON_OPMOD_SHIFT) +# define MCP25XXFD_CAN_CON_REQOP_BITS 3 +# define MCP25XXFD_CAN_CON_REQOP_SHIFT 24 +# define MCP25XXFD_CAN_CON_REQOP_MASK \ + GENMASK(MCP25XXFD_CAN_CON_REQOP_SHIFT + \ + MCP25XXFD_CAN_CON_REQOP_BITS - 1, \ + MCP25XXFD_CAN_CON_REQOP_SHIFT) +# define MCP25XXFD_CAN_CON_MODE_MIXED 0 +# define MCP25XXFD_CAN_CON_MODE_SLEEP 1 +# define MCP25XXFD_CAN_CON_MODE_INT_LOOPBACK 2 +# define MCP25XXFD_CAN_CON_MODE_LISTENONLY 3 +# define MCP25XXFD_CAN_CON_MODE_CONFIG 4 +# define MCP25XXFD_CAN_CON_MODE_EXT_LOOPBACK 5 +# define MCP25XXFD_CAN_CON_MODE_CAN2_0 6 +# define MCP25XXFD_CAN_CON_MODE_RESTRICTED 7 +# define MCP25XXFD_CAN_CON_ABAT BIT(27) +# define MCP25XXFD_CAN_CON_TXBWS_BITS 3 +# define MCP25XXFD_CAN_CON_TXBWS_SHIFT 28 +# define MCP25XXFD_CAN_CON_TXBWS_MASK \ + GENMASK(MCP25XXFD_CAN_CON_TXBWS_SHIFT + \ + MCP25XXFD_CAN_CON_TXBWS_BITS - 1, \ + MCP25XXFD_CAN_CON_TXBWS_SHIFT) +# define MCP25XXFD_CAN_CON_DEFAULT \ + (MCP25XXFD_CAN_CON_ISOCRCEN | \ + MCP25XXFD_CAN_CON_PXEDIS | \ + MCP25XXFD_CAN_CON_WAKFIL | \ + (3 << MCP25XXFD_CAN_CON_WFT_SHIFT) | \ + MCP25XXFD_CAN_CON_STEF | \ + MCP25XXFD_CAN_CON_TXQEN | \ + (MCP25XXFD_CAN_CON_MODE_CONFIG << MCP25XXFD_CAN_CON_OPMOD_SHIFT) | \ + (MCP25XXFD_CAN_CON_MODE_CONFIG << MCP25XXFD_CAN_CON_REQOP_SHIFT)) +# define MCP25XXFD_CAN_CON_DEFAULT_MASK \ + (MCP25XXFD_CAN_CON_DNCNT_MASK | \ + MCP25XXFD_CAN_CON_ISOCRCEN | \ + MCP25XXFD_CAN_CON_PXEDIS | \ + MCP25XXFD_CAN_CON_WAKFIL | \ + MCP25XXFD_CAN_CON_WFT_MASK | \ + MCP25XXFD_CAN_CON_BRSDIS | \ + MCP25XXFD_CAN_CON_RTXAT | \ + MCP25XXFD_CAN_CON_ESIGM | \ + MCP25XXFD_CAN_CON_SERR2LOM | \ + MCP25XXFD_CAN_CON_STEF | \ + MCP25XXFD_CAN_CON_TXQEN | \ + MCP25XXFD_CAN_CON_OPMOD_MASK | \ + MCP25XXFD_CAN_CON_REQOP_MASK | \ + MCP25XXFD_CAN_CON_ABAT | \ + MCP25XXFD_CAN_CON_TXBWS_MASK) +#define MCP25XXFD_CAN_NBTCFG MCP25XXFD_CAN_SFR_BASE(0x04) +# define MCP25XXFD_CAN_NBTCFG_SJW_BITS 7 +# define MCP25XXFD_CAN_NBTCFG_SJW_SHIFT 0 +# define MCP25XXFD_CAN_NBTCFG_SJW_MASK \ + GENMASK(MCP25XXFD_CAN_NBTCFG_SJW_SHIFT + \ + MCP25XXFD_CAN_NBTCFG_SJW_BITS - 1, \ + MCP25XXFD_CAN_NBTCFG_SJW_SHIFT) +# define MCP25XXFD_CAN_NBTCFG_TSEG2_BITS 7 +# define MCP25XXFD_CAN_NBTCFG_TSEG2_SHIFT 8 +# define MCP25XXFD_CAN_NBTCFG_TSEG2_MASK \ + GENMASK(MCP25XXFD_CAN_NBTCFG_TSEG2_SHIFT + \ + MCP25XXFD_CAN_NBTCFG_TSEG2_BITS - 1, \ + MCP25XXFD_CAN_NBTCFG_TSEG2_SHIFT) +# define MCP25XXFD_CAN_NBTCFG_TSEG1_BITS 8 +# define MCP25XXFD_CAN_NBTCFG_TSEG1_SHIFT 16 +# define MCP25XXFD_CAN_NBTCFG_TSEG1_MASK \ + GENMASK(MCP25XXFD_CAN_NBTCFG_TSEG1_SHIFT + \ + MCP25XXFD_CAN_NBTCFG_TSEG1_BITS - 1, \ + MCP25XXFD_CAN_NBTCFG_TSEG1_SHIFT) +# define MCP25XXFD_CAN_NBTCFG_BRP_BITS 8 +# define MCP25XXFD_CAN_NBTCFG_BRP_SHIFT 24 +# define MCP25XXFD_CAN_NBTCFG_BRP_MASK \ + GENMASK(MCP25XXFD_CAN_NBTCFG_BRP_SHIFT + \ + MCP25XXFD_CAN_NBTCFG_BRP_BITS - 1, \ + MCP25XXFD_CAN_NBTCFG_BRP_SHIFT) +#define MCP25XXFD_CAN_DBTCFG MCP25XXFD_CAN_SFR_BASE(0x08) +# define MCP25XXFD_CAN_DBTCFG_SJW_BITS 4 +# define MCP25XXFD_CAN_DBTCFG_SJW_SHIFT 0 +# define MCP25XXFD_CAN_DBTCFG_SJW_MASK \ + GENMASK(MCP25XXFD_CAN_DBTCFG_SJW_SHIFT + \ + MCP25XXFD_CAN_DBTCFG_SJW_BITS - 1, \ + MCP25XXFD_CAN_DBTCFG_SJW_SHIFT) +# define MCP25XXFD_CAN_DBTCFG_TSEG2_BITS 4 +# define MCP25XXFD_CAN_DBTCFG_TSEG2_SHIFT 8 +# define MCP25XXFD_CAN_DBTCFG_TSEG2_MASK \ + GENMASK(MCP25XXFD_CAN_DBTCFG_TSEG2_SHIFT + \ + MCP25XXFD_CAN_DBTCFG_TSEG2_BITS - 1, \ + MCP25XXFD_CAN_DBTCFG_TSEG2_SHIFT) +# define MCP25XXFD_CAN_DBTCFG_TSEG1_BITS 5 +# define MCP25XXFD_CAN_DBTCFG_TSEG1_SHIFT 16 +# define MCP25XXFD_CAN_DBTCFG_TSEG1_MASK \ + GENMASK(MCP25XXFD_CAN_DBTCFG_TSEG1_SHIFT + \ + MCP25XXFD_CAN_DBTCFG_TSEG1_BITS - 1, \ + MCP25XXFD_CAN_DBTCFG_TSEG1_SHIFT) +# define MCP25XXFD_CAN_DBTCFG_BRP_BITS 8 +# define MCP25XXFD_CAN_DBTCFG_BRP_SHIFT 24 +# define MCP25XXFD_CAN_DBTCFG_BRP_MASK \ + GENMASK(MCP25XXFD_CAN_DBTCFG_BRP_SHIFT + \ + MCP25XXFD_CAN_DBTCFG_BRP_BITS - 1, \ + MCP25XXFD_CAN_DBTCFG_BRP_SHIFT) +#define MCP25XXFD_CAN_TDC MCP25XXFD_CAN_SFR_BASE(0x0C) +# define MCP25XXFD_CAN_TDC_TDCV_BITS 5 +# define MCP25XXFD_CAN_TDC_TDCV_SHIFT 0 +# define MCP25XXFD_CAN_TDC_TDCV_MASK \ + GENMASK(MCP25XXFD_CAN_TDC_TDCV_SHIFT + \ + MCP25XXFD_CAN_TDC_TDCV_BITS - 1, \ + MCP25XXFD_CAN_TDC_TDCV_SHIFT) +# define MCP25XXFD_CAN_TDC_TDCO_BITS 5 +# define MCP25XXFD_CAN_TDC_TDCO_SHIFT 8 +# define MCP25XXFD_CAN_TDC_TDCO_MASK \ + GENMASK(MCP25XXFD_CAN_TDC_TDCO_SHIFT + \ + MCP25XXFD_CAN_TDC_TDCO_BITS - 1, \ + MCP25XXFD_CAN_TDC_TDCO_SHIFT) +# define MCP25XXFD_CAN_TDC_TDCMOD_BITS 2 +# define MCP25XXFD_CAN_TDC_TDCMOD_SHIFT 16 +# define MCP25XXFD_CAN_TDC_TDCMOD_MASK \ + GENMASK(MCP25XXFD_CAN_TDC_TDCMOD_SHIFT + \ + MCP25XXFD_CAN_TDC_TDCMOD_BITS - 1, \ + MCP25XXFD_CAN_TDC_TDCMOD_SHIFT) +# define MCP25XXFD_CAN_TDC_TDCMOD_DISABLED 0 +# define MCP25XXFD_CAN_TDC_TDCMOD_MANUAL 1 +# define MCP25XXFD_CAN_TDC_TDCMOD_AUTO 2 +# define MCP25XXFD_CAN_TDC_SID11EN BIT(24) +# define MCP25XXFD_CAN_TDC_EDGFLTEN BIT(25) +#define MCP25XXFD_CAN_TBC MCP25XXFD_CAN_SFR_BASE(0x10) +#define MCP25XXFD_CAN_TSCON MCP25XXFD_CAN_SFR_BASE(0x14) +# define MCP25XXFD_CAN_TSCON_TBCPRE_BITS 10 +# define MCP25XXFD_CAN_TSCON_TBCPRE_SHIFT 0 +# define MCP25XXFD_CAN_TSCON_TBCPRE_MASK \ + GENMASK(MCP25XXFD_CAN_TSCON_TBCPRE_SHIFT + \ + MCP25XXFD_CAN_TSCON_TBCPRE_BITS - 1, \ + MCP25XXFD_CAN_TSCON_TBCPRE_SHIFT) +# define MCP25XXFD_CAN_TSCON_TBCEN BIT(16) +# define MCP25XXFD_CAN_TSCON_TSEOF BIT(17) +# define MCP25XXFD_CAN_TSCON_TSRES BIT(18) +#define MCP25XXFD_CAN_VEC MCP25XXFD_CAN_SFR_BASE(0x18) +# define MCP25XXFD_CAN_VEC_ICODE_BITS 7 +# define MCP25XXFD_CAN_VEC_ICODE_SHIFT 0 +# define MCP25XXFD_CAN_VEC_ICODE_MASK \ + GENMASK(MCP25XXFD_CAN_VEC_ICODE_SHIFT + \ + MCP25XXFD_CAN_VEC_ICODE_BITS - 1, \ + MCP25XXFD_CAN_VEC_ICODE_SHIFT) +# define MCP25XXFD_CAN_VEC_FILHIT_BITS 5 +# define MCP25XXFD_CAN_VEC_FILHIT_SHIFT 8 +# define MCP25XXFD_CAN_VEC_FILHIT_MASK \ + GENMASK(MCP25XXFD_CAN_VEC_FILHIT_SHIFT + \ + MCP25XXFD_CAN_VEC_FILHIT_BITS - 1, \ + MCP25XXFD_CAN_VEC_FILHIT_SHIFT) +# define MCP25XXFD_CAN_VEC_TXCODE_BITS 7 +# define MCP25XXFD_CAN_VEC_TXCODE_SHIFT 16 +# define MCP25XXFD_CAN_VEC_TXCODE_MASK \ + GENMASK(MCP25XXFD_CAN_VEC_TXCODE_SHIFT + \ + MCP25XXFD_CAN_VEC_TXCODE_BITS - 1, \ + MCP25XXFD_CAN_VEC_TXCODE_SHIFT) +# define MCP25XXFD_CAN_VEC_RXCODE_BITS 7 +# define MCP25XXFD_CAN_VEC_RXCODE_SHIFT 24 +# define MCP25XXFD_CAN_VEC_RXCODE_MASK \ + GENMASK(MCP25XXFD_CAN_VEC_RXCODE_SHIFT + \ + MCP25XXFD_CAN_VEC_RXCODE_BITS - 1, \ + MCP25XXFD_CAN_VEC_RXCODE_SHIFT) +#define MCP25XXFD_CAN_INT MCP25XXFD_CAN_SFR_BASE(0x1C) +# define MCP25XXFD_CAN_INT_IF_SHIFT 0 +# define MCP25XXFD_CAN_INT_TXIF BIT(0) +# define MCP25XXFD_CAN_INT_RXIF BIT(1) +# define MCP25XXFD_CAN_INT_TBCIF BIT(2) +# define MCP25XXFD_CAN_INT_MODIF BIT(3) +# define MCP25XXFD_CAN_INT_TEFIF BIT(4) +# define MCP25XXFD_CAN_INT_ECCIF BIT(8) +# define MCP25XXFD_CAN_INT_SPICRCIF BIT(9) +# define MCP25XXFD_CAN_INT_TXATIF BIT(10) +# define MCP25XXFD_CAN_INT_RXOVIF BIT(11) +# define MCP25XXFD_CAN_INT_SERRIF BIT(12) +# define MCP25XXFD_CAN_INT_CERRIF BIT(13) +# define MCP25XXFD_CAN_INT_WAKIF BIT(14) +# define MCP25XXFD_CAN_INT_IVMIF BIT(15) +# define MCP25XXFD_CAN_INT_IF_MASK \ + (MCP25XXFD_CAN_INT_TXIF | \ + MCP25XXFD_CAN_INT_RXIF | \ + MCP25XXFD_CAN_INT_TBCIF | \ + MCP25XXFD_CAN_INT_MODIF | \ + MCP25XXFD_CAN_INT_TEFIF | \ + MCP25XXFD_CAN_INT_ECCIF | \ + MCP25XXFD_CAN_INT_SPICRCIF | \ + MCP25XXFD_CAN_INT_TXATIF | \ + MCP25XXFD_CAN_INT_RXOVIF | \ + MCP25XXFD_CAN_INT_CERRIF | \ + MCP25XXFD_CAN_INT_SERRIF | \ + MCP25XXFD_CAN_INT_WAKIF | \ + MCP25XXFD_CAN_INT_IVMIF) +# define MCP25XXFD_CAN_INT_IF_CLEAR_MASK \ + (MCP25XXFD_CAN_INT_TBCIF | \ + MCP25XXFD_CAN_INT_MODIF | \ + MCP25XXFD_CAN_INT_CERRIF | \ + MCP25XXFD_CAN_INT_SERRIF | \ + MCP25XXFD_CAN_INT_WAKIF | \ + MCP25XXFD_CAN_INT_IVMIF) +# define MCP25XXFD_CAN_INT_IE_SHIFT 16 +# define MCP25XXFD_CAN_INT_TXIE \ + (MCP25XXFD_CAN_INT_TXIF << MCP25XXFD_CAN_INT_IE_SHIFT) +# define MCP25XXFD_CAN_INT_RXIE \ + (MCP25XXFD_CAN_INT_RXIF << MCP25XXFD_CAN_INT_IE_SHIFT) +# define MCP25XXFD_CAN_INT_TBCIE \ + (MCP25XXFD_CAN_INT_TBCIF << MCP25XXFD_CAN_INT_IE_SHIFT) +# define MCP25XXFD_CAN_INT_MODIE \ + (MCP25XXFD_CAN_INT_MODIF << MCP25XXFD_CAN_INT_IE_SHIFT) +# define MCP25XXFD_CAN_INT_TEFIE \ + (MCP25XXFD_CAN_INT_TEFIF << MCP25XXFD_CAN_INT_IE_SHIFT) +# define MCP25XXFD_CAN_INT_ECCIE \ + (MCP25XXFD_CAN_INT_ECCIF << MCP25XXFD_CAN_INT_IE_SHIFT) +# define MCP25XXFD_CAN_INT_SPICRCIE \ + (MCP25XXFD_CAN_INT_SPICRCIF << MCP25XXFD_CAN_INT_IE_SHIFT) +# define MCP25XXFD_CAN_INT_TXATIE \ + (MCP25XXFD_CAN_INT_TXATIF << MCP25XXFD_CAN_INT_IE_SHIFT) +# define MCP25XXFD_CAN_INT_RXOVIE \ + (MCP25XXFD_CAN_INT_RXOVIF << MCP25XXFD_CAN_INT_IE_SHIFT) +# define MCP25XXFD_CAN_INT_CERRIE \ + (MCP25XXFD_CAN_INT_CERRIF << MCP25XXFD_CAN_INT_IE_SHIFT) +# define MCP25XXFD_CAN_INT_SERRIE \ + (MCP25XXFD_CAN_INT_SERRIF << MCP25XXFD_CAN_INT_IE_SHIFT) +# define MCP25XXFD_CAN_INT_WAKIE \ + (MCP25XXFD_CAN_INT_WAKIF << MCP25XXFD_CAN_INT_IE_SHIFT) +# define MCP25XXFD_CAN_INT_IVMIE \ + (MCP25XXFD_CAN_INT_IVMIF << MCP25XXFD_CAN_INT_IE_SHIFT) +# define MCP25XXFD_CAN_INT_IE_MASK \ + (MCP25XXFD_CAN_INT_TXIE | \ + MCP25XXFD_CAN_INT_RXIE | \ + MCP25XXFD_CAN_INT_TBCIE | \ + MCP25XXFD_CAN_INT_MODIE | \ + MCP25XXFD_CAN_INT_TEFIE | \ + MCP25XXFD_CAN_INT_ECCIE | \ + MCP25XXFD_CAN_INT_SPICRCIE | \ + MCP25XXFD_CAN_INT_TXATIE | \ + MCP25XXFD_CAN_INT_RXOVIE | \ + MCP25XXFD_CAN_INT_CERRIE | \ + MCP25XXFD_CAN_INT_SERRIE | \ + MCP25XXFD_CAN_INT_WAKIE | \ + MCP25XXFD_CAN_INT_IVMIE) +#define MCP25XXFD_CAN_RXIF MCP25XXFD_CAN_SFR_BASE(0x20) +#define MCP25XXFD_CAN_TXIF MCP25XXFD_CAN_SFR_BASE(0x24) +#define MCP25XXFD_CAN_RXOVIF MCP25XXFD_CAN_SFR_BASE(0x28) +#define MCP25XXFD_CAN_TXATIF MCP25XXFD_CAN_SFR_BASE(0x2C) +#define MCP25XXFD_CAN_TXREQ MCP25XXFD_CAN_SFR_BASE(0x30) +#define MCP25XXFD_CAN_TREC MCP25XXFD_CAN_SFR_BASE(0x34) +# define MCP25XXFD_CAN_TREC_REC_BITS 8 +# define MCP25XXFD_CAN_TREC_REC_SHIFT 0 +# define MCP25XXFD_CAN_TREC_REC_MASK \ + GENMASK(MCP25XXFD_CAN_TREC_REC_SHIFT + \ + MCP25XXFD_CAN_TREC_REC_BITS - 1, \ + MCP25XXFD_CAN_TREC_REC_SHIFT) +# define MCP25XXFD_CAN_TREC_TEC_BITS 8 +# define MCP25XXFD_CAN_TREC_TEC_SHIFT 8 +# define MCP25XXFD_CAN_TREC_TEC_MASK \ + GENMASK(MCP25XXFD_CAN_TREC_TEC_SHIFT + \ + MCP25XXFD_CAN_TREC_TEC_BITS - 1, \ + MCP25XXFD_CAN_TREC_TEC_SHIFT) +# define MCP25XXFD_CAN_TREC_EWARN BIT(16) +# define MCP25XXFD_CAN_TREC_RXWARN BIT(17) +# define MCP25XXFD_CAN_TREC_TXWARN BIT(18) +# define MCP25XXFD_CAN_TREC_RXBP BIT(19) +# define MCP25XXFD_CAN_TREC_TXBP BIT(20) +# define MCP25XXFD_CAN_TREC_TXBO BIT(21) +#define MCP25XXFD_CAN_BDIAG0 MCP25XXFD_CAN_SFR_BASE(0x38) +# define MCP25XXFD_CAN_BDIAG0_NRERRCNT_BITS 8 +# define MCP25XXFD_CAN_BDIAG0_NRERRCNT_SHIFT 0 +# define MCP25XXFD_CAN_BDIAG0_NRERRCNT_MASK \ + GENMASK(MCP25XXFD_CAN_BDIAG0_NRERRCNT_SHIFT + \ + MCP25XXFD_CAN_BDIAG0_NRERRCNT_BITS - 1, \ + MCP25XXFD_CAN_BDIAG0_NRERRCNT_SHIFT) +# define MCP25XXFD_CAN_BDIAG0_NTERRCNT_BITS 8 +# define MCP25XXFD_CAN_BDIAG0_NTERRCNT_SHIFT 8 +# define MCP25XXFD_CAN_BDIAG0_NTERRCNT_MASK \ + GENMASK(MCP25XXFD_CAN_BDIAG0_NTERRCNT_SHIFT + \ + MCP25XXFD_CAN_BDIAG0_NTERRCNT_BITS - 1, \ + MCP25XXFD_CAN_BDIAG0_NTERRCNT_SHIFT) +# define MCP25XXFD_CAN_BDIAG0_DRERRCNT_BITS 8 +# define MCP25XXFD_CAN_BDIAG0_DRERRCNT_SHIFT 16 +# define MCP25XXFD_CAN_BDIAG0_DRERRCNT_MASK \ + GENMASK(MCP25XXFD_CAN_BDIAG0_DRERRCNT_SHIFT + \ + MCP25XXFD_CAN_BDIAG0_DRERRCNT_BITS - 1, \ + MCP25XXFD_CAN_BDIAG0_DRERRCNT_SHIFT) +# define MCP25XXFD_CAN_BDIAG0_DTERRCNT_BITS 8 +# define MCP25XXFD_CAN_BDIAG0_DTERRCNT_SHIFT 24 +# define MCP25XXFD_CAN_BDIAG0_DTERRCNT_MASK \ + GENMASK(MCP25XXFD_CAN_BDIAG0_DTERRCNT_SHIFT + \ + MCP25XXFD_CAN_BDIAG0_DTERRCNT_BITS - 1, \ + MCP25XXFD_CAN_BDIAG0_DTERRCNT_SHIFT) +#define MCP25XXFD_CAN_BDIAG1 MCP25XXFD_CAN_SFR_BASE(0x3C) +# define MCP25XXFD_CAN_BDIAG1_EFMSGCNT_BITS 16 +# define MCP25XXFD_CAN_BDIAG1_EFMSGCNT_SHIFT 0 +# define MCP25XXFD_CAN_BDIAG1_EFMSGCNT_MASK \ + GENMASK(MCP25XXFD_CAN_BDIAG1_EFMSGCNT_SHIFT + \ + MCP25XXFD_CAN_BDIAG1_EFMSGCNT_BITS - 1, \ + MCP25XXFD_CAN_BDIAG1_EFMSGCNT_SHIFT) +# define MCP25XXFD_CAN_BDIAG1_NBIT0ERR BIT(16) +# define MCP25XXFD_CAN_BDIAG1_NBIT1ERR BIT(17) +# define MCP25XXFD_CAN_BDIAG1_NACKERR BIT(18) +# define MCP25XXFD_CAN_BDIAG1_NSTUFERR BIT(19) +# define MCP25XXFD_CAN_BDIAG1_NFORMERR BIT(20) +# define MCP25XXFD_CAN_BDIAG1_NCRCERR BIT(21) +# define MCP25XXFD_CAN_BDIAG1_TXBOERR BIT(23) +# define MCP25XXFD_CAN_BDIAG1_DBIT0ERR BIT(24) +# define MCP25XXFD_CAN_BDIAG1_DBIT1ERR BIT(25) +# define MCP25XXFD_CAN_BDIAG1_DFORMERR BIT(27) +# define MCP25XXFD_CAN_BDIAG1_DSTUFERR BIT(28) +# define MCP25XXFD_CAN_BDIAG1_DCRCERR BIT(29) +# define MCP25XXFD_CAN_BDIAG1_ESI BIT(30) +# define MCP25XXFD_CAN_BDIAG1_DLCMM BIT(31) +#define MCP25XXFD_CAN_TEFCON MCP25XXFD_CAN_SFR_BASE(0x40) +# define MCP25XXFD_CAN_TEFCON_TEFNEIE BIT(0) +# define MCP25XXFD_CAN_TEFCON_TEFHIE BIT(1) +# define MCP25XXFD_CAN_TEFCON_TEFFIE BIT(2) +# define MCP25XXFD_CAN_TEFCON_TEFOVIE BIT(3) +# define MCP25XXFD_CAN_TEFCON_TEFTSEN BIT(5) +# define MCP25XXFD_CAN_TEFCON_UINC BIT(8) +# define MCP25XXFD_CAN_TEFCON_FRESET BIT(10) +# define MCP25XXFD_CAN_TEFCON_FSIZE_BITS 5 +# define MCP25XXFD_CAN_TEFCON_FSIZE_SHIFT 24 +# define MCP25XXFD_CAN_TEFCON_FSIZE_MASK \ + GENMASK(MCP25XXFD_CAN_TEFCON_FSIZE_SHIFT + \ + MCP25XXFD_CAN_TEFCON_FSIZE_BITS - 1, \ + MCP25XXFD_CAN_TEFCON_FSIZE_SHIFT) +#define MCP25XXFD_CAN_TEFSTA MCP25XXFD_CAN_SFR_BASE(0x44) +# define MCP25XXFD_CAN_TEFSTA_TEFNEIF BIT(0) +# define MCP25XXFD_CAN_TEFSTA_TEFHIF BIT(1) +# define MCP25XXFD_CAN_TEFSTA_TEFFIF BIT(2) +# define MCP25XXFD_CAN_TEFSTA_TEVOVIF BIT(3) +#define MCP25XXFD_CAN_TEFUA MCP25XXFD_CAN_SFR_BASE(0x48) +#define MCP25XXFD_CAN_RESERVED MCP25XXFD_CAN_SFR_BASE(0x4C) +#define MCP25XXFD_CAN_TXQCON MCP25XXFD_CAN_SFR_BASE(0x50) +# define MCP25XXFD_CAN_TXQCON_TXQNIE BIT(0) +# define MCP25XXFD_CAN_TXQCON_TXQEIE BIT(2) +# define MCP25XXFD_CAN_TXQCON_TXATIE BIT(4) +# define MCP25XXFD_CAN_TXQCON_TXEN BIT(7) +# define MCP25XXFD_CAN_TXQCON_UINC BIT(8) +# define MCP25XXFD_CAN_TXQCON_TXREQ BIT(9) +# define MCP25XXFD_CAN_TXQCON_FRESET BIT(10) +# define MCP25XXFD_CAN_TXQCON_TXPRI_BITS 5 +# define MCP25XXFD_CAN_TXQCON_TXPRI_SHIFT 16 +# define MCP25XXFD_CAN_TXQCON_TXPRI_MASK \ + GENMASK(MCP25XXFD_CAN_TXQCON_TXPRI_SHIFT + \ + MCP25XXFD_CAN_TXQCON_TXPRI_BITS - 1, \ + MCP25XXFD_CAN_TXQCON_TXPRI_SHIFT) +# define MCP25XXFD_CAN_TXQCON_TXAT_BITS 2 +# define MCP25XXFD_CAN_TXQCON_TXAT_SHIFT 21 +# define MCP25XXFD_CAN_TXQCON_TXAT_MASK \ + GENMASK(MCP25XXFD_CAN_TXQCON_TXAT_SHIFT + \ + #MCP25XXFD_CAN_TXQCON_TXAT_BITS - 1, \ + MCP25XXFD_CAN_TXQCON_TXAT_SHIFT) +# define MCP25XXFD_CAN_TXQCON_FSIZE_BITS 5 +# define MCP25XXFD_CAN_TXQCON_FSIZE_SHIFT 24 +# define MCP25XXFD_CAN_TXQCON_FSIZE_MASK \ + GENMASK(MCP25XXFD_CAN_TXQCON_FSIZE_SHIFT + \ + MCP25XXFD_CAN_TXQCON_FSIZE_BITS - 1, \ + MCP25XXFD_CAN_TXQCON_FSIZE_SHIFT) +# define MCP25XXFD_CAN_TXQCON_PLSIZE_BITS 3 +# define MCP25XXFD_CAN_TXQCON_PLSIZE_SHIFT 29 +# define MCP25XXFD_CAN_TXQCON_PLSIZE_MASK \ + GENMASK(MCP25XXFD_CAN_TXQCON_PLSIZE_SHIFT + \ + MCP25XXFD_CAN_TXQCON_PLSIZE_BITS - 1, \ + MCP25XXFD_CAN_TXQCON_PLSIZE_SHIFT) +# define MCP25XXFD_CAN_TXQCON_PLSIZE_8 0 +# define MCP25XXFD_CAN_TXQCON_PLSIZE_12 1 +# define MCP25XXFD_CAN_TXQCON_PLSIZE_16 2 +# define MCP25XXFD_CAN_TXQCON_PLSIZE_20 3 +# define MCP25XXFD_CAN_TXQCON_PLSIZE_24 4 +# define MCP25XXFD_CAN_TXQCON_PLSIZE_32 5 +# define MCP25XXFD_CAN_TXQCON_PLSIZE_48 6 +# define MCP25XXFD_CAN_TXQCON_PLSIZE_64 7 + +#define MCP25XXFD_CAN_TXQSTA MCP25XXFD_CAN_SFR_BASE(0x54) +# define MCP25XXFD_CAN_TXQSTA_TXQNIF BIT(0) +# define MCP25XXFD_CAN_TXQSTA_TXQEIF BIT(2) +# define MCP25XXFD_CAN_TXQSTA_TXATIF BIT(4) +# define MCP25XXFD_CAN_TXQSTA_TXERR BIT(5) +# define MCP25XXFD_CAN_TXQSTA_TXLARB BIT(6) +# define MCP25XXFD_CAN_TXQSTA_TXABT BIT(7) +# define MCP25XXFD_CAN_TXQSTA_TXQCI_BITS 5 +# define MCP25XXFD_CAN_TXQSTA_TXQCI_SHIFT 8 +# define MCP25XXFD_CAN_TXQSTA_TXQCI_MASK \ + GENMASK(MCP25XXFD_CAN_TXQSTA_TXQCI_SHIFT + \ + MCP25XXFD_CAN_TXQSTA_TXQCI_BITS - 1, \ + MCP25XXFD_CAN_TXQSTA_TXQCI_SHIFT) + +#define MCP25XXFD_CAN_TXQUA MCP25XXFD_CAN_SFR_BASE(0x58) +#define MCP25XXFD_CAN_FIFOCON(x) \ + MCP25XXFD_CAN_SFR_BASE(0x5C + 12 * ((x) - 1)) +#define MCP25XXFD_CAN_FIFOCON_TFNRFNIE BIT(0) +#define MCP25XXFD_CAN_FIFOCON_TFHRFHIE BIT(1) +#define MCP25XXFD_CAN_FIFOCON_TFERFFIE BIT(2) +#define MCP25XXFD_CAN_FIFOCON_RXOVIE BIT(3) +#define MCP25XXFD_CAN_FIFOCON_TXATIE BIT(4) +#define MCP25XXFD_CAN_FIFOCON_RXTSEN BIT(5) +#define MCP25XXFD_CAN_FIFOCON_RTREN BIT(6) +#define MCP25XXFD_CAN_FIFOCON_TXEN BIT(7) +#define MCP25XXFD_CAN_FIFOCON_UINC BIT(8) +#define MCP25XXFD_CAN_FIFOCON_TXREQ BIT(9) +#define MCP25XXFD_CAN_FIFOCON_FRESET BIT(10) +# define MCP25XXFD_CAN_FIFOCON_TXPRI_BITS 5 +# define MCP25XXFD_CAN_FIFOCON_TXPRI_SHIFT 16 +# define MCP25XXFD_CAN_FIFOCON_TXPRI_MASK \ + GENMASK(MCP25XXFD_CAN_FIFOCON_TXPRI_SHIFT + \ + MCP25XXFD_CAN_FIFOCON_TXPRI_BITS - 1, \ + MCP25XXFD_CAN_FIFOCON_TXPRI_SHIFT) +# define MCP25XXFD_CAN_FIFOCON_TXAT_BITS 2 +# define MCP25XXFD_CAN_FIFOCON_TXAT_SHIFT 21 +# define MCP25XXFD_CAN_FIFOCON_TXAT_MASK \ + GENMASK(MCP25XXFD_CAN_FIFOCON_TXAT_SHIFT + \ + MCP25XXFD_CAN_FIFOCON_TXAT_BITS - 1, \ + MCP25XXFD_CAN_FIFOCON_TXAT_SHIFT) +# define MCP25XXFD_CAN_FIFOCON_TXAT_ONE_SHOT 0 +# define MCP25XXFD_CAN_FIFOCON_TXAT_THREE_SHOT 1 +# define MCP25XXFD_CAN_FIFOCON_TXAT_UNLIMITED 2 +# define MCP25XXFD_CAN_FIFOCON_FSIZE_BITS 5 +# define MCP25XXFD_CAN_FIFOCON_FSIZE_SHIFT 24 +# define MCP25XXFD_CAN_FIFOCON_FSIZE_MASK \ + GENMASK(MCP25XXFD_CAN_FIFOCON_FSIZE_SHIFT + \ + MCP25XXFD_CAN_FIFOCON_FSIZE_BITS - 1, \ + MCP25XXFD_CAN_FIFOCON_FSIZE_SHIFT) +# define MCP25XXFD_CAN_FIFOCON_PLSIZE_BITS 3 +# define MCP25XXFD_CAN_FIFOCON_PLSIZE_SHIFT 29 +# define MCP25XXFD_CAN_FIFOCON_PLSIZE_MASK \ + GENMASK(MCP25XXFD_CAN_FIFOCON_PLSIZE_SHIFT + \ + MCP25XXFD_CAN_FIFOCON_PLSIZE_BITS - 1, \ + MCP25XXFD_CAN_FIFOCON_PLSIZE_SHIFT) +#define MCP25XXFD_CAN_FIFOSTA(x) \ + MCP25XXFD_CAN_SFR_BASE(0x60 + 12 * ((x) - 1)) +# define MCP25XXFD_CAN_FIFOSTA_TFNRFNIF BIT(0) +# define MCP25XXFD_CAN_FIFOSTA_TFHRFHIF BIT(1) +# define MCP25XXFD_CAN_FIFOSTA_TFERFFIF BIT(2) +# define MCP25XXFD_CAN_FIFOSTA_RXOVIF BIT(3) +# define MCP25XXFD_CAN_FIFOSTA_TXATIF BIT(4) +# define MCP25XXFD_CAN_FIFOSTA_TXERR BIT(5) +# define MCP25XXFD_CAN_FIFOSTA_TXLARB BIT(6) +# define MCP25XXFD_CAN_FIFOSTA_TXABT BIT(7) +# define MCP25XXFD_CAN_FIFOSTA_FIFOCI_BITS 5 +# define MCP25XXFD_CAN_FIFOSTA_FIFOCI_SHIFT 8 +# define MCP25XXFD_CAN_FIFOSTA_FIFOCI_MASK \ + GENMASK(MCP25XXFD_CAN_FIFOSTA_FIFOCI_SHIFT + \ + MCP25XXFD_CAN_FIFOSTA_FIFOCI_BITS - 1, \ + MCP25XXFD_CAN_FIFOSTA_FIFOCI_SHIFT) +#define MCP25XXFD_CAN_FIFOUA(x) \ + MCP25XXFD_CAN_SFR_BASE(0x64 + 12 * ((x) - 1)) +#define MCP25XXFD_CAN_FLTCON(x) \ + MCP25XXFD_CAN_SFR_BASE(0x1D0 + ((x) & 0x1c)) +# define MCP25XXFD_CAN_FILCON_SHIFT(x) (((x) & 3) * 8) +# define MCP25XXFD_CAN_FILCON_BITS(x) MCP25XXFD_CAN_FILCON_BITS_ +# define MCP25XXFD_CAN_FILCON_BITS_ 4 + /* avoid macro reuse warning, so do not use GENMASK as above */ +# define MCP25XXFD_CAN_FILCON_MASK(x) \ + (GENMASK(MCP25XXFD_CAN_FILCON_BITS_ - 1, 0) << \ + MCP25XXFD_CAN_FILCON_SHIFT(x)) +# define MCP25XXFD_CAN_FIFOCON_FLTEN(x) \ + BIT(7 + MCP25XXFD_CAN_FILCON_SHIFT(x)) +#define MCP25XXFD_CAN_FLTOBJ(x) \ + MCP25XXFD_CAN_SFR_BASE(0x1F0 + 8 * (x)) +# define MCP25XXFD_CAN_FILOBJ_SID_BITS 11 +# define MCP25XXFD_CAN_FILOBJ_SID_SHIFT 0 +# define MCP25XXFD_CAN_FILOBJ_SID_MASK \ + GENMASK(MCP25XXFD_CAN_FILOBJ_SID_SHIFT + \ + MCP25XXFD_CAN_FILOBJ_SID_BITS - 1, \ + MCP25XXFD_CAN_FILOBJ_SID_SHIFT) +# define MCP25XXFD_CAN_FILOBJ_EID_BITS 18 +# define MCP25XXFD_CAN_FILOBJ_EID_SHIFT 12 +# define MCP25XXFD_CAN_FILOBJ_EID_MASK \ + GENMASK(MCP25XXFD_CAN_FILOBJ_EID_SHIFT + \ + MCP25XXFD_CAN_FILOBJ_EID_BITS - 1, \ + MCP25XXFD_CAN_FILOBJ_EID_SHIFT) +# define MCP25XXFD_CAN_FILOBJ_SID11 BIT(29) +# define MCP25XXFD_CAN_FILOBJ_EXIDE BIT(30) +#define MCP25XXFD_CAN_FLTMASK(x) \ + MCP25XXFD_CAN_SFR_BASE(0x1F4 + 8 * (x)) +# define MCP25XXFD_CAN_FILMASK_MSID_BITS 11 +# define MCP25XXFD_CAN_FILMASK_MSID_SHIFT 0 +# define MCP25XXFD_CAN_FILMASK_MSID_MASK \ + GENMASK(MCP25XXFD_CAN_FILMASK_MSID_SHIFT + \ + MCP25XXFD_CAN_FILMASK_MSID_BITS - 1, \ + MCP25XXFD_CAN_FILMASK_MSID_SHIFT) +# define MCP25XXFD_CAN_FILMASK_MEID_BITS 18 +# define MCP25XXFD_CAN_FILMASK_MEID_SHIFT 12 +# define MCP25XXFD_CAN_FILMASK_MEID_MASK \ + GENMASK(MCP25XXFD_CAN_FILMASK_MEID_SHIFT + \ + MCP25XXFD_CAN_FILMASK_MEID_BITS - 1, \ + MCP25XXFD_CAN_FILMASK_MEID_SHIFT) +# define MCP25XXFD_CAN_FILMASK_MSID11 BIT(29) +# define MCP25XXFD_CAN_FILMASK_MIDE BIT(30) + +/* the FIFO Objects in SRAM */ +#define MCP25XXFD_SRAM_SIZE 2048 +#define MCP25XXFD_SRAM_ADDR(x) (0x400 + (x)) + +/* memory structure in sram for tx fifos */ +struct mcp25xxfd_can_obj_tx { + u32 id; + u32 flags; + u8 data[]; +}; + +/* memory structure in sram for rx fifos */ +struct mcp25xxfd_can_obj_rx { + u32 id; + u32 flags; + u32 ts; + u8 data[]; +}; + +/* memory structure in sram for tef fifos */ +struct mcp25xxfd_can_obj_tef { + u32 id; + u32 flags; + u32 ts; +}; + +#define MCP25XXFD_CAN_OBJ_ID_SID_BITS 11 +#define MCP25XXFD_CAN_OBJ_ID_SID_SHIFT 0 +#define MCP25XXFD_CAN_OBJ_ID_SID_MASK \ + GENMASK(MCP25XXFD_CAN_OBJ_ID_SID_SHIFT + \ + MCP25XXFD_CAN_OBJ_ID_SID_BITS - 1, \ + MCP25XXFD_CAN_OBJ_ID_SID_SHIFT) +#define MCP25XXFD_CAN_OBJ_ID_EID_BITS 18 +#define MCP25XXFD_CAN_OBJ_ID_EID_SHIFT 11 +#define MCP25XXFD_CAN_OBJ_ID_EID_MASK \ + GENMASK(MCP25XXFD_CAN_OBJ_ID_EID_SHIFT + \ + MCP25XXFD_CAN_OBJ_ID_EID_BITS - 1, \ + MCP25XXFD_CAN_OBJ_ID_EID_SHIFT) +#define MCP25XXFD_CAN_OBJ_ID_SID_BIT11 BIT(29) + +#define MCP25XXFD_CAN_OBJ_FLAGS_DLC_BITS 4 +#define MCP25XXFD_CAN_OBJ_FLAGS_DLC_SHIFT 0 +#define MCP25XXFD_CAN_OBJ_FLAGS_DLC_MASK \ + GENMASK(MCP25XXFD_CAN_OBJ_FLAGS_DLC_SHIFT + \ + MCP25XXFD_CAN_OBJ_FLAGS_DLC_BITS - 1, \ + MCP25XXFD_CAN_OBJ_FLAGS_DLC_SHIFT) +#define MCP25XXFD_CAN_OBJ_FLAGS_IDE BIT(4) +#define MCP25XXFD_CAN_OBJ_FLAGS_RTR BIT(5) +#define MCP25XXFD_CAN_OBJ_FLAGS_BRS BIT(6) +#define MCP25XXFD_CAN_OBJ_FLAGS_FDF BIT(7) +#define MCP25XXFD_CAN_OBJ_FLAGS_ESI BIT(8) +#define MCP25XXFD_CAN_OBJ_FLAGS_SEQ_BITS 7 +#define MCP25XXFD_CAN_OBJ_FLAGS_SEQ_SHIFT 9 +#define MCP25XXFD_CAN_OBJ_FLAGS_SEQ_MASK \ + GENMASK(MCP25XXFD_CAN_OBJ_FLAGS_SEQ_SHIFT + \ + MCP25XXFD_CAN_OBJ_FLAGS_SEQ_BITS - 1, \ + MCP25XXFD_CAN_OBJ_FLAGS_SEQ_SHIFT) +#define MCP25XXFD_CAN_OBJ_FLAGS_FILHIT_BITS 11 +#define MCP25XXFD_CAN_OBJ_FLAGS_FILHIT_SHIFT 5 +#define MCP25XXFD_CAN_OBJ_FLAGS_FILHIT_MASK \ + GENMASK(MCP25XXFD_CAN_FLAGS_FILHIT_SHIFT + \ + MCP25XXFD_CAN_FLAGS_FILHIT_BITS - 1, \ + MCP25XXFD_CAN_FLAGS_FILHIT_SHIFT) + +#endif /* __MCP25XXFD_REGS_H */ From patchwork Wed Jun 10 07:44:39 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Manivannan Sadhasivam X-Patchwork-Id: 187730 Delivered-To: patch@linaro.org Received: by 2002:a92:cf06:0:0:0:0:0 with SMTP id c6csp254161ilo; Wed, 10 Jun 2020 00:45:19 -0700 (PDT) X-Google-Smtp-Source: ABdhPJyC9dNr6qJWAbnjByLYz1wjUg24ZmHYd2Pk9q3MZaZa75SLGSjW642Qvb2GB5yMoOylFrBO X-Received: by 2002:a17:906:689:: with SMTP id u9mr2071530ejb.13.1591775119471; Wed, 10 Jun 2020 00:45:19 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1591775119; cv=none; d=google.com; s=arc-20160816; b=lKqXAT1Lkx1dH2xmB02b9jQR1MUVvZVq37NJ3fv1o5z/abcTdL61ToHWiXUsCwCrb6 xuF2rOx66Ac9zykZpRRboVkuBrvp/M+eaWXJNORx/K/viGDUs/ql8zrQ3i70WvJZjvEk 1udxoflWDOIUBR3VgYYo7zalW996BvUxHraNOaNeAiHGIe/bQnQPJydLbn7pvu6zXc9o gTGJ7i6cVsFx49YVdvCoUYAOzX+iex5k+nKhFVc5BaisTMgrju/DM7IfqEjaFkdmRmiK 8rtwNyGVoq1mTB7omRys1QCSi95RC+i6Vs7omYak9RlFZvoIkjliErUfgjwS8ZHa/q6Z 3hDA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:dkim-signature; bh=KPH+/JEy/C0E3Wvv3TmYr7qJX6IchbSuJkgneJXNq0k=; b=W6gFZG2zo51ryHFVzLbM/H39R1ZIHCUlnmr7OorwvNSsqd71oAuTQ40eIOQXljkihy gOvUfZ86KjQacpjdshf23eOvpR7hBqW4peC2V1cpjRP23WSPcRY3EjAUG9dzUuIPE4Ds kCK5jOo3B0TUPgoqZj0/JdaTGndognqbBUuLG9M4x/AAKCza3maJ1GIOVdf000o1Apn6 ppeKa2oxelqfdI3YnmPIVuicRdhvIxAJUFBPDSm77x1LtjgUC/uY+c6cfqfCR4kdXqor T8TueIp3/SDCqmxUrjamR0mhNxtsFmMgsI47xQk/9K2jGRqCeOzUMoAwSsVozzMs5m8R CCzw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=pqjfwjdQ; spf=pass (google.com: domain of netdev-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=netdev-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id n11si11263268ejh.543.2020.06.10.00.45.17; Wed, 10 Jun 2020 00:45:19 -0700 (PDT) Received-SPF: pass (google.com: domain of netdev-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=pqjfwjdQ; spf=pass (google.com: domain of netdev-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=netdev-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726620AbgFJHpM (ORCPT + 9 others); Wed, 10 Jun 2020 03:45:12 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:57318 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726581AbgFJHpK (ORCPT ); Wed, 10 Jun 2020 03:45:10 -0400 Received: from mail-pl1-x642.google.com (mail-pl1-x642.google.com [IPv6:2607:f8b0:4864:20::642]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 16E6CC08C5C1 for ; Wed, 10 Jun 2020 00:45:10 -0700 (PDT) Received: by mail-pl1-x642.google.com with SMTP id d8so631911plo.12 for ; Wed, 10 Jun 2020 00:45:10 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=KPH+/JEy/C0E3Wvv3TmYr7qJX6IchbSuJkgneJXNq0k=; b=pqjfwjdQNIxd+58YwScOXQNiRaD9A/eMW2XxFnm1xVyzZqlytLTENAXH+WBmYt6HZk +4+YIfCZgFpHgWJ3ztJNjnnddfEnTAIQbzOCacHTvL5st18f6FjrupC2dMCS4ukmdAPB zWGom4cZHvF6ffnDTb4dmfertnCubYJSnbd977ABdNK1PKUDcilgYFSix3ut7BtOGk+3 xOTnUl3yQMeTzLngNYlRqfTtpIYwIRIiOPRBY2+R92iLIAt8i3c0FnPpQC0wkwQtT7q3 2A0lhGrvIDG8JCasmfAxLx280RkNFJtocK83ZO9PpJGo9KiOvgBqRlity+KWskHR09Dp YuSA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=KPH+/JEy/C0E3Wvv3TmYr7qJX6IchbSuJkgneJXNq0k=; b=MNwEOboyGPbYYDBUQeQHWaArOXsB0K1RzlUtOA8HwPy6dYKgKaMkNajnob+kdYkqqg MXRWYu6nKcynbWImJf6FiAqClA5IDMqFeu6Xm8hiEoavcIokoEwvHDdisB1yhTAWnAFQ 5MiG+uM2sP6d7Jn9eb1bh8vUUqATFzQojzeKgIngnwyBGoZIQdMDpkiMWH/9zcjJyMt4 //Bh55go1HG+fs/ZQjrPRZALChtxyBygkObT9q/qwvzVWscE/tYn2/4/E4GRXNnORMM9 RJbMrmKwslObxqLw3fmfGKRPm0I7QFhZMcRk90O8UjMlA75czT2qTtiJLtKX2dzoLZm2 UU8A== X-Gm-Message-State: AOAM532tVrwnNM4h/IwPm7gqQo0ltfyKS5QhOvae9zpUdI4MXwzFh8lU 8+YJbNPfc6ELO3G53TMP9Dcx X-Received: by 2002:a17:90a:930a:: with SMTP id p10mr1671918pjo.203.1591775109116; Wed, 10 Jun 2020 00:45:09 -0700 (PDT) Received: from Mani-XPS-13-9360.localdomain ([2409:4072:630f:1dba:c41:a14e:6586:388a]) by smtp.gmail.com with ESMTPSA id a20sm11516795pff.147.2020.06.10.00.45.04 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 10 Jun 2020 00:45:08 -0700 (PDT) From: Manivannan Sadhasivam To: wg@grandegger.com, mkl@pengutronix.de Cc: kernel@martin.sperl.org, linux-can@vger.kernel.org, netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Manivannan Sadhasivam Subject: [PATCH 3/6] can: mcp25xxfd: Add support for CAN reception Date: Wed, 10 Jun 2020 13:14:39 +0530 Message-Id: <20200610074442.10808-4-manivannan.sadhasivam@linaro.org> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200610074442.10808-1-manivannan.sadhasivam@linaro.org> References: <20200610074442.10808-1-manivannan.sadhasivam@linaro.org> Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org From: Martin Sperl Add un-optimized CAN2.0 and CAN-FD reception support. On a Rasperry pi 3 it is already able to process CAN2.0 Frames with DLC=0 on a CAN bus with 1MHz without losing any packets on the SPI side. Packets still get lost inside the network stack. Signed-off-by: Martin Sperl [mani: misc cleanups for upstream] Signed-off-by: Manivannan Sadhasivam --- drivers/net/can/spi/mcp25xxfd/Makefile | 3 + .../net/can/spi/mcp25xxfd/mcp25xxfd_base.c | 6 + drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.c | 395 ++++++++++- drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.h | 35 + .../can/spi/mcp25xxfd/mcp25xxfd_can_fifo.c | 228 ++++++ .../can/spi/mcp25xxfd/mcp25xxfd_can_fifo.h | 16 + .../net/can/spi/mcp25xxfd/mcp25xxfd_can_id.h | 69 ++ .../net/can/spi/mcp25xxfd/mcp25xxfd_can_int.c | 655 ++++++++++++++++++ .../net/can/spi/mcp25xxfd/mcp25xxfd_can_int.h | 18 + .../can/spi/mcp25xxfd/mcp25xxfd_can_priv.h | 131 ++++ .../net/can/spi/mcp25xxfd/mcp25xxfd_can_rx.c | 233 +++++++ .../net/can/spi/mcp25xxfd/mcp25xxfd_can_rx.h | 18 + drivers/net/can/spi/mcp25xxfd/mcp25xxfd_int.c | 14 +- .../net/can/spi/mcp25xxfd/mcp25xxfd_priv.h | 2 + .../net/can/spi/mcp25xxfd/mcp25xxfd_regs.h | 5 + 15 files changed, 1823 insertions(+), 5 deletions(-) create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_fifo.c create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_fifo.h create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_id.h create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_int.c create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_int.h create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_priv.h create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_rx.c create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_rx.h -- 2.17.1 diff --git a/drivers/net/can/spi/mcp25xxfd/Makefile b/drivers/net/can/spi/mcp25xxfd/Makefile index d8fdb76a9578..5787bdd57a9d 100644 --- a/drivers/net/can/spi/mcp25xxfd/Makefile +++ b/drivers/net/can/spi/mcp25xxfd/Makefile @@ -1,6 +1,9 @@ obj-$(CONFIG_CAN_MCP25XXFD) += mcp25xxfd.o mcp25xxfd-objs := mcp25xxfd_base.o mcp25xxfd-objs += mcp25xxfd_can.o +mcp25xxfd-objs += mcp25xxfd_can_fifo.o +mcp25xxfd-objs += mcp25xxfd_can_int.o +mcp25xxfd-objs += mcp25xxfd_can_rx.o mcp25xxfd-objs += mcp25xxfd_cmd.o mcp25xxfd-objs += mcp25xxfd_crc.o mcp25xxfd-objs += mcp25xxfd_ecc.o diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_base.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_base.c index 4be456df0998..c6b67c54a3cd 100644 --- a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_base.c +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_base.c @@ -123,6 +123,11 @@ static int mcp25xxfd_base_probe(struct spi_device *spi) if (ret) goto out_power; + /* setting up CAN */ + ret = mcp25xxfd_can_setup(priv); + if (ret) + goto out_power; + dev_info(&spi->dev, "MCP%04x successfully initialized.\n", model); return 0; @@ -140,6 +145,7 @@ static int mcp25xxfd_base_remove(struct spi_device *spi) { struct mcp25xxfd_priv *priv = spi_get_drvdata(spi); + mcp25xxfd_can_remove(priv); mcp25xxfd_base_power_enable(priv->power, 0); clk_disable_unprepare(priv->clk); diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.c index 41a5ab508582..2ac78024c171 100644 --- a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.c +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.c @@ -5,15 +5,119 @@ * Copyright 2019 Martin Sperl */ +#include +#include +#include #include +#include #include +#include +#include +#include #include +#include "mcp25xxfd_base.h" +#include "mcp25xxfd_can_fifo.h" +#include "mcp25xxfd_can_int.h" +#include "mcp25xxfd_can_priv.h" #include "mcp25xxfd_can.h" #include "mcp25xxfd_cmd.h" +#include "mcp25xxfd_int.h" #include "mcp25xxfd_priv.h" #include "mcp25xxfd_regs.h" +#include + +/* everything related to bit timing */ +static +const struct can_bittiming_const mcp25xxfd_can_nominal_bittiming_const = { + .name = DEVICE_NAME, + .tseg1_min = 2, + .tseg1_max = BIT(MCP25XXFD_CAN_NBTCFG_TSEG1_BITS), + .tseg2_min = 1, + .tseg2_max = BIT(MCP25XXFD_CAN_NBTCFG_TSEG2_BITS), + .sjw_max = BIT(MCP25XXFD_CAN_NBTCFG_SJW_BITS), + .brp_min = 1, + .brp_max = BIT(MCP25XXFD_CAN_NBTCFG_BRP_BITS), + .brp_inc = 1, +}; + +static +const struct can_bittiming_const mcp25xxfd_can_data_bittiming_const = { + .name = DEVICE_NAME, + .tseg1_min = 1, + .tseg1_max = BIT(MCP25XXFD_CAN_DBTCFG_TSEG1_BITS), + .tseg2_min = 1, + .tseg2_max = BIT(MCP25XXFD_CAN_DBTCFG_TSEG2_BITS), + .sjw_max = BIT(MCP25XXFD_CAN_DBTCFG_SJW_BITS), + .brp_min = 1, + .brp_max = BIT(MCP25XXFD_CAN_DBTCFG_BRP_BITS), + .brp_inc = 1, +}; + +static int mcp25xxfd_can_do_set_nominal_bittiming(struct net_device *net) +{ + struct mcp25xxfd_can_priv *cpriv = netdev_priv(net); + struct can_bittiming *bt = &cpriv->can.bittiming; + int sjw = bt->sjw; + int pseg2 = bt->phase_seg2; + int pseg1 = bt->phase_seg1; + int propseg = bt->prop_seg; + int brp = bt->brp; + int tseg1 = propseg + pseg1; + int tseg2 = pseg2; + + /* calculate nominal bit timing */ + cpriv->regs.nbtcfg = ((sjw - 1) << MCP25XXFD_CAN_NBTCFG_SJW_SHIFT) | + ((tseg2 - 1) << MCP25XXFD_CAN_NBTCFG_TSEG2_SHIFT) | + ((tseg1 - 1) << MCP25XXFD_CAN_NBTCFG_TSEG1_SHIFT) | + ((brp - 1) << MCP25XXFD_CAN_NBTCFG_BRP_SHIFT); + + return mcp25xxfd_cmd_write(cpriv->priv->spi, MCP25XXFD_CAN_NBTCFG, + cpriv->regs.nbtcfg); +} + +static int mcp25xxfd_can_do_set_data_bittiming(struct net_device *net) +{ + struct mcp25xxfd_can_priv *cpriv = netdev_priv(net); + struct mcp25xxfd_priv *priv = cpriv->priv; + struct can_bittiming *bt = &cpriv->can.data_bittiming; + struct spi_device *spi = priv->spi; + int sjw = bt->sjw; + int pseg2 = bt->phase_seg2; + int pseg1 = bt->phase_seg1; + int propseg = bt->prop_seg; + int brp = bt->brp; + int tseg1 = propseg + pseg1; + int tseg2 = pseg2; + int tdco; + int ret; + + /* set up Transmitter delay compensation */ + cpriv->regs.tdc = FIELD_PREP(MCP25XXFD_CAN_TDC_TDCMOD_MASK, + MCP25XXFD_CAN_TDC_TDCMOD_AUTO); + + /* configure TDC offsets */ + tdco = clamp_t(int, bt->brp * tseg1, -64, 63); + cpriv->regs.tdc &= ~MCP25XXFD_CAN_TDC_TDCO_MASK; + cpriv->regs.tdc |= FIELD_PREP(MCP25XXFD_CAN_TDC_TDCO_MASK, tdco); + + /* set TDC */ + ret = mcp25xxfd_cmd_write(spi, MCP25XXFD_CAN_TDC, cpriv->regs.tdc); + if (ret) + return ret; + + /* calculate data bit timing */ + cpriv->regs.dbtcfg = + FIELD_PREP(MCP25XXFD_CAN_DBTCFG_SJW_MASK, (sjw - 1)) | + FIELD_PREP(MCP25XXFD_CAN_DBTCFG_TSEG2_MASK, (tseg2 - 1)) | + FIELD_PREP(MCP25XXFD_CAN_DBTCFG_TSEG1_MASK, (tseg1 - 1)) | + FIELD_PREP(MCP25XXFD_CAN_DBTCFG_BRP_MASK, (brp - 1)); + + return mcp25xxfd_cmd_write(spi, MCP25XXFD_CAN_DBTCFG, + cpriv->regs.dbtcfg); +} + int mcp25xxfd_can_get_mode(struct mcp25xxfd_priv *priv, u32 *reg) { int ret; @@ -25,10 +129,10 @@ int mcp25xxfd_can_get_mode(struct mcp25xxfd_priv *priv, u32 *reg) return FIELD_GET(MCP25XXFD_CAN_CON_OPMOD_MASK, *reg); } -static int mcp25xxfd_can_switch_mode(struct mcp25xxfd_priv *priv, - u32 *reg, int mode) +int mcp25xxfd_can_switch_mode_no_wait(struct mcp25xxfd_priv *priv, + u32 *reg, int mode) { - int ret, i; + int ret; ret = mcp25xxfd_can_get_mode(priv, reg); if (ret < 0) @@ -41,7 +145,15 @@ static int mcp25xxfd_can_switch_mode(struct mcp25xxfd_priv *priv, FIELD_PREP(MCP25XXFD_CAN_CON_OPMOD_MASK, mode); /* Request the mode switch */ - ret = mcp25xxfd_cmd_write(priv->spi, MCP25XXFD_CAN_CON, *reg); + return mcp25xxfd_cmd_write(priv->spi, MCP25XXFD_CAN_CON, *reg); +} + +int mcp25xxfd_can_switch_mode(struct mcp25xxfd_priv *priv, u32 *reg, int mode) +{ + int ret, i; + + /* trigger the mode switch itself */ + ret = mcp25xxfd_can_switch_mode_no_wait(priv, reg, mode); if (ret) return ret; @@ -139,3 +251,278 @@ int mcp25xxfd_can_probe(struct mcp25xxfd_priv *priv) /* Finally check if modeswitch is really working */ return mcp25xxfd_can_probe_modeswitch(priv); } + +static int mcp25xxfd_can_config(struct net_device *net) +{ + struct mcp25xxfd_can_priv *cpriv = netdev_priv(net); + struct mcp25xxfd_priv *priv = cpriv->priv; + struct spi_device *spi = priv->spi; + int ret; + + /* setup value of con_register */ + cpriv->regs.con = MCP25XXFD_CAN_CON_STEF; /* enable TEF, disable TXQ */ + + /* non iso FD mode */ + if (!(cpriv->can.ctrlmode & CAN_CTRLMODE_FD_NON_ISO)) + cpriv->regs.con |= MCP25XXFD_CAN_CON_ISOCRCEN; + + /* one shot */ + if (cpriv->can.ctrlmode & CAN_CTRLMODE_ONE_SHOT) + cpriv->regs.con |= MCP25XXFD_CAN_CON_RTXAT; + + /* apply it now together with a mode switch */ + ret = mcp25xxfd_can_switch_mode(cpriv->priv, &cpriv->regs.con, + MCP25XXFD_CAN_CON_MODE_CONFIG); + if (ret) + return 0; + + /* time stamp control register - 1ns resolution */ + cpriv->regs.tscon = 0; + ret = mcp25xxfd_cmd_write(spi, MCP25XXFD_CAN_TBC, 0); + if (ret) + return ret; + + cpriv->regs.tscon = MCP25XXFD_CAN_TSCON_TBCEN | + FIELD_PREP(MCP25XXFD_CAN_TSCON_TBCPRE_MASK, + ((cpriv->can.clock.freq / 1000000))); + ret = mcp25xxfd_cmd_write(spi, MCP25XXFD_CAN_TSCON, cpriv->regs.tscon); + if (ret) + return ret; + + /* setup fifos */ + ret = mcp25xxfd_can_fifo_setup(cpriv); + if (ret) + return ret; + + /* setup can bittiming now - the do_set_bittiming methods + * are not used as they get called before open + */ + ret = mcp25xxfd_can_do_set_nominal_bittiming(net); + if (ret) + return ret; + + ret = mcp25xxfd_can_do_set_data_bittiming(net); + if (ret) + return ret; + + return ret; +} + +/* mode setting */ +static int mcp25xxfd_can_do_set_mode(struct net_device *net, + enum can_mode mode) +{ + switch (mode) { + case CAN_MODE_START: + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +/* binary error counters */ +static int mcp25xxfd_can_get_berr_counter(const struct net_device *net, + struct can_berr_counter *bec) +{ + struct mcp25xxfd_can_priv *cpriv = netdev_priv(net); + + bec->txerr = FIELD_PREP(MCP25XXFD_CAN_TREC_TEC_MASK, + cpriv->status.trec); + bec->rxerr = FIELD_PREP(MCP25XXFD_CAN_TREC_REC_MASK, + cpriv->status.trec); + + return 0; +} + +static int mcp25xxfd_can_open(struct net_device *net) +{ + struct mcp25xxfd_can_priv *cpriv = netdev_priv(net); + struct spi_device *spi = cpriv->priv->spi; + int ret, mode; + + ret = open_candev(net); + if (ret) { + netdev_err(net, "unable to set initial baudrate!\n"); + return ret; + } + + /* request an IRQ but keep disabled for now */ + ret = request_threaded_irq(spi->irq, NULL, + mcp25xxfd_can_int, + IRQF_ONESHOT | IRQF_TRIGGER_LOW, + cpriv->priv->device_name, cpriv); + if (ret) { + dev_err(&spi->dev, "failed to acquire irq %d - %i\n", + spi->irq, ret); + goto out_candev; + } + + disable_irq(spi->irq); + cpriv->irq.allocated = true; + cpriv->irq.enabled = false; + + /* enable power to the transceiver */ + ret = mcp25xxfd_base_power_enable(cpriv->transceiver, 1); + if (ret) + goto out_irq; + + /* configure controller for reception */ + ret = mcp25xxfd_can_config(net); + if (ret) + goto out_power; + + /* setting up state */ + cpriv->can.state = CAN_STATE_ERROR_ACTIVE; + + /* enable interrupts */ + ret = mcp25xxfd_int_enable(cpriv->priv, true); + if (ret) + goto out_canconfig; + + if (cpriv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) + mode = MCP25XXFD_CAN_CON_MODE_EXT_LOOPBACK; + else if (cpriv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) + mode = MCP25XXFD_CAN_CON_MODE_LISTENONLY; + else if (cpriv->can.ctrlmode & CAN_CTRLMODE_FD) + mode = MCP25XXFD_CAN_CON_MODE_MIXED; + else + mode = MCP25XXFD_CAN_CON_MODE_CAN2_0; + + /* switch to active mode */ + ret = mcp25xxfd_can_switch_mode(cpriv->priv, &cpriv->regs.con, mode); + if (ret) + goto out_int; + + return 0; + +out_int: + mcp25xxfd_int_enable(cpriv->priv, false); +out_canconfig: + mcp25xxfd_can_fifo_release(cpriv); +out_power: + mcp25xxfd_base_power_enable(cpriv->transceiver, 0); +out_irq: + free_irq(spi->irq, cpriv); + cpriv->irq.allocated = false; + cpriv->irq.enabled = false; +out_candev: + close_candev(net); + return ret; +} + +static void mcp25xxfd_can_shutdown(struct mcp25xxfd_can_priv *cpriv) +{ + /* switch us to CONFIG mode - this disables the controller */ + mcp25xxfd_can_switch_mode(cpriv->priv, &cpriv->regs.con, + MCP25XXFD_CAN_CON_MODE_CONFIG); +} + +static int mcp25xxfd_can_stop(struct net_device *net) +{ + struct mcp25xxfd_can_priv *cpriv = netdev_priv(net); + struct mcp25xxfd_priv *priv = cpriv->priv; + struct spi_device *spi = priv->spi; + + /* shutdown the can controller */ + mcp25xxfd_can_shutdown(cpriv); + + /* disable inerrupts on controller */ + mcp25xxfd_int_enable(cpriv->priv, false); + + /* disable the transceiver */ + mcp25xxfd_base_power_enable(cpriv->transceiver, 0); + + /* disable interrupt on host */ + free_irq(spi->irq, cpriv); + cpriv->irq.allocated = false; + cpriv->irq.enabled = false; + + /* close the can_decice */ + close_candev(net); + + return 0; +} + +static const struct net_device_ops mcp25xxfd_netdev_ops = { + .ndo_open = mcp25xxfd_can_open, + .ndo_stop = mcp25xxfd_can_stop, + .ndo_change_mtu = can_change_mtu, +}; + +/* probe and remove */ +int mcp25xxfd_can_setup(struct mcp25xxfd_priv *priv) +{ + struct spi_device *spi = priv->spi; + struct mcp25xxfd_can_priv *cpriv; + struct net_device *net; + struct regulator *transceiver; + int ret; + + /* get transceiver power regulator*/ + transceiver = devm_regulator_get(&spi->dev, "xceiver"); + if (PTR_ERR(transceiver) == -EPROBE_DEFER) + return PTR_ERR(transceiver); + + /* allocate can device */ + net = alloc_candev(sizeof(*cpriv), TX_ECHO_SKB_MAX); + if (!net) + return -ENOMEM; + + cpriv = netdev_priv(net); + cpriv->priv = priv; + priv->cpriv = cpriv; + + /* setup network */ + SET_NETDEV_DEV(net, &spi->dev); + net->netdev_ops = &mcp25xxfd_netdev_ops; + net->flags |= IFF_ECHO; + + cpriv->transceiver = transceiver; + + cpriv->can.clock.freq = priv->clock_freq; + cpriv->can.bittiming_const = + &mcp25xxfd_can_nominal_bittiming_const; + cpriv->can.data_bittiming_const = + &mcp25xxfd_can_data_bittiming_const; + + /* we are not setting bit-timing methods here as they get called by + * the framework before open. So the controller would be still in sleep + * mode, which does not help as things are configured in open instead. + */ + cpriv->can.do_set_mode = + mcp25xxfd_can_do_set_mode; + cpriv->can.do_get_berr_counter = + mcp25xxfd_can_get_berr_counter; + cpriv->can.ctrlmode_supported = + CAN_CTRLMODE_FD | + CAN_CTRLMODE_FD_NON_ISO | + CAN_CTRLMODE_LOOPBACK | + CAN_CTRLMODE_LISTENONLY | + CAN_CTRLMODE_BERR_REPORTING | + CAN_CTRLMODE_ONE_SHOT; + + ret = register_candev(net); + if (ret) { + dev_err(&spi->dev, "Failed to register can device\n"); + goto out; + } + + return 0; + +out: + free_candev(net); + priv->cpriv = NULL; + + return ret; +} + +void mcp25xxfd_can_remove(struct mcp25xxfd_priv *priv) +{ + if (priv->cpriv) { + unregister_candev(priv->cpriv->can.dev); + free_candev(priv->cpriv->can.dev); + priv->cpriv = NULL; + } +} diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.h b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.h index f54c716735fb..4b18b5bb3d45 100644 --- a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.h +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.h @@ -8,9 +8,44 @@ #ifndef __MCP25XXFD_CAN_H #define __MCP25XXFD_CAN_H +#include "mcp25xxfd_can_priv.h" #include "mcp25xxfd_priv.h" +#include "mcp25xxfd_regs.h" + +/* get the optimal controller target mode */ +static inline +int mcp25xxfd_can_targetmode(struct mcp25xxfd_can_priv *cpriv) +{ + return (cpriv->can.dev->mtu == CAN_MTU) ? + MCP25XXFD_CAN_CON_MODE_CAN2_0 : MCP25XXFD_CAN_CON_MODE_MIXED; +} + +static inline +void mcp25xxfd_can_queue_frame(struct mcp25xxfd_can_priv *cpriv, + s32 fifo, u16 ts) +{ + int idx = cpriv->fifos.submit_queue_count; + + cpriv->fifos.submit_queue[idx].fifo = fifo; + cpriv->fifos.submit_queue[idx].ts = ts; + + cpriv->fifos.submit_queue_count++; +} + +/* get the current controller mode */ +int mcp25xxfd_can_get_mode(struct mcp25xxfd_priv *priv, u32 *reg); + +/* switch controller mode */ +int mcp25xxfd_can_switch_mode_no_wait(struct mcp25xxfd_priv *priv, + u32 *reg, int mode); +int mcp25xxfd_can_switch_mode(struct mcp25xxfd_priv *priv, + u32 *reg, int mode); /* probe the can controller */ int mcp25xxfd_can_probe(struct mcp25xxfd_priv *priv); +/* setup and the can controller net interface */ +int mcp25xxfd_can_setup(struct mcp25xxfd_priv *priv); +void mcp25xxfd_can_remove(struct mcp25xxfd_priv *priv); + #endif /* __MCP25XXFD_CAN_H */ diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_fifo.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_fifo.c new file mode 100644 index 000000000000..4bd776772d2d --- /dev/null +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_fifo.c @@ -0,0 +1,228 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface + * + * Copyright 2019 Martin Sperl + */ + +/* here we define and configure the fifo layout */ + +#include +#include +#include + +#include "mcp25xxfd_can.h" +#include "mcp25xxfd_can_fifo.h" +#include "mcp25xxfd_can_priv.h" +#include "mcp25xxfd_cmd.h" + +static int mcp25xxfd_can_fifo_get_address(struct mcp25xxfd_can_priv *cpriv) +{ + int fifo, ret; + + /* we need to move out of config mode to force address computation */ + ret = mcp25xxfd_can_switch_mode(cpriv->priv, &cpriv->regs.con, + MCP25XXFD_CAN_CON_MODE_INT_LOOPBACK); + if (ret) + return ret; + + /* and get back into config mode */ + ret = mcp25xxfd_can_switch_mode(cpriv->priv, &cpriv->regs.con, + MCP25XXFD_CAN_CON_MODE_CONFIG); + if (ret) + return ret; + + /* read address and config back in */ + for (fifo = 1; fifo < 32; fifo++) { + ret = mcp25xxfd_cmd_read(cpriv->priv->spi, + MCP25XXFD_CAN_FIFOUA(fifo), + &cpriv->fifos.info[fifo].offset); + if (ret) + return ret; + } + + return 0; +} + +static int mcp25xxfd_can_fifo_setup_config(struct mcp25xxfd_can_priv *cpriv, + struct mcp25xxfd_fifo *desc, + u32 flags, u32 flags_last) +{ + u32 val; + int i, p, f, c, ret; + + for (i = 0, f = desc->start, c = desc->count, p = 31; + c > 0; i++, f++, p--, c--) { + val = (c > 1) ? flags : flags_last; + + /* write the config to the controller in one go */ + ret = mcp25xxfd_cmd_write(cpriv->priv->spi, + MCP25XXFD_CAN_FIFOCON(f), val); + if (ret) + return ret; + } + + return 0; +} + +static int mcp25xxfd_can_fifo_setup_rx(struct mcp25xxfd_can_priv *cpriv) +{ + u32 rx_flags = MCP25XXFD_CAN_FIFOCON_FRESET | /* reset FIFO */ + MCP25XXFD_CAN_FIFOCON_RXTSEN | /* RX timestamps */ + MCP25XXFD_CAN_FIFOCON_TFERFFIE | /* FIFO Full */ + MCP25XXFD_CAN_FIFOCON_TFHRFHIE | /* FIFO Half Full*/ + MCP25XXFD_CAN_FIFOCON_TFNRFNIE | /* FIFO not empty */ + (cpriv->fifos.payload_mode << + MCP25XXFD_CAN_FIFOCON_PLSIZE_SHIFT) | + (0 << MCP25XXFD_CAN_FIFOCON_FSIZE_SHIFT); /* 1 FIFO deep */ + /* enable overflow int on last fifo */ + u32 rx_flags_last = rx_flags | MCP25XXFD_CAN_FIFOCON_RXOVIE; + + return mcp25xxfd_can_fifo_setup_config(cpriv, &cpriv->fifos.rx, + rx_flags, rx_flags_last); +} + +static int mcp25xxfd_can_fifo_setup_rxfilter(struct mcp25xxfd_can_priv *cpriv) +{ + u8 filter_con[32]; + int c, f; + + /* clear the filters and filter mappings for all filters */ + memset(filter_con, 0, sizeof(filter_con)); + + /* and now set up the rx filters */ + for (c = 0, f = cpriv->fifos.rx.start; c < cpriv->fifos.rx.count; + c++, f++) { + /* set up filter config - we can use the mask of filter 0 */ + filter_con[c] = MCP25XXFD_CAN_FIFOCON_FLTEN(0) | + (f << MCP25XXFD_CAN_FILCON_SHIFT(0)); + } + + /* and set up filter control */ + return mcp25xxfd_cmd_write_regs(cpriv->priv->spi, + MCP25XXFD_CAN_FLTCON(0), + (u32 *)filter_con, sizeof(filter_con)); +} + +static int mcp25xxfd_can_fifo_compute(struct mcp25xxfd_can_priv *cpriv) +{ + int rx_memory_available; + + switch (cpriv->can.dev->mtu) { + case CAN_MTU: + /* MTU is 8 */ + cpriv->fifos.payload_size = 8; + cpriv->fifos.payload_mode = MCP25XXFD_CAN_TXQCON_PLSIZE_8; + + break; + case CANFD_MTU: + /* MTU is 64 */ + cpriv->fifos.payload_size = 64; + cpriv->fifos.payload_mode = MCP25XXFD_CAN_TXQCON_PLSIZE_64; + + break; + default: + return -EINVAL; + } + + /* compute effective sizes */ + cpriv->fifos.rx.size = sizeof(struct mcp25xxfd_can_obj_rx) + + cpriv->fifos.payload_size; + + /* calculate evailable memory for RX_fifos */ + rx_memory_available = MCP25XXFD_SRAM_SIZE; + + /* calculate possible amount of RX fifos */ + cpriv->fifos.rx.count = rx_memory_available / cpriv->fifos.rx.size; + + /* now calculate effective number of rx-fifos. There are only 31 fifos + * available in total, so we need to limit ourselves + */ + if (cpriv->fifos.rx.count > 31) + cpriv->fifos.rx.count = 31; + + cpriv->fifos.rx.start = 1; + + return 0; +} + +static int mcp25xxfd_can_fifo_clear_regs(struct mcp25xxfd_can_priv *cpriv, + u32 start, u32 end) +{ + size_t len = end - start; + u8 *data; + int ret; + + data = kzalloc(len, GFP_KERNEL); + if (!data) + return -ENOMEM; + + ret = mcp25xxfd_cmd_write_regs(cpriv->priv->spi, + start, (u32 *)data, len); + + kfree(data); + + return ret; +} + +static int mcp25xxfd_can_fifo_clear(struct mcp25xxfd_can_priv *cpriv) +{ + int ret; + + memset(&cpriv->fifos.info, 0, sizeof(cpriv->fifos.info)); + memset(&cpriv->fifos.rx, 0, sizeof(cpriv->fifos.rx)); + + /* clear FIFO config */ + ret = mcp25xxfd_can_fifo_clear_regs(cpriv, MCP25XXFD_CAN_FIFOCON(1), + MCP25XXFD_CAN_FIFOCON(32)); + if (ret) + return ret; + + /* clear the filter mask - match any frame with every filter */ + return mcp25xxfd_can_fifo_clear_regs(cpriv, MCP25XXFD_CAN_FLTCON(0), + MCP25XXFD_CAN_FLTCON(32)); +} + +int mcp25xxfd_can_fifo_setup(struct mcp25xxfd_can_priv *cpriv) +{ + int ret; + + /* clear fifo config */ + ret = mcp25xxfd_can_fifo_clear(cpriv); + if (ret) + return ret; + + ret = mcp25xxfd_can_fifo_compute(cpriv); + if (ret) + return ret; + + cpriv->regs.tefcon = 0; + ret = mcp25xxfd_cmd_write(cpriv->priv->spi, MCP25XXFD_CAN_TEFCON, + cpriv->regs.tefcon); + if (ret) + return ret; + + ret = mcp25xxfd_cmd_write(cpriv->priv->spi, MCP25XXFD_CAN_TXQCON, 0); + if (ret) + return ret; + + ret = mcp25xxfd_can_fifo_setup_rx(cpriv); + if (ret) + return ret; + + ret = mcp25xxfd_can_fifo_setup_rxfilter(cpriv); + if (ret) + return ret; + + /* get fifo addresses */ + ret = mcp25xxfd_can_fifo_get_address(cpriv); + if (ret) + return ret; + + return 0; +} + +void mcp25xxfd_can_fifo_release(struct mcp25xxfd_can_priv *cpriv) +{ + mcp25xxfd_can_fifo_clear(cpriv); +} diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_fifo.h b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_fifo.h new file mode 100644 index 000000000000..ed2daa05220a --- /dev/null +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_fifo.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface + * + * Copyright 2019 Martin Sperl + */ + +#ifndef __MCP25XXFD_CAN_FIFO_H +#define __MCP25XXFD_CAN_FIFO_H + +#include "mcp25xxfd_can_priv.h" + +int mcp25xxfd_can_fifo_setup(struct mcp25xxfd_can_priv *cpriv); +void mcp25xxfd_can_fifo_release(struct mcp25xxfd_can_priv *cpriv); + +#endif /* __MCP25XXFD_CAN_FIFO_H */ diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_id.h b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_id.h new file mode 100644 index 000000000000..00a6c6639bd5 --- /dev/null +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_id.h @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface + * + * Copyright 2019 Martin Sperl + */ + +#ifndef __MCP25XXFD_CAN_IF_H +#define __MCP25XXFD_CAN_IF_H + +#include + +#include "mcp25xxfd_can_id.h" +#include "mcp25xxfd_regs.h" + +/* ideally these would be defined in uapi/linux/can.h */ +#define MCP25XXFD_CAN_EFF_SID_SHIFT (CAN_EFF_ID_BITS - CAN_SFF_ID_BITS) +#define MCP25XXFD_CAN_EFF_SID_BITS CAN_SFF_ID_BITS +#define MCP25XXFD_CAN_EFF_SID_MASK \ + GENMASK(MCP25XXFD_CAN_EFF_SID_SHIFT + MCP25XXFD_CAN_EFF_SID_BITS - 1, \ + MCP25XXFD_CAN_EFF_SID_SHIFT) +#define MCP25XXFD_CAN_EFF_EID_SHIFT 0 +#define MCP25XXFD_CAN_EFF_EID_BITS MCP25XXFD_CAN_EFF_SID_SHIFT +#define MCP25XXFD_CAN_EFF_EID_MASK \ + GENMASK(MCP25XXFD_CAN_EFF_EID_SHIFT + MCP25XXFD_CAN_EFF_EID_BITS - 1, \ + MCP25XXFD_CAN_EFF_EID_SHIFT) + +static inline +void mcp25xxfd_can_id_from_mcp25xxfd(u32 mcp_id, u32 mcp_flags, u32 *can_id) +{ + u32 sid = (mcp_id & MCP25XXFD_CAN_OBJ_ID_SID_MASK) >> + MCP25XXFD_CAN_OBJ_ID_SID_SHIFT; + u32 eid = (mcp_id & MCP25XXFD_CAN_OBJ_ID_EID_MASK) >> + MCP25XXFD_CAN_OBJ_ID_EID_SHIFT; + + /* select normal or extended ids */ + if (mcp_flags & MCP25XXFD_CAN_OBJ_FLAGS_IDE) { + *can_id = (eid << MCP25XXFD_CAN_EFF_EID_SHIFT) | + (sid << MCP25XXFD_CAN_EFF_SID_SHIFT) | + CAN_EFF_FLAG; + } else { + *can_id = sid << MCP25XXFD_CAN_EFF_EID_SHIFT; + } + /* handle rtr */ + *can_id |= (mcp_flags & MCP25XXFD_CAN_OBJ_FLAGS_RTR) ? CAN_RTR_FLAG : 0; +} + +static inline +void mcp25xxfd_can_id_to_mcp25xxfd(u32 can_id, u32 *id, u32 *flags) +{ + /* depending on can_id flag compute extended or standard ids */ + if (can_id & CAN_EFF_FLAG) { + int sid = (can_id & MCP25XXFD_CAN_EFF_SID_MASK) >> + MCP25XXFD_CAN_EFF_SID_SHIFT; + int eid = (can_id & MCP25XXFD_CAN_EFF_EID_MASK) >> + MCP25XXFD_CAN_EFF_EID_SHIFT; + *id = (eid << MCP25XXFD_CAN_OBJ_ID_EID_SHIFT) | + (sid << MCP25XXFD_CAN_OBJ_ID_SID_SHIFT); + *flags = MCP25XXFD_CAN_OBJ_FLAGS_IDE; + } else { + *id = can_id & CAN_SFF_MASK; + *flags = 0; + } + + /* Handle RTR */ + *flags |= (can_id & CAN_RTR_FLAG) ? MCP25XXFD_CAN_OBJ_FLAGS_RTR : 0; +} + +#endif /* __MCP25XXFD_CAN_IF_H */ diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_int.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_int.c new file mode 100644 index 000000000000..83656b2604df --- /dev/null +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_int.c @@ -0,0 +1,655 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface + * + * Copyright 2019 Martin Sperl + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mcp25xxfd_regs.h" +#include "mcp25xxfd_can.h" +#include "mcp25xxfd_can_int.h" +#include "mcp25xxfd_can_priv.h" +#include "mcp25xxfd_can_rx.h" +#include "mcp25xxfd_cmd.h" +#include "mcp25xxfd_ecc.h" + +#define MCP25XXFD_RESCHEDULE_TIMES 4 + +static void mcp25xxfd_can_int_send_error_skb(struct mcp25xxfd_can_priv *cpriv) +{ + struct net_device *net = cpriv->can.dev; + struct sk_buff *skb; + struct can_frame *frame; + + /* allocate error frame */ + skb = alloc_can_err_skb(net, &frame); + if (!skb) { + netdev_err(net, "cannot allocate error skb\n"); + return; + } + + /* setup can error frame data */ + frame->can_id |= cpriv->error_frame.id; + memcpy(frame->data, cpriv->error_frame.data, sizeof(frame->data)); + + /* and submit it */ + netif_receive_skb(skb); +} + +static int mcp25xxfd_can_int_compare_obj_ts(const void *a, const void *b) +{ + s32 ats = ((struct mcp25xxfd_obj_ts *)a)->ts; + s32 bts = ((struct mcp25xxfd_obj_ts *)b)->ts; + + if (ats < bts) + return -1; + if (ats > bts) + return 1; + + return 0; +} + +static int mcp25xxfd_can_int_submit_frames(struct mcp25xxfd_can_priv *cpriv) +{ + struct mcp25xxfd_obj_ts *queue = cpriv->fifos.submit_queue; + int count = cpriv->fifos.submit_queue_count; + int i, fifo; + int ret; + + /* skip processing if the queue count is 0 */ + if (count == 0) + goto out; + + /* sort the fifos (rx and tx - actually TEF) by receive timestamp */ + sort(queue, count, sizeof(*queue), + mcp25xxfd_can_int_compare_obj_ts, NULL); + + /* now submit the fifos */ + for (i = 0; i < count; i++) { + fifo = queue[i].fifo; + ret = mcp25xxfd_can_rx_submit_frame(cpriv, fifo); + if (ret) + return ret; + } + + /* if we have received or transmitted something and the IVMIE is + * disabled, then enable it. This is mostly to avoid unnecessary + * interrupts when CAN bus is disconnected. + */ + if (!(cpriv->status.intf | MCP25XXFD_CAN_INT_IVMIE)) { + cpriv->status.intf |= MCP25XXFD_CAN_INT_IVMIE; + ret = mcp25xxfd_cmd_write_mask(cpriv->priv->spi, + MCP25XXFD_CAN_INT, + cpriv->status.intf, + MCP25XXFD_CAN_INT_IVMIE); + if (ret) + return ret; + } + +out: + return 0; +} + +static int mcp25xxfd_can_int_clear_int_flags(struct mcp25xxfd_can_priv *cpriv) +{ + u32 clearable_irq_active = cpriv->status.intf & + MCP25XXFD_CAN_INT_IF_CLEAR_MASK; + u32 clear_irq = cpriv->status.intf & (~MCP25XXFD_CAN_INT_IF_CLEAR_MASK); + + if (!clearable_irq_active) + return 0; + + return mcp25xxfd_cmd_write_mask(cpriv->priv->spi, MCP25XXFD_CAN_INT, + clear_irq, clearable_irq_active); +} + +static +int mcp25xxfd_can_int_handle_serrif_txmab(struct mcp25xxfd_can_priv *cpriv) +{ + int mode = mcp25xxfd_can_targetmode(cpriv); + + cpriv->can.dev->stats.tx_fifo_errors++; + cpriv->can.dev->stats.tx_errors++; + + /* data7 contains custom mcp25xxfd error flags */ + cpriv->error_frame.data[7] |= MCP25XXFD_CAN_ERR_DATA7_MCP25XXFD_SERR_TX; + + /* and switch back into the correct mode */ + return mcp25xxfd_can_switch_mode_no_wait(cpriv->priv, + &cpriv->regs.con, mode); +} + +static +int mcp25xxfd_can_int_handle_serrif_rxmab(struct mcp25xxfd_can_priv *cpriv) +{ + cpriv->can.dev->stats.rx_dropped++; + cpriv->can.dev->stats.rx_errors++; + + /* data7 contains custom mcp25xxfd error flags */ + cpriv->error_frame.data[7] |= MCP25XXFD_CAN_ERR_DATA7_MCP25XXFD_SERR_RX; + + return 0; +} + +static int mcp25xxfd_can_int_handle_serrif(struct mcp25xxfd_can_priv *cpriv) +{ + if (!(cpriv->status.intf & MCP25XXFD_CAN_INT_SERRIF)) + return 0; + + /* Errors here are: + * * Bus Bandwidth Error: when a RX Message Assembly Buffer + * is still full when the next message has already arrived + * the recived message shall be ignored + * * TX MAB Underflow: when a TX Message is invalid + * due to ECC errors or TXMAB underflow + * in this situatioon the system will transition to + * Restricted or Listen Only mode + */ + + cpriv->error_frame.id |= CAN_ERR_CRTL; + cpriv->error_frame.data[1] |= CAN_ERR_CRTL_UNSPEC; + + /* mode change + invalid message would indicate TX MAB Underflow */ + if ((cpriv->status.intf & MCP25XXFD_CAN_INT_MODIF) && + (cpriv->status.intf & MCP25XXFD_CAN_INT_IVMIF)) { + return mcp25xxfd_can_int_handle_serrif_txmab(cpriv); + } + + /* for RX there is only the RXIF an indicator - surprizingly RX-MAB + * does not change mode or anything + */ + if (cpriv->status.intf & MCP25XXFD_CAN_INT_RXIF) + return mcp25xxfd_can_int_handle_serrif_rxmab(cpriv); + + dev_warn_ratelimited(&cpriv->priv->spi->dev, + "unidentified system interrupt - intf = %08x\n", + cpriv->status.intf); + + return 0; +} + +static int mcp25xxfd_can_int_handle_modif(struct mcp25xxfd_can_priv *cpriv) +{ + struct spi_device *spi = cpriv->priv->spi; + int mode; + int ret; + + /* Note that this irq does not get triggered in all situations + * for example SERRIF will move to RESTICTED or LISTENONLY but MODIF + * will not be raised! + */ + if (!(cpriv->status.intf & MCP25XXFD_CAN_INT_MODIF)) + return 0; + + /* get the current mode */ + ret = mcp25xxfd_can_get_mode(cpriv->priv, &mode); + if (ret) + return ret; + + mode = ret; + + /* switches to the same mode as before are ignored + * - this typically happens if the driver is shortly + * switching to a different mode and then returning to the + * original mode + */ + if (mode == cpriv->mode) + return 0; + + /* if we are restricted, then return to "normal" mode */ + if (mode == MCP25XXFD_CAN_CON_MODE_RESTRICTED) { + cpriv->mode = mode; + mode = mcp25xxfd_can_targetmode(cpriv); + return mcp25xxfd_can_switch_mode_no_wait(cpriv->priv, + &cpriv->regs.con, + mode); + } + + /* the controller itself will transition to sleep, so we ignore it */ + if (mode == MCP25XXFD_CAN_CON_MODE_SLEEP) { + cpriv->mode = mode; + return 0; + } + + dev_warn(&spi->dev, + "Controller unexpectedly switched from mode %u to %u\n", + cpriv->mode, mode); + + /* assign the mode as current */ + cpriv->mode = mode; + + return 0; +} + +static int mcp25xxfd_can_int_handle_eccif(struct mcp25xxfd_can_priv *cpriv) +{ + if (!(cpriv->status.intf & MCP25XXFD_CAN_INT_ECCIF)) + return 0; + + /* and prepare ERROR FRAME */ + cpriv->error_frame.id |= CAN_ERR_CRTL; + cpriv->error_frame.data[1] |= CAN_ERR_CRTL_UNSPEC; + /* data7 contains custom mcp25xxfd error flags */ + cpriv->error_frame.data[7] |= MCP25XXFD_CAN_ERR_DATA7_MCP25XXFD_ECC; + + /* delegate to interrupt cleaning */ + return mcp25xxfd_ecc_clear_int(cpriv->priv); +} + +static void mcp25xxfd_can_int_handle_ivmif_tx(struct mcp25xxfd_can_priv *cpriv, + u32 *mask) +{ + /* check if it is really a known tx error */ + if ((cpriv->bus.bdiag[1] & + (MCP25XXFD_CAN_BDIAG1_DBIT1ERR | + MCP25XXFD_CAN_BDIAG1_DBIT0ERR | + MCP25XXFD_CAN_BDIAG1_NACKERR | + MCP25XXFD_CAN_BDIAG1_NBIT1ERR | + MCP25XXFD_CAN_BDIAG1_NBIT0ERR + )) == 0) + return; + + /* mark it as a protocol error */ + cpriv->error_frame.id |= CAN_ERR_PROT; + + /* and update statistics */ + cpriv->can.dev->stats.tx_errors++; + + /* and handle all the known cases */ + if (cpriv->bus.bdiag[1] & MCP25XXFD_CAN_BDIAG1_NACKERR) { + /* TX-Frame not acknowledged - connected to CAN-bus? */ + *mask |= MCP25XXFD_CAN_BDIAG1_NACKERR; + cpriv->error_frame.data[2] |= CAN_ERR_PROT_TX; + cpriv->can.dev->stats.tx_aborted_errors++; + } + + if (cpriv->bus.bdiag[1] & MCP25XXFD_CAN_BDIAG1_NBIT1ERR) { + /* TX-Frame CAN-BUS Level is unexpectedly dominant */ + *mask |= MCP25XXFD_CAN_BDIAG1_NBIT1ERR; + cpriv->can.dev->stats.tx_carrier_errors++; + cpriv->error_frame.data[2] |= CAN_ERR_PROT_BIT1; + } + + if (cpriv->bus.bdiag[1] & MCP25XXFD_CAN_BDIAG1_NBIT0ERR) { + /* TX-Frame CAN-BUS Level is unexpectedly recessive */ + *mask |= MCP25XXFD_CAN_BDIAG1_NBIT0ERR; + cpriv->can.dev->stats.tx_carrier_errors++; + cpriv->error_frame.data[2] |= CAN_ERR_PROT_BIT0; + } + + if (cpriv->bus.bdiag[1] & MCP25XXFD_CAN_BDIAG1_DBIT1ERR) { + /* TX-Frame CAN-BUS Level is unexpectedly dominant + * during data phase + */ + *mask |= MCP25XXFD_CAN_BDIAG1_DBIT1ERR; + cpriv->can.dev->stats.tx_carrier_errors++; + cpriv->error_frame.data[2] |= CAN_ERR_PROT_BIT1; + } + + if (cpriv->bus.bdiag[1] & MCP25XXFD_CAN_BDIAG1_DBIT0ERR) { + /* TX-Frame CAN-BUS Level is unexpectedly recessive + * during data phase + */ + *mask |= MCP25XXFD_CAN_BDIAG1_DBIT0ERR; + cpriv->can.dev->stats.tx_carrier_errors++; + cpriv->error_frame.data[2] |= CAN_ERR_PROT_BIT0; + } +} + +static void mcp25xxfd_can_int_handle_ivmif_rx(struct mcp25xxfd_can_priv *cpriv, + u32 *mask) +{ + /* check if it is really a known tx error */ + if ((cpriv->bus.bdiag[1] & + (MCP25XXFD_CAN_BDIAG1_DCRCERR | + MCP25XXFD_CAN_BDIAG1_DSTUFERR | + MCP25XXFD_CAN_BDIAG1_DFORMERR | + MCP25XXFD_CAN_BDIAG1_NCRCERR | + MCP25XXFD_CAN_BDIAG1_NSTUFERR | + MCP25XXFD_CAN_BDIAG1_NFORMERR + )) == 0) + return; + + /* mark it as a protocol error */ + cpriv->error_frame.id |= CAN_ERR_PROT; + + /* and update statistics */ + cpriv->can.dev->stats.rx_errors++; + + /* handle the cases */ + if (cpriv->bus.bdiag[1] & MCP25XXFD_CAN_BDIAG1_DCRCERR) { + /* RX-Frame with bad CRC during data phase */ + *mask |= MCP25XXFD_CAN_BDIAG1_DCRCERR; + cpriv->can.dev->stats.rx_crc_errors++; + cpriv->error_frame.data[3] |= CAN_ERR_PROT_LOC_CRC_SEQ; + } + + if (cpriv->bus.bdiag[1] & MCP25XXFD_CAN_BDIAG1_DSTUFERR) { + /* RX-Frame with bad stuffing during data phase */ + *mask |= MCP25XXFD_CAN_BDIAG1_DSTUFERR; + cpriv->can.dev->stats.rx_frame_errors++; + cpriv->error_frame.data[2] |= CAN_ERR_PROT_STUFF; + } + + if (cpriv->bus.bdiag[1] & MCP25XXFD_CAN_BDIAG1_DFORMERR) { + /* RX-Frame with bad format during data phase */ + *mask |= MCP25XXFD_CAN_BDIAG1_DFORMERR; + cpriv->can.dev->stats.rx_frame_errors++; + cpriv->error_frame.data[2] |= CAN_ERR_PROT_FORM; + } + + if (cpriv->bus.bdiag[1] & MCP25XXFD_CAN_BDIAG1_NCRCERR) { + /* RX-Frame with bad CRC during data phase */ + *mask |= MCP25XXFD_CAN_BDIAG1_NCRCERR; + cpriv->can.dev->stats.rx_crc_errors++; + cpriv->error_frame.data[3] |= CAN_ERR_PROT_LOC_CRC_SEQ; + } + + if (cpriv->bus.bdiag[1] & MCP25XXFD_CAN_BDIAG1_NSTUFERR) { + /* RX-Frame with bad stuffing during data phase */ + *mask |= MCP25XXFD_CAN_BDIAG1_NSTUFERR; + cpriv->can.dev->stats.rx_frame_errors++; + cpriv->error_frame.data[2] |= CAN_ERR_PROT_STUFF; + } + + if (cpriv->bus.bdiag[1] & MCP25XXFD_CAN_BDIAG1_NFORMERR) { + /* RX-Frame with bad format during data phase */ + *mask |= MCP25XXFD_CAN_BDIAG1_NFORMERR; + cpriv->can.dev->stats.rx_frame_errors++; + cpriv->error_frame.data[2] |= CAN_ERR_PROT_FORM; + } +} + +static int mcp25xxfd_can_int_handle_ivmif(struct mcp25xxfd_can_priv *cpriv) +{ + struct spi_device *spi = cpriv->priv->spi; + u32 mask, bdiag1; + int ret; + + if (!(cpriv->status.intf & MCP25XXFD_CAN_INT_IVMIF)) + return 0; + + if (cpriv->status.intf & MCP25XXFD_CAN_INT_SERRIF) + return 0; + + ret = mcp25xxfd_cmd_read_regs(spi, MCP25XXFD_CAN_BDIAG0, + cpriv->bus.bdiag, + sizeof(cpriv->bus.bdiag)); + if (ret) + return ret; + + mask = 0; + + /* check rx and tx errors */ + mcp25xxfd_can_int_handle_ivmif_tx(cpriv, &mask); + mcp25xxfd_can_int_handle_ivmif_rx(cpriv, &mask); + + /* clear flags if we have bits masked */ + if (!mask) { + dev_warn_once(&spi->dev, + "found IVMIF situation not supported by driver - bdiag = [0x%08x, 0x%08x]", + cpriv->bus.bdiag[0], cpriv->bus.bdiag[1]); + return -EINVAL; + } + + bdiag1 = cpriv->bus.bdiag[1] & (~mask); + ret = mcp25xxfd_cmd_write_mask(spi, MCP25XXFD_CAN_BDIAG1, bdiag1, mask); + if (ret) + return ret; + + /* clear the interrupt flag until we have received or transmited */ + cpriv->status.intf &= ~(MCP25XXFD_CAN_INT_IVMIE); + return mcp25xxfd_cmd_write_mask(spi, MCP25XXFD_CAN_INT, + cpriv->status.intf, + MCP25XXFD_CAN_INT_IVMIE); +} + +static int mcp25xxfd_can_int_handle_cerrif(struct mcp25xxfd_can_priv *cpriv) +{ + if (!(cpriv->status.intf & MCP25XXFD_CAN_INT_CERRIF)) + return 0; + + /* this interrupt exists primarilly to counter possible bus off + * situations. More detailed information can be found and controlled in + * the TREC register + */ + + netdev_warn(cpriv->can.dev, "CAN Bus error experienced"); + + return 0; +} + +static int mcp25xxfd_can_int_error_counters(struct mcp25xxfd_can_priv *cpriv) +{ + if (cpriv->status.trec & MCP25XXFD_CAN_TREC_TXWARN) { + cpriv->bus.new_state = CAN_STATE_ERROR_WARNING; + cpriv->error_frame.id |= CAN_ERR_CRTL; + cpriv->error_frame.data[1] |= CAN_ERR_CRTL_TX_WARNING; + } + + if (cpriv->status.trec & MCP25XXFD_CAN_TREC_RXWARN) { + cpriv->bus.new_state = CAN_STATE_ERROR_WARNING; + cpriv->error_frame.id |= CAN_ERR_CRTL; + cpriv->error_frame.data[1] |= CAN_ERR_CRTL_RX_WARNING; + } + + if (cpriv->status.trec & MCP25XXFD_CAN_TREC_TXBP) { + cpriv->bus.new_state = CAN_STATE_ERROR_PASSIVE; + cpriv->error_frame.id |= CAN_ERR_CRTL; + cpriv->error_frame.data[1] |= CAN_ERR_CRTL_TX_PASSIVE; + } + + if (cpriv->status.trec & MCP25XXFD_CAN_TREC_RXBP) { + cpriv->bus.new_state = CAN_STATE_ERROR_PASSIVE; + cpriv->error_frame.id |= CAN_ERR_CRTL; + cpriv->error_frame.data[1] |= CAN_ERR_CRTL_RX_PASSIVE; + } + + if (cpriv->status.trec & MCP25XXFD_CAN_TREC_TXBO) { + cpriv->bus.new_state = CAN_STATE_BUS_OFF; + cpriv->error_frame.id |= CAN_ERR_BUSOFF; + } + + return 0; +} + +static int mcp25xxfd_can_int_error_handling(struct mcp25xxfd_can_priv *cpriv) +{ + /* based on the last state state check the new state */ + switch (cpriv->can.state) { + case CAN_STATE_ERROR_ACTIVE: + if (cpriv->bus.new_state >= CAN_STATE_ERROR_WARNING && + cpriv->bus.new_state <= CAN_STATE_BUS_OFF) + cpriv->can.can_stats.error_warning++; + fallthrough; + case CAN_STATE_ERROR_WARNING: + if (cpriv->bus.new_state >= CAN_STATE_ERROR_PASSIVE && + cpriv->bus.new_state <= CAN_STATE_BUS_OFF) + cpriv->can.can_stats.error_passive++; + break; + default: + break; + } + + cpriv->can.state = cpriv->bus.new_state; + + /* send error packet */ + if (cpriv->error_frame.id) + mcp25xxfd_can_int_send_error_skb(cpriv); + + /* handle BUS OFF */ + if (cpriv->can.state == CAN_STATE_BUS_OFF) { + if (cpriv->can.restart_ms == 0) { + cpriv->can.can_stats.bus_off++; + can_bus_off(cpriv->can.dev); + } + } + + return 0; +} + +static int mcp25xxfd_can_int_handle_status(struct mcp25xxfd_can_priv *cpriv) +{ + int ret; + + ret = mcp25xxfd_can_int_clear_int_flags(cpriv); + if (ret) + return ret; + + /* set up new state and error frame for this loop */ + cpriv->bus.new_state = cpriv->bus.state; + memset(&cpriv->error_frame, 0, sizeof(cpriv->error_frame)); + + /* setup the process queue by clearing the counter */ + cpriv->fifos.submit_queue_count = 0; + + /* system error interrupt needs to get handled first + * to get us out of restricted mode + */ + ret = mcp25xxfd_can_int_handle_serrif(cpriv); + if (ret) + return ret; + + /* mode change interrupt */ + ret = mcp25xxfd_can_int_handle_modif(cpriv); + if (ret) + return ret; + + /* handle the rx */ + ret = mcp25xxfd_can_rx_handle_int_rxif(cpriv); + if (ret) + return ret; + + /* handle error interrupt flags */ + ret = mcp25xxfd_can_rx_handle_int_rxovif(cpriv); + if (ret) + return ret; + + /* sram ECC error interrupt */ + ret = mcp25xxfd_can_int_handle_eccif(cpriv); + if (ret) + return ret; + + /* message format interrupt */ + ret = mcp25xxfd_can_int_handle_ivmif(cpriv); + if (ret) + return ret; + + /* handle bus errors in more detail */ + ret = mcp25xxfd_can_int_handle_cerrif(cpriv); + if (ret) + return ret; + + /* error counter handling */ + ret = mcp25xxfd_can_int_error_counters(cpriv); + if (ret) + return ret; + + /* error counter handling */ + ret = mcp25xxfd_can_int_error_handling(cpriv); + if (ret) + return ret; + + /* and submit can frames to network stack */ + ret = mcp25xxfd_can_int_submit_frames(cpriv); + + return ret; +} + +irqreturn_t mcp25xxfd_can_int(int irq, void *dev_id) +{ + struct mcp25xxfd_can_priv *cpriv = dev_id; + int loops, ret; + + /* loop forever unless we need to exit */ + for (loops = 0; true; loops++) { + /* read interrupt status flags in bulk */ + ret = mcp25xxfd_cmd_read_regs(cpriv->priv->spi, + MCP25XXFD_CAN_INT, + &cpriv->status.intf, + sizeof(cpriv->status)); + if (ret) + return ret; + + /* only act if the IE mask configured has active IF bits + * otherwise the Interrupt line should be deasserted already + * so we can exit the loop + */ + if (((cpriv->status.intf >> MCP25XXFD_CAN_INT_IE_SHIFT) & + cpriv->status.intf) == 0) + break; + + /* handle the status */ + ret = mcp25xxfd_can_int_handle_status(cpriv); + if (ret) + return ret; + + /* allow voluntarily rescheduling every so often to avoid + * long CS lows at the end of a transfer on low power CPUs + * avoiding SERR happening + */ + if (loops % MCP25XXFD_RESCHEDULE_TIMES == 0) + cond_resched(); + } + + return IRQ_HANDLED; +} + +int mcp25xxfd_can_int_clear(struct mcp25xxfd_priv *priv) +{ + return mcp25xxfd_cmd_write_mask(priv->spi, MCP25XXFD_CAN_INT, 0, + MCP25XXFD_CAN_INT_IF_MASK); +} + +int mcp25xxfd_can_int_enable(struct mcp25xxfd_priv *priv, bool enable) +{ + struct mcp25xxfd_can_priv *cpriv = priv->cpriv; + const u32 mask = MCP25XXFD_CAN_INT_TEFIE | + MCP25XXFD_CAN_INT_RXIE | + MCP25XXFD_CAN_INT_MODIE | + MCP25XXFD_CAN_INT_SERRIE | + MCP25XXFD_CAN_INT_IVMIE | + MCP25XXFD_CAN_INT_CERRIE | + MCP25XXFD_CAN_INT_RXOVIE | + MCP25XXFD_CAN_INT_ECCIE; + u32 value = cpriv ? cpriv->status.intf : 0; + int ret; + + value &= ~(MCP25XXFD_CAN_INT_IE_MASK); + if (enable) + value |= mask; + + ret = mcp25xxfd_cmd_write_mask(priv->spi, MCP25XXFD_CAN_INT, + value, mask); + if (ret) + return ret; + + if (!cpriv) + return 0; + + cpriv->status.intf = value; + if (cpriv->irq.allocated) { + if (enable && !cpriv->irq.enabled) + enable_irq(cpriv->priv->spi->irq); + if (!enable && cpriv->irq.enabled) + disable_irq(cpriv->priv->spi->irq); + cpriv->irq.enabled = enable; + } else { + cpriv->irq.enabled = false; + } + + return 0; +} diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_int.h b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_int.h new file mode 100644 index 000000000000..aa67a5da9271 --- /dev/null +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_int.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface + * + * Copyright 2019 Martin Sperl + */ +#ifndef __MCP25XXFD_CAN_INT_H +#define __MCP25XXFD_CAN_INT_H + +#include "mcp25xxfd_priv.h" +#include + +int mcp25xxfd_can_int_clear(struct mcp25xxfd_priv *priv); +int mcp25xxfd_can_int_enable(struct mcp25xxfd_priv *priv, bool enable); + +irqreturn_t mcp25xxfd_can_int(int irq, void *dev_id); + +#endif /* __MCP25XXFD_CAN_INT_H */ diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_priv.h b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_priv.h new file mode 100644 index 000000000000..e043b262a868 --- /dev/null +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_priv.h @@ -0,0 +1,131 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface + * + * Copyright 2019 Martin Sperl + */ + +#ifndef __MCP25XXFD_CAN_PRIV_H +#define __MCP25XXFD_CAN_PRIV_H + +#include +#include + +#include "mcp25xxfd_priv.h" + +#define TX_ECHO_SKB_MAX 32 + +/* information on each fifo type */ +struct mcp25xxfd_fifo { + u32 count; + u32 start; + u32 size; +}; + +/* used for sorting incoming messages */ +struct mcp25xxfd_obj_ts { + s32 ts; /* using signed to handle rollover correctly when sorting */ + u16 fifo; +}; + +/* general info on each fifo */ +struct mcp25xxfd_fifo_info { + u32 offset; + u32 priority; +}; + +struct mcp25xxfd_can_priv { + /* can_priv has to be the first one to be usable with alloc_candev + * which expects struct can_priv to be right at the start of the + * priv structure + */ + struct can_priv can; + struct mcp25xxfd_priv *priv; + struct regulator *transceiver; + + /* the can mode currently active */ + int mode; + + /* interrupt state */ + struct { + int enabled; + int allocated; + } irq; + + /* can config registers */ + struct { + u32 con; + u32 tdc; + u32 tscon; + u32 tefcon; + u32 nbtcfg; + u32 dbtcfg; + } regs; + + /* can status registers (mostly) - read in one go + * bdiag0 and bdiag1 are optional, but when + * berr counters are requested on a regular basis + * during high CAN-bus load this would trigger the fact + * that spi_sync would get queued for execution in the + * spi thread and the spi handler would not get + * called inline in the interrupt thread without any + * context switches or wakeups... + */ + struct { + u32 intf; + /* ASSERT(CAN_INT + 4 == CAN_RXIF) */ + u32 rxif; + /* ASSERT(CAN_RXIF + 4 == CAN_TXIF) */ + u32 txif; + /* ASSERT(CAN_TXIF + 4 == CAN_RXOVIF) */ + u32 rxovif; + /* ASSERT(CAN_RXOVIF + 4 == CAN_TXATIF) */ + u32 txatif; + /* ASSERT(CAN_TXATIF + 4 == CAN_TXREQ) */ + u32 txreq; + /* ASSERT(CAN_TXREQ + 4 == CAN_TREC) */ + u32 trec; + } status; + + /* information of fifo setup */ + struct { + /* define payload size and mode */ + u32 payload_size; + u32 payload_mode; + + /* infos on fifo layout */ + + /* info on each fifo */ + struct mcp25xxfd_fifo_info info[32]; + + /* extra info on rx fifo groups */ + struct mcp25xxfd_fifo rx; + + /* queue of can frames that need to get submitted + * to the network stack during an interrupt loop in one go + * (this gets sorted by timestamp before submission + * and contains both rx frames as well tx frames that have + * gone over the CAN bus successfully + */ + struct mcp25xxfd_obj_ts submit_queue[32]; + int submit_queue_count; + } fifos; + + /* bus state */ + struct { + u32 state; + u32 new_state; + u32 bdiag[2]; + } bus; + + /* can error messages */ + struct { + u32 id; + u8 data[8]; + } error_frame; + + /* a copy of mcp25xxfd-sram in ram */ + u8 sram[MCP25XXFD_SRAM_SIZE]; +}; + +#endif /* __MCP25XXFD_CAN_PRIV_H */ diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_rx.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_rx.c new file mode 100644 index 000000000000..5e3f706e7a3f --- /dev/null +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_rx.c @@ -0,0 +1,233 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface + * + * Copyright 2019 Martin Sperl + * + * Based on Microchip MCP251x CAN controller driver written by + * David Vrabel, Copyright 2006 Arcom Control Systems Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mcp25xxfd_cmd.h" +#include "mcp25xxfd_can.h" +#include "mcp25xxfd_can_id.h" +#include "mcp25xxfd_can_priv.h" +#include "mcp25xxfd_can_rx.h" + +static struct sk_buff * +mcp25xxfd_can_rx_submit_normal_frame(struct mcp25xxfd_can_priv *cpriv, + u32 id, u32 dlc, u8 **data) +{ + struct can_frame *frame; + struct sk_buff *skb; + + /* allocate frame */ + skb = alloc_can_skb(cpriv->can.dev, &frame); + if (!skb) + return NULL; + + /* set id, dlc and flags */ + frame->can_id = id; + frame->can_dlc = dlc; + + /* and set the pointer to data */ + *data = frame->data; + + return skb; +} + +/* it is almost identical except for the type of the frame... */ +static struct sk_buff * +mcp25xxfd_can_rx_submit_fd_frame(struct mcp25xxfd_can_priv *cpriv, + u32 id, u32 flags, u32 len, u8 **data) +{ + struct canfd_frame *frame; + struct sk_buff *skb; + + /* allocate frame */ + skb = alloc_canfd_skb(cpriv->can.dev, &frame); + if (!skb) + return NULL; + + /* set id, dlc and flags */ + frame->can_id = id; + frame->len = len; + frame->flags |= flags; + + /* and set the pointer to data */ + *data = frame->data; + + return skb; +} + +int mcp25xxfd_can_rx_submit_frame(struct mcp25xxfd_can_priv *cpriv, int fifo) +{ + struct net_device *net = cpriv->can.dev; + int addr = cpriv->fifos.info[fifo].offset; + struct mcp25xxfd_can_obj_rx *rx = + (struct mcp25xxfd_can_obj_rx *)(cpriv->sram + addr); + u8 *data = NULL; + struct sk_buff *skb; + u32 id, dlc, len, flags; + + /* compute the can_id */ + mcp25xxfd_can_id_from_mcp25xxfd(rx->id, rx->flags, &id); + + /* and dlc */ + dlc = (rx->flags & MCP25XXFD_CAN_OBJ_FLAGS_DLC_MASK) >> + MCP25XXFD_CAN_OBJ_FLAGS_DLC_SHIFT; + len = can_dlc2len(dlc); + + /* update stats */ + net->stats.rx_packets++; + net->stats.rx_bytes += len; + + /* allocate the skb buffer */ + if (rx->flags & MCP25XXFD_CAN_OBJ_FLAGS_FDF) { + flags = 0; + flags |= (rx->flags & MCP25XXFD_CAN_OBJ_FLAGS_BRS) ? + CANFD_BRS : 0; + flags |= (rx->flags & MCP25XXFD_CAN_OBJ_FLAGS_ESI) ? + CANFD_ESI : 0; + skb = mcp25xxfd_can_rx_submit_fd_frame(cpriv, id, flags, + len, &data); + } else { + skb = mcp25xxfd_can_rx_submit_normal_frame(cpriv, id, + len, &data); + } + if (!skb) { + netdev_err(net, "cannot allocate RX skb\n"); + net->stats.rx_dropped++; + return -ENOMEM; + } + + /* copy the payload data */ + memcpy(data, rx->data, len); + + /* and submit the frame */ + netif_rx_ni(skb); + + return 0; +} + +static int mcp25xxfd_can_rx_read_frame(struct mcp25xxfd_can_priv *cpriv, + int fifo, int prefetch_bytes) +{ + struct spi_device *spi = cpriv->priv->spi; + struct net_device *net = cpriv->can.dev; + int addr = cpriv->fifos.info[fifo].offset; + struct mcp25xxfd_can_obj_rx *rx = + (struct mcp25xxfd_can_obj_rx *)(cpriv->sram + addr); + int dlc; + int len, ret; + + /* we read the header plus prefetch_bytes */ + ret = mcp25xxfd_cmd_read_multi(spi, MCP25XXFD_SRAM_ADDR(addr), + rx, sizeof(*rx) + prefetch_bytes); + if (ret) + return ret; + + /* transpose the headers to CPU format*/ + rx->id = le32_to_cpu(rx->id); + rx->flags = le32_to_cpu(rx->flags); + rx->ts = le32_to_cpu(rx->ts); + + /* compute len */ + dlc = (rx->flags & MCP25XXFD_CAN_OBJ_FLAGS_DLC_MASK) >> + MCP25XXFD_CAN_OBJ_FLAGS_DLC_SHIFT; + len = can_dlc2len(min_t(int, dlc, (net->mtu == CANFD_MTU) ? 15 : 8)); + + /* read the remaining data for canfd frames */ + if (len > prefetch_bytes) { + /* here the extra portion reading data after prefetch */ + ret = mcp25xxfd_cmd_read_multi(spi, + MCP25XXFD_SRAM_ADDR(addr) + + sizeof(*rx) + prefetch_bytes, + &rx->data[prefetch_bytes], + len - prefetch_bytes); + if (ret) + return ret; + } + + /* clear the rest of the buffer - just to be safe */ + memset(rx->data + len, 0, ((net->mtu == CANFD_MTU) ? 64 : 8) - len); + + /* add the fifo to the process queues */ + mcp25xxfd_can_queue_frame(cpriv, fifo, rx->ts); + + /* and clear the interrupt flag for that fifo */ + return mcp25xxfd_cmd_write_mask(spi, MCP25XXFD_CAN_FIFOCON(fifo), + MCP25XXFD_CAN_FIFOCON_FRESET, + MCP25XXFD_CAN_FIFOCON_FRESET); +} + +static int mcp25xxfd_can_rx_read_frames(struct mcp25xxfd_can_priv *cpriv) +{ + int i, f, prefetch; + int ret; + + prefetch = 8; + /* TODO: Optimize this */ + for (i = 0, f = cpriv->fifos.rx.start; i < cpriv->fifos.rx.count; + i++, f++) { + if (cpriv->status.rxif & BIT(f)) { + /* read the frame */ + ret = mcp25xxfd_can_rx_read_frame(cpriv, f, prefetch); + if (ret) + return ret; + } + } + + return 0; +} + +int mcp25xxfd_can_rx_handle_int_rxif(struct mcp25xxfd_can_priv *cpriv) +{ + if (!cpriv->status.rxif) + return 0; + + /* read all the fifos */ + return mcp25xxfd_can_rx_read_frames(cpriv); +} + +int mcp25xxfd_can_rx_handle_int_rxovif(struct mcp25xxfd_can_priv *cpriv) +{ + u32 mask = MCP25XXFD_CAN_FIFOSTA_RXOVIF; + int ret, i, reg; + + if (!cpriv->status.rxovif) + return 0; + + /* clear all fifos that have an overflow bit set */ + for (i = 0; i < 32; i++) { + if (cpriv->status.rxovif & BIT(i)) { + /* clear fifo status */ + reg = MCP25XXFD_CAN_FIFOSTA(i); + ret = mcp25xxfd_cmd_write_mask(cpriv->priv->spi, + reg, 0, mask); + if (ret) + return ret; + + /* update statistics */ + cpriv->can.dev->stats.rx_over_errors++; + cpriv->can.dev->stats.rx_errors++; + + /* and prepare ERROR FRAME */ + cpriv->error_frame.id |= CAN_ERR_CRTL; + cpriv->error_frame.data[1] |= + CAN_ERR_CRTL_RX_OVERFLOW; + } + } + + return 0; +} diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_rx.h b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_rx.h new file mode 100644 index 000000000000..71953e2f3615 --- /dev/null +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_rx.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface + * + * Copyright 2019 Martin Sperl + */ + +#ifndef __MCP25XXFD_CAN_RX_H +#define __MCP25XXFD_CAN_RX_H + +#include "mcp25xxfd_priv.h" + +int mcp25xxfd_can_rx_submit_frame(struct mcp25xxfd_can_priv *cpriv, int fifo); + +int mcp25xxfd_can_rx_handle_int_rxif(struct mcp25xxfd_can_priv *cpriv); +int mcp25xxfd_can_rx_handle_int_rxovif(struct mcp25xxfd_can_priv *cpriv); + +#endif /* __MCP25XXFD_CAN_RX_H */ diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_int.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_int.c index 5e274d452646..182172b6c59c 100644 --- a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_int.c +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_int.c @@ -8,6 +8,7 @@ #include #include +#include "mcp25xxfd_can_int.h" #include "mcp25xxfd_crc.h" #include "mcp25xxfd_ecc.h" #include "mcp25xxfd_int.h" @@ -21,7 +22,11 @@ int mcp25xxfd_int_clear(struct mcp25xxfd_priv *priv) if (ret) return ret; - return mcp25xxfd_crc_clear_int(priv); + ret = mcp25xxfd_crc_clear_int(priv); + if (ret) + return ret; + + return mcp25xxfd_can_int_clear(priv); } int mcp25xxfd_int_enable(struct mcp25xxfd_priv *priv, bool enable) @@ -47,12 +52,19 @@ int mcp25xxfd_int_enable(struct mcp25xxfd_priv *priv, bool enable) if (ret) goto out_crc; + ret = mcp25xxfd_can_int_enable(priv, enable); + if (ret) + goto out_ecc; + /* If we disable interrupts, then clear interrupt flags last */ if (!enable) mcp25xxfd_int_clear(priv); return 0; +out_ecc: + mcp25xxfd_ecc_enable_int(priv, false); + out_crc: mcp25xxfd_crc_enable_int(priv, false); return ret; diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_priv.h b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_priv.h index 8bc7a599224c..85c27a7f6785 100644 --- a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_priv.h +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_priv.h @@ -23,9 +23,11 @@ enum mcp25xxfd_model { CAN_MCP2517FD = 0x2517, }; +struct mcp25xxfd_can_priv; struct mcp25xxfd_priv { struct spi_device *spi; struct clk *clk; + struct mcp25xxfd_can_priv *cpriv; /* actual model of the mcp25xxfd */ enum mcp25xxfd_model model; diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_regs.h b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_regs.h index b500cb46b9a4..222527439c70 100644 --- a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_regs.h +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_regs.h @@ -653,4 +653,9 @@ struct mcp25xxfd_can_obj_tef { MCP25XXFD_CAN_FLAGS_FILHIT_BITS - 1, \ MCP25XXFD_CAN_FLAGS_FILHIT_SHIFT) +/* custom status error */ +#define MCP25XXFD_CAN_ERR_DATA7_MCP25XXFD_SERR_RX BIT(0) +#define MCP25XXFD_CAN_ERR_DATA7_MCP25XXFD_SERR_TX BIT(1) +#define MCP25XXFD_CAN_ERR_DATA7_MCP25XXFD_ECC BIT(2) + #endif /* __MCP25XXFD_REGS_H */ From patchwork Wed Jun 10 07:44:40 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Manivannan Sadhasivam X-Patchwork-Id: 187732 Delivered-To: patch@linaro.org Received: by 2002:a92:cf06:0:0:0:0:0 with SMTP id c6csp254245ilo; Wed, 10 Jun 2020 00:45:29 -0700 (PDT) X-Google-Smtp-Source: ABdhPJw1dgWyPpFaFuX7XNPxV/c6ksfLOFgV87YZoNMZos8zxNCspWKc52/EMhUmkQb++48Z4hPb X-Received: by 2002:a17:906:17c5:: with SMTP id u5mr2082138eje.275.1591775129036; Wed, 10 Jun 2020 00:45:29 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1591775129; cv=none; d=google.com; s=arc-20160816; b=gqpNDPjhwj+Yh1nXMh/+D0Y0GUBZppe4ey0TyrBC6KuBU9s1NZ4dILPOvyYHhMeM0O ZC4+G3PSVBWzaJctJybLEKfxh7RMAPyPLh8OKhH1EHGTWRDKRjFIQxXkutfVX8HwgIrc ad24rMchcYY9nymznyOLIy/0u5Z9+ZhX1MO6piXhNfEeGEHRvZQ485UUBsZXw2MC4ieP il37JQ9mBd0NWVB6z8Z/fJDzVMPz3baYAkjB3Myk954hokuijrRYFIM9FKK11nZTOClM G6IJA2SlFwOtQWJjzEfA3872IsjzWSblB7o0x6fOv2UsuBDiP3wITCFqkBKe+Esftzhz 7O3Q== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:dkim-signature; bh=s8w+cXn0Jb29Ftkb7WcWynQaPBBugviXzJo6qIddcD0=; b=uhR4j/TH3GsjaWngr1xj+EoeweEzmP5wDXXl/vtjbdegUNFAStsV9nzZztuhaK//pm ATkTvD5fyR4Po8/9bT4V0NrWlui1p2ZVHFneGQm6P9PUItsrabagnr6N+6HyKcMd9Y6v UlFgoy5215Ax4tz7/DDo+vAsPAKXF2QEg65PH88glJqYOby3V/gorZFrOP/w6vu9h1Cz CRoLpD0lwXhenWt4zfNtED8F5IOmdj1XR8k8tokHXI70MPuAHdobbw4+aOtA1xwQXpDf fyrLes1RxzJIXTQ1KY0ahpAkbaOI3kEdSPgh/f00+DSGKN2lxl8rmRB3BageT50zMyAZ b+5Q== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=wGZiDY+t; spf=pass (google.com: domain of netdev-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=netdev-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id n11si11263268ejh.543.2020.06.10.00.45.28; Wed, 10 Jun 2020 00:45:29 -0700 (PDT) Received-SPF: pass (google.com: domain of netdev-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=wGZiDY+t; spf=pass (google.com: domain of netdev-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=netdev-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726643AbgFJHp1 (ORCPT + 9 others); Wed, 10 Jun 2020 03:45:27 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:57336 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726624AbgFJHpQ (ORCPT ); Wed, 10 Jun 2020 03:45:16 -0400 Received: from mail-pl1-x642.google.com (mail-pl1-x642.google.com [IPv6:2607:f8b0:4864:20::642]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 4F130C03E96B for ; Wed, 10 Jun 2020 00:45:15 -0700 (PDT) Received: by mail-pl1-x642.google.com with SMTP id d8so632006plo.12 for ; Wed, 10 Jun 2020 00:45:15 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=s8w+cXn0Jb29Ftkb7WcWynQaPBBugviXzJo6qIddcD0=; b=wGZiDY+tb55B4l8Q+ML50+mfazcvyKsKG65z2oB3Wry1Kbnowhf3zHM8fE9WSRq7bD VSmiBZN/6EJ+XhcI73O9C3+9N0g7QY8r/pjzA3Ayj3F0h8pyWY2nNCwQ3hjCfao8mocb p9wFDclEhe86irQJgkoZ1ZJDpH/bHdNumK6tO1AnfBydocyAVzY38PkE6DYTeflY1SNq ICri7PK8t4IkfaGO4LSLjZ45C6pPy9wo2jxgnRSeKYfrT0oz23KHTlWRDdPjFeB29iBb c6734rJUmFyfk0NIhP5a7GkHWp7zEPLRaiCt9TtkU2dI3KfNobhF8MiiFgPYFznaKzqk J97Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=s8w+cXn0Jb29Ftkb7WcWynQaPBBugviXzJo6qIddcD0=; b=N5J+Mgy0riE8IrscLKgam1NC6IRkS+Doq7/V6CJFRgSQOomj/wL1q3NfDtYhUUzBhx Ab1erN3piwbBc1KrOlUAwMxiQCEjcHnVGdvKiDwWMm5bd2B2kyT2R09nEuKqmPqAtQ7u mcTErvSFf3zJyPfwVE236LPwb8L2vuIqCGFplsut+88GBT+SlqbEGfoNs8rXVmHDWgLJ Jqo1qTTmqUxlQGCBl5EVnwfS/KubQLteYKeUJ7RH+IHTGnoeDGzs12B+NXvKPZnxgN7f rFsmKsW58uVQvez8+ECAmUhYQJ4EHVdClucynHC3Z9btfCeeDLBQUqlgrQixRCA0xxlg Fmww== X-Gm-Message-State: AOAM532/lMoc9jbBbqSBQBtF1lRhGckzMfZLWpJv+enukX2UX92OZOYM /7uS+OTon0fUElqocM/8fzUk X-Received: by 2002:a17:902:ea92:: with SMTP id x18mr1963874plb.157.1591775114518; Wed, 10 Jun 2020 00:45:14 -0700 (PDT) Received: from Mani-XPS-13-9360.localdomain ([2409:4072:630f:1dba:c41:a14e:6586:388a]) by smtp.gmail.com with ESMTPSA id a20sm11516795pff.147.2020.06.10.00.45.09 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 10 Jun 2020 00:45:13 -0700 (PDT) From: Manivannan Sadhasivam To: wg@grandegger.com, mkl@pengutronix.de Cc: kernel@martin.sperl.org, linux-can@vger.kernel.org, netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Manivannan Sadhasivam Subject: [PATCH 4/6] can: mcp25xxfd: Add CAN transmission support Date: Wed, 10 Jun 2020 13:14:40 +0530 Message-Id: <20200610074442.10808-5-manivannan.sadhasivam@linaro.org> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200610074442.10808-1-manivannan.sadhasivam@linaro.org> References: <20200610074442.10808-1-manivannan.sadhasivam@linaro.org> Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org From: Martin Sperl Add un-optimized CAN2.0 and CAN-FD transmission support. On a Rpi3 we can saturate the CAN bus at 1MHz transmitting CAN2.0 frames with DLC=0 for the number of configured tx-fifos. Afterwards we need some time to recover the state before we can fill in the fifos again. With 7 tx fifos we can: send those 7 frames in 0.33ms and then we wait for 0.26ms so that is a 56% duty cycle. With 24 tx fifos this changes to: 1.19ms for 24 frames and then we wait for 0.52ms so that is a 70% duty cycle. Signed-off-by: Martin Sperl Signed-off-by: Manivannan Sadhasivam --- drivers/net/can/spi/mcp25xxfd/Makefile | 1 + drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.c | 10 + drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.h | 3 +- .../can/spi/mcp25xxfd/mcp25xxfd_can_fifo.c | 93 ++- .../net/can/spi/mcp25xxfd/mcp25xxfd_can_int.c | 21 +- .../can/spi/mcp25xxfd/mcp25xxfd_can_priv.h | 15 +- .../net/can/spi/mcp25xxfd/mcp25xxfd_can_rx.c | 2 +- .../net/can/spi/mcp25xxfd/mcp25xxfd_can_tx.c | 620 ++++++++++++++++++ .../net/can/spi/mcp25xxfd/mcp25xxfd_can_tx.h | 86 +++ 9 files changed, 839 insertions(+), 12 deletions(-) create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_tx.c create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_tx.h -- 2.17.1 diff --git a/drivers/net/can/spi/mcp25xxfd/Makefile b/drivers/net/can/spi/mcp25xxfd/Makefile index 5787bdd57a9d..a586b2555ff9 100644 --- a/drivers/net/can/spi/mcp25xxfd/Makefile +++ b/drivers/net/can/spi/mcp25xxfd/Makefile @@ -4,6 +4,7 @@ mcp25xxfd-objs += mcp25xxfd_can.o mcp25xxfd-objs += mcp25xxfd_can_fifo.o mcp25xxfd-objs += mcp25xxfd_can_int.o mcp25xxfd-objs += mcp25xxfd_can_rx.o +mcp25xxfd-objs += mcp25xxfd_can_tx.o mcp25xxfd-objs += mcp25xxfd_cmd.o mcp25xxfd-objs += mcp25xxfd_crc.o mcp25xxfd-objs += mcp25xxfd_ecc.o diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.c index 2ac78024c171..1417f1a22d6e 100644 --- a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.c +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.c @@ -20,6 +20,7 @@ #include "mcp25xxfd_can_fifo.h" #include "mcp25xxfd_can_int.h" #include "mcp25xxfd_can_priv.h" +#include "mcp25xxfd_can_tx.h" #include "mcp25xxfd_can.h" #include "mcp25xxfd_cmd.h" #include "mcp25xxfd_int.h" @@ -395,6 +396,10 @@ static int mcp25xxfd_can_open(struct net_device *net) if (ret) goto out_int; + /* start the tx_queue */ + mcp25xxfd_can_tx_queue_manage(cpriv, + MCP25XXFD_CAN_TX_QUEUE_STATE_STARTED); + return 0; out_int: @@ -425,6 +430,10 @@ static int mcp25xxfd_can_stop(struct net_device *net) struct mcp25xxfd_priv *priv = cpriv->priv; struct spi_device *spi = priv->spi; + /* stop transmit queue */ + mcp25xxfd_can_tx_queue_manage(cpriv, + MCP25XXFD_CAN_TX_QUEUE_STATE_STOPPED); + /* shutdown the can controller */ mcp25xxfd_can_shutdown(cpriv); @@ -448,6 +457,7 @@ static int mcp25xxfd_can_stop(struct net_device *net) static const struct net_device_ops mcp25xxfd_netdev_ops = { .ndo_open = mcp25xxfd_can_open, .ndo_stop = mcp25xxfd_can_stop, + .ndo_start_xmit = mcp25xxfd_can_tx_start_xmit, .ndo_change_mtu = can_change_mtu, }; diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.h b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.h index 4b18b5bb3d45..8ec20827f099 100644 --- a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.h +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.h @@ -22,12 +22,13 @@ int mcp25xxfd_can_targetmode(struct mcp25xxfd_can_priv *cpriv) static inline void mcp25xxfd_can_queue_frame(struct mcp25xxfd_can_priv *cpriv, - s32 fifo, u16 ts) + s32 fifo, u16 ts, bool is_rx) { int idx = cpriv->fifos.submit_queue_count; cpriv->fifos.submit_queue[idx].fifo = fifo; cpriv->fifos.submit_queue[idx].ts = ts; + cpriv->fifos.submit_queue[idx].is_rx = is_rx; cpriv->fifos.submit_queue_count++; } diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_fifo.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_fifo.c index 4bd776772d2d..4a1ad250bdaf 100644 --- a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_fifo.c +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_fifo.c @@ -5,8 +5,6 @@ * Copyright 2019 Martin Sperl */ -/* here we define and configure the fifo layout */ - #include #include #include @@ -14,6 +12,7 @@ #include "mcp25xxfd_can.h" #include "mcp25xxfd_can_fifo.h" #include "mcp25xxfd_can_priv.h" +#include "mcp25xxfd_can_tx.h" #include "mcp25xxfd_cmd.h" static int mcp25xxfd_can_fifo_get_address(struct mcp25xxfd_can_priv *cpriv) @@ -55,6 +54,15 @@ static int mcp25xxfd_can_fifo_setup_config(struct mcp25xxfd_can_priv *cpriv, c > 0; i++, f++, p--, c--) { val = (c > 1) ? flags : flags_last; + /* are we in tx mode? */ + if (flags & MCP25XXFD_CAN_FIFOCON_TXEN) { + cpriv->fifos.info[f].is_rx = false; + cpriv->fifos.info[f].priority = p; + val |= (p << MCP25XXFD_CAN_FIFOCON_TXPRI_SHIFT); + } else { + cpriv->fifos.info[f].is_rx = true; + } + /* write the config to the controller in one go */ ret = mcp25xxfd_cmd_write(cpriv->priv->spi, MCP25XXFD_CAN_FIFOCON(f), val); @@ -65,6 +73,27 @@ static int mcp25xxfd_can_fifo_setup_config(struct mcp25xxfd_can_priv *cpriv, return 0; } +static int mcp25xxfd_can_fifo_setup_tx(struct mcp25xxfd_can_priv *cpriv) +{ + u32 tx_flags = MCP25XXFD_CAN_FIFOCON_FRESET | /* reset FIFO */ + MCP25XXFD_CAN_FIFOCON_TXEN | /* a tx FIFO */ + MCP25XXFD_CAN_FIFOCON_TXATIE | /* state in txatif */ + (cpriv->fifos.payload_mode << + MCP25XXFD_CAN_FIFOCON_PLSIZE_SHIFT) | /* paylod size */ + (0 << MCP25XXFD_CAN_FIFOCON_FSIZE_SHIFT); /* 1 FIFO deep */ + + /* handle oneshot/three-shot */ + if (cpriv->can.ctrlmode & CAN_CTRLMODE_ONE_SHOT) + tx_flags |= MCP25XXFD_CAN_FIFOCON_TXAT_ONE_SHOT << + MCP25XXFD_CAN_FIFOCON_TXAT_SHIFT; + else + tx_flags |= MCP25XXFD_CAN_FIFOCON_TXAT_UNLIMITED << + MCP25XXFD_CAN_FIFOCON_TXAT_SHIFT; + + return mcp25xxfd_can_fifo_setup_config(cpriv, &cpriv->fifos.tx, + tx_flags, tx_flags); +} + static int mcp25xxfd_can_fifo_setup_rx(struct mcp25xxfd_can_priv *cpriv) { u32 rx_flags = MCP25XXFD_CAN_FIFOCON_FRESET | /* reset FIFO */ @@ -106,7 +135,7 @@ static int mcp25xxfd_can_fifo_setup_rxfilter(struct mcp25xxfd_can_priv *cpriv) static int mcp25xxfd_can_fifo_compute(struct mcp25xxfd_can_priv *cpriv) { - int rx_memory_available; + int tef_memory_used, tx_memory_used, rx_memory_available; switch (cpriv->can.dev->mtu) { case CAN_MTU: @@ -114,23 +143,48 @@ static int mcp25xxfd_can_fifo_compute(struct mcp25xxfd_can_priv *cpriv) cpriv->fifos.payload_size = 8; cpriv->fifos.payload_mode = MCP25XXFD_CAN_TXQCON_PLSIZE_8; + /* 7 tx fifos */ + cpriv->fifos.tx.count = 7; + break; case CANFD_MTU: /* MTU is 64 */ cpriv->fifos.payload_size = 64; cpriv->fifos.payload_mode = MCP25XXFD_CAN_TXQCON_PLSIZE_64; + /* 7 tx fifos */ + cpriv->fifos.tx.count = 7; + break; default: return -EINVAL; } /* compute effective sizes */ + cpriv->fifos.tef.size = sizeof(struct mcp25xxfd_can_obj_tef); + cpriv->fifos.tx.size = sizeof(struct mcp25xxfd_can_obj_tx) + + cpriv->fifos.payload_size; cpriv->fifos.rx.size = sizeof(struct mcp25xxfd_can_obj_rx) + cpriv->fifos.payload_size; + /* set tef fifos to the number of tx fifos */ + cpriv->fifos.tef.count = cpriv->fifos.tx.count; + + /* compute size of the tx fifos and TEF */ + tx_memory_used = cpriv->fifos.tx.count * cpriv->fifos.tx.size; + tef_memory_used = cpriv->fifos.tef.count * cpriv->fifos.tef.size; + /* calculate evailable memory for RX_fifos */ - rx_memory_available = MCP25XXFD_SRAM_SIZE; + rx_memory_available = MCP25XXFD_SRAM_SIZE - tx_memory_used - + tef_memory_used; + + /* we need at least one RX Frame */ + if (rx_memory_available < cpriv->fifos.rx.size) { + netdev_err(cpriv->can.dev, + "Configured %i tx-fifos exceeds available memory already\n", + cpriv->fifos.tx.count); + return -EINVAL; + } /* calculate possible amount of RX fifos */ cpriv->fifos.rx.count = rx_memory_available / cpriv->fifos.rx.size; @@ -138,10 +192,11 @@ static int mcp25xxfd_can_fifo_compute(struct mcp25xxfd_can_priv *cpriv) /* now calculate effective number of rx-fifos. There are only 31 fifos * available in total, so we need to limit ourselves */ - if (cpriv->fifos.rx.count > 31) - cpriv->fifos.rx.count = 31; + if (cpriv->fifos.rx.count + cpriv->fifos.tx.count > 31) + cpriv->fifos.rx.count = 31 - cpriv->fifos.tx.count; - cpriv->fifos.rx.start = 1; + cpriv->fifos.tx.start = 1; + cpriv->fifos.rx.start = cpriv->fifos.tx.start + cpriv->fifos.tx.count; return 0; } @@ -170,6 +225,7 @@ static int mcp25xxfd_can_fifo_clear(struct mcp25xxfd_can_priv *cpriv) int ret; memset(&cpriv->fifos.info, 0, sizeof(cpriv->fifos.info)); + memset(&cpriv->fifos.tx, 0, sizeof(cpriv->fifos.tx)); memset(&cpriv->fifos.rx, 0, sizeof(cpriv->fifos.rx)); /* clear FIFO config */ @@ -196,16 +252,31 @@ int mcp25xxfd_can_fifo_setup(struct mcp25xxfd_can_priv *cpriv) if (ret) return ret; - cpriv->regs.tefcon = 0; + /* configure TEF */ + if (cpriv->fifos.tef.count) + cpriv->regs.tefcon = + MCP25XXFD_CAN_TEFCON_FRESET | + MCP25XXFD_CAN_TEFCON_TEFNEIE | + MCP25XXFD_CAN_TEFCON_TEFTSEN | + ((cpriv->fifos.tef.count - 1) << + MCP25XXFD_CAN_TEFCON_FSIZE_SHIFT); + else + cpriv->regs.tefcon = 0; ret = mcp25xxfd_cmd_write(cpriv->priv->spi, MCP25XXFD_CAN_TEFCON, cpriv->regs.tefcon); if (ret) return ret; + /* TXQueue disabled */ ret = mcp25xxfd_cmd_write(cpriv->priv->spi, MCP25XXFD_CAN_TXQCON, 0); if (ret) return ret; + /* configure FIFOS themselves */ + ret = mcp25xxfd_can_fifo_setup_tx(cpriv); + if (ret) + return ret; + ret = mcp25xxfd_can_fifo_setup_rx(cpriv); if (ret) return ret; @@ -219,10 +290,16 @@ int mcp25xxfd_can_fifo_setup(struct mcp25xxfd_can_priv *cpriv) if (ret) return ret; + /* setup tx_fifo_queue */ + ret = mcp25xxfd_can_tx_queue_alloc(cpriv); + if (ret) + return ret; + return 0; } void mcp25xxfd_can_fifo_release(struct mcp25xxfd_can_priv *cpriv) { + mcp25xxfd_can_tx_queue_free(cpriv); mcp25xxfd_can_fifo_clear(cpriv); } diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_int.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_int.c index 83656b2604df..b3cf3e77c299 100644 --- a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_int.c +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_int.c @@ -23,6 +23,7 @@ #include "mcp25xxfd_can_int.h" #include "mcp25xxfd_can_priv.h" #include "mcp25xxfd_can_rx.h" +#include "mcp25xxfd_can_tx.h" #include "mcp25xxfd_cmd.h" #include "mcp25xxfd_ecc.h" @@ -80,7 +81,9 @@ static int mcp25xxfd_can_int_submit_frames(struct mcp25xxfd_can_priv *cpriv) /* now submit the fifos */ for (i = 0; i < count; i++) { fifo = queue[i].fifo; - ret = mcp25xxfd_can_rx_submit_frame(cpriv, fifo); + ret = (queue[i].is_rx) ? + mcp25xxfd_can_rx_submit_frame(cpriv, fifo) : + mcp25xxfd_can_tx_submit_frame(cpriv, fifo); if (ret) return ret; } @@ -100,6 +103,9 @@ static int mcp25xxfd_can_int_submit_frames(struct mcp25xxfd_can_priv *cpriv) } out: + /* enable tx_queue if necessary */ + mcp25xxfd_can_tx_queue_restart(cpriv); + return 0; } @@ -496,6 +502,9 @@ static int mcp25xxfd_can_int_error_handling(struct mcp25xxfd_can_priv *cpriv) cpriv->can.can_stats.bus_off++; can_bus_off(cpriv->can.dev); } + } else { + /* restart the tx queue if needed */ + mcp25xxfd_can_tx_queue_restart(cpriv); } return 0; @@ -533,6 +542,16 @@ static int mcp25xxfd_can_int_handle_status(struct mcp25xxfd_can_priv *cpriv) if (ret) return ret; + /* handle aborted TX FIFOs */ + ret = mcp25xxfd_can_tx_handle_int_txatif(cpriv); + if (ret) + return ret; + + /* handle the TEF */ + ret = mcp25xxfd_can_tx_handle_int_tefif(cpriv); + if (ret) + return ret; + /* handle error interrupt flags */ ret = mcp25xxfd_can_rx_handle_int_rxovif(cpriv); if (ret) diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_priv.h b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_priv.h index e043b262a868..99c3ef6d08e0 100644 --- a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_priv.h +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_priv.h @@ -26,10 +26,12 @@ struct mcp25xxfd_fifo { struct mcp25xxfd_obj_ts { s32 ts; /* using signed to handle rollover correctly when sorting */ u16 fifo; + s16 is_rx; }; /* general info on each fifo */ struct mcp25xxfd_fifo_info { + u32 is_rx; u32 offset; u32 priority; }; @@ -95,10 +97,18 @@ struct mcp25xxfd_can_priv { /* infos on fifo layout */ + /* TEF */ + struct { + u32 count; + u32 size; + u32 index; + } tef; + /* info on each fifo */ struct mcp25xxfd_fifo_info info[32]; - /* extra info on rx fifo groups */ + /* extra info on rx/tx fifo groups */ + struct mcp25xxfd_fifo tx; struct mcp25xxfd_fifo rx; /* queue of can frames that need to get submitted @@ -109,6 +119,9 @@ struct mcp25xxfd_can_priv { */ struct mcp25xxfd_obj_ts submit_queue[32]; int submit_queue_count; + + /* the tx queue of spi messages */ + struct mcp25xxfd_tx_spi_message_queue *tx_queue; } fifos; /* bus state */ diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_rx.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_rx.c index 5e3f706e7a3f..da99145e0c94 100644 --- a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_rx.c +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_rx.c @@ -163,7 +163,7 @@ static int mcp25xxfd_can_rx_read_frame(struct mcp25xxfd_can_priv *cpriv, memset(rx->data + len, 0, ((net->mtu == CANFD_MTU) ? 64 : 8) - len); /* add the fifo to the process queues */ - mcp25xxfd_can_queue_frame(cpriv, fifo, rx->ts); + mcp25xxfd_can_queue_frame(cpriv, fifo, rx->ts, true); /* and clear the interrupt flag for that fifo */ return mcp25xxfd_cmd_write_mask(spi, MCP25XXFD_CAN_FIFOCON(fifo), diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_tx.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_tx.c new file mode 100644 index 000000000000..5ea1e525e776 --- /dev/null +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_tx.c @@ -0,0 +1,620 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface + * + * Copyright 2019 Martin Sperl + * + * Based on Microchip MCP251x CAN controller driver written by + * David Vrabel, Copyright 2006 Arcom Control Systems Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mcp25xxfd_can.h" +#include "mcp25xxfd_can_id.h" +#include "mcp25xxfd_can_tx.h" +#include "mcp25xxfd_cmd.h" +#include "mcp25xxfd_regs.h" + +static struct mcp25xxfd_tx_spi_message * +mcp25xxfd_can_tx_queue_first_spi_message(struct mcp25xxfd_tx_spi_message_queue * + queue, u32 *bitmap) +{ + u32 first = ffs(*bitmap); + + if (!first) + return NULL; + + return queue->fifo2message[first - 1]; +} + +static void mcp25xxfd_can_tx_queue_remove_spi_message(u32 *bitmap, int fifo) +{ + *bitmap &= ~BIT(fifo); +} + +static void mcp25xxfd_can_tx_queue_add_spi_message(u32 *bitmap, int fifo) +{ + *bitmap |= BIT(fifo); +} + +static void mcp25xxfd_can_tx_queue_move_spi_message(u32 *src, u32 *dest, + int fifo) +{ + mcp25xxfd_can_tx_queue_remove_spi_message(src, fifo); + mcp25xxfd_can_tx_queue_add_spi_message(dest, fifo); +} + +static void mcp25xxfd_can_tx_spi_message_fill_fifo_complete(void *context) +{ + struct mcp25xxfd_tx_spi_message *msg = context; + struct mcp25xxfd_can_priv *cpriv = msg->cpriv; + struct mcp25xxfd_tx_spi_message_queue *q = cpriv->fifos.tx_queue; + unsigned long flags; + + /* reset transfer length to without data (DLC = 0) */ + msg->fill_fifo.xfer.len = sizeof(msg->fill_fifo.data.cmd) + + sizeof(msg->fill_fifo.data.header); + + spin_lock_irqsave(&cpriv->fifos.tx_queue->lock, flags); + + /* move to in_trigger_fifo_transfer */ + mcp25xxfd_can_tx_queue_move_spi_message(&q->in_fill_fifo_transfer, + &q->in_trigger_fifo_transfer, + msg->fifo); + + spin_unlock_irqrestore(&cpriv->fifos.tx_queue->lock, flags); +} + +static void mcp25xxfd_can_tx_spi_message_trigger_fifo_complete(void *context) +{ + struct mcp25xxfd_tx_spi_message *msg = context; + struct mcp25xxfd_can_priv *cpriv = msg->cpriv; + struct mcp25xxfd_tx_spi_message_queue *q = cpriv->fifos.tx_queue; + unsigned long flags; + + spin_lock_irqsave(&cpriv->fifos.tx_queue->lock, flags); + + /* move to can_transfer */ + mcp25xxfd_can_tx_queue_move_spi_message(&q->in_trigger_fifo_transfer, + &q->in_can_transfer, + msg->fifo); + + spin_unlock_irqrestore(&cpriv->fifos.tx_queue->lock, flags); +} + +static +void mcp25xxfd_can_tx_message_init(struct mcp25xxfd_can_priv *cpriv, + struct mcp25xxfd_tx_spi_message *msg, + int fifo) +{ + const u32 trigger = MCP25XXFD_CAN_FIFOCON_TXREQ | + MCP25XXFD_CAN_FIFOCON_UINC; + const int first_byte = mcp25xxfd_cmd_first_byte(trigger); + u32 addr; + + msg->cpriv = cpriv; + msg->fifo = fifo; + + /* init fill_fifo */ + spi_message_init(&msg->fill_fifo.msg); + msg->fill_fifo.msg.complete = + mcp25xxfd_can_tx_spi_message_fill_fifo_complete; + msg->fill_fifo.msg.context = msg; + + msg->fill_fifo.xfer.tx_buf = msg->fill_fifo.data.cmd; + msg->fill_fifo.xfer.len = sizeof(msg->fill_fifo.data.cmd) + + sizeof(msg->fill_fifo.data.header); + spi_message_add_tail(&msg->fill_fifo.xfer, &msg->fill_fifo.msg); + + addr = MCP25XXFD_SRAM_ADDR(cpriv->fifos.info[fifo].offset); + mcp25xxfd_cmd_calc(MCP25XXFD_INSTRUCTION_WRITE, addr, + msg->fill_fifo.data.cmd); + + /* init trigger_fifo */ + spi_message_init(&msg->trigger_fifo.msg); + msg->trigger_fifo.msg.complete = + mcp25xxfd_can_tx_spi_message_trigger_fifo_complete; + msg->trigger_fifo.msg.context = msg; + + msg->trigger_fifo.xfer.tx_buf = msg->trigger_fifo.data.cmd; + msg->trigger_fifo.xfer.len = sizeof(msg->trigger_fifo.data.cmd) + + sizeof(msg->trigger_fifo.data.data); + spi_message_add_tail(&msg->trigger_fifo.xfer, &msg->trigger_fifo.msg); + + mcp25xxfd_cmd_calc(MCP25XXFD_INSTRUCTION_WRITE, + MCP25XXFD_CAN_FIFOCON(fifo) + first_byte, + msg->trigger_fifo.data.cmd); + msg->trigger_fifo.data.data = trigger >> (8 * first_byte); + + /* add to idle tx transfers */ + mcp25xxfd_can_tx_queue_add_spi_message(&cpriv->fifos.tx_queue->idle, + fifo); +} + +static +void mcp25xxfd_can_tx_queue_manage_nolock(struct mcp25xxfd_can_priv *cpriv, + int state) +{ + struct net_device *net = cpriv->can.dev; + + if (state == cpriv->fifos.tx_queue->state) + return; + + /* start/stop netif_queue if necessary */ + switch (cpriv->fifos.tx_queue->state) { + case MCP25XXFD_CAN_TX_QUEUE_STATE_RUNABLE: + switch (state) { + case MCP25XXFD_CAN_TX_QUEUE_STATE_RESTART: + case MCP25XXFD_CAN_TX_QUEUE_STATE_STARTED: + netif_wake_queue(net); + cpriv->fifos.tx_queue->state = + MCP25XXFD_CAN_TX_QUEUE_STATE_STARTED; + break; + } + break; + case MCP25XXFD_CAN_TX_QUEUE_STATE_STOPPED: + switch (state) { + case MCP25XXFD_CAN_TX_QUEUE_STATE_STARTED: + netif_wake_queue(net); + cpriv->fifos.tx_queue->state = state; + break; + } + break; + case MCP25XXFD_CAN_TX_QUEUE_STATE_STARTED: + switch (state) { + case MCP25XXFD_CAN_TX_QUEUE_STATE_RUNABLE: + case MCP25XXFD_CAN_TX_QUEUE_STATE_STOPPED: + netif_stop_queue(net); + cpriv->fifos.tx_queue->state = state; + break; + } + break; + default: + netdev_err(cpriv->can.dev, "Unsupported tx_queue state: %i\n", + cpriv->fifos.tx_queue->state); + break; + } +} + +void mcp25xxfd_can_tx_queue_manage(struct mcp25xxfd_can_priv *cpriv, int state) +{ + unsigned long flags; + + spin_lock_irqsave(&cpriv->fifos.tx_queue->lock, flags); + + mcp25xxfd_can_tx_queue_manage_nolock(cpriv, state); + + spin_unlock_irqrestore(&cpriv->fifos.tx_queue->lock, flags); +} + +void mcp25xxfd_can_tx_queue_restart(struct mcp25xxfd_can_priv *cpriv) +{ + u32 state = MCP25XXFD_CAN_TX_QUEUE_STATE_RESTART; + unsigned long flags; + u32 mask; + + spin_lock_irqsave(&cpriv->fifos.tx_queue->lock, flags); + + /* only move if there is nothing pending or idle */ + mask = cpriv->fifos.tx_queue->idle | + cpriv->fifos.tx_queue->in_fill_fifo_transfer | + cpriv->fifos.tx_queue->in_trigger_fifo_transfer | + cpriv->fifos.tx_queue->in_can_transfer; + if (mask) + goto out; + + /* move all items from transferred to idle */ + cpriv->fifos.tx_queue->idle |= cpriv->fifos.tx_queue->transferred; + cpriv->fifos.tx_queue->transferred = 0; + + /* and enable queue */ + mcp25xxfd_can_tx_queue_manage_nolock(cpriv, state); +out: + spin_unlock_irqrestore(&cpriv->fifos.tx_queue->lock, flags); +} + +static +int mcp25xxfd_can_tx_handle_int_tefif_fifo(struct mcp25xxfd_can_priv *cpriv) +{ + u32 tef_offset = cpriv->fifos.tef.index * cpriv->fifos.tef.size; + struct mcp25xxfd_can_obj_tef *tef = + (struct mcp25xxfd_can_obj_tef *)(cpriv->sram + tef_offset); + int fifo, ret; + unsigned long flags; + + /* read the next TEF entry to get the transmit timestamp and fifo */ + ret = mcp25xxfd_cmd_read_regs(cpriv->priv->spi, + MCP25XXFD_SRAM_ADDR(tef_offset), + &tef->id, sizeof(*tef)); + if (ret) + return ret; + + /* get the fifo from tef */ + fifo = FIELD_GET(MCP25XXFD_CAN_OBJ_FLAGS_SEQ_MASK, tef->flags); + + /* check that the fifo is valid */ + spin_lock_irqsave(&cpriv->fifos.tx_queue->lock, flags); + if ((cpriv->fifos.tx_queue->in_can_transfer & BIT(fifo)) == 0) + netdev_err(cpriv->can.dev, + "tefif: fifo %i not pending - tef data: id: %08x flags: %08x, ts: %08x - this may be a problem with spi signal quality- try reducing spi-clock speed if this can get reproduced", + fifo, tef->id, tef->flags, tef->ts); + spin_unlock_irqrestore(&cpriv->fifos.tx_queue->lock, flags); + + /* now we can schedule the fifo for echo submission */ + mcp25xxfd_can_queue_frame(cpriv, fifo, tef->ts, false); + + /* increment the tef index with wraparound */ + cpriv->fifos.tef.index++; + if (cpriv->fifos.tef.index >= cpriv->fifos.tef.count) + cpriv->fifos.tef.index = 0; + + /* finally just increment the TEF pointer */ + return mcp25xxfd_cmd_write_mask(cpriv->priv->spi, MCP25XXFD_CAN_TEFCON, + MCP25XXFD_CAN_TEFCON_UINC, + MCP25XXFD_CAN_TEFCON_UINC); +} + +static int +mcp25xxfd_can_tx_handle_int_tefif_conservative(struct mcp25xxfd_can_priv *cpriv) +{ + u32 tefsta; + int ret; + + /* read the TEF status */ + ret = mcp25xxfd_cmd_read_mask(cpriv->priv->spi, MCP25XXFD_CAN_TEFSTA, + &tefsta, MCP25XXFD_CAN_TEFSTA_TEFNEIF); + if (ret) + return ret; + + /* read the tef in an inefficient loop */ + while (tefsta & MCP25XXFD_CAN_TEFSTA_TEFNEIF) { + /* read one tef */ + ret = mcp25xxfd_can_tx_handle_int_tefif_fifo(cpriv); + if (ret) + return ret; + + /* read the TEF status */ + ret = mcp25xxfd_cmd_read_mask(cpriv->priv->spi, + MCP25XXFD_CAN_TEFSTA, &tefsta, + MCP25XXFD_CAN_TEFSTA_TEFNEIF); + if (ret) + return ret; + } + + return 0; +} + +int mcp25xxfd_can_tx_handle_int_tefif(struct mcp25xxfd_can_priv *cpriv) +{ + unsigned long flags; + u32 finished; + + if (!(cpriv->status.intf & MCP25XXFD_CAN_INT_TEFIF)) + return 0; + + spin_lock_irqsave(&cpriv->fifos.tx_queue->lock, flags); + + /* compute finished fifos and clear them immediately */ + finished = (cpriv->fifos.tx_queue->in_can_transfer ^ + cpriv->status.txreq) & + cpriv->fifos.tx_queue->in_can_transfer; + + spin_unlock_irqrestore(&cpriv->fifos.tx_queue->lock, flags); + + return mcp25xxfd_can_tx_handle_int_tefif_conservative(cpriv); +} + +static +void mcp25xxfd_can_tx_fill_fifo_common(struct mcp25xxfd_can_priv *cpriv, + struct mcp25xxfd_tx_spi_message *smsg, + struct mcp25xxfd_can_obj_tx *tx, + int dlc, u8 *data) +{ + int len = can_dlc2len(dlc); + + /* add fifo number as seq */ + tx->flags |= smsg->fifo << MCP25XXFD_CAN_OBJ_FLAGS_SEQ_SHIFT; + + /* copy data to tx->data for future reference */ + memcpy(tx->data, data, len); + + /* transform header to controller format */ + mcp25xxfd_cmd_convert_from_cpu(&tx->id, sizeof(*tx) / sizeof(u32)); + + /* copy header + data to final location - we are not aligned */ + memcpy(smsg->fill_fifo.data.header, &tx->id, sizeof(*tx) + len); + + /* transfers to sram should be a multiple of 4 and be zero padded */ + for (; len & 3; len++) + *(smsg->fill_fifo.data.header + sizeof(*tx) + len) = 0; + + /* convert it back to CPU format */ + mcp25xxfd_cmd_convert_to_cpu(&tx->id, sizeof(*tx) / sizeof(u32)); + + /* set up size of transfer */ + smsg->fill_fifo.xfer.len = sizeof(smsg->fill_fifo.data.cmd) + + sizeof(smsg->fill_fifo.data.header) + len; +} + +static +void mcp25xxfd_can_tx_fill_fifo_fd(struct mcp25xxfd_can_priv *cpriv, + struct canfd_frame *frame, + struct mcp25xxfd_tx_spi_message *smsg, + struct mcp25xxfd_can_obj_tx *tx) +{ + int dlc = can_len2dlc(frame->len); + + /* compute can id */ + mcp25xxfd_can_id_to_mcp25xxfd(frame->can_id, &tx->id, &tx->flags); + + /* setup flags */ + tx->flags |= dlc << MCP25XXFD_CAN_OBJ_FLAGS_DLC_SHIFT; + tx->flags |= (frame->can_id & CAN_EFF_FLAG) ? + MCP25XXFD_CAN_OBJ_FLAGS_IDE : 0; + tx->flags |= (frame->can_id & CAN_RTR_FLAG) ? + MCP25XXFD_CAN_OBJ_FLAGS_RTR : 0; + if (frame->flags & CANFD_BRS) + tx->flags |= MCP25XXFD_CAN_OBJ_FLAGS_BRS; + + tx->flags |= (frame->flags & CANFD_ESI) ? + MCP25XXFD_CAN_OBJ_FLAGS_ESI : 0; + tx->flags |= MCP25XXFD_CAN_OBJ_FLAGS_FDF; + + /* and do common processing */ + mcp25xxfd_can_tx_fill_fifo_common(cpriv, smsg, tx, dlc, frame->data); +} + +static +void mcp25xxfd_can_tx_fill_fifo(struct mcp25xxfd_can_priv *cpriv, + struct can_frame *frame, + struct mcp25xxfd_tx_spi_message *smsg, + struct mcp25xxfd_can_obj_tx *tx) +{ + /* set frame to valid dlc */ + if (frame->can_dlc > 8) + frame->can_dlc = 8; + + /* compute can id */ + mcp25xxfd_can_id_to_mcp25xxfd(frame->can_id, &tx->id, &tx->flags); + + /* setup flags */ + tx->flags |= frame->can_dlc << MCP25XXFD_CAN_OBJ_FLAGS_DLC_SHIFT; + tx->flags |= (frame->can_id & CAN_EFF_FLAG) ? + MCP25XXFD_CAN_OBJ_FLAGS_IDE : 0; + tx->flags |= (frame->can_id & CAN_RTR_FLAG) ? + MCP25XXFD_CAN_OBJ_FLAGS_RTR : 0; + + /* and do common processing */ + mcp25xxfd_can_tx_fill_fifo_common(cpriv, smsg, tx, frame->can_dlc, + frame->data); +} + +static struct mcp25xxfd_tx_spi_message * +mcp25xxfd_can_tx_queue_get_next_fifo(struct mcp25xxfd_can_priv *cpriv) +{ + u32 state = MCP25XXFD_CAN_TX_QUEUE_STATE_RUNABLE; + struct mcp25xxfd_tx_spi_message_queue *q = cpriv->fifos.tx_queue; + struct mcp25xxfd_tx_spi_message *smsg; + unsigned long flags; + + spin_lock_irqsave(&q->lock, flags); + + /* get the first entry from idle */ + smsg = mcp25xxfd_can_tx_queue_first_spi_message(q, &q->idle); + if (!smsg) + goto out_busy; + + /* and move the fifo to next stage */ + mcp25xxfd_can_tx_queue_move_spi_message(&q->idle, + &q->in_fill_fifo_transfer, + smsg->fifo); + + /* if queue is empty then stop the network queue immediately */ + if (!q->idle) + mcp25xxfd_can_tx_queue_manage_nolock(cpriv, state); +out_busy: + spin_unlock_irqrestore(&q->lock, flags); + + return smsg; +} + +/* submit the can message to the can-bus */ +netdev_tx_t mcp25xxfd_can_tx_start_xmit(struct sk_buff *skb, + struct net_device *net) +{ + u32 state = MCP25XXFD_CAN_TX_QUEUE_STATE_STOPPED; + struct mcp25xxfd_can_priv *cpriv = netdev_priv(net); + struct mcp25xxfd_tx_spi_message_queue *q = cpriv->fifos.tx_queue; + struct mcp25xxfd_priv *priv = cpriv->priv; + struct spi_device *spi = priv->spi; + struct mcp25xxfd_tx_spi_message *smsg; + struct mcp25xxfd_can_obj_tx *tx; + unsigned long flags; + int ret; + + /* invalid skb we can ignore */ + if (can_dropped_invalid_skb(net, skb)) + return NETDEV_TX_OK; + + spin_lock_irqsave(&q->spi_lock, flags); + + /* get the fifo message structure to process now */ + smsg = mcp25xxfd_can_tx_queue_get_next_fifo(cpriv); + if (!smsg) + goto out_busy; + + /* compute the fifo in sram */ + tx = (struct mcp25xxfd_can_obj_tx *) + (cpriv->sram + cpriv->fifos.info[smsg->fifo].offset); + + /* fill in message from skb->data depending on can2.0 or canfd */ + if (can_is_canfd_skb(skb)) + mcp25xxfd_can_tx_fill_fifo_fd(cpriv, + (struct canfd_frame *)skb->data, + smsg, tx); + else + mcp25xxfd_can_tx_fill_fifo(cpriv, + (struct can_frame *)skb->data, + smsg, tx); + + /* submit the two messages asyncronously + * the reason why we separate transfers into two spi_messages is: + * * because the spi framework (currently) does add a 10us delay + * between 2 spi_transfers in a single spi_message when + * change_cs is set - 2 consecutive spi messages show a shorter + * cs disable phase increasing bus utilization + * (code reduction with a fix in spi core would be aprox.50 lines) + * * this allows the interrupt handler to start spi messages earlier + * so reducing latencies a bit and to allow for better concurrency + * * this separation - in the future - may get used to fill fifos + * early and reduce the delay on "rollover" + */ + ret = spi_async(spi, &smsg->fill_fifo.msg); + if (ret) + goto out_async_failed; + + ret = spi_async(spi, &smsg->trigger_fifo.msg); + if (ret) + goto out_async_failed; + + spin_unlock_irqrestore(&q->spi_lock, flags); + + can_put_echo_skb(skb, net, smsg->fifo); + + return NETDEV_TX_OK; + +out_async_failed: + netdev_err(net, "spi_async submission of fifo %i failed - %i\n", + smsg->fifo, ret); + +out_busy: + mcp25xxfd_can_tx_queue_manage_nolock(cpriv, state); + spin_unlock_irqrestore(&q->spi_lock, flags); + + return NETDEV_TX_BUSY; +} + +/* submit the fifo back to the network stack */ +int mcp25xxfd_can_tx_submit_frame(struct mcp25xxfd_can_priv *cpriv, int fifo) +{ + struct mcp25xxfd_tx_spi_message_queue *q = cpriv->fifos.tx_queue; + struct mcp25xxfd_can_obj_tx *tx = (struct mcp25xxfd_can_obj_tx *) + (cpriv->sram + cpriv->fifos.info[fifo].offset); + int dlc = (tx->flags & MCP25XXFD_CAN_OBJ_FLAGS_DLC_MASK) >> + MCP25XXFD_CAN_OBJ_FLAGS_DLC_SHIFT; + unsigned long flags; + + /* update counters */ + cpriv->can.dev->stats.tx_packets++; + cpriv->can.dev->stats.tx_bytes += can_dlc2len(dlc); + + spin_lock_irqsave(&cpriv->fifos.tx_queue->lock, flags); + + /* release the echo buffer */ + can_get_echo_skb(cpriv->can.dev, fifo); + + /* move from in_can_transfer to transferred */ + mcp25xxfd_can_tx_queue_move_spi_message(&q->in_can_transfer, + &q->transferred, fifo); + + spin_unlock_irqrestore(&cpriv->fifos.tx_queue->lock, flags); + + return 0; +} + +static int +mcp25xxfd_can_tx_handle_int_txatif_fifo(struct mcp25xxfd_can_priv *cpriv, + int fifo) +{ + struct mcp25xxfd_tx_spi_message_queue *q = cpriv->fifos.tx_queue; + unsigned long flags; + u32 val; + int ret; + + ret = mcp25xxfd_cmd_read(cpriv->priv->spi, + MCP25XXFD_CAN_FIFOSTA(fifo), &val); + if (ret) + return ret; + + ret = mcp25xxfd_cmd_write_mask(cpriv->priv->spi, + MCP25XXFD_CAN_FIFOSTA(fifo), 0, + MCP25XXFD_CAN_FIFOSTA_TXABT | + MCP25XXFD_CAN_FIFOSTA_TXLARB | + MCP25XXFD_CAN_FIFOSTA_TXERR | + MCP25XXFD_CAN_FIFOSTA_TXATIF); + if (ret) + return ret; + + spin_lock_irqsave(&q->lock, flags); + + can_get_echo_skb(cpriv->can.dev, fifo); + mcp25xxfd_can_tx_queue_move_spi_message(&q->in_can_transfer, + &q->transferred, fifo); + + spin_unlock_irqrestore(&q->lock, flags); + + cpriv->status.txif &= ~BIT(fifo); + cpriv->can.dev->stats.tx_aborted_errors++; + + return 0; +} + +int mcp25xxfd_can_tx_handle_int_txatif(struct mcp25xxfd_can_priv *cpriv) +{ + int i, f, ret; + + if (!cpriv->status.txatif) + return 0; + + /* process all the fifos with txatif flag set */ + for (i = 0, f = cpriv->fifos.tx.start; i < cpriv->fifos.tx.count; + i++, f++) { + if (cpriv->status.txatif & BIT(f)) { + ret = mcp25xxfd_can_tx_handle_int_txatif_fifo(cpriv, f); + if (ret) + return ret; + } + } + + return 0; +} + +int mcp25xxfd_can_tx_queue_alloc(struct mcp25xxfd_can_priv *cpriv) +{ + struct mcp25xxfd_tx_spi_message *msg; + size_t size = sizeof(struct mcp25xxfd_tx_spi_message_queue) + + cpriv->fifos.tx.count * sizeof(*msg); + int i, f; + + cpriv->fifos.tx_queue = kzalloc(size, GFP_KERNEL); + if (!cpriv->fifos.tx_queue) + return -ENOMEM; + + spin_lock_init(&cpriv->fifos.tx_queue->lock); + spin_lock_init(&cpriv->fifos.tx_queue->spi_lock); + + /* initialize the individual spi_message structures */ + for (i = 0, f = cpriv->fifos.tx.start; i < cpriv->fifos.tx.count; + i++, f++) { + msg = &cpriv->fifos.tx_queue->message[i]; + cpriv->fifos.tx_queue->fifo2message[f] = msg; + mcp25xxfd_can_tx_message_init(cpriv, msg, f); + } + + return 0; +} + +void mcp25xxfd_can_tx_queue_free(struct mcp25xxfd_can_priv *cpriv) +{ + kfree(cpriv->fifos.tx_queue); + cpriv->fifos.tx_queue = NULL; +} diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_tx.h b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_tx.h new file mode 100644 index 000000000000..1947b3420d58 --- /dev/null +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_tx.h @@ -0,0 +1,86 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface + * + * Copyright 2019 Martin Sperl + */ + +#ifndef __MCP25XXFD_CAN_TX_H +#define __MCP25XXFD_CAN_TX_H + +#include +#include + +#include "mcp25xxfd_can_priv.h" + +/* structure of a spi message that is prepared and can get submitted quickly */ +struct mcp25xxfd_tx_spi_message { + /* the network device this is related to */ + struct mcp25xxfd_can_priv *cpriv; + /* the fifo this fills */ + u32 fifo; + /* the xfer to fill in the fifo data */ + struct { + struct spi_message msg; + struct spi_transfer xfer; + struct { + u8 cmd[2]; + u8 header[sizeof(struct mcp25xxfd_can_obj_tx)]; + u8 data[64]; + } data; + } fill_fifo; + /* the xfer to enable transmission on the can bus */ + struct { + struct spi_message msg; + struct spi_transfer xfer; + struct { + u8 cmd[2]; + u8 data; + } data; + } trigger_fifo; +}; + +struct mcp25xxfd_tx_spi_message_queue { + /* spinlock protecting the bitmaps + * as well as state and the skb_echo_* functions + */ + spinlock_t lock; + /* bitmap of which fifo is in which stage */ + u32 idle; + u32 in_fill_fifo_transfer; + u32 in_trigger_fifo_transfer; + u32 in_can_transfer; + u32 transferred; + + /* the queue state as seen per controller */ + int state; +#define MCP25XXFD_CAN_TX_QUEUE_STATE_STOPPED 0 +#define MCP25XXFD_CAN_TX_QUEUE_STATE_STARTED 1 +#define MCP25XXFD_CAN_TX_QUEUE_STATE_RUNABLE 2 +#define MCP25XXFD_CAN_TX_QUEUE_STATE_RESTART 3 + + /* spinlock protecting spi submission order */ + spinlock_t spi_lock; + + /* map each fifo to a mcp25xxfd_tx_spi_message */ + struct mcp25xxfd_tx_spi_message *fifo2message[32]; + + /* the individual messages */ + struct mcp25xxfd_tx_spi_message message[]; +}; + +int mcp25xxfd_can_tx_submit_frame(struct mcp25xxfd_can_priv *cpriv, int fifo); +void mcp25xxfd_can_tx_queue_restart(struct mcp25xxfd_can_priv *cpriv); + +int mcp25xxfd_can_tx_handle_int_txatif(struct mcp25xxfd_can_priv *cpriv); +int mcp25xxfd_can_tx_handle_int_tefif(struct mcp25xxfd_can_priv *cpriv); + +netdev_tx_t mcp25xxfd_can_tx_start_xmit(struct sk_buff *skb, + struct net_device *net); + +void mcp25xxfd_can_tx_queue_manage(struct mcp25xxfd_can_priv *cpriv, int state); + +int mcp25xxfd_can_tx_queue_alloc(struct mcp25xxfd_can_priv *cpriv); +void mcp25xxfd_can_tx_queue_free(struct mcp25xxfd_can_priv *cpriv); + +#endif /* __MCP25XXFD_CAN_TX_H */ From patchwork Wed Jun 10 07:44:41 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Manivannan Sadhasivam X-Patchwork-Id: 187733 Delivered-To: patch@linaro.org Received: by 2002:a92:cf06:0:0:0:0:0 with SMTP id c6csp254259ilo; Wed, 10 Jun 2020 00:45:30 -0700 (PDT) X-Google-Smtp-Source: ABdhPJzlhJTGyOWw4fQ4DW7l4UpV9bmRkGPZt/qD8v+ZYLnJVfk8CQpbAUe55kgmnY2Asq2nQgy8 X-Received: by 2002:a17:907:9f0:: with SMTP id ce16mr2068395ejc.476.1591775130773; Wed, 10 Jun 2020 00:45:30 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1591775130; cv=none; d=google.com; s=arc-20160816; b=WNu/7o0EuC8PGxqOYFfW3vaNeZUE7cvAQLqOSFg4zHZrgnd6KBCY0PGGJuZvHDYxqB InW3VS8LJ6c9Yb5lK5HrcLsRMSIoaXu2HlwttxCAbUJqRSSvFk0JaRpaHsD8if2uNuAG Ruj7gD+CGa1JKEbbG11Lm2xk64gO5/L2IqlcfNFUVinxEdPRk2fYZQZLn7OMqWvoy7wQ yjmUACSqhZDmXGl0CdrOA0C4R+Ypi1uhwREdceHqsYnZ3laSstGYgF3gMTHT4JIBHFL/ K89Z93flC3lTtsX+HkQMK/NB86976JKkXNxLD5UXe3iMrPUdgbw9GOhosrNWbxoVyiWE 8sLg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:dkim-signature; bh=dSE9dqY0eKEnI3JM3RkevfVx7P9DxAySSIY6zGnKG0k=; b=HOkPfCUirjgVL6CRXCWBeNKBrcC0aaOcSAZyEJiRKu0/bABThShSmaZqVwXB76XJPe XHO0cdAfS0AefnGOoe4HmyKSvmn8qy1E29d/meJgR5V/Ga2+AGaRd2ACVHW1LSHYeeCI +N0jQcAzfHqEA+O8Hk0iC9KNArPFPE8BPS0jO347FWjpgFahQQWTwT2gSRKnyakgC1bS tTTv6Vlq4srhLFAoT7WGacBHtQClIQRJdixwnMgfGHPjWxTx3HUIeg4V2feFgnYnzP+Y +84FgTGMc79Z5azcLYB/it5qgwWcraosMS3B/z+edaVMoOzQOZsfSTGvnbIvAlGAuFKK VyHA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=WXzfKpRR; spf=pass (google.com: domain of netdev-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=netdev-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id n11si11263268ejh.543.2020.06.10.00.45.30; Wed, 10 Jun 2020 00:45:30 -0700 (PDT) Received-SPF: pass (google.com: domain of netdev-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=WXzfKpRR; spf=pass (google.com: domain of netdev-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=netdev-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726650AbgFJHp2 (ORCPT + 9 others); Wed, 10 Jun 2020 03:45:28 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:57346 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726627AbgFJHpS (ORCPT ); Wed, 10 Jun 2020 03:45:18 -0400 Received: from mail-pj1-x1041.google.com (mail-pj1-x1041.google.com [IPv6:2607:f8b0:4864:20::1041]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 94448C08C5C2 for ; Wed, 10 Jun 2020 00:45:18 -0700 (PDT) Received: by mail-pj1-x1041.google.com with SMTP id jz3so524633pjb.0 for ; Wed, 10 Jun 2020 00:45:18 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=dSE9dqY0eKEnI3JM3RkevfVx7P9DxAySSIY6zGnKG0k=; b=WXzfKpRRB+NVEcrx1DrXeFn4Zl0QZkDdxU//UBjGNAaCX+JQVGITtK+UGOBEoCUXIK myf8udUAJmwR/9W5QhgROJsAIQ3emSO1B/VNRSPChKLzT2h188lJKbMGa+mE+fk0V55+ 90JALIk6+qnfPhWOvhpZY6kPp/te71ZWukNxu2+zzKmF3sc0xj0+nFd8cYcdWs+/tEgv 0wRxswm3c+jDYZFIahRpQ77uyjSj87kH5OzsVodMBxQRLzcQjv02kH4YlPyAj+0O0QN6 3w4R02ldFEClJoNRIqcy60ArKH74VkIuJiAUUrR1vRbWef5r5f3OosL3rTBUkMktIN7v mAXg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=dSE9dqY0eKEnI3JM3RkevfVx7P9DxAySSIY6zGnKG0k=; b=U6IGxIPJsfVvemzzjSmezGA5U68eGeqax74rwskKK6U974lMJY8mYV0RNOaler5uXd gIltQwpxnF+9HS1qvbZfn+7iKnmO+o58T2MXa3cxzhodHvTM1tjw4VpcvoFwIycNCpnY AJOlqYGSPjYO/IApFe5gX14SvjbZwQdLL+NsfVPsMHvaMAlaA6ycI+aRo0M7CPROTb78 6vTYd4gMvjq6ABGZEyJLt+nVWH7g1tsVGaSTY59gWnrG3ntQnw7henbPO0MjvTOePS1r /kNBqWOL/vqcO37+CeLdKJw6FEAysUwU2CDlFVMi6Q7l8JGi9NTpUm3L5BXMmBKsG7KO bwrA== X-Gm-Message-State: AOAM532LCIoem8JwT8P0rxEM295lCwJRLhr0aKJ10wK8+ZUvXpO0xk3I 1/X1hXshJUrCVcBI+2tO1GB2 X-Received: by 2002:a17:90a:8a18:: with SMTP id w24mr1702381pjn.222.1591775118019; Wed, 10 Jun 2020 00:45:18 -0700 (PDT) Received: from Mani-XPS-13-9360.localdomain ([2409:4072:630f:1dba:c41:a14e:6586:388a]) by smtp.gmail.com with ESMTPSA id a20sm11516795pff.147.2020.06.10.00.45.14 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 10 Jun 2020 00:45:17 -0700 (PDT) From: Manivannan Sadhasivam To: wg@grandegger.com, mkl@pengutronix.de Cc: kernel@martin.sperl.org, linux-can@vger.kernel.org, netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Manivannan Sadhasivam Subject: [PATCH 5/6] can: mcp25xxfd: Optimize TEF read by avoiding unnecessary SPI transfers Date: Wed, 10 Jun 2020 13:14:41 +0530 Message-Id: <20200610074442.10808-6-manivannan.sadhasivam@linaro.org> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200610074442.10808-1-manivannan.sadhasivam@linaro.org> References: <20200610074442.10808-1-manivannan.sadhasivam@linaro.org> Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org From: Martin Sperl We have already enough information to know how many tx-messages have been terminated so that we do not have to query TEF every time if there is anything pending but we can read the tefs blindly. This avoids 1 SPI transfer per TEF read. Signed-off-by: Martin Sperl Signed-off-by: Manivannan Sadhasivam --- .../net/can/spi/mcp25xxfd/mcp25xxfd_can_tx.c | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) -- 2.17.1 diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_tx.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_tx.c index 5ea1e525e776..6f066cb95844 100644 --- a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_tx.c +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_tx.c @@ -263,6 +263,13 @@ int mcp25xxfd_can_tx_handle_int_tefif_fifo(struct mcp25xxfd_can_priv *cpriv) MCP25XXFD_CAN_TEFCON_UINC); } +/* reading TEF entries can be made even more efficient by reading + * multiple TEF entries in one go. + * Under the assumption that we have count(TEF) >= count(TX_FIFO) + * we can even release TEFs early (before we read them) + * (and potentially restarting the transmit-queue early aswell) + */ + static int mcp25xxfd_can_tx_handle_int_tefif_conservative(struct mcp25xxfd_can_priv *cpriv) { @@ -293,6 +300,25 @@ mcp25xxfd_can_tx_handle_int_tefif_conservative(struct mcp25xxfd_can_priv *cpriv) return 0; } +static int +mcp25xxfd_can_tx_handle_int_tefif_optimized(struct mcp25xxfd_can_priv *cpriv, + u32 finished) +{ + int i, fifo, ret; + + /* now iterate those */ + for (i = 0, fifo = cpriv->fifos.tx.start; i < cpriv->fifos.tx.count; + i++, fifo++) { + if (finished & BIT(fifo)) { + ret = mcp25xxfd_can_tx_handle_int_tefif_fifo(cpriv); + if (ret) + return ret; + } + } + + return 0; +} + int mcp25xxfd_can_tx_handle_int_tefif(struct mcp25xxfd_can_priv *cpriv) { unsigned long flags; @@ -310,6 +336,13 @@ int mcp25xxfd_can_tx_handle_int_tefif(struct mcp25xxfd_can_priv *cpriv) spin_unlock_irqrestore(&cpriv->fifos.tx_queue->lock, flags); + /* run in optimized mode if possible */ + if (finished) + return mcp25xxfd_can_tx_handle_int_tefif_optimized(cpriv, + finished); + /* otherwise play it safe */ + netdev_warn(cpriv->can.dev, + "Something is wrong - we got a TEF interrupt but we were not able to detect a finished fifo\n"); return mcp25xxfd_can_tx_handle_int_tefif_conservative(cpriv); } From patchwork Wed Jun 10 07:44:42 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Manivannan Sadhasivam X-Patchwork-Id: 187734 Delivered-To: patch@linaro.org Received: by 2002:a92:cf06:0:0:0:0:0 with SMTP id c6csp254275ilo; Wed, 10 Jun 2020 00:45:33 -0700 (PDT) X-Google-Smtp-Source: ABdhPJzYwdk/dqb63a+d4CeNt0DMCj+0oIqqdfaIxPFF4ezRcczipKauSBJ9jBruDA956WD9qaG6 X-Received: by 2002:a17:907:20ee:: with SMTP id rh14mr2168899ejb.395.1591775132965; Wed, 10 Jun 2020 00:45:32 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1591775132; cv=none; d=google.com; s=arc-20160816; b=tKz66sjALbgHgEYPfisbKa1xHuW5AjG7oWlrgaLKjsvpajQg6jvMWFS1Z9RvkA5k8v QOMp9bbdkHdwByX/GSRvPqNlTtK9jRTq1bMX9hR1A4Khr/9hiYNOEOjtLUsNtz7pi2qR 39PlA4XBFd5wBmweLwypr0329phk9cxNIb6kqm4UQY0IUrxMOvxegPuLNHYIOzAGRo51 WtTHh8LeKy290hB2uITxCePCjRGWM/Cr9e5QsPtQtER+h1QV+2dDAzwAWHTsws29FVsr T7vGeifq/vWhN/vwpN0Opi2spXlRTzzSA0QIGjcbN3XoahNyyrwM11uEkuiRRn3RRiE0 3uLQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:dkim-signature; bh=mBmy6hb5a23KMKQCkK27hu5NhYaoTF5cRzQ7jlandjM=; b=jmyckoDWPcIbk0Orpza9+BaEUKKBDM07dRJuZNaoguVXG0//dtA6SU8FXOthSWZCqM b0nl0ThiM+14HuZT7L3ocq6kqKMsQbsvkQEszMMtaEHD2D8VlkSfD965S/LG3gO4wSby FCbWhnR5mvy9qnNKqkUR6oqFked6/THkTUbxcKDft+dHdmMI3dUo5NO1DOqlhAWxBBSn tTl/NThuilBXJLFc2LWlnZ3xdc0FKBMrBI0bvvJ+Klm/zhW6mXd2uuDtjW6T5AylSAKT JWVr9iWHL5CK4K064EimwaosUMHS7r8tjKra3qMPaPjMaZ4bKGzqtKQErO4ziOyUj8hb S99g== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=ozHbKmHv; spf=pass (google.com: domain of netdev-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=netdev-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id n11si11263268ejh.543.2020.06.10.00.45.32; Wed, 10 Jun 2020 00:45:32 -0700 (PDT) Received-SPF: pass (google.com: domain of netdev-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=ozHbKmHv; spf=pass (google.com: domain of netdev-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=netdev-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726663AbgFJHpb (ORCPT + 9 others); Wed, 10 Jun 2020 03:45:31 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:57378 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726628AbgFJHpZ (ORCPT ); Wed, 10 Jun 2020 03:45:25 -0400 Received: from mail-pl1-x641.google.com (mail-pl1-x641.google.com [IPv6:2607:f8b0:4864:20::641]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 595FBC08C5C7 for ; Wed, 10 Jun 2020 00:45:23 -0700 (PDT) Received: by mail-pl1-x641.google.com with SMTP id t16so642397plo.7 for ; Wed, 10 Jun 2020 00:45:23 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=mBmy6hb5a23KMKQCkK27hu5NhYaoTF5cRzQ7jlandjM=; b=ozHbKmHvb28+iRGd9myPIYtpesyrfnme4ad6GKCdJ1tYawHaW7VZxstkzgovVgeT5W ICdxDBw/wLmuBAnI/nc7Ue9csPpFKwUczWrf8WKtIJ488USHOc+Nd1F3Znw2AAedAhN7 0GGM6m0rBqL09iYDtuKC4LEaqhpBJ+IwkATu/ORI8r97eW4PAt3gQe0GDXJFzP/x0EnV pktPdj7OGduVeFfJ+0Rzyv13UZOxaieWfWFX40ptaFfPUJ14cEWdnQhg7Um7PPp0ft5u TY29UJo8hFOtxn2OAauM88TGGiwhvhdT+CoqUFb+OAIZDo7r7Oph9bFRTkxwb3HWJlaE g64w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=mBmy6hb5a23KMKQCkK27hu5NhYaoTF5cRzQ7jlandjM=; b=hvmaOmz+c1NvyoMegZ12RLgmG3m7ZNB4WAJg9DAjVuqPYCgQum2cGpBPmC7pZhQIUR w/RcCbmaZCsoXW1r/M0DXvpl6a49oIJ3RH/MsL631qMD1fxhf1C6VpipkZ6Dc8Tjt+PH LH6+uX2l3OpSLlBjzMms2mw/0Kue16PVz9h5ZfXz+oP9BWjMVINJWPWkiANEDV/QI1DM qqIof/hWdabiuVfBXnyaJcf4o4BvCw+ud3zGaH7s67W2nOVDkXTrcRbPpArW6ZC9cAWO rqhTGwEAeuiZgt6oqxnSa4UFlNMfcdu+Kpx+3pD5FczvysYaplpbcr89trzGUy+bznbK oTMA== X-Gm-Message-State: AOAM531eoiGnwNcu6mwHaWmHrpYMQ0SOVqTQaLTmoXQeksnhFaj+PcWR fchfOpeUqytK01prfiF08oqX X-Received: by 2002:a17:90a:3489:: with SMTP id p9mr1852483pjb.56.1591775122820; Wed, 10 Jun 2020 00:45:22 -0700 (PDT) Received: from Mani-XPS-13-9360.localdomain ([2409:4072:630f:1dba:c41:a14e:6586:388a]) by smtp.gmail.com with ESMTPSA id a20sm11516795pff.147.2020.06.10.00.45.18 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 10 Jun 2020 00:45:22 -0700 (PDT) From: Manivannan Sadhasivam To: wg@grandegger.com, mkl@pengutronix.de Cc: kernel@martin.sperl.org, linux-can@vger.kernel.org, netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Manivannan Sadhasivam Subject: [PATCH 6/6] MAINTAINERS: Add entry for Microchip MCP25XXFD CAN network driver Date: Wed, 10 Jun 2020 13:14:42 +0530 Message-Id: <20200610074442.10808-7-manivannan.sadhasivam@linaro.org> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200610074442.10808-1-manivannan.sadhasivam@linaro.org> References: <20200610074442.10808-1-manivannan.sadhasivam@linaro.org> Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Add MAINTAINERS entry for Microchip MCP25XXFD CAN network driver. Signed-off-by: Manivannan Sadhasivam --- MAINTAINERS | 8 ++++++++ 1 file changed, 8 insertions(+) -- 2.17.1 diff --git a/MAINTAINERS b/MAINTAINERS index b816a453b10e..591b6fc2d83a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -10360,6 +10360,14 @@ L: linux-input@vger.kernel.org S: Maintained F: drivers/hid/hid-mcp2221.c +MCP25XXFD CAN NETWORK DRIVER +M: Martin Sperl +M: Manivannan Sadhasivam +L: linux-can@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/net/can/microchip,mcp25xxfd.yaml +F: drivers/net/can/mcp25xxfd/ + MCP4018 AND MCP4531 MICROCHIP DIGITAL POTENTIOMETER DRIVERS M: Peter Rosin L: linux-iio@vger.kernel.org