1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Nano River Technologies viperboard driver 4 * 5 * This is the core driver for the viperboard. There are cell drivers 6 * available for I2C, ADC and both GPIOs. SPI is not yet supported. 7 * The drivers do not support all features the board exposes. See user 8 * manual of the viperboard. 9 * 10 * (C) 2012 by Lemonage GmbH 11 * Author: Lars Poeschel <poeschel@lemonage.de> 12 * All rights reserved. 13 */ 14 15 #include <linux/kernel.h> 16 #include <linux/errno.h> 17 #include <linux/module.h> 18 #include <linux/slab.h> 19 #include <linux/types.h> 20 #include <linux/mutex.h> 21 22 #include <linux/mfd/core.h> 23 #include <linux/mfd/viperboard.h> 24 25 #include <linux/usb.h> 26 27 28 static const struct usb_device_id vprbrd_table[] = { 29 { USB_DEVICE(0x2058, 0x1005) }, /* Nano River Technologies */ 30 { } /* Terminating entry */ 31 }; 32 33 MODULE_DEVICE_TABLE(usb, vprbrd_table); 34 35 static const struct mfd_cell vprbrd_devs[] = { 36 { 37 .name = "viperboard-gpio", 38 }, 39 { 40 .name = "viperboard-i2c", 41 }, 42 { 43 .name = "viperboard-adc", 44 }, 45 }; 46 47 static int vprbrd_probe(struct usb_interface *interface, 48 const struct usb_device_id *id) 49 { 50 struct vprbrd *vb; 51 52 u16 version = 0; 53 int pipe, ret; 54 55 /* allocate memory for our device state and initialize it */ 56 vb = kzalloc(sizeof(*vb), GFP_KERNEL); 57 if (!vb) 58 return -ENOMEM; 59 60 mutex_init(&vb->lock); 61 62 vb->usb_dev = usb_get_dev(interface_to_usbdev(interface)); 63 64 /* save our data pointer in this interface device */ 65 usb_set_intfdata(interface, vb); 66 dev_set_drvdata(&vb->pdev.dev, vb); 67 68 /* get version information, major first, minor then */ 69 pipe = usb_rcvctrlpipe(vb->usb_dev, 0); 70 ret = usb_control_msg(vb->usb_dev, pipe, VPRBRD_USB_REQUEST_MAJOR, 71 VPRBRD_USB_TYPE_IN, 0x0000, 0x0000, vb->buf, 1, 72 VPRBRD_USB_TIMEOUT_MS); 73 if (ret == 1) 74 version = vb->buf[0]; 75 76 ret = usb_control_msg(vb->usb_dev, pipe, VPRBRD_USB_REQUEST_MINOR, 77 VPRBRD_USB_TYPE_IN, 0x0000, 0x0000, vb->buf, 1, 78 VPRBRD_USB_TIMEOUT_MS); 79 if (ret == 1) { 80 version <<= 8; 81 version = version | vb->buf[0]; 82 } 83 84 dev_info(&interface->dev, 85 "version %x.%02x found at bus %03d address %03d\n", 86 version >> 8, version & 0xff, 87 vb->usb_dev->bus->busnum, vb->usb_dev->devnum); 88 89 ret = mfd_add_hotplug_devices(&interface->dev, vprbrd_devs, 90 ARRAY_SIZE(vprbrd_devs)); 91 if (ret != 0) { 92 dev_err(&interface->dev, "Failed to add mfd devices to core."); 93 goto error; 94 } 95 96 return 0; 97 98 error: 99 if (vb) { 100 usb_put_dev(vb->usb_dev); 101 kfree(vb); 102 } 103 104 return ret; 105 } 106 107 static void vprbrd_disconnect(struct usb_interface *interface) 108 { 109 struct vprbrd *vb = usb_get_intfdata(interface); 110 111 mfd_remove_devices(&interface->dev); 112 usb_set_intfdata(interface, NULL); 113 usb_put_dev(vb->usb_dev); 114 kfree(vb); 115 116 dev_dbg(&interface->dev, "disconnected\n"); 117 } 118 119 static struct usb_driver vprbrd_driver = { 120 .name = "viperboard", 121 .probe = vprbrd_probe, 122 .disconnect = vprbrd_disconnect, 123 .id_table = vprbrd_table, 124 }; 125 126 module_usb_driver(vprbrd_driver); 127 128 MODULE_DESCRIPTION("Nano River Technologies viperboard mfd core driver"); 129 MODULE_AUTHOR("Lars Poeschel <poeschel@lemonage.de>"); 130 MODULE_LICENSE("GPL"); 131