1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
23 */
24/*
25 * This file contains SM-HBA support for PMC-S driver
26 */
27
28#include <sys/scsi/adapters/pmcs/pmcs.h>
29
30
31void
32pmcs_smhba_add_hba_prop(pmcs_hw_t *pwp, data_type_t dt,
33    char *prop_name, void *prop_val)
34{
35	ASSERT(pwp != NULL);
36
37	switch (dt) {
38	case DATA_TYPE_INT32:
39		if (ddi_prop_update_int(DDI_DEV_T_NONE, pwp->dip,
40		    prop_name, *(int *)prop_val)) {
41			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
42			    "%s: %s prop update failed", __func__, prop_name);
43		}
44		break;
45	case DATA_TYPE_STRING:
46		if (ddi_prop_update_string(DDI_DEV_T_NONE, pwp->dip,
47		    prop_name, (char *)prop_val)) {
48			pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
49			    "%s: %s prop update failed", __func__, prop_name);
50		}
51		break;
52	default:
53		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "%s: "
54		    "Unhandled datatype(%d) for (%s). Skipping prop update.",
55		    __func__, dt, prop_name);
56	}
57}
58
59
60/*
61 * Called with iport lock held.
62 */
63void
64pmcs_smhba_add_iport_prop(pmcs_iport_t *iport, data_type_t dt,
65    char *prop_name, void *prop_val)
66{
67	ASSERT(iport != NULL);
68	ASSERT(mutex_owned(&iport->lock));
69
70	switch (dt) {
71	case DATA_TYPE_INT32:
72		if (ddi_prop_update_int(DDI_DEV_T_NONE, iport->dip,
73		    prop_name, *(int *)prop_val)) {
74			pmcs_prt(iport->pwp, PMCS_PRT_DEBUG, NULL, NULL,
75			    "%s: %s prop update failed", __func__, prop_name);
76		}
77		break;
78	case DATA_TYPE_STRING:
79		if (ddi_prop_update_string(DDI_DEV_T_NONE, iport->dip,
80		    prop_name, (char *)prop_val)) {
81			pmcs_prt(iport->pwp, PMCS_PRT_DEBUG, NULL, NULL,
82			    "%s: %s prop update failed", __func__, prop_name);
83		}
84		break;
85	default:
86		pmcs_prt(iport->pwp, PMCS_PRT_DEBUG, NULL, NULL, "%s: "
87		    "Unhandled datatype(%d) for(%s). Skipping prop update.",
88		    __func__, dt, prop_name);
89	}
90
91	pmcs_smhba_set_phy_props(iport);
92}
93
94
95void
96pmcs_smhba_add_tgt_prop(pmcs_xscsi_t *tgt, data_type_t dt,
97    char *prop_name, void *prop_val)
98{
99	ASSERT(tgt != NULL);
100
101	switch (dt) {
102	case DATA_TYPE_INT32:
103		if (ddi_prop_update_int(DDI_DEV_T_NONE, tgt->dip,
104		    prop_name, *(int *)prop_val)) {
105			pmcs_prt(tgt->pwp, PMCS_PRT_DEBUG, NULL, NULL,
106			    "%s: %s prop update failed", __func__, prop_name);
107		}
108		break;
109	case DATA_TYPE_STRING:
110		if (ddi_prop_update_string(DDI_DEV_T_NONE, tgt->dip,
111		    prop_name, (char *)prop_val)) {
112			pmcs_prt(tgt->pwp, PMCS_PRT_DEBUG, NULL, NULL,
113			    "%s: %s prop update failed", __func__, prop_name);
114		}
115		break;
116	default:
117		pmcs_prt(tgt->pwp, PMCS_PRT_DEBUG, NULL, NULL, "%s: "
118		    "Unhandled datatype(%d) for (%s). Skipping prop update.",
119		    __func__, dt, prop_name);
120	}
121}
122
123/* ARGSUSED */
124void
125pmcs_smhba_set_scsi_device_props(pmcs_hw_t *pwp, pmcs_phy_t *pptr,
126    struct scsi_device *sd)
127{
128	char		*paddr, *addr;
129	int		ua_form = 1;
130	uint64_t	wwn, pwwn;
131	pmcs_phy_t	*pphy;
132
133	pphy = pptr->parent;
134
135	if (pphy != NULL) {
136		paddr = kmem_zalloc(PMCS_MAX_UA_SIZE, KM_SLEEP);
137		pwwn = pmcs_barray2wwn(pphy->sas_address);
138		(void) scsi_wwn_to_wwnstr(pwwn, ua_form, paddr);
139
140		addr = kmem_zalloc(PMCS_MAX_UA_SIZE, KM_SLEEP);
141		wwn = pmcs_barray2wwn(pptr->sas_address);
142		(void) scsi_wwn_to_wwnstr(wwn, ua_form, addr);
143
144		if ((pptr->dtype == SATA) || pptr->virtual) {
145			(void) scsi_device_prop_update_string(sd,
146			    SCSI_DEVICE_PROP_PATH,
147			    SCSI_ADDR_PROP_BRIDGE_PORT, addr);
148		}
149		if (pphy->dtype == EXPANDER) {
150			(void) scsi_device_prop_update_string(sd,
151			    SCSI_DEVICE_PROP_PATH,
152			    SCSI_ADDR_PROP_ATTACHED_PORT, paddr);
153		}
154		kmem_free(addr, PMCS_MAX_UA_SIZE);
155		kmem_free(paddr, PMCS_MAX_UA_SIZE);
156	}
157
158	if (pptr->dtype != EXPANDER) {
159		(void) scsi_device_prop_update_int(sd,
160		    SCSI_DEVICE_PROP_PATH, SCSI_ADDR_PROP_TARGET_PORT_DEPTH,
161		    pptr->level);
162	}
163}
164
165void
166pmcs_smhba_set_phy_props(pmcs_iport_t *iport)
167{
168	int		i;
169	size_t		packed_size;
170	char		*packed_data;
171	pmcs_hw_t	*pwp = iport->pwp;
172	pmcs_phy_t	*phy_ptr;
173	nvlist_t	**phy_props;
174	nvlist_t	*nvl;
175
176	ASSERT(mutex_owned(&iport->lock));
177	if (iport->nphy == 0) {
178		return;
179	}
180
181	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) {
182		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
183		    "%s: nvlist_alloc() failed", __func__);
184	}
185
186	phy_props = kmem_zalloc(sizeof (nvlist_t *) * iport->nphy, KM_SLEEP);
187
188	for (phy_ptr = list_head(&iport->phys), i = 0;
189	    phy_ptr != NULL;
190	    phy_ptr = list_next(&iport->phys, phy_ptr), i++) {
191		pmcs_lock_phy(phy_ptr);
192
193		(void) nvlist_alloc(&phy_props[i], NV_UNIQUE_NAME, 0);
194
195		(void) nvlist_add_uint8(phy_props[i], SAS_PHY_ID,
196		    phy_ptr->phynum);
197		(void) nvlist_add_int8(phy_props[i], SAS_NEG_LINK_RATE,
198		    phy_ptr->link_rate);
199		(void) nvlist_add_int8(phy_props[i], SAS_PROG_MIN_LINK_RATE,
200		    phy_ptr->state.prog_min_rate);
201		(void) nvlist_add_int8(phy_props[i], SAS_HW_MIN_LINK_RATE,
202		    phy_ptr->state.hw_min_rate);
203		(void) nvlist_add_int8(phy_props[i], SAS_PROG_MAX_LINK_RATE,
204		    phy_ptr->state.prog_max_rate);
205		(void) nvlist_add_int8(phy_props[i], SAS_HW_MAX_LINK_RATE,
206		    phy_ptr->state.hw_max_rate);
207
208		pmcs_unlock_phy(phy_ptr);
209	}
210
211	(void) nvlist_add_nvlist_array(nvl, SAS_PHY_INFO_NVL, phy_props,
212	    iport->nphy);
213
214	(void) nvlist_size(nvl, &packed_size, NV_ENCODE_NATIVE);
215	packed_data = kmem_zalloc(packed_size, KM_SLEEP);
216	(void) nvlist_pack(nvl, &packed_data, &packed_size,
217	    NV_ENCODE_NATIVE, 0);
218
219	(void) ddi_prop_update_byte_array(DDI_DEV_T_NONE, iport->dip,
220	    SAS_PHY_INFO, (uchar_t *)packed_data, packed_size);
221
222	for (i = 0; i < iport->nphy && phy_props[i] != NULL; i++) {
223		nvlist_free(phy_props[i]);
224	}
225	nvlist_free(nvl);
226	kmem_free(phy_props, sizeof (nvlist_t *) * iport->nphy);
227	kmem_free(packed_data, packed_size);
228}
229
230/*
231 * Called with PHY lock held on phyp
232 */
233void
234pmcs_smhba_log_sysevent(pmcs_hw_t *pwp, char *subclass, char *etype,
235    pmcs_phy_t *phyp)
236{
237	nvlist_t	*attr_list;
238	char		*pname;
239	char		sas_addr[PMCS_MAX_UA_SIZE];
240	uint8_t		phynum = 0;
241	uint8_t		lrate = 0;
242	uint64_t	wwn;
243	int		ua_form = 0;
244
245	if (pwp->dip == NULL)
246		return;
247	if (phyp == NULL)
248		return;
249
250	pname = kmem_zalloc(MAXPATHLEN, KM_NOSLEEP);
251	if (pname == NULL)
252		return;
253
254	if ((strcmp(subclass, ESC_SAS_PHY_EVENT) == 0) ||
255	    (strcmp(subclass, ESC_SAS_HBA_PORT_BROADCAST) == 0)) {
256		ASSERT(phyp != NULL);
257		(void) strncpy(pname, phyp->path, strlen(phyp->path));
258		phynum = phyp->phynum;
259		wwn = pmcs_barray2wwn(phyp->sas_address);
260		(void) scsi_wwn_to_wwnstr(wwn, ua_form, sas_addr);
261		if (strcmp(etype, SAS_PHY_ONLINE) == 0) {
262			lrate = phyp->link_rate;
263		}
264	}
265	if (strcmp(subclass, ESC_SAS_HBA_PORT_BROADCAST) == 0) {
266		(void) ddi_pathname(pwp->dip, pname);
267	}
268
269	if (nvlist_alloc(&attr_list, NV_UNIQUE_NAME_TYPE, 0) != 0) {
270		pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
271		    "%s: Failed to post sysevent", __func__);
272		kmem_free(pname, MAXPATHLEN);
273		return;
274	}
275
276	if (nvlist_add_int32(attr_list, SAS_DRV_INST,
277	    ddi_get_instance(pwp->dip)) != 0)
278		goto fail;
279
280	if (nvlist_add_string(attr_list, SAS_PORT_ADDR, sas_addr) != 0)
281		goto fail;
282
283	if (nvlist_add_string(attr_list, SAS_DEVFS_PATH, pname) != 0)
284		goto fail;
285
286	if (nvlist_add_uint8(attr_list, SAS_PHY_ID, phynum) != 0)
287		goto fail;
288
289	if (strcmp(etype, SAS_PHY_ONLINE) == 0) {
290		if (nvlist_add_uint8(attr_list, SAS_LINK_RATE, lrate) != 0)
291			goto fail;
292	}
293
294	if (nvlist_add_string(attr_list, SAS_EVENT_TYPE, etype) != 0)
295		goto fail;
296
297	(void) ddi_log_sysevent(pwp->dip, DDI_VENDOR_SUNW, EC_HBA, subclass,
298	    attr_list, NULL, DDI_NOSLEEP);
299
300fail:
301	kmem_free(pname, MAXPATHLEN);
302	nvlist_free(attr_list);
303}
304