xref: /illumos-gate/usr/src/uts/common/io/gpio/gpio_sim.c (revision fd71220ba0fafcc9cf5ea0785db206f3f31336e7)
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  * Copyright 2022 Oxide Computer Company
14  */
15 
16 /*
17  * This is a simulator driver for the GPIO subsystem that exists for testing
18  * purposes.
19  */
20 
21 #include <sys/types.h>
22 #include <sys/file.h>
23 #include <sys/errno.h>
24 #include <sys/open.h>
25 #include <sys/cred.h>
26 #include <sys/ddi.h>
27 #include <sys/sunddi.h>
28 #include <sys/stat.h>
29 #include <sys/conf.h>
30 #include <sys/devops.h>
31 #include <sys/cmn_err.h>
32 #include <sys/sysmacros.h>
33 #include <sys/stdbool.h>
34 
35 #include <sys/gpio/gpio_sim.h>
36 #include <sys/gpio/kgpio_provider.h>
37 
38 typedef enum {
39 	/*
40 	 * Use specific pull strengths
41 	 */
42 	GPIO_SIM_F_USE_PS	= 1 << 0,
43 	/*
44 	 * Treat as open drain, limiting output options
45 	 */
46 	GPIO_SIM_F_OPEN_DRAIN	= 1 << 1,
47 	/*
48 	 * Indicates that this is something whose input should toggle when we
49 	 * run our periodic if the output is disabled.
50 	 */
51 	GPIO_SIM_F_PERIODIC	= 1 << 2
52 } gpio_sim_flags_t;
53 
54 typedef struct gpio_sim_info {
55 	const char *gsp_name;
56 	gpio_sim_output_t gsp_output;
57 	gpio_sim_input_t gsp_input;
58 	gpio_sim_pull_t gsp_pull;
59 	gpio_sim_voltage_t gsp_volt;
60 	gpio_sim_speed_t gsp_speed;
61 	/* This controls which set of pull up values we say are valid */
62 	gpio_sim_flags_t gsp_flags;
63 } gpio_sim_pin_t;
64 
65 /*
66  * This is the initial table of GPIOs that exist in the driver and are then
67  * copied into each instances state so that way they can modify their state.
68  */
69 static const gpio_sim_pin_t gpio_sim_pins[] = {
70 	{ "1v8", GPIO_SIM_OUTPUT_DISABLED, GPIO_SIM_INPUT_HIGH,
71 	    GPIO_SIM_PULL_UP_40K, GPIO_SIM_VOLTAGE_1P8, GPIO_SIM_SPEED_LOW,
72 	    GPIO_SIM_F_USE_PS },
73 	{ "3v3", GPIO_SIM_OUTPUT_DISABLED, GPIO_SIM_INPUT_LOW,
74 	    GPIO_SIM_PULL_DOWN, GPIO_SIM_VOLTAGE_3P3, GPIO_SIM_SPEED_LOW,
75 	    0 },
76 	{ "12V", GPIO_SIM_OUTPUT_HIGH, GPIO_SIM_INPUT_HIGH,
77 	    GPIO_SIM_PULL_DISABLED, GPIO_SIM_VOLTAGE_12P0,
78 	    GPIO_SIM_SPEED_MEDIUM, 0 },
79 	{ "54V", GPIO_SIM_OUTPUT_LOW, GPIO_SIM_INPUT_LOW,
80 	    GPIO_SIM_PULL_DOWN_23K, GPIO_SIM_VOLTAGE_54P5,
81 	    GPIO_SIM_SPEED_VERY_HIGH, GPIO_SIM_F_USE_PS },
82 	{ "periodic-500ms", GPIO_SIM_OUTPUT_DISABLED, GPIO_SIM_INPUT_LOW,
83 	    GPIO_SIM_PULL_DISABLED, GPIO_SIM_VOLTAGE_1P8, GPIO_SIM_SPEED_LOW,
84 	    GPIO_SIM_F_PERIODIC },
85 	{ "open-drain", GPIO_SIM_OUTPUT_DISABLED, GPIO_SIM_INPUT_HIGH,
86 	    GPIO_SIM_PULL_DISABLED, GPIO_SIM_VOLTAGE_1P8, GPIO_SIM_SPEED_MEDIUM,
87 	    GPIO_SIM_F_OPEN_DRAIN },
88 };
89 
90 typedef struct gpio_sim {
91 	dev_info_t *gs_dip;
92 	uint32_t gs_npins;
93 	kmutex_t gs_mutex;
94 	gpio_sim_pin_t *gs_pins;
95 	ddi_periodic_t gs_period;
96 } gpio_sim_t;
97 
98 static void *gpio_sim_state;
99 
100 /*
101  * This basically simulates an "interrupt" that has occurred and changed the
102  * state of the periodic pin. In the future, when we have the ability to tell
103  * the framework that an interrupt has occurred that should cause a poll event
104  * to happen, we should call back into it -- but we must not hold our locks
105  * across that.
106  */
107 static void
gpio_sim_periodic(void * arg)108 gpio_sim_periodic(void *arg)
109 {
110 	gpio_sim_t *gs = arg;
111 
112 	mutex_enter(&gs->gs_mutex);
113 	for (uint32_t i = 0; i < gs->gs_npins; i++) {
114 		gpio_sim_pin_t *pin = &gs->gs_pins[i];
115 		if ((pin->gsp_flags & GPIO_SIM_F_PERIODIC) == 0 ||
116 		    pin->gsp_output != GPIO_SIM_OUTPUT_DISABLED) {
117 			continue;
118 		}
119 
120 		if (pin->gsp_input == GPIO_SIM_INPUT_LOW) {
121 			pin->gsp_input = GPIO_SIM_INPUT_HIGH;
122 		} else {
123 			pin->gsp_input = GPIO_SIM_INPUT_LOW;
124 		}
125 	}
126 	mutex_exit(&gs->gs_mutex);
127 }
128 
129 static void
gpio_sim_update_input(gpio_sim_pin_t * pin)130 gpio_sim_update_input(gpio_sim_pin_t *pin)
131 {
132 	switch (pin->gsp_output) {
133 	case GPIO_SIM_OUTPUT_DISABLED:
134 		if ((pin->gsp_flags & GPIO_SIM_F_OPEN_DRAIN) != 0) {
135 			pin->gsp_input = GPIO_SIM_INPUT_HIGH;
136 			break;
137 		}
138 
139 		switch (pin->gsp_pull) {
140 		case GPIO_SIM_PULL_UP:
141 		case GPIO_SIM_PULL_UP_5K:
142 		case GPIO_SIM_PULL_UP_40K:
143 			pin->gsp_input = GPIO_SIM_INPUT_HIGH;
144 			break;
145 		case GPIO_SIM_PULL_BOTH:
146 		case GPIO_SIM_PULL_DISABLED:
147 		case GPIO_SIM_PULL_DOWN:
148 		case GPIO_SIM_PULL_DOWN_23K:
149 		default:
150 			pin->gsp_input = GPIO_SIM_INPUT_LOW;
151 			break;
152 		}
153 		break;
154 	case GPIO_SIM_OUTPUT_LOW:
155 		pin->gsp_input = GPIO_SIM_INPUT_LOW;
156 		break;
157 	case GPIO_SIM_OUTPUT_HIGH:
158 		pin->gsp_input = GPIO_SIM_INPUT_HIGH;
159 		break;
160 	default:
161 		break;
162 	}
163 }
164 
165 static int
gpio_sim_op_name2id(void * arg,const char * name,uint32_t * idp)166 gpio_sim_op_name2id(void *arg, const char *name, uint32_t *idp)
167 {
168 	gpio_sim_t *gs = arg;
169 
170 	for (uint32_t i = 0; i < gs->gs_npins; i++) {
171 		if (strcmp(name, gs->gs_pins[i].gsp_name) == 0) {
172 			*idp = i;
173 			return (0);
174 		}
175 	}
176 
177 	return (ENOENT);
178 }
179 
180 static int
gpio_sim_op_attr_get(void * arg,uint32_t gpio_id,nvlist_t * nvl)181 gpio_sim_op_attr_get(void *arg, uint32_t gpio_id, nvlist_t *nvl)
182 {
183 	gpio_sim_pin_t *pin;
184 	nvlist_t *meta;
185 
186 	gpio_sim_t *gs = arg;
187 	gpio_sim_output_t output_od[2] = { GPIO_SIM_OUTPUT_DISABLED,
188 	    GPIO_SIM_OUTPUT_LOW };
189 	gpio_sim_output_t output_pp[3] = { GPIO_SIM_OUTPUT_DISABLED,
190 	    GPIO_SIM_OUTPUT_LOW, GPIO_SIM_OUTPUT_HIGH };
191 	gpio_sim_input_t inputs[2] = { GPIO_SIM_INPUT_LOW,
192 	    GPIO_SIM_INPUT_HIGH };
193 	gpio_sim_pull_t pulls_nops[4] = { GPIO_SIM_PULL_DISABLED,
194 	    GPIO_SIM_PULL_DOWN, GPIO_SIM_PULL_UP, GPIO_SIM_PULL_BOTH };
195 	gpio_sim_pull_t pulls_ps[4] = { GPIO_SIM_PULL_DISABLED,
196 	    GPIO_SIM_PULL_DOWN_23K, GPIO_SIM_PULL_UP_5K, GPIO_SIM_PULL_UP_40K };
197 	gpio_sim_speed_t speeds[4] = { GPIO_SIM_SPEED_LOW,
198 	    GPIO_SIM_SPEED_MEDIUM, GPIO_SIM_SPEED_HIGH,
199 	    GPIO_SIM_SPEED_VERY_HIGH };
200 
201 	pin = &gs->gs_pins[gpio_id];
202 	meta = fnvlist_alloc();
203 
204 	mutex_enter(&gs->gs_mutex);
205 	kgpio_nvl_attr_fill_str(nvl, meta, KGPIO_ATTR_NAME, pin->gsp_name, 0,
206 	    NULL, KGPIO_PROT_RO);
207 	if ((pin->gsp_flags & GPIO_SIM_F_OPEN_DRAIN) != 0) {
208 		kgpio_nvl_attr_fill_u32(nvl, meta, GPIO_SIM_ATTR_OUTPUT,
209 		    pin->gsp_output, ARRAY_SIZE(output_od), output_od,
210 		    KGPIO_PROT_RW);
211 	} else {
212 		kgpio_nvl_attr_fill_u32(nvl, meta, GPIO_SIM_ATTR_OUTPUT,
213 		    pin->gsp_output, ARRAY_SIZE(output_pp), output_pp,
214 		    KGPIO_PROT_RW);
215 	}
216 	kgpio_nvl_attr_fill_u32(nvl, meta, GPIO_SIM_ATTR_INPUT,
217 	    pin->gsp_input, ARRAY_SIZE(inputs), inputs, KGPIO_PROT_RO);
218 	if ((pin->gsp_flags & GPIO_SIM_F_USE_PS) != 0) {
219 		kgpio_nvl_attr_fill_u32(nvl, meta, GPIO_SIM_ATTR_PULL,
220 		    pin->gsp_pull, ARRAY_SIZE(pulls_ps), pulls_ps,
221 		    KGPIO_PROT_RW);
222 	} else {
223 		kgpio_nvl_attr_fill_u32(nvl, meta, GPIO_SIM_ATTR_PULL,
224 		    pin->gsp_pull, ARRAY_SIZE(pulls_nops), pulls_nops,
225 		    KGPIO_PROT_RW);
226 	}
227 	kgpio_nvl_attr_fill_u32(nvl, meta, GPIO_SIM_ATTR_VOLTAGE, pin->gsp_volt,
228 	    1, &pin->gsp_volt, KGPIO_PROT_RO);
229 	kgpio_nvl_attr_fill_u32(nvl, meta, GPIO_SIM_ATTR_SPEED, pin->gsp_speed,
230 	    ARRAY_SIZE(speeds), speeds, KGPIO_PROT_RW);
231 
232 	mutex_exit(&gs->gs_mutex);
233 
234 	fnvlist_add_nvlist(nvl, KGPIO_ATTR_META, meta);
235 	fnvlist_free(meta);
236 
237 	return (0);
238 }
239 
240 static bool
gpio_sim_op_attr_set_output(gpio_sim_pin_t * pin,nvpair_t * nvpair,nvlist_t * errs)241 gpio_sim_op_attr_set_output(gpio_sim_pin_t *pin, nvpair_t *nvpair,
242     nvlist_t *errs)
243 {
244 	uint32_t val;
245 
246 	if (nvpair_value_uint32(nvpair, &val) != 0) {
247 		fnvlist_add_uint32(errs, nvpair_name(nvpair),
248 		    (uint32_t)KGPIO_ATTR_ERR_BAD_TYPE);
249 		return (false);
250 	}
251 
252 	switch (val) {
253 	case GPIO_SIM_OUTPUT_DISABLED:
254 	case GPIO_SIM_OUTPUT_LOW:
255 		pin->gsp_output = val;
256 		break;
257 	case GPIO_SIM_OUTPUT_HIGH:
258 		if ((pin->gsp_flags & GPIO_SIM_F_OPEN_DRAIN) != 0) {
259 			fnvlist_add_uint32(errs, nvpair_name(nvpair),
260 			    (uint32_t)KGPIO_ATTR_ERR_CANT_APPLY_VAL);
261 			return (false);
262 		}
263 		pin->gsp_output = val;
264 		break;
265 	default:
266 		fnvlist_add_uint32(errs, nvpair_name(nvpair),
267 		    (uint32_t)KGPIO_ATTR_ERR_UNKNOWN_VAL);
268 		return (false);
269 	}
270 
271 	return (true);
272 }
273 
274 static bool
gpio_sim_op_attr_set_pull(gpio_sim_pin_t * pin,nvpair_t * nvpair,nvlist_t * errs)275 gpio_sim_op_attr_set_pull(gpio_sim_pin_t *pin, nvpair_t *nvpair, nvlist_t *errs)
276 {
277 	uint32_t val;
278 
279 	if (nvpair_value_uint32(nvpair, &val) != 0) {
280 		fnvlist_add_uint32(errs, nvpair_name(nvpair),
281 		    (uint32_t)KGPIO_ATTR_ERR_BAD_TYPE);
282 		return (false);
283 	}
284 
285 	switch (val) {
286 	case GPIO_SIM_PULL_DISABLED:
287 		pin->gsp_pull = val;
288 		break;
289 	case GPIO_SIM_PULL_DOWN:
290 	case GPIO_SIM_PULL_UP:
291 	case GPIO_SIM_PULL_BOTH:
292 		if ((pin->gsp_flags & GPIO_SIM_F_USE_PS) != 0) {
293 			fnvlist_add_uint32(errs, nvpair_name(nvpair),
294 			    (uint32_t)KGPIO_ATTR_ERR_CANT_APPLY_VAL);
295 			return (false);
296 		}
297 		pin->gsp_pull = val;
298 		break;
299 	case GPIO_SIM_PULL_DOWN_23K:
300 	case GPIO_SIM_PULL_UP_5K:
301 	case GPIO_SIM_PULL_UP_40K:
302 		if ((pin->gsp_flags & GPIO_SIM_F_USE_PS) == 0) {
303 			fnvlist_add_uint32(errs, nvpair_name(nvpair),
304 			    (uint32_t)KGPIO_ATTR_ERR_CANT_APPLY_VAL);
305 			return (false);
306 		}
307 		pin->gsp_pull = val;
308 		break;
309 	default:
310 		fnvlist_add_uint32(errs, nvpair_name(nvpair),
311 		    (uint32_t)KGPIO_ATTR_ERR_UNKNOWN_VAL);
312 		return (false);
313 	}
314 
315 	return (true);
316 }
317 
318 static int
gpio_sim_op_attr_set(void * arg,uint32_t gpio_id,nvlist_t * nvl,nvlist_t * errs)319 gpio_sim_op_attr_set(void *arg, uint32_t gpio_id, nvlist_t *nvl, nvlist_t *errs)
320 {
321 	gpio_sim_t *gs = arg;
322 	gpio_sim_pin_t *pin, orig;
323 	bool valid = true;
324 
325 	mutex_enter(&gs->gs_mutex);
326 	pin = &gs->gs_pins[gpio_id];
327 	bcopy(pin, &orig, sizeof (pin));
328 	for (nvpair_t *nvpair = nvlist_next_nvpair(nvl, NULL); nvpair != NULL;
329 	    nvpair = nvlist_next_nvpair(nvl, nvpair)) {
330 		const char *name = nvpair_name(nvpair);
331 
332 		if (strcmp(name, KGPIO_ATTR_NAME) == 0 ||
333 		    strcmp(name, GPIO_SIM_ATTR_INPUT) == 0 ||
334 		    strcmp(name, GPIO_SIM_ATTR_VOLTAGE) == 0) {
335 			fnvlist_add_uint32(errs, nvpair_name(nvpair),
336 			    (uint32_t)KGPIO_ATTR_ERR_ATTR_RO);
337 			valid = false;
338 		} else if (strcmp(name, GPIO_SIM_ATTR_OUTPUT) == 0) {
339 			if (!gpio_sim_op_attr_set_output(pin, nvpair, errs)) {
340 				valid = false;
341 			}
342 		} else if (strcmp(name, GPIO_SIM_ATTR_PULL) == 0) {
343 			if (!gpio_sim_op_attr_set_pull(pin, nvpair, errs)) {
344 				valid = false;
345 			}
346 		} else if (strcmp(name, GPIO_SIM_ATTR_SPEED) == 0) {
347 			uint32_t val;
348 
349 			if (nvpair_value_uint32(nvpair, &val) != 0) {
350 				fnvlist_add_uint32(errs, nvpair_name(nvpair),
351 				    (uint32_t)KGPIO_ATTR_ERR_BAD_TYPE);
352 				valid = false;
353 				continue;
354 			}
355 
356 			switch (val) {
357 			case GPIO_SIM_SPEED_LOW:
358 			case GPIO_SIM_SPEED_MEDIUM:
359 			case GPIO_SIM_SPEED_HIGH:
360 			case GPIO_SIM_SPEED_VERY_HIGH:
361 				pin->gsp_speed = val;
362 				break;
363 			default:
364 				fnvlist_add_uint32(errs, nvpair_name(nvpair),
365 				    (uint32_t)KGPIO_ATTR_ERR_UNKNOWN_VAL);
366 				valid = false;
367 				break;
368 			}
369 		} else {
370 			fnvlist_add_uint32(errs, name,
371 			    (uint32_t)KGPIO_ATTR_ERR_UNKNOWN_ATTR);
372 			valid = false;
373 		}
374 	}
375 
376 	/*
377 	 * Because we're modifying things in place rather than building up state
378 	 * to change in hardware, we need to restore the original pin state if
379 	 * we found an error.
380 	 */
381 	if (!valid) {
382 		bcopy(&orig, pin, sizeof (pin));
383 	} else {
384 		gpio_sim_update_input(pin);
385 	}
386 
387 	mutex_exit(&gs->gs_mutex);
388 	return (valid ? 0 : EINVAL);
389 }
390 
391 static int
gpio_sim_op_attr_cap(void * arg,uint32_t gpio_id,dpio_caps_t * caps)392 gpio_sim_op_attr_cap(void *arg, uint32_t gpio_id, dpio_caps_t *caps)
393 {
394 	gpio_sim_t *gs = arg;
395 
396 	*caps = DPIO_C_READ | DPIO_C_WRITE;
397 	if ((gs->gs_pins[gpio_id].gsp_flags & GPIO_SIM_F_PERIODIC) != 0) {
398 		*caps |= DPIO_C_POLL;
399 	}
400 
401 	return (0);
402 }
403 
404 static int
gpio_sim_op_attr_dpio_input(void * arg,uint32_t gpio_id,dpio_input_t * input)405 gpio_sim_op_attr_dpio_input(void *arg, uint32_t gpio_id, dpio_input_t *input)
406 {
407 	gpio_sim_t *gs = arg;
408 
409 	mutex_enter(&gs->gs_mutex);
410 	if (gs->gs_pins[gpio_id].gsp_input == GPIO_SIM_INPUT_HIGH) {
411 		*input = DPIO_INPUT_HIGH;
412 	} else {
413 		*input = DPIO_INPUT_LOW;
414 	}
415 	mutex_exit(&gs->gs_mutex);
416 	return (0);
417 }
418 
419 static int
gpio_sim_op_attr_dpio_output_state(void * arg,uint32_t gpio_id,dpio_output_t * output)420 gpio_sim_op_attr_dpio_output_state(void *arg, uint32_t gpio_id,
421     dpio_output_t *output)
422 {
423 	gpio_sim_t *gs = arg;
424 
425 	mutex_enter(&gs->gs_mutex);
426 	switch (gs->gs_pins[gpio_id].gsp_output) {
427 	case GPIO_SIM_OUTPUT_DISABLED:
428 		*output = DPIO_OUTPUT_DISABLE;
429 		break;
430 	case GPIO_SIM_OUTPUT_LOW:
431 		*output = DPIO_OUTPUT_LOW;
432 		break;
433 	case GPIO_SIM_OUTPUT_HIGH:
434 		*output = DPIO_OUTPUT_HIGH;
435 		break;
436 	default:
437 		mutex_exit(&gs->gs_mutex);
438 		return (EIO);
439 	}
440 	mutex_exit(&gs->gs_mutex);
441 
442 	return (0);
443 }
444 
445 static int
gpio_sim_op_attr_dpio_output(void * arg,uint32_t gpio_id,dpio_output_t output)446 gpio_sim_op_attr_dpio_output(void *arg, uint32_t gpio_id,
447     dpio_output_t output)
448 {
449 	gpio_sim_t *gs = arg;
450 	gpio_sim_pin_t *pin;
451 
452 	pin = &gs->gs_pins[gpio_id];
453 
454 	mutex_enter(&gs->gs_mutex);
455 	switch (output) {
456 	case DPIO_OUTPUT_DISABLE:
457 		pin->gsp_output = GPIO_SIM_OUTPUT_DISABLED;
458 		break;
459 	case DPIO_OUTPUT_LOW:
460 		pin->gsp_output = GPIO_SIM_OUTPUT_LOW;
461 		break;
462 	case DPIO_OUTPUT_HIGH:
463 		if ((pin->gsp_flags & GPIO_SIM_F_OPEN_DRAIN) != 0) {
464 			mutex_exit(&gs->gs_mutex);
465 			return (ENOTSUP);
466 		}
467 		pin->gsp_output = GPIO_SIM_OUTPUT_HIGH;
468 		break;
469 	default:
470 		mutex_exit(&gs->gs_mutex);
471 		return (EINVAL);
472 	}
473 
474 	gpio_sim_update_input(pin);
475 	mutex_exit(&gs->gs_mutex);
476 
477 	return (0);
478 }
479 
480 static const kgpio_ops_t gpio_sim_ops = {
481 	.kgo_name2id = gpio_sim_op_name2id,
482 	.kgo_get = gpio_sim_op_attr_get,
483 	.kgo_set = gpio_sim_op_attr_set,
484 	.kgo_cap = gpio_sim_op_attr_cap,
485 	.kgo_input = gpio_sim_op_attr_dpio_input,
486 	.kgo_output_state = gpio_sim_op_attr_dpio_output_state,
487 	.kgo_output = gpio_sim_op_attr_dpio_output
488 };
489 
490 static void
gpio_sim_cleanup(gpio_sim_t * gs)491 gpio_sim_cleanup(gpio_sim_t *gs)
492 {
493 	int inst = ddi_get_instance(gs->gs_dip);
494 
495 	ddi_periodic_delete(gs->gs_period);
496 	kmem_free(gs->gs_pins, sizeof (gpio_sim_pin_t) * gs->gs_npins);
497 	mutex_destroy(&gs->gs_mutex);
498 	ddi_soft_state_free(gpio_sim_state, inst);
499 }
500 
501 static int
gpio_sim_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)502 gpio_sim_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
503 {
504 	int inst, ret;
505 	gpio_sim_t *gs;
506 
507 	switch (cmd) {
508 	case DDI_ATTACH:
509 		break;
510 	case DDI_RESUME:
511 		return (DDI_SUCCESS);
512 	default:
513 		return (DDI_FAILURE);
514 	}
515 
516 	inst = ddi_get_instance(dip);
517 	if (ddi_get_soft_state(gpio_sim_state, inst) != NULL) {
518 		dev_err(dip, CE_WARN, "dip is already attached?!");
519 		return (DDI_FAILURE);
520 	}
521 
522 	if (ddi_soft_state_zalloc(gpio_sim_state, inst) != 0) {
523 		dev_err(dip, CE_WARN, "failed to allocate soft state");
524 		return (DDI_FAILURE);
525 	}
526 
527 	gs = ddi_get_soft_state(gpio_sim_state, inst);
528 	ASSERT3P(gs, !=, NULL);
529 
530 	gs->gs_npins = ARRAY_SIZE(gpio_sim_pins);
531 	gs->gs_pins = kmem_alloc(sizeof (gpio_sim_pin_t) * gs->gs_npins,
532 	    KM_SLEEP);
533 	bcopy(gpio_sim_pins, gs->gs_pins, sizeof (gpio_sim_pin_t) *
534 	    gs->gs_npins);
535 	mutex_init(&gs->gs_mutex, NULL, MUTEX_DRIVER, NULL);
536 	gs->gs_dip = dip;
537 	gs->gs_period = ddi_periodic_add(gpio_sim_periodic, gs, MSEC2NSEC(500),
538 	    DDI_IPL_0);
539 
540 	ret = kgpio_register(dip, &gpio_sim_ops, gs, gs->gs_npins);
541 	if (ret != 0) {
542 		dev_err(dip, CE_WARN, "failed to register with kgpio "
543 		    "interface: %d\n", ret);
544 		gpio_sim_cleanup(gs);
545 		return (DDI_FAILURE);
546 	}
547 
548 	return (DDI_SUCCESS);
549 }
550 
551 static int
gpio_sim_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)552 gpio_sim_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
553 {
554 	int inst, ret;
555 	gpio_sim_t *gs;
556 
557 	switch (cmd) {
558 	case DDI_DETACH:
559 		break;
560 	case DDI_SUSPEND:
561 		return (DDI_SUCCESS);
562 	default:
563 		return (DDI_FAILURE);
564 	}
565 
566 	inst = ddi_get_instance(dip);
567 	gs = ddi_get_soft_state(gpio_sim_state, inst);
568 	if (gs == NULL) {
569 		dev_err(dip, CE_WARN, "asked to detach instance with no state");
570 		return (DDI_FAILURE);
571 	}
572 
573 	ASSERT3P(dip, ==, gs->gs_dip);
574 
575 	ret = kgpio_unregister(gs->gs_dip);
576 	if (ret != 0) {
577 		dev_err(dip, CE_WARN, "failed to unregister from kgpio "
578 		    "framework: %d", ret);
579 		return (DDI_FAILURE);
580 	}
581 
582 	gpio_sim_cleanup(gs);
583 	return (DDI_SUCCESS);
584 }
585 
586 static struct dev_ops gpio_sim_dev_ops = {
587 	.devo_rev = DEVO_REV,
588 	.devo_refcnt = 0,
589 	.devo_identify = nulldev,
590 	.devo_probe = nulldev,
591 	.devo_attach = gpio_sim_attach,
592 	.devo_detach = gpio_sim_detach,
593 	.devo_reset = nodev,
594 	.devo_quiesce = ddi_quiesce_not_needed,
595 };
596 
597 static struct modldrv gpio_sim_modldrv = {
598 	.drv_modops = &mod_driverops,
599 	.drv_linkinfo = "GPIO Simulator Driver",
600 	.drv_dev_ops = &gpio_sim_dev_ops
601 };
602 
603 static struct modlinkage gpio_sim_modlinkage = {
604 	.ml_rev = MODREV_1,
605 	.ml_linkage = { &gpio_sim_modldrv, NULL }
606 };
607 
608 int
_init(void)609 _init(void)
610 {
611 	int ret;
612 
613 	ret = ddi_soft_state_init(&gpio_sim_state, sizeof (gpio_sim_t), 1);
614 	if (ret != 0) {
615 		return (ret);
616 
617 	}
618 
619 	ret = mod_install(&gpio_sim_modlinkage);
620 	if (ret != 0) {
621 		ddi_soft_state_fini(&gpio_sim_state);
622 		return (ret);
623 	}
624 
625 	return (ret);
626 }
627 
628 int
_info(struct modinfo * modinfop)629 _info(struct modinfo *modinfop)
630 {
631 	return (mod_info(&gpio_sim_modlinkage, modinfop));
632 }
633 
634 int
_fini(void)635 _fini(void)
636 {
637 	int ret;
638 
639 	ret = mod_remove(&gpio_sim_modlinkage);
640 	if (ret != 0) {
641 		return (ret);
642 	}
643 
644 	ddi_soft_state_fini(&gpio_sim_state);
645 	return (ret);
646 }
647