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/*
23 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27
28#include <sys/types.h>
29#include <sys/conf.h>
30#include <sys/ddi.h>
31#include <sys/sunddi.h>
32#include <sys/ddi_impldefs.h>
33#include <sys/obpdefs.h>
34#include <sys/cmn_err.h>
35#include <sys/errno.h>
36#include <sys/kmem.h>
37#include <sys/debug.h>
38#include <sys/sysmacros.h>
39#include <sys/autoconf.h>
40#include <sys/modctl.h>
41
42#include <sys/fhc.h>
43#include <sys/sram.h>
44#include <sys/promif.h>
45
46/* Useful debugging Stuff */
47#include <sys/nexusdebug.h>
48
49/*
50 * Function protoypes
51 */
52
53static int sram_attach(dev_info_t *, ddi_attach_cmd_t);
54
55static int sram_detach(dev_info_t *, ddi_detach_cmd_t);
56
57static void sram_add_kstats(struct sram_soft_state *);
58
59/*
60 * Configuration data structures
61 */
62static struct cb_ops sram_cb_ops = {
63	nulldev,			/* open */
64	nulldev,			/* close */
65	nulldev,			/* strategy */
66	nulldev,			/* print */
67	nodev,				/* dump */
68	nulldev,			/* read */
69	nulldev,			/* write */
70	nulldev,			/* ioctl */
71	nodev,				/* devmap */
72	nodev,				/* mmap */
73	nodev,				/* segmap */
74	nochpoll,			/* poll */
75	ddi_prop_op,			/* cb_prop_op */
76	0,				/* streamtab */
77	D_MP | D_NEW | D_HOTPLUG,	/* Driver compatibility flag */
78	CB_REV,				/* rev */
79	nodev,				/* cb_aread */
80	nodev				/* cb_awrite */
81};
82
83static struct dev_ops sram_ops = {
84	DEVO_REV,			/* rev */
85	0,				/* refcnt  */
86	ddi_no_info,			/* getinfo */
87	nulldev,			/* identify */
88	nulldev,			/* probe */
89	sram_attach,			/* attach */
90	sram_detach,			/* detach */
91	nulldev,			/* reset */
92	&sram_cb_ops,			/* cb_ops */
93	(struct bus_ops *)0,		/* bus_ops */
94	nulldev,			/* power */
95	ddi_quiesce_not_needed,			/* quiesce */
96};
97
98
99/*
100 * Driver globals
101 */
102void *sramp;			/* sram soft state hook */
103static struct kstat *resetinfo_ksp = NULL;
104static int reset_info_created = 0;
105
106extern struct mod_ops mod_driverops;
107
108static struct modldrv modldrv = {
109	&mod_driverops,		/* Type of module.  This one is a driver */
110	"Sram Leaf",		/* name of module */
111	&sram_ops,		/* driver ops */
112};
113
114static struct modlinkage modlinkage = {
115	MODREV_1,
116	(void *)&modldrv,
117	NULL
118};
119
120#ifndef	lint
121char _depends_on[] = "drv/fhc";
122#endif	/* lint */
123
124/*
125 * These are the module initialization routines.
126 */
127
128int
129_init(void)
130{
131	int error;
132
133	if ((error = ddi_soft_state_init(&sramp,
134	    sizeof (struct sram_soft_state), 1)) == 0 &&
135	    (error = mod_install(&modlinkage)) != 0)
136		ddi_soft_state_fini(&sramp);
137	return (error);
138}
139
140int
141_fini(void)
142{
143	int error;
144
145	if ((error = mod_remove(&modlinkage)) == 0)
146		ddi_soft_state_fini(&sramp);
147	return (error);
148}
149
150int
151_info(struct modinfo *modinfop)
152{
153	return (mod_info(&modlinkage, modinfop));
154}
155
156static int
157sram_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
158{
159	int instance;
160	struct sram_soft_state *softsp;
161
162	switch (cmd) {
163	case DDI_ATTACH:
164		break;
165
166	case DDI_RESUME:
167		return (DDI_SUCCESS);
168
169	default:
170		return (DDI_FAILURE);
171	}
172
173	instance = ddi_get_instance(devi);
174
175	if (ddi_soft_state_zalloc(sramp, instance) != DDI_SUCCESS)
176		return (DDI_FAILURE);
177
178	softsp = ddi_get_soft_state(sramp, instance);
179
180	/* Set the dip in the soft state */
181	softsp->dip = devi;
182
183	/* get the board number from this devices parent. */
184	softsp->pdip = ddi_get_parent(softsp->dip);
185	if ((softsp->board = (int)ddi_getprop(DDI_DEV_T_ANY, softsp->pdip,
186	    DDI_PROP_DONTPASS, OBP_BOARDNUM, -1)) == -1) {
187		cmn_err(CE_WARN, "sram%d: unable to retrieve %s property",
188		    instance, OBP_BOARDNUM);
189		goto bad;
190	}
191
192	DPRINTF(SRAM_ATTACH_DEBUG, ("sram%d: devi= 0x%p\n, "
193	    " softsp=0x%p\n", instance, (void *)devi, (void *)softsp));
194
195	/* map in the registers for this device. */
196	if (ddi_map_regs(softsp->dip, 0,
197	    (caddr_t *)&softsp->sram_base, 0, 0)) {
198		cmn_err(CE_WARN, "sram%d: unable to map registers",
199		    instance);
200		goto bad;
201	}
202
203	/* nothing to suspend/resume here */
204	(void) ddi_prop_update_string(DDI_DEV_T_NONE, devi,
205	    "pm-hardware-state", "no-suspend-resume");
206
207	/* create the kstats for this device. */
208	sram_add_kstats(softsp);
209
210	ddi_report_dev(devi);
211
212	return (DDI_SUCCESS);
213
214bad:
215	ddi_soft_state_free(sramp, instance);
216	return (DDI_FAILURE);
217}
218
219/* ARGSUSED */
220static int
221sram_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
222{
223	int instance;
224	struct sram_soft_state *softsp;
225
226	/* get the instance of this devi */
227	instance = ddi_get_instance(devi);
228
229	/* get the soft state pointer for this device node */
230	softsp = ddi_get_soft_state(sramp, instance);
231
232	switch (cmd) {
233	case DDI_SUSPEND:
234		return (DDI_SUCCESS);
235
236	case DDI_DETACH:
237		(void) fhc_bdlist_lock(softsp->board);
238		if (fhc_bd_detachable(softsp->board))
239			break;
240		else
241			fhc_bdlist_unlock();
242		/* FALLTHROUGH */
243
244	default:
245		return (DDI_FAILURE);
246	}
247
248	fhc_bdlist_unlock();
249
250	/*
251	 * We do not remove the kstat here. There is only one instance for
252	 * the whole machine, and it must remain in existence while the
253	 * system is running.
254	 */
255
256
257	/* unmap the registers */
258	ddi_unmap_regs(softsp->dip, 0,
259	    (caddr_t *)&softsp->sram_base, 0, 0);
260
261	/* free the soft state structure */
262	ddi_soft_state_free(sramp, instance);
263
264	ddi_prop_remove_all(devi);
265
266	return (DDI_SUCCESS);
267}
268
269/*
270 * The Reset-info structure passed up by POST has it's own kstat.
271 * It only needs to get created once. So the first sram instance
272 * that gets created will check for the OBP property 'reset-info'
273 * in the root node of the OBP device tree. If this property exists,
274 * then the reset-info kstat will get created. Otherwise it will
275 * not get created. This will inform users whether or not a fatal
276 * hardware reset has recently occurred.
277 */
278static void
279sram_add_kstats(struct sram_soft_state *softsp)
280{
281	int reset_size;		/* size of data collected by POST */
282	char *ksptr;		/* memory pointer for byte copy */
283	char *srptr;		/* pointer to sram for byte copy */
284	int i;
285	union  {
286		char size[4];	/* copy in word byte-by-byte */
287		uint_t len;
288	} rst_size;
289
290	/*
291	 * only one reset_info kstat per system, so don't create it if
292	 * it exists already.
293	 */
294	if (reset_info_created) {
295		return;
296	}
297
298	/* mark that this code has been run. */
299	reset_info_created = 1;
300
301	/* does the root node have a 'fatal-reset-info' property? */
302	if (prom_getprop(prom_rootnode(), "fatal-reset-info",
303	    (caddr_t)&softsp->offset) == -1) {
304		return;
305	}
306
307	/* XXX - workaround for OBP bug */
308	softsp->reset_info = softsp->sram_base + softsp->offset;
309
310	/*
311	 * First read size. In case FW has not word aligned structure,
312	 * copy the unsigned int into a 4 byte union, then read it out as
313	 * an inteeger.
314	 */
315	for (i = 0, srptr = softsp->reset_info; i < 4; i++) {
316		rst_size.size[i] = *srptr++;
317	}
318	reset_size = rst_size.len;
319
320	/*
321	 * If the reset size is zero, then POST did not
322	 * record any info.
323	 */
324	if ((uint_t)reset_size == 0) {
325		return;
326	}
327
328	/* Check for illegal size values. */
329	if ((uint_t)reset_size > MX_RSTINFO_SZ) {
330		cmn_err(CE_NOTE, "sram%d: illegal "
331		    "reset_size: 0x%x",
332		    ddi_get_instance(softsp->dip),
333		    reset_size);
334		return;
335	}
336
337	/* create the reset-info kstat */
338	resetinfo_ksp = kstat_create("unix", 0,
339	    RESETINFO_KSTAT_NAME, "misc", KSTAT_TYPE_RAW,
340	    reset_size, KSTAT_FLAG_PERSISTENT);
341
342	if (resetinfo_ksp == NULL) {
343		cmn_err(CE_WARN, "sram%d: kstat_create failed",
344		    ddi_get_instance(softsp->dip));
345		return;
346	}
347
348	/*
349	 * now copy the data into kstat. Don't use block
350	 * copy, the local space sram does not support this.
351	 */
352	srptr = softsp->reset_info;
353
354	ksptr = (char *)resetinfo_ksp->ks_data;
355
356	for (i = 0; i < reset_size; i++) {
357		*ksptr++ = *srptr++;
358	}
359
360	kstat_install(resetinfo_ksp);
361}
362