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, Intel Corporation.
23  * All rights reserved.
24  */
25 
26 #include <sys/types.h>
27 #include <sys/atomic.h>
28 #include <sys/sunddi.h>
29 #include <sys/sunndi.h>
30 #include <sys/acpi/acpi.h>
31 #include <sys/acpica.h>
32 #include <sys/acpidev.h>
33 #include <sys/acpidev_rsc.h>
34 #include <sys/acpidev_dr.h>
35 #include <sys/acpidev_impl.h>
36 
37 static ACPI_STATUS acpidev_memory_probe(acpidev_walk_info_t *infop);
38 static acpidev_filter_result_t acpidev_memory_filter(
39     acpidev_walk_info_t *infop, char *devname, int maxlen);
40 static ACPI_STATUS acpidev_memory_init(acpidev_walk_info_t *infop);
41 
42 /*
43  * Default class driver for ACPI memory objects.
44  */
45 acpidev_class_t acpidev_class_memory = {
46 	0,				/* adc_refcnt */
47 	ACPIDEV_CLASS_REV1,		/* adc_version */
48 	ACPIDEV_CLASS_ID_MEMORY,	/* adc_class_id */
49 	"ACPI memory",			/* adc_class_name */
50 	ACPIDEV_TYPE_MEMORY,		/* adc_dev_type */
51 	NULL,				/* adc_private */
52 	NULL,				/* adc_pre_probe */
53 	NULL,				/* adc_post_probe */
54 	acpidev_memory_probe,		/* adc_probe */
55 	acpidev_memory_filter,		/* adc_filter */
56 	acpidev_memory_init,		/* adc_init */
57 	NULL,				/* adc_fini */
58 };
59 
60 /*
61  * List of class drivers which will be called in order when handling
62  * children of ACPI memory objects.
63  */
64 acpidev_class_list_t *acpidev_class_list_memory = NULL;
65 
66 static char *acpidev_memory_device_ids[] = {
67 	ACPIDEV_HID_MEMORY,
68 };
69 
70 static char *acpidev_memory_uid_formats[] = {
71 	"MEM%x-%x",
72 };
73 
74 /* Filter rule table for memory objects. */
75 static acpidev_filter_rule_t acpidev_memory_filters[] = {
76 	{	/* Ignore all memory objects under the ACPI root object */
77 		NULL,
78 		0,
79 		ACPIDEV_FILTER_SKIP,
80 		NULL,
81 		1,
82 		1,
83 		NULL,
84 		NULL,
85 	},
86 	{	/* Create node and scan child for all other memory objects */
87 		NULL,
88 		0,
89 		ACPIDEV_FILTER_DEFAULT,
90 		&acpidev_class_list_device,
91 		2,
92 		INT_MAX,
93 		NULL,
94 		ACPIDEV_NODE_NAME_MEMORY,
95 	}
96 };
97 
98 static ACPI_STATUS
acpidev_memory_probe(acpidev_walk_info_t * infop)99 acpidev_memory_probe(acpidev_walk_info_t *infop)
100 {
101 	ACPI_STATUS rc = AE_OK;
102 	int flags;
103 
104 	ASSERT(infop != NULL);
105 	ASSERT(infop->awi_hdl != NULL);
106 	ASSERT(infop->awi_info != NULL);
107 	if (infop->awi_info->Type != ACPI_TYPE_DEVICE ||
108 	    acpidev_match_device_id(infop->awi_info,
109 	    ACPIDEV_ARRAY_PARAM(acpidev_memory_device_ids)) == 0) {
110 		return (AE_OK);
111 	}
112 
113 	flags = ACPIDEV_PROCESS_FLAG_SCAN;
114 	switch (infop->awi_op_type) {
115 	case ACPIDEV_OP_BOOT_PROBE:
116 		if (acpica_get_devcfg_feature(ACPI_DEVCFG_MEMORY)) {
117 			flags |= ACPIDEV_PROCESS_FLAG_CREATE;
118 			acpidev_dr_check(infop);
119 		}
120 		break;
121 
122 	case ACPIDEV_OP_BOOT_REPROBE:
123 		break;
124 
125 	case ACPIDEV_OP_HOTPLUG_PROBE:
126 		if (acpica_get_devcfg_feature(ACPI_DEVCFG_MEMORY)) {
127 			flags |= ACPIDEV_PROCESS_FLAG_CREATE |
128 			    ACPIDEV_PROCESS_FLAG_SYNCSTATUS |
129 			    ACPIDEV_PROCESS_FLAG_HOLDBRANCH;
130 		}
131 		break;
132 
133 	default:
134 		ACPIDEV_DEBUG(CE_WARN, "!acpidev: unknown operation type %u "
135 		    "in acpidev_memory_probe.", infop->awi_op_type);
136 		rc = AE_BAD_PARAMETER;
137 		break;
138 	}
139 
140 	if (rc == AE_OK) {
141 		rc = acpidev_process_object(infop, flags);
142 	}
143 	if (ACPI_FAILURE(rc) && rc != AE_NOT_EXIST && rc != AE_ALREADY_EXISTS) {
144 		cmn_err(CE_WARN,
145 		    "!acpidev: failed to process memory object %s.",
146 		    infop->awi_name);
147 	} else {
148 		rc = AE_OK;
149 	}
150 
151 	return (rc);
152 }
153 
154 static acpidev_filter_result_t
acpidev_memory_filter(acpidev_walk_info_t * infop,char * devname,int maxlen)155 acpidev_memory_filter(acpidev_walk_info_t *infop, char *devname, int maxlen)
156 {
157 	acpidev_filter_result_t res;
158 
159 	ASSERT(infop != NULL);
160 	if (infop->awi_op_type == ACPIDEV_OP_BOOT_PROBE ||
161 	    infop->awi_op_type == ACPIDEV_OP_BOOT_REPROBE ||
162 	    infop->awi_op_type == ACPIDEV_OP_HOTPLUG_PROBE) {
163 		res = acpidev_filter_device(infop, infop->awi_hdl,
164 		    ACPIDEV_ARRAY_PARAM(acpidev_memory_filters),
165 		    devname, maxlen);
166 	} else {
167 		res = ACPIDEV_FILTER_FAILED;
168 	}
169 
170 	return (res);
171 }
172 
173 static ACPI_STATUS
acpidev_memory_init(acpidev_walk_info_t * infop)174 acpidev_memory_init(acpidev_walk_info_t *infop)
175 {
176 	char *compatible[] = {
177 		ACPIDEV_TYPE_MEMORY,
178 		"mem"
179 	};
180 
181 	ASSERT(infop != NULL);
182 	ASSERT(infop->awi_hdl != NULL);
183 	ASSERT(infop->awi_dip != NULL);
184 	if (ACPI_FAILURE(acpidev_resource_process(infop, B_TRUE))) {
185 		cmn_err(CE_WARN, "!acpidev: failed to process resources of "
186 		    "memory device %s.", infop->awi_name);
187 		return (AE_ERROR);
188 	}
189 
190 	if (ACPI_FAILURE(acpidev_set_compatible(infop,
191 	    ACPIDEV_ARRAY_PARAM(compatible)))) {
192 		return (AE_ERROR);
193 	}
194 
195 	if (ACPI_FAILURE(acpidev_set_unitaddr(infop,
196 	    ACPIDEV_ARRAY_PARAM(acpidev_memory_uid_formats), NULL))) {
197 		return (AE_ERROR);
198 	}
199 
200 	return (AE_OK);
201 }
202