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 
31 void
pmcs_smhba_add_hba_prop(pmcs_hw_t * pwp,data_type_t dt,char * prop_name,void * prop_val)32 pmcs_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  */
63 void
pmcs_smhba_add_iport_prop(pmcs_iport_t * iport,data_type_t dt,char * prop_name,void * prop_val)64 pmcs_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 
95 void
pmcs_smhba_add_tgt_prop(pmcs_xscsi_t * tgt,data_type_t dt,char * prop_name,void * prop_val)96 pmcs_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 */
124 void
pmcs_smhba_set_scsi_device_props(pmcs_hw_t * pwp,pmcs_phy_t * pptr,struct scsi_device * sd)125 pmcs_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 
165 void
pmcs_smhba_set_phy_props(pmcs_iport_t * iport)166 pmcs_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  */
233 void
pmcs_smhba_log_sysevent(pmcs_hw_t * pwp,char * subclass,char * etype,pmcs_phy_t * phyp)234 pmcs_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 
300 fail:
301 	kmem_free(pname, MAXPATHLEN);
302 	nvlist_free(attr_list);
303 }
304