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 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
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
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
113 _info(struct modinfo *mi)
114 {
115 	return (mod_info(&modlinkage, mi));
116 }
117 
118 static int
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 	rc = mac_register(mac, &mh);
151 	mac_free(mac);
152 	if (rc != 0) {
153 		cmn_err(CE_WARN, "%s%d: failed to register version %d mac.",
154 		    ddi_driver_name(pi->dip), ddi_get_instance(pi->dip),
155 		    MAC_VERSION);
156 		return (DDI_FAILURE);
157 	}
158 	pi->mh = mh;
159 
160 	/*
161 	 * Link state from this point onwards to the time interface is plumbed,
162 	 * should be set to LINK_STATE_UNKNOWN. The mac should be updated about
163 	 * the link state as either LINK_STATE_UP or LINK_STATE_DOWN based on
164 	 * the actual link state detection after interface plumb.
165 	 */
166 	mac_link_update(mh, LINK_STATE_UNKNOWN);
167 
168 	ddi_report_dev(dip);
169 
170 	return (DDI_SUCCESS);
171 }
172 
173 static int
174 cxgbe_devo_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
175 {
176 	struct port_info_stub *pi;
177 	mac_handle_t mh;
178 
179 	if (cmd != DDI_DETACH)
180 		return (DDI_FAILURE);
181 
182 	pi = ddi_get_parent_data(dip);
183 	if (pi == NULL)
184 		return (DDI_FAILURE);
185 
186 	mh = pi->mh;
187 	pi->mh = NULL;
188 
189 	return (mac_unregister(mh));
190 }
191