Linux-libre 5.3.12-gnu
[librecmc/linux-libre.git] / drivers / net / ethernet / freescale / enetc / enetc_msg.c
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(&si->hw);
26         schedule_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, i, &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, msg->size, &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(&si->pdev->dev, msg->size, msg->vaddr, 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(pf->msg_int_name, sizeof(pf->msg_int_name), "%s-vfmsg",
114                  si->ndev->name);
115         vector = pci_irq_vector(si->pdev, ENETC_SI_INT_IDX);
116         err = request_irq(vector, enetc_msg_psi_msix, 0, pf->msg_int_name, 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, i);
131                 if (err)
132                         goto err_init_mbx;
133         }
134
135         /* enable MR interrupts */
136         enetc_msg_enable_mr_int(&si->hw);
137
138         return 0;
139
140 err_init_mbx:
141         for (i--; i >= 0; i--)
142                 enetc_msg_free_mbx(si, 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(&pf->msg_task);
155
156         /* disable MR interrupts */
157         enetc_msg_disable_mr_int(&si->hw);
158
159         for (i = 0; i < pf->num_vfs; i++)
160                 enetc_msg_free_mbx(si, i);
161
162         /* de-register message passing interrupt handler */
163         free_irq(pci_irq_vector(si->pdev, ENETC_SI_INT_IDX), si);
164 }