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