blob: 543d557b014921524c946a3b68ccdbf4ca204a7f [file] [log] [blame]
/* Copyright (c) 2011, The Linux Foundation. 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 version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/uaccess.h>
#include <linux/version.h>
#include <linux/debugfs.h>
#include <linux/kernel.h>
#include <linux/param.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <mach/gpio.h>
#include <mach/vreg.h>
#include <mach/gpiomux.h>
#define VERSION "1.0"
struct dentry *pin_debugfs_dent;
/* UART GPIO lines for 8660 */
enum uartpins {
UARTDM_TX = 53,
UARTDM_RX = 54,
UARTDM_CTS = 55,
UARTDM_RFR = 56
};
/* Aux PCM GPIO lines for 8660 */
enum auxpcmpins {
AUX_PCM_CLK = 114,
AUX_PCM_SYNC = 113,
AUX_PCM_DIN = 112,
AUX_PCM_DOUT = 111
};
/*Number of UART and PCM pins */
#define PIN_COUNT 8
static struct gpiomux_setting pin_test_config = {
.func = GPIOMUX_FUNC_GPIO,
.drv = GPIOMUX_DRV_2MA,
.pull = GPIOMUX_PULL_NONE,
};
/* Static array to intialise the return config */
static struct gpiomux_setting currentconfig[2*PIN_COUNT];
static struct msm_gpiomux_config pin_test_configs[] = {
{
.gpio = AUX_PCM_DOUT,
.settings = {
[GPIOMUX_ACTIVE] = &pin_test_config,
[GPIOMUX_SUSPENDED] = &pin_test_config,
},
},
{
.gpio = AUX_PCM_DIN,
.settings = {
[GPIOMUX_ACTIVE] = &pin_test_config,
[GPIOMUX_SUSPENDED] = &pin_test_config,
},
},
{
.gpio = AUX_PCM_SYNC,
.settings = {
[GPIOMUX_ACTIVE] = &pin_test_config,
[GPIOMUX_SUSPENDED] = &pin_test_config,
},
},
{
.gpio = AUX_PCM_CLK,
.settings = {
[GPIOMUX_ACTIVE] = &pin_test_config,
[GPIOMUX_SUSPENDED] = &pin_test_config,
},
},
{
.gpio = UARTDM_TX,
.settings = {
[GPIOMUX_ACTIVE] = &pin_test_config,
[GPIOMUX_SUSPENDED] = &pin_test_config,
},
},
{
.gpio = UARTDM_RX,
.settings = {
[GPIOMUX_ACTIVE] = &pin_test_config,
[GPIOMUX_SUSPENDED] = &pin_test_config,
},
},
{
.gpio = UARTDM_CTS,
.settings = {
[GPIOMUX_ACTIVE] = &pin_test_config,
[GPIOMUX_SUSPENDED] = &pin_test_config,
},
},
{
.gpio = UARTDM_RFR,
.settings = {
[GPIOMUX_ACTIVE] = &pin_test_config,
[GPIOMUX_SUSPENDED] = &pin_test_config,
},
},
};
static struct msm_gpiomux_config pin_config[PIN_COUNT];
static int pintest_open(struct inode *inode, struct file *file)
{
/* non-seekable */
file->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE);
return 0;
}
static int pintest_release(struct inode *inode, struct file *file)
{
return 0;
}
static int configure_pins(struct msm_gpiomux_config *config,
struct msm_gpiomux_config *oldconfig,
unsigned int num_configs)
{
int rc = 0, j, i;
for (i = 0; i < num_configs; i++) {
for (j = 0; j < GPIOMUX_NSETTINGS; j++) {
(oldconfig + i)->gpio = (config + i)->gpio;
rc = msm_gpiomux_write((config + i)->gpio,
j,
(config + i)->settings[j],
(oldconfig + i)->settings[j]);
if (rc < 0)
break;
}
}
return rc;
}
static void init_current_config_pointers(void)
{
int i = 0, j = 0;
/* The current config variables will hold the current configuration
* which is getting overwritten during a msm_gpiomux_write call
*/
for (i = 0, j = 0; i < PIN_COUNT; i += 1, j += 2) {
pin_config[i].settings[GPIOMUX_ACTIVE] = &currentconfig[j];
pin_config[i].settings[GPIOMUX_SUSPENDED] =
&currentconfig[j + 1];
}
}
static ssize_t pintest_write(
struct file *file,
const char __user *buff,
size_t count,
loff_t *ppos)
{
char mode;
int rc = 0;
if (count < 1)
return -EINVAL;
if (buff == NULL)
return -EINVAL;
if (copy_from_user(&mode, buff, count))
return -EFAULT;
mode = mode - '0';
init_current_config_pointers();
if (mode) {
/* Configure all pin test gpios for the custom settings */
rc = configure_pins(pin_test_configs, pin_config,
ARRAY_SIZE(pin_test_configs));
if (rc < 0)
return rc;
} else {
/* Configure all pin test gpios for the original settings */
rc = configure_pins(pin_config, pin_test_configs,
ARRAY_SIZE(pin_test_configs));
if (rc < 0)
return rc;
}
return rc;
}
static const struct file_operations pintest_debugfs_fops = {
.open = pintest_open,
.release = pintest_release,
.write = pintest_write,
};
static int __init bluepintest_init(void)
{
pin_debugfs_dent = debugfs_create_dir("btpintest", NULL);
if (IS_ERR(pin_debugfs_dent)) {
printk(KERN_ERR "%s(%d): debugfs_create_dir fail, error %ld\n",
__FILE__, __LINE__, PTR_ERR(pin_debugfs_dent));
return -ENOMEM;
}
if (debugfs_create_file("enable", 0644, pin_debugfs_dent,
0, &pintest_debugfs_fops) == NULL) {
printk(KERN_ERR "%s(%d): debugfs_create_file: index fail\n",
__FILE__, __LINE__);
return -ENOMEM;
}
return 0;
}
static void __exit bluepintest_exit(void)
{
debugfs_remove_recursive(pin_debugfs_dent);
}
module_init(bluepintest_init);
module_exit(bluepintest_exit);
MODULE_DESCRIPTION("Bluetooth Pin Connectivty Test Driver ver %s " VERSION);
MODULE_LICENSE("GPL v2");