1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * This file is part of the Chelsio T4 support code.
14 *
15 * Copyright (C) 2011-2013 Chelsio Communications. All rights reserved.
16 *
17 * This program is distributed in the hope that it will be useful, but WITHOUT
18 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 * FITNESS FOR A PARTICULAR PURPOSE. See the LICENSE file included in this
20 * release for licensing terms and conditions.
21 */
22
23 #include <sys/ddi.h>
24 #include <sys/sunddi.h>
25 #include <sys/modctl.h>
26 #include <sys/conf.h>
27 #include <sys/atomic.h>
28 #include <sys/ethernet.h>
29 #include <sys/mac_provider.h>
30 #include <sys/mac_ether.h>
31
32 /*
33 * NOTE: The "real" NIC driver is in the nexus. This is just a thin wrapper
34 * whose only purpose is to register the mac.
35 */
36 #include "shared.h"
37 #include "version.h"
38
39 struct port_info_stub {
40 PORT_INFO_HDR;
41 };
42
43 static struct cb_ops cxgbe_cb_ops = {
44 .cb_open = nulldev,
45 .cb_close = nulldev,
46 .cb_strategy = nodev,
47 .cb_print = nodev,
48 .cb_dump = nodev,
49 .cb_read = nodev,
50 .cb_write = nodev,
51 .cb_ioctl = nodev,
52 .cb_devmap = nodev,
53 .cb_mmap = nodev,
54 .cb_segmap = nodev,
55 .cb_chpoll = nochpoll,
56 .cb_prop_op = ddi_prop_op,
57 .cb_flag = D_MP,
58 .cb_rev = CB_REV,
59 .cb_aread = nodev,
60 .cb_awrite = nodev
61 };
62
63 static int cxgbe_devo_attach(dev_info_t *, ddi_attach_cmd_t);
64 static int cxgbe_devo_detach(dev_info_t *, ddi_detach_cmd_t);
65 struct dev_ops cxgbe_dev_ops = {
66 .devo_rev = DEVO_REV,
67 .devo_identify = nulldev,
68 .devo_probe = nulldev,
69 .devo_attach = cxgbe_devo_attach,
70 .devo_detach = cxgbe_devo_detach,
71 .devo_reset = nodev,
72 .devo_cb_ops = &cxgbe_cb_ops,
73 };
74
75 static struct modldrv modldrv = {
76 .drv_modops = &mod_driverops,
77 .drv_linkinfo = "Chelsio T4/T5 NIC " DRV_VERSION,
78 .drv_dev_ops = &cxgbe_dev_ops
79 };
80
81 static struct modlinkage modlinkage = {
82 .ml_rev = MODREV_1,
83 .ml_linkage = {&modldrv, NULL},
84 };
85
86 int
_init(void)87 _init(void)
88 {
89 int rc;
90
91 mac_init_ops(&cxgbe_dev_ops, T4_PORT_NAME);
92 rc = mod_install(&modlinkage);
93 if (rc != 0)
94 mac_fini_ops(&cxgbe_dev_ops);
95
96 return (rc);
97 }
98
99 int
_fini(void)100 _fini(void)
101 {
102 int rc;
103
104 rc = mod_remove(&modlinkage);
105 if (rc != 0)
106 return (rc);
107
108 mac_fini_ops(&cxgbe_dev_ops);
109 return (0);
110 }
111
112 int
_info(struct modinfo * mi)113 _info(struct modinfo *mi)
114 {
115 return (mod_info(&modlinkage, mi));
116 }
117
118 static int
cxgbe_devo_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)119 cxgbe_devo_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
120 {
121 struct port_info_stub *pi;
122 mac_register_t *mac;
123 mac_handle_t mh;
124 int rc;
125
126 if (cmd != DDI_ATTACH)
127 return (DDI_FAILURE);
128
129 pi = ddi_get_parent_data(dip);
130 if (pi == NULL)
131 return (DDI_FAILURE);
132
133 mac = mac_alloc(MAC_VERSION);
134 if (mac == NULL) {
135 cmn_err(CE_WARN, "%s%d: failed to allocate version %d mac.",
136 ddi_driver_name(pi->dip), ddi_get_instance(pi->dip),
137 MAC_VERSION);
138 return (DDI_FAILURE);
139 }
140
141 mac->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
142 mac->m_driver = pi;
143 mac->m_dip = dip;
144 mac->m_src_addr = pi->hw_addr;
145 mac->m_callbacks = pi->mc;
146 mac->m_max_sdu = pi->mtu;
147 mac->m_priv_props = pi->props;
148 mac->m_margin = 22; /* TODO: mac_register(9s) and onnv code disagree */
149
150 if (!mac->m_callbacks->mc_unicst) {
151 cmn_err(CE_NOTE, "%s%d: Multiple Rings Enabled",
152 ddi_driver_name(pi->dip), ddi_get_instance(pi->dip));
153 mac->m_v12n = MAC_VIRT_LEVEL1;
154 } else
155 cmn_err(CE_NOTE, "%s%d: Multiple Rings Disbled",
156 ddi_driver_name(pi->dip), ddi_get_instance(pi->dip));
157 rc = mac_register(mac, &mh);
158 mac_free(mac);
159 if (rc != 0) {
160 cmn_err(CE_WARN, "%s%d: failed to register version %d mac.",
161 ddi_driver_name(pi->dip), ddi_get_instance(pi->dip),
162 MAC_VERSION);
163 return (DDI_FAILURE);
164 }
165 pi->mh = mh;
166
167 /*
168 * Link state from this point onwards to the time interface is plumbed,
169 * should be set to LINK_STATE_UNKNOWN. The mac should be updated about
170 * the link state as either LINK_STATE_UP or LINK_STATE_DOWN based on
171 * the actual link state detection after interface plumb.
172 */
173 mac_link_update(mh, LINK_STATE_UNKNOWN);
174
175 ddi_report_dev(dip);
176
177 return (DDI_SUCCESS);
178 }
179
180 static int
cxgbe_devo_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)181 cxgbe_devo_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
182 {
183 struct port_info_stub *pi;
184 mac_handle_t mh;
185
186 if (cmd != DDI_DETACH)
187 return (DDI_FAILURE);
188
189 pi = ddi_get_parent_data(dip);
190 if (pi == NULL)
191 return (DDI_FAILURE);
192
193 mh = pi->mh;
194 pi->mh = NULL;
195
196 return (mac_unregister(mh));
197 }
198