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