18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * TI HECC (CAN) device driver 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * This driver supports TI's HECC (High End CAN Controller module) and the 58c2ecf20Sopenharmony_ci * specs for the same is available at <http://www.ti.com> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ 88c2ecf20Sopenharmony_ci * Copyright (C) 2019 Jeroen Hofstee <jhofstee@victronenergy.com> 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or 118c2ecf20Sopenharmony_ci * modify it under the terms of the GNU General Public License as 128c2ecf20Sopenharmony_ci * published by the Free Software Foundation version 2. 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * This program is distributed as is WITHOUT ANY WARRANTY of any 158c2ecf20Sopenharmony_ci * kind, whether express or implied; without even the implied warranty 168c2ecf20Sopenharmony_ci * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 178c2ecf20Sopenharmony_ci * GNU General Public License for more details. 188c2ecf20Sopenharmony_ci * 198c2ecf20Sopenharmony_ci */ 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include <linux/module.h> 228c2ecf20Sopenharmony_ci#include <linux/kernel.h> 238c2ecf20Sopenharmony_ci#include <linux/types.h> 248c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 258c2ecf20Sopenharmony_ci#include <linux/errno.h> 268c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 278c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 288c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 298c2ecf20Sopenharmony_ci#include <linux/clk.h> 308c2ecf20Sopenharmony_ci#include <linux/io.h> 318c2ecf20Sopenharmony_ci#include <linux/of.h> 328c2ecf20Sopenharmony_ci#include <linux/of_device.h> 338c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h> 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#include <linux/can/dev.h> 368c2ecf20Sopenharmony_ci#include <linux/can/error.h> 378c2ecf20Sopenharmony_ci#include <linux/can/led.h> 388c2ecf20Sopenharmony_ci#include <linux/can/rx-offload.h> 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#define DRV_NAME "ti_hecc" 418c2ecf20Sopenharmony_ci#define HECC_MODULE_VERSION "0.7" 428c2ecf20Sopenharmony_ciMODULE_VERSION(HECC_MODULE_VERSION); 438c2ecf20Sopenharmony_ci#define DRV_DESC "TI High End CAN Controller Driver " HECC_MODULE_VERSION 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/* TX / RX Mailbox Configuration */ 468c2ecf20Sopenharmony_ci#define HECC_MAX_MAILBOXES 32 /* hardware mailboxes - do not change */ 478c2ecf20Sopenharmony_ci#define MAX_TX_PRIO 0x3F /* hardware value - do not change */ 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci/* Important Note: TX mailbox configuration 508c2ecf20Sopenharmony_ci * TX mailboxes should be restricted to the number of SKB buffers to avoid 518c2ecf20Sopenharmony_ci * maintaining SKB buffers separately. TX mailboxes should be a power of 2 528c2ecf20Sopenharmony_ci * for the mailbox logic to work. Top mailbox numbers are reserved for RX 538c2ecf20Sopenharmony_ci * and lower mailboxes for TX. 548c2ecf20Sopenharmony_ci * 558c2ecf20Sopenharmony_ci * HECC_MAX_TX_MBOX HECC_MB_TX_SHIFT 568c2ecf20Sopenharmony_ci * 4 (default) 2 578c2ecf20Sopenharmony_ci * 8 3 588c2ecf20Sopenharmony_ci * 16 4 598c2ecf20Sopenharmony_ci */ 608c2ecf20Sopenharmony_ci#define HECC_MB_TX_SHIFT 2 /* as per table above */ 618c2ecf20Sopenharmony_ci#define HECC_MAX_TX_MBOX BIT(HECC_MB_TX_SHIFT) 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci#define HECC_TX_PRIO_SHIFT (HECC_MB_TX_SHIFT) 648c2ecf20Sopenharmony_ci#define HECC_TX_PRIO_MASK (MAX_TX_PRIO << HECC_MB_TX_SHIFT) 658c2ecf20Sopenharmony_ci#define HECC_TX_MB_MASK (HECC_MAX_TX_MBOX - 1) 668c2ecf20Sopenharmony_ci#define HECC_TX_MASK ((HECC_MAX_TX_MBOX - 1) | HECC_TX_PRIO_MASK) 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci/* RX mailbox configuration 698c2ecf20Sopenharmony_ci * 708c2ecf20Sopenharmony_ci * The remaining mailboxes are used for reception and are delivered 718c2ecf20Sopenharmony_ci * based on their timestamp, to avoid a hardware race when CANME is 728c2ecf20Sopenharmony_ci * changed while CAN-bus traffic is being received. 738c2ecf20Sopenharmony_ci */ 748c2ecf20Sopenharmony_ci#define HECC_MAX_RX_MBOX (HECC_MAX_MAILBOXES - HECC_MAX_TX_MBOX) 758c2ecf20Sopenharmony_ci#define HECC_RX_FIRST_MBOX (HECC_MAX_MAILBOXES - 1) 768c2ecf20Sopenharmony_ci#define HECC_RX_LAST_MBOX (HECC_MAX_TX_MBOX) 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci/* TI HECC module registers */ 798c2ecf20Sopenharmony_ci#define HECC_CANME 0x0 /* Mailbox enable */ 808c2ecf20Sopenharmony_ci#define HECC_CANMD 0x4 /* Mailbox direction */ 818c2ecf20Sopenharmony_ci#define HECC_CANTRS 0x8 /* Transmit request set */ 828c2ecf20Sopenharmony_ci#define HECC_CANTRR 0xC /* Transmit request */ 838c2ecf20Sopenharmony_ci#define HECC_CANTA 0x10 /* Transmission acknowledge */ 848c2ecf20Sopenharmony_ci#define HECC_CANAA 0x14 /* Abort acknowledge */ 858c2ecf20Sopenharmony_ci#define HECC_CANRMP 0x18 /* Receive message pending */ 868c2ecf20Sopenharmony_ci#define HECC_CANRML 0x1C /* Receive message lost */ 878c2ecf20Sopenharmony_ci#define HECC_CANRFP 0x20 /* Remote frame pending */ 888c2ecf20Sopenharmony_ci#define HECC_CANGAM 0x24 /* SECC only:Global acceptance mask */ 898c2ecf20Sopenharmony_ci#define HECC_CANMC 0x28 /* Master control */ 908c2ecf20Sopenharmony_ci#define HECC_CANBTC 0x2C /* Bit timing configuration */ 918c2ecf20Sopenharmony_ci#define HECC_CANES 0x30 /* Error and status */ 928c2ecf20Sopenharmony_ci#define HECC_CANTEC 0x34 /* Transmit error counter */ 938c2ecf20Sopenharmony_ci#define HECC_CANREC 0x38 /* Receive error counter */ 948c2ecf20Sopenharmony_ci#define HECC_CANGIF0 0x3C /* Global interrupt flag 0 */ 958c2ecf20Sopenharmony_ci#define HECC_CANGIM 0x40 /* Global interrupt mask */ 968c2ecf20Sopenharmony_ci#define HECC_CANGIF1 0x44 /* Global interrupt flag 1 */ 978c2ecf20Sopenharmony_ci#define HECC_CANMIM 0x48 /* Mailbox interrupt mask */ 988c2ecf20Sopenharmony_ci#define HECC_CANMIL 0x4C /* Mailbox interrupt level */ 998c2ecf20Sopenharmony_ci#define HECC_CANOPC 0x50 /* Overwrite protection control */ 1008c2ecf20Sopenharmony_ci#define HECC_CANTIOC 0x54 /* Transmit I/O control */ 1018c2ecf20Sopenharmony_ci#define HECC_CANRIOC 0x58 /* Receive I/O control */ 1028c2ecf20Sopenharmony_ci#define HECC_CANLNT 0x5C /* HECC only: Local network time */ 1038c2ecf20Sopenharmony_ci#define HECC_CANTOC 0x60 /* HECC only: Time-out control */ 1048c2ecf20Sopenharmony_ci#define HECC_CANTOS 0x64 /* HECC only: Time-out status */ 1058c2ecf20Sopenharmony_ci#define HECC_CANTIOCE 0x68 /* SCC only:Enhanced TX I/O control */ 1068c2ecf20Sopenharmony_ci#define HECC_CANRIOCE 0x6C /* SCC only:Enhanced RX I/O control */ 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci/* TI HECC RAM registers */ 1098c2ecf20Sopenharmony_ci#define HECC_CANMOTS 0x80 /* Message object time stamp */ 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci/* Mailbox registers */ 1128c2ecf20Sopenharmony_ci#define HECC_CANMID 0x0 1138c2ecf20Sopenharmony_ci#define HECC_CANMCF 0x4 1148c2ecf20Sopenharmony_ci#define HECC_CANMDL 0x8 1158c2ecf20Sopenharmony_ci#define HECC_CANMDH 0xC 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci#define HECC_SET_REG 0xFFFFFFFF 1188c2ecf20Sopenharmony_ci#define HECC_CANID_MASK 0x3FF /* 18 bits mask for extended id's */ 1198c2ecf20Sopenharmony_ci#define HECC_CCE_WAIT_COUNT 100 /* Wait for ~1 sec for CCE bit */ 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci#define HECC_CANMC_SCM BIT(13) /* SCC compat mode */ 1228c2ecf20Sopenharmony_ci#define HECC_CANMC_CCR BIT(12) /* Change config request */ 1238c2ecf20Sopenharmony_ci#define HECC_CANMC_PDR BIT(11) /* Local Power down - for sleep mode */ 1248c2ecf20Sopenharmony_ci#define HECC_CANMC_ABO BIT(7) /* Auto Bus On */ 1258c2ecf20Sopenharmony_ci#define HECC_CANMC_STM BIT(6) /* Self test mode - loopback */ 1268c2ecf20Sopenharmony_ci#define HECC_CANMC_SRES BIT(5) /* Software reset */ 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci#define HECC_CANTIOC_EN BIT(3) /* Enable CAN TX I/O pin */ 1298c2ecf20Sopenharmony_ci#define HECC_CANRIOC_EN BIT(3) /* Enable CAN RX I/O pin */ 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci#define HECC_CANMID_IDE BIT(31) /* Extended frame format */ 1328c2ecf20Sopenharmony_ci#define HECC_CANMID_AME BIT(30) /* Acceptance mask enable */ 1338c2ecf20Sopenharmony_ci#define HECC_CANMID_AAM BIT(29) /* Auto answer mode */ 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci#define HECC_CANES_FE BIT(24) /* form error */ 1368c2ecf20Sopenharmony_ci#define HECC_CANES_BE BIT(23) /* bit error */ 1378c2ecf20Sopenharmony_ci#define HECC_CANES_SA1 BIT(22) /* stuck at dominant error */ 1388c2ecf20Sopenharmony_ci#define HECC_CANES_CRCE BIT(21) /* CRC error */ 1398c2ecf20Sopenharmony_ci#define HECC_CANES_SE BIT(20) /* stuff bit error */ 1408c2ecf20Sopenharmony_ci#define HECC_CANES_ACKE BIT(19) /* ack error */ 1418c2ecf20Sopenharmony_ci#define HECC_CANES_BO BIT(18) /* Bus off status */ 1428c2ecf20Sopenharmony_ci#define HECC_CANES_EP BIT(17) /* Error passive status */ 1438c2ecf20Sopenharmony_ci#define HECC_CANES_EW BIT(16) /* Error warning status */ 1448c2ecf20Sopenharmony_ci#define HECC_CANES_SMA BIT(5) /* suspend mode ack */ 1458c2ecf20Sopenharmony_ci#define HECC_CANES_CCE BIT(4) /* Change config enabled */ 1468c2ecf20Sopenharmony_ci#define HECC_CANES_PDA BIT(3) /* Power down mode ack */ 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci#define HECC_CANBTC_SAM BIT(7) /* sample points */ 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci#define HECC_BUS_ERROR (HECC_CANES_FE | HECC_CANES_BE |\ 1518c2ecf20Sopenharmony_ci HECC_CANES_CRCE | HECC_CANES_SE |\ 1528c2ecf20Sopenharmony_ci HECC_CANES_ACKE) 1538c2ecf20Sopenharmony_ci#define HECC_CANES_FLAGS (HECC_BUS_ERROR | HECC_CANES_BO |\ 1548c2ecf20Sopenharmony_ci HECC_CANES_EP | HECC_CANES_EW) 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci#define HECC_CANMCF_RTR BIT(4) /* Remote transmit request */ 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci#define HECC_CANGIF_MAIF BIT(17) /* Message alarm interrupt */ 1598c2ecf20Sopenharmony_ci#define HECC_CANGIF_TCOIF BIT(16) /* Timer counter overflow int */ 1608c2ecf20Sopenharmony_ci#define HECC_CANGIF_GMIF BIT(15) /* Global mailbox interrupt */ 1618c2ecf20Sopenharmony_ci#define HECC_CANGIF_AAIF BIT(14) /* Abort ack interrupt */ 1628c2ecf20Sopenharmony_ci#define HECC_CANGIF_WDIF BIT(13) /* Write denied interrupt */ 1638c2ecf20Sopenharmony_ci#define HECC_CANGIF_WUIF BIT(12) /* Wake up interrupt */ 1648c2ecf20Sopenharmony_ci#define HECC_CANGIF_RMLIF BIT(11) /* Receive message lost interrupt */ 1658c2ecf20Sopenharmony_ci#define HECC_CANGIF_BOIF BIT(10) /* Bus off interrupt */ 1668c2ecf20Sopenharmony_ci#define HECC_CANGIF_EPIF BIT(9) /* Error passive interrupt */ 1678c2ecf20Sopenharmony_ci#define HECC_CANGIF_WLIF BIT(8) /* Warning level interrupt */ 1688c2ecf20Sopenharmony_ci#define HECC_CANGIF_MBOX_MASK 0x1F /* Mailbox number mask */ 1698c2ecf20Sopenharmony_ci#define HECC_CANGIM_I1EN BIT(1) /* Int line 1 enable */ 1708c2ecf20Sopenharmony_ci#define HECC_CANGIM_I0EN BIT(0) /* Int line 0 enable */ 1718c2ecf20Sopenharmony_ci#define HECC_CANGIM_DEF_MASK 0x700 /* only busoff/warning/passive */ 1728c2ecf20Sopenharmony_ci#define HECC_CANGIM_SIL BIT(2) /* system interrupts to int line 1 */ 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci/* CAN Bittiming constants as per HECC specs */ 1758c2ecf20Sopenharmony_cistatic const struct can_bittiming_const ti_hecc_bittiming_const = { 1768c2ecf20Sopenharmony_ci .name = DRV_NAME, 1778c2ecf20Sopenharmony_ci .tseg1_min = 1, 1788c2ecf20Sopenharmony_ci .tseg1_max = 16, 1798c2ecf20Sopenharmony_ci .tseg2_min = 1, 1808c2ecf20Sopenharmony_ci .tseg2_max = 8, 1818c2ecf20Sopenharmony_ci .sjw_max = 4, 1828c2ecf20Sopenharmony_ci .brp_min = 1, 1838c2ecf20Sopenharmony_ci .brp_max = 256, 1848c2ecf20Sopenharmony_ci .brp_inc = 1, 1858c2ecf20Sopenharmony_ci}; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cistruct ti_hecc_priv { 1888c2ecf20Sopenharmony_ci struct can_priv can; /* MUST be first member/field */ 1898c2ecf20Sopenharmony_ci struct can_rx_offload offload; 1908c2ecf20Sopenharmony_ci struct net_device *ndev; 1918c2ecf20Sopenharmony_ci struct clk *clk; 1928c2ecf20Sopenharmony_ci void __iomem *base; 1938c2ecf20Sopenharmony_ci void __iomem *hecc_ram; 1948c2ecf20Sopenharmony_ci void __iomem *mbx; 1958c2ecf20Sopenharmony_ci bool use_hecc1int; 1968c2ecf20Sopenharmony_ci spinlock_t mbx_lock; /* CANME register needs protection */ 1978c2ecf20Sopenharmony_ci u32 tx_head; 1988c2ecf20Sopenharmony_ci u32 tx_tail; 1998c2ecf20Sopenharmony_ci struct regulator *reg_xceiver; 2008c2ecf20Sopenharmony_ci}; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_cistatic inline int get_tx_head_mb(struct ti_hecc_priv *priv) 2038c2ecf20Sopenharmony_ci{ 2048c2ecf20Sopenharmony_ci return priv->tx_head & HECC_TX_MB_MASK; 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic inline int get_tx_tail_mb(struct ti_hecc_priv *priv) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci return priv->tx_tail & HECC_TX_MB_MASK; 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic inline int get_tx_head_prio(struct ti_hecc_priv *priv) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci return (priv->tx_head >> HECC_TX_PRIO_SHIFT) & MAX_TX_PRIO; 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cistatic inline void hecc_write_lam(struct ti_hecc_priv *priv, u32 mbxno, u32 val) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci __raw_writel(val, priv->hecc_ram + mbxno * 4); 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_cistatic inline u32 hecc_read_stamp(struct ti_hecc_priv *priv, u32 mbxno) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci return __raw_readl(priv->hecc_ram + HECC_CANMOTS + mbxno * 4); 2258c2ecf20Sopenharmony_ci} 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cistatic inline void hecc_write_mbx(struct ti_hecc_priv *priv, u32 mbxno, 2288c2ecf20Sopenharmony_ci u32 reg, u32 val) 2298c2ecf20Sopenharmony_ci{ 2308c2ecf20Sopenharmony_ci __raw_writel(val, priv->mbx + mbxno * 0x10 + reg); 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_cistatic inline u32 hecc_read_mbx(struct ti_hecc_priv *priv, u32 mbxno, u32 reg) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci return __raw_readl(priv->mbx + mbxno * 0x10 + reg); 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cistatic inline void hecc_write(struct ti_hecc_priv *priv, u32 reg, u32 val) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci __raw_writel(val, priv->base + reg); 2418c2ecf20Sopenharmony_ci} 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_cistatic inline u32 hecc_read(struct ti_hecc_priv *priv, int reg) 2448c2ecf20Sopenharmony_ci{ 2458c2ecf20Sopenharmony_ci return __raw_readl(priv->base + reg); 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_cistatic inline void hecc_set_bit(struct ti_hecc_priv *priv, int reg, 2498c2ecf20Sopenharmony_ci u32 bit_mask) 2508c2ecf20Sopenharmony_ci{ 2518c2ecf20Sopenharmony_ci hecc_write(priv, reg, hecc_read(priv, reg) | bit_mask); 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_cistatic inline void hecc_clear_bit(struct ti_hecc_priv *priv, int reg, 2558c2ecf20Sopenharmony_ci u32 bit_mask) 2568c2ecf20Sopenharmony_ci{ 2578c2ecf20Sopenharmony_ci hecc_write(priv, reg, hecc_read(priv, reg) & ~bit_mask); 2588c2ecf20Sopenharmony_ci} 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_cistatic inline u32 hecc_get_bit(struct ti_hecc_priv *priv, int reg, u32 bit_mask) 2618c2ecf20Sopenharmony_ci{ 2628c2ecf20Sopenharmony_ci return (hecc_read(priv, reg) & bit_mask) ? 1 : 0; 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistatic int ti_hecc_set_btc(struct ti_hecc_priv *priv) 2668c2ecf20Sopenharmony_ci{ 2678c2ecf20Sopenharmony_ci struct can_bittiming *bit_timing = &priv->can.bittiming; 2688c2ecf20Sopenharmony_ci u32 can_btc; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci can_btc = (bit_timing->phase_seg2 - 1) & 0x7; 2718c2ecf20Sopenharmony_ci can_btc |= ((bit_timing->phase_seg1 + bit_timing->prop_seg - 1) 2728c2ecf20Sopenharmony_ci & 0xF) << 3; 2738c2ecf20Sopenharmony_ci if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) { 2748c2ecf20Sopenharmony_ci if (bit_timing->brp > 4) 2758c2ecf20Sopenharmony_ci can_btc |= HECC_CANBTC_SAM; 2768c2ecf20Sopenharmony_ci else 2778c2ecf20Sopenharmony_ci netdev_warn(priv->ndev, 2788c2ecf20Sopenharmony_ci "WARN: Triple sampling not set due to h/w limitations"); 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci can_btc |= ((bit_timing->sjw - 1) & 0x3) << 8; 2818c2ecf20Sopenharmony_ci can_btc |= ((bit_timing->brp - 1) & 0xFF) << 16; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci /* ERM being set to 0 by default meaning resync at falling edge */ 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci hecc_write(priv, HECC_CANBTC, can_btc); 2868c2ecf20Sopenharmony_ci netdev_info(priv->ndev, "setting CANBTC=%#x\n", can_btc); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci return 0; 2898c2ecf20Sopenharmony_ci} 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_cistatic int ti_hecc_transceiver_switch(const struct ti_hecc_priv *priv, 2928c2ecf20Sopenharmony_ci int on) 2938c2ecf20Sopenharmony_ci{ 2948c2ecf20Sopenharmony_ci if (!priv->reg_xceiver) 2958c2ecf20Sopenharmony_ci return 0; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci if (on) 2988c2ecf20Sopenharmony_ci return regulator_enable(priv->reg_xceiver); 2998c2ecf20Sopenharmony_ci else 3008c2ecf20Sopenharmony_ci return regulator_disable(priv->reg_xceiver); 3018c2ecf20Sopenharmony_ci} 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_cistatic void ti_hecc_reset(struct net_device *ndev) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci u32 cnt; 3068c2ecf20Sopenharmony_ci struct ti_hecc_priv *priv = netdev_priv(ndev); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci netdev_dbg(ndev, "resetting hecc ...\n"); 3098c2ecf20Sopenharmony_ci hecc_set_bit(priv, HECC_CANMC, HECC_CANMC_SRES); 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci /* Set change control request and wait till enabled */ 3128c2ecf20Sopenharmony_ci hecc_set_bit(priv, HECC_CANMC, HECC_CANMC_CCR); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci /* INFO: It has been observed that at times CCE bit may not be 3158c2ecf20Sopenharmony_ci * set and hw seems to be ok even if this bit is not set so 3168c2ecf20Sopenharmony_ci * timing out with a timing of 1ms to respect the specs 3178c2ecf20Sopenharmony_ci */ 3188c2ecf20Sopenharmony_ci cnt = HECC_CCE_WAIT_COUNT; 3198c2ecf20Sopenharmony_ci while (!hecc_get_bit(priv, HECC_CANES, HECC_CANES_CCE) && cnt != 0) { 3208c2ecf20Sopenharmony_ci --cnt; 3218c2ecf20Sopenharmony_ci udelay(10); 3228c2ecf20Sopenharmony_ci } 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci /* Note: On HECC, BTC can be programmed only in initialization mode, so 3258c2ecf20Sopenharmony_ci * it is expected that the can bittiming parameters are set via ip 3268c2ecf20Sopenharmony_ci * utility before the device is opened 3278c2ecf20Sopenharmony_ci */ 3288c2ecf20Sopenharmony_ci ti_hecc_set_btc(priv); 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci /* Clear CCR (and CANMC register) and wait for CCE = 0 enable */ 3318c2ecf20Sopenharmony_ci hecc_write(priv, HECC_CANMC, 0); 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci /* INFO: CAN net stack handles bus off and hence disabling auto-bus-on 3348c2ecf20Sopenharmony_ci * hecc_set_bit(priv, HECC_CANMC, HECC_CANMC_ABO); 3358c2ecf20Sopenharmony_ci */ 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci /* INFO: It has been observed that at times CCE bit may not be 3388c2ecf20Sopenharmony_ci * set and hw seems to be ok even if this bit is not set so 3398c2ecf20Sopenharmony_ci */ 3408c2ecf20Sopenharmony_ci cnt = HECC_CCE_WAIT_COUNT; 3418c2ecf20Sopenharmony_ci while (hecc_get_bit(priv, HECC_CANES, HECC_CANES_CCE) && cnt != 0) { 3428c2ecf20Sopenharmony_ci --cnt; 3438c2ecf20Sopenharmony_ci udelay(10); 3448c2ecf20Sopenharmony_ci } 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci /* Enable TX and RX I/O Control pins */ 3478c2ecf20Sopenharmony_ci hecc_write(priv, HECC_CANTIOC, HECC_CANTIOC_EN); 3488c2ecf20Sopenharmony_ci hecc_write(priv, HECC_CANRIOC, HECC_CANRIOC_EN); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci /* Clear registers for clean operation */ 3518c2ecf20Sopenharmony_ci hecc_write(priv, HECC_CANTA, HECC_SET_REG); 3528c2ecf20Sopenharmony_ci hecc_write(priv, HECC_CANRMP, HECC_SET_REG); 3538c2ecf20Sopenharmony_ci hecc_write(priv, HECC_CANGIF0, HECC_SET_REG); 3548c2ecf20Sopenharmony_ci hecc_write(priv, HECC_CANGIF1, HECC_SET_REG); 3558c2ecf20Sopenharmony_ci hecc_write(priv, HECC_CANME, 0); 3568c2ecf20Sopenharmony_ci hecc_write(priv, HECC_CANMD, 0); 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci /* SCC compat mode NOT supported (and not needed too) */ 3598c2ecf20Sopenharmony_ci hecc_set_bit(priv, HECC_CANMC, HECC_CANMC_SCM); 3608c2ecf20Sopenharmony_ci} 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_cistatic void ti_hecc_start(struct net_device *ndev) 3638c2ecf20Sopenharmony_ci{ 3648c2ecf20Sopenharmony_ci struct ti_hecc_priv *priv = netdev_priv(ndev); 3658c2ecf20Sopenharmony_ci u32 cnt, mbxno, mbx_mask; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci /* put HECC in initialization mode and set btc */ 3688c2ecf20Sopenharmony_ci ti_hecc_reset(ndev); 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci priv->tx_head = HECC_TX_MASK; 3718c2ecf20Sopenharmony_ci priv->tx_tail = HECC_TX_MASK; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci /* Enable local and global acceptance mask registers */ 3748c2ecf20Sopenharmony_ci hecc_write(priv, HECC_CANGAM, HECC_SET_REG); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci /* Prepare configured mailboxes to receive messages */ 3778c2ecf20Sopenharmony_ci for (cnt = 0; cnt < HECC_MAX_RX_MBOX; cnt++) { 3788c2ecf20Sopenharmony_ci mbxno = HECC_MAX_MAILBOXES - 1 - cnt; 3798c2ecf20Sopenharmony_ci mbx_mask = BIT(mbxno); 3808c2ecf20Sopenharmony_ci hecc_clear_bit(priv, HECC_CANME, mbx_mask); 3818c2ecf20Sopenharmony_ci hecc_write_mbx(priv, mbxno, HECC_CANMID, HECC_CANMID_AME); 3828c2ecf20Sopenharmony_ci hecc_write_lam(priv, mbxno, HECC_SET_REG); 3838c2ecf20Sopenharmony_ci hecc_set_bit(priv, HECC_CANMD, mbx_mask); 3848c2ecf20Sopenharmony_ci hecc_set_bit(priv, HECC_CANME, mbx_mask); 3858c2ecf20Sopenharmony_ci hecc_set_bit(priv, HECC_CANMIM, mbx_mask); 3868c2ecf20Sopenharmony_ci } 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci /* Enable tx interrupts */ 3898c2ecf20Sopenharmony_ci hecc_set_bit(priv, HECC_CANMIM, BIT(HECC_MAX_TX_MBOX) - 1); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci /* Prevent message over-write to create a rx fifo, but not for 3928c2ecf20Sopenharmony_ci * the lowest priority mailbox, since that allows detecting 3938c2ecf20Sopenharmony_ci * overflows instead of the hardware silently dropping the 3948c2ecf20Sopenharmony_ci * messages. 3958c2ecf20Sopenharmony_ci */ 3968c2ecf20Sopenharmony_ci mbx_mask = ~BIT(HECC_RX_LAST_MBOX); 3978c2ecf20Sopenharmony_ci hecc_write(priv, HECC_CANOPC, mbx_mask); 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci /* Enable interrupts */ 4008c2ecf20Sopenharmony_ci if (priv->use_hecc1int) { 4018c2ecf20Sopenharmony_ci hecc_write(priv, HECC_CANMIL, HECC_SET_REG); 4028c2ecf20Sopenharmony_ci hecc_write(priv, HECC_CANGIM, HECC_CANGIM_DEF_MASK | 4038c2ecf20Sopenharmony_ci HECC_CANGIM_I1EN | HECC_CANGIM_SIL); 4048c2ecf20Sopenharmony_ci } else { 4058c2ecf20Sopenharmony_ci hecc_write(priv, HECC_CANMIL, 0); 4068c2ecf20Sopenharmony_ci hecc_write(priv, HECC_CANGIM, 4078c2ecf20Sopenharmony_ci HECC_CANGIM_DEF_MASK | HECC_CANGIM_I0EN); 4088c2ecf20Sopenharmony_ci } 4098c2ecf20Sopenharmony_ci priv->can.state = CAN_STATE_ERROR_ACTIVE; 4108c2ecf20Sopenharmony_ci} 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_cistatic void ti_hecc_stop(struct net_device *ndev) 4138c2ecf20Sopenharmony_ci{ 4148c2ecf20Sopenharmony_ci struct ti_hecc_priv *priv = netdev_priv(ndev); 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci /* Disable the CPK; stop sending, erroring and acking */ 4178c2ecf20Sopenharmony_ci hecc_set_bit(priv, HECC_CANMC, HECC_CANMC_CCR); 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci /* Disable interrupts and disable mailboxes */ 4208c2ecf20Sopenharmony_ci hecc_write(priv, HECC_CANGIM, 0); 4218c2ecf20Sopenharmony_ci hecc_write(priv, HECC_CANMIM, 0); 4228c2ecf20Sopenharmony_ci hecc_write(priv, HECC_CANME, 0); 4238c2ecf20Sopenharmony_ci priv->can.state = CAN_STATE_STOPPED; 4248c2ecf20Sopenharmony_ci} 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_cistatic int ti_hecc_do_set_mode(struct net_device *ndev, enum can_mode mode) 4278c2ecf20Sopenharmony_ci{ 4288c2ecf20Sopenharmony_ci int ret = 0; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci switch (mode) { 4318c2ecf20Sopenharmony_ci case CAN_MODE_START: 4328c2ecf20Sopenharmony_ci ti_hecc_start(ndev); 4338c2ecf20Sopenharmony_ci netif_wake_queue(ndev); 4348c2ecf20Sopenharmony_ci break; 4358c2ecf20Sopenharmony_ci default: 4368c2ecf20Sopenharmony_ci ret = -EOPNOTSUPP; 4378c2ecf20Sopenharmony_ci break; 4388c2ecf20Sopenharmony_ci } 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci return ret; 4418c2ecf20Sopenharmony_ci} 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_cistatic int ti_hecc_get_berr_counter(const struct net_device *ndev, 4448c2ecf20Sopenharmony_ci struct can_berr_counter *bec) 4458c2ecf20Sopenharmony_ci{ 4468c2ecf20Sopenharmony_ci struct ti_hecc_priv *priv = netdev_priv(ndev); 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci bec->txerr = hecc_read(priv, HECC_CANTEC); 4498c2ecf20Sopenharmony_ci bec->rxerr = hecc_read(priv, HECC_CANREC); 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci return 0; 4528c2ecf20Sopenharmony_ci} 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci/* ti_hecc_xmit: HECC Transmit 4558c2ecf20Sopenharmony_ci * 4568c2ecf20Sopenharmony_ci * The transmit mailboxes start from 0 to HECC_MAX_TX_MBOX. In HECC the 4578c2ecf20Sopenharmony_ci * priority of the mailbox for transmission is dependent upon priority setting 4588c2ecf20Sopenharmony_ci * field in mailbox registers. The mailbox with highest value in priority field 4598c2ecf20Sopenharmony_ci * is transmitted first. Only when two mailboxes have the same value in 4608c2ecf20Sopenharmony_ci * priority field the highest numbered mailbox is transmitted first. 4618c2ecf20Sopenharmony_ci * 4628c2ecf20Sopenharmony_ci * To utilize the HECC priority feature as described above we start with the 4638c2ecf20Sopenharmony_ci * highest numbered mailbox with highest priority level and move on to the next 4648c2ecf20Sopenharmony_ci * mailbox with the same priority level and so on. Once we loop through all the 4658c2ecf20Sopenharmony_ci * transmit mailboxes we choose the next priority level (lower) and so on 4668c2ecf20Sopenharmony_ci * until we reach the lowest priority level on the lowest numbered mailbox 4678c2ecf20Sopenharmony_ci * when we stop transmission until all mailboxes are transmitted and then 4688c2ecf20Sopenharmony_ci * restart at highest numbered mailbox with highest priority. 4698c2ecf20Sopenharmony_ci * 4708c2ecf20Sopenharmony_ci * Two counters (head and tail) are used to track the next mailbox to transmit 4718c2ecf20Sopenharmony_ci * and to track the echo buffer for already transmitted mailbox. The queue 4728c2ecf20Sopenharmony_ci * is stopped when all the mailboxes are busy or when there is a priority 4738c2ecf20Sopenharmony_ci * value roll-over happens. 4748c2ecf20Sopenharmony_ci */ 4758c2ecf20Sopenharmony_cistatic netdev_tx_t ti_hecc_xmit(struct sk_buff *skb, struct net_device *ndev) 4768c2ecf20Sopenharmony_ci{ 4778c2ecf20Sopenharmony_ci struct ti_hecc_priv *priv = netdev_priv(ndev); 4788c2ecf20Sopenharmony_ci struct can_frame *cf = (struct can_frame *)skb->data; 4798c2ecf20Sopenharmony_ci u32 mbxno, mbx_mask, data; 4808c2ecf20Sopenharmony_ci unsigned long flags; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci if (can_dropped_invalid_skb(ndev, skb)) 4838c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci mbxno = get_tx_head_mb(priv); 4868c2ecf20Sopenharmony_ci mbx_mask = BIT(mbxno); 4878c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->mbx_lock, flags); 4888c2ecf20Sopenharmony_ci if (unlikely(hecc_read(priv, HECC_CANME) & mbx_mask)) { 4898c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->mbx_lock, flags); 4908c2ecf20Sopenharmony_ci netif_stop_queue(ndev); 4918c2ecf20Sopenharmony_ci netdev_err(priv->ndev, 4928c2ecf20Sopenharmony_ci "BUG: TX mbx not ready tx_head=%08X, tx_tail=%08X\n", 4938c2ecf20Sopenharmony_ci priv->tx_head, priv->tx_tail); 4948c2ecf20Sopenharmony_ci return NETDEV_TX_BUSY; 4958c2ecf20Sopenharmony_ci } 4968c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->mbx_lock, flags); 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci /* Prepare mailbox for transmission */ 4998c2ecf20Sopenharmony_ci data = cf->can_dlc | (get_tx_head_prio(priv) << 8); 5008c2ecf20Sopenharmony_ci if (cf->can_id & CAN_RTR_FLAG) /* Remote transmission request */ 5018c2ecf20Sopenharmony_ci data |= HECC_CANMCF_RTR; 5028c2ecf20Sopenharmony_ci hecc_write_mbx(priv, mbxno, HECC_CANMCF, data); 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci if (cf->can_id & CAN_EFF_FLAG) /* Extended frame format */ 5058c2ecf20Sopenharmony_ci data = (cf->can_id & CAN_EFF_MASK) | HECC_CANMID_IDE; 5068c2ecf20Sopenharmony_ci else /* Standard frame format */ 5078c2ecf20Sopenharmony_ci data = (cf->can_id & CAN_SFF_MASK) << 18; 5088c2ecf20Sopenharmony_ci hecc_write_mbx(priv, mbxno, HECC_CANMID, data); 5098c2ecf20Sopenharmony_ci hecc_write_mbx(priv, mbxno, HECC_CANMDL, 5108c2ecf20Sopenharmony_ci be32_to_cpu(*(__be32 *)(cf->data))); 5118c2ecf20Sopenharmony_ci if (cf->can_dlc > 4) 5128c2ecf20Sopenharmony_ci hecc_write_mbx(priv, mbxno, HECC_CANMDH, 5138c2ecf20Sopenharmony_ci be32_to_cpu(*(__be32 *)(cf->data + 4))); 5148c2ecf20Sopenharmony_ci else 5158c2ecf20Sopenharmony_ci *(u32 *)(cf->data + 4) = 0; 5168c2ecf20Sopenharmony_ci can_put_echo_skb(skb, ndev, mbxno); 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->mbx_lock, flags); 5198c2ecf20Sopenharmony_ci --priv->tx_head; 5208c2ecf20Sopenharmony_ci if ((hecc_read(priv, HECC_CANME) & BIT(get_tx_head_mb(priv))) || 5218c2ecf20Sopenharmony_ci (priv->tx_head & HECC_TX_MASK) == HECC_TX_MASK) { 5228c2ecf20Sopenharmony_ci netif_stop_queue(ndev); 5238c2ecf20Sopenharmony_ci } 5248c2ecf20Sopenharmony_ci hecc_set_bit(priv, HECC_CANME, mbx_mask); 5258c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->mbx_lock, flags); 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci hecc_write(priv, HECC_CANTRS, mbx_mask); 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 5308c2ecf20Sopenharmony_ci} 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_cistatic inline 5338c2ecf20Sopenharmony_cistruct ti_hecc_priv *rx_offload_to_priv(struct can_rx_offload *offload) 5348c2ecf20Sopenharmony_ci{ 5358c2ecf20Sopenharmony_ci return container_of(offload, struct ti_hecc_priv, offload); 5368c2ecf20Sopenharmony_ci} 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_cistatic struct sk_buff *ti_hecc_mailbox_read(struct can_rx_offload *offload, 5398c2ecf20Sopenharmony_ci unsigned int mbxno, u32 *timestamp, 5408c2ecf20Sopenharmony_ci bool drop) 5418c2ecf20Sopenharmony_ci{ 5428c2ecf20Sopenharmony_ci struct ti_hecc_priv *priv = rx_offload_to_priv(offload); 5438c2ecf20Sopenharmony_ci struct sk_buff *skb; 5448c2ecf20Sopenharmony_ci struct can_frame *cf; 5458c2ecf20Sopenharmony_ci u32 data, mbx_mask; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci mbx_mask = BIT(mbxno); 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci if (unlikely(drop)) { 5508c2ecf20Sopenharmony_ci skb = ERR_PTR(-ENOBUFS); 5518c2ecf20Sopenharmony_ci goto mark_as_read; 5528c2ecf20Sopenharmony_ci } 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci skb = alloc_can_skb(offload->dev, &cf); 5558c2ecf20Sopenharmony_ci if (unlikely(!skb)) { 5568c2ecf20Sopenharmony_ci skb = ERR_PTR(-ENOMEM); 5578c2ecf20Sopenharmony_ci goto mark_as_read; 5588c2ecf20Sopenharmony_ci } 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci data = hecc_read_mbx(priv, mbxno, HECC_CANMID); 5618c2ecf20Sopenharmony_ci if (data & HECC_CANMID_IDE) 5628c2ecf20Sopenharmony_ci cf->can_id = (data & CAN_EFF_MASK) | CAN_EFF_FLAG; 5638c2ecf20Sopenharmony_ci else 5648c2ecf20Sopenharmony_ci cf->can_id = (data >> 18) & CAN_SFF_MASK; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci data = hecc_read_mbx(priv, mbxno, HECC_CANMCF); 5678c2ecf20Sopenharmony_ci if (data & HECC_CANMCF_RTR) 5688c2ecf20Sopenharmony_ci cf->can_id |= CAN_RTR_FLAG; 5698c2ecf20Sopenharmony_ci cf->can_dlc = get_can_dlc(data & 0xF); 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci data = hecc_read_mbx(priv, mbxno, HECC_CANMDL); 5728c2ecf20Sopenharmony_ci *(__be32 *)(cf->data) = cpu_to_be32(data); 5738c2ecf20Sopenharmony_ci if (cf->can_dlc > 4) { 5748c2ecf20Sopenharmony_ci data = hecc_read_mbx(priv, mbxno, HECC_CANMDH); 5758c2ecf20Sopenharmony_ci *(__be32 *)(cf->data + 4) = cpu_to_be32(data); 5768c2ecf20Sopenharmony_ci } 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci *timestamp = hecc_read_stamp(priv, mbxno); 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci /* Check for FIFO overrun. 5818c2ecf20Sopenharmony_ci * 5828c2ecf20Sopenharmony_ci * All but the last RX mailbox have activated overwrite 5838c2ecf20Sopenharmony_ci * protection. So skip check for overrun, if we're not 5848c2ecf20Sopenharmony_ci * handling the last RX mailbox. 5858c2ecf20Sopenharmony_ci * 5868c2ecf20Sopenharmony_ci * As the overwrite protection for the last RX mailbox is 5878c2ecf20Sopenharmony_ci * disabled, the CAN core might update while we're reading 5888c2ecf20Sopenharmony_ci * it. This means the skb might be inconsistent. 5898c2ecf20Sopenharmony_ci * 5908c2ecf20Sopenharmony_ci * Return an error to let rx-offload discard this CAN frame. 5918c2ecf20Sopenharmony_ci */ 5928c2ecf20Sopenharmony_ci if (unlikely(mbxno == HECC_RX_LAST_MBOX && 5938c2ecf20Sopenharmony_ci hecc_read(priv, HECC_CANRML) & mbx_mask)) 5948c2ecf20Sopenharmony_ci skb = ERR_PTR(-ENOBUFS); 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci mark_as_read: 5978c2ecf20Sopenharmony_ci hecc_write(priv, HECC_CANRMP, mbx_mask); 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci return skb; 6008c2ecf20Sopenharmony_ci} 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_cistatic int ti_hecc_error(struct net_device *ndev, int int_status, 6038c2ecf20Sopenharmony_ci int err_status) 6048c2ecf20Sopenharmony_ci{ 6058c2ecf20Sopenharmony_ci struct ti_hecc_priv *priv = netdev_priv(ndev); 6068c2ecf20Sopenharmony_ci struct can_frame *cf; 6078c2ecf20Sopenharmony_ci struct sk_buff *skb; 6088c2ecf20Sopenharmony_ci u32 timestamp; 6098c2ecf20Sopenharmony_ci int err; 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci if (err_status & HECC_BUS_ERROR) { 6128c2ecf20Sopenharmony_ci /* propagate the error condition to the can stack */ 6138c2ecf20Sopenharmony_ci skb = alloc_can_err_skb(ndev, &cf); 6148c2ecf20Sopenharmony_ci if (!skb) { 6158c2ecf20Sopenharmony_ci if (net_ratelimit()) 6168c2ecf20Sopenharmony_ci netdev_err(priv->ndev, 6178c2ecf20Sopenharmony_ci "%s: alloc_can_err_skb() failed\n", 6188c2ecf20Sopenharmony_ci __func__); 6198c2ecf20Sopenharmony_ci return -ENOMEM; 6208c2ecf20Sopenharmony_ci } 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci ++priv->can.can_stats.bus_error; 6238c2ecf20Sopenharmony_ci cf->can_id |= CAN_ERR_BUSERROR | CAN_ERR_PROT; 6248c2ecf20Sopenharmony_ci if (err_status & HECC_CANES_FE) 6258c2ecf20Sopenharmony_ci cf->data[2] |= CAN_ERR_PROT_FORM; 6268c2ecf20Sopenharmony_ci if (err_status & HECC_CANES_BE) 6278c2ecf20Sopenharmony_ci cf->data[2] |= CAN_ERR_PROT_BIT; 6288c2ecf20Sopenharmony_ci if (err_status & HECC_CANES_SE) 6298c2ecf20Sopenharmony_ci cf->data[2] |= CAN_ERR_PROT_STUFF; 6308c2ecf20Sopenharmony_ci if (err_status & HECC_CANES_CRCE) 6318c2ecf20Sopenharmony_ci cf->data[3] = CAN_ERR_PROT_LOC_CRC_SEQ; 6328c2ecf20Sopenharmony_ci if (err_status & HECC_CANES_ACKE) 6338c2ecf20Sopenharmony_ci cf->data[3] = CAN_ERR_PROT_LOC_ACK; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci timestamp = hecc_read(priv, HECC_CANLNT); 6368c2ecf20Sopenharmony_ci err = can_rx_offload_queue_sorted(&priv->offload, skb, 6378c2ecf20Sopenharmony_ci timestamp); 6388c2ecf20Sopenharmony_ci if (err) 6398c2ecf20Sopenharmony_ci ndev->stats.rx_fifo_errors++; 6408c2ecf20Sopenharmony_ci } 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci hecc_write(priv, HECC_CANES, HECC_CANES_FLAGS); 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci return 0; 6458c2ecf20Sopenharmony_ci} 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_cistatic void ti_hecc_change_state(struct net_device *ndev, 6488c2ecf20Sopenharmony_ci enum can_state rx_state, 6498c2ecf20Sopenharmony_ci enum can_state tx_state) 6508c2ecf20Sopenharmony_ci{ 6518c2ecf20Sopenharmony_ci struct ti_hecc_priv *priv = netdev_priv(ndev); 6528c2ecf20Sopenharmony_ci struct can_frame *cf; 6538c2ecf20Sopenharmony_ci struct sk_buff *skb; 6548c2ecf20Sopenharmony_ci u32 timestamp; 6558c2ecf20Sopenharmony_ci int err; 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci skb = alloc_can_err_skb(priv->ndev, &cf); 6588c2ecf20Sopenharmony_ci if (unlikely(!skb)) { 6598c2ecf20Sopenharmony_ci priv->can.state = max(tx_state, rx_state); 6608c2ecf20Sopenharmony_ci return; 6618c2ecf20Sopenharmony_ci } 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci can_change_state(priv->ndev, cf, tx_state, rx_state); 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci if (max(tx_state, rx_state) != CAN_STATE_BUS_OFF) { 6668c2ecf20Sopenharmony_ci cf->data[6] = hecc_read(priv, HECC_CANTEC); 6678c2ecf20Sopenharmony_ci cf->data[7] = hecc_read(priv, HECC_CANREC); 6688c2ecf20Sopenharmony_ci } 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci timestamp = hecc_read(priv, HECC_CANLNT); 6718c2ecf20Sopenharmony_ci err = can_rx_offload_queue_sorted(&priv->offload, skb, timestamp); 6728c2ecf20Sopenharmony_ci if (err) 6738c2ecf20Sopenharmony_ci ndev->stats.rx_fifo_errors++; 6748c2ecf20Sopenharmony_ci} 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_cistatic irqreturn_t ti_hecc_interrupt(int irq, void *dev_id) 6778c2ecf20Sopenharmony_ci{ 6788c2ecf20Sopenharmony_ci struct net_device *ndev = (struct net_device *)dev_id; 6798c2ecf20Sopenharmony_ci struct ti_hecc_priv *priv = netdev_priv(ndev); 6808c2ecf20Sopenharmony_ci struct net_device_stats *stats = &ndev->stats; 6818c2ecf20Sopenharmony_ci u32 mbxno, mbx_mask, int_status, err_status, stamp; 6828c2ecf20Sopenharmony_ci unsigned long flags, rx_pending; 6838c2ecf20Sopenharmony_ci u32 handled = 0; 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci int_status = hecc_read(priv, 6868c2ecf20Sopenharmony_ci priv->use_hecc1int ? 6878c2ecf20Sopenharmony_ci HECC_CANGIF1 : HECC_CANGIF0); 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci if (!int_status) 6908c2ecf20Sopenharmony_ci return IRQ_NONE; 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci err_status = hecc_read(priv, HECC_CANES); 6938c2ecf20Sopenharmony_ci if (unlikely(err_status & HECC_CANES_FLAGS)) 6948c2ecf20Sopenharmony_ci ti_hecc_error(ndev, int_status, err_status); 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci if (unlikely(int_status & HECC_CANGIM_DEF_MASK)) { 6978c2ecf20Sopenharmony_ci enum can_state rx_state, tx_state; 6988c2ecf20Sopenharmony_ci u32 rec = hecc_read(priv, HECC_CANREC); 6998c2ecf20Sopenharmony_ci u32 tec = hecc_read(priv, HECC_CANTEC); 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci if (int_status & HECC_CANGIF_WLIF) { 7028c2ecf20Sopenharmony_ci handled |= HECC_CANGIF_WLIF; 7038c2ecf20Sopenharmony_ci rx_state = rec >= tec ? CAN_STATE_ERROR_WARNING : 0; 7048c2ecf20Sopenharmony_ci tx_state = rec <= tec ? CAN_STATE_ERROR_WARNING : 0; 7058c2ecf20Sopenharmony_ci netdev_dbg(priv->ndev, "Error Warning interrupt\n"); 7068c2ecf20Sopenharmony_ci ti_hecc_change_state(ndev, rx_state, tx_state); 7078c2ecf20Sopenharmony_ci } 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci if (int_status & HECC_CANGIF_EPIF) { 7108c2ecf20Sopenharmony_ci handled |= HECC_CANGIF_EPIF; 7118c2ecf20Sopenharmony_ci rx_state = rec >= tec ? CAN_STATE_ERROR_PASSIVE : 0; 7128c2ecf20Sopenharmony_ci tx_state = rec <= tec ? CAN_STATE_ERROR_PASSIVE : 0; 7138c2ecf20Sopenharmony_ci netdev_dbg(priv->ndev, "Error passive interrupt\n"); 7148c2ecf20Sopenharmony_ci ti_hecc_change_state(ndev, rx_state, tx_state); 7158c2ecf20Sopenharmony_ci } 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci if (int_status & HECC_CANGIF_BOIF) { 7188c2ecf20Sopenharmony_ci handled |= HECC_CANGIF_BOIF; 7198c2ecf20Sopenharmony_ci rx_state = CAN_STATE_BUS_OFF; 7208c2ecf20Sopenharmony_ci tx_state = CAN_STATE_BUS_OFF; 7218c2ecf20Sopenharmony_ci netdev_dbg(priv->ndev, "Bus off interrupt\n"); 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci /* Disable all interrupts */ 7248c2ecf20Sopenharmony_ci hecc_write(priv, HECC_CANGIM, 0); 7258c2ecf20Sopenharmony_ci can_bus_off(ndev); 7268c2ecf20Sopenharmony_ci ti_hecc_change_state(ndev, rx_state, tx_state); 7278c2ecf20Sopenharmony_ci } 7288c2ecf20Sopenharmony_ci } else if (unlikely(priv->can.state != CAN_STATE_ERROR_ACTIVE)) { 7298c2ecf20Sopenharmony_ci enum can_state new_state, tx_state, rx_state; 7308c2ecf20Sopenharmony_ci u32 rec = hecc_read(priv, HECC_CANREC); 7318c2ecf20Sopenharmony_ci u32 tec = hecc_read(priv, HECC_CANTEC); 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci if (rec >= 128 || tec >= 128) 7348c2ecf20Sopenharmony_ci new_state = CAN_STATE_ERROR_PASSIVE; 7358c2ecf20Sopenharmony_ci else if (rec >= 96 || tec >= 96) 7368c2ecf20Sopenharmony_ci new_state = CAN_STATE_ERROR_WARNING; 7378c2ecf20Sopenharmony_ci else 7388c2ecf20Sopenharmony_ci new_state = CAN_STATE_ERROR_ACTIVE; 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci if (new_state < priv->can.state) { 7418c2ecf20Sopenharmony_ci rx_state = rec >= tec ? new_state : 0; 7428c2ecf20Sopenharmony_ci tx_state = rec <= tec ? new_state : 0; 7438c2ecf20Sopenharmony_ci ti_hecc_change_state(ndev, rx_state, tx_state); 7448c2ecf20Sopenharmony_ci } 7458c2ecf20Sopenharmony_ci } 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci if (int_status & HECC_CANGIF_GMIF) { 7488c2ecf20Sopenharmony_ci while (priv->tx_tail - priv->tx_head > 0) { 7498c2ecf20Sopenharmony_ci mbxno = get_tx_tail_mb(priv); 7508c2ecf20Sopenharmony_ci mbx_mask = BIT(mbxno); 7518c2ecf20Sopenharmony_ci if (!(mbx_mask & hecc_read(priv, HECC_CANTA))) 7528c2ecf20Sopenharmony_ci break; 7538c2ecf20Sopenharmony_ci hecc_write(priv, HECC_CANTA, mbx_mask); 7548c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->mbx_lock, flags); 7558c2ecf20Sopenharmony_ci hecc_clear_bit(priv, HECC_CANME, mbx_mask); 7568c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->mbx_lock, flags); 7578c2ecf20Sopenharmony_ci stamp = hecc_read_stamp(priv, mbxno); 7588c2ecf20Sopenharmony_ci stats->tx_bytes += 7598c2ecf20Sopenharmony_ci can_rx_offload_get_echo_skb(&priv->offload, 7608c2ecf20Sopenharmony_ci mbxno, stamp); 7618c2ecf20Sopenharmony_ci stats->tx_packets++; 7628c2ecf20Sopenharmony_ci can_led_event(ndev, CAN_LED_EVENT_TX); 7638c2ecf20Sopenharmony_ci --priv->tx_tail; 7648c2ecf20Sopenharmony_ci } 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci /* restart queue if wrap-up or if queue stalled on last pkt */ 7678c2ecf20Sopenharmony_ci if ((priv->tx_head == priv->tx_tail && 7688c2ecf20Sopenharmony_ci ((priv->tx_head & HECC_TX_MASK) != HECC_TX_MASK)) || 7698c2ecf20Sopenharmony_ci (((priv->tx_tail & HECC_TX_MASK) == HECC_TX_MASK) && 7708c2ecf20Sopenharmony_ci ((priv->tx_head & HECC_TX_MASK) == HECC_TX_MASK))) 7718c2ecf20Sopenharmony_ci netif_wake_queue(ndev); 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci /* offload RX mailboxes and let NAPI deliver them */ 7748c2ecf20Sopenharmony_ci while ((rx_pending = hecc_read(priv, HECC_CANRMP))) { 7758c2ecf20Sopenharmony_ci can_rx_offload_irq_offload_timestamp(&priv->offload, 7768c2ecf20Sopenharmony_ci rx_pending); 7778c2ecf20Sopenharmony_ci } 7788c2ecf20Sopenharmony_ci } 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci /* clear all interrupt conditions - read back to avoid spurious ints */ 7818c2ecf20Sopenharmony_ci if (priv->use_hecc1int) { 7828c2ecf20Sopenharmony_ci hecc_write(priv, HECC_CANGIF1, handled); 7838c2ecf20Sopenharmony_ci int_status = hecc_read(priv, HECC_CANGIF1); 7848c2ecf20Sopenharmony_ci } else { 7858c2ecf20Sopenharmony_ci hecc_write(priv, HECC_CANGIF0, handled); 7868c2ecf20Sopenharmony_ci int_status = hecc_read(priv, HECC_CANGIF0); 7878c2ecf20Sopenharmony_ci } 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci return IRQ_HANDLED; 7908c2ecf20Sopenharmony_ci} 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_cistatic int ti_hecc_open(struct net_device *ndev) 7938c2ecf20Sopenharmony_ci{ 7948c2ecf20Sopenharmony_ci struct ti_hecc_priv *priv = netdev_priv(ndev); 7958c2ecf20Sopenharmony_ci int err; 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci err = request_irq(ndev->irq, ti_hecc_interrupt, IRQF_SHARED, 7988c2ecf20Sopenharmony_ci ndev->name, ndev); 7998c2ecf20Sopenharmony_ci if (err) { 8008c2ecf20Sopenharmony_ci netdev_err(ndev, "error requesting interrupt\n"); 8018c2ecf20Sopenharmony_ci return err; 8028c2ecf20Sopenharmony_ci } 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci ti_hecc_transceiver_switch(priv, 1); 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci /* Open common can device */ 8078c2ecf20Sopenharmony_ci err = open_candev(ndev); 8088c2ecf20Sopenharmony_ci if (err) { 8098c2ecf20Sopenharmony_ci netdev_err(ndev, "open_candev() failed %d\n", err); 8108c2ecf20Sopenharmony_ci ti_hecc_transceiver_switch(priv, 0); 8118c2ecf20Sopenharmony_ci free_irq(ndev->irq, ndev); 8128c2ecf20Sopenharmony_ci return err; 8138c2ecf20Sopenharmony_ci } 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci can_led_event(ndev, CAN_LED_EVENT_OPEN); 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci ti_hecc_start(ndev); 8188c2ecf20Sopenharmony_ci can_rx_offload_enable(&priv->offload); 8198c2ecf20Sopenharmony_ci netif_start_queue(ndev); 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci return 0; 8228c2ecf20Sopenharmony_ci} 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_cistatic int ti_hecc_close(struct net_device *ndev) 8258c2ecf20Sopenharmony_ci{ 8268c2ecf20Sopenharmony_ci struct ti_hecc_priv *priv = netdev_priv(ndev); 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci netif_stop_queue(ndev); 8298c2ecf20Sopenharmony_ci can_rx_offload_disable(&priv->offload); 8308c2ecf20Sopenharmony_ci ti_hecc_stop(ndev); 8318c2ecf20Sopenharmony_ci free_irq(ndev->irq, ndev); 8328c2ecf20Sopenharmony_ci close_candev(ndev); 8338c2ecf20Sopenharmony_ci ti_hecc_transceiver_switch(priv, 0); 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci can_led_event(ndev, CAN_LED_EVENT_STOP); 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci return 0; 8388c2ecf20Sopenharmony_ci} 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_cistatic const struct net_device_ops ti_hecc_netdev_ops = { 8418c2ecf20Sopenharmony_ci .ndo_open = ti_hecc_open, 8428c2ecf20Sopenharmony_ci .ndo_stop = ti_hecc_close, 8438c2ecf20Sopenharmony_ci .ndo_start_xmit = ti_hecc_xmit, 8448c2ecf20Sopenharmony_ci .ndo_change_mtu = can_change_mtu, 8458c2ecf20Sopenharmony_ci}; 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_cistatic const struct of_device_id ti_hecc_dt_ids[] = { 8488c2ecf20Sopenharmony_ci { 8498c2ecf20Sopenharmony_ci .compatible = "ti,am3517-hecc", 8508c2ecf20Sopenharmony_ci }, 8518c2ecf20Sopenharmony_ci { } 8528c2ecf20Sopenharmony_ci}; 8538c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, ti_hecc_dt_ids); 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_cistatic int ti_hecc_probe(struct platform_device *pdev) 8568c2ecf20Sopenharmony_ci{ 8578c2ecf20Sopenharmony_ci struct net_device *ndev = (struct net_device *)0; 8588c2ecf20Sopenharmony_ci struct ti_hecc_priv *priv; 8598c2ecf20Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 8608c2ecf20Sopenharmony_ci struct resource *irq; 8618c2ecf20Sopenharmony_ci struct regulator *reg_xceiver; 8628c2ecf20Sopenharmony_ci int err = -ENODEV; 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci if (!IS_ENABLED(CONFIG_OF) || !np) 8658c2ecf20Sopenharmony_ci return -EINVAL; 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci reg_xceiver = devm_regulator_get(&pdev->dev, "xceiver"); 8688c2ecf20Sopenharmony_ci if (PTR_ERR(reg_xceiver) == -EPROBE_DEFER) 8698c2ecf20Sopenharmony_ci return -EPROBE_DEFER; 8708c2ecf20Sopenharmony_ci else if (IS_ERR(reg_xceiver)) 8718c2ecf20Sopenharmony_ci reg_xceiver = NULL; 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci ndev = alloc_candev(sizeof(struct ti_hecc_priv), HECC_MAX_TX_MBOX); 8748c2ecf20Sopenharmony_ci if (!ndev) { 8758c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "alloc_candev failed\n"); 8768c2ecf20Sopenharmony_ci return -ENOMEM; 8778c2ecf20Sopenharmony_ci } 8788c2ecf20Sopenharmony_ci priv = netdev_priv(ndev); 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci /* handle hecc memory */ 8818c2ecf20Sopenharmony_ci priv->base = devm_platform_ioremap_resource_byname(pdev, "hecc"); 8828c2ecf20Sopenharmony_ci if (IS_ERR(priv->base)) { 8838c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "hecc ioremap failed\n"); 8848c2ecf20Sopenharmony_ci err = PTR_ERR(priv->base); 8858c2ecf20Sopenharmony_ci goto probe_exit_candev; 8868c2ecf20Sopenharmony_ci } 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci /* handle hecc-ram memory */ 8898c2ecf20Sopenharmony_ci priv->hecc_ram = devm_platform_ioremap_resource_byname(pdev, 8908c2ecf20Sopenharmony_ci "hecc-ram"); 8918c2ecf20Sopenharmony_ci if (IS_ERR(priv->hecc_ram)) { 8928c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "hecc-ram ioremap failed\n"); 8938c2ecf20Sopenharmony_ci err = PTR_ERR(priv->hecc_ram); 8948c2ecf20Sopenharmony_ci goto probe_exit_candev; 8958c2ecf20Sopenharmony_ci } 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci /* handle mbx memory */ 8988c2ecf20Sopenharmony_ci priv->mbx = devm_platform_ioremap_resource_byname(pdev, "mbx"); 8998c2ecf20Sopenharmony_ci if (IS_ERR(priv->mbx)) { 9008c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "mbx ioremap failed\n"); 9018c2ecf20Sopenharmony_ci err = PTR_ERR(priv->mbx); 9028c2ecf20Sopenharmony_ci goto probe_exit_candev; 9038c2ecf20Sopenharmony_ci } 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); 9068c2ecf20Sopenharmony_ci if (!irq) { 9078c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "No irq resource\n"); 9088c2ecf20Sopenharmony_ci goto probe_exit_candev; 9098c2ecf20Sopenharmony_ci } 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci priv->ndev = ndev; 9128c2ecf20Sopenharmony_ci priv->reg_xceiver = reg_xceiver; 9138c2ecf20Sopenharmony_ci priv->use_hecc1int = of_property_read_bool(np, "ti,use-hecc1int"); 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci priv->can.bittiming_const = &ti_hecc_bittiming_const; 9168c2ecf20Sopenharmony_ci priv->can.do_set_mode = ti_hecc_do_set_mode; 9178c2ecf20Sopenharmony_ci priv->can.do_get_berr_counter = ti_hecc_get_berr_counter; 9188c2ecf20Sopenharmony_ci priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES; 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci spin_lock_init(&priv->mbx_lock); 9218c2ecf20Sopenharmony_ci ndev->irq = irq->start; 9228c2ecf20Sopenharmony_ci ndev->flags |= IFF_ECHO; 9238c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, ndev); 9248c2ecf20Sopenharmony_ci SET_NETDEV_DEV(ndev, &pdev->dev); 9258c2ecf20Sopenharmony_ci ndev->netdev_ops = &ti_hecc_netdev_ops; 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci priv->clk = clk_get(&pdev->dev, "hecc_ck"); 9288c2ecf20Sopenharmony_ci if (IS_ERR(priv->clk)) { 9298c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "No clock available\n"); 9308c2ecf20Sopenharmony_ci err = PTR_ERR(priv->clk); 9318c2ecf20Sopenharmony_ci priv->clk = NULL; 9328c2ecf20Sopenharmony_ci goto probe_exit_candev; 9338c2ecf20Sopenharmony_ci } 9348c2ecf20Sopenharmony_ci priv->can.clock.freq = clk_get_rate(priv->clk); 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci err = clk_prepare_enable(priv->clk); 9378c2ecf20Sopenharmony_ci if (err) { 9388c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "clk_prepare_enable() failed\n"); 9398c2ecf20Sopenharmony_ci goto probe_exit_release_clk; 9408c2ecf20Sopenharmony_ci } 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci priv->offload.mailbox_read = ti_hecc_mailbox_read; 9438c2ecf20Sopenharmony_ci priv->offload.mb_first = HECC_RX_FIRST_MBOX; 9448c2ecf20Sopenharmony_ci priv->offload.mb_last = HECC_RX_LAST_MBOX; 9458c2ecf20Sopenharmony_ci err = can_rx_offload_add_timestamp(ndev, &priv->offload); 9468c2ecf20Sopenharmony_ci if (err) { 9478c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "can_rx_offload_add_timestamp() failed\n"); 9488c2ecf20Sopenharmony_ci goto probe_exit_disable_clk; 9498c2ecf20Sopenharmony_ci } 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci err = register_candev(ndev); 9528c2ecf20Sopenharmony_ci if (err) { 9538c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "register_candev() failed\n"); 9548c2ecf20Sopenharmony_ci goto probe_exit_offload; 9558c2ecf20Sopenharmony_ci } 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci devm_can_led_init(ndev); 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "device registered (reg_base=%p, irq=%u)\n", 9608c2ecf20Sopenharmony_ci priv->base, (u32)ndev->irq); 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci return 0; 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ciprobe_exit_offload: 9658c2ecf20Sopenharmony_ci can_rx_offload_del(&priv->offload); 9668c2ecf20Sopenharmony_ciprobe_exit_disable_clk: 9678c2ecf20Sopenharmony_ci clk_disable_unprepare(priv->clk); 9688c2ecf20Sopenharmony_ciprobe_exit_release_clk: 9698c2ecf20Sopenharmony_ci clk_put(priv->clk); 9708c2ecf20Sopenharmony_ciprobe_exit_candev: 9718c2ecf20Sopenharmony_ci free_candev(ndev); 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci return err; 9748c2ecf20Sopenharmony_ci} 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_cistatic int ti_hecc_remove(struct platform_device *pdev) 9778c2ecf20Sopenharmony_ci{ 9788c2ecf20Sopenharmony_ci struct net_device *ndev = platform_get_drvdata(pdev); 9798c2ecf20Sopenharmony_ci struct ti_hecc_priv *priv = netdev_priv(ndev); 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci unregister_candev(ndev); 9828c2ecf20Sopenharmony_ci clk_disable_unprepare(priv->clk); 9838c2ecf20Sopenharmony_ci clk_put(priv->clk); 9848c2ecf20Sopenharmony_ci can_rx_offload_del(&priv->offload); 9858c2ecf20Sopenharmony_ci free_candev(ndev); 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci return 0; 9888c2ecf20Sopenharmony_ci} 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 9918c2ecf20Sopenharmony_cistatic int ti_hecc_suspend(struct platform_device *pdev, pm_message_t state) 9928c2ecf20Sopenharmony_ci{ 9938c2ecf20Sopenharmony_ci struct net_device *dev = platform_get_drvdata(pdev); 9948c2ecf20Sopenharmony_ci struct ti_hecc_priv *priv = netdev_priv(dev); 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci if (netif_running(dev)) { 9978c2ecf20Sopenharmony_ci netif_stop_queue(dev); 9988c2ecf20Sopenharmony_ci netif_device_detach(dev); 9998c2ecf20Sopenharmony_ci } 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci hecc_set_bit(priv, HECC_CANMC, HECC_CANMC_PDR); 10028c2ecf20Sopenharmony_ci priv->can.state = CAN_STATE_SLEEPING; 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci clk_disable_unprepare(priv->clk); 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci return 0; 10078c2ecf20Sopenharmony_ci} 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_cistatic int ti_hecc_resume(struct platform_device *pdev) 10108c2ecf20Sopenharmony_ci{ 10118c2ecf20Sopenharmony_ci struct net_device *dev = platform_get_drvdata(pdev); 10128c2ecf20Sopenharmony_ci struct ti_hecc_priv *priv = netdev_priv(dev); 10138c2ecf20Sopenharmony_ci int err; 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci err = clk_prepare_enable(priv->clk); 10168c2ecf20Sopenharmony_ci if (err) 10178c2ecf20Sopenharmony_ci return err; 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci hecc_clear_bit(priv, HECC_CANMC, HECC_CANMC_PDR); 10208c2ecf20Sopenharmony_ci priv->can.state = CAN_STATE_ERROR_ACTIVE; 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci if (netif_running(dev)) { 10238c2ecf20Sopenharmony_ci netif_device_attach(dev); 10248c2ecf20Sopenharmony_ci netif_start_queue(dev); 10258c2ecf20Sopenharmony_ci } 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci return 0; 10288c2ecf20Sopenharmony_ci} 10298c2ecf20Sopenharmony_ci#else 10308c2ecf20Sopenharmony_ci#define ti_hecc_suspend NULL 10318c2ecf20Sopenharmony_ci#define ti_hecc_resume NULL 10328c2ecf20Sopenharmony_ci#endif 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci/* TI HECC netdevice driver: platform driver structure */ 10358c2ecf20Sopenharmony_cistatic struct platform_driver ti_hecc_driver = { 10368c2ecf20Sopenharmony_ci .driver = { 10378c2ecf20Sopenharmony_ci .name = DRV_NAME, 10388c2ecf20Sopenharmony_ci .of_match_table = ti_hecc_dt_ids, 10398c2ecf20Sopenharmony_ci }, 10408c2ecf20Sopenharmony_ci .probe = ti_hecc_probe, 10418c2ecf20Sopenharmony_ci .remove = ti_hecc_remove, 10428c2ecf20Sopenharmony_ci .suspend = ti_hecc_suspend, 10438c2ecf20Sopenharmony_ci .resume = ti_hecc_resume, 10448c2ecf20Sopenharmony_ci}; 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_cimodule_platform_driver(ti_hecc_driver); 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ciMODULE_AUTHOR("Anant Gole <anantgole@ti.com>"); 10498c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 10508c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRV_DESC); 10518c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:" DRV_NAME); 1052