18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Nano River Technologies viperboard GPIO lib driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * (C) 2012 by Lemonage GmbH 68c2ecf20Sopenharmony_ci * Author: Lars Poeschel <poeschel@lemonage.de> 78c2ecf20Sopenharmony_ci * All rights reserved. 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/errno.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci#include <linux/types.h> 158c2ecf20Sopenharmony_ci#include <linux/mutex.h> 168c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 178c2ecf20Sopenharmony_ci#include <linux/usb.h> 188c2ecf20Sopenharmony_ci#include <linux/gpio/driver.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <linux/mfd/viperboard.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define VPRBRD_GPIOA_CLK_1MHZ 0 238c2ecf20Sopenharmony_ci#define VPRBRD_GPIOA_CLK_100KHZ 1 248c2ecf20Sopenharmony_ci#define VPRBRD_GPIOA_CLK_10KHZ 2 258c2ecf20Sopenharmony_ci#define VPRBRD_GPIOA_CLK_1KHZ 3 268c2ecf20Sopenharmony_ci#define VPRBRD_GPIOA_CLK_100HZ 4 278c2ecf20Sopenharmony_ci#define VPRBRD_GPIOA_CLK_10HZ 5 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define VPRBRD_GPIOA_FREQ_DEFAULT 1000 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define VPRBRD_GPIOA_CMD_CONT 0x00 328c2ecf20Sopenharmony_ci#define VPRBRD_GPIOA_CMD_PULSE 0x01 338c2ecf20Sopenharmony_ci#define VPRBRD_GPIOA_CMD_PWM 0x02 348c2ecf20Sopenharmony_ci#define VPRBRD_GPIOA_CMD_SETOUT 0x03 358c2ecf20Sopenharmony_ci#define VPRBRD_GPIOA_CMD_SETIN 0x04 368c2ecf20Sopenharmony_ci#define VPRBRD_GPIOA_CMD_SETINT 0x05 378c2ecf20Sopenharmony_ci#define VPRBRD_GPIOA_CMD_GETIN 0x06 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#define VPRBRD_GPIOB_CMD_SETDIR 0x00 408c2ecf20Sopenharmony_ci#define VPRBRD_GPIOB_CMD_SETVAL 0x01 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistruct vprbrd_gpioa_msg { 438c2ecf20Sopenharmony_ci u8 cmd; 448c2ecf20Sopenharmony_ci u8 clk; 458c2ecf20Sopenharmony_ci u8 offset; 468c2ecf20Sopenharmony_ci u8 t1; 478c2ecf20Sopenharmony_ci u8 t2; 488c2ecf20Sopenharmony_ci u8 invert; 498c2ecf20Sopenharmony_ci u8 pwmlevel; 508c2ecf20Sopenharmony_ci u8 outval; 518c2ecf20Sopenharmony_ci u8 risefall; 528c2ecf20Sopenharmony_ci u8 answer; 538c2ecf20Sopenharmony_ci u8 __fill; 548c2ecf20Sopenharmony_ci} __packed; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistruct vprbrd_gpiob_msg { 578c2ecf20Sopenharmony_ci u8 cmd; 588c2ecf20Sopenharmony_ci u16 val; 598c2ecf20Sopenharmony_ci u16 mask; 608c2ecf20Sopenharmony_ci} __packed; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistruct vprbrd_gpio { 638c2ecf20Sopenharmony_ci struct gpio_chip gpioa; /* gpio a related things */ 648c2ecf20Sopenharmony_ci u32 gpioa_out; 658c2ecf20Sopenharmony_ci u32 gpioa_val; 668c2ecf20Sopenharmony_ci struct gpio_chip gpiob; /* gpio b related things */ 678c2ecf20Sopenharmony_ci u32 gpiob_out; 688c2ecf20Sopenharmony_ci u32 gpiob_val; 698c2ecf20Sopenharmony_ci struct vprbrd *vb; 708c2ecf20Sopenharmony_ci}; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci/* gpioa sampling clock module parameter */ 738c2ecf20Sopenharmony_cistatic unsigned char gpioa_clk; 748c2ecf20Sopenharmony_cistatic unsigned int gpioa_freq = VPRBRD_GPIOA_FREQ_DEFAULT; 758c2ecf20Sopenharmony_cimodule_param(gpioa_freq, uint, 0); 768c2ecf20Sopenharmony_ciMODULE_PARM_DESC(gpioa_freq, 778c2ecf20Sopenharmony_ci "gpio-a sampling freq in Hz (default is 1000Hz) valid values: 10, 100, 1000, 10000, 100000, 1000000"); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci/* ----- begin of gipo a chip -------------------------------------------- */ 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic int vprbrd_gpioa_get(struct gpio_chip *chip, 828c2ecf20Sopenharmony_ci unsigned int offset) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci int ret, answer, error = 0; 858c2ecf20Sopenharmony_ci struct vprbrd_gpio *gpio = gpiochip_get_data(chip); 868c2ecf20Sopenharmony_ci struct vprbrd *vb = gpio->vb; 878c2ecf20Sopenharmony_ci struct vprbrd_gpioa_msg *gamsg = (struct vprbrd_gpioa_msg *)vb->buf; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci /* if io is set to output, just return the saved value */ 908c2ecf20Sopenharmony_ci if (gpio->gpioa_out & (1 << offset)) 918c2ecf20Sopenharmony_ci return !!(gpio->gpioa_val & (1 << offset)); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci mutex_lock(&vb->lock); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci gamsg->cmd = VPRBRD_GPIOA_CMD_GETIN; 968c2ecf20Sopenharmony_ci gamsg->clk = 0x00; 978c2ecf20Sopenharmony_ci gamsg->offset = offset; 988c2ecf20Sopenharmony_ci gamsg->t1 = 0x00; 998c2ecf20Sopenharmony_ci gamsg->t2 = 0x00; 1008c2ecf20Sopenharmony_ci gamsg->invert = 0x00; 1018c2ecf20Sopenharmony_ci gamsg->pwmlevel = 0x00; 1028c2ecf20Sopenharmony_ci gamsg->outval = 0x00; 1038c2ecf20Sopenharmony_ci gamsg->risefall = 0x00; 1048c2ecf20Sopenharmony_ci gamsg->answer = 0x00; 1058c2ecf20Sopenharmony_ci gamsg->__fill = 0x00; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci ret = usb_control_msg(vb->usb_dev, usb_sndctrlpipe(vb->usb_dev, 0), 1088c2ecf20Sopenharmony_ci VPRBRD_USB_REQUEST_GPIOA, VPRBRD_USB_TYPE_OUT, 0x0000, 1098c2ecf20Sopenharmony_ci 0x0000, gamsg, sizeof(struct vprbrd_gpioa_msg), 1108c2ecf20Sopenharmony_ci VPRBRD_USB_TIMEOUT_MS); 1118c2ecf20Sopenharmony_ci if (ret != sizeof(struct vprbrd_gpioa_msg)) 1128c2ecf20Sopenharmony_ci error = -EREMOTEIO; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci ret = usb_control_msg(vb->usb_dev, usb_rcvctrlpipe(vb->usb_dev, 0), 1158c2ecf20Sopenharmony_ci VPRBRD_USB_REQUEST_GPIOA, VPRBRD_USB_TYPE_IN, 0x0000, 1168c2ecf20Sopenharmony_ci 0x0000, gamsg, sizeof(struct vprbrd_gpioa_msg), 1178c2ecf20Sopenharmony_ci VPRBRD_USB_TIMEOUT_MS); 1188c2ecf20Sopenharmony_ci answer = gamsg->answer & 0x01; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci mutex_unlock(&vb->lock); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci if (ret != sizeof(struct vprbrd_gpioa_msg)) 1238c2ecf20Sopenharmony_ci error = -EREMOTEIO; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci if (error) 1268c2ecf20Sopenharmony_ci return error; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci return answer; 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic void vprbrd_gpioa_set(struct gpio_chip *chip, 1328c2ecf20Sopenharmony_ci unsigned int offset, int value) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci int ret; 1358c2ecf20Sopenharmony_ci struct vprbrd_gpio *gpio = gpiochip_get_data(chip); 1368c2ecf20Sopenharmony_ci struct vprbrd *vb = gpio->vb; 1378c2ecf20Sopenharmony_ci struct vprbrd_gpioa_msg *gamsg = (struct vprbrd_gpioa_msg *)vb->buf; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci if (gpio->gpioa_out & (1 << offset)) { 1408c2ecf20Sopenharmony_ci if (value) 1418c2ecf20Sopenharmony_ci gpio->gpioa_val |= (1 << offset); 1428c2ecf20Sopenharmony_ci else 1438c2ecf20Sopenharmony_ci gpio->gpioa_val &= ~(1 << offset); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci mutex_lock(&vb->lock); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci gamsg->cmd = VPRBRD_GPIOA_CMD_SETOUT; 1488c2ecf20Sopenharmony_ci gamsg->clk = 0x00; 1498c2ecf20Sopenharmony_ci gamsg->offset = offset; 1508c2ecf20Sopenharmony_ci gamsg->t1 = 0x00; 1518c2ecf20Sopenharmony_ci gamsg->t2 = 0x00; 1528c2ecf20Sopenharmony_ci gamsg->invert = 0x00; 1538c2ecf20Sopenharmony_ci gamsg->pwmlevel = 0x00; 1548c2ecf20Sopenharmony_ci gamsg->outval = value; 1558c2ecf20Sopenharmony_ci gamsg->risefall = 0x00; 1568c2ecf20Sopenharmony_ci gamsg->answer = 0x00; 1578c2ecf20Sopenharmony_ci gamsg->__fill = 0x00; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci ret = usb_control_msg(vb->usb_dev, 1608c2ecf20Sopenharmony_ci usb_sndctrlpipe(vb->usb_dev, 0), 1618c2ecf20Sopenharmony_ci VPRBRD_USB_REQUEST_GPIOA, VPRBRD_USB_TYPE_OUT, 1628c2ecf20Sopenharmony_ci 0x0000, 0x0000, gamsg, 1638c2ecf20Sopenharmony_ci sizeof(struct vprbrd_gpioa_msg), VPRBRD_USB_TIMEOUT_MS); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci mutex_unlock(&vb->lock); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci if (ret != sizeof(struct vprbrd_gpioa_msg)) 1688c2ecf20Sopenharmony_ci dev_err(chip->parent, "usb error setting pin value\n"); 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic int vprbrd_gpioa_direction_input(struct gpio_chip *chip, 1738c2ecf20Sopenharmony_ci unsigned int offset) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci int ret; 1768c2ecf20Sopenharmony_ci struct vprbrd_gpio *gpio = gpiochip_get_data(chip); 1778c2ecf20Sopenharmony_ci struct vprbrd *vb = gpio->vb; 1788c2ecf20Sopenharmony_ci struct vprbrd_gpioa_msg *gamsg = (struct vprbrd_gpioa_msg *)vb->buf; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci gpio->gpioa_out &= ~(1 << offset); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci mutex_lock(&vb->lock); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci gamsg->cmd = VPRBRD_GPIOA_CMD_SETIN; 1858c2ecf20Sopenharmony_ci gamsg->clk = gpioa_clk; 1868c2ecf20Sopenharmony_ci gamsg->offset = offset; 1878c2ecf20Sopenharmony_ci gamsg->t1 = 0x00; 1888c2ecf20Sopenharmony_ci gamsg->t2 = 0x00; 1898c2ecf20Sopenharmony_ci gamsg->invert = 0x00; 1908c2ecf20Sopenharmony_ci gamsg->pwmlevel = 0x00; 1918c2ecf20Sopenharmony_ci gamsg->outval = 0x00; 1928c2ecf20Sopenharmony_ci gamsg->risefall = 0x00; 1938c2ecf20Sopenharmony_ci gamsg->answer = 0x00; 1948c2ecf20Sopenharmony_ci gamsg->__fill = 0x00; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci ret = usb_control_msg(vb->usb_dev, usb_sndctrlpipe(vb->usb_dev, 0), 1978c2ecf20Sopenharmony_ci VPRBRD_USB_REQUEST_GPIOA, VPRBRD_USB_TYPE_OUT, 0x0000, 1988c2ecf20Sopenharmony_ci 0x0000, gamsg, sizeof(struct vprbrd_gpioa_msg), 1998c2ecf20Sopenharmony_ci VPRBRD_USB_TIMEOUT_MS); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci mutex_unlock(&vb->lock); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci if (ret != sizeof(struct vprbrd_gpioa_msg)) 2048c2ecf20Sopenharmony_ci return -EREMOTEIO; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci return 0; 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cistatic int vprbrd_gpioa_direction_output(struct gpio_chip *chip, 2108c2ecf20Sopenharmony_ci unsigned int offset, int value) 2118c2ecf20Sopenharmony_ci{ 2128c2ecf20Sopenharmony_ci int ret; 2138c2ecf20Sopenharmony_ci struct vprbrd_gpio *gpio = gpiochip_get_data(chip); 2148c2ecf20Sopenharmony_ci struct vprbrd *vb = gpio->vb; 2158c2ecf20Sopenharmony_ci struct vprbrd_gpioa_msg *gamsg = (struct vprbrd_gpioa_msg *)vb->buf; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci gpio->gpioa_out |= (1 << offset); 2188c2ecf20Sopenharmony_ci if (value) 2198c2ecf20Sopenharmony_ci gpio->gpioa_val |= (1 << offset); 2208c2ecf20Sopenharmony_ci else 2218c2ecf20Sopenharmony_ci gpio->gpioa_val &= ~(1 << offset); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci mutex_lock(&vb->lock); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci gamsg->cmd = VPRBRD_GPIOA_CMD_SETOUT; 2268c2ecf20Sopenharmony_ci gamsg->clk = 0x00; 2278c2ecf20Sopenharmony_ci gamsg->offset = offset; 2288c2ecf20Sopenharmony_ci gamsg->t1 = 0x00; 2298c2ecf20Sopenharmony_ci gamsg->t2 = 0x00; 2308c2ecf20Sopenharmony_ci gamsg->invert = 0x00; 2318c2ecf20Sopenharmony_ci gamsg->pwmlevel = 0x00; 2328c2ecf20Sopenharmony_ci gamsg->outval = value; 2338c2ecf20Sopenharmony_ci gamsg->risefall = 0x00; 2348c2ecf20Sopenharmony_ci gamsg->answer = 0x00; 2358c2ecf20Sopenharmony_ci gamsg->__fill = 0x00; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci ret = usb_control_msg(vb->usb_dev, usb_sndctrlpipe(vb->usb_dev, 0), 2388c2ecf20Sopenharmony_ci VPRBRD_USB_REQUEST_GPIOA, VPRBRD_USB_TYPE_OUT, 0x0000, 2398c2ecf20Sopenharmony_ci 0x0000, gamsg, sizeof(struct vprbrd_gpioa_msg), 2408c2ecf20Sopenharmony_ci VPRBRD_USB_TIMEOUT_MS); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci mutex_unlock(&vb->lock); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci if (ret != sizeof(struct vprbrd_gpioa_msg)) 2458c2ecf20Sopenharmony_ci return -EREMOTEIO; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci return 0; 2488c2ecf20Sopenharmony_ci} 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci/* ----- end of gpio a chip ---------------------------------------------- */ 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci/* ----- begin of gipo b chip -------------------------------------------- */ 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_cistatic int vprbrd_gpiob_setdir(struct vprbrd *vb, unsigned int offset, 2558c2ecf20Sopenharmony_ci unsigned int dir) 2568c2ecf20Sopenharmony_ci{ 2578c2ecf20Sopenharmony_ci struct vprbrd_gpiob_msg *gbmsg = (struct vprbrd_gpiob_msg *)vb->buf; 2588c2ecf20Sopenharmony_ci int ret; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci gbmsg->cmd = VPRBRD_GPIOB_CMD_SETDIR; 2618c2ecf20Sopenharmony_ci gbmsg->val = cpu_to_be16(dir << offset); 2628c2ecf20Sopenharmony_ci gbmsg->mask = cpu_to_be16(0x0001 << offset); 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci ret = usb_control_msg(vb->usb_dev, usb_sndctrlpipe(vb->usb_dev, 0), 2658c2ecf20Sopenharmony_ci VPRBRD_USB_REQUEST_GPIOB, VPRBRD_USB_TYPE_OUT, 0x0000, 2668c2ecf20Sopenharmony_ci 0x0000, gbmsg, sizeof(struct vprbrd_gpiob_msg), 2678c2ecf20Sopenharmony_ci VPRBRD_USB_TIMEOUT_MS); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci if (ret != sizeof(struct vprbrd_gpiob_msg)) 2708c2ecf20Sopenharmony_ci return -EREMOTEIO; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci return 0; 2738c2ecf20Sopenharmony_ci} 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_cistatic int vprbrd_gpiob_get(struct gpio_chip *chip, 2768c2ecf20Sopenharmony_ci unsigned int offset) 2778c2ecf20Sopenharmony_ci{ 2788c2ecf20Sopenharmony_ci int ret; 2798c2ecf20Sopenharmony_ci u16 val; 2808c2ecf20Sopenharmony_ci struct vprbrd_gpio *gpio = gpiochip_get_data(chip); 2818c2ecf20Sopenharmony_ci struct vprbrd *vb = gpio->vb; 2828c2ecf20Sopenharmony_ci struct vprbrd_gpiob_msg *gbmsg = (struct vprbrd_gpiob_msg *)vb->buf; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci /* if io is set to output, just return the saved value */ 2858c2ecf20Sopenharmony_ci if (gpio->gpiob_out & (1 << offset)) 2868c2ecf20Sopenharmony_ci return gpio->gpiob_val & (1 << offset); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci mutex_lock(&vb->lock); 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci ret = usb_control_msg(vb->usb_dev, usb_rcvctrlpipe(vb->usb_dev, 0), 2918c2ecf20Sopenharmony_ci VPRBRD_USB_REQUEST_GPIOB, VPRBRD_USB_TYPE_IN, 0x0000, 2928c2ecf20Sopenharmony_ci 0x0000, gbmsg, sizeof(struct vprbrd_gpiob_msg), 2938c2ecf20Sopenharmony_ci VPRBRD_USB_TIMEOUT_MS); 2948c2ecf20Sopenharmony_ci val = gbmsg->val; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci mutex_unlock(&vb->lock); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci if (ret != sizeof(struct vprbrd_gpiob_msg)) 2998c2ecf20Sopenharmony_ci return ret; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci /* cache the read values */ 3028c2ecf20Sopenharmony_ci gpio->gpiob_val = be16_to_cpu(val); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci return (gpio->gpiob_val >> offset) & 0x1; 3058c2ecf20Sopenharmony_ci} 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_cistatic void vprbrd_gpiob_set(struct gpio_chip *chip, 3088c2ecf20Sopenharmony_ci unsigned int offset, int value) 3098c2ecf20Sopenharmony_ci{ 3108c2ecf20Sopenharmony_ci int ret; 3118c2ecf20Sopenharmony_ci struct vprbrd_gpio *gpio = gpiochip_get_data(chip); 3128c2ecf20Sopenharmony_ci struct vprbrd *vb = gpio->vb; 3138c2ecf20Sopenharmony_ci struct vprbrd_gpiob_msg *gbmsg = (struct vprbrd_gpiob_msg *)vb->buf; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci if (gpio->gpiob_out & (1 << offset)) { 3168c2ecf20Sopenharmony_ci if (value) 3178c2ecf20Sopenharmony_ci gpio->gpiob_val |= (1 << offset); 3188c2ecf20Sopenharmony_ci else 3198c2ecf20Sopenharmony_ci gpio->gpiob_val &= ~(1 << offset); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci mutex_lock(&vb->lock); 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci gbmsg->cmd = VPRBRD_GPIOB_CMD_SETVAL; 3248c2ecf20Sopenharmony_ci gbmsg->val = cpu_to_be16(value << offset); 3258c2ecf20Sopenharmony_ci gbmsg->mask = cpu_to_be16(0x0001 << offset); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci ret = usb_control_msg(vb->usb_dev, 3288c2ecf20Sopenharmony_ci usb_sndctrlpipe(vb->usb_dev, 0), 3298c2ecf20Sopenharmony_ci VPRBRD_USB_REQUEST_GPIOB, VPRBRD_USB_TYPE_OUT, 3308c2ecf20Sopenharmony_ci 0x0000, 0x0000, gbmsg, 3318c2ecf20Sopenharmony_ci sizeof(struct vprbrd_gpiob_msg), VPRBRD_USB_TIMEOUT_MS); 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci mutex_unlock(&vb->lock); 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci if (ret != sizeof(struct vprbrd_gpiob_msg)) 3368c2ecf20Sopenharmony_ci dev_err(chip->parent, "usb error setting pin value\n"); 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci} 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_cistatic int vprbrd_gpiob_direction_input(struct gpio_chip *chip, 3418c2ecf20Sopenharmony_ci unsigned int offset) 3428c2ecf20Sopenharmony_ci{ 3438c2ecf20Sopenharmony_ci int ret; 3448c2ecf20Sopenharmony_ci struct vprbrd_gpio *gpio = gpiochip_get_data(chip); 3458c2ecf20Sopenharmony_ci struct vprbrd *vb = gpio->vb; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci gpio->gpiob_out &= ~(1 << offset); 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci mutex_lock(&vb->lock); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci ret = vprbrd_gpiob_setdir(vb, offset, 0); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci mutex_unlock(&vb->lock); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci if (ret) 3568c2ecf20Sopenharmony_ci dev_err(chip->parent, "usb error setting pin to input\n"); 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci return ret; 3598c2ecf20Sopenharmony_ci} 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_cistatic int vprbrd_gpiob_direction_output(struct gpio_chip *chip, 3628c2ecf20Sopenharmony_ci unsigned int offset, int value) 3638c2ecf20Sopenharmony_ci{ 3648c2ecf20Sopenharmony_ci int ret; 3658c2ecf20Sopenharmony_ci struct vprbrd_gpio *gpio = gpiochip_get_data(chip); 3668c2ecf20Sopenharmony_ci struct vprbrd *vb = gpio->vb; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci gpio->gpiob_out |= (1 << offset); 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci mutex_lock(&vb->lock); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci ret = vprbrd_gpiob_setdir(vb, offset, 1); 3738c2ecf20Sopenharmony_ci if (ret) 3748c2ecf20Sopenharmony_ci dev_err(chip->parent, "usb error setting pin to output\n"); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci mutex_unlock(&vb->lock); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci vprbrd_gpiob_set(chip, offset, value); 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci return ret; 3818c2ecf20Sopenharmony_ci} 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci/* ----- end of gpio b chip ---------------------------------------------- */ 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_cistatic int vprbrd_gpio_probe(struct platform_device *pdev) 3868c2ecf20Sopenharmony_ci{ 3878c2ecf20Sopenharmony_ci struct vprbrd *vb = dev_get_drvdata(pdev->dev.parent); 3888c2ecf20Sopenharmony_ci struct vprbrd_gpio *vb_gpio; 3898c2ecf20Sopenharmony_ci int ret; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci vb_gpio = devm_kzalloc(&pdev->dev, sizeof(*vb_gpio), GFP_KERNEL); 3928c2ecf20Sopenharmony_ci if (vb_gpio == NULL) 3938c2ecf20Sopenharmony_ci return -ENOMEM; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci vb_gpio->vb = vb; 3968c2ecf20Sopenharmony_ci /* registering gpio a */ 3978c2ecf20Sopenharmony_ci vb_gpio->gpioa.label = "viperboard gpio a"; 3988c2ecf20Sopenharmony_ci vb_gpio->gpioa.parent = &pdev->dev; 3998c2ecf20Sopenharmony_ci vb_gpio->gpioa.owner = THIS_MODULE; 4008c2ecf20Sopenharmony_ci vb_gpio->gpioa.base = -1; 4018c2ecf20Sopenharmony_ci vb_gpio->gpioa.ngpio = 16; 4028c2ecf20Sopenharmony_ci vb_gpio->gpioa.can_sleep = true; 4038c2ecf20Sopenharmony_ci vb_gpio->gpioa.set = vprbrd_gpioa_set; 4048c2ecf20Sopenharmony_ci vb_gpio->gpioa.get = vprbrd_gpioa_get; 4058c2ecf20Sopenharmony_ci vb_gpio->gpioa.direction_input = vprbrd_gpioa_direction_input; 4068c2ecf20Sopenharmony_ci vb_gpio->gpioa.direction_output = vprbrd_gpioa_direction_output; 4078c2ecf20Sopenharmony_ci ret = devm_gpiochip_add_data(&pdev->dev, &vb_gpio->gpioa, vb_gpio); 4088c2ecf20Sopenharmony_ci if (ret < 0) { 4098c2ecf20Sopenharmony_ci dev_err(vb_gpio->gpioa.parent, "could not add gpio a"); 4108c2ecf20Sopenharmony_ci return ret; 4118c2ecf20Sopenharmony_ci } 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci /* registering gpio b */ 4148c2ecf20Sopenharmony_ci vb_gpio->gpiob.label = "viperboard gpio b"; 4158c2ecf20Sopenharmony_ci vb_gpio->gpiob.parent = &pdev->dev; 4168c2ecf20Sopenharmony_ci vb_gpio->gpiob.owner = THIS_MODULE; 4178c2ecf20Sopenharmony_ci vb_gpio->gpiob.base = -1; 4188c2ecf20Sopenharmony_ci vb_gpio->gpiob.ngpio = 16; 4198c2ecf20Sopenharmony_ci vb_gpio->gpiob.can_sleep = true; 4208c2ecf20Sopenharmony_ci vb_gpio->gpiob.set = vprbrd_gpiob_set; 4218c2ecf20Sopenharmony_ci vb_gpio->gpiob.get = vprbrd_gpiob_get; 4228c2ecf20Sopenharmony_ci vb_gpio->gpiob.direction_input = vprbrd_gpiob_direction_input; 4238c2ecf20Sopenharmony_ci vb_gpio->gpiob.direction_output = vprbrd_gpiob_direction_output; 4248c2ecf20Sopenharmony_ci ret = devm_gpiochip_add_data(&pdev->dev, &vb_gpio->gpiob, vb_gpio); 4258c2ecf20Sopenharmony_ci if (ret < 0) { 4268c2ecf20Sopenharmony_ci dev_err(vb_gpio->gpiob.parent, "could not add gpio b"); 4278c2ecf20Sopenharmony_ci return ret; 4288c2ecf20Sopenharmony_ci } 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, vb_gpio); 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci return ret; 4338c2ecf20Sopenharmony_ci} 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_cistatic struct platform_driver vprbrd_gpio_driver = { 4368c2ecf20Sopenharmony_ci .driver.name = "viperboard-gpio", 4378c2ecf20Sopenharmony_ci .probe = vprbrd_gpio_probe, 4388c2ecf20Sopenharmony_ci}; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_cistatic int __init vprbrd_gpio_init(void) 4418c2ecf20Sopenharmony_ci{ 4428c2ecf20Sopenharmony_ci switch (gpioa_freq) { 4438c2ecf20Sopenharmony_ci case 1000000: 4448c2ecf20Sopenharmony_ci gpioa_clk = VPRBRD_GPIOA_CLK_1MHZ; 4458c2ecf20Sopenharmony_ci break; 4468c2ecf20Sopenharmony_ci case 100000: 4478c2ecf20Sopenharmony_ci gpioa_clk = VPRBRD_GPIOA_CLK_100KHZ; 4488c2ecf20Sopenharmony_ci break; 4498c2ecf20Sopenharmony_ci case 10000: 4508c2ecf20Sopenharmony_ci gpioa_clk = VPRBRD_GPIOA_CLK_10KHZ; 4518c2ecf20Sopenharmony_ci break; 4528c2ecf20Sopenharmony_ci case 1000: 4538c2ecf20Sopenharmony_ci gpioa_clk = VPRBRD_GPIOA_CLK_1KHZ; 4548c2ecf20Sopenharmony_ci break; 4558c2ecf20Sopenharmony_ci case 100: 4568c2ecf20Sopenharmony_ci gpioa_clk = VPRBRD_GPIOA_CLK_100HZ; 4578c2ecf20Sopenharmony_ci break; 4588c2ecf20Sopenharmony_ci case 10: 4598c2ecf20Sopenharmony_ci gpioa_clk = VPRBRD_GPIOA_CLK_10HZ; 4608c2ecf20Sopenharmony_ci break; 4618c2ecf20Sopenharmony_ci default: 4628c2ecf20Sopenharmony_ci pr_warn("invalid gpioa_freq (%d)\n", gpioa_freq); 4638c2ecf20Sopenharmony_ci gpioa_clk = VPRBRD_GPIOA_CLK_1KHZ; 4648c2ecf20Sopenharmony_ci } 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci return platform_driver_register(&vprbrd_gpio_driver); 4678c2ecf20Sopenharmony_ci} 4688c2ecf20Sopenharmony_cisubsys_initcall(vprbrd_gpio_init); 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_cistatic void __exit vprbrd_gpio_exit(void) 4718c2ecf20Sopenharmony_ci{ 4728c2ecf20Sopenharmony_ci platform_driver_unregister(&vprbrd_gpio_driver); 4738c2ecf20Sopenharmony_ci} 4748c2ecf20Sopenharmony_cimodule_exit(vprbrd_gpio_exit); 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ciMODULE_AUTHOR("Lars Poeschel <poeschel@lemonage.de>"); 4778c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("GPIO driver for Nano River Techs Viperboard"); 4788c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 4798c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:viperboard-gpio"); 480