Commit 00acebcb authored by Pavel Pisa's avatar Pavel Pisa
Browse files

driver: use configurable io routines in HW and ensure that driver loops to solve all IRQs.



The loop is mandatory when edge triggered interrupts are used.
It is necessary to achieve state when all enabled interrupts are
served and cleared or disabled and actual IRQ pin or MSI
request is inactive.

Some more cleanup to access chip only through routines
and register description found in ctu_can_fd_hw.h
and ctu_can_fd_regs.h.
Signed-off-by: Pavel Pisa's avatarPavel Pisa <pisa@cmp.felk.cvut.cz>
parent f9fa2d10
......@@ -77,8 +77,13 @@ struct ctucan_priv {
struct napi_struct napi;
struct device *dev;
struct clk *can_clk;
int irq_flags;
long unsigned int drv_flags;
};
#define CTUCAN_FLAG_RX_SCHED 1
static int ctucan_reset(struct net_device *ndev)
{
int i;
......@@ -109,12 +114,9 @@ static int ctucan_set_bittiming(struct net_device *ndev)
{
struct ctucan_priv *priv = netdev_priv(ndev);
struct can_bittiming *bt = &priv->can.bittiming;
union ctu_can_fd_mode_command_status_settings mode;
netdev_dbg(ndev, "ctucan_set_bittiming");
mode.u32 = priv->p.read_reg(&priv->p, CTU_CAN_FD_MODE);
if (mode.s.ena) {
if (ctu_can_fd_is_enabled(&priv->p)) {
netdev_alert(ndev,
"BUG! Cannot set bittiming - CAN is enabled\n");
return -EPERM;
......@@ -138,12 +140,9 @@ static int ctucan_set_data_bittiming(struct net_device *ndev)
{
struct ctucan_priv *priv = netdev_priv(ndev);
struct can_bittiming *dbt = &priv->can.data_bittiming;
union ctu_can_fd_mode_command_status_settings mode;
netdev_dbg(ndev, "ctucan_set_data_bittiming");
mode.u32 = priv->p.read_reg(&priv->p, CTU_CAN_FD_MODE);
if (mode.s.ena) {
if (ctu_can_fd_is_enabled(&priv->p)) {
netdev_alert(ndev,
"BUG! Cannot set bittiming - CAN is enabled\n");
return -EPERM;
......@@ -217,8 +216,10 @@ static int ctucan_chip_start(struct net_device *ndev)
int_msk.u32 = ~int_ena.u32; /* mask all disabled interrupts */
ctu_can_fd_int_ena(&priv->p, int_ena, int_enamask_mask);
clear_bit(CTUCAN_FLAG_RX_SCHED, &priv->drv_flags);
ctu_can_fd_int_mask(&priv->p, int_msk, int_enamask_mask);
ctu_can_fd_int_ena(&priv->p, int_ena, int_enamask_mask);
priv->can.state = CAN_STATE_ERROR_ACTIVE;
......@@ -331,10 +332,9 @@ static int ctucan_rx(struct net_device *ndev)
struct sk_buff *skb;
u64 ts;
union ctu_can_fd_frame_form_w ffw;
//netdev_dbg(ndev, "ctucan_rx");
ffw = ctu_can_fd_read_rx_ffw(&priv->p);
ffw.u32 = priv->p.read_reg(&priv->p, CTU_CAN_FD_RX_DATA);
if (ffw.s.fdf == FD_CAN)
skb = alloc_canfd_skb(ndev, &cf);
else
......@@ -344,7 +344,7 @@ static int ctucan_rx(struct net_device *ndev)
int i;
/* Remove the rest of the frame from the controller */
for (i = 0; i < ffw.s.rwcnt; i++)
priv->p.read_reg(&priv->p, CTU_CAN_FD_RX_DATA);
ctu_can_fd_read_rx_word(&priv->p);
stats->rx_dropped++;
return 0;
......@@ -539,7 +539,8 @@ static int ctucan_rx_poll(struct napi_struct *napi, int quota)
if (work_done < quota) {
if (napi_complete_done(napi, work_done)) {
iec.s.doi = 1; /* Also re-enable DOI */
priv->p.write_reg(&priv->p, CTU_CAN_FD_INT_ENA_SET, iec.u32);
clear_bit(CTUCAN_FLAG_RX_SCHED, &priv->drv_flags);
ctu_can_fd_int_ena_set(&priv->p, iec);
}
}
......@@ -664,60 +665,66 @@ static irqreturn_t ctucan_interrupt(int irq, void *dev_id)
struct net_device *ndev = (struct net_device *)dev_id;
struct ctucan_priv *priv = netdev_priv(ndev);
union ctu_can_fd_int_stat isr, icr;
int irq_loops = 0;
netdev_dbg(ndev, "ctucan_interrupt");
/* Get the interrupt status */
isr = ctu_can_fd_int_sts(&priv->p);
do {
/* Get the interrupt status */
isr = ctu_can_fd_int_sts(&priv->p);
if (!isr.u32)
return IRQ_NONE;
if (test_bit(CTUCAN_FLAG_RX_SCHED, &priv->drv_flags))
isr.s.rbnei = 0;
/* Receive Buffer Not Empty Interrupt */
if (isr.s.rbnei) {
netdev_dbg(ndev, "RXBNEI");
icr.u32 = 0;
icr.s.rbnei = 1;
ctu_can_fd_int_clr(&priv->p, icr);
if (!isr.u32)
return irq_loops? IRQ_HANDLED: IRQ_NONE;
/* Disable RXBNEI and DOI */
icr.s.doi = 1;
priv->p.write_reg(&priv->p, CTU_CAN_FD_INT_ENA_CLR, icr.u32);
napi_schedule(&priv->napi);
}
#define CTUCANFD_INT_RI BIT(0)
#define CTUCANFD_INT_TI BIT(1)
#define CTUCANFD_INT_EI BIT(2)
#define CTUCANFD_INT_DOI BIT(3)
#define CTUCANFD_INT_EPI BIT(4)
#define CTUCANFD_INT_ALI BIT(5)
#define CTUCANFD_INT_BEI BIT(6)
#define CTUCANFD_INT_LFI BIT(7)
#define CTUCANFD_INT_RFI BIT(8)
#define CTUCANFD_INT_BSI BIT(9)
#define CTUCANFD_INT_RBNEI BIT(10)
#define CTUCANFD_INT_TXBHCI BIT(11)
#define CTUCANFD_INT_ERROR (CTUCANFD_INT_EI | CTUCANFD_INT_DOI | \
CTUCANFD_INT_EPI | CTUCANFD_INT_ALI | \
CTUCANFD_INT_BEI)
/* TX Buffer HW Command Interrupt */
if (isr.s.txbhci) {
netdev_dbg(ndev, "TXBHCI");
icr.u32 = 0;
icr.s.txbhci = 1;
ctu_can_fd_int_clr(&priv->p, icr);
ctucan_tx_interrupt(ndev);
}
/* Receive Buffer Not Empty Interrupt */
if (isr.s.rbnei) {
netdev_dbg(ndev, "RXBNEI");
icr.u32 = 0;
icr.s.rbnei = 1;
ctu_can_fd_int_clr(&priv->p, icr);
/* Error interrupts */
if (isr.s.ewli || isr.s.doi || isr.s.epi || isr.s.ali) {
icr.u32 = isr.u32 & CTUCANFD_INT_ERROR;
netdev_dbg(ndev, "some ERR interrupt: clearing 0x%08x", icr.u32);
ctu_can_fd_int_clr(&priv->p, icr);
ctucan_err_interrupt(ndev, isr);
}
/* Disable RXBNEI and DOI */
icr.s.doi = 1;
ctu_can_fd_int_ena_clr(&priv->p, icr);
set_bit(CTUCAN_FLAG_RX_SCHED, &priv->drv_flags);
napi_schedule(&priv->napi);
}
/* Ignore RI, TI, LFI, RFI, BSI */
/* TX Buffer HW Command Interrupt */
if (isr.s.txbhci) {
netdev_dbg(ndev, "TXBHCI");
icr.u32 = 0;
icr.s.txbhci = 1;
ctu_can_fd_int_clr(&priv->p, icr);
ctucan_tx_interrupt(ndev);
}
/* Error interrupts */
if (isr.s.ewli || isr.s.doi || isr.s.epi || isr.s.ali) {
union ctu_can_fd_int_stat ierrmask = { .s =
{ .ewli = 1, .doi = 1, .epi = 1,
.ali = 1, .bei = 1 }};
icr.u32 = isr.u32 & ierrmask.u32;
netdev_dbg(ndev, "some ERR interrupt: clearing 0x%08x", icr.u32);
ctu_can_fd_int_clr(&priv->p, icr);
ctucan_err_interrupt(ndev, isr);
}
/* Ignore RI, TI, LFI, RFI, BSI */
} while (irq_loops++ < 10000);
netdev_err(ndev, "ctucan_interrupt: stuck interrupt, stopping\n");
{
union ctu_can_fd_int_stat imask, ival;
imask.u32 = 0xffffffff;
ival.u32 = 0;
ctu_can_fd_int_ena(&priv->p, imask, ival);
ctu_can_fd_int_mask(&priv->p, imask, ival);
}
return IRQ_HANDLED;
}
......@@ -740,6 +747,7 @@ static void ctucan_chip_stop(struct net_device *ndev)
/* Disable interrupts and disable can */
ctu_can_fd_int_ena(&priv->p, ena, mask);
ctu_can_fd_enable(&priv->p, false);
clear_bit(CTUCAN_FLAG_RX_SCHED, &priv->drv_flags);
priv->can.state = CAN_STATE_STOPPED;
}
......
......@@ -286,11 +286,28 @@ static inline union ctu_can_fd_mode_command_status_settings ctu_can_get_status(s
{
// MODE and STATUS are within the same word
union ctu_can_fd_mode_command_status_settings res;
res.u32 = ctu_can_fd_read32(priv, CTU_CAN_FD_MODE);
res.u32 = priv->read_reg(priv, CTU_CAN_FD_MODE);
return res;
}
/*
* Test if core is enabled..
*
* Arguments:
* priv Private info
* Returns:
* Return true if core is in enabled/active state..
*/
static inline bool ctu_can_fd_is_enabled(struct ctucanfd_priv *priv)
{
union ctu_can_fd_mode_command_status_settings reg;
reg.u32 = priv->read_reg(priv, CTU_CAN_FD_MODE);
return reg.s.ena == ENABLED;
}
/*
* Reads the interrupt status vector from CTU CAN FD Core.
*
......@@ -303,7 +320,7 @@ static inline union ctu_can_fd_mode_command_status_settings ctu_can_get_status(s
static inline union ctu_can_fd_int_stat ctu_can_fd_int_sts(struct ctucanfd_priv *priv)
{
union ctu_can_fd_int_stat res;
res.u32 = ctu_can_fd_read32(priv, CTU_CAN_FD_INT_STAT);
res.u32 = priv->read_reg(priv, CTU_CAN_FD_INT_STAT);
return res;
}
......@@ -317,7 +334,33 @@ static inline union ctu_can_fd_int_stat ctu_can_fd_int_sts(struct ctucanfd_priv
*/
static inline void ctu_can_fd_int_clr(struct ctucanfd_priv *priv, union ctu_can_fd_int_stat mask)
{
ctu_can_fd_write32(priv, CTU_CAN_FD_INT_STAT, mask.u32);
priv->write_reg(priv, CTU_CAN_FD_INT_STAT, mask.u32);
}
/*
* Sets enable interrupt bits.
*
* Arguments:
* priv Private info
* mask Mask of interrupts which should be disabled.
*/
static inline void ctu_can_fd_int_ena_set(struct ctucanfd_priv *priv, union ctu_can_fd_int_stat mask)
{
priv->write_reg(priv, CTU_CAN_FD_INT_ENA_SET, mask.u32);
}
/*
* Clears enable interrupt bits.
*
* Arguments:
* priv Private info
* mask Mask of interrupts which should be disabled.
*/
static inline void ctu_can_fd_int_ena_clr(struct ctucanfd_priv *priv, union ctu_can_fd_int_stat mask)
{
priv->write_reg(priv, CTU_CAN_FD_INT_ENA_CLR, mask.u32);
}
......@@ -429,7 +472,7 @@ void ctu_can_fd_read_err_ctrs(struct ctucanfd_priv *priv, struct can_berr_counte
static inline u16 ctu_can_fd_read_nom_errs(struct ctucanfd_priv *priv)
{
union ctu_can_fd_err_norm_err_fd reg;
reg.u32 = ctu_can_fd_read32(priv, CTU_CAN_FD_ERR_NORM);
reg.u32 = priv->read_reg(priv, CTU_CAN_FD_ERR_NORM);
return reg.s.err_norm_val;
}
......@@ -446,7 +489,7 @@ static inline void ctu_can_fd_erase_nom_errs(struct ctucanfd_priv *priv)
union ctu_can_fd_ctr_pres reg;
reg.u32 = 0;
reg.s.enorm = 1;
ctu_can_fd_write32(priv, CTU_CAN_FD_CTR_PRES, reg.u32);
priv->write_reg(priv, CTU_CAN_FD_CTR_PRES, reg.u32);
}
......@@ -462,7 +505,7 @@ static inline void ctu_can_fd_erase_nom_errs(struct ctucanfd_priv *priv)
static inline u16 ctu_can_fd_read_fd_errs(struct ctucanfd_priv *priv)
{
union ctu_can_fd_err_norm_err_fd reg;
reg.u32 = ctu_can_fd_read32(priv, CTU_CAN_FD_ERR_NORM);
reg.u32 = priv->read_reg(priv, CTU_CAN_FD_ERR_NORM);
return reg.s.err_fd_val;
}
......@@ -478,7 +521,7 @@ static inline void ctu_can_fd_erase_fd_errs(struct ctucanfd_priv *priv)
union ctu_can_fd_ctr_pres reg;
reg.u32 = 0;
reg.s.efd = 1;
ctu_can_fd_write32(priv, CTU_CAN_FD_CTR_PRES, reg.u32);
priv->write_reg(priv, CTU_CAN_FD_CTR_PRES, reg.u32);
}
......@@ -567,7 +610,7 @@ void ctu_can_fd_set_range_filter(struct ctucanfd_priv *priv, canid_t low_th,
static inline u16 ctu_can_fd_get_rx_fifo_size(struct ctucanfd_priv *priv)
{
union ctu_can_fd_rx_mem_info reg;
reg.u32 = ctu_can_fd_read32(priv, CTU_CAN_FD_RX_MEM_INFO);
reg.u32 = priv->read_reg(priv, CTU_CAN_FD_RX_MEM_INFO);
return reg.s.rx_buff_size;
}
......@@ -583,7 +626,7 @@ static inline u16 ctu_can_fd_get_rx_fifo_size(struct ctucanfd_priv *priv)
static inline u16 ctu_can_fd_get_rx_fifo_mem_free(struct ctucanfd_priv *priv)
{
union ctu_can_fd_rx_mem_info reg;
reg.u32 = ctu_can_fd_read32(priv, CTU_CAN_FD_RX_MEM_INFO);
reg.u32 = priv->read_reg(priv, CTU_CAN_FD_RX_MEM_INFO);
return reg.s.rx_mem_free;
}
......@@ -599,7 +642,7 @@ static inline u16 ctu_can_fd_get_rx_fifo_mem_free(struct ctucanfd_priv *priv)
static inline bool ctu_can_fd_is_rx_fifo_empty(struct ctucanfd_priv *priv)
{
union ctu_can_fd_rx_status_rx_settings reg;
reg.u32 = ctu_can_fd_read32(priv, CTU_CAN_FD_RX_STATUS);
reg.u32 = priv->read_reg(priv, CTU_CAN_FD_RX_STATUS);
return reg.s.rxe;
}
......@@ -615,7 +658,7 @@ static inline bool ctu_can_fd_is_rx_fifo_empty(struct ctucanfd_priv *priv)
static inline bool ctu_can_fd_is_rx_fifo_full(struct ctucanfd_priv *priv)
{
union ctu_can_fd_rx_status_rx_settings reg;
reg.u32 = ctu_can_fd_read32(priv, CTU_CAN_FD_RX_STATUS);
reg.u32 = priv->read_reg(priv, CTU_CAN_FD_RX_STATUS);
return reg.s.rxf;
}
......@@ -631,7 +674,7 @@ static inline bool ctu_can_fd_is_rx_fifo_full(struct ctucanfd_priv *priv)
static inline u16 ctu_can_fd_get_rx_frame_count(struct ctucanfd_priv *priv)
{
union ctu_can_fd_rx_status_rx_settings reg;
reg.u32 = ctu_can_fd_read32(priv, CTU_CAN_FD_RX_STATUS);
reg.u32 = priv->read_reg(priv, CTU_CAN_FD_RX_STATUS);
return reg.s.rxfrc;
}
......@@ -646,6 +689,37 @@ static inline u16 ctu_can_fd_get_rx_frame_count(struct ctucanfd_priv *priv)
void ctu_can_fd_set_rx_tsop(struct ctucanfd_priv *priv, enum ctu_can_fd_rx_settings_rtsop val);
/*
* Reads the first word of CAN Frame from RX FIFO Buffer.
*
* Arguments:
* priv Private info
*
* Returns:
* The firts word of received frame
*/
static inline union ctu_can_fd_frame_form_w ctu_can_fd_read_rx_ffw(struct ctucanfd_priv *priv)
{
union ctu_can_fd_frame_form_w ffw;
ffw.u32 = priv->read_reg(priv, CTU_CAN_FD_RX_DATA);
return ffw;
}
/*
* Reads one word of CAN Frame from RX FIFO Buffer.
*
* Arguments:
* priv Private info
*
* Returns:
* One wword of received frame
*/
static inline u32 ctu_can_fd_read_rx_word(struct ctucanfd_priv *priv)
{
return priv->read_reg(priv, CTU_CAN_FD_RX_DATA);
}
/*
* Reads CAN Frame from RX FIFO Buffer and stores it to a buffer.
*
......@@ -781,7 +855,7 @@ bool ctu_can_fd_insert_frame(struct ctucanfd_priv *priv, const struct canfd_fram
static inline u16 ctu_can_fd_get_tran_delay(struct ctucanfd_priv *priv)
{
union ctu_can_fd_trv_delay reg;
reg.u32 = ctu_can_fd_read32(priv, CTU_CAN_FD_TRV_DELAY);
reg.u32 = priv->read_reg(priv, CTU_CAN_FD_TRV_DELAY);
return reg.s.trv_delay_value;
}
......@@ -797,7 +871,7 @@ static inline u16 ctu_can_fd_get_tran_delay(struct ctucanfd_priv *priv)
static inline u32 ctu_can_fd_get_tx_frame_ctr(struct ctucanfd_priv *priv)
{
union ctu_can_fd_tx_counter reg;
reg.u32 = ctu_can_fd_read32(priv, CTU_CAN_FD_TX_COUNTER);
reg.u32 = priv->read_reg(priv, CTU_CAN_FD_TX_COUNTER);
return reg.s.tx_counter_val;
}
......@@ -813,7 +887,7 @@ static inline u32 ctu_can_fd_get_tx_frame_ctr(struct ctucanfd_priv *priv)
static inline u32 ctu_can_fd_get_rx_frame_ctr(struct ctucanfd_priv *priv)
{
union ctu_can_fd_rx_counter reg;
reg.u32 = ctu_can_fd_read32(priv, CTU_CAN_FD_RX_COUNTER);
reg.u32 = priv->read_reg(priv, CTU_CAN_FD_RX_COUNTER);
return reg.s.rx_counter_val;
}
......@@ -829,7 +903,7 @@ static inline u32 ctu_can_fd_get_rx_frame_ctr(struct ctucanfd_priv *priv)
static inline union ctu_can_fd_debug_register ctu_can_fd_read_debug_info(struct ctucanfd_priv *priv)
{
union ctu_can_fd_debug_register reg;
reg.u32 = ctu_can_fd_read32(priv, CTU_CAN_FD_DEBUG_REGISTER);
reg.u32 = priv->read_reg(priv, CTU_CAN_FD_DEBUG_REGISTER);
return reg;
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment