From: "David H. Lynch Jr" <dh...@dlasys.net> Little Wire is a USB protocol to manage USB IO devices like the Atmel attiny-85 This driver was tested on the Olimexino-85 with 1.13 of Little Wire
Signed-off-by: David Lynch <dh...@dlasys.net> --- drivers/gpio/Kconfig | 6 + drivers/gpio/Makefile | 1 + drivers/gpio/gpio-lw-usb.c | 371 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 378 insertions(+) create mode 100644 drivers/gpio/gpio-lw-usb.c diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 0f04444..5db18e3 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -780,6 +780,12 @@ config GPIO_BCM_KONA comment "USB GPIO expanders:" +config GPIO_LITTLEWIRE + tristate "Little Wire GPIO support" + depends on USB + help + Say yes here to access the GPIO signals of a Little Wire USB Device + config GPIO_VIPERBOARD tristate "Viperboard GPIO a & b support" depends on MFD_VIPERBOARD && USB diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 7971e36..665ae23 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -88,6 +88,7 @@ obj-$(CONFIG_GPIO_TWL6040) += gpio-twl6040.o obj-$(CONFIG_GPIO_TZ1090) += gpio-tz1090.o obj-$(CONFIG_GPIO_TZ1090_PDC) += gpio-tz1090-pdc.o obj-$(CONFIG_GPIO_UCB1400) += gpio-ucb1400.o +obj-$(CONFIG_GPIO_LITTLEWIRE) += gpio-lw-usb.o obj-$(CONFIG_GPIO_VIPERBOARD) += gpio-viperboard.o obj-$(CONFIG_GPIO_VR41XX) += gpio-vr41xx.o obj-$(CONFIG_GPIO_VX855) += gpio-vx855.o diff --git a/drivers/gpio/gpio-lw-usb.c b/drivers/gpio/gpio-lw-usb.c new file mode 100644 index 0000000..f613eab --- /dev/null +++ b/drivers/gpio/gpio-lw-usb.c @@ -0,0 +1,371 @@ +/* + * Little Wire - Little Wire USB device GPIO driver + * Copyright (C) 2013 David Lynch, DLA Systems + * + * tested on Olimexino-85 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/mutex.h> +#include <linux/slab.h> + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/types.h> + +/* include interfaces to usb layer */ +#include <linux/usb.h> +#include <linux/gpio.h> + + +/** + * enum Little Wire Commands + */ +enum { + /* Generic requests */ + LW_ECHO, /* 00 echo test */ + LW_READ, /* 01 read byte (wIndex:address) */ + LW_WRITE, /* 02 write byte (wIndex:address, wValue:value)*/ + LW_CLR, /* 03 clear bit (wIndex:address, wValue:bitno) */ + LW_SET, /* 04 set bit (wIndex:address, wValue:bitno) */ + /* Programming requests */ + LW_POWERUP, /* 05 apply power (wValue:SCK-period, wIndex:RESET) */ + LW_POWERDOWN, /* 06 remove power from chip */ + LW_SPI, /* 07 issue SPI command (wValue:c1c0, wIndex:c3c2) */ + LW_POLL_BYTES, /* 08 set poll bytes for write (wValue:p1p2) */ + LW_FLASH_READ, /* 09 read flash (wIndex:address) */ + LW_FLASH_WRITE, /* 10 write flash (wIndex:address, wValue:timeout) */ + LW_EEPROM_READ, /* 11 read eeprom (wIndex:address) */ + LW_EEPROM_WRITE, /* 12 write eeprom (wIndex:address, wValue:timeout) */ + /* Additional requests - ihsanKehribar */ + + LW_PIN_SET_INPUT, /* 13 */ + LW_PIN_SET_OUTPUT, /* 14 */ + LW_READ_ADC, /* 15 */ + LW_SETUP_PWM, /* 16 */ + LW_UPDATE_PWM_COMPARE, /* 17 */ + LW_PIN_SET_HIGH, /* 18 */ + LW_PIN_SET_LOW, /* 19 */ + LW_PIN_READ, /* 20 */ + LW_SINGLE_SPI, /* 21 */ + LW_CHANGE_PWM_PRESCALE, /* 22 */ + LW_SETUP_SPI, /* 23 */ + LW_SETUP_I2C, /* 24 */ + LW_I2C_BEGIN_TX, /* 25 */ + LW_I2C_ADD_BUFFER, /* 26 */ + LW_I2C_SEND_BUFFER, /* 27 */ + LW_SPI_ADD_BUFFER, /* 28 */ + LW_SPI_SEND_BUFFER, /* 29 */ + LW_I2C_REQUEST_FROM, /* 30 */ + LW_SPI_UPDATE_DELAY, /* 31 */ + LW_STOP_PWM, /* 32 */ + LW_DEBUG_SPI, /* 33 */ + LW_VERSION_QUERY, /* 34 */ +}; + +#define USB_TIMEOUT 2000 +#define GEN_LW_NUMBER_GPIOS 5 +#define LW_USB_TYPE_OUT 0x40 +#define LW_USB_TYPE_IN 0xc0 +/* + * GPIO names + */ +static const char *lw_gpio_names[GEN_LW_NUMBER_GPIOS] = { + "PB0", "PB1", "PB2", "PB3", "PB5" +}; + + +/** + * struct lw_gpio - local driver data for Little Wire device + * @usb_dev: pointer to the USB device + * @interface: pointer to the USB interface + * @gpio_chip: pointer to the gpio lib struct + * @lock: mutex for the device + */ +struct lw_gpio { + struct usb_device *usb_dev; /* the usb device for this device */ + struct usb_interface *interface;/* the interface for this device */ + //struct device *master; + //u8 *buffer; + struct gpio_chip gpio_chip; + struct mutex lock; +}; + +/** + * gpio_to_lw() - derive local information pointer from global information pointer + * @gc: global information pointer. + * + * device a pointer to the local struct lw_gpio from a pointer to the + * global struct gpio_chip + * + * Return: pointer to the local lw_gpio struct. + */ + +static struct lw_gpio * +gpio_to_lw(struct gpio_chip *gc) +{ + return container_of(gc, struct lw_gpio, gpio_chip); +} + +/** + * lw_usb_read() - submit a Little Wire USB read request + * @gc: global information pointer. + * @pin: gpio pin to read. + * @cmd: Little Wire command to read. + * + * Return: first byte of data read. + */ +static int +lw_usb_read(struct gpio_chip *gc, unsigned pin, unsigned cmd) +{ + struct lw_gpio *lwgc = gpio_to_lw(gc); + uint8_t data[8]; + int rc = 0; + + mutex_lock(&lwgc->lock); + rc = usb_control_msg(lwgc->usb_dev, usb_rcvctrlpipe(lwgc->usb_dev, 0), + cmd, LW_USB_TYPE_IN, pin, 0, data, sizeof(data), + USB_TIMEOUT); + mutex_unlock(&lwgc->lock); + if (rc != sizeof(data)) + dev_err(&lwgc->interface->dev, "lw_usb_read error %d\n", rc); + dev_info(&lwgc->interface->dev, "lw_usb_read pin %d = %d\n", pin, + data[0]); + + return data[0]; +} + +/** + * lw_usb_write() - submit a Little Wire USB write request + * @gc: global information pointer. + * @pin: gpio pin to write. + * @cmd: Little Wire command to write. + * + * Return: first byte of data read. + */ +static int +lw_usb_write(struct gpio_chip *gc, unsigned pin, unsigned cmd) +{ + struct lw_gpio *lwgc = gpio_to_lw(gc); + uint8_t data[8]; + int rc = 0; + + mutex_lock(&lwgc->lock); + rc = usb_control_msg(lwgc->usb_dev, usb_sndctrlpipe(lwgc->usb_dev, 0), + cmd, LW_USB_TYPE_OUT, pin, 0, data, sizeof(data), + USB_TIMEOUT); + mutex_unlock(&lwgc->lock); + if (rc != sizeof(data)) + dev_err(&lwgc->interface->dev, "lw_usb_write error %d\n", rc); + dev_info(&lwgc->interface->dev, "lw_usb_write pin %d = %d\n", pin, + data[0]); + + return data[0]; +} + +/** + * lw_get_value() - read a gpio pin value + * @gc: global information pointer. + * @pin: gpio pin to read. + * + * Return: pin state + */ +static int +lw_get_value(struct gpio_chip *gc, unsigned pin) +{ + return lw_usb_read(gc, pin, LW_PIN_READ); +} + +/** + * lw_set_value() - write a gpio pin value + * @gc: global information pointer. + * @pin: gpio pin to write. + * + * Return: none + */ +static void +lw_set_value(struct gpio_chip *gc, unsigned pin, int state) +{ + int rc; + + if (state) { + rc = lw_usb_write(gc, pin, LW_PIN_SET_HIGH); + return; + } + rc = lw_usb_write(gc, pin, LW_PIN_SET_LOW); + return; +} + +/** + * lw_direction_input() - set pin to input + * @gc: global information pointer. + * @pin: gpio pin to make input. + * + * Return: success + */ +static int +lw_direction_input(struct gpio_chip *gc, unsigned pin) +{ + lw_usb_write(gc, pin, LW_PIN_SET_INPUT); + return 0; +} + +/** + * lw_direction_output() - set pin to output + * @gc: global information pointer. + * @pin: gpio pin to make output. + * + * Return: success + */ +static int +lw_direction_output(struct gpio_chip *gc, unsigned pin, int val) +{ + lw_usb_write(gc, pin, LW_PIN_SET_OUTPUT); + return 0; +} + +/* ----- begin of usb layer ---------------------------------------------- */ + +/* + * Initially the usb interface uses a vid/pid pair donated by + * Future Technology Devices International Ltd., later a pair was + * bought from EZPrototypes + */ +static const struct usb_device_id lw_usb_table[] = { + {USB_DEVICE(0x1781, 0x0c9f)}, /* USB ATTINY85 */ + {} /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, lw_usb_table); + +/** + * lw_usb_free() - release the USB device + * @lwgc: local information pointer. + * + * Return: none + */ +static void +lw_usb_free(struct lw_gpio *lwgc) +{ + usb_put_dev(lwgc->usb_dev); + kfree(lwgc); +} + +/** + * lw_usb_probe() - check for device presence and if so setup driver + * @interface: pointer to usb interface to check + * @id: usb id to match + * + * Return: 0 on SUCCESS + */ +static int +lw_usb_probe(struct usb_interface *interface, const struct usb_device_id *id) +{ + struct lw_gpio *lwgc; + int rc = -ENOMEM; + u16 version; + + dev_dbg(&interface->dev, "probing usb device\n"); + + /* allocate memory for our device state and initialize it */ + lwgc = kzalloc(sizeof(struct lw_gpio), GFP_KERNEL); + if (lwgc == NULL) { + dev_err(&interface->dev, "Out of memory\n"); + goto error; + } + + lwgc->usb_dev = usb_get_dev(interface_to_usbdev(interface)); + lwgc->interface = interface; + + /* save our data pointer in this interface device */ + usb_set_intfdata(interface, lwgc); + + version = le16_to_cpu(lwgc->usb_dev->descriptor.bcdDevice); + dev_info(&interface->dev, + "version %x.%02x found at bus %03d address %03d\n", + version >> 8, version & 0xff, + lwgc->usb_dev->bus->busnum, lwgc->usb_dev->devnum); + + + mutex_init(&lwgc->lock); + lwgc->gpio_chip.direction_input = lw_direction_input; + lwgc->gpio_chip.direction_output = lw_direction_output; + lwgc->gpio_chip.get = lw_get_value; + lwgc->gpio_chip.set = lw_set_value; + lwgc->gpio_chip.ngpio = GEN_LW_NUMBER_GPIOS; + lwgc->gpio_chip.names = lw_gpio_names, lwgc->gpio_chip.can_sleep = 1; + lwgc->gpio_chip.owner = THIS_MODULE; + rc = gpiochip_add(&lwgc->gpio_chip); + + if (rc) { + dev_err(&interface->dev, "Failed writing: %d\n", rc); + goto error; + } + + /* this should be get little wire version */ + rc = lw_usb_read(&lwgc->gpio_chip, 0, 34); + dev_info(&interface->dev, "LittleWire %d.%02d\n", (rc >> 4) & 0xf, + rc & 0xf); + dev_info(&interface->dev, "LittleWire %x\n", rc); + + /* inform user about successful attachment to gpio layer */ + dev_info(&interface->dev, "connected gpio-lw-usb device\n"); + + return 0; + + error: + if (lwgc) { + mutex_destroy(&lwgc->lock); + lw_usb_free(lwgc); + } + return rc; +} + +/** + * lw_usb_disconnect() - disconnect driver from USB + * @interface: USB interface being disconnected from + * + * Return: none + */ +static void +lw_usb_disconnect(struct usb_interface *interface) +{ + struct lw_gpio *lwgc = usb_get_intfdata(interface); + int rc; + + if (lwgc == NULL) + return; + + rc = gpiochip_remove(&lwgc->gpio_chip); + if (!rc) + mutex_destroy(&lwgc->lock); + else + dev_err(&interface->dev, + "Failed to remove the GPIO controller: %d\n", rc); + + usb_set_intfdata(interface, NULL); + lw_usb_free(lwgc); + + dev_dbg(&interface->dev, "disconnected\n"); +} + +static struct usb_driver lw_usb_driver = { + .name = "gpio-lw-usb", + .probe = lw_usb_probe, + .disconnect = lw_usb_disconnect, + .id_table = lw_usb_table, +}; + +module_usb_driver(lw_usb_driver); +/* ----- end of usb layer ------------------------------------------------ */ + +MODULE_AUTHOR("David Lynch"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("GPIO driver for Little Wire"); -- 1.7.10.4 -- To unsubscribe from this list: send the line "unsubscribe linux-gpio" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html