1 | // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) |
2 | /* Copyright 2017-2019 NXP */ |
3 | |
4 | #include "enetc_pf.h" |
5 | |
6 | static 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 | |
13 | static 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 | |
20 | static 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 | |
31 | static 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 */ |
64 | static 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 | |
93 | static 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 | |
107 | int 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 | |
140 | err_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 | |
149 | void 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 | |