1// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
2/* Copyright 2017-2019 NXP */
3
4#include "enetc_pf.h"
5
6static void enetc_msg_disable_mr_int(struct enetc_hw *hw)
7{
8 u32 psiier = enetc_rd(hw, ENETC_PSIIER);
9 /* disable MR int source(s) */
10 enetc_wr(hw, ENETC_PSIIER, psiier & ~ENETC_PSIIER_MR_MASK);
11}
12
13static void enetc_msg_enable_mr_int(struct enetc_hw *hw)
14{
15 u32 psiier = enetc_rd(hw, ENETC_PSIIER);
16
17 enetc_wr(hw, ENETC_PSIIER, psiier | ENETC_PSIIER_MR_MASK);
18}
19
20static irqreturn_t enetc_msg_psi_msix(int irq, void *data)
21{
22 struct enetc_si *si = (struct enetc_si *)data;
23 struct enetc_pf *pf = enetc_si_priv(si);
24
25 enetc_msg_disable_mr_int(hw: &si->hw);
26 schedule_work(work: &pf->msg_task);
27
28 return IRQ_HANDLED;
29}
30
31static void enetc_msg_task(struct work_struct *work)
32{
33 struct enetc_pf *pf = container_of(work, struct enetc_pf, msg_task);
34 struct enetc_hw *hw = &pf->si->hw;
35 unsigned long mr_mask;
36 int i;
37
38 for (;;) {
39 mr_mask = enetc_rd(hw, ENETC_PSIMSGRR) & ENETC_PSIMSGRR_MR_MASK;
40 if (!mr_mask) {
41 /* re-arm MR interrupts, w1c the IDR reg */
42 enetc_wr(hw, ENETC_PSIIDR, ENETC_PSIIER_MR_MASK);
43 enetc_msg_enable_mr_int(hw);
44 return;
45 }
46
47 for (i = 0; i < pf->num_vfs; i++) {
48 u32 psimsgrr;
49 u16 msg_code;
50
51 if (!(ENETC_PSIMSGRR_MR(i) & mr_mask))
52 continue;
53
54 enetc_msg_handle_rxmsg(pf, mbox_id: i, status: &msg_code);
55
56 psimsgrr = ENETC_SIMSGSR_SET_MC(msg_code);
57 psimsgrr |= ENETC_PSIMSGRR_MR(i); /* w1c */
58 enetc_wr(hw, ENETC_PSIMSGRR, psimsgrr);
59 }
60 }
61}
62
63/* Init */
64static int enetc_msg_alloc_mbx(struct enetc_si *si, int idx)
65{
66 struct enetc_pf *pf = enetc_si_priv(si);
67 struct device *dev = &si->pdev->dev;
68 struct enetc_hw *hw = &si->hw;
69 struct enetc_msg_swbd *msg;
70 u32 val;
71
72 msg = &pf->rxmsg[idx];
73 /* allocate and set receive buffer */
74 msg->size = ENETC_DEFAULT_MSG_SIZE;
75
76 msg->vaddr = dma_alloc_coherent(dev, size: msg->size, dma_handle: &msg->dma,
77 GFP_KERNEL);
78 if (!msg->vaddr) {
79 dev_err(dev, "msg: fail to alloc dma buffer of size: %d\n",
80 msg->size);
81 return -ENOMEM;
82 }
83
84 /* set multiple of 32 bytes */
85 val = lower_32_bits(msg->dma);
86 enetc_wr(hw, ENETC_PSIVMSGRCVAR0(idx), val);
87 val = upper_32_bits(msg->dma);
88 enetc_wr(hw, ENETC_PSIVMSGRCVAR1(idx), val);
89
90 return 0;
91}
92
93static void enetc_msg_free_mbx(struct enetc_si *si, int idx)
94{
95 struct enetc_pf *pf = enetc_si_priv(si);
96 struct enetc_hw *hw = &si->hw;
97 struct enetc_msg_swbd *msg;
98
99 msg = &pf->rxmsg[idx];
100 dma_free_coherent(dev: &si->pdev->dev, size: msg->size, cpu_addr: msg->vaddr, dma_handle: msg->dma);
101 memset(msg, 0, sizeof(*msg));
102
103 enetc_wr(hw, ENETC_PSIVMSGRCVAR0(idx), 0);
104 enetc_wr(hw, ENETC_PSIVMSGRCVAR1(idx), 0);
105}
106
107int enetc_msg_psi_init(struct enetc_pf *pf)
108{
109 struct enetc_si *si = pf->si;
110 int vector, i, err;
111
112 /* register message passing interrupt handler */
113 snprintf(buf: pf->msg_int_name, size: sizeof(pf->msg_int_name), fmt: "%s-vfmsg",
114 si->ndev->name);
115 vector = pci_irq_vector(dev: si->pdev, ENETC_SI_INT_IDX);
116 err = request_irq(irq: vector, handler: enetc_msg_psi_msix, flags: 0, name: pf->msg_int_name, dev: si);
117 if (err) {
118 dev_err(&si->pdev->dev,
119 "PSI messaging: request_irq() failed!\n");
120 return err;
121 }
122
123 /* set one IRQ entry for PSI message receive notification (SI int) */
124 enetc_wr(&si->hw, ENETC_SIMSIVR, ENETC_SI_INT_IDX);
125
126 /* initialize PSI mailbox */
127 INIT_WORK(&pf->msg_task, enetc_msg_task);
128
129 for (i = 0; i < pf->num_vfs; i++) {
130 err = enetc_msg_alloc_mbx(si, idx: i);
131 if (err)
132 goto err_init_mbx;
133 }
134
135 /* enable MR interrupts */
136 enetc_msg_enable_mr_int(hw: &si->hw);
137
138 return 0;
139
140err_init_mbx:
141 for (i--; i >= 0; i--)
142 enetc_msg_free_mbx(si, idx: i);
143
144 free_irq(vector, si);
145
146 return err;
147}
148
149void enetc_msg_psi_free(struct enetc_pf *pf)
150{
151 struct enetc_si *si = pf->si;
152 int i;
153
154 cancel_work_sync(work: &pf->msg_task);
155
156 /* disable MR interrupts */
157 enetc_msg_disable_mr_int(hw: &si->hw);
158
159 for (i = 0; i < pf->num_vfs; i++)
160 enetc_msg_free_mbx(si, idx: i);
161
162 /* de-register message passing interrupt handler */
163 free_irq(pci_irq_vector(dev: si->pdev, ENETC_SI_INT_IDX), si);
164}
165

source code of linux/drivers/net/ethernet/freescale/enetc/enetc_msg.c