162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  Nano River Technologies viperboard driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  This is the core driver for the viperboard. There are cell drivers
662306a36Sopenharmony_ci *  available for I2C, ADC and both GPIOs. SPI is not yet supported.
762306a36Sopenharmony_ci *  The drivers do not support all features the board exposes. See user
862306a36Sopenharmony_ci *  manual of the viperboard.
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci *  (C) 2012 by Lemonage GmbH
1162306a36Sopenharmony_ci *  Author: Lars Poeschel <poeschel@lemonage.de>
1262306a36Sopenharmony_ci *  All rights reserved.
1362306a36Sopenharmony_ci */
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include <linux/kernel.h>
1662306a36Sopenharmony_ci#include <linux/errno.h>
1762306a36Sopenharmony_ci#include <linux/module.h>
1862306a36Sopenharmony_ci#include <linux/slab.h>
1962306a36Sopenharmony_ci#include <linux/types.h>
2062306a36Sopenharmony_ci#include <linux/mutex.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include <linux/mfd/core.h>
2362306a36Sopenharmony_ci#include <linux/mfd/viperboard.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#include <linux/usb.h>
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistatic const struct usb_device_id vprbrd_table[] = {
2962306a36Sopenharmony_ci	{ USB_DEVICE(0x2058, 0x1005) },   /* Nano River Technologies */
3062306a36Sopenharmony_ci	{ }                               /* Terminating entry */
3162306a36Sopenharmony_ci};
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, vprbrd_table);
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistatic const struct mfd_cell vprbrd_devs[] = {
3662306a36Sopenharmony_ci	{
3762306a36Sopenharmony_ci		.name = "viperboard-gpio",
3862306a36Sopenharmony_ci	},
3962306a36Sopenharmony_ci	{
4062306a36Sopenharmony_ci		.name = "viperboard-i2c",
4162306a36Sopenharmony_ci	},
4262306a36Sopenharmony_ci	{
4362306a36Sopenharmony_ci		.name = "viperboard-adc",
4462306a36Sopenharmony_ci	},
4562306a36Sopenharmony_ci};
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistatic int vprbrd_probe(struct usb_interface *interface,
4862306a36Sopenharmony_ci			      const struct usb_device_id *id)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	struct vprbrd *vb;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	u16 version = 0;
5362306a36Sopenharmony_ci	int pipe, ret;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	/* allocate memory for our device state and initialize it */
5662306a36Sopenharmony_ci	vb = kzalloc(sizeof(*vb), GFP_KERNEL);
5762306a36Sopenharmony_ci	if (!vb)
5862306a36Sopenharmony_ci		return -ENOMEM;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	mutex_init(&vb->lock);
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	vb->usb_dev = usb_get_dev(interface_to_usbdev(interface));
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	/* save our data pointer in this interface device */
6562306a36Sopenharmony_ci	usb_set_intfdata(interface, vb);
6662306a36Sopenharmony_ci	dev_set_drvdata(&vb->pdev.dev, vb);
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	/* get version information, major first, minor then */
6962306a36Sopenharmony_ci	pipe = usb_rcvctrlpipe(vb->usb_dev, 0);
7062306a36Sopenharmony_ci	ret = usb_control_msg(vb->usb_dev, pipe, VPRBRD_USB_REQUEST_MAJOR,
7162306a36Sopenharmony_ci		VPRBRD_USB_TYPE_IN, 0x0000, 0x0000, vb->buf, 1,
7262306a36Sopenharmony_ci		VPRBRD_USB_TIMEOUT_MS);
7362306a36Sopenharmony_ci	if (ret == 1)
7462306a36Sopenharmony_ci		version = vb->buf[0];
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	ret = usb_control_msg(vb->usb_dev, pipe, VPRBRD_USB_REQUEST_MINOR,
7762306a36Sopenharmony_ci		VPRBRD_USB_TYPE_IN, 0x0000, 0x0000, vb->buf, 1,
7862306a36Sopenharmony_ci		VPRBRD_USB_TIMEOUT_MS);
7962306a36Sopenharmony_ci	if (ret == 1) {
8062306a36Sopenharmony_ci		version <<= 8;
8162306a36Sopenharmony_ci		version = version | vb->buf[0];
8262306a36Sopenharmony_ci	}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	dev_info(&interface->dev,
8562306a36Sopenharmony_ci		 "version %x.%02x found at bus %03d address %03d\n",
8662306a36Sopenharmony_ci		 version >> 8, version & 0xff,
8762306a36Sopenharmony_ci		 vb->usb_dev->bus->busnum, vb->usb_dev->devnum);
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	ret = mfd_add_hotplug_devices(&interface->dev, vprbrd_devs,
9062306a36Sopenharmony_ci				      ARRAY_SIZE(vprbrd_devs));
9162306a36Sopenharmony_ci	if (ret != 0) {
9262306a36Sopenharmony_ci		dev_err(&interface->dev, "Failed to add mfd devices to core.");
9362306a36Sopenharmony_ci		goto error;
9462306a36Sopenharmony_ci	}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	return 0;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cierror:
9962306a36Sopenharmony_ci	if (vb) {
10062306a36Sopenharmony_ci		usb_put_dev(vb->usb_dev);
10162306a36Sopenharmony_ci		kfree(vb);
10262306a36Sopenharmony_ci	}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	return ret;
10562306a36Sopenharmony_ci}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_cistatic void vprbrd_disconnect(struct usb_interface *interface)
10862306a36Sopenharmony_ci{
10962306a36Sopenharmony_ci	struct vprbrd *vb = usb_get_intfdata(interface);
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	mfd_remove_devices(&interface->dev);
11262306a36Sopenharmony_ci	usb_set_intfdata(interface, NULL);
11362306a36Sopenharmony_ci	usb_put_dev(vb->usb_dev);
11462306a36Sopenharmony_ci	kfree(vb);
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	dev_dbg(&interface->dev, "disconnected\n");
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_cistatic struct usb_driver vprbrd_driver = {
12062306a36Sopenharmony_ci	.name		= "viperboard",
12162306a36Sopenharmony_ci	.probe		= vprbrd_probe,
12262306a36Sopenharmony_ci	.disconnect	= vprbrd_disconnect,
12362306a36Sopenharmony_ci	.id_table	= vprbrd_table,
12462306a36Sopenharmony_ci};
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cimodule_usb_driver(vprbrd_driver);
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ciMODULE_DESCRIPTION("Nano River Technologies viperboard mfd core driver");
12962306a36Sopenharmony_ciMODULE_AUTHOR("Lars Poeschel <poeschel@lemonage.de>");
13062306a36Sopenharmony_ciMODULE_LICENSE("GPL");
131