19d5b72deSLars Poeschel /*
29d5b72deSLars Poeschel  *  Nano River Technologies viperboard GPIO lib driver
39d5b72deSLars Poeschel  *
49d5b72deSLars Poeschel  *  (C) 2012 by Lemonage GmbH
59d5b72deSLars Poeschel  *  Author: Lars Poeschel <poeschel@lemonage.de>
69d5b72deSLars Poeschel  *  All rights reserved.
79d5b72deSLars Poeschel  *
89d5b72deSLars Poeschel  *  This program is free software; you can redistribute  it and/or modify it
99d5b72deSLars Poeschel  *  under  the terms of  the GNU General  Public License as published by the
109d5b72deSLars Poeschel  *  Free Software Foundation;  either version 2 of the	License, or (at your
119d5b72deSLars Poeschel  *  option) any later version.
129d5b72deSLars Poeschel  *
139d5b72deSLars Poeschel  */
149d5b72deSLars Poeschel 
159d5b72deSLars Poeschel #include <linux/kernel.h>
169d5b72deSLars Poeschel #include <linux/errno.h>
179d5b72deSLars Poeschel #include <linux/module.h>
189d5b72deSLars Poeschel #include <linux/slab.h>
199d5b72deSLars Poeschel #include <linux/types.h>
209d5b72deSLars Poeschel #include <linux/mutex.h>
219d5b72deSLars Poeschel #include <linux/platform_device.h>
229d5b72deSLars Poeschel 
239d5b72deSLars Poeschel #include <linux/usb.h>
249d5b72deSLars Poeschel #include <linux/gpio.h>
259d5b72deSLars Poeschel 
269d5b72deSLars Poeschel #include <linux/mfd/viperboard.h>
279d5b72deSLars Poeschel 
289d5b72deSLars Poeschel #define VPRBRD_GPIOA_CLK_1MHZ		0
299d5b72deSLars Poeschel #define VPRBRD_GPIOA_CLK_100KHZ		1
309d5b72deSLars Poeschel #define VPRBRD_GPIOA_CLK_10KHZ		2
319d5b72deSLars Poeschel #define VPRBRD_GPIOA_CLK_1KHZ		3
329d5b72deSLars Poeschel #define VPRBRD_GPIOA_CLK_100HZ		4
339d5b72deSLars Poeschel #define VPRBRD_GPIOA_CLK_10HZ		5
349d5b72deSLars Poeschel 
359d5b72deSLars Poeschel #define VPRBRD_GPIOA_FREQ_DEFAULT	1000
369d5b72deSLars Poeschel 
379d5b72deSLars Poeschel #define VPRBRD_GPIOA_CMD_CONT		0x00
389d5b72deSLars Poeschel #define VPRBRD_GPIOA_CMD_PULSE		0x01
399d5b72deSLars Poeschel #define VPRBRD_GPIOA_CMD_PWM		0x02
409d5b72deSLars Poeschel #define VPRBRD_GPIOA_CMD_SETOUT		0x03
419d5b72deSLars Poeschel #define VPRBRD_GPIOA_CMD_SETIN		0x04
429d5b72deSLars Poeschel #define VPRBRD_GPIOA_CMD_SETINT		0x05
439d5b72deSLars Poeschel #define VPRBRD_GPIOA_CMD_GETIN		0x06
449d5b72deSLars Poeschel 
459d5b72deSLars Poeschel #define VPRBRD_GPIOB_CMD_SETDIR		0x00
469d5b72deSLars Poeschel #define VPRBRD_GPIOB_CMD_SETVAL		0x01
479d5b72deSLars Poeschel 
489d5b72deSLars Poeschel struct vprbrd_gpioa_msg {
499d5b72deSLars Poeschel 	u8 cmd;
509d5b72deSLars Poeschel 	u8 clk;
519d5b72deSLars Poeschel 	u8 offset;
529d5b72deSLars Poeschel 	u8 t1;
539d5b72deSLars Poeschel 	u8 t2;
549d5b72deSLars Poeschel 	u8 invert;
559d5b72deSLars Poeschel 	u8 pwmlevel;
569d5b72deSLars Poeschel 	u8 outval;
579d5b72deSLars Poeschel 	u8 risefall;
589d5b72deSLars Poeschel 	u8 answer;
599d5b72deSLars Poeschel 	u8 __fill;
609d5b72deSLars Poeschel } __packed;
619d5b72deSLars Poeschel 
629d5b72deSLars Poeschel struct vprbrd_gpiob_msg {
639d5b72deSLars Poeschel 	u8 cmd;
649d5b72deSLars Poeschel 	u16 val;
659d5b72deSLars Poeschel 	u16 mask;
669d5b72deSLars Poeschel } __packed;
679d5b72deSLars Poeschel 
689d5b72deSLars Poeschel struct vprbrd_gpio {
699d5b72deSLars Poeschel 	struct gpio_chip gpioa; /* gpio a related things */
709d5b72deSLars Poeschel 	u32 gpioa_out;
719d5b72deSLars Poeschel 	u32 gpioa_val;
729d5b72deSLars Poeschel 	struct gpio_chip gpiob; /* gpio b related things */
739d5b72deSLars Poeschel 	u32 gpiob_out;
749d5b72deSLars Poeschel 	u32 gpiob_val;
759d5b72deSLars Poeschel 	struct vprbrd *vb;
769d5b72deSLars Poeschel };
779d5b72deSLars Poeschel 
789d5b72deSLars Poeschel /* gpioa sampling clock module parameter */
799d5b72deSLars Poeschel static unsigned char gpioa_clk;
809d5b72deSLars Poeschel static unsigned int gpioa_freq = VPRBRD_GPIOA_FREQ_DEFAULT;
819d5b72deSLars Poeschel module_param(gpioa_freq, uint, 0);
829d5b72deSLars Poeschel MODULE_PARM_DESC(gpioa_freq,
839d5b72deSLars Poeschel 	"gpio-a sampling freq in Hz (default is 1000Hz) valid values: 10, 100, 1000, 10000, 100000, 1000000");
849d5b72deSLars Poeschel 
859d5b72deSLars Poeschel /* ----- begin of gipo a chip -------------------------------------------- */
869d5b72deSLars Poeschel 
879d5b72deSLars Poeschel static int vprbrd_gpioa_get(struct gpio_chip *chip,
889d5b72deSLars Poeschel 		unsigned offset)
899d5b72deSLars Poeschel {
909d5b72deSLars Poeschel 	int ret, answer, error = 0;
919d5b72deSLars Poeschel 	struct vprbrd_gpio *gpio =
929d5b72deSLars Poeschel 			container_of(chip, struct vprbrd_gpio, gpioa);
939d5b72deSLars Poeschel 	struct vprbrd *vb = gpio->vb;
949d5b72deSLars Poeschel 	struct vprbrd_gpioa_msg *gamsg = (struct vprbrd_gpioa_msg *)vb->buf;
959d5b72deSLars Poeschel 
969d5b72deSLars Poeschel 	/* if io is set to output, just return the saved value */
979d5b72deSLars Poeschel 	if (gpio->gpioa_out & (1 << offset))
989d5b72deSLars Poeschel 		return gpio->gpioa_val & (1 << offset);
999d5b72deSLars Poeschel 
1009d5b72deSLars Poeschel 	mutex_lock(&vb->lock);
1019d5b72deSLars Poeschel 
1029d5b72deSLars Poeschel 	gamsg->cmd = VPRBRD_GPIOA_CMD_GETIN;
1039d5b72deSLars Poeschel 	gamsg->clk = 0x00;
1049d5b72deSLars Poeschel 	gamsg->offset = offset;
1059d5b72deSLars Poeschel 	gamsg->t1 = 0x00;
1069d5b72deSLars Poeschel 	gamsg->t2 = 0x00;
1079d5b72deSLars Poeschel 	gamsg->invert = 0x00;
1089d5b72deSLars Poeschel 	gamsg->pwmlevel = 0x00;
1099d5b72deSLars Poeschel 	gamsg->outval = 0x00;
1109d5b72deSLars Poeschel 	gamsg->risefall = 0x00;
1119d5b72deSLars Poeschel 	gamsg->answer = 0x00;
1129d5b72deSLars Poeschel 	gamsg->__fill = 0x00;
1139d5b72deSLars Poeschel 
1149d5b72deSLars Poeschel 	ret = usb_control_msg(vb->usb_dev, usb_sndctrlpipe(vb->usb_dev, 0),
1159d5b72deSLars Poeschel 		VPRBRD_USB_REQUEST_GPIOA, VPRBRD_USB_TYPE_OUT, 0x0000,
1169d5b72deSLars Poeschel 		0x0000, gamsg, sizeof(struct vprbrd_gpioa_msg),
1179d5b72deSLars Poeschel 		VPRBRD_USB_TIMEOUT_MS);
1189d5b72deSLars Poeschel 	if (ret != sizeof(struct vprbrd_gpioa_msg))
1199d5b72deSLars Poeschel 		error = -EREMOTEIO;
1209d5b72deSLars Poeschel 
1219d5b72deSLars Poeschel 	ret = usb_control_msg(vb->usb_dev, usb_rcvctrlpipe(vb->usb_dev, 0),
1229d5b72deSLars Poeschel 		VPRBRD_USB_REQUEST_GPIOA, VPRBRD_USB_TYPE_IN, 0x0000,
1239d5b72deSLars Poeschel 		0x0000, gamsg, sizeof(struct vprbrd_gpioa_msg),
1249d5b72deSLars Poeschel 		VPRBRD_USB_TIMEOUT_MS);
1259d5b72deSLars Poeschel 	answer = gamsg->answer & 0x01;
1269d5b72deSLars Poeschel 
1279d5b72deSLars Poeschel 	mutex_unlock(&vb->lock);
1289d5b72deSLars Poeschel 
1299d5b72deSLars Poeschel 	if (ret != sizeof(struct vprbrd_gpioa_msg))
1309d5b72deSLars Poeschel 		error = -EREMOTEIO;
1319d5b72deSLars Poeschel 
1329d5b72deSLars Poeschel 	if (error)
1339d5b72deSLars Poeschel 		return error;
1349d5b72deSLars Poeschel 
1359d5b72deSLars Poeschel 	return answer;
1369d5b72deSLars Poeschel }
1379d5b72deSLars Poeschel 
1389d5b72deSLars Poeschel static void vprbrd_gpioa_set(struct gpio_chip *chip,
1399d5b72deSLars Poeschel 		unsigned offset, int value)
1409d5b72deSLars Poeschel {
1419d5b72deSLars Poeschel 	int ret;
1429d5b72deSLars Poeschel 	struct vprbrd_gpio *gpio =
1439d5b72deSLars Poeschel 			container_of(chip, struct vprbrd_gpio, gpioa);
1449d5b72deSLars Poeschel 	struct vprbrd *vb = gpio->vb;
1459d5b72deSLars Poeschel 	struct vprbrd_gpioa_msg *gamsg = (struct vprbrd_gpioa_msg *)vb->buf;
1469d5b72deSLars Poeschel 
1479d5b72deSLars Poeschel 	if (gpio->gpioa_out & (1 << offset)) {
1489d5b72deSLars Poeschel 		if (value)
1499d5b72deSLars Poeschel 			gpio->gpioa_val |= (1 << offset);
1509d5b72deSLars Poeschel 		else
1519d5b72deSLars Poeschel 			gpio->gpioa_val &= ~(1 << offset);
1529d5b72deSLars Poeschel 
1539d5b72deSLars Poeschel 		mutex_lock(&vb->lock);
1549d5b72deSLars Poeschel 
1559d5b72deSLars Poeschel 		gamsg->cmd = VPRBRD_GPIOA_CMD_SETOUT;
1569d5b72deSLars Poeschel 		gamsg->clk = 0x00;
1579d5b72deSLars Poeschel 		gamsg->offset = offset;
1589d5b72deSLars Poeschel 		gamsg->t1 = 0x00;
1599d5b72deSLars Poeschel 		gamsg->t2 = 0x00;
1609d5b72deSLars Poeschel 		gamsg->invert = 0x00;
1619d5b72deSLars Poeschel 		gamsg->pwmlevel = 0x00;
1629d5b72deSLars Poeschel 		gamsg->outval = value;
1639d5b72deSLars Poeschel 		gamsg->risefall = 0x00;
1649d5b72deSLars Poeschel 		gamsg->answer = 0x00;
1659d5b72deSLars Poeschel 		gamsg->__fill = 0x00;
1669d5b72deSLars Poeschel 
1679d5b72deSLars Poeschel 		ret = usb_control_msg(vb->usb_dev,
1689d5b72deSLars Poeschel 			usb_sndctrlpipe(vb->usb_dev, 0),
1699d5b72deSLars Poeschel 			VPRBRD_USB_REQUEST_GPIOA, VPRBRD_USB_TYPE_OUT,
1709d5b72deSLars Poeschel 			0x0000,	0x0000, gamsg,
1719d5b72deSLars Poeschel 			sizeof(struct vprbrd_gpioa_msg), VPRBRD_USB_TIMEOUT_MS);
1729d5b72deSLars Poeschel 
1739d5b72deSLars Poeschel 		mutex_unlock(&vb->lock);
1749d5b72deSLars Poeschel 
1759d5b72deSLars Poeschel 		if (ret != sizeof(struct vprbrd_gpioa_msg))
17658383c78SLinus Walleij 			dev_err(chip->parent, "usb error setting pin value\n");
1779d5b72deSLars Poeschel 	}
1789d5b72deSLars Poeschel }
1799d5b72deSLars Poeschel 
1809d5b72deSLars Poeschel static int vprbrd_gpioa_direction_input(struct gpio_chip *chip,
1819d5b72deSLars Poeschel 			unsigned offset)
1829d5b72deSLars Poeschel {
1839d5b72deSLars Poeschel 	int ret;
1849d5b72deSLars Poeschel 	struct vprbrd_gpio *gpio =
1859d5b72deSLars Poeschel 			container_of(chip, struct vprbrd_gpio, gpioa);
1869d5b72deSLars Poeschel 	struct vprbrd *vb = gpio->vb;
1879d5b72deSLars Poeschel 	struct vprbrd_gpioa_msg *gamsg = (struct vprbrd_gpioa_msg *)vb->buf;
1889d5b72deSLars Poeschel 
1899d5b72deSLars Poeschel 	gpio->gpioa_out &= ~(1 << offset);
1909d5b72deSLars Poeschel 
1919d5b72deSLars Poeschel 	mutex_lock(&vb->lock);
1929d5b72deSLars Poeschel 
1939d5b72deSLars Poeschel 	gamsg->cmd = VPRBRD_GPIOA_CMD_SETIN;
1949d5b72deSLars Poeschel 	gamsg->clk = gpioa_clk;
1959d5b72deSLars Poeschel 	gamsg->offset = offset;
1969d5b72deSLars Poeschel 	gamsg->t1 = 0x00;
1979d5b72deSLars Poeschel 	gamsg->t2 = 0x00;
1989d5b72deSLars Poeschel 	gamsg->invert = 0x00;
1999d5b72deSLars Poeschel 	gamsg->pwmlevel = 0x00;
2009d5b72deSLars Poeschel 	gamsg->outval = 0x00;
2019d5b72deSLars Poeschel 	gamsg->risefall = 0x00;
2029d5b72deSLars Poeschel 	gamsg->answer = 0x00;
2039d5b72deSLars Poeschel 	gamsg->__fill = 0x00;
2049d5b72deSLars Poeschel 
2059d5b72deSLars Poeschel 	ret = usb_control_msg(vb->usb_dev, usb_sndctrlpipe(vb->usb_dev, 0),
2069d5b72deSLars Poeschel 		VPRBRD_USB_REQUEST_GPIOA, VPRBRD_USB_TYPE_OUT, 0x0000,
2079d5b72deSLars Poeschel 		0x0000, gamsg, sizeof(struct vprbrd_gpioa_msg),
2089d5b72deSLars Poeschel 		VPRBRD_USB_TIMEOUT_MS);
2099d5b72deSLars Poeschel 
2109d5b72deSLars Poeschel 	mutex_unlock(&vb->lock);
2119d5b72deSLars Poeschel 
2129d5b72deSLars Poeschel 	if (ret != sizeof(struct vprbrd_gpioa_msg))
2139d5b72deSLars Poeschel 		return -EREMOTEIO;
2149d5b72deSLars Poeschel 
2159d5b72deSLars Poeschel 	return 0;
2169d5b72deSLars Poeschel }
2179d5b72deSLars Poeschel 
2189d5b72deSLars Poeschel static int vprbrd_gpioa_direction_output(struct gpio_chip *chip,
2199d5b72deSLars Poeschel 			unsigned offset, int value)
2209d5b72deSLars Poeschel {
2219d5b72deSLars Poeschel 	int ret;
2229d5b72deSLars Poeschel 	struct vprbrd_gpio *gpio =
2239d5b72deSLars Poeschel 			container_of(chip, struct vprbrd_gpio, gpioa);
2249d5b72deSLars Poeschel 	struct vprbrd *vb = gpio->vb;
2259d5b72deSLars Poeschel 	struct vprbrd_gpioa_msg *gamsg = (struct vprbrd_gpioa_msg *)vb->buf;
2269d5b72deSLars Poeschel 
2279d5b72deSLars Poeschel 	gpio->gpioa_out |= (1 << offset);
2289d5b72deSLars Poeschel 	if (value)
2299d5b72deSLars Poeschel 		gpio->gpioa_val |= (1 << offset);
2309d5b72deSLars Poeschel 	else
2319d5b72deSLars Poeschel 		gpio->gpioa_val &= ~(1 << offset);
2329d5b72deSLars Poeschel 
2339d5b72deSLars Poeschel 	mutex_lock(&vb->lock);
2349d5b72deSLars Poeschel 
2359d5b72deSLars Poeschel 	gamsg->cmd = VPRBRD_GPIOA_CMD_SETOUT;
2369d5b72deSLars Poeschel 	gamsg->clk = 0x00;
2379d5b72deSLars Poeschel 	gamsg->offset = offset;
2389d5b72deSLars Poeschel 	gamsg->t1 = 0x00;
2399d5b72deSLars Poeschel 	gamsg->t2 = 0x00;
2409d5b72deSLars Poeschel 	gamsg->invert = 0x00;
2419d5b72deSLars Poeschel 	gamsg->pwmlevel = 0x00;
2429d5b72deSLars Poeschel 	gamsg->outval = value;
2439d5b72deSLars Poeschel 	gamsg->risefall = 0x00;
2449d5b72deSLars Poeschel 	gamsg->answer = 0x00;
2459d5b72deSLars Poeschel 	gamsg->__fill = 0x00;
2469d5b72deSLars Poeschel 
2479d5b72deSLars Poeschel 	ret = usb_control_msg(vb->usb_dev, usb_sndctrlpipe(vb->usb_dev, 0),
2489d5b72deSLars Poeschel 		VPRBRD_USB_REQUEST_GPIOA, VPRBRD_USB_TYPE_OUT, 0x0000,
2499d5b72deSLars Poeschel 		0x0000, gamsg, sizeof(struct vprbrd_gpioa_msg),
2509d5b72deSLars Poeschel 		VPRBRD_USB_TIMEOUT_MS);
2519d5b72deSLars Poeschel 
2529d5b72deSLars Poeschel 	mutex_unlock(&vb->lock);
2539d5b72deSLars Poeschel 
2549d5b72deSLars Poeschel 	if (ret != sizeof(struct vprbrd_gpioa_msg))
2559d5b72deSLars Poeschel 		return -EREMOTEIO;
2569d5b72deSLars Poeschel 
2579d5b72deSLars Poeschel 	return 0;
2589d5b72deSLars Poeschel }
2599d5b72deSLars Poeschel 
2609d5b72deSLars Poeschel /* ----- end of gpio a chip ---------------------------------------------- */
2619d5b72deSLars Poeschel 
2629d5b72deSLars Poeschel /* ----- begin of gipo b chip -------------------------------------------- */
2639d5b72deSLars Poeschel 
2649d5b72deSLars Poeschel static int vprbrd_gpiob_setdir(struct vprbrd *vb, unsigned offset,
2659d5b72deSLars Poeschel 	unsigned dir)
2669d5b72deSLars Poeschel {
2679d5b72deSLars Poeschel 	struct vprbrd_gpiob_msg *gbmsg = (struct vprbrd_gpiob_msg *)vb->buf;
2689d5b72deSLars Poeschel 	int ret;
2699d5b72deSLars Poeschel 
2709d5b72deSLars Poeschel 	gbmsg->cmd = VPRBRD_GPIOB_CMD_SETDIR;
2719d5b72deSLars Poeschel 	gbmsg->val = cpu_to_be16(dir << offset);
2729d5b72deSLars Poeschel 	gbmsg->mask = cpu_to_be16(0x0001 << offset);
2739d5b72deSLars Poeschel 
2749d5b72deSLars Poeschel 	ret = usb_control_msg(vb->usb_dev, usb_sndctrlpipe(vb->usb_dev, 0),
2759d5b72deSLars Poeschel 		VPRBRD_USB_REQUEST_GPIOB, VPRBRD_USB_TYPE_OUT, 0x0000,
2769d5b72deSLars Poeschel 		0x0000, gbmsg, sizeof(struct vprbrd_gpiob_msg),
2779d5b72deSLars Poeschel 		VPRBRD_USB_TIMEOUT_MS);
2789d5b72deSLars Poeschel 
2799d5b72deSLars Poeschel 	if (ret != sizeof(struct vprbrd_gpiob_msg))
2809d5b72deSLars Poeschel 		return -EREMOTEIO;
2819d5b72deSLars Poeschel 
2829d5b72deSLars Poeschel 	return 0;
2839d5b72deSLars Poeschel }
2849d5b72deSLars Poeschel 
2859d5b72deSLars Poeschel static int vprbrd_gpiob_get(struct gpio_chip *chip,
2869d5b72deSLars Poeschel 		unsigned offset)
2879d5b72deSLars Poeschel {
2889d5b72deSLars Poeschel 	int ret;
2899d5b72deSLars Poeschel 	u16 val;
2909d5b72deSLars Poeschel 	struct vprbrd_gpio *gpio =
2919d5b72deSLars Poeschel 			container_of(chip, struct vprbrd_gpio, gpiob);
2929d5b72deSLars Poeschel 	struct vprbrd *vb = gpio->vb;
2939d5b72deSLars Poeschel 	struct vprbrd_gpiob_msg *gbmsg = (struct vprbrd_gpiob_msg *)vb->buf;
2949d5b72deSLars Poeschel 
2959d5b72deSLars Poeschel 	/* if io is set to output, just return the saved value */
2969d5b72deSLars Poeschel 	if (gpio->gpiob_out & (1 << offset))
2979d5b72deSLars Poeschel 		return gpio->gpiob_val & (1 << offset);
2989d5b72deSLars Poeschel 
2999d5b72deSLars Poeschel 	mutex_lock(&vb->lock);
3009d5b72deSLars Poeschel 
3019d5b72deSLars Poeschel 	ret = usb_control_msg(vb->usb_dev, usb_rcvctrlpipe(vb->usb_dev, 0),
3029d5b72deSLars Poeschel 		VPRBRD_USB_REQUEST_GPIOB, VPRBRD_USB_TYPE_IN, 0x0000,
3039d5b72deSLars Poeschel 		0x0000, gbmsg,	sizeof(struct vprbrd_gpiob_msg),
3049d5b72deSLars Poeschel 		VPRBRD_USB_TIMEOUT_MS);
3059d5b72deSLars Poeschel 	val = gbmsg->val;
3069d5b72deSLars Poeschel 
3079d5b72deSLars Poeschel 	mutex_unlock(&vb->lock);
3089d5b72deSLars Poeschel 
3099d5b72deSLars Poeschel 	if (ret != sizeof(struct vprbrd_gpiob_msg))
3109d5b72deSLars Poeschel 		return ret;
3119d5b72deSLars Poeschel 
3129d5b72deSLars Poeschel 	/* cache the read values */
3139d5b72deSLars Poeschel 	gpio->gpiob_val = be16_to_cpu(val);
3149d5b72deSLars Poeschel 
3159d5b72deSLars Poeschel 	return (gpio->gpiob_val >> offset) & 0x1;
3169d5b72deSLars Poeschel }
3179d5b72deSLars Poeschel 
3189d5b72deSLars Poeschel static void vprbrd_gpiob_set(struct gpio_chip *chip,
3199d5b72deSLars Poeschel 		unsigned offset, int value)
3209d5b72deSLars Poeschel {
3219d5b72deSLars Poeschel 	int ret;
3229d5b72deSLars Poeschel 	struct vprbrd_gpio *gpio =
3239d5b72deSLars Poeschel 			container_of(chip, struct vprbrd_gpio, gpiob);
3249d5b72deSLars Poeschel 	struct vprbrd *vb = gpio->vb;
3259d5b72deSLars Poeschel 	struct vprbrd_gpiob_msg *gbmsg = (struct vprbrd_gpiob_msg *)vb->buf;
3269d5b72deSLars Poeschel 
3279d5b72deSLars Poeschel 	if (gpio->gpiob_out & (1 << offset)) {
3289d5b72deSLars Poeschel 		if (value)
3299d5b72deSLars Poeschel 			gpio->gpiob_val |= (1 << offset);
3309d5b72deSLars Poeschel 		else
3319d5b72deSLars Poeschel 			gpio->gpiob_val &= ~(1 << offset);
3329d5b72deSLars Poeschel 
3339d5b72deSLars Poeschel 		mutex_lock(&vb->lock);
3349d5b72deSLars Poeschel 
3359d5b72deSLars Poeschel 		gbmsg->cmd = VPRBRD_GPIOB_CMD_SETVAL;
3369d5b72deSLars Poeschel 		gbmsg->val = cpu_to_be16(value << offset);
3379d5b72deSLars Poeschel 		gbmsg->mask = cpu_to_be16(0x0001 << offset);
3389d5b72deSLars Poeschel 
3399d5b72deSLars Poeschel 		ret = usb_control_msg(vb->usb_dev,
3409d5b72deSLars Poeschel 			usb_sndctrlpipe(vb->usb_dev, 0),
3419d5b72deSLars Poeschel 			VPRBRD_USB_REQUEST_GPIOB, VPRBRD_USB_TYPE_OUT,
3429d5b72deSLars Poeschel 			0x0000,	0x0000, gbmsg,
3439d5b72deSLars Poeschel 			sizeof(struct vprbrd_gpiob_msg), VPRBRD_USB_TIMEOUT_MS);
3449d5b72deSLars Poeschel 
3459d5b72deSLars Poeschel 		mutex_unlock(&vb->lock);
3469d5b72deSLars Poeschel 
3479d5b72deSLars Poeschel 		if (ret != sizeof(struct vprbrd_gpiob_msg))
34858383c78SLinus Walleij 			dev_err(chip->parent, "usb error setting pin value\n");
3499d5b72deSLars Poeschel 	}
3509d5b72deSLars Poeschel }
3519d5b72deSLars Poeschel 
3529d5b72deSLars Poeschel static int vprbrd_gpiob_direction_input(struct gpio_chip *chip,
3539d5b72deSLars Poeschel 			unsigned offset)
3549d5b72deSLars Poeschel {
3559d5b72deSLars Poeschel 	int ret;
3569d5b72deSLars Poeschel 	struct vprbrd_gpio *gpio =
3579d5b72deSLars Poeschel 			container_of(chip, struct vprbrd_gpio, gpiob);
3589d5b72deSLars Poeschel 	struct vprbrd *vb = gpio->vb;
3599d5b72deSLars Poeschel 
3609d5b72deSLars Poeschel 	gpio->gpiob_out &= ~(1 << offset);
3619d5b72deSLars Poeschel 
3629d5b72deSLars Poeschel 	mutex_lock(&vb->lock);
3639d5b72deSLars Poeschel 
3649d5b72deSLars Poeschel 	ret = vprbrd_gpiob_setdir(vb, offset, 0);
3659d5b72deSLars Poeschel 
3669d5b72deSLars Poeschel 	mutex_unlock(&vb->lock);
3679d5b72deSLars Poeschel 
3689d5b72deSLars Poeschel 	if (ret)
36958383c78SLinus Walleij 		dev_err(chip->parent, "usb error setting pin to input\n");
3709d5b72deSLars Poeschel 
3719d5b72deSLars Poeschel 	return ret;
3729d5b72deSLars Poeschel }
3739d5b72deSLars Poeschel 
3749d5b72deSLars Poeschel static int vprbrd_gpiob_direction_output(struct gpio_chip *chip,
3759d5b72deSLars Poeschel 			unsigned offset, int value)
3769d5b72deSLars Poeschel {
3779d5b72deSLars Poeschel 	int ret;
3789d5b72deSLars Poeschel 	struct vprbrd_gpio *gpio =
3799d5b72deSLars Poeschel 			container_of(chip, struct vprbrd_gpio, gpiob);
3809d5b72deSLars Poeschel 	struct vprbrd *vb = gpio->vb;
3819d5b72deSLars Poeschel 
3829d5b72deSLars Poeschel 	gpio->gpiob_out |= (1 << offset);
3839d5b72deSLars Poeschel 
3849d5b72deSLars Poeschel 	mutex_lock(&vb->lock);
3859d5b72deSLars Poeschel 
3869d5b72deSLars Poeschel 	ret = vprbrd_gpiob_setdir(vb, offset, 1);
3879d5b72deSLars Poeschel 	if (ret)
38858383c78SLinus Walleij 		dev_err(chip->parent, "usb error setting pin to output\n");
3899d5b72deSLars Poeschel 
3909d5b72deSLars Poeschel 	mutex_unlock(&vb->lock);
3919d5b72deSLars Poeschel 
3929d5b72deSLars Poeschel 	vprbrd_gpiob_set(chip, offset, value);
3939d5b72deSLars Poeschel 
3949d5b72deSLars Poeschel 	return ret;
3959d5b72deSLars Poeschel }
3969d5b72deSLars Poeschel 
3979d5b72deSLars Poeschel /* ----- end of gpio b chip ---------------------------------------------- */
3989d5b72deSLars Poeschel 
3990fe763c5SGreg Kroah-Hartman static int vprbrd_gpio_probe(struct platform_device *pdev)
4009d5b72deSLars Poeschel {
4019d5b72deSLars Poeschel 	struct vprbrd *vb = dev_get_drvdata(pdev->dev.parent);
4029d5b72deSLars Poeschel 	struct vprbrd_gpio *vb_gpio;
4039d5b72deSLars Poeschel 	int ret;
4049d5b72deSLars Poeschel 
4059d5b72deSLars Poeschel 	vb_gpio = devm_kzalloc(&pdev->dev, sizeof(*vb_gpio), GFP_KERNEL);
4069d5b72deSLars Poeschel 	if (vb_gpio == NULL)
4079d5b72deSLars Poeschel 		return -ENOMEM;
4089d5b72deSLars Poeschel 
4099d5b72deSLars Poeschel 	vb_gpio->vb = vb;
4109d5b72deSLars Poeschel 	/* registering gpio a */
4119d5b72deSLars Poeschel 	vb_gpio->gpioa.label = "viperboard gpio a";
41258383c78SLinus Walleij 	vb_gpio->gpioa.parent = &pdev->dev;
4139d5b72deSLars Poeschel 	vb_gpio->gpioa.owner = THIS_MODULE;
4149d5b72deSLars Poeschel 	vb_gpio->gpioa.base = -1;
4159d5b72deSLars Poeschel 	vb_gpio->gpioa.ngpio = 16;
4169fb1f39eSLinus Walleij 	vb_gpio->gpioa.can_sleep = true;
4179d5b72deSLars Poeschel 	vb_gpio->gpioa.set = vprbrd_gpioa_set;
4189d5b72deSLars Poeschel 	vb_gpio->gpioa.get = vprbrd_gpioa_get;
4199d5b72deSLars Poeschel 	vb_gpio->gpioa.direction_input = vprbrd_gpioa_direction_input;
4209d5b72deSLars Poeschel 	vb_gpio->gpioa.direction_output = vprbrd_gpioa_direction_output;
4219d5b72deSLars Poeschel 	ret = gpiochip_add(&vb_gpio->gpioa);
4229d5b72deSLars Poeschel 	if (ret < 0) {
42358383c78SLinus Walleij 		dev_err(vb_gpio->gpioa.parent, "could not add gpio a");
4249d5b72deSLars Poeschel 		goto err_gpioa;
4259d5b72deSLars Poeschel 	}
4269d5b72deSLars Poeschel 
4279d5b72deSLars Poeschel 	/* registering gpio b */
4289d5b72deSLars Poeschel 	vb_gpio->gpiob.label = "viperboard gpio b";
42958383c78SLinus Walleij 	vb_gpio->gpiob.parent = &pdev->dev;
4309d5b72deSLars Poeschel 	vb_gpio->gpiob.owner = THIS_MODULE;
4319d5b72deSLars Poeschel 	vb_gpio->gpiob.base = -1;
4329d5b72deSLars Poeschel 	vb_gpio->gpiob.ngpio = 16;
4339fb1f39eSLinus Walleij 	vb_gpio->gpiob.can_sleep = true;
4349d5b72deSLars Poeschel 	vb_gpio->gpiob.set = vprbrd_gpiob_set;
4359d5b72deSLars Poeschel 	vb_gpio->gpiob.get = vprbrd_gpiob_get;
4369d5b72deSLars Poeschel 	vb_gpio->gpiob.direction_input = vprbrd_gpiob_direction_input;
4379d5b72deSLars Poeschel 	vb_gpio->gpiob.direction_output = vprbrd_gpiob_direction_output;
4389d5b72deSLars Poeschel 	ret = gpiochip_add(&vb_gpio->gpiob);
4399d5b72deSLars Poeschel 	if (ret < 0) {
44058383c78SLinus Walleij 		dev_err(vb_gpio->gpiob.parent, "could not add gpio b");
4419d5b72deSLars Poeschel 		goto err_gpiob;
4429d5b72deSLars Poeschel 	}
4439d5b72deSLars Poeschel 
4449d5b72deSLars Poeschel 	platform_set_drvdata(pdev, vb_gpio);
4459d5b72deSLars Poeschel 
4469d5b72deSLars Poeschel 	return ret;
4479d5b72deSLars Poeschel 
4489d5b72deSLars Poeschel err_gpiob:
4499f5132aeSabdoulaye berthe 	gpiochip_remove(&vb_gpio->gpioa);
4509d5b72deSLars Poeschel 
4519d5b72deSLars Poeschel err_gpioa:
4529d5b72deSLars Poeschel 	return ret;
4539d5b72deSLars Poeschel }
4549d5b72deSLars Poeschel 
4550fe763c5SGreg Kroah-Hartman static int vprbrd_gpio_remove(struct platform_device *pdev)
4569d5b72deSLars Poeschel {
4579d5b72deSLars Poeschel 	struct vprbrd_gpio *vb_gpio = platform_get_drvdata(pdev);
4589d5b72deSLars Poeschel 
4599f5132aeSabdoulaye berthe 	gpiochip_remove(&vb_gpio->gpiob);
4609d5b72deSLars Poeschel 
4619f5132aeSabdoulaye berthe 	return 0;
4629d5b72deSLars Poeschel }
4639d5b72deSLars Poeschel 
4649d5b72deSLars Poeschel static struct platform_driver vprbrd_gpio_driver = {
4659d5b72deSLars Poeschel 	.driver.name	= "viperboard-gpio",
4669d5b72deSLars Poeschel 	.driver.owner	= THIS_MODULE,
4679d5b72deSLars Poeschel 	.probe		= vprbrd_gpio_probe,
4680fe763c5SGreg Kroah-Hartman 	.remove		= vprbrd_gpio_remove,
4699d5b72deSLars Poeschel };
4709d5b72deSLars Poeschel 
4719d5b72deSLars Poeschel static int __init vprbrd_gpio_init(void)
4729d5b72deSLars Poeschel {
4739d5b72deSLars Poeschel 	switch (gpioa_freq) {
4749d5b72deSLars Poeschel 	case 1000000:
4759d5b72deSLars Poeschel 		gpioa_clk = VPRBRD_GPIOA_CLK_1MHZ;
4769d5b72deSLars Poeschel 		break;
4779d5b72deSLars Poeschel 	case 100000:
4789d5b72deSLars Poeschel 		gpioa_clk = VPRBRD_GPIOA_CLK_100KHZ;
4799d5b72deSLars Poeschel 		break;
4809d5b72deSLars Poeschel 	case 10000:
4819d5b72deSLars Poeschel 		gpioa_clk = VPRBRD_GPIOA_CLK_10KHZ;
4829d5b72deSLars Poeschel 		break;
4839d5b72deSLars Poeschel 	case 1000:
4849d5b72deSLars Poeschel 		gpioa_clk = VPRBRD_GPIOA_CLK_1KHZ;
4859d5b72deSLars Poeschel 		break;
4869d5b72deSLars Poeschel 	case 100:
4879d5b72deSLars Poeschel 		gpioa_clk = VPRBRD_GPIOA_CLK_100HZ;
4889d5b72deSLars Poeschel 		break;
4899d5b72deSLars Poeschel 	case 10:
4909d5b72deSLars Poeschel 		gpioa_clk = VPRBRD_GPIOA_CLK_10HZ;
4919d5b72deSLars Poeschel 		break;
4929d5b72deSLars Poeschel 	default:
4939d5b72deSLars Poeschel 		pr_warn("invalid gpioa_freq (%d)\n", gpioa_freq);
4949d5b72deSLars Poeschel 		gpioa_clk = VPRBRD_GPIOA_CLK_1KHZ;
4959d5b72deSLars Poeschel 	}
4969d5b72deSLars Poeschel 
4979d5b72deSLars Poeschel 	return platform_driver_register(&vprbrd_gpio_driver);
4989d5b72deSLars Poeschel }
4999d5b72deSLars Poeschel subsys_initcall(vprbrd_gpio_init);
5009d5b72deSLars Poeschel 
5019d5b72deSLars Poeschel static void __exit vprbrd_gpio_exit(void)
5029d5b72deSLars Poeschel {
5039d5b72deSLars Poeschel 	platform_driver_unregister(&vprbrd_gpio_driver);
5049d5b72deSLars Poeschel }
5059d5b72deSLars Poeschel module_exit(vprbrd_gpio_exit);
5069d5b72deSLars Poeschel 
5079d5b72deSLars Poeschel MODULE_AUTHOR("Lars Poeschel <poeschel@lemonage.de>");
5089d5b72deSLars Poeschel MODULE_DESCRIPTION("GPIO driver for Nano River Techs Viperboard");
5099d5b72deSLars Poeschel MODULE_LICENSE("GPL");
5109d5b72deSLars Poeschel MODULE_ALIAS("platform:viperboard-gpio");
511