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
39struct port_info_stub {
40	PORT_INFO_HDR;
41};
42
43static 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
63static int cxgbe_devo_attach(dev_info_t *, ddi_attach_cmd_t);
64static int cxgbe_devo_detach(dev_info_t *, ddi_detach_cmd_t);
65struct 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
75static 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
81static struct modlinkage modlinkage = {
82	.ml_rev =		MODREV_1,
83	.ml_linkage =		{&modldrv, NULL},
84};
85
86int
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
99int
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
112int
113_info(struct modinfo *mi)
114{
115	return (mod_info(&modlinkage, mi));
116}
117
118static int
119cxgbe_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
180static int
181cxgbe_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