mfd: Add viperboard driver

Add mfd driver for Nano River Technologies viperboard.

Signed-off-by: Lars Poeschel <poeschel@lemonage.de>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
This commit is contained in:
Lars Poeschel 2012-11-05 15:48:23 +01:00 committed by Samuel Ortiz
parent b9fbb62eb6
commit f01312d846
4 changed files with 249 additions and 0 deletions

View File

@ -1065,6 +1065,20 @@ config MFD_PALMAS
If you say yes here you get support for the Palmas
series of PMIC chips from Texas Instruments.
config MFD_VIPERBOARD
tristate "Support for Nano River Technologies Viperboard"
select MFD_CORE
depends on USB
default n
help
Say yes here if you want support for Nano River Technologies
Viperboard.
There are mfd cell drivers available for i2c master, adc and
both gpios found on the board. The spi part does not yet
have a driver.
You need to select the mfd cell drivers separately.
The drivers do not support all features the board exposes.
endmenu
endif

View File

@ -139,6 +139,7 @@ obj-$(CONFIG_MFD_TPS65090) += tps65090.o
obj-$(CONFIG_MFD_AAT2870_CORE) += aat2870-core.o
obj-$(CONFIG_MFD_INTEL_MSIC) += intel_msic.o
obj-$(CONFIG_MFD_PALMAS) += palmas.o
obj-$(CONFIG_MFD_VIPERBOARD) += viperboard.o
obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o
obj-$(CONFIG_MFD_SEC_CORE) += sec-core.o sec-irq.o
obj-$(CONFIG_MFD_SYSCON) += syscon.o

129
drivers/mfd/viperboard.c Normal file
View File

@ -0,0 +1,129 @@
/*
* Nano River Technologies viperboard driver
*
* This is the core driver for the viperboard. There are cell drivers
* available for I2C, ADC and both GPIOs. SPI is not yet supported.
* The drivers do not support all features the board exposes. See user
* manual of the viperboard.
*
* (C) 2012 by Lemonage GmbH
* Author: Lars Poeschel <poeschel@lemonage.de>
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/mutex.h>
#include <linux/mfd/core.h>
#include <linux/mfd/viperboard.h>
#include <linux/usb.h>
static const struct usb_device_id vprbrd_table[] = {
{ USB_DEVICE(0x2058, 0x1005) }, /* Nano River Technologies */
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, vprbrd_table);
static struct mfd_cell vprbrd_devs[] = {
};
static int vprbrd_probe(struct usb_interface *interface,
const struct usb_device_id *id)
{
struct vprbrd *vb;
u16 version = 0;
int pipe, ret;
unsigned char buf[1];
/* allocate memory for our device state and initialize it */
vb = kzalloc(sizeof(*vb), GFP_KERNEL);
if (vb == NULL) {
dev_err(&interface->dev, "Out of memory\n");
return -ENOMEM;
}
mutex_init(&vb->lock);
vb->usb_dev = usb_get_dev(interface_to_usbdev(interface));
/* save our data pointer in this interface device */
usb_set_intfdata(interface, vb);
dev_set_drvdata(&vb->pdev.dev, vb);
/* get version information, major first, minor then */
pipe = usb_rcvctrlpipe(vb->usb_dev, 0);
ret = usb_control_msg(vb->usb_dev, pipe, VPRBRD_USB_REQUEST_MAJOR,
VPRBRD_USB_TYPE_IN, 0x0000, 0x0000, buf, 1,
VPRBRD_USB_TIMEOUT_MS);
if (ret == 1)
version = buf[0];
ret = usb_control_msg(vb->usb_dev, pipe, VPRBRD_USB_REQUEST_MINOR,
VPRBRD_USB_TYPE_IN, 0x0000, 0x0000, buf, 1,
VPRBRD_USB_TIMEOUT_MS);
if (ret == 1) {
version <<= 8;
version = version | buf[0];
}
dev_info(&interface->dev,
"version %x.%02x found at bus %03d address %03d\n",
version >> 8, version & 0xff,
vb->usb_dev->bus->busnum, vb->usb_dev->devnum);
ret = mfd_add_devices(&interface->dev, -1, vprbrd_devs,
ARRAY_SIZE(vprbrd_devs), NULL, 0, NULL);
if (ret != 0) {
dev_err(&interface->dev, "Failed to add mfd devices to core.");
goto error;
}
return 0;
error:
if (vb) {
usb_put_dev(vb->usb_dev);
kfree(vb);
}
return ret;
}
static void vprbrd_disconnect(struct usb_interface *interface)
{
struct vprbrd *vb = usb_get_intfdata(interface);
mfd_remove_devices(&interface->dev);
usb_set_intfdata(interface, NULL);
usb_put_dev(vb->usb_dev);
kfree(vb);
dev_dbg(&interface->dev, "disconnected\n");
}
static struct usb_driver vprbrd_driver = {
.name = "viperboard",
.probe = vprbrd_probe,
.disconnect = vprbrd_disconnect,
.id_table = vprbrd_table,
};
module_usb_driver(vprbrd_driver);
MODULE_DESCRIPTION("Nano River Technologies viperboard mfd core driver");
MODULE_AUTHOR("Lars Poeschel <poeschel@lemonage.de>");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,105 @@
/*
* include/linux/mfd/viperboard.h
*
* Nano River Technologies viperboard definitions
*
* (C) 2012 by Lemonage GmbH
* Author: Lars Poeschel <poeschel@lemonage.de>
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#ifndef __MFD_VIPERBOARD_H__
#define __MFD_VIPERBOARD_H__
#include <linux/types.h>
#include <linux/usb.h>
#define VPRBRD_EP_OUT 0x02
#define VPRBRD_EP_IN 0x86
#define VPRBRD_I2C_MSG_LEN 512 /* max length of a msg on USB level */
#define VPRBRD_I2C_FREQ_6MHZ 1 /* 6 MBit/s */
#define VPRBRD_I2C_FREQ_3MHZ 2 /* 3 MBit/s */
#define VPRBRD_I2C_FREQ_1MHZ 3 /* 1 MBit/s */
#define VPRBRD_I2C_FREQ_FAST 4 /* 400 kbit/s */
#define VPRBRD_I2C_FREQ_400KHZ VPRBRD_I2C_FREQ_FAST
#define VPRBRD_I2C_FREQ_200KHZ 5 /* 200 kbit/s */
#define VPRBRD_I2C_FREQ_STD 6 /* 100 kbit/s */
#define VPRBRD_I2C_FREQ_100KHZ VPRBRD_I2C_FREQ_STD
#define VPRBRD_I2C_FREQ_10KHZ 7 /* 10 kbit/s */
#define VPRBRD_I2C_CMD_WRITE 0x00
#define VPRBRD_I2C_CMD_READ 0x01
#define VPRBRD_I2C_CMD_ADDR 0x02
#define VPRBRD_USB_TYPE_OUT 0x40
#define VPRBRD_USB_TYPE_IN 0xc0
#define VPRBRD_USB_TIMEOUT_MS 100
#define VPRBRD_USB_REQUEST_MAJOR 0xea
#define VPRBRD_USB_REQUEST_MINOR 0xeb
struct vprbrd_i2c_write_hdr {
u8 cmd;
u16 addr;
u8 len1;
u8 len2;
u8 last;
u8 chan;
u16 spi;
} __packed;
struct vprbrd_i2c_read_hdr {
u8 cmd;
u16 addr;
u8 len0;
u8 len1;
u8 len2;
u8 len3;
u8 len4;
u8 len5;
u16 tf1; /* transfer 1 length */
u16 tf2; /* transfer 2 length */
} __packed;
struct vprbrd_i2c_status {
u8 unknown[11];
u8 status;
} __packed;
struct vprbrd_i2c_write_msg {
struct vprbrd_i2c_write_hdr header;
u8 data[VPRBRD_I2C_MSG_LEN
- sizeof(struct vprbrd_i2c_write_hdr)];
} __packed;
struct vprbrd_i2c_read_msg {
struct vprbrd_i2c_read_hdr header;
u8 data[VPRBRD_I2C_MSG_LEN
- sizeof(struct vprbrd_i2c_read_hdr)];
} __packed;
struct vprbrd_i2c_addr_msg {
u8 cmd;
u8 addr;
u8 unknown1;
u16 len;
u8 unknown2;
u8 unknown3;
} __packed;
/* Structure to hold all device specific stuff */
struct vprbrd {
struct usb_device *usb_dev; /* the usb device for this device */
struct mutex lock;
u8 buf[sizeof(struct vprbrd_i2c_write_msg)];
struct platform_device pdev;
};
#endif /* __MFD_VIPERBOARD_H__ */