1ef270ab1SKenneth D. Merry /*-
2ef270ab1SKenneth D. Merry * Copyright (c) 2017 Broadcom. All rights reserved.
3ef270ab1SKenneth D. Merry * The term "Broadcom" refers to Broadcom Limited and/or its subsidiaries.
4ef270ab1SKenneth D. Merry *
5ef270ab1SKenneth D. Merry * Redistribution and use in source and binary forms, with or without
6ef270ab1SKenneth D. Merry * modification, are permitted provided that the following conditions are met:
7ef270ab1SKenneth D. Merry *
8ef270ab1SKenneth D. Merry * 1. Redistributions of source code must retain the above copyright notice,
9ef270ab1SKenneth D. Merry * this list of conditions and the following disclaimer.
10ef270ab1SKenneth D. Merry *
11ef270ab1SKenneth D. Merry * 2. Redistributions in binary form must reproduce the above copyright notice,
12ef270ab1SKenneth D. Merry * this list of conditions and the following disclaimer in the documentation
13ef270ab1SKenneth D. Merry * and/or other materials provided with the distribution.
14ef270ab1SKenneth D. Merry *
15ef270ab1SKenneth D. Merry * 3. Neither the name of the copyright holder nor the names of its contributors
16ef270ab1SKenneth D. Merry * may be used to endorse or promote products derived from this software
17ef270ab1SKenneth D. Merry * without specific prior written permission.
18ef270ab1SKenneth D. Merry *
19ef270ab1SKenneth D. Merry * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20ef270ab1SKenneth D. Merry * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21ef270ab1SKenneth D. Merry * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22ef270ab1SKenneth D. Merry * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23ef270ab1SKenneth D. Merry * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24ef270ab1SKenneth D. Merry * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25ef270ab1SKenneth D. Merry * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26ef270ab1SKenneth D. Merry * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27ef270ab1SKenneth D. Merry * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28ef270ab1SKenneth D. Merry * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29ef270ab1SKenneth D. Merry * POSSIBILITY OF SUCH DAMAGE.
30ef270ab1SKenneth D. Merry */
31ef270ab1SKenneth D. Merry
32ef270ab1SKenneth D. Merry #define OCS_COPYRIGHT "Copyright (C) 2017 Broadcom. All rights reserved."
33ef270ab1SKenneth D. Merry
34ef270ab1SKenneth D. Merry /**
35ef270ab1SKenneth D. Merry * @file
36ef270ab1SKenneth D. Merry * Implementation of required FreeBSD PCI interface functions
37ef270ab1SKenneth D. Merry */
38ef270ab1SKenneth D. Merry
39ef270ab1SKenneth D. Merry #include "ocs.h"
40ef270ab1SKenneth D. Merry #include "version.h"
41ef270ab1SKenneth D. Merry #include <sys/sysctl.h>
42ef270ab1SKenneth D. Merry #include <sys/malloc.h>
43ef270ab1SKenneth D. Merry
44ef270ab1SKenneth D. Merry static MALLOC_DEFINE(M_OCS, "OCS", "OneCore Storage data");
45ef270ab1SKenneth D. Merry
46ef270ab1SKenneth D. Merry #include <dev/pci/pcireg.h>
47ef270ab1SKenneth D. Merry #include <dev/pci/pcivar.h>
48ef270ab1SKenneth D. Merry
49ef270ab1SKenneth D. Merry #include <machine/bus.h>
50ef270ab1SKenneth D. Merry
51ef270ab1SKenneth D. Merry /**
52ef270ab1SKenneth D. Merry * Tunable parameters for transport
53ef270ab1SKenneth D. Merry */
54ef270ab1SKenneth D. Merry int logmask = 0;
55ef270ab1SKenneth D. Merry int ctrlmask = 2;
56ef270ab1SKenneth D. Merry int logdest = 1;
57ef270ab1SKenneth D. Merry int loglevel = LOG_INFO;
58ef270ab1SKenneth D. Merry int ramlog_size = 1*1024*1024;
59ef270ab1SKenneth D. Merry int ddump_saved_size = 0;
60ef270ab1SKenneth D. Merry static const char *queue_topology = "eq cq rq cq mq $nulp($nwq(cq wq:ulp=$rpt1)) cq wq:len=256:class=1";
61ef270ab1SKenneth D. Merry
62ef270ab1SKenneth D. Merry static void ocs_release_bus(struct ocs_softc *);
63ef270ab1SKenneth D. Merry static int32_t ocs_intr_alloc(struct ocs_softc *);
64ef270ab1SKenneth D. Merry static int32_t ocs_intr_setup(struct ocs_softc *);
65ef270ab1SKenneth D. Merry static int32_t ocs_intr_teardown(struct ocs_softc *);
66ef270ab1SKenneth D. Merry static int ocs_pci_intx_filter(void *);
67ef270ab1SKenneth D. Merry static void ocs_pci_intr(void *);
68ef270ab1SKenneth D. Merry static int32_t ocs_init_dma_tag(struct ocs_softc *ocs);
69ef270ab1SKenneth D. Merry
70ef270ab1SKenneth D. Merry static int32_t ocs_setup_fcports(ocs_t *ocs);
71ef270ab1SKenneth D. Merry
72ef270ab1SKenneth D. Merry ocs_t *ocs_devices[MAX_OCS_DEVICES];
73ef270ab1SKenneth D. Merry
74ef270ab1SKenneth D. Merry /**
75ef270ab1SKenneth D. Merry * @brief Check support for the given device
76ef270ab1SKenneth D. Merry *
77ef270ab1SKenneth D. Merry * Determine support for a given device by examining the PCI vendor and
78ef270ab1SKenneth D. Merry * device IDs
79ef270ab1SKenneth D. Merry *
80ef270ab1SKenneth D. Merry * @param dev device abstraction
81ef270ab1SKenneth D. Merry *
82ef270ab1SKenneth D. Merry * @return 0 if device is supported, ENXIO otherwise
83ef270ab1SKenneth D. Merry */
84ef270ab1SKenneth D. Merry static int
ocs_pci_probe(device_t dev)85ef270ab1SKenneth D. Merry ocs_pci_probe(device_t dev)
86ef270ab1SKenneth D. Merry {
87ef270ab1SKenneth D. Merry char *desc = NULL;
88ef270ab1SKenneth D. Merry
89ef270ab1SKenneth D. Merry if (pci_get_vendor(dev) != PCI_VENDOR_EMULEX) {
90ef270ab1SKenneth D. Merry return ENXIO;
91ef270ab1SKenneth D. Merry }
92ef270ab1SKenneth D. Merry
93ef270ab1SKenneth D. Merry switch (pci_get_device(dev)) {
94ef270ab1SKenneth D. Merry case PCI_PRODUCT_EMULEX_OCE16001:
95ef270ab1SKenneth D. Merry desc = "Emulex LightPulse FC Adapter";
96ef270ab1SKenneth D. Merry break;
97ef270ab1SKenneth D. Merry case PCI_PRODUCT_EMULEX_LPE31004:
98ef270ab1SKenneth D. Merry desc = "Emulex LightPulse FC Adapter";
99ef270ab1SKenneth D. Merry break;
100ef270ab1SKenneth D. Merry case PCI_PRODUCT_EMULEX_OCE50102:
101ef270ab1SKenneth D. Merry desc = "Emulex LightPulse 10GbE FCoE/NIC Adapter";
102ef270ab1SKenneth D. Merry break;
1033bf42363SRam Kishore Vegesna case PCI_PRODUCT_EMULEX_LANCER_G7:
1043bf42363SRam Kishore Vegesna desc = "Emulex LightPulse G7 FC Adapter";
1053bf42363SRam Kishore Vegesna break;
106ef270ab1SKenneth D. Merry default:
107ef270ab1SKenneth D. Merry return ENXIO;
108ef270ab1SKenneth D. Merry }
109ef270ab1SKenneth D. Merry
110ef270ab1SKenneth D. Merry device_set_desc(dev, desc);
111ef270ab1SKenneth D. Merry
112ef270ab1SKenneth D. Merry return BUS_PROBE_DEFAULT;
113ef270ab1SKenneth D. Merry }
114ef270ab1SKenneth D. Merry
115ef270ab1SKenneth D. Merry static int
ocs_map_g7_bars(device_t dev,struct ocs_softc * ocs)1163bf42363SRam Kishore Vegesna ocs_map_g7_bars(device_t dev, struct ocs_softc *ocs)
1173bf42363SRam Kishore Vegesna {
1183bf42363SRam Kishore Vegesna int i, r;
1193bf42363SRam Kishore Vegesna uint32_t val = 0;
1203bf42363SRam Kishore Vegesna
1213bf42363SRam Kishore Vegesna for (i = 0, r = 0; i < PCI_MAX_BAR; i++) {
1223bf42363SRam Kishore Vegesna val = pci_read_config(dev, PCIR_BAR(i), 4);
1233bf42363SRam Kishore Vegesna if (!PCI_BAR_MEM(val)) {
1243bf42363SRam Kishore Vegesna continue;
1253bf42363SRam Kishore Vegesna }
1263bf42363SRam Kishore Vegesna if (!(val & PCIM_BAR_MEM_BASE)) {
1273bf42363SRam Kishore Vegesna /* no address */
1283bf42363SRam Kishore Vegesna continue;
1293bf42363SRam Kishore Vegesna }
1303bf42363SRam Kishore Vegesna ocs->reg[r].rid = PCIR_BAR(i);
1313bf42363SRam Kishore Vegesna ocs->reg[r].res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
1323bf42363SRam Kishore Vegesna &ocs->reg[r].rid, RF_ACTIVE);
1333bf42363SRam Kishore Vegesna if (ocs->reg[r].res) {
1343bf42363SRam Kishore Vegesna ocs->reg[r].btag = rman_get_bustag(ocs->reg[r].res);
1353bf42363SRam Kishore Vegesna ocs->reg[r].bhandle = rman_get_bushandle(ocs->reg[r].res);
1363bf42363SRam Kishore Vegesna r++;
1373bf42363SRam Kishore Vegesna } else {
1383bf42363SRam Kishore Vegesna device_printf(dev, "bus_alloc_resource failed rid=%#x\n",
1393bf42363SRam Kishore Vegesna ocs->reg[r].rid);
1403bf42363SRam Kishore Vegesna ocs_release_bus(ocs);
1413bf42363SRam Kishore Vegesna return ENXIO;
1423bf42363SRam Kishore Vegesna }
1433bf42363SRam Kishore Vegesna
1443bf42363SRam Kishore Vegesna /*
1453bf42363SRam Kishore Vegesna * If the 64-bit attribute is set, both this BAR and the
1463bf42363SRam Kishore Vegesna * next form the complete address. Skip processing the
1473bf42363SRam Kishore Vegesna * next BAR.
1483bf42363SRam Kishore Vegesna */
1493bf42363SRam Kishore Vegesna if (val & PCIM_BAR_MEM_64) {
1503bf42363SRam Kishore Vegesna i++;
1513bf42363SRam Kishore Vegesna }
1523bf42363SRam Kishore Vegesna }
1533bf42363SRam Kishore Vegesna
1543bf42363SRam Kishore Vegesna return 0;
1553bf42363SRam Kishore Vegesna }
1563bf42363SRam Kishore Vegesna
1573bf42363SRam Kishore Vegesna static int
ocs_map_bars(device_t dev,struct ocs_softc * ocs)158ef270ab1SKenneth D. Merry ocs_map_bars(device_t dev, struct ocs_softc *ocs)
159ef270ab1SKenneth D. Merry {
160ef270ab1SKenneth D. Merry /*
161ef270ab1SKenneth D. Merry * Map PCI BAR0 register into the CPU's space.
162ef270ab1SKenneth D. Merry */
163ef270ab1SKenneth D. Merry
164ef270ab1SKenneth D. Merry ocs->reg[0].rid = PCIR_BAR(PCI_64BIT_BAR0);
165ef270ab1SKenneth D. Merry ocs->reg[0].res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
166ef270ab1SKenneth D. Merry &ocs->reg[0].rid, RF_ACTIVE);
167ef270ab1SKenneth D. Merry
168ef270ab1SKenneth D. Merry if (ocs->reg[0].res == NULL) {
169ef270ab1SKenneth D. Merry device_printf(dev, "bus_alloc_resource failed rid=%#x\n",
170ef270ab1SKenneth D. Merry ocs->reg[0].rid);
171ef270ab1SKenneth D. Merry return ENXIO;
172ef270ab1SKenneth D. Merry }
173ef270ab1SKenneth D. Merry
174ef270ab1SKenneth D. Merry ocs->reg[0].btag = rman_get_bustag(ocs->reg[0].res);
175ef270ab1SKenneth D. Merry ocs->reg[0].bhandle = rman_get_bushandle(ocs->reg[0].res);
176ef270ab1SKenneth D. Merry return 0;
177ef270ab1SKenneth D. Merry }
178ef270ab1SKenneth D. Merry
179ef270ab1SKenneth D. Merry static int
ocs_setup_params(struct ocs_softc * ocs)180ef270ab1SKenneth D. Merry ocs_setup_params(struct ocs_softc *ocs)
181ef270ab1SKenneth D. Merry {
182ef270ab1SKenneth D. Merry int32_t i = 0;
183ef270ab1SKenneth D. Merry const char *hw_war_version;
184ef270ab1SKenneth D. Merry /* Setup tunable parameters */
185ef270ab1SKenneth D. Merry ocs->ctrlmask = ctrlmask;
186ef270ab1SKenneth D. Merry ocs->speed = 0;
187ef270ab1SKenneth D. Merry ocs->topology = 0;
188ef270ab1SKenneth D. Merry ocs->ethernet_license = 0;
189ef270ab1SKenneth D. Merry ocs->num_scsi_ios = 8192;
190ef270ab1SKenneth D. Merry ocs->enable_hlm = 0;
191ef270ab1SKenneth D. Merry ocs->hlm_group_size = 8;
192ef270ab1SKenneth D. Merry ocs->logmask = logmask;
193ef270ab1SKenneth D. Merry
194ef270ab1SKenneth D. Merry ocs->config_tgt = FALSE;
195ef270ab1SKenneth D. Merry if (0 == resource_int_value(device_get_name(ocs->dev), device_get_unit(ocs->dev),
196ef270ab1SKenneth D. Merry "target", &i)) {
197ef270ab1SKenneth D. Merry if (1 == i) {
198ef270ab1SKenneth D. Merry ocs->config_tgt = TRUE;
199ef270ab1SKenneth D. Merry device_printf(ocs->dev, "Enabling target\n");
200ef270ab1SKenneth D. Merry }
201ef270ab1SKenneth D. Merry }
202ef270ab1SKenneth D. Merry
203ef270ab1SKenneth D. Merry ocs->config_ini = TRUE;
204ef270ab1SKenneth D. Merry if (0 == resource_int_value(device_get_name(ocs->dev), device_get_unit(ocs->dev),
205ef270ab1SKenneth D. Merry "initiator", &i)) {
206ef270ab1SKenneth D. Merry if (0 == i) {
207ef270ab1SKenneth D. Merry ocs->config_ini = FALSE;
208ef270ab1SKenneth D. Merry device_printf(ocs->dev, "Disabling initiator\n");
209ef270ab1SKenneth D. Merry }
210ef270ab1SKenneth D. Merry }
211ef270ab1SKenneth D. Merry ocs->enable_ini = ocs->config_ini;
212ef270ab1SKenneth D. Merry
213ef270ab1SKenneth D. Merry if (!ocs->config_ini && !ocs->config_tgt) {
214ef270ab1SKenneth D. Merry device_printf(ocs->dev, "Unsupported, both initiator and target mode disabled.\n");
215ef270ab1SKenneth D. Merry return 1;
216ef270ab1SKenneth D. Merry }
217ef270ab1SKenneth D. Merry
218ef270ab1SKenneth D. Merry if (0 == resource_int_value(device_get_name(ocs->dev), device_get_unit(ocs->dev),
219ef270ab1SKenneth D. Merry "logmask", &logmask)) {
220ef270ab1SKenneth D. Merry device_printf(ocs->dev, "logmask = %#x\n", logmask);
221ef270ab1SKenneth D. Merry }
222ef270ab1SKenneth D. Merry
223ef270ab1SKenneth D. Merry if (0 == resource_int_value(device_get_name(ocs->dev), device_get_unit(ocs->dev),
224ef270ab1SKenneth D. Merry "logdest", &logdest)) {
225ef270ab1SKenneth D. Merry device_printf(ocs->dev, "logdest = %#x\n", logdest);
226ef270ab1SKenneth D. Merry }
227ef270ab1SKenneth D. Merry
228ef270ab1SKenneth D. Merry if (0 == resource_int_value(device_get_name(ocs->dev), device_get_unit(ocs->dev),
229ef270ab1SKenneth D. Merry "loglevel", &loglevel)) {
230ef270ab1SKenneth D. Merry device_printf(ocs->dev, "loglevel = %#x\n", loglevel);
231ef270ab1SKenneth D. Merry }
232ef270ab1SKenneth D. Merry
233ef270ab1SKenneth D. Merry if (0 == resource_int_value(device_get_name(ocs->dev), device_get_unit(ocs->dev),
234ef270ab1SKenneth D. Merry "ramlog_size", &ramlog_size)) {
235ef270ab1SKenneth D. Merry device_printf(ocs->dev, "ramlog_size = %#x\n", ramlog_size);
236ef270ab1SKenneth D. Merry }
237ef270ab1SKenneth D. Merry
238ef270ab1SKenneth D. Merry if (0 == resource_int_value(device_get_name(ocs->dev), device_get_unit(ocs->dev),
239ef270ab1SKenneth D. Merry "ddump_saved_size", &ddump_saved_size)) {
240ef270ab1SKenneth D. Merry device_printf(ocs->dev, "ddump_saved_size= %#x\n", ddump_saved_size);
241ef270ab1SKenneth D. Merry }
242ef270ab1SKenneth D. Merry
243ef270ab1SKenneth D. Merry /* If enabled, initailize a RAM logging buffer */
244ef270ab1SKenneth D. Merry if (logdest & 2) {
245ef270ab1SKenneth D. Merry ocs->ramlog = ocs_ramlog_init(ocs, ramlog_size/OCS_RAMLOG_DEFAULT_BUFFERS,
246ef270ab1SKenneth D. Merry OCS_RAMLOG_DEFAULT_BUFFERS);
247ef270ab1SKenneth D. Merry /* If NULL was returned, then we'll simply skip using the ramlog but */
248ef270ab1SKenneth D. Merry /* set logdest to 1 to ensure that we at least get default logging. */
249ef270ab1SKenneth D. Merry if (ocs->ramlog == NULL) {
250ef270ab1SKenneth D. Merry logdest = 1;
251ef270ab1SKenneth D. Merry }
252ef270ab1SKenneth D. Merry }
253ef270ab1SKenneth D. Merry
254ef270ab1SKenneth D. Merry /* initialize a saved ddump */
255ef270ab1SKenneth D. Merry if (ddump_saved_size) {
256ef270ab1SKenneth D. Merry if (ocs_textbuf_alloc(ocs, &ocs->ddump_saved, ddump_saved_size)) {
257ef270ab1SKenneth D. Merry ocs_log_err(ocs, "failed to allocate memory for saved ddump\n");
258ef270ab1SKenneth D. Merry }
259ef270ab1SKenneth D. Merry }
260ef270ab1SKenneth D. Merry
261ef270ab1SKenneth D. Merry if (0 == resource_string_value(device_get_name(ocs->dev), device_get_unit(ocs->dev),
262ef270ab1SKenneth D. Merry "hw_war_version", &hw_war_version)) {
263ef270ab1SKenneth D. Merry device_printf(ocs->dev, "hw_war_version = %s\n", hw_war_version);
264ef270ab1SKenneth D. Merry ocs->hw_war_version = strdup(hw_war_version, M_OCS);
265ef270ab1SKenneth D. Merry }
266ef270ab1SKenneth D. Merry
267ef270ab1SKenneth D. Merry if (0 == resource_int_value(device_get_name(ocs->dev), device_get_unit(ocs->dev),
268ef270ab1SKenneth D. Merry "explicit_buffer_list", &i)) {
269ef270ab1SKenneth D. Merry ocs->explicit_buffer_list = i;
270ef270ab1SKenneth D. Merry }
271ef270ab1SKenneth D. Merry
272ef270ab1SKenneth D. Merry if (0 == resource_int_value(device_get_name(ocs->dev), device_get_unit(ocs->dev),
273ef270ab1SKenneth D. Merry "ethernet_license", &i)) {
274ef270ab1SKenneth D. Merry ocs->ethernet_license = i;
275ef270ab1SKenneth D. Merry }
276ef270ab1SKenneth D. Merry
277ef270ab1SKenneth D. Merry if (0 == resource_int_value(device_get_name(ocs->dev), device_get_unit(ocs->dev),
278ef270ab1SKenneth D. Merry "speed", &i)) {
279ef270ab1SKenneth D. Merry device_printf(ocs->dev, "speed = %d Mbps\n", i);
280ef270ab1SKenneth D. Merry ocs->speed = i;
281ef270ab1SKenneth D. Merry }
282ef270ab1SKenneth D. Merry ocs->desc = device_get_desc(ocs->dev);
283ef270ab1SKenneth D. Merry
284ef270ab1SKenneth D. Merry ocs_device_lock_init(ocs);
285ef270ab1SKenneth D. Merry ocs->driver_version = STR_BE_MAJOR "." STR_BE_MINOR "." STR_BE_BUILD "." STR_BE_BRANCH;
286ef270ab1SKenneth D. Merry ocs->model = ocs_pci_model(ocs->pci_vendor, ocs->pci_device);
287ef270ab1SKenneth D. Merry
288ef270ab1SKenneth D. Merry if (0 == resource_int_value(device_get_name(ocs->dev), device_get_unit(ocs->dev),
289ef270ab1SKenneth D. Merry "enable_hlm", &i)) {
290ef270ab1SKenneth D. Merry device_printf(ocs->dev, "enable_hlm = %d\n", i);
291ef270ab1SKenneth D. Merry ocs->enable_hlm = i;
292ef270ab1SKenneth D. Merry if (ocs->enable_hlm) {
293ef270ab1SKenneth D. Merry ocs->hlm_group_size = 8;
294ef270ab1SKenneth D. Merry
295ef270ab1SKenneth D. Merry if (0 == resource_int_value(device_get_name(ocs->dev), device_get_unit(ocs->dev),
296ef270ab1SKenneth D. Merry "hlm_group_size", &i)) {
297ef270ab1SKenneth D. Merry ocs->hlm_group_size = i;
298ef270ab1SKenneth D. Merry }
299ef270ab1SKenneth D. Merry device_printf(ocs->dev, "hlm_group_size = %d\n", i);
300ef270ab1SKenneth D. Merry }
301ef270ab1SKenneth D. Merry }
302ef270ab1SKenneth D. Merry
303ef270ab1SKenneth D. Merry if (0 == resource_int_value(device_get_name(ocs->dev), device_get_unit(ocs->dev),
304ef270ab1SKenneth D. Merry "num_scsi_ios", &i)) {
305ef270ab1SKenneth D. Merry ocs->num_scsi_ios = i;
306ef270ab1SKenneth D. Merry device_printf(ocs->dev, "num_scsi_ios = %d\n", ocs->num_scsi_ios);
307ef270ab1SKenneth D. Merry } else {
308ef270ab1SKenneth D. Merry ocs->num_scsi_ios = 8192;
309ef270ab1SKenneth D. Merry }
310ef270ab1SKenneth D. Merry
311ef270ab1SKenneth D. Merry if (0 == resource_int_value(device_get_name(ocs->dev), device_get_unit(ocs->dev),
312ef270ab1SKenneth D. Merry "topology", &i)) {
313ef270ab1SKenneth D. Merry ocs->topology = i;
314ef270ab1SKenneth D. Merry device_printf(ocs->dev, "Setting topology=%#x\n", i);
315ef270ab1SKenneth D. Merry }
316ef270ab1SKenneth D. Merry
317ef270ab1SKenneth D. Merry if (0 == resource_int_value(device_get_name(ocs->dev), device_get_unit(ocs->dev),
318ef270ab1SKenneth D. Merry "num_vports", &i)) {
319ef270ab1SKenneth D. Merry if (i >= 0 && i <= 254) {
320ef270ab1SKenneth D. Merry device_printf(ocs->dev, "num_vports = %d\n", i);
321ef270ab1SKenneth D. Merry ocs->num_vports = i;
322ef270ab1SKenneth D. Merry } else {
323ef270ab1SKenneth D. Merry device_printf(ocs->dev, "num_vports: %d not supported \n", i);
324ef270ab1SKenneth D. Merry }
325ef270ab1SKenneth D. Merry }
326ef270ab1SKenneth D. Merry
327ef270ab1SKenneth D. Merry if (0 == resource_int_value(device_get_name(ocs->dev), device_get_unit(ocs->dev),
328ef270ab1SKenneth D. Merry "external_loopback", &i)) {
329ef270ab1SKenneth D. Merry device_printf(ocs->dev, "external_loopback = %d\n", i);
330ef270ab1SKenneth D. Merry ocs->external_loopback = i;
331ef270ab1SKenneth D. Merry }
332ef270ab1SKenneth D. Merry
333ef270ab1SKenneth D. Merry if (0 == resource_int_value(device_get_name(ocs->dev), device_get_unit(ocs->dev),
334ef270ab1SKenneth D. Merry "tgt_rscn_delay", &i)) {
335ef270ab1SKenneth D. Merry device_printf(ocs->dev, "tgt_rscn_delay = %d\n", i);
336ef270ab1SKenneth D. Merry ocs->tgt_rscn_delay_msec = i * 1000;
337ef270ab1SKenneth D. Merry }
338ef270ab1SKenneth D. Merry
339ef270ab1SKenneth D. Merry if (0 == resource_int_value(device_get_name(ocs->dev), device_get_unit(ocs->dev),
340ef270ab1SKenneth D. Merry "tgt_rscn_period", &i)) {
341ef270ab1SKenneth D. Merry device_printf(ocs->dev, "tgt_rscn_period = %d\n", i);
342ef270ab1SKenneth D. Merry ocs->tgt_rscn_period_msec = i * 1000;
343ef270ab1SKenneth D. Merry }
344ef270ab1SKenneth D. Merry
345ef270ab1SKenneth D. Merry if (0 == resource_int_value(device_get_name(ocs->dev), device_get_unit(ocs->dev),
346ef270ab1SKenneth D. Merry "target_io_timer", &i)) {
347ef270ab1SKenneth D. Merry device_printf(ocs->dev, "target_io_timer = %d\n", i);
348ef270ab1SKenneth D. Merry ocs->target_io_timer_sec = i;
349ef270ab1SKenneth D. Merry }
350ef270ab1SKenneth D. Merry
351ef270ab1SKenneth D. Merry hw_global.queue_topology_string = queue_topology;
352ef270ab1SKenneth D. Merry ocs->rq_selection_policy = 0;
353ef270ab1SKenneth D. Merry ocs->rr_quanta = 1;
354ef270ab1SKenneth D. Merry ocs->filter_def = "0,0,0,0";
355ef270ab1SKenneth D. Merry
356ef270ab1SKenneth D. Merry return 0;
357ef270ab1SKenneth D. Merry }
358ef270ab1SKenneth D. Merry
359ef270ab1SKenneth D. Merry static int32_t
ocs_setup_fcports(ocs_t * ocs)360ef270ab1SKenneth D. Merry ocs_setup_fcports(ocs_t *ocs)
361ef270ab1SKenneth D. Merry {
362ef270ab1SKenneth D. Merry uint32_t i = 0, role = 0;
363ef270ab1SKenneth D. Merry uint64_t sli_wwpn, sli_wwnn;
364ef270ab1SKenneth D. Merry size_t size;
365ef270ab1SKenneth D. Merry ocs_xport_t *xport = ocs->xport;
366ef270ab1SKenneth D. Merry ocs_vport_spec_t *vport;
367ef270ab1SKenneth D. Merry ocs_fcport *fcp = NULL;
368ef270ab1SKenneth D. Merry
369ef270ab1SKenneth D. Merry size = sizeof(ocs_fcport) * (ocs->num_vports + 1);
370ef270ab1SKenneth D. Merry
371ef270ab1SKenneth D. Merry ocs->fcports = ocs_malloc(ocs, size, M_ZERO|M_NOWAIT);
372ef270ab1SKenneth D. Merry if (ocs->fcports == NULL) {
373ef270ab1SKenneth D. Merry device_printf(ocs->dev, "Can't allocate fcport \n");
374ef270ab1SKenneth D. Merry return 1;
375ef270ab1SKenneth D. Merry }
376ef270ab1SKenneth D. Merry
377ef270ab1SKenneth D. Merry role = (ocs->enable_ini)? KNOB_ROLE_INITIATOR: 0 |
378ef270ab1SKenneth D. Merry (ocs->enable_tgt)? KNOB_ROLE_TARGET: 0;
379ef270ab1SKenneth D. Merry
380ef270ab1SKenneth D. Merry fcp = FCPORT(ocs, i);
381ef270ab1SKenneth D. Merry fcp->role = role;
382ef270ab1SKenneth D. Merry i++;
383ef270ab1SKenneth D. Merry
384ef270ab1SKenneth D. Merry ocs_list_foreach(&xport->vport_list, vport) {
385ef270ab1SKenneth D. Merry fcp = FCPORT(ocs, i);
386ef270ab1SKenneth D. Merry vport->tgt_data = fcp;
387ef270ab1SKenneth D. Merry fcp->vport = vport;
388ef270ab1SKenneth D. Merry fcp->role = role;
389ef270ab1SKenneth D. Merry
390ef270ab1SKenneth D. Merry if (ocs_hw_get_def_wwn(ocs, i, &sli_wwpn, &sli_wwnn)) {
391ef270ab1SKenneth D. Merry ocs_log_err(ocs, "Get default wwn failed \n");
392ef270ab1SKenneth D. Merry i++;
393ef270ab1SKenneth D. Merry continue;
394ef270ab1SKenneth D. Merry }
395ef270ab1SKenneth D. Merry
396ef270ab1SKenneth D. Merry vport->wwpn = ocs_be64toh(sli_wwpn);
397ef270ab1SKenneth D. Merry vport->wwnn = ocs_be64toh(sli_wwnn);
398ef270ab1SKenneth D. Merry i++;
399ef270ab1SKenneth D. Merry ocs_log_debug(ocs, "VPort wwpn: %lx wwnn: %lx \n", vport->wwpn, vport->wwnn);
400ef270ab1SKenneth D. Merry }
401ef270ab1SKenneth D. Merry
402ef270ab1SKenneth D. Merry return 0;
403ef270ab1SKenneth D. Merry }
404ef270ab1SKenneth D. Merry
405ef270ab1SKenneth D. Merry int32_t
ocs_device_attach(ocs_t * ocs)406ef270ab1SKenneth D. Merry ocs_device_attach(ocs_t *ocs)
407ef270ab1SKenneth D. Merry {
408ef270ab1SKenneth D. Merry int32_t i;
409ef270ab1SKenneth D. Merry ocs_io_t *io = NULL;
410ef270ab1SKenneth D. Merry
411ef270ab1SKenneth D. Merry if (ocs->attached) {
412ef270ab1SKenneth D. Merry ocs_log_warn(ocs, "%s: Device is already attached\n", __func__);
413ef270ab1SKenneth D. Merry return -1;
414ef270ab1SKenneth D. Merry }
415ef270ab1SKenneth D. Merry
416ef270ab1SKenneth D. Merry /* Allocate transport object and bring online */
417ef270ab1SKenneth D. Merry ocs->xport = ocs_xport_alloc(ocs);
418ef270ab1SKenneth D. Merry if (ocs->xport == NULL) {
419ef270ab1SKenneth D. Merry device_printf(ocs->dev, "failed to allocate transport object\n");
420ef270ab1SKenneth D. Merry return ENOMEM;
421ef270ab1SKenneth D. Merry } else if (ocs_xport_attach(ocs->xport) != 0) {
422ef270ab1SKenneth D. Merry device_printf(ocs->dev, "%s: failed to attach transport object\n", __func__);
423ef270ab1SKenneth D. Merry goto fail_xport_attach;
424ef270ab1SKenneth D. Merry } else if (ocs_xport_initialize(ocs->xport) != 0) {
425ef270ab1SKenneth D. Merry device_printf(ocs->dev, "%s: failed to initialize transport object\n", __func__);
426ef270ab1SKenneth D. Merry goto fail_xport_init;
427ef270ab1SKenneth D. Merry }
428ef270ab1SKenneth D. Merry
429ef270ab1SKenneth D. Merry if (ocs_init_dma_tag(ocs)) {
430ef270ab1SKenneth D. Merry goto fail_intr_setup;
431ef270ab1SKenneth D. Merry }
432ef270ab1SKenneth D. Merry
433ef270ab1SKenneth D. Merry for (i = 0; (io = ocs_io_get_instance(ocs, i)); i++) {
434ef270ab1SKenneth D. Merry if (bus_dmamap_create(ocs->buf_dmat, 0, &io->tgt_io.dmap)) {
435ef270ab1SKenneth D. Merry device_printf(ocs->dev, "%s: bad dma map create\n", __func__);
436ef270ab1SKenneth D. Merry }
437ef270ab1SKenneth D. Merry
438ef270ab1SKenneth D. Merry io->tgt_io.state = OCS_CAM_IO_FREE;
439ef270ab1SKenneth D. Merry }
440ef270ab1SKenneth D. Merry
441ef270ab1SKenneth D. Merry if (ocs_setup_fcports(ocs)) {
442ef270ab1SKenneth D. Merry device_printf(ocs->dev, "FCports creation failed\n");
443ef270ab1SKenneth D. Merry goto fail_intr_setup;
444ef270ab1SKenneth D. Merry }
445ef270ab1SKenneth D. Merry
446ef270ab1SKenneth D. Merry if (ocs_cam_attach(ocs)) {
447ef270ab1SKenneth D. Merry device_printf(ocs->dev, "cam attach failed \n");
448ef270ab1SKenneth D. Merry goto fail_intr_setup;
449ef270ab1SKenneth D. Merry }
450ef270ab1SKenneth D. Merry
451ef270ab1SKenneth D. Merry if (ocs_intr_setup(ocs)) {
452ef270ab1SKenneth D. Merry device_printf(ocs->dev, "Interrupt setup failed\n");
453ef270ab1SKenneth D. Merry goto fail_intr_setup;
454ef270ab1SKenneth D. Merry }
455ef270ab1SKenneth D. Merry
456ef270ab1SKenneth D. Merry if (ocs->enable_ini || ocs->enable_tgt) {
457ef270ab1SKenneth D. Merry if (ocs_xport_control(ocs->xport, OCS_XPORT_PORT_ONLINE)) {
458ef270ab1SKenneth D. Merry device_printf(ocs->dev, "Can't init port\n");
459ef270ab1SKenneth D. Merry goto fail_xport_online;
460ef270ab1SKenneth D. Merry }
461ef270ab1SKenneth D. Merry }
462ef270ab1SKenneth D. Merry
463ef270ab1SKenneth D. Merry ocs->attached = true;
464ef270ab1SKenneth D. Merry
465ef270ab1SKenneth D. Merry return 0;
466ef270ab1SKenneth D. Merry
467ef270ab1SKenneth D. Merry fail_xport_online:
468ef270ab1SKenneth D. Merry if (ocs_xport_control(ocs->xport, OCS_XPORT_SHUTDOWN)) {
469ef270ab1SKenneth D. Merry device_printf(ocs->dev, "Transport Shutdown timed out\n");
470ef270ab1SKenneth D. Merry }
471ef270ab1SKenneth D. Merry ocs_intr_teardown(ocs);
472ef270ab1SKenneth D. Merry fail_intr_setup:
473ef270ab1SKenneth D. Merry fail_xport_init:
474ef270ab1SKenneth D. Merry ocs_xport_detach(ocs->xport);
475ef270ab1SKenneth D. Merry if (ocs->config_tgt)
476ef270ab1SKenneth D. Merry ocs_scsi_tgt_del_device(ocs);
477ef270ab1SKenneth D. Merry
478ef270ab1SKenneth D. Merry ocs_xport_free(ocs->xport);
479ef270ab1SKenneth D. Merry ocs->xport = NULL;
480ef270ab1SKenneth D. Merry fail_xport_attach:
481ef270ab1SKenneth D. Merry if (ocs->xport)
482ef270ab1SKenneth D. Merry ocs_free(ocs, ocs->xport, sizeof(*(ocs->xport)));
483ef270ab1SKenneth D. Merry ocs->xport = NULL;
484ef270ab1SKenneth D. Merry return ENXIO;
485ef270ab1SKenneth D. Merry }
486ef270ab1SKenneth D. Merry
487ef270ab1SKenneth D. Merry /**
488ef270ab1SKenneth D. Merry * @brief Connect the driver to the given device
489ef270ab1SKenneth D. Merry *
490ef270ab1SKenneth D. Merry * If the probe routine is successful, the OS will give the driver
491ef270ab1SKenneth D. Merry * the opportunity to connect itself to the device. This routine
492ef270ab1SKenneth D. Merry * maps PCI resources (memory BARs and interrupts) and initialize a
493ef270ab1SKenneth D. Merry * hardware object.
494ef270ab1SKenneth D. Merry *
495ef270ab1SKenneth D. Merry * @param dev device abstraction
496ef270ab1SKenneth D. Merry *
497ef270ab1SKenneth D. Merry * @return 0 if the driver attaches to the device, ENXIO otherwise
498ef270ab1SKenneth D. Merry */
499ef270ab1SKenneth D. Merry
500ef270ab1SKenneth D. Merry static int
ocs_pci_attach(device_t dev)501ef270ab1SKenneth D. Merry ocs_pci_attach(device_t dev)
502ef270ab1SKenneth D. Merry {
503ef270ab1SKenneth D. Merry struct ocs_softc *ocs;
504ef270ab1SKenneth D. Merry int instance;
505ef270ab1SKenneth D. Merry
506ef270ab1SKenneth D. Merry instance = device_get_unit(dev);
507ef270ab1SKenneth D. Merry
508ef270ab1SKenneth D. Merry ocs = (struct ocs_softc *)device_get_softc(dev);
509ef270ab1SKenneth D. Merry if (NULL == ocs) {
510ef270ab1SKenneth D. Merry device_printf(dev, "cannot allocate softc\n");
511ef270ab1SKenneth D. Merry return ENOMEM;
512ef270ab1SKenneth D. Merry }
513ef270ab1SKenneth D. Merry memset(ocs, 0, sizeof(struct ocs_softc));
514ef270ab1SKenneth D. Merry
515ef270ab1SKenneth D. Merry if (instance < ARRAY_SIZE(ocs_devices)) {
516ef270ab1SKenneth D. Merry ocs_devices[instance] = ocs;
517ef270ab1SKenneth D. Merry } else {
518ef270ab1SKenneth D. Merry device_printf(dev, "got unexpected ocs instance number %d\n", instance);
519ef270ab1SKenneth D. Merry }
520ef270ab1SKenneth D. Merry
521ef270ab1SKenneth D. Merry ocs->instance_index = instance;
522ef270ab1SKenneth D. Merry
523ef270ab1SKenneth D. Merry ocs->dev = dev;
524ef270ab1SKenneth D. Merry
525ef270ab1SKenneth D. Merry pci_enable_io(dev, SYS_RES_MEMORY);
526ef270ab1SKenneth D. Merry pci_enable_busmaster(dev);
527ef270ab1SKenneth D. Merry
528ef270ab1SKenneth D. Merry ocs->pci_vendor = pci_get_vendor(dev);
529ef270ab1SKenneth D. Merry ocs->pci_device = pci_get_device(dev);
530cba757efSRam Kishore Vegesna ocs->pci_subsystem_vendor = pci_get_subvendor(dev);
531cba757efSRam Kishore Vegesna ocs->pci_subsystem_device = pci_get_subdevice(dev);
532cba757efSRam Kishore Vegesna
533ef270ab1SKenneth D. Merry snprintf(ocs->businfo, sizeof(ocs->businfo), "%02X:%02X:%02X",
534ef270ab1SKenneth D. Merry pci_get_bus(dev), pci_get_slot(dev), pci_get_function(dev));
535ef270ab1SKenneth D. Merry
536ef270ab1SKenneth D. Merry /* Map all memory BARs */
5373bf42363SRam Kishore Vegesna if (ocs->pci_device == PCI_PRODUCT_EMULEX_LANCER_G7) {
5383bf42363SRam Kishore Vegesna if(ocs_map_g7_bars(dev,ocs)) {
5393bf42363SRam Kishore Vegesna device_printf(dev, "Failed to map pci bars\n");
5403bf42363SRam Kishore Vegesna goto release_bus;
5413bf42363SRam Kishore Vegesna }
5423bf42363SRam Kishore Vegesna } else {
543ef270ab1SKenneth D. Merry if (ocs_map_bars(dev, ocs)) {
544ef270ab1SKenneth D. Merry device_printf(dev, "Failed to map pci bars\n");
545ef270ab1SKenneth D. Merry goto release_bus;
546ef270ab1SKenneth D. Merry }
5473bf42363SRam Kishore Vegesna }
548ef270ab1SKenneth D. Merry
549ef270ab1SKenneth D. Merry /* create a root DMA tag for the device */
550ef270ab1SKenneth D. Merry if (bus_dma_tag_create(bus_get_dma_tag(dev),
551ef270ab1SKenneth D. Merry 1, /* byte alignment */
552ef270ab1SKenneth D. Merry 0, /* no boundary restrictions */
553ef270ab1SKenneth D. Merry BUS_SPACE_MAXADDR, /* no minimum low address */
554ef270ab1SKenneth D. Merry BUS_SPACE_MAXADDR, /* no maximum high address */
555ef270ab1SKenneth D. Merry NULL, /* no filter function */
556ef270ab1SKenneth D. Merry NULL, /* or arguments */
557ef270ab1SKenneth D. Merry BUS_SPACE_MAXSIZE, /* max size covered by tag */
558ef270ab1SKenneth D. Merry BUS_SPACE_UNRESTRICTED, /* no segment count restrictions */
559ef270ab1SKenneth D. Merry BUS_SPACE_MAXSIZE, /* no segment length restrictions */
560ef270ab1SKenneth D. Merry 0, /* flags */
561ef270ab1SKenneth D. Merry NULL, /* no lock manipulation function */
562ef270ab1SKenneth D. Merry NULL, /* or arguments */
563ef270ab1SKenneth D. Merry &ocs->dmat)) {
564ef270ab1SKenneth D. Merry device_printf(dev, "parent DMA tag allocation failed\n");
565ef270ab1SKenneth D. Merry goto release_bus;
566ef270ab1SKenneth D. Merry }
567ef270ab1SKenneth D. Merry
568ef270ab1SKenneth D. Merry if (ocs_intr_alloc(ocs)) {
569ef270ab1SKenneth D. Merry device_printf(dev, "Interrupt allocation failed\n");
570ef270ab1SKenneth D. Merry goto release_bus;
571ef270ab1SKenneth D. Merry }
572ef270ab1SKenneth D. Merry
573ef270ab1SKenneth D. Merry if (PCIC_SERIALBUS == pci_get_class(dev) &&
574ef270ab1SKenneth D. Merry PCIS_SERIALBUS_FC == pci_get_subclass(dev))
575ef270ab1SKenneth D. Merry ocs->ocs_xport = OCS_XPORT_FC;
576ef270ab1SKenneth D. Merry else {
577ef270ab1SKenneth D. Merry device_printf(dev, "unsupported class (%#x : %#x)\n",
578ef270ab1SKenneth D. Merry pci_get_class(dev),
579ef270ab1SKenneth D. Merry pci_get_class(dev));
580ef270ab1SKenneth D. Merry goto release_bus;
581ef270ab1SKenneth D. Merry }
582ef270ab1SKenneth D. Merry
583ef270ab1SKenneth D. Merry /* Setup tunable parameters */
584ef270ab1SKenneth D. Merry if (ocs_setup_params(ocs)) {
585ef270ab1SKenneth D. Merry device_printf(ocs->dev, "failed to setup params\n");
586ef270ab1SKenneth D. Merry goto release_bus;
587ef270ab1SKenneth D. Merry }
588ef270ab1SKenneth D. Merry
589ef270ab1SKenneth D. Merry if (ocs_device_attach(ocs)) {
590ef270ab1SKenneth D. Merry device_printf(ocs->dev, "failed to attach device\n");
591ef270ab1SKenneth D. Merry goto release_params;
592ef270ab1SKenneth D. Merry }
593ef270ab1SKenneth D. Merry
594ef270ab1SKenneth D. Merry ocs->fc_type = FC_TYPE_FCP;
595ef270ab1SKenneth D. Merry
596ef270ab1SKenneth D. Merry ocs_debug_attach(ocs);
597ef270ab1SKenneth D. Merry
598ef270ab1SKenneth D. Merry return 0;
599ef270ab1SKenneth D. Merry
600ef270ab1SKenneth D. Merry release_params:
601ef270ab1SKenneth D. Merry ocs_ramlog_free(ocs, ocs->ramlog);
602ef270ab1SKenneth D. Merry ocs_device_lock_free(ocs);
603ef270ab1SKenneth D. Merry free(ocs->hw_war_version, M_OCS);
604ef270ab1SKenneth D. Merry release_bus:
605ef270ab1SKenneth D. Merry ocs_release_bus(ocs);
606ef270ab1SKenneth D. Merry return ENXIO;
607ef270ab1SKenneth D. Merry }
608ef270ab1SKenneth D. Merry
609ef270ab1SKenneth D. Merry /**
610ef270ab1SKenneth D. Merry * @brief free resources when pci device detach
611ef270ab1SKenneth D. Merry *
612ef270ab1SKenneth D. Merry * @param ocs pointer to ocs structure
613ef270ab1SKenneth D. Merry *
614ef270ab1SKenneth D. Merry * @return 0 for success, a negative error code value for failure.
615ef270ab1SKenneth D. Merry */
616ef270ab1SKenneth D. Merry
617ef270ab1SKenneth D. Merry int32_t
ocs_device_detach(ocs_t * ocs)618ef270ab1SKenneth D. Merry ocs_device_detach(ocs_t *ocs)
619ef270ab1SKenneth D. Merry {
620ef270ab1SKenneth D. Merry int32_t rc = 0, i;
621ef270ab1SKenneth D. Merry ocs_io_t *io = NULL;
622ef270ab1SKenneth D. Merry
623ef270ab1SKenneth D. Merry if (ocs != NULL) {
624ef270ab1SKenneth D. Merry if (!ocs->attached) {
625ef270ab1SKenneth D. Merry ocs_log_warn(ocs, "%s: Device is not attached\n", __func__);
626ef270ab1SKenneth D. Merry return -1;
627ef270ab1SKenneth D. Merry }
628ef270ab1SKenneth D. Merry
6296affb8ebSRam Kishore Vegesna ocs->attached = FALSE;
6306affb8ebSRam Kishore Vegesna
631ef270ab1SKenneth D. Merry rc = ocs_xport_control(ocs->xport, OCS_XPORT_SHUTDOWN);
632ef270ab1SKenneth D. Merry if (rc) {
633ef270ab1SKenneth D. Merry ocs_log_err(ocs, "%s: Transport Shutdown timed out\n", __func__);
634ef270ab1SKenneth D. Merry }
635ef270ab1SKenneth D. Merry
636ef270ab1SKenneth D. Merry ocs_intr_teardown(ocs);
637ef270ab1SKenneth D. Merry
638ef270ab1SKenneth D. Merry if (ocs_xport_detach(ocs->xport) != 0) {
639ef270ab1SKenneth D. Merry ocs_log_err(ocs, "%s: Transport detach failed\n", __func__);
640ef270ab1SKenneth D. Merry }
641ef270ab1SKenneth D. Merry
642ef270ab1SKenneth D. Merry ocs_cam_detach(ocs);
6434915e5c7SRam Kishore Vegesna ocs_free(ocs, ocs->fcports, sizeof(*(ocs->fcports)));
644ef270ab1SKenneth D. Merry
645ef270ab1SKenneth D. Merry for (i = 0; (io = ocs_io_get_instance(ocs, i)); i++) {
646ef270ab1SKenneth D. Merry if (bus_dmamap_destroy(ocs->buf_dmat, io->tgt_io.dmap)) {
647ef270ab1SKenneth D. Merry device_printf(ocs->dev, "%s: bad dma map destroy\n", __func__);
648ef270ab1SKenneth D. Merry }
649ef270ab1SKenneth D. Merry }
650ef270ab1SKenneth D. Merry bus_dma_tag_destroy(ocs->dmat);
651ef270ab1SKenneth D. Merry ocs_xport_free(ocs->xport);
652ef270ab1SKenneth D. Merry ocs->xport = NULL;
653ef270ab1SKenneth D. Merry }
654ef270ab1SKenneth D. Merry
655ef270ab1SKenneth D. Merry return 0;
656ef270ab1SKenneth D. Merry }
657ef270ab1SKenneth D. Merry
658ef270ab1SKenneth D. Merry /**
659ef270ab1SKenneth D. Merry * @brief Detach the driver from the given device
660ef270ab1SKenneth D. Merry *
661ef270ab1SKenneth D. Merry * If the driver is a loadable module, this routine gets called at unload
662ef270ab1SKenneth D. Merry * time. This routine will stop the device and free any allocated resources.
663ef270ab1SKenneth D. Merry *
664ef270ab1SKenneth D. Merry * @param dev device abstraction
665ef270ab1SKenneth D. Merry *
666ef270ab1SKenneth D. Merry * @return 0 if the driver detaches from the device, ENXIO otherwise
667ef270ab1SKenneth D. Merry */
668ef270ab1SKenneth D. Merry static int
ocs_pci_detach(device_t dev)669ef270ab1SKenneth D. Merry ocs_pci_detach(device_t dev)
670ef270ab1SKenneth D. Merry {
671ef270ab1SKenneth D. Merry struct ocs_softc *ocs;
672ef270ab1SKenneth D. Merry
673ef270ab1SKenneth D. Merry ocs = (struct ocs_softc *)device_get_softc(dev);
674ef270ab1SKenneth D. Merry if (!ocs) {
675ef270ab1SKenneth D. Merry device_printf(dev, "no driver context?!?\n");
676ef270ab1SKenneth D. Merry return -1;
677ef270ab1SKenneth D. Merry }
678ef270ab1SKenneth D. Merry
679ef270ab1SKenneth D. Merry if (ocs->config_tgt && ocs->enable_tgt) {
680ef270ab1SKenneth D. Merry device_printf(dev, "can't detach with target mode enabled\n");
681ef270ab1SKenneth D. Merry return EBUSY;
682ef270ab1SKenneth D. Merry }
683ef270ab1SKenneth D. Merry
684ef270ab1SKenneth D. Merry ocs_device_detach(ocs);
685ef270ab1SKenneth D. Merry
686ef270ab1SKenneth D. Merry /*
687ef270ab1SKenneth D. Merry * Workaround for OCS SCSI Transport quirk.
688ef270ab1SKenneth D. Merry *
689ef270ab1SKenneth D. Merry * CTL requires that target mode is disabled prior to unloading the
690ef270ab1SKenneth D. Merry * driver (ie ocs->enable_tgt = FALSE), but once the target is disabled,
691ef270ab1SKenneth D. Merry * the transport will not call ocs_scsi_tgt_del_device() which deallocates
692ef270ab1SKenneth D. Merry * CAM resources. The workaround is to explicitly make the call here.
693ef270ab1SKenneth D. Merry */
694ef270ab1SKenneth D. Merry if (ocs->config_tgt)
695ef270ab1SKenneth D. Merry ocs_scsi_tgt_del_device(ocs);
696ef270ab1SKenneth D. Merry
697ef270ab1SKenneth D. Merry /* free strdup created buffer.*/
698ef270ab1SKenneth D. Merry free(ocs->hw_war_version, M_OCS);
699ef270ab1SKenneth D. Merry
700ef270ab1SKenneth D. Merry ocs_device_lock_free(ocs);
701ef270ab1SKenneth D. Merry
702ef270ab1SKenneth D. Merry ocs_debug_detach(ocs);
703ef270ab1SKenneth D. Merry
704ef270ab1SKenneth D. Merry ocs_ramlog_free(ocs, ocs->ramlog);
705ef270ab1SKenneth D. Merry
706ef270ab1SKenneth D. Merry ocs_release_bus(ocs);
707ef270ab1SKenneth D. Merry
708ef270ab1SKenneth D. Merry return 0;
709ef270ab1SKenneth D. Merry }
710ef270ab1SKenneth D. Merry
711ef270ab1SKenneth D. Merry /**
712ef270ab1SKenneth D. Merry * @brief Notify driver of system shutdown
713ef270ab1SKenneth D. Merry *
714ef270ab1SKenneth D. Merry * @param dev device abstraction
715ef270ab1SKenneth D. Merry *
716ef270ab1SKenneth D. Merry * @return 0 if the driver attaches to the device, ENXIO otherwise
717ef270ab1SKenneth D. Merry */
718ef270ab1SKenneth D. Merry static int
ocs_pci_shutdown(device_t dev)719ef270ab1SKenneth D. Merry ocs_pci_shutdown(device_t dev)
720ef270ab1SKenneth D. Merry {
721ef270ab1SKenneth D. Merry device_printf(dev, "%s\n", __func__);
722ef270ab1SKenneth D. Merry return 0;
723ef270ab1SKenneth D. Merry }
724ef270ab1SKenneth D. Merry
725ef270ab1SKenneth D. Merry /**
726ef270ab1SKenneth D. Merry * @brief Release bus resources allocated within the soft context
727ef270ab1SKenneth D. Merry *
728ef270ab1SKenneth D. Merry * @param ocs Pointer to the driver's context
729ef270ab1SKenneth D. Merry *
730ef270ab1SKenneth D. Merry * @return none
731ef270ab1SKenneth D. Merry */
732ef270ab1SKenneth D. Merry static void
ocs_release_bus(struct ocs_softc * ocs)733ef270ab1SKenneth D. Merry ocs_release_bus(struct ocs_softc *ocs)
734ef270ab1SKenneth D. Merry {
735ef270ab1SKenneth D. Merry
736ef270ab1SKenneth D. Merry if (NULL != ocs) {
737ef270ab1SKenneth D. Merry uint32_t i;
738ef270ab1SKenneth D. Merry
739ef270ab1SKenneth D. Merry ocs_intr_teardown(ocs);
740ef270ab1SKenneth D. Merry
741ef270ab1SKenneth D. Merry if (ocs->irq) {
742ef270ab1SKenneth D. Merry bus_release_resource(ocs->dev, SYS_RES_IRQ,
743ef270ab1SKenneth D. Merry rman_get_rid(ocs->irq), ocs->irq);
744ef270ab1SKenneth D. Merry
745ef270ab1SKenneth D. Merry if (ocs->n_vec) {
746ef270ab1SKenneth D. Merry pci_release_msi(ocs->dev);
747ef270ab1SKenneth D. Merry ocs->n_vec = 0;
748ef270ab1SKenneth D. Merry }
749ef270ab1SKenneth D. Merry
750ef270ab1SKenneth D. Merry ocs->irq = NULL;
751ef270ab1SKenneth D. Merry }
752ef270ab1SKenneth D. Merry
753ef270ab1SKenneth D. Merry bus_dma_tag_destroy(ocs->dmat);
754ef270ab1SKenneth D. Merry
755ef270ab1SKenneth D. Merry for (i = 0; i < PCI_MAX_BAR; i++) {
756ef270ab1SKenneth D. Merry if (ocs->reg[i].res) {
757ef270ab1SKenneth D. Merry bus_release_resource(ocs->dev, SYS_RES_MEMORY,
758ef270ab1SKenneth D. Merry ocs->reg[i].rid,
759ef270ab1SKenneth D. Merry ocs->reg[i].res);
760ef270ab1SKenneth D. Merry }
761ef270ab1SKenneth D. Merry }
762ef270ab1SKenneth D. Merry }
763ef270ab1SKenneth D. Merry }
764ef270ab1SKenneth D. Merry
765ef270ab1SKenneth D. Merry /**
766ef270ab1SKenneth D. Merry * @brief Allocate and initialize interrupts
767ef270ab1SKenneth D. Merry *
768ef270ab1SKenneth D. Merry * @param ocs Pointer to the driver's context
769ef270ab1SKenneth D. Merry *
770ef270ab1SKenneth D. Merry * @return none
771ef270ab1SKenneth D. Merry */
772ef270ab1SKenneth D. Merry static int32_t
ocs_intr_alloc(struct ocs_softc * ocs)773ef270ab1SKenneth D. Merry ocs_intr_alloc(struct ocs_softc *ocs)
774ef270ab1SKenneth D. Merry {
775ef270ab1SKenneth D. Merry
776ef270ab1SKenneth D. Merry ocs->n_vec = 1;
777ef270ab1SKenneth D. Merry if (pci_alloc_msix(ocs->dev, &ocs->n_vec)) {
778ef270ab1SKenneth D. Merry device_printf(ocs->dev, "MSI-X allocation failed\n");
779ef270ab1SKenneth D. Merry if (pci_alloc_msi(ocs->dev, &ocs->n_vec)) {
780ef270ab1SKenneth D. Merry device_printf(ocs->dev, "MSI allocation failed \n");
781ef270ab1SKenneth D. Merry ocs->irqid = 0;
782ef270ab1SKenneth D. Merry ocs->n_vec = 0;
783ef270ab1SKenneth D. Merry } else
784ef270ab1SKenneth D. Merry ocs->irqid = 1;
785ef270ab1SKenneth D. Merry } else {
786ef270ab1SKenneth D. Merry ocs->irqid = 1;
787ef270ab1SKenneth D. Merry }
788ef270ab1SKenneth D. Merry
789ef270ab1SKenneth D. Merry ocs->irq = bus_alloc_resource_any(ocs->dev, SYS_RES_IRQ, &ocs->irqid,
790ef270ab1SKenneth D. Merry RF_ACTIVE | RF_SHAREABLE);
791ef270ab1SKenneth D. Merry if (NULL == ocs->irq) {
792ef270ab1SKenneth D. Merry device_printf(ocs->dev, "could not allocate interrupt\n");
793ef270ab1SKenneth D. Merry return -1;
794ef270ab1SKenneth D. Merry }
795ef270ab1SKenneth D. Merry
796ef270ab1SKenneth D. Merry ocs->intr_ctx.vec = 0;
797ef270ab1SKenneth D. Merry ocs->intr_ctx.softc = ocs;
798ef270ab1SKenneth D. Merry snprintf(ocs->intr_ctx.name, sizeof(ocs->intr_ctx.name),
799ef270ab1SKenneth D. Merry "%s_intr_%d",
800ef270ab1SKenneth D. Merry device_get_nameunit(ocs->dev),
801ef270ab1SKenneth D. Merry ocs->intr_ctx.vec);
802ef270ab1SKenneth D. Merry
803ef270ab1SKenneth D. Merry return 0;
804ef270ab1SKenneth D. Merry }
805ef270ab1SKenneth D. Merry
806ef270ab1SKenneth D. Merry /**
807ef270ab1SKenneth D. Merry * @brief Create and attach an interrupt handler
808ef270ab1SKenneth D. Merry *
809ef270ab1SKenneth D. Merry * @param ocs Pointer to the driver's context
810ef270ab1SKenneth D. Merry *
811ef270ab1SKenneth D. Merry * @return 0 on success, non-zero otherwise
812ef270ab1SKenneth D. Merry */
813ef270ab1SKenneth D. Merry static int32_t
ocs_intr_setup(struct ocs_softc * ocs)814ef270ab1SKenneth D. Merry ocs_intr_setup(struct ocs_softc *ocs)
815ef270ab1SKenneth D. Merry {
816ef270ab1SKenneth D. Merry driver_filter_t *filter = NULL;
817ef270ab1SKenneth D. Merry
818ef270ab1SKenneth D. Merry if (0 == ocs->n_vec) {
819ef270ab1SKenneth D. Merry filter = ocs_pci_intx_filter;
820ef270ab1SKenneth D. Merry }
821ef270ab1SKenneth D. Merry
822ef270ab1SKenneth D. Merry if (bus_setup_intr(ocs->dev, ocs->irq, INTR_MPSAFE | INTR_TYPE_CAM,
823ef270ab1SKenneth D. Merry filter, ocs_pci_intr, &ocs->intr_ctx,
824ef270ab1SKenneth D. Merry &ocs->tag)) {
825ef270ab1SKenneth D. Merry device_printf(ocs->dev, "could not initialize interrupt\n");
826ef270ab1SKenneth D. Merry return -1;
827ef270ab1SKenneth D. Merry }
828ef270ab1SKenneth D. Merry
829ef270ab1SKenneth D. Merry return 0;
830ef270ab1SKenneth D. Merry }
831ef270ab1SKenneth D. Merry
832ef270ab1SKenneth D. Merry /**
833ef270ab1SKenneth D. Merry * @brief Detach an interrupt handler
834ef270ab1SKenneth D. Merry *
835ef270ab1SKenneth D. Merry * @param ocs Pointer to the driver's context
836ef270ab1SKenneth D. Merry *
837ef270ab1SKenneth D. Merry * @return 0 on success, non-zero otherwise
838ef270ab1SKenneth D. Merry */
839ef270ab1SKenneth D. Merry static int32_t
ocs_intr_teardown(struct ocs_softc * ocs)840ef270ab1SKenneth D. Merry ocs_intr_teardown(struct ocs_softc *ocs)
841ef270ab1SKenneth D. Merry {
842ef270ab1SKenneth D. Merry
843ef270ab1SKenneth D. Merry if (!ocs) {
844ef270ab1SKenneth D. Merry printf("%s: bad driver context?!?\n", __func__);
845ef270ab1SKenneth D. Merry return -1;
846ef270ab1SKenneth D. Merry }
847ef270ab1SKenneth D. Merry
848ef270ab1SKenneth D. Merry if (ocs->tag) {
849ef270ab1SKenneth D. Merry bus_teardown_intr(ocs->dev, ocs->irq, ocs->tag);
850ef270ab1SKenneth D. Merry ocs->tag = NULL;
851ef270ab1SKenneth D. Merry }
852ef270ab1SKenneth D. Merry
853ef270ab1SKenneth D. Merry return 0;
854ef270ab1SKenneth D. Merry }
855ef270ab1SKenneth D. Merry
856ef270ab1SKenneth D. Merry /**
857ef270ab1SKenneth D. Merry * @brief PCI interrupt handler
858ef270ab1SKenneth D. Merry *
859ef270ab1SKenneth D. Merry * @param arg pointer to the driver's software context
860ef270ab1SKenneth D. Merry *
861ef270ab1SKenneth D. Merry * @return FILTER_HANDLED if interrupt is processed, FILTER_STRAY otherwise
862ef270ab1SKenneth D. Merry */
863ef270ab1SKenneth D. Merry static int
ocs_pci_intx_filter(void * arg)864ef270ab1SKenneth D. Merry ocs_pci_intx_filter(void *arg)
865ef270ab1SKenneth D. Merry {
866ef270ab1SKenneth D. Merry ocs_intr_ctx_t *intr = arg;
867ef270ab1SKenneth D. Merry struct ocs_softc *ocs = NULL;
868ef270ab1SKenneth D. Merry uint16_t val = 0;
869ef270ab1SKenneth D. Merry
870ef270ab1SKenneth D. Merry if (NULL == intr) {
871ef270ab1SKenneth D. Merry return FILTER_STRAY;
872ef270ab1SKenneth D. Merry }
873ef270ab1SKenneth D. Merry
874ef270ab1SKenneth D. Merry ocs = intr->softc;
875ef270ab1SKenneth D. Merry #ifndef PCIM_STATUS_INTR
876ef270ab1SKenneth D. Merry #define PCIM_STATUS_INTR 0x0008
877ef270ab1SKenneth D. Merry #endif
878ef270ab1SKenneth D. Merry val = pci_read_config(ocs->dev, PCIR_STATUS, 2);
879ef270ab1SKenneth D. Merry if (0xffff == val) {
880ef270ab1SKenneth D. Merry device_printf(ocs->dev, "%s: pci_read_config(PCIR_STATUS) failed\n", __func__);
881ef270ab1SKenneth D. Merry return FILTER_STRAY;
882ef270ab1SKenneth D. Merry }
883ef270ab1SKenneth D. Merry if (0 == (val & PCIM_STATUS_INTR)) {
884ef270ab1SKenneth D. Merry return FILTER_STRAY;
885ef270ab1SKenneth D. Merry }
886ef270ab1SKenneth D. Merry
887ef270ab1SKenneth D. Merry val = pci_read_config(ocs->dev, PCIR_COMMAND, 2);
888ef270ab1SKenneth D. Merry val |= PCIM_CMD_INTxDIS;
889ef270ab1SKenneth D. Merry pci_write_config(ocs->dev, PCIR_COMMAND, val, 2);
890ef270ab1SKenneth D. Merry
891ef270ab1SKenneth D. Merry return FILTER_SCHEDULE_THREAD;
892ef270ab1SKenneth D. Merry }
893ef270ab1SKenneth D. Merry
894ef270ab1SKenneth D. Merry /**
895ef270ab1SKenneth D. Merry * @brief interrupt handler
896ef270ab1SKenneth D. Merry *
897ef270ab1SKenneth D. Merry * @param context pointer to the interrupt context
898ef270ab1SKenneth D. Merry */
899ef270ab1SKenneth D. Merry static void
ocs_pci_intr(void * context)900ef270ab1SKenneth D. Merry ocs_pci_intr(void *context)
901ef270ab1SKenneth D. Merry {
902ef270ab1SKenneth D. Merry ocs_intr_ctx_t *intr = context;
903ef270ab1SKenneth D. Merry struct ocs_softc *ocs = intr->softc;
904ef270ab1SKenneth D. Merry
905ef270ab1SKenneth D. Merry mtx_lock(&ocs->sim_lock);
906ef270ab1SKenneth D. Merry ocs_hw_process(&ocs->hw, intr->vec, OCS_OS_MAX_ISR_TIME_MSEC);
907ef270ab1SKenneth D. Merry mtx_unlock(&ocs->sim_lock);
908ef270ab1SKenneth D. Merry }
909ef270ab1SKenneth D. Merry
910ef270ab1SKenneth D. Merry /**
911ef270ab1SKenneth D. Merry * @brief Initialize DMA tag
912ef270ab1SKenneth D. Merry *
913ef270ab1SKenneth D. Merry * @param ocs the driver instance's software context
914ef270ab1SKenneth D. Merry *
915ef270ab1SKenneth D. Merry * @return 0 on success, non-zero otherwise
916ef270ab1SKenneth D. Merry */
917ef270ab1SKenneth D. Merry static int32_t
ocs_init_dma_tag(struct ocs_softc * ocs)918ef270ab1SKenneth D. Merry ocs_init_dma_tag(struct ocs_softc *ocs)
919ef270ab1SKenneth D. Merry {
920ef270ab1SKenneth D. Merry uint32_t max_sgl = 0;
921ef270ab1SKenneth D. Merry uint32_t max_sge = 0;
922ef270ab1SKenneth D. Merry
923ef270ab1SKenneth D. Merry /*
924ef270ab1SKenneth D. Merry * IOs can't use the parent DMA tag and must create their
925ef270ab1SKenneth D. Merry * own, based primarily on a restricted number of DMA segments.
926ef270ab1SKenneth D. Merry * This is more of a BSD requirement than a SLI Port requirement
927ef270ab1SKenneth D. Merry */
928ef270ab1SKenneth D. Merry ocs_hw_get(&ocs->hw, OCS_HW_N_SGL, &max_sgl);
929ef270ab1SKenneth D. Merry ocs_hw_get(&ocs->hw, OCS_HW_MAX_SGE, &max_sge);
930ef270ab1SKenneth D. Merry
931ef270ab1SKenneth D. Merry if (bus_dma_tag_create(ocs->dmat,
932ef270ab1SKenneth D. Merry 1, /* byte alignment */
933ef270ab1SKenneth D. Merry 0, /* no boundary restrictions */
934ef270ab1SKenneth D. Merry BUS_SPACE_MAXADDR, /* no minimum low address */
935ef270ab1SKenneth D. Merry BUS_SPACE_MAXADDR, /* no maximum high address */
936ef270ab1SKenneth D. Merry NULL, /* no filter function */
937ef270ab1SKenneth D. Merry NULL, /* or arguments */
938ef270ab1SKenneth D. Merry BUS_SPACE_MAXSIZE, /* max size covered by tag */
939ef270ab1SKenneth D. Merry max_sgl, /* segment count restrictions */
940ef270ab1SKenneth D. Merry max_sge, /* segment length restrictions */
941ef270ab1SKenneth D. Merry 0, /* flags */
942ef270ab1SKenneth D. Merry NULL, /* no lock manipulation function */
943ef270ab1SKenneth D. Merry NULL, /* or arguments */
944ef270ab1SKenneth D. Merry &ocs->buf_dmat)) {
945ef270ab1SKenneth D. Merry device_printf(ocs->dev, "%s: bad bus_dma_tag_create(buf_dmat)\n", __func__);
946ef270ab1SKenneth D. Merry return -1;
947ef270ab1SKenneth D. Merry }
948ef270ab1SKenneth D. Merry return 0;
949ef270ab1SKenneth D. Merry }
950ef270ab1SKenneth D. Merry
951ef270ab1SKenneth D. Merry int32_t
ocs_get_property(const char * prop_name,char * buffer,uint32_t buffer_len)952ef270ab1SKenneth D. Merry ocs_get_property(const char *prop_name, char *buffer, uint32_t buffer_len)
953ef270ab1SKenneth D. Merry {
954ef270ab1SKenneth D. Merry return -1;
955ef270ab1SKenneth D. Merry }
956ef270ab1SKenneth D. Merry
957ef270ab1SKenneth D. Merry /**
958ef270ab1SKenneth D. Merry * @brief return pointer to ocs structure given instance index
959ef270ab1SKenneth D. Merry *
960ef270ab1SKenneth D. Merry * A pointer to an ocs structure is returned given an instance index.
961ef270ab1SKenneth D. Merry *
962ef270ab1SKenneth D. Merry * @param index index to ocs_devices array
963ef270ab1SKenneth D. Merry *
964ef270ab1SKenneth D. Merry * @return ocs pointer
965ef270ab1SKenneth D. Merry */
966ef270ab1SKenneth D. Merry
ocs_get_instance(uint32_t index)967ef270ab1SKenneth D. Merry ocs_t *ocs_get_instance(uint32_t index)
968ef270ab1SKenneth D. Merry {
969ef270ab1SKenneth D. Merry if (index < ARRAY_SIZE(ocs_devices)) {
970ef270ab1SKenneth D. Merry return ocs_devices[index];
971ef270ab1SKenneth D. Merry }
972ef270ab1SKenneth D. Merry return NULL;
973ef270ab1SKenneth D. Merry }
974ef270ab1SKenneth D. Merry
975ef270ab1SKenneth D. Merry /**
976ef270ab1SKenneth D. Merry * @brief Return instance index of an opaque ocs structure
977ef270ab1SKenneth D. Merry *
978ef270ab1SKenneth D. Merry * Returns the ocs instance index
979ef270ab1SKenneth D. Merry *
980ef270ab1SKenneth D. Merry * @param os pointer to ocs instance
981ef270ab1SKenneth D. Merry *
982ef270ab1SKenneth D. Merry * @return pointer to ocs instance index
983ef270ab1SKenneth D. Merry */
984ef270ab1SKenneth D. Merry uint32_t
ocs_instance(void * os)985ef270ab1SKenneth D. Merry ocs_instance(void *os)
986ef270ab1SKenneth D. Merry {
987ef270ab1SKenneth D. Merry ocs_t *ocs = os;
988ef270ab1SKenneth D. Merry return ocs->instance_index;
989ef270ab1SKenneth D. Merry }
990ef270ab1SKenneth D. Merry
991ef270ab1SKenneth D. Merry static device_method_t ocs_methods[] = {
992ef270ab1SKenneth D. Merry DEVMETHOD(device_probe, ocs_pci_probe),
993ef270ab1SKenneth D. Merry DEVMETHOD(device_attach, ocs_pci_attach),
994ef270ab1SKenneth D. Merry DEVMETHOD(device_detach, ocs_pci_detach),
995ef270ab1SKenneth D. Merry DEVMETHOD(device_shutdown, ocs_pci_shutdown),
996ef270ab1SKenneth D. Merry {0, 0}
997ef270ab1SKenneth D. Merry };
998ef270ab1SKenneth D. Merry
999ef270ab1SKenneth D. Merry static driver_t ocs_driver = {
1000ef270ab1SKenneth D. Merry "ocs_fc",
1001ef270ab1SKenneth D. Merry ocs_methods,
1002ef270ab1SKenneth D. Merry sizeof(struct ocs_softc)
1003ef270ab1SKenneth D. Merry };
1004ef270ab1SKenneth D. Merry
1005*b64711b6SJohn Baldwin DRIVER_MODULE(ocs_fc, pci, ocs_driver, 0, 0);
1006ef270ab1SKenneth D. Merry MODULE_VERSION(ocs_fc, 1);
1007