net/sfc: add init on attach
[dpdk.git] / drivers / net / sfc / sfc_mcdi.c
1 /*-
2  * Copyright (c) 2016 Solarflare Communications Inc.
3  * All rights reserved.
4  *
5  * This software was jointly developed between OKTET Labs (under contract
6  * for Solarflare) and Solarflare Communications, Inc.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright notice,
12  *    this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright notice,
14  *    this list of conditions and the following disclaimer in the documentation
15  *    and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
19  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
21  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
24  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
27  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include <rte_cycles.h>
31
32 #include "efx.h"
33 #include "efx_mcdi.h"
34 #include "efx_regs_mcdi.h"
35
36 #include "sfc.h"
37 #include "sfc_log.h"
38
39 #define SFC_MCDI_POLL_INTERVAL_MIN_US   10              /* 10us in 1us units */
40 #define SFC_MCDI_POLL_INTERVAL_MAX_US   (US_PER_S / 10) /* 100ms in 1us units */
41 #define SFC_MCDI_WATCHDOG_INTERVAL_US   (10 * US_PER_S) /* 10s in 1us units */
42
43 static void
44 sfc_mcdi_timeout(struct sfc_adapter *sa)
45 {
46         sfc_warn(sa, "MC TIMEOUT");
47
48         sfc_panic(sa, "MCDI timeout handling is not implemented\n");
49 }
50
51 static void
52 sfc_mcdi_poll(struct sfc_adapter *sa)
53 {
54         efx_nic_t *enp;
55         unsigned int delay_total;
56         unsigned int delay_us;
57         boolean_t aborted __rte_unused;
58
59         delay_total = 0;
60         delay_us = SFC_MCDI_POLL_INTERVAL_MIN_US;
61         enp = sa->nic;
62
63         do {
64                 if (efx_mcdi_request_poll(enp))
65                         return;
66
67                 if (delay_total > SFC_MCDI_WATCHDOG_INTERVAL_US) {
68                         aborted = efx_mcdi_request_abort(enp);
69                         SFC_ASSERT(aborted);
70                         sfc_mcdi_timeout(sa);
71                         return;
72                 }
73
74                 rte_delay_us(delay_us);
75
76                 delay_total += delay_us;
77
78                 /* Exponentially back off the poll frequency */
79                 RTE_BUILD_BUG_ON(SFC_MCDI_POLL_INTERVAL_MAX_US > UINT_MAX / 2);
80                 delay_us *= 2;
81                 if (delay_us > SFC_MCDI_POLL_INTERVAL_MAX_US)
82                         delay_us = SFC_MCDI_POLL_INTERVAL_MAX_US;
83
84         } while (1);
85 }
86
87 static void
88 sfc_mcdi_execute(void *arg, efx_mcdi_req_t *emrp)
89 {
90         struct sfc_adapter *sa = (struct sfc_adapter *)arg;
91         struct sfc_mcdi *mcdi = &sa->mcdi;
92
93         rte_spinlock_lock(&mcdi->lock);
94
95         SFC_ASSERT(mcdi->state == SFC_MCDI_INITIALIZED);
96
97         efx_mcdi_request_start(sa->nic, emrp, B_FALSE);
98         sfc_mcdi_poll(sa);
99
100         rte_spinlock_unlock(&mcdi->lock);
101 }
102
103 static void
104 sfc_mcdi_ev_cpl(void *arg)
105 {
106         struct sfc_adapter *sa = (struct sfc_adapter *)arg;
107         struct sfc_mcdi *mcdi __rte_unused;
108
109         mcdi = &sa->mcdi;
110         SFC_ASSERT(mcdi->state == SFC_MCDI_INITIALIZED);
111
112         /* MCDI is polled, completions are not expected */
113         SFC_ASSERT(0);
114 }
115
116 static void
117 sfc_mcdi_exception(void *arg, efx_mcdi_exception_t eme)
118 {
119         struct sfc_adapter *sa = (struct sfc_adapter *)arg;
120
121         sfc_warn(sa, "MC %s",
122             (eme == EFX_MCDI_EXCEPTION_MC_REBOOT) ? "REBOOT" :
123             (eme == EFX_MCDI_EXCEPTION_MC_BADASSERT) ? "BADASSERT" : "UNKNOWN");
124
125         sfc_panic(sa, "MCDI exceptions handling is not implemented\n");
126 }
127
128 int
129 sfc_mcdi_init(struct sfc_adapter *sa)
130 {
131         struct sfc_mcdi *mcdi;
132         size_t max_msg_size;
133         efx_mcdi_transport_t *emtp;
134         int rc;
135
136         sfc_log_init(sa, "entry");
137
138         mcdi = &sa->mcdi;
139
140         SFC_ASSERT(mcdi->state == SFC_MCDI_UNINITIALIZED);
141
142         rte_spinlock_init(&mcdi->lock);
143
144         mcdi->state = SFC_MCDI_INITIALIZED;
145
146         max_msg_size = sizeof(uint32_t) + MCDI_CTL_SDU_LEN_MAX_V2;
147         rc = sfc_dma_alloc(sa, "mcdi", 0, max_msg_size, sa->socket_id,
148                            &mcdi->mem);
149         if (rc != 0)
150                 goto fail_dma_alloc;
151
152         emtp = &mcdi->transport;
153         emtp->emt_context = sa;
154         emtp->emt_dma_mem = &mcdi->mem;
155         emtp->emt_execute = sfc_mcdi_execute;
156         emtp->emt_ev_cpl = sfc_mcdi_ev_cpl;
157         emtp->emt_exception = sfc_mcdi_exception;
158
159         sfc_log_init(sa, "init MCDI");
160         rc = efx_mcdi_init(sa->nic, emtp);
161         if (rc != 0)
162                 goto fail_mcdi_init;
163
164         return 0;
165
166 fail_mcdi_init:
167         memset(emtp, 0, sizeof(*emtp));
168         sfc_dma_free(sa, &mcdi->mem);
169
170 fail_dma_alloc:
171         mcdi->state = SFC_MCDI_UNINITIALIZED;
172         return rc;
173 }
174
175 void
176 sfc_mcdi_fini(struct sfc_adapter *sa)
177 {
178         struct sfc_mcdi *mcdi;
179         efx_mcdi_transport_t *emtp;
180
181         sfc_log_init(sa, "entry");
182
183         mcdi = &sa->mcdi;
184         emtp = &mcdi->transport;
185
186         rte_spinlock_lock(&mcdi->lock);
187
188         SFC_ASSERT(mcdi->state == SFC_MCDI_INITIALIZED);
189         mcdi->state = SFC_MCDI_UNINITIALIZED;
190
191         sfc_log_init(sa, "fini MCDI");
192         efx_mcdi_fini(sa->nic);
193         memset(emtp, 0, sizeof(*emtp));
194
195         rte_spinlock_unlock(&mcdi->lock);
196
197         sfc_dma_free(sa, &mcdi->mem);
198 }