162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * sun4i_can.c - CAN bus controller driver for Allwinner SUN4I&SUN7I based SoCs 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2013 Peter Chen 562306a36Sopenharmony_ci * Copyright (C) 2015 Gerhard Bertelsmann 662306a36Sopenharmony_ci * All rights reserved. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Parts of this software are based on (derived from) the SJA1000 code by: 962306a36Sopenharmony_ci * Copyright (C) 2014 Oliver Hartkopp <oliver.hartkopp@volkswagen.de> 1062306a36Sopenharmony_ci * Copyright (C) 2007 Wolfgang Grandegger <wg@grandegger.com> 1162306a36Sopenharmony_ci * Copyright (C) 2002-2007 Volkswagen Group Electronic Research 1262306a36Sopenharmony_ci * Copyright (C) 2003 Matthias Brukner, Trajet Gmbh, Rebenring 33, 1362306a36Sopenharmony_ci * 38106 Braunschweig, GERMANY 1462306a36Sopenharmony_ci * 1562306a36Sopenharmony_ci * Redistribution and use in source and binary forms, with or without 1662306a36Sopenharmony_ci * modification, are permitted provided that the following conditions 1762306a36Sopenharmony_ci * are met: 1862306a36Sopenharmony_ci * 1. Redistributions of source code must retain the above copyright 1962306a36Sopenharmony_ci * notice, this list of conditions and the following disclaimer. 2062306a36Sopenharmony_ci * 2. Redistributions in binary form must reproduce the above copyright 2162306a36Sopenharmony_ci * notice, this list of conditions and the following disclaimer in the 2262306a36Sopenharmony_ci * documentation and/or other materials provided with the distribution. 2362306a36Sopenharmony_ci * 3. Neither the name of Volkswagen nor the names of its contributors 2462306a36Sopenharmony_ci * may be used to endorse or promote products derived from this software 2562306a36Sopenharmony_ci * without specific prior written permission. 2662306a36Sopenharmony_ci * 2762306a36Sopenharmony_ci * Alternatively, provided that this notice is retained in full, this 2862306a36Sopenharmony_ci * software may be distributed under the terms of the GNU General 2962306a36Sopenharmony_ci * Public License ("GPL") version 2, in which case the provisions of the 3062306a36Sopenharmony_ci * GPL apply INSTEAD OF those given above. 3162306a36Sopenharmony_ci * 3262306a36Sopenharmony_ci * The provided data structures and external interfaces from this code 3362306a36Sopenharmony_ci * are not restricted to be used by modules with a GPL compatible license. 3462306a36Sopenharmony_ci * 3562306a36Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 3662306a36Sopenharmony_ci * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 3762306a36Sopenharmony_ci * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 3862306a36Sopenharmony_ci * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 3962306a36Sopenharmony_ci * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 4062306a36Sopenharmony_ci * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 4162306a36Sopenharmony_ci * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 4262306a36Sopenharmony_ci * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 4362306a36Sopenharmony_ci * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 4462306a36Sopenharmony_ci * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 4562306a36Sopenharmony_ci * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 4662306a36Sopenharmony_ci * DAMAGE. 4762306a36Sopenharmony_ci * 4862306a36Sopenharmony_ci */ 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci#include <linux/netdevice.h> 5162306a36Sopenharmony_ci#include <linux/can.h> 5262306a36Sopenharmony_ci#include <linux/can/dev.h> 5362306a36Sopenharmony_ci#include <linux/can/error.h> 5462306a36Sopenharmony_ci#include <linux/clk.h> 5562306a36Sopenharmony_ci#include <linux/delay.h> 5662306a36Sopenharmony_ci#include <linux/ethtool.h> 5762306a36Sopenharmony_ci#include <linux/interrupt.h> 5862306a36Sopenharmony_ci#include <linux/init.h> 5962306a36Sopenharmony_ci#include <linux/io.h> 6062306a36Sopenharmony_ci#include <linux/module.h> 6162306a36Sopenharmony_ci#include <linux/of.h> 6262306a36Sopenharmony_ci#include <linux/platform_device.h> 6362306a36Sopenharmony_ci#include <linux/reset.h> 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci#define DRV_NAME "sun4i_can" 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci/* Registers address (physical base address 0x01C2BC00) */ 6862306a36Sopenharmony_ci#define SUN4I_REG_MSEL_ADDR 0x0000 /* CAN Mode Select */ 6962306a36Sopenharmony_ci#define SUN4I_REG_CMD_ADDR 0x0004 /* CAN Command */ 7062306a36Sopenharmony_ci#define SUN4I_REG_STA_ADDR 0x0008 /* CAN Status */ 7162306a36Sopenharmony_ci#define SUN4I_REG_INT_ADDR 0x000c /* CAN Interrupt Flag */ 7262306a36Sopenharmony_ci#define SUN4I_REG_INTEN_ADDR 0x0010 /* CAN Interrupt Enable */ 7362306a36Sopenharmony_ci#define SUN4I_REG_BTIME_ADDR 0x0014 /* CAN Bus Timing 0 */ 7462306a36Sopenharmony_ci#define SUN4I_REG_TEWL_ADDR 0x0018 /* CAN Tx Error Warning Limit */ 7562306a36Sopenharmony_ci#define SUN4I_REG_ERRC_ADDR 0x001c /* CAN Error Counter */ 7662306a36Sopenharmony_ci#define SUN4I_REG_RMCNT_ADDR 0x0020 /* CAN Receive Message Counter */ 7762306a36Sopenharmony_ci#define SUN4I_REG_RBUFSA_ADDR 0x0024 /* CAN Receive Buffer Start Address */ 7862306a36Sopenharmony_ci#define SUN4I_REG_BUF0_ADDR 0x0040 /* CAN Tx/Rx Buffer 0 */ 7962306a36Sopenharmony_ci#define SUN4I_REG_BUF1_ADDR 0x0044 /* CAN Tx/Rx Buffer 1 */ 8062306a36Sopenharmony_ci#define SUN4I_REG_BUF2_ADDR 0x0048 /* CAN Tx/Rx Buffer 2 */ 8162306a36Sopenharmony_ci#define SUN4I_REG_BUF3_ADDR 0x004c /* CAN Tx/Rx Buffer 3 */ 8262306a36Sopenharmony_ci#define SUN4I_REG_BUF4_ADDR 0x0050 /* CAN Tx/Rx Buffer 4 */ 8362306a36Sopenharmony_ci#define SUN4I_REG_BUF5_ADDR 0x0054 /* CAN Tx/Rx Buffer 5 */ 8462306a36Sopenharmony_ci#define SUN4I_REG_BUF6_ADDR 0x0058 /* CAN Tx/Rx Buffer 6 */ 8562306a36Sopenharmony_ci#define SUN4I_REG_BUF7_ADDR 0x005c /* CAN Tx/Rx Buffer 7 */ 8662306a36Sopenharmony_ci#define SUN4I_REG_BUF8_ADDR 0x0060 /* CAN Tx/Rx Buffer 8 */ 8762306a36Sopenharmony_ci#define SUN4I_REG_BUF9_ADDR 0x0064 /* CAN Tx/Rx Buffer 9 */ 8862306a36Sopenharmony_ci#define SUN4I_REG_BUF10_ADDR 0x0068 /* CAN Tx/Rx Buffer 10 */ 8962306a36Sopenharmony_ci#define SUN4I_REG_BUF11_ADDR 0x006c /* CAN Tx/Rx Buffer 11 */ 9062306a36Sopenharmony_ci#define SUN4I_REG_BUF12_ADDR 0x0070 /* CAN Tx/Rx Buffer 12 */ 9162306a36Sopenharmony_ci#define SUN4I_REG_ACPC_ADDR 0x0040 /* CAN Acceptance Code 0 */ 9262306a36Sopenharmony_ci#define SUN4I_REG_ACPM_ADDR 0x0044 /* CAN Acceptance Mask 0 */ 9362306a36Sopenharmony_ci#define SUN4I_REG_ACPC_ADDR_D1 0x0028 /* CAN Acceptance Code 0 on the D1 */ 9462306a36Sopenharmony_ci#define SUN4I_REG_ACPM_ADDR_D1 0x002C /* CAN Acceptance Mask 0 on the D1 */ 9562306a36Sopenharmony_ci#define SUN4I_REG_RBUF_RBACK_START_ADDR 0x0180 /* CAN transmit buffer start */ 9662306a36Sopenharmony_ci#define SUN4I_REG_RBUF_RBACK_END_ADDR 0x01b0 /* CAN transmit buffer end */ 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci/* Controller Register Description */ 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci/* mode select register (r/w) 10162306a36Sopenharmony_ci * offset:0x0000 default:0x0000_0001 10262306a36Sopenharmony_ci */ 10362306a36Sopenharmony_ci#define SUN4I_MSEL_SLEEP_MODE (0x01 << 4) /* write in reset mode */ 10462306a36Sopenharmony_ci#define SUN4I_MSEL_WAKE_UP (0x00 << 4) 10562306a36Sopenharmony_ci#define SUN4I_MSEL_SINGLE_FILTER (0x01 << 3) /* write in reset mode */ 10662306a36Sopenharmony_ci#define SUN4I_MSEL_DUAL_FILTERS (0x00 << 3) 10762306a36Sopenharmony_ci#define SUN4I_MSEL_LOOPBACK_MODE BIT(2) 10862306a36Sopenharmony_ci#define SUN4I_MSEL_LISTEN_ONLY_MODE BIT(1) 10962306a36Sopenharmony_ci#define SUN4I_MSEL_RESET_MODE BIT(0) 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci/* command register (w) 11262306a36Sopenharmony_ci * offset:0x0004 default:0x0000_0000 11362306a36Sopenharmony_ci */ 11462306a36Sopenharmony_ci#define SUN4I_CMD_BUS_OFF_REQ BIT(5) 11562306a36Sopenharmony_ci#define SUN4I_CMD_SELF_RCV_REQ BIT(4) 11662306a36Sopenharmony_ci#define SUN4I_CMD_CLEAR_OR_FLAG BIT(3) 11762306a36Sopenharmony_ci#define SUN4I_CMD_RELEASE_RBUF BIT(2) 11862306a36Sopenharmony_ci#define SUN4I_CMD_ABORT_REQ BIT(1) 11962306a36Sopenharmony_ci#define SUN4I_CMD_TRANS_REQ BIT(0) 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci/* status register (r) 12262306a36Sopenharmony_ci * offset:0x0008 default:0x0000_003c 12362306a36Sopenharmony_ci */ 12462306a36Sopenharmony_ci#define SUN4I_STA_BIT_ERR (0x00 << 22) 12562306a36Sopenharmony_ci#define SUN4I_STA_FORM_ERR (0x01 << 22) 12662306a36Sopenharmony_ci#define SUN4I_STA_STUFF_ERR (0x02 << 22) 12762306a36Sopenharmony_ci#define SUN4I_STA_OTHER_ERR (0x03 << 22) 12862306a36Sopenharmony_ci#define SUN4I_STA_MASK_ERR (0x03 << 22) 12962306a36Sopenharmony_ci#define SUN4I_STA_ERR_DIR BIT(21) 13062306a36Sopenharmony_ci#define SUN4I_STA_ERR_SEG_CODE (0x1f << 16) 13162306a36Sopenharmony_ci#define SUN4I_STA_START (0x03 << 16) 13262306a36Sopenharmony_ci#define SUN4I_STA_ID28_21 (0x02 << 16) 13362306a36Sopenharmony_ci#define SUN4I_STA_ID20_18 (0x06 << 16) 13462306a36Sopenharmony_ci#define SUN4I_STA_SRTR (0x04 << 16) 13562306a36Sopenharmony_ci#define SUN4I_STA_IDE (0x05 << 16) 13662306a36Sopenharmony_ci#define SUN4I_STA_ID17_13 (0x07 << 16) 13762306a36Sopenharmony_ci#define SUN4I_STA_ID12_5 (0x0f << 16) 13862306a36Sopenharmony_ci#define SUN4I_STA_ID4_0 (0x0e << 16) 13962306a36Sopenharmony_ci#define SUN4I_STA_RTR (0x0c << 16) 14062306a36Sopenharmony_ci#define SUN4I_STA_RB1 (0x0d << 16) 14162306a36Sopenharmony_ci#define SUN4I_STA_RB0 (0x09 << 16) 14262306a36Sopenharmony_ci#define SUN4I_STA_DLEN (0x0b << 16) 14362306a36Sopenharmony_ci#define SUN4I_STA_DATA_FIELD (0x0a << 16) 14462306a36Sopenharmony_ci#define SUN4I_STA_CRC_SEQUENCE (0x08 << 16) 14562306a36Sopenharmony_ci#define SUN4I_STA_CRC_DELIMITER (0x18 << 16) 14662306a36Sopenharmony_ci#define SUN4I_STA_ACK (0x19 << 16) 14762306a36Sopenharmony_ci#define SUN4I_STA_ACK_DELIMITER (0x1b << 16) 14862306a36Sopenharmony_ci#define SUN4I_STA_END (0x1a << 16) 14962306a36Sopenharmony_ci#define SUN4I_STA_INTERMISSION (0x12 << 16) 15062306a36Sopenharmony_ci#define SUN4I_STA_ACTIVE_ERROR (0x11 << 16) 15162306a36Sopenharmony_ci#define SUN4I_STA_PASSIVE_ERROR (0x16 << 16) 15262306a36Sopenharmony_ci#define SUN4I_STA_TOLERATE_DOMINANT_BITS (0x13 << 16) 15362306a36Sopenharmony_ci#define SUN4I_STA_ERROR_DELIMITER (0x17 << 16) 15462306a36Sopenharmony_ci#define SUN4I_STA_OVERLOAD (0x1c << 16) 15562306a36Sopenharmony_ci#define SUN4I_STA_BUS_OFF BIT(7) 15662306a36Sopenharmony_ci#define SUN4I_STA_ERR_STA BIT(6) 15762306a36Sopenharmony_ci#define SUN4I_STA_TRANS_BUSY BIT(5) 15862306a36Sopenharmony_ci#define SUN4I_STA_RCV_BUSY BIT(4) 15962306a36Sopenharmony_ci#define SUN4I_STA_TRANS_OVER BIT(3) 16062306a36Sopenharmony_ci#define SUN4I_STA_TBUF_RDY BIT(2) 16162306a36Sopenharmony_ci#define SUN4I_STA_DATA_ORUN BIT(1) 16262306a36Sopenharmony_ci#define SUN4I_STA_RBUF_RDY BIT(0) 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci/* interrupt register (r) 16562306a36Sopenharmony_ci * offset:0x000c default:0x0000_0000 16662306a36Sopenharmony_ci */ 16762306a36Sopenharmony_ci#define SUN4I_INT_BUS_ERR BIT(7) 16862306a36Sopenharmony_ci#define SUN4I_INT_ARB_LOST BIT(6) 16962306a36Sopenharmony_ci#define SUN4I_INT_ERR_PASSIVE BIT(5) 17062306a36Sopenharmony_ci#define SUN4I_INT_WAKEUP BIT(4) 17162306a36Sopenharmony_ci#define SUN4I_INT_DATA_OR BIT(3) 17262306a36Sopenharmony_ci#define SUN4I_INT_ERR_WRN BIT(2) 17362306a36Sopenharmony_ci#define SUN4I_INT_TBUF_VLD BIT(1) 17462306a36Sopenharmony_ci#define SUN4I_INT_RBUF_VLD BIT(0) 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci/* interrupt enable register (r/w) 17762306a36Sopenharmony_ci * offset:0x0010 default:0x0000_0000 17862306a36Sopenharmony_ci */ 17962306a36Sopenharmony_ci#define SUN4I_INTEN_BERR BIT(7) 18062306a36Sopenharmony_ci#define SUN4I_INTEN_ARB_LOST BIT(6) 18162306a36Sopenharmony_ci#define SUN4I_INTEN_ERR_PASSIVE BIT(5) 18262306a36Sopenharmony_ci#define SUN4I_INTEN_WAKEUP BIT(4) 18362306a36Sopenharmony_ci#define SUN4I_INTEN_OR BIT(3) 18462306a36Sopenharmony_ci#define SUN4I_INTEN_ERR_WRN BIT(2) 18562306a36Sopenharmony_ci#define SUN4I_INTEN_TX BIT(1) 18662306a36Sopenharmony_ci#define SUN4I_INTEN_RX BIT(0) 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci/* error code */ 18962306a36Sopenharmony_ci#define SUN4I_ERR_INRCV (0x1 << 5) 19062306a36Sopenharmony_ci#define SUN4I_ERR_INTRANS (0x0 << 5) 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci/* filter mode */ 19362306a36Sopenharmony_ci#define SUN4I_FILTER_CLOSE 0 19462306a36Sopenharmony_ci#define SUN4I_SINGLE_FLTER_MODE 1 19562306a36Sopenharmony_ci#define SUN4I_DUAL_FILTER_MODE 2 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci/* message buffer flags */ 19862306a36Sopenharmony_ci#define SUN4I_MSG_EFF_FLAG BIT(7) 19962306a36Sopenharmony_ci#define SUN4I_MSG_RTR_FLAG BIT(6) 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci/* max. number of interrupts handled in ISR */ 20262306a36Sopenharmony_ci#define SUN4I_CAN_MAX_IRQ 20 20362306a36Sopenharmony_ci#define SUN4I_MODE_MAX_RETRIES 100 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci/** 20662306a36Sopenharmony_ci * struct sun4ican_quirks - Differences between SoC variants. 20762306a36Sopenharmony_ci * 20862306a36Sopenharmony_ci * @has_reset: SoC needs reset deasserted. 20962306a36Sopenharmony_ci * @acp_offset: Offset of ACPC and ACPM registers 21062306a36Sopenharmony_ci */ 21162306a36Sopenharmony_cistruct sun4ican_quirks { 21262306a36Sopenharmony_ci bool has_reset; 21362306a36Sopenharmony_ci int acp_offset; 21462306a36Sopenharmony_ci}; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_cistruct sun4ican_priv { 21762306a36Sopenharmony_ci struct can_priv can; 21862306a36Sopenharmony_ci void __iomem *base; 21962306a36Sopenharmony_ci struct clk *clk; 22062306a36Sopenharmony_ci struct reset_control *reset; 22162306a36Sopenharmony_ci spinlock_t cmdreg_lock; /* lock for concurrent cmd register writes */ 22262306a36Sopenharmony_ci int acp_offset; 22362306a36Sopenharmony_ci}; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_cistatic const struct can_bittiming_const sun4ican_bittiming_const = { 22662306a36Sopenharmony_ci .name = DRV_NAME, 22762306a36Sopenharmony_ci .tseg1_min = 1, 22862306a36Sopenharmony_ci .tseg1_max = 16, 22962306a36Sopenharmony_ci .tseg2_min = 1, 23062306a36Sopenharmony_ci .tseg2_max = 8, 23162306a36Sopenharmony_ci .sjw_max = 4, 23262306a36Sopenharmony_ci .brp_min = 1, 23362306a36Sopenharmony_ci .brp_max = 64, 23462306a36Sopenharmony_ci .brp_inc = 1, 23562306a36Sopenharmony_ci}; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistatic void sun4i_can_write_cmdreg(struct sun4ican_priv *priv, u8 val) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci unsigned long flags; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci spin_lock_irqsave(&priv->cmdreg_lock, flags); 24262306a36Sopenharmony_ci writel(val, priv->base + SUN4I_REG_CMD_ADDR); 24362306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->cmdreg_lock, flags); 24462306a36Sopenharmony_ci} 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic int set_normal_mode(struct net_device *dev) 24762306a36Sopenharmony_ci{ 24862306a36Sopenharmony_ci struct sun4ican_priv *priv = netdev_priv(dev); 24962306a36Sopenharmony_ci int retry = SUN4I_MODE_MAX_RETRIES; 25062306a36Sopenharmony_ci u32 mod_reg_val = 0; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci do { 25362306a36Sopenharmony_ci mod_reg_val = readl(priv->base + SUN4I_REG_MSEL_ADDR); 25462306a36Sopenharmony_ci mod_reg_val &= ~SUN4I_MSEL_RESET_MODE; 25562306a36Sopenharmony_ci writel(mod_reg_val, priv->base + SUN4I_REG_MSEL_ADDR); 25662306a36Sopenharmony_ci } while (retry-- && (mod_reg_val & SUN4I_MSEL_RESET_MODE)); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci if (readl(priv->base + SUN4I_REG_MSEL_ADDR) & SUN4I_MSEL_RESET_MODE) { 25962306a36Sopenharmony_ci netdev_err(dev, 26062306a36Sopenharmony_ci "setting controller into normal mode failed!\n"); 26162306a36Sopenharmony_ci return -ETIMEDOUT; 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci return 0; 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_cistatic int set_reset_mode(struct net_device *dev) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci struct sun4ican_priv *priv = netdev_priv(dev); 27062306a36Sopenharmony_ci int retry = SUN4I_MODE_MAX_RETRIES; 27162306a36Sopenharmony_ci u32 mod_reg_val = 0; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci do { 27462306a36Sopenharmony_ci mod_reg_val = readl(priv->base + SUN4I_REG_MSEL_ADDR); 27562306a36Sopenharmony_ci mod_reg_val |= SUN4I_MSEL_RESET_MODE; 27662306a36Sopenharmony_ci writel(mod_reg_val, priv->base + SUN4I_REG_MSEL_ADDR); 27762306a36Sopenharmony_ci } while (retry-- && !(mod_reg_val & SUN4I_MSEL_RESET_MODE)); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci if (!(readl(priv->base + SUN4I_REG_MSEL_ADDR) & 28062306a36Sopenharmony_ci SUN4I_MSEL_RESET_MODE)) { 28162306a36Sopenharmony_ci netdev_err(dev, "setting controller into reset mode failed!\n"); 28262306a36Sopenharmony_ci return -ETIMEDOUT; 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci return 0; 28662306a36Sopenharmony_ci} 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci/* bittiming is called in reset_mode only */ 28962306a36Sopenharmony_cistatic int sun4ican_set_bittiming(struct net_device *dev) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci struct sun4ican_priv *priv = netdev_priv(dev); 29262306a36Sopenharmony_ci struct can_bittiming *bt = &priv->can.bittiming; 29362306a36Sopenharmony_ci u32 cfg; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci cfg = ((bt->brp - 1) & 0x3FF) | 29662306a36Sopenharmony_ci (((bt->sjw - 1) & 0x3) << 14) | 29762306a36Sopenharmony_ci (((bt->prop_seg + bt->phase_seg1 - 1) & 0xf) << 16) | 29862306a36Sopenharmony_ci (((bt->phase_seg2 - 1) & 0x7) << 20); 29962306a36Sopenharmony_ci if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) 30062306a36Sopenharmony_ci cfg |= 0x800000; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci netdev_dbg(dev, "setting BITTIMING=0x%08x\n", cfg); 30362306a36Sopenharmony_ci writel(cfg, priv->base + SUN4I_REG_BTIME_ADDR); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci return 0; 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_cistatic int sun4ican_get_berr_counter(const struct net_device *dev, 30962306a36Sopenharmony_ci struct can_berr_counter *bec) 31062306a36Sopenharmony_ci{ 31162306a36Sopenharmony_ci struct sun4ican_priv *priv = netdev_priv(dev); 31262306a36Sopenharmony_ci u32 errors; 31362306a36Sopenharmony_ci int err; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci err = clk_prepare_enable(priv->clk); 31662306a36Sopenharmony_ci if (err) { 31762306a36Sopenharmony_ci netdev_err(dev, "could not enable clock\n"); 31862306a36Sopenharmony_ci return err; 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci errors = readl(priv->base + SUN4I_REG_ERRC_ADDR); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci bec->txerr = errors & 0xFF; 32462306a36Sopenharmony_ci bec->rxerr = (errors >> 16) & 0xFF; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci clk_disable_unprepare(priv->clk); 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci return 0; 32962306a36Sopenharmony_ci} 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_cistatic int sun4i_can_start(struct net_device *dev) 33262306a36Sopenharmony_ci{ 33362306a36Sopenharmony_ci struct sun4ican_priv *priv = netdev_priv(dev); 33462306a36Sopenharmony_ci int err; 33562306a36Sopenharmony_ci u32 mod_reg_val; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci /* we need to enter the reset mode */ 33862306a36Sopenharmony_ci err = set_reset_mode(dev); 33962306a36Sopenharmony_ci if (err) { 34062306a36Sopenharmony_ci netdev_err(dev, "could not enter reset mode\n"); 34162306a36Sopenharmony_ci return err; 34262306a36Sopenharmony_ci } 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci /* set filters - we accept all */ 34562306a36Sopenharmony_ci writel(0x00000000, priv->base + SUN4I_REG_ACPC_ADDR + priv->acp_offset); 34662306a36Sopenharmony_ci writel(0xFFFFFFFF, priv->base + SUN4I_REG_ACPM_ADDR + priv->acp_offset); 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci /* clear error counters and error code capture */ 34962306a36Sopenharmony_ci writel(0, priv->base + SUN4I_REG_ERRC_ADDR); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci /* enable interrupts */ 35262306a36Sopenharmony_ci if (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) 35362306a36Sopenharmony_ci writel(0xFF, priv->base + SUN4I_REG_INTEN_ADDR); 35462306a36Sopenharmony_ci else 35562306a36Sopenharmony_ci writel(0xFF & ~SUN4I_INTEN_BERR, 35662306a36Sopenharmony_ci priv->base + SUN4I_REG_INTEN_ADDR); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci /* enter the selected mode */ 35962306a36Sopenharmony_ci mod_reg_val = readl(priv->base + SUN4I_REG_MSEL_ADDR); 36062306a36Sopenharmony_ci if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) 36162306a36Sopenharmony_ci mod_reg_val |= SUN4I_MSEL_LOOPBACK_MODE; 36262306a36Sopenharmony_ci else if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) 36362306a36Sopenharmony_ci mod_reg_val |= SUN4I_MSEL_LISTEN_ONLY_MODE; 36462306a36Sopenharmony_ci writel(mod_reg_val, priv->base + SUN4I_REG_MSEL_ADDR); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci err = sun4ican_set_bittiming(dev); 36762306a36Sopenharmony_ci if (err) 36862306a36Sopenharmony_ci return err; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci /* we are ready to enter the normal mode */ 37162306a36Sopenharmony_ci err = set_normal_mode(dev); 37262306a36Sopenharmony_ci if (err) { 37362306a36Sopenharmony_ci netdev_err(dev, "could not enter normal mode\n"); 37462306a36Sopenharmony_ci return err; 37562306a36Sopenharmony_ci } 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci priv->can.state = CAN_STATE_ERROR_ACTIVE; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci return 0; 38062306a36Sopenharmony_ci} 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_cistatic int sun4i_can_stop(struct net_device *dev) 38362306a36Sopenharmony_ci{ 38462306a36Sopenharmony_ci struct sun4ican_priv *priv = netdev_priv(dev); 38562306a36Sopenharmony_ci int err; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci priv->can.state = CAN_STATE_STOPPED; 38862306a36Sopenharmony_ci /* we need to enter reset mode */ 38962306a36Sopenharmony_ci err = set_reset_mode(dev); 39062306a36Sopenharmony_ci if (err) { 39162306a36Sopenharmony_ci netdev_err(dev, "could not enter reset mode\n"); 39262306a36Sopenharmony_ci return err; 39362306a36Sopenharmony_ci } 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci /* disable all interrupts */ 39662306a36Sopenharmony_ci writel(0, priv->base + SUN4I_REG_INTEN_ADDR); 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci return 0; 39962306a36Sopenharmony_ci} 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_cistatic int sun4ican_set_mode(struct net_device *dev, enum can_mode mode) 40262306a36Sopenharmony_ci{ 40362306a36Sopenharmony_ci int err; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci switch (mode) { 40662306a36Sopenharmony_ci case CAN_MODE_START: 40762306a36Sopenharmony_ci err = sun4i_can_start(dev); 40862306a36Sopenharmony_ci if (err) { 40962306a36Sopenharmony_ci netdev_err(dev, "starting CAN controller failed!\n"); 41062306a36Sopenharmony_ci return err; 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci if (netif_queue_stopped(dev)) 41362306a36Sopenharmony_ci netif_wake_queue(dev); 41462306a36Sopenharmony_ci break; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci default: 41762306a36Sopenharmony_ci return -EOPNOTSUPP; 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci return 0; 42062306a36Sopenharmony_ci} 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci/* transmit a CAN message 42362306a36Sopenharmony_ci * message layout in the sk_buff should be like this: 42462306a36Sopenharmony_ci * xx xx xx xx ff ll 00 11 22 33 44 55 66 77 42562306a36Sopenharmony_ci * [ can_id ] [flags] [len] [can data (up to 8 bytes] 42662306a36Sopenharmony_ci */ 42762306a36Sopenharmony_cistatic netdev_tx_t sun4ican_start_xmit(struct sk_buff *skb, struct net_device *dev) 42862306a36Sopenharmony_ci{ 42962306a36Sopenharmony_ci struct sun4ican_priv *priv = netdev_priv(dev); 43062306a36Sopenharmony_ci struct can_frame *cf = (struct can_frame *)skb->data; 43162306a36Sopenharmony_ci u8 dlc; 43262306a36Sopenharmony_ci u32 dreg, msg_flag_n; 43362306a36Sopenharmony_ci canid_t id; 43462306a36Sopenharmony_ci int i; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci if (can_dev_dropped_skb(dev, skb)) 43762306a36Sopenharmony_ci return NETDEV_TX_OK; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci netif_stop_queue(dev); 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci id = cf->can_id; 44262306a36Sopenharmony_ci dlc = cf->len; 44362306a36Sopenharmony_ci msg_flag_n = dlc; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci if (id & CAN_RTR_FLAG) 44662306a36Sopenharmony_ci msg_flag_n |= SUN4I_MSG_RTR_FLAG; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci if (id & CAN_EFF_FLAG) { 44962306a36Sopenharmony_ci msg_flag_n |= SUN4I_MSG_EFF_FLAG; 45062306a36Sopenharmony_ci dreg = SUN4I_REG_BUF5_ADDR; 45162306a36Sopenharmony_ci writel((id >> 21) & 0xFF, priv->base + SUN4I_REG_BUF1_ADDR); 45262306a36Sopenharmony_ci writel((id >> 13) & 0xFF, priv->base + SUN4I_REG_BUF2_ADDR); 45362306a36Sopenharmony_ci writel((id >> 5) & 0xFF, priv->base + SUN4I_REG_BUF3_ADDR); 45462306a36Sopenharmony_ci writel((id << 3) & 0xF8, priv->base + SUN4I_REG_BUF4_ADDR); 45562306a36Sopenharmony_ci } else { 45662306a36Sopenharmony_ci dreg = SUN4I_REG_BUF3_ADDR; 45762306a36Sopenharmony_ci writel((id >> 3) & 0xFF, priv->base + SUN4I_REG_BUF1_ADDR); 45862306a36Sopenharmony_ci writel((id << 5) & 0xE0, priv->base + SUN4I_REG_BUF2_ADDR); 45962306a36Sopenharmony_ci } 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci for (i = 0; i < dlc; i++) 46262306a36Sopenharmony_ci writel(cf->data[i], priv->base + (dreg + i * 4)); 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci writel(msg_flag_n, priv->base + SUN4I_REG_BUF0_ADDR); 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci can_put_echo_skb(skb, dev, 0, 0); 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) 46962306a36Sopenharmony_ci sun4i_can_write_cmdreg(priv, SUN4I_CMD_SELF_RCV_REQ); 47062306a36Sopenharmony_ci else 47162306a36Sopenharmony_ci sun4i_can_write_cmdreg(priv, SUN4I_CMD_TRANS_REQ); 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci return NETDEV_TX_OK; 47462306a36Sopenharmony_ci} 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_cistatic void sun4i_can_rx(struct net_device *dev) 47762306a36Sopenharmony_ci{ 47862306a36Sopenharmony_ci struct sun4ican_priv *priv = netdev_priv(dev); 47962306a36Sopenharmony_ci struct net_device_stats *stats = &dev->stats; 48062306a36Sopenharmony_ci struct can_frame *cf; 48162306a36Sopenharmony_ci struct sk_buff *skb; 48262306a36Sopenharmony_ci u8 fi; 48362306a36Sopenharmony_ci u32 dreg; 48462306a36Sopenharmony_ci canid_t id; 48562306a36Sopenharmony_ci int i; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci /* create zero'ed CAN frame buffer */ 48862306a36Sopenharmony_ci skb = alloc_can_skb(dev, &cf); 48962306a36Sopenharmony_ci if (!skb) 49062306a36Sopenharmony_ci return; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci fi = readl(priv->base + SUN4I_REG_BUF0_ADDR); 49362306a36Sopenharmony_ci cf->len = can_cc_dlc2len(fi & 0x0F); 49462306a36Sopenharmony_ci if (fi & SUN4I_MSG_EFF_FLAG) { 49562306a36Sopenharmony_ci dreg = SUN4I_REG_BUF5_ADDR; 49662306a36Sopenharmony_ci id = (readl(priv->base + SUN4I_REG_BUF1_ADDR) << 21) | 49762306a36Sopenharmony_ci (readl(priv->base + SUN4I_REG_BUF2_ADDR) << 13) | 49862306a36Sopenharmony_ci (readl(priv->base + SUN4I_REG_BUF3_ADDR) << 5) | 49962306a36Sopenharmony_ci ((readl(priv->base + SUN4I_REG_BUF4_ADDR) >> 3) & 0x1f); 50062306a36Sopenharmony_ci id |= CAN_EFF_FLAG; 50162306a36Sopenharmony_ci } else { 50262306a36Sopenharmony_ci dreg = SUN4I_REG_BUF3_ADDR; 50362306a36Sopenharmony_ci id = (readl(priv->base + SUN4I_REG_BUF1_ADDR) << 3) | 50462306a36Sopenharmony_ci ((readl(priv->base + SUN4I_REG_BUF2_ADDR) >> 5) & 0x7); 50562306a36Sopenharmony_ci } 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci /* remote frame ? */ 50862306a36Sopenharmony_ci if (fi & SUN4I_MSG_RTR_FLAG) { 50962306a36Sopenharmony_ci id |= CAN_RTR_FLAG; 51062306a36Sopenharmony_ci } else { 51162306a36Sopenharmony_ci for (i = 0; i < cf->len; i++) 51262306a36Sopenharmony_ci cf->data[i] = readl(priv->base + dreg + i * 4); 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci stats->rx_bytes += cf->len; 51562306a36Sopenharmony_ci } 51662306a36Sopenharmony_ci stats->rx_packets++; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci cf->can_id = id; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci sun4i_can_write_cmdreg(priv, SUN4I_CMD_RELEASE_RBUF); 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci netif_rx(skb); 52362306a36Sopenharmony_ci} 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_cistatic int sun4i_can_err(struct net_device *dev, u8 isrc, u8 status) 52662306a36Sopenharmony_ci{ 52762306a36Sopenharmony_ci struct sun4ican_priv *priv = netdev_priv(dev); 52862306a36Sopenharmony_ci struct net_device_stats *stats = &dev->stats; 52962306a36Sopenharmony_ci struct can_frame *cf; 53062306a36Sopenharmony_ci struct sk_buff *skb; 53162306a36Sopenharmony_ci enum can_state state = priv->can.state; 53262306a36Sopenharmony_ci enum can_state rx_state, tx_state; 53362306a36Sopenharmony_ci unsigned int rxerr, txerr, errc; 53462306a36Sopenharmony_ci u32 ecc, alc; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci /* we don't skip if alloc fails because we want the stats anyhow */ 53762306a36Sopenharmony_ci skb = alloc_can_err_skb(dev, &cf); 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci errc = readl(priv->base + SUN4I_REG_ERRC_ADDR); 54062306a36Sopenharmony_ci rxerr = (errc >> 16) & 0xFF; 54162306a36Sopenharmony_ci txerr = errc & 0xFF; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci if (isrc & SUN4I_INT_DATA_OR) { 54462306a36Sopenharmony_ci /* data overrun interrupt */ 54562306a36Sopenharmony_ci netdev_dbg(dev, "data overrun interrupt\n"); 54662306a36Sopenharmony_ci if (likely(skb)) { 54762306a36Sopenharmony_ci cf->can_id |= CAN_ERR_CRTL; 54862306a36Sopenharmony_ci cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; 54962306a36Sopenharmony_ci } 55062306a36Sopenharmony_ci stats->rx_over_errors++; 55162306a36Sopenharmony_ci stats->rx_errors++; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci /* reset the CAN IP by entering reset mode 55462306a36Sopenharmony_ci * ignoring timeout error 55562306a36Sopenharmony_ci */ 55662306a36Sopenharmony_ci set_reset_mode(dev); 55762306a36Sopenharmony_ci set_normal_mode(dev); 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci /* clear bit */ 56062306a36Sopenharmony_ci sun4i_can_write_cmdreg(priv, SUN4I_CMD_CLEAR_OR_FLAG); 56162306a36Sopenharmony_ci } 56262306a36Sopenharmony_ci if (isrc & SUN4I_INT_ERR_WRN) { 56362306a36Sopenharmony_ci /* error warning interrupt */ 56462306a36Sopenharmony_ci netdev_dbg(dev, "error warning interrupt\n"); 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci if (status & SUN4I_STA_BUS_OFF) 56762306a36Sopenharmony_ci state = CAN_STATE_BUS_OFF; 56862306a36Sopenharmony_ci else if (status & SUN4I_STA_ERR_STA) 56962306a36Sopenharmony_ci state = CAN_STATE_ERROR_WARNING; 57062306a36Sopenharmony_ci else 57162306a36Sopenharmony_ci state = CAN_STATE_ERROR_ACTIVE; 57262306a36Sopenharmony_ci } 57362306a36Sopenharmony_ci if (skb && state != CAN_STATE_BUS_OFF) { 57462306a36Sopenharmony_ci cf->can_id |= CAN_ERR_CNT; 57562306a36Sopenharmony_ci cf->data[6] = txerr; 57662306a36Sopenharmony_ci cf->data[7] = rxerr; 57762306a36Sopenharmony_ci } 57862306a36Sopenharmony_ci if (isrc & SUN4I_INT_BUS_ERR) { 57962306a36Sopenharmony_ci /* bus error interrupt */ 58062306a36Sopenharmony_ci netdev_dbg(dev, "bus error interrupt\n"); 58162306a36Sopenharmony_ci priv->can.can_stats.bus_error++; 58262306a36Sopenharmony_ci stats->rx_errors++; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci if (likely(skb)) { 58562306a36Sopenharmony_ci ecc = readl(priv->base + SUN4I_REG_STA_ADDR); 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci switch (ecc & SUN4I_STA_MASK_ERR) { 59062306a36Sopenharmony_ci case SUN4I_STA_BIT_ERR: 59162306a36Sopenharmony_ci cf->data[2] |= CAN_ERR_PROT_BIT; 59262306a36Sopenharmony_ci break; 59362306a36Sopenharmony_ci case SUN4I_STA_FORM_ERR: 59462306a36Sopenharmony_ci cf->data[2] |= CAN_ERR_PROT_FORM; 59562306a36Sopenharmony_ci break; 59662306a36Sopenharmony_ci case SUN4I_STA_STUFF_ERR: 59762306a36Sopenharmony_ci cf->data[2] |= CAN_ERR_PROT_STUFF; 59862306a36Sopenharmony_ci break; 59962306a36Sopenharmony_ci default: 60062306a36Sopenharmony_ci cf->data[3] = (ecc & SUN4I_STA_ERR_SEG_CODE) 60162306a36Sopenharmony_ci >> 16; 60262306a36Sopenharmony_ci break; 60362306a36Sopenharmony_ci } 60462306a36Sopenharmony_ci /* error occurred during transmission? */ 60562306a36Sopenharmony_ci if ((ecc & SUN4I_STA_ERR_DIR) == 0) 60662306a36Sopenharmony_ci cf->data[2] |= CAN_ERR_PROT_TX; 60762306a36Sopenharmony_ci } 60862306a36Sopenharmony_ci } 60962306a36Sopenharmony_ci if (isrc & SUN4I_INT_ERR_PASSIVE) { 61062306a36Sopenharmony_ci /* error passive interrupt */ 61162306a36Sopenharmony_ci netdev_dbg(dev, "error passive interrupt\n"); 61262306a36Sopenharmony_ci if (state == CAN_STATE_ERROR_PASSIVE) 61362306a36Sopenharmony_ci state = CAN_STATE_ERROR_WARNING; 61462306a36Sopenharmony_ci else 61562306a36Sopenharmony_ci state = CAN_STATE_ERROR_PASSIVE; 61662306a36Sopenharmony_ci } 61762306a36Sopenharmony_ci if (isrc & SUN4I_INT_ARB_LOST) { 61862306a36Sopenharmony_ci /* arbitration lost interrupt */ 61962306a36Sopenharmony_ci netdev_dbg(dev, "arbitration lost interrupt\n"); 62062306a36Sopenharmony_ci alc = readl(priv->base + SUN4I_REG_STA_ADDR); 62162306a36Sopenharmony_ci priv->can.can_stats.arbitration_lost++; 62262306a36Sopenharmony_ci if (likely(skb)) { 62362306a36Sopenharmony_ci cf->can_id |= CAN_ERR_LOSTARB; 62462306a36Sopenharmony_ci cf->data[0] = (alc >> 8) & 0x1f; 62562306a36Sopenharmony_ci } 62662306a36Sopenharmony_ci } 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci if (state != priv->can.state) { 62962306a36Sopenharmony_ci tx_state = txerr >= rxerr ? state : 0; 63062306a36Sopenharmony_ci rx_state = txerr <= rxerr ? state : 0; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci if (likely(skb)) 63362306a36Sopenharmony_ci can_change_state(dev, cf, tx_state, rx_state); 63462306a36Sopenharmony_ci else 63562306a36Sopenharmony_ci priv->can.state = state; 63662306a36Sopenharmony_ci if (state == CAN_STATE_BUS_OFF) 63762306a36Sopenharmony_ci can_bus_off(dev); 63862306a36Sopenharmony_ci } 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci if (likely(skb)) 64162306a36Sopenharmony_ci netif_rx(skb); 64262306a36Sopenharmony_ci else 64362306a36Sopenharmony_ci return -ENOMEM; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci return 0; 64662306a36Sopenharmony_ci} 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_cistatic irqreturn_t sun4i_can_interrupt(int irq, void *dev_id) 64962306a36Sopenharmony_ci{ 65062306a36Sopenharmony_ci struct net_device *dev = (struct net_device *)dev_id; 65162306a36Sopenharmony_ci struct sun4ican_priv *priv = netdev_priv(dev); 65262306a36Sopenharmony_ci struct net_device_stats *stats = &dev->stats; 65362306a36Sopenharmony_ci u8 isrc, status; 65462306a36Sopenharmony_ci int n = 0; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci while ((isrc = readl(priv->base + SUN4I_REG_INT_ADDR)) && 65762306a36Sopenharmony_ci (n < SUN4I_CAN_MAX_IRQ)) { 65862306a36Sopenharmony_ci n++; 65962306a36Sopenharmony_ci status = readl(priv->base + SUN4I_REG_STA_ADDR); 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci if (isrc & SUN4I_INT_WAKEUP) 66262306a36Sopenharmony_ci netdev_warn(dev, "wakeup interrupt\n"); 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci if (isrc & SUN4I_INT_TBUF_VLD) { 66562306a36Sopenharmony_ci /* transmission complete interrupt */ 66662306a36Sopenharmony_ci stats->tx_bytes += can_get_echo_skb(dev, 0, NULL); 66762306a36Sopenharmony_ci stats->tx_packets++; 66862306a36Sopenharmony_ci netif_wake_queue(dev); 66962306a36Sopenharmony_ci } 67062306a36Sopenharmony_ci if ((isrc & SUN4I_INT_RBUF_VLD) && 67162306a36Sopenharmony_ci !(isrc & SUN4I_INT_DATA_OR)) { 67262306a36Sopenharmony_ci /* receive interrupt - don't read if overrun occurred */ 67362306a36Sopenharmony_ci while (status & SUN4I_STA_RBUF_RDY) { 67462306a36Sopenharmony_ci /* RX buffer is not empty */ 67562306a36Sopenharmony_ci sun4i_can_rx(dev); 67662306a36Sopenharmony_ci status = readl(priv->base + SUN4I_REG_STA_ADDR); 67762306a36Sopenharmony_ci } 67862306a36Sopenharmony_ci } 67962306a36Sopenharmony_ci if (isrc & 68062306a36Sopenharmony_ci (SUN4I_INT_DATA_OR | SUN4I_INT_ERR_WRN | SUN4I_INT_BUS_ERR | 68162306a36Sopenharmony_ci SUN4I_INT_ERR_PASSIVE | SUN4I_INT_ARB_LOST)) { 68262306a36Sopenharmony_ci /* error interrupt */ 68362306a36Sopenharmony_ci if (sun4i_can_err(dev, isrc, status)) 68462306a36Sopenharmony_ci netdev_err(dev, "can't allocate buffer - clearing pending interrupts\n"); 68562306a36Sopenharmony_ci } 68662306a36Sopenharmony_ci /* clear interrupts */ 68762306a36Sopenharmony_ci writel(isrc, priv->base + SUN4I_REG_INT_ADDR); 68862306a36Sopenharmony_ci readl(priv->base + SUN4I_REG_INT_ADDR); 68962306a36Sopenharmony_ci } 69062306a36Sopenharmony_ci if (n >= SUN4I_CAN_MAX_IRQ) 69162306a36Sopenharmony_ci netdev_dbg(dev, "%d messages handled in ISR", n); 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci return (n) ? IRQ_HANDLED : IRQ_NONE; 69462306a36Sopenharmony_ci} 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_cistatic int sun4ican_open(struct net_device *dev) 69762306a36Sopenharmony_ci{ 69862306a36Sopenharmony_ci struct sun4ican_priv *priv = netdev_priv(dev); 69962306a36Sopenharmony_ci int err; 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci /* common open */ 70262306a36Sopenharmony_ci err = open_candev(dev); 70362306a36Sopenharmony_ci if (err) 70462306a36Sopenharmony_ci return err; 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci /* register interrupt handler */ 70762306a36Sopenharmony_ci err = request_irq(dev->irq, sun4i_can_interrupt, 0, dev->name, dev); 70862306a36Sopenharmony_ci if (err) { 70962306a36Sopenharmony_ci netdev_err(dev, "request_irq err: %d\n", err); 71062306a36Sopenharmony_ci goto exit_irq; 71162306a36Sopenharmony_ci } 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci /* software reset deassert */ 71462306a36Sopenharmony_ci err = reset_control_deassert(priv->reset); 71562306a36Sopenharmony_ci if (err) { 71662306a36Sopenharmony_ci netdev_err(dev, "could not deassert CAN reset\n"); 71762306a36Sopenharmony_ci goto exit_soft_reset; 71862306a36Sopenharmony_ci } 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci /* turn on clocking for CAN peripheral block */ 72162306a36Sopenharmony_ci err = clk_prepare_enable(priv->clk); 72262306a36Sopenharmony_ci if (err) { 72362306a36Sopenharmony_ci netdev_err(dev, "could not enable CAN peripheral clock\n"); 72462306a36Sopenharmony_ci goto exit_clock; 72562306a36Sopenharmony_ci } 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci err = sun4i_can_start(dev); 72862306a36Sopenharmony_ci if (err) { 72962306a36Sopenharmony_ci netdev_err(dev, "could not start CAN peripheral\n"); 73062306a36Sopenharmony_ci goto exit_can_start; 73162306a36Sopenharmony_ci } 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci netif_start_queue(dev); 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci return 0; 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ciexit_can_start: 73862306a36Sopenharmony_ci clk_disable_unprepare(priv->clk); 73962306a36Sopenharmony_ciexit_clock: 74062306a36Sopenharmony_ci reset_control_assert(priv->reset); 74162306a36Sopenharmony_ciexit_soft_reset: 74262306a36Sopenharmony_ci free_irq(dev->irq, dev); 74362306a36Sopenharmony_ciexit_irq: 74462306a36Sopenharmony_ci close_candev(dev); 74562306a36Sopenharmony_ci return err; 74662306a36Sopenharmony_ci} 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_cistatic int sun4ican_close(struct net_device *dev) 74962306a36Sopenharmony_ci{ 75062306a36Sopenharmony_ci struct sun4ican_priv *priv = netdev_priv(dev); 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci netif_stop_queue(dev); 75362306a36Sopenharmony_ci sun4i_can_stop(dev); 75462306a36Sopenharmony_ci clk_disable_unprepare(priv->clk); 75562306a36Sopenharmony_ci reset_control_assert(priv->reset); 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci free_irq(dev->irq, dev); 75862306a36Sopenharmony_ci close_candev(dev); 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci return 0; 76162306a36Sopenharmony_ci} 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_cistatic const struct net_device_ops sun4ican_netdev_ops = { 76462306a36Sopenharmony_ci .ndo_open = sun4ican_open, 76562306a36Sopenharmony_ci .ndo_stop = sun4ican_close, 76662306a36Sopenharmony_ci .ndo_start_xmit = sun4ican_start_xmit, 76762306a36Sopenharmony_ci}; 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_cistatic const struct ethtool_ops sun4ican_ethtool_ops = { 77062306a36Sopenharmony_ci .get_ts_info = ethtool_op_get_ts_info, 77162306a36Sopenharmony_ci}; 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_cistatic const struct sun4ican_quirks sun4ican_quirks_a10 = { 77462306a36Sopenharmony_ci .has_reset = false, 77562306a36Sopenharmony_ci .acp_offset = 0, 77662306a36Sopenharmony_ci}; 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_cistatic const struct sun4ican_quirks sun4ican_quirks_r40 = { 77962306a36Sopenharmony_ci .has_reset = true, 78062306a36Sopenharmony_ci .acp_offset = 0, 78162306a36Sopenharmony_ci}; 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_cistatic const struct sun4ican_quirks sun4ican_quirks_d1 = { 78462306a36Sopenharmony_ci .has_reset = true, 78562306a36Sopenharmony_ci .acp_offset = (SUN4I_REG_ACPC_ADDR_D1 - SUN4I_REG_ACPC_ADDR), 78662306a36Sopenharmony_ci}; 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_cistatic const struct of_device_id sun4ican_of_match[] = { 78962306a36Sopenharmony_ci { 79062306a36Sopenharmony_ci .compatible = "allwinner,sun4i-a10-can", 79162306a36Sopenharmony_ci .data = &sun4ican_quirks_a10 79262306a36Sopenharmony_ci }, { 79362306a36Sopenharmony_ci .compatible = "allwinner,sun7i-a20-can", 79462306a36Sopenharmony_ci .data = &sun4ican_quirks_a10 79562306a36Sopenharmony_ci }, { 79662306a36Sopenharmony_ci .compatible = "allwinner,sun8i-r40-can", 79762306a36Sopenharmony_ci .data = &sun4ican_quirks_r40 79862306a36Sopenharmony_ci }, { 79962306a36Sopenharmony_ci .compatible = "allwinner,sun20i-d1-can", 80062306a36Sopenharmony_ci .data = &sun4ican_quirks_d1 80162306a36Sopenharmony_ci }, { 80262306a36Sopenharmony_ci /* sentinel */ 80362306a36Sopenharmony_ci }, 80462306a36Sopenharmony_ci}; 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, sun4ican_of_match); 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_cistatic void sun4ican_remove(struct platform_device *pdev) 80962306a36Sopenharmony_ci{ 81062306a36Sopenharmony_ci struct net_device *dev = platform_get_drvdata(pdev); 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci unregister_netdev(dev); 81362306a36Sopenharmony_ci free_candev(dev); 81462306a36Sopenharmony_ci} 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_cistatic int sun4ican_probe(struct platform_device *pdev) 81762306a36Sopenharmony_ci{ 81862306a36Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 81962306a36Sopenharmony_ci struct clk *clk; 82062306a36Sopenharmony_ci struct reset_control *reset = NULL; 82162306a36Sopenharmony_ci void __iomem *addr; 82262306a36Sopenharmony_ci int err, irq; 82362306a36Sopenharmony_ci struct net_device *dev; 82462306a36Sopenharmony_ci struct sun4ican_priv *priv; 82562306a36Sopenharmony_ci const struct sun4ican_quirks *quirks; 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci quirks = of_device_get_match_data(&pdev->dev); 82862306a36Sopenharmony_ci if (!quirks) { 82962306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to determine the quirks to use\n"); 83062306a36Sopenharmony_ci err = -ENODEV; 83162306a36Sopenharmony_ci goto exit; 83262306a36Sopenharmony_ci } 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci if (quirks->has_reset) { 83562306a36Sopenharmony_ci reset = devm_reset_control_get_exclusive(&pdev->dev, NULL); 83662306a36Sopenharmony_ci if (IS_ERR(reset)) { 83762306a36Sopenharmony_ci dev_err(&pdev->dev, "unable to request reset\n"); 83862306a36Sopenharmony_ci err = PTR_ERR(reset); 83962306a36Sopenharmony_ci goto exit; 84062306a36Sopenharmony_ci } 84162306a36Sopenharmony_ci } 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci clk = of_clk_get(np, 0); 84462306a36Sopenharmony_ci if (IS_ERR(clk)) { 84562306a36Sopenharmony_ci dev_err(&pdev->dev, "unable to request clock\n"); 84662306a36Sopenharmony_ci err = -ENODEV; 84762306a36Sopenharmony_ci goto exit; 84862306a36Sopenharmony_ci } 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci irq = platform_get_irq(pdev, 0); 85162306a36Sopenharmony_ci if (irq < 0) { 85262306a36Sopenharmony_ci err = -ENODEV; 85362306a36Sopenharmony_ci goto exit; 85462306a36Sopenharmony_ci } 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci addr = devm_platform_ioremap_resource(pdev, 0); 85762306a36Sopenharmony_ci if (IS_ERR(addr)) { 85862306a36Sopenharmony_ci err = PTR_ERR(addr); 85962306a36Sopenharmony_ci goto exit; 86062306a36Sopenharmony_ci } 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci dev = alloc_candev(sizeof(struct sun4ican_priv), 1); 86362306a36Sopenharmony_ci if (!dev) { 86462306a36Sopenharmony_ci dev_err(&pdev->dev, 86562306a36Sopenharmony_ci "could not allocate memory for CAN device\n"); 86662306a36Sopenharmony_ci err = -ENOMEM; 86762306a36Sopenharmony_ci goto exit; 86862306a36Sopenharmony_ci } 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci dev->netdev_ops = &sun4ican_netdev_ops; 87162306a36Sopenharmony_ci dev->ethtool_ops = &sun4ican_ethtool_ops; 87262306a36Sopenharmony_ci dev->irq = irq; 87362306a36Sopenharmony_ci dev->flags |= IFF_ECHO; 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci priv = netdev_priv(dev); 87662306a36Sopenharmony_ci priv->can.clock.freq = clk_get_rate(clk); 87762306a36Sopenharmony_ci priv->can.bittiming_const = &sun4ican_bittiming_const; 87862306a36Sopenharmony_ci priv->can.do_set_mode = sun4ican_set_mode; 87962306a36Sopenharmony_ci priv->can.do_get_berr_counter = sun4ican_get_berr_counter; 88062306a36Sopenharmony_ci priv->can.ctrlmode_supported = CAN_CTRLMODE_BERR_REPORTING | 88162306a36Sopenharmony_ci CAN_CTRLMODE_LISTENONLY | 88262306a36Sopenharmony_ci CAN_CTRLMODE_LOOPBACK | 88362306a36Sopenharmony_ci CAN_CTRLMODE_3_SAMPLES; 88462306a36Sopenharmony_ci priv->base = addr; 88562306a36Sopenharmony_ci priv->clk = clk; 88662306a36Sopenharmony_ci priv->reset = reset; 88762306a36Sopenharmony_ci priv->acp_offset = quirks->acp_offset; 88862306a36Sopenharmony_ci spin_lock_init(&priv->cmdreg_lock); 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci platform_set_drvdata(pdev, dev); 89162306a36Sopenharmony_ci SET_NETDEV_DEV(dev, &pdev->dev); 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci err = register_candev(dev); 89462306a36Sopenharmony_ci if (err) { 89562306a36Sopenharmony_ci dev_err(&pdev->dev, "registering %s failed (err=%d)\n", 89662306a36Sopenharmony_ci DRV_NAME, err); 89762306a36Sopenharmony_ci goto exit_free; 89862306a36Sopenharmony_ci } 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci dev_info(&pdev->dev, "device registered (base=%p, irq=%d)\n", 90162306a36Sopenharmony_ci priv->base, dev->irq); 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci return 0; 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ciexit_free: 90662306a36Sopenharmony_ci free_candev(dev); 90762306a36Sopenharmony_ciexit: 90862306a36Sopenharmony_ci return err; 90962306a36Sopenharmony_ci} 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_cistatic struct platform_driver sun4i_can_driver = { 91262306a36Sopenharmony_ci .driver = { 91362306a36Sopenharmony_ci .name = DRV_NAME, 91462306a36Sopenharmony_ci .of_match_table = sun4ican_of_match, 91562306a36Sopenharmony_ci }, 91662306a36Sopenharmony_ci .probe = sun4ican_probe, 91762306a36Sopenharmony_ci .remove_new = sun4ican_remove, 91862306a36Sopenharmony_ci}; 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_cimodule_platform_driver(sun4i_can_driver); 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ciMODULE_AUTHOR("Peter Chen <xingkongcp@gmail.com>"); 92362306a36Sopenharmony_ciMODULE_AUTHOR("Gerhard Bertelsmann <info@gerhard-bertelsmann.de>"); 92462306a36Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 92562306a36Sopenharmony_ciMODULE_DESCRIPTION("CAN driver for Allwinner SoCs (A10/A20/D1)"); 926