1/*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2020 Alstom Group.
5 * Copyright (c) 2020 Semihalf.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29/*
30 * Driver for TI TCA64XX I2C GPIO expander module.
31 *
32 * This driver only supports basic functionality
33 * (interrupt handling and polarity inversion were omitted).
34 */
35
36#include <sys/param.h>
37#include <sys/bus.h>
38#include <sys/gpio.h>
39#include <sys/kernel.h>
40#include <sys/module.h>
41#include <sys/proc.h>
42#include <sys/systm.h>
43#include <sys/sysctl.h>
44
45#include <machine/bus.h>
46
47#include <dev/ofw/openfirm.h>
48#include <dev/ofw/ofw_bus.h>
49#include <dev/ofw/ofw_bus_subr.h>
50
51#include <dev/iicbus/iicbus.h>
52#include <dev/iicbus/iiconf.h>
53
54#include <dev/gpio/gpiobusvar.h>
55
56#include "gpio_if.h"
57
58/* Base addresses of registers. LSB omitted. */
59
60#define TCA64XX_PINS_PER_REG		8
61
62#define TCA64XX_BIT_FROM_PIN(pin)	(pin % TCA64XX_PINS_PER_REG)
63#define TCA64XX_REG_ADDR(pin, baseaddr)	(baseaddr | (pin / \
64					TCA64XX_PINS_PER_REG))
65#define	TCA64XX_PIN_CAPS		(GPIO_PIN_OUTPUT | GPIO_PIN_INPUT \
66					| GPIO_PIN_PUSHPULL | GPIO_PIN_INVIN)
67
68#define TCA6416_IN_PORT_REG		0x0
69#define	TCA6416_OUT_PORT_REG		0x2
70#define	TCA6416_POLARITY_INV_REG	0x4
71#define	TCA6416_CONF_REG		0x6
72#define	TCA6416_NUM_PINS		16
73
74#define TCA6408_IN_PORT_REG		0x0
75#define TCA6408_OUT_PORT_REG		0x1
76#define TCA6408_POLARITY_INV_REG	0x2
77#define TCA6408_CONF_REG		0x3
78#define TCA6408_NUM_PINS		8
79
80#ifdef DEBUG
81#define	dbg_dev_printf(dev, fmt, args...)	\
82	device_printf(dev, fmt, ##args)
83#else
84#define	dbg_dev_printf(dev, fmt, args...)
85#endif
86
87enum chip_type{
88	TCA6416_TYPE = 1,
89	TCA6408_TYPE
90};
91
92struct tca64xx_softc {
93	device_t	dev;
94	device_t	busdev;
95	enum chip_type	chip;
96	struct mtx	mtx;
97	uint32_t	addr;
98	uint8_t		num_pins;
99	uint8_t 	in_port_reg;
100	uint8_t		out_port_reg;
101	uint8_t		polarity_inv_reg;
102	uint8_t		conf_reg;
103	uint8_t		pin_caps;
104};
105
106static int tca64xx_read(device_t, uint8_t, uint8_t *);
107static int tca64xx_write(device_t, uint8_t, uint8_t);
108static int tca64xx_probe(device_t);
109static int tca64xx_attach(device_t);
110static int tca64xx_detach(device_t);
111static device_t tca64xx_get_bus(device_t);
112static int tca64xx_pin_max(device_t, int *);
113static int tca64xx_pin_getcaps(device_t, uint32_t, uint32_t *);
114static int tca64xx_pin_getflags(device_t, uint32_t, uint32_t *);
115static int tca64xx_pin_setflags(device_t, uint32_t, uint32_t);
116static int tca64xx_pin_getname(device_t, uint32_t, char *);
117static int tca64xx_pin_get(device_t, uint32_t, unsigned int *);
118static int tca64xx_pin_set(device_t, uint32_t, unsigned int);
119static int tca64xx_pin_toggle(device_t, uint32_t);
120#ifdef DEBUG
121static void tca6408_regdump_setup(device_t dev);
122static void tca6416_regdump_setup(device_t dev);
123static int tca64xx_regdump_sysctl(SYSCTL_HANDLER_ARGS);
124#endif
125
126static device_method_t tca64xx_methods[] = {
127	DEVMETHOD(device_probe,		tca64xx_probe),
128	DEVMETHOD(device_attach,	tca64xx_attach),
129	DEVMETHOD(device_detach,	tca64xx_detach),
130
131	/* GPIO methods */
132	DEVMETHOD(gpio_get_bus,		tca64xx_get_bus),
133	DEVMETHOD(gpio_pin_max,		tca64xx_pin_max),
134	DEVMETHOD(gpio_pin_getcaps,	tca64xx_pin_getcaps),
135	DEVMETHOD(gpio_pin_getflags,	tca64xx_pin_getflags),
136	DEVMETHOD(gpio_pin_setflags,	tca64xx_pin_setflags),
137	DEVMETHOD(gpio_pin_getname,	tca64xx_pin_getname),
138	DEVMETHOD(gpio_pin_get,		tca64xx_pin_get),
139	DEVMETHOD(gpio_pin_set,		tca64xx_pin_set),
140	DEVMETHOD(gpio_pin_toggle,	tca64xx_pin_toggle),
141
142	DEVMETHOD_END
143};
144
145static driver_t tca64xx_driver = {
146	"gpio",
147	tca64xx_methods,
148	sizeof(struct tca64xx_softc)
149};
150
151DRIVER_MODULE(tca64xx, iicbus, tca64xx_driver, 0, 0);
152MODULE_VERSION(tca64xx, 1);
153
154static struct ofw_compat_data compat_data[] = {
155	{"nxp,pca9555",	TCA6416_TYPE},
156	{"ti,tca6408",	TCA6408_TYPE},
157	{"ti,tca6416",	TCA6416_TYPE},
158	{"ti,tca9539",	TCA6416_TYPE},
159	{0,0}
160};
161
162static int
163tca64xx_read(device_t dev, uint8_t reg, uint8_t *data)
164{
165	struct iic_msg msgs[2];
166	struct tca64xx_softc *sc;
167	int error;
168
169	sc = device_get_softc(dev);
170	if (data == NULL)
171		return (EINVAL);
172
173	msgs[0].slave = sc->addr;
174	msgs[0].flags = IIC_M_WR | IIC_M_NOSTOP;
175	msgs[0].len = 1;
176	msgs[0].buf = &reg;
177
178	msgs[1].slave = sc->addr;
179	msgs[1].flags = IIC_M_RD;
180	msgs[1].len = 1;
181	msgs[1].buf = data;
182
183	error = iicbus_transfer_excl(dev, msgs, 2, IIC_WAIT);
184	return (iic2errno(error));
185}
186
187static int
188tca64xx_write(device_t dev, uint8_t reg, uint8_t val)
189{
190	struct iic_msg msg;
191	struct tca64xx_softc *sc;
192	int error;
193	uint8_t buffer[2] = {reg, val};
194
195	sc = device_get_softc(dev);
196
197	msg.slave = sc->addr;
198	msg.flags = IIC_M_WR;
199	msg.len = 2;
200	msg.buf = buffer;
201
202	error = iicbus_transfer_excl(dev, &msg, 1, IIC_WAIT);
203	return (iic2errno(error));
204}
205
206static int
207tca64xx_probe(device_t dev)
208{
209	const struct ofw_compat_data *compat_ptr;
210
211	if (!ofw_bus_status_okay(dev))
212		return (ENXIO);
213
214	compat_ptr = ofw_bus_search_compatible(dev, compat_data);
215
216	switch (compat_ptr->ocd_data) {
217	case TCA6416_TYPE:
218		device_set_desc(dev, "TCA6416 I/O expander");
219		break;
220	case TCA6408_TYPE:
221		device_set_desc(dev, "TCA6408 I/O expander");
222		break;
223	default:
224		return (ENXIO);
225	}
226
227	return (BUS_PROBE_DEFAULT);
228}
229
230static int
231tca64xx_attach(device_t dev)
232{
233	struct tca64xx_softc *sc;
234	const struct ofw_compat_data *compat_ptr;
235
236	sc = device_get_softc(dev);
237	compat_ptr = ofw_bus_search_compatible(dev, compat_data);
238
239	switch (compat_ptr->ocd_data) {
240	case TCA6416_TYPE:
241		sc->in_port_reg = TCA6416_IN_PORT_REG;
242		sc->out_port_reg = TCA6416_OUT_PORT_REG;
243		sc->polarity_inv_reg = TCA6416_POLARITY_INV_REG;
244		sc->conf_reg = TCA6416_CONF_REG;
245		sc->num_pins = TCA6416_NUM_PINS;
246		break;
247	case TCA6408_TYPE:
248		sc->in_port_reg = TCA6408_IN_PORT_REG;
249		sc->out_port_reg = TCA6408_OUT_PORT_REG;
250		sc->polarity_inv_reg = TCA6408_POLARITY_INV_REG;
251		sc->conf_reg = TCA6408_CONF_REG;
252		sc->num_pins = TCA6408_NUM_PINS;
253		break;
254	default:
255		__assert_unreachable();
256	}
257
258	sc->pin_caps = TCA64XX_PIN_CAPS;
259	sc->chip = compat_ptr->ocd_data;
260	sc->dev = dev;
261	sc->addr = iicbus_get_addr(dev);
262
263	mtx_init(&sc->mtx, "tca64xx gpio", "gpio", MTX_DEF);
264	sc->busdev = gpiobus_attach_bus(dev);
265	if (sc->busdev == NULL) {
266		device_printf(dev, "Could not create busdev child\n");
267		return (ENXIO);
268	}
269
270	OF_device_register_xref(OF_xref_from_node(ofw_bus_get_node(dev)), dev);
271
272#ifdef DEBUG
273	switch (sc->chip) {
274	case TCA6416_TYPE:
275		tca6416_regdump_setup(dev);
276		break;
277	case TCA6408_TYPE:
278		tca6408_regdump_setup(dev);
279		break;
280	default:
281		__assert_unreachable();
282	}
283#endif
284
285	return (0);
286}
287
288static int
289tca64xx_detach(device_t dev)
290{
291	struct tca64xx_softc *sc;
292
293	sc = device_get_softc(dev);
294
295	if (sc->busdev != NULL)
296		gpiobus_detach_bus(sc->busdev);
297
298	mtx_destroy(&sc->mtx);
299
300	return (0);
301}
302
303static device_t
304tca64xx_get_bus(device_t dev)
305{
306	struct tca64xx_softc *sc;
307
308	sc = device_get_softc(dev);
309
310	return (sc->busdev);
311}
312
313static int
314tca64xx_pin_max(device_t dev __unused, int *maxpin)
315{
316	struct tca64xx_softc *sc;
317
318	sc = device_get_softc(dev);
319
320	if (maxpin == NULL)
321		return (EINVAL);
322
323	*maxpin = sc->num_pins-1;
324
325	return (0);
326}
327
328static int
329tca64xx_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
330{
331	struct tca64xx_softc *sc;
332
333	sc = device_get_softc(dev);
334
335	if (pin >= sc->num_pins || caps == NULL)
336		return (EINVAL);
337	*caps = sc->pin_caps;
338
339	return (0);
340}
341
342static int
343tca64xx_pin_getflags(device_t dev, uint32_t pin, uint32_t *pflags)
344{
345	int error;
346	uint8_t bit, val, addr;
347	struct tca64xx_softc *sc;
348
349	sc = device_get_softc(dev);
350
351	bit = TCA64XX_BIT_FROM_PIN(pin);
352
353	if (pin >= sc->num_pins || pflags == NULL)
354		return (EINVAL);
355
356	addr = TCA64XX_REG_ADDR(pin, sc->conf_reg);
357	error = tca64xx_read(dev, addr, &val);
358	if (error != 0)
359		return (error);
360
361	*pflags = (val & (1 << bit)) ? GPIO_PIN_INPUT : GPIO_PIN_OUTPUT;
362
363	addr = TCA64XX_REG_ADDR(pin, sc->polarity_inv_reg);
364	error = tca64xx_read(dev, addr, &val);
365	if (error != 0)
366		return (error);
367
368	if (val & (1 << bit))
369		*pflags |= GPIO_PIN_INVIN;
370
371	return (0);
372}
373
374static int
375tca64xx_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
376{
377	uint8_t bit, val, addr, pins, inv_val;
378	int error;
379	struct tca64xx_softc *sc;
380
381	sc = device_get_softc(dev);
382
383	pins = sc->num_pins;
384	bit = TCA64XX_BIT_FROM_PIN(pin);
385
386	if (pin >= pins)
387		return (EINVAL);
388	mtx_lock(&sc->mtx);
389
390	addr = TCA64XX_REG_ADDR(pin, sc->conf_reg);
391	error = tca64xx_read(dev, addr, &val);
392	if (error != 0)
393		goto fail;
394
395	addr = TCA64XX_REG_ADDR(pin, sc->polarity_inv_reg);
396	error = tca64xx_read(dev, addr, &inv_val);
397	if (error != 0)
398		goto fail;
399
400	if (flags & GPIO_PIN_INPUT)
401		val |= (1 << bit);
402	else if (flags & GPIO_PIN_OUTPUT)
403		val &= ~(1 << bit);
404
405	if (flags & GPIO_PIN_INVIN)
406		inv_val |= (1 << bit);
407	else
408		inv_val &= ~(1 << bit);
409
410	addr = TCA64XX_REG_ADDR(pin, sc->conf_reg);
411	error = tca64xx_write(dev, addr, val);
412	if (error != 0)
413		goto fail;
414
415	addr = TCA64XX_REG_ADDR(pin, sc->polarity_inv_reg);
416	error = tca64xx_write(dev, addr, inv_val);
417
418fail:
419	mtx_unlock(&sc->mtx);
420	return (error);
421}
422
423static int
424tca64xx_pin_getname(device_t dev, uint32_t pin, char *name)
425{
426	struct tca64xx_softc *sc;
427
428	sc = device_get_softc(dev);
429
430	if (pin >= sc->num_pins || name == NULL)
431		return (EINVAL);
432
433	snprintf(name, GPIOMAXNAME, "gpio_P%d%d", pin / TCA64XX_PINS_PER_REG,
434	    pin % TCA64XX_PINS_PER_REG);
435
436	return (0);
437}
438
439static int
440tca64xx_pin_get(device_t dev, uint32_t pin, unsigned int *pval)
441{
442	uint8_t bit, addr, pins, reg_pvalue;
443	int error;
444	struct tca64xx_softc *sc;
445
446	sc = device_get_softc(dev);
447
448	pins = sc->num_pins;
449	addr = TCA64XX_REG_ADDR(pin, sc->in_port_reg);
450	bit = TCA64XX_BIT_FROM_PIN(pin);
451
452	if (pin >= pins || pval == NULL)
453		return (EINVAL);
454
455	dbg_dev_printf(dev, "Reading pin %u pvalue.", pin);
456
457	error = tca64xx_read(dev, addr, &reg_pvalue);
458	if (error != 0)
459		return (error);
460	*pval = reg_pvalue & (1 << bit) ? 1 : 0;
461
462	return (0);
463}
464
465static int
466tca64xx_pin_set(device_t dev, uint32_t pin, unsigned int val)
467{
468	uint8_t bit, addr, pins, value;
469	int error;
470	struct tca64xx_softc *sc;
471
472	sc = device_get_softc(dev);
473
474	pins = sc->num_pins;
475	addr = TCA64XX_REG_ADDR(pin, sc->out_port_reg);
476	bit = TCA64XX_BIT_FROM_PIN(pin);
477
478	if (pin >= pins)
479		return (EINVAL);
480
481	dbg_dev_printf(dev, "Setting pin: %u to %u\n", pin, val);
482
483	mtx_lock(&sc->mtx);
484
485	error = tca64xx_read(dev, addr, &value);
486	if (error != 0) {
487		mtx_unlock(&sc->mtx);
488		dbg_dev_printf(dev, "Failed to read from register.\n");
489		return (error);
490	}
491
492	if (val != 0)
493		value |= (1 << bit);
494	else
495		value &= ~(1 << bit);
496
497	error = tca64xx_write(dev, addr, value);
498	if (error != 0) {
499		mtx_unlock(&sc->mtx);
500		dbg_dev_printf(dev, "Could not write to register.\n");
501		return (error);
502	}
503
504	mtx_unlock(&sc->mtx);
505
506	return (0);
507}
508
509static int
510tca64xx_pin_toggle(device_t dev, uint32_t pin)
511{
512	int error;
513	uint8_t bit, addr, pins, value;
514	struct tca64xx_softc *sc;
515
516	sc = device_get_softc(dev);
517
518	pins = sc->num_pins;
519	addr = TCA64XX_REG_ADDR(pin, sc->out_port_reg);
520	bit = TCA64XX_BIT_FROM_PIN(pin);
521
522	if (pin >= pins)
523		return (EINVAL);
524
525	dbg_dev_printf(dev, "Toggling pin: %d\n", pin);
526
527	mtx_lock(&sc->mtx);
528
529	error = tca64xx_read(dev, addr, &value);
530	if (error != 0) {
531		mtx_unlock(&sc->mtx);
532		dbg_dev_printf(dev, "Cannot read from register.\n");
533		return (error);
534	}
535
536	value ^= (1 << bit);
537
538	error = tca64xx_write(dev, addr, value);
539	if (error != 0) {
540		mtx_unlock(&sc->mtx);
541		dbg_dev_printf(dev, "Cannot write to register.\n");
542		return (error);
543	}
544
545	mtx_unlock(&sc->mtx);
546
547	return (0);
548}
549
550#ifdef DEBUG
551static void
552tca6416_regdump_setup(device_t dev)
553{
554	struct sysctl_ctx_list *ctx;
555	struct sysctl_oid *node;
556
557	ctx = device_get_sysctl_ctx(dev);
558	node = device_get_sysctl_tree(dev);
559
560	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(node), OID_AUTO, "in_reg_1",
561	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, dev,
562	    TCA6416_IN_PORT_REG, tca64xx_regdump_sysctl, "A", "Input port 1");
563	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(node), OID_AUTO, "in_reg_2",
564	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, dev,
565	    TCA6416_IN_PORT_REG | 1, tca64xx_regdump_sysctl, "A",
566	    "Input port 2");
567	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(node), OID_AUTO, "out_reg_1",
568	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, dev,
569	    TCA6416_OUT_PORT_REG, tca64xx_regdump_sysctl, "A",
570	    "Output port 1");
571	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(node), OID_AUTO, "out_reg_2",
572	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, dev,
573	    TCA6416_OUT_PORT_REG | 1, tca64xx_regdump_sysctl, "A",
574	    "Output port 2");
575	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(node), OID_AUTO, "pol_inv_1",
576	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, dev,
577	    TCA6416_POLARITY_INV_REG, tca64xx_regdump_sysctl, "A",
578	    "Polarity inv 1");
579	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(node), OID_AUTO, "pol_inv_2",
580	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, dev,
581	    TCA6416_POLARITY_INV_REG | 1, tca64xx_regdump_sysctl, "A",
582	    "Polarity inv 2");
583	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(node), OID_AUTO, "conf_reg_1",
584	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, dev,
585	    TCA6416_CONF_REG, tca64xx_regdump_sysctl, "A", "Configuration 1");
586	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(node), OID_AUTO, "conf_reg_2",
587	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, dev,
588	    TCA6416_CONF_REG | 1, tca64xx_regdump_sysctl, "A",
589	    "Configuration 2");
590}
591
592static void
593tca6408_regdump_setup(device_t dev)
594{
595	struct sysctl_ctx_list *ctx;
596	struct sysctl_oid *node;
597
598	ctx = device_get_sysctl_ctx(dev);
599	node = device_get_sysctl_tree(dev);
600
601	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(node), OID_AUTO, "in_reg_1",
602	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, dev,
603	    TCA6408_IN_PORT_REG, tca64xx_regdump_sysctl, "A", "Input port 1");
604	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(node), OID_AUTO, "out_reg_1",
605	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, dev,
606	    TCA6408_OUT_PORT_REG, tca64xx_regdump_sysctl, "A",
607	    "Output port 1");
608	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(node), OID_AUTO, "pol_inv_1",
609	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, dev,
610	    TCA6408_POLARITY_INV_REG, tca64xx_regdump_sysctl,
611	    "A", "Polarity inv 1");
612	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(node), OID_AUTO, "conf_reg_1",
613	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, dev,
614	    TCA6408_CONF_REG, tca64xx_regdump_sysctl, "A", "Configuration 1");
615}
616
617static int
618tca64xx_regdump_sysctl(SYSCTL_HANDLER_ARGS)
619{
620	device_t dev;
621	char buf[5];
622	int len, error;
623	uint8_t reg, regval;
624
625	dev = (device_t)arg1;
626	reg = (uint8_t)arg2;
627
628	error = tca64xx_read(dev, reg, &regval);
629	if (error != 0) {
630		return (error);
631	}
632
633	len = snprintf(buf, 5, "0x%02x", regval);
634
635	error = sysctl_handle_string(oidp, buf, len, req);
636
637	return (error);
638}
639#endif
640