Commit 43e60fdb authored by Juha Yrjölä's avatar Juha Yrjölä Committed by Tony Lindgren

Add CBUS support

CBUS is the bus that Energy Management ASICs are connected to on
some Nokia mobile devices. Added support for CBUS and two EM ASIC
drivers.

Also added board config structures for some of the Nokia 770 drivers.
Signed-off-by: default avatarJuha Yrjölä <juha.yrjola@nokia.com>
parent fe6f42fc
......@@ -997,6 +997,10 @@ source "drivers/mmc/Kconfig"
source "drivers/rtc/Kconfig"
if ARCH_OMAP
source "drivers/cbus/Kconfig"
endif
endmenu
source "fs/Kconfig"
......
......@@ -27,7 +27,7 @@ obj-$(CONFIG_FB_INTEL) += video/intelfb/
obj-y += serial/
obj-$(CONFIG_PARPORT) += parport/
obj-y += base/ block/ misc/ mfd/ net/ media/
obj-y += base/ block/ misc/ mfd/ net/ media/ cbus/
obj-$(CONFIG_NUBUS) += nubus/
obj-$(CONFIG_ATM) += atm/
obj-y += macintosh/
......
#
# CBUS device configuration
#
menu "CBUS support"
config CBUS
depends on ARCH_OMAP
bool "CBUS support on OMAP"
---help---
CBUS is a proprietary serial protocol by Nokia. It is mainly
used for accessing Energy Management auxiliary chips.
If you want CBUS support, you should say Y here.
config CBUS_TAHVO
depends on CBUS
bool "Support for Tahvo"
---help---
Tahvo is a mixed signal ASIC with some system features
If you want Tahvo support, you should say Y here.
config CBUS_TAHVO_USER
depends on CBUS_TAHVO
bool "Support for Tahvo user space functions"
---help---
If you want support for Tahvo's user space read/write etc. functions,
you should say Y here.
config CBUS_TAHVO_USB
depends on CBUS_TAHVO && USB
tristate "Support for Tahvo USB transceiver"
---help---
If you want Tahvo support for USB transceiver, say Y or M here.
config CBUS_TAHVO_USB_HOST_BY_DEFAULT
depends on CBUS_TAHVO_USB && USB_OTG
boolean "Device in USB host mode by default"
---help---
Say Y here, if you want the device to enter USB host mode
by default on bootup.
config CBUS_RETU
depends on CBUS
bool "Support for Retu"
---help---
Retu is a mixed signal ASIC with some system features
If you want Retu support, you should say Y here.
config CBUS_RETU_USER
depends on CBUS_RETU
bool "Support for Retu user space functions"
---help---
If you want support for Retu's user space read/write etc. functions,
you should say Y here.
config CBUS_RETU_POWERBUTTON
depends on CBUS_RETU
bool "Support for Retu power button"
---help---
The power button on Nokia 770 is connected to the Retu ASIC.
If you want support for the Retu power button, you should say Y here.
config CBUS_RETU_RTC
depends on CBUS_RETU && SYSFS
tristate "Support for Retu pseudo-RTC"
---help---
Say Y here if you want support for the device that alleges to be an
RTC in Retu. This will expose a sysfs interface for it.
config CBUS_RETU_WDT
depends on CBUS_RETU && SYSFS
tristate "Support for Retu watchdog timer"
---help---
Say Y here if you want support for the watchdog in Retu. This will
expose a sysfs interface to grok it.
endmenu
#
# Makefile for CBUS.
#
obj-$(CONFIG_CBUS) += cbus.o
obj-$(CONFIG_CBUS_TAHVO) += tahvo.o
obj-$(CONFIG_CBUS_RETU) += retu.o
obj-$(CONFIG_CBUS_TAHVO_USB) += tahvo-usb.o
obj-$(CONFIG_CBUS_RETU_POWERBUTTON) += retu-pwrbutton.o
obj-$(CONFIG_CBUS_RETU_RTC) += retu-rtc.o
obj-$(CONFIG_CBUS_RETU_WDT) += retu-wdt.o
obj-$(CONFIG_CBUS_TAHVO_USER) += tahvo-user.o
obj-$(CONFIG_CBUS_RETU_USER) += retu-user.o
/*
* drivers/cbus/cbus.c
*
* Support functions for CBUS serial protocol
*
* Copyright (C) 2004, 2005 Nokia Corporation
*
* Written by Juha Yrjl <juha.yrjola@nokia.com>,
* David Weinehall <david.weinehall@nokia.com>, and
* Mikko Ylinen <mikko.k.ylinen@nokia.com>
*
* This file is subject to the terms and conditions of the GNU General
* Public License. See the file "COPYING" in the main directory of this
* archive for more details.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/device.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/spinlock.h>
#include <asm/arch/gpio.h>
#include <asm/arch/board.h>
#include <asm/io.h>
#include "cbus.h"
struct cbus_host *cbus_host = NULL;
#ifdef CONFIG_ARCH_OMAP1
/* We use our own MPUIO functions to get closer to 1MHz bus speed */
static inline void cbus_set_gpio_direction(u32 base, int mpuio, int is_input)
{
u16 w;
mpuio &= 0x0f;
w = __raw_readw(base + OMAP_MPUIO_IO_CNTL);
if (is_input)
w |= 1 << mpuio;
else
w &= ~(1 << mpuio);
__raw_writew(w, base + OMAP_MPUIO_IO_CNTL);
}
static inline void cbus_set_gpio_dataout(u32 base, int mpuio, int enable)
{
u16 w;
mpuio &= 0x0f;
w = __raw_readw(base + OMAP_MPUIO_OUTPUT);
if (enable)
w |= 1 << mpuio;
else
w &= ~(1 << mpuio);
__raw_writew(w, base + OMAP_MPUIO_OUTPUT);
}
static inline int cbus_get_gpio_datain(u32 base, int mpuio)
{
mpuio &= 0x0f;
return (__raw_readw(base + OMAP_MPUIO_INPUT_LATCH) & (1 << mpuio)) != 0;
}
static void cbus_send_bit(struct cbus_host *host, u32 base, int bit,
int set_to_input)
{
cbus_set_gpio_dataout(base, host->dat_gpio, bit ? 1 : 0);
cbus_set_gpio_dataout(base, host->clk_gpio, 1);
/* The data bit is read on the rising edge of CLK */
if (set_to_input)
cbus_set_gpio_direction(base, host->dat_gpio, 1);
cbus_set_gpio_dataout(base, host->clk_gpio, 0);
}
static u8 cbus_receive_bit(struct cbus_host *host, u32 base)
{
u8 ret;
cbus_set_gpio_dataout(base, host->clk_gpio, 1);
ret = cbus_get_gpio_datain(base, host->dat_gpio);
cbus_set_gpio_dataout(base, host->clk_gpio, 0);
return ret;
}
#else
#define cbus_set_gpio_direction(base, gpio, is_input) omap_set_gpio_direction(gpio, is_input)
#define cbus_set_gpio_dataout(base, gpio, enable) omap_set_gpio_dataout(gpio, enable)
#define cbus_get_gpio_datain(base, int, gpio) omap_get_gpio_datain(gpio)
static void _cbus_send_bit(struct cbus_host *host, int bit, int set_to_input)
{
omap_set_gpio_dataout(host->dat_gpio, bit ? 1 : 0);
omap_set_gpio_dataout(host->clk_gpio, 1);
/* The data bit is read on the rising edge of CLK */
if (set_to_input)
omap_set_gpio_direction(host->dat_gpio, 1);
omap_set_gpio_dataout(host->clk_gpio, 0);
}
static u8 _cbus_receive_bit(struct cbus_host *host)
{
u8 ret;
omap_set_gpio_dataout(host->clk_gpio, 1);
ret = omap_get_gpio_datain(host->dat_gpio);
omap_set_gpio_dataout(host->clk_gpio, 0);
return ret;
}
#define cbus_send_bit(host, base, bit, set_to_input) _cbus_send_bit(host, bit, set_to_input)
#define cbus_receive_bit(host, base) _cbus_receive_bit(host)
#endif
static int cbus_transfer(struct cbus_host *host, int dev, int reg, int data)
{
int i;
int is_read = 0;
unsigned long flags;
u32 base;
#ifdef CONFIG_ARCH_OMAP1
base = (u32) io_p2v(OMAP_MPUIO_BASE);
#else
base = 0;
#endif
if (data < 0)
is_read = 1;
/* We don't want interrupts disturbing our transfer */
spin_lock_irqsave(&host->lock, flags);
/* Reset state and start of transfer, SEL stays down during transfer */
cbus_set_gpio_dataout(base, host->sel_gpio, 0);
/* Set the DAT pin to output */
cbus_set_gpio_direction(base, host->dat_gpio, 0);
/* Send the device address */
for (i = 3; i > 0; i--)
cbus_send_bit(host, base, dev & (1 << (i - 1)), 0);
/* Send the rw flag */
cbus_send_bit(host, base, is_read, 0);
/* Send the register address */
for (i = 5; i > 0; i--) {
int set_to_input = 0;
if (is_read && i == 1)
set_to_input = 1;
cbus_send_bit(host, base, reg & (1 << (i - 1)), set_to_input);
}
if (!is_read) {
for (i = 16; i > 0; i--)
cbus_send_bit(host, base, data & (1 << (i - 1)), 0);
} else {
cbus_set_gpio_dataout(base, host->clk_gpio, 1);
data = 0;
for (i = 16; i > 0; i--) {
u8 bit = cbus_receive_bit(host, base);
if (bit)
data |= 1 << (i - 1);
}
}
/* Indicate end of transfer, SEL goes up until next transfer */
cbus_set_gpio_dataout(base, host->sel_gpio, 1);
cbus_set_gpio_dataout(base, host->clk_gpio, 1);
cbus_set_gpio_dataout(base, host->clk_gpio, 0);
spin_unlock_irqrestore(&host->lock, flags);
return is_read ? data : 0;
}
/*
* Read a given register from the device
*/
int cbus_read_reg(struct cbus_host *host, int dev, int reg)
{
return cbus_host ? cbus_transfer(host, dev, reg, -1) : -ENODEV;
}
/*
* Write to a given register of the device
*/
int cbus_write_reg(struct cbus_host *host, int dev, int reg, u16 val)
{
return cbus_host ? cbus_transfer(host, dev, reg, (int)val) : -ENODEV;
}
int __init cbus_bus_init(void)
{
const struct omap_cbus_config * cbus_config;
struct cbus_host *chost;
int ret;
chost = kmalloc(sizeof (*chost), GFP_KERNEL);
if (chost == NULL)
return -ENOMEM;
memset(chost, 0, sizeof (*chost));
spin_lock_init(&chost->lock);
cbus_config = omap_get_config(OMAP_TAG_CBUS, struct omap_cbus_config);
if (cbus_config == NULL) {
printk(KERN_ERR "cbus: Unable to retrieve config data\n");
return -ENODATA;
}
chost->clk_gpio = cbus_config->clk_gpio;
chost->dat_gpio = cbus_config->dat_gpio;
chost->sel_gpio = cbus_config->sel_gpio;
#ifdef CONFIG_ARCH_OMAP1
if (!OMAP_GPIO_IS_MPUIO(chost->clk_gpio) ||
!OMAP_GPIO_IS_MPUIO(chost->dat_gpio) ||
!OMAP_GPIO_IS_MPUIO(chost->sel_gpio)) {
printk(KERN_ERR "cbus: Only MPUIO pins supported\n");
ret = -ENODEV;
goto exit1;
}
#endif
if ((ret = omap_request_gpio(chost->clk_gpio)) < 0)
goto exit1;
if ((ret = omap_request_gpio(chost->dat_gpio)) < 0)
goto exit2;
if ((ret = omap_request_gpio(chost->sel_gpio)) < 0)
goto exit3;
omap_set_gpio_dataout(chost->clk_gpio, 0);
omap_set_gpio_dataout(chost->sel_gpio, 1);
omap_set_gpio_direction(chost->clk_gpio, 0);
omap_set_gpio_direction(chost->dat_gpio, 1);
omap_set_gpio_direction(chost->sel_gpio, 0);
omap_set_gpio_dataout(chost->clk_gpio, 1);
omap_set_gpio_dataout(chost->clk_gpio, 0);
cbus_host = chost;
return 0;
exit3:
omap_free_gpio(chost->dat_gpio);
exit2:
omap_free_gpio(chost->clk_gpio);
exit1:
kfree(chost);
return ret;
}
subsys_initcall(cbus_bus_init);
EXPORT_SYMBOL(cbus_host);
EXPORT_SYMBOL(cbus_read_reg);
EXPORT_SYMBOL(cbus_write_reg);
MODULE_DESCRIPTION("CBUS serial protocol");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Juha Yrjl, David Weinehall, and Mikko Ylinen");
/*
* drivers/cbus/cbus.h
*
* Copyright (C) 2004, 2005 Nokia Corporation
*
* Written by Juha Yrjölä <juha.yrjola@nokia.com> and
* David Weinehall <david.weinehall@nokia.com>
*
* This file is subject to the terms and conditions of the GNU General
* Public License. See the file "COPYING" in the main directory of this
* archive for more details.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __DRIVERS_CBUS_CBUS_H
#define __DRIVERS_CBUS_CBUS_H
struct cbus_host {
int clk_gpio, dat_gpio, sel_gpio;
spinlock_t lock;
};
extern struct cbus_host *cbus_host;
extern int cbus_read_reg(struct cbus_host *host, int dev, int reg);
extern int cbus_write_reg(struct cbus_host *host, int dev, int reg, u16 val);
#endif /* __DRIVERS_CBUS_CBUS_H */
/**
* drivers/cbus/retu-pwrbutton.c
*
* Driver for sending retu power button event to input-layer
*
* Copyright (C) 2004 Nokia Corporation
*
* Written by Ari Saastamoinen <ari.saastamoinen@elektrobit.com>
*
* Contact Juha Yrjl <juha.yrjola@nokia.com>
*
* This file is subject to the terms and conditions of the GNU General
* Public License. See the file "COPYING" in the main directory of this
* archive for more details.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/input.h>
#include <linux/timer.h>
#include <linux/jiffies.h>
#include "retu.h"
#define RETU_STATUS_PWRONX (1 << 5)
#define PWRBTN_DELAY 20
#define PWRBTN_UP 0
#define PWRBTN_PRESSED 1
static int pwrbtn_state;
static struct input_dev *pwrbtn_dev;
static struct timer_list pwrbtn_timer;
static void retubutton_timer_func(unsigned long arg)
{
int state;
if (retu_read_reg(RETU_REG_STATUS) & RETU_STATUS_PWRONX)
state = PWRBTN_UP;
else
state = PWRBTN_PRESSED;
if (pwrbtn_state != state) {
input_report_key(pwrbtn_dev, KEY_POWER, state);
pwrbtn_state = state;
}
}
/**
* Interrupt function is called whenever power button key is pressed
* or released.
*/
static void retubutton_irq(unsigned long arg)
{
retu_ack_irq(RETU_INT_PWR);
mod_timer(&pwrbtn_timer, jiffies + msecs_to_jiffies(PWRBTN_DELAY));
}
/**
* Init function.
* Allocates interrupt for power button and registers itself to input layer.
*/
static int __init retubutton_init(void)
{
int irq;
printk(KERN_INFO "Retu power button driver initialized\n");
irq = RETU_INT_PWR;
init_timer(&pwrbtn_timer);
pwrbtn_timer.function = retubutton_timer_func;
if (retu_request_irq(irq, &retubutton_irq, 0, "PwrOnX") < 0) {
printk(KERN_ERR "%s@%s: Cannot allocate irq\n",
__FUNCTION__, __FILE__);
return -EBUSY;
}
pwrbtn_dev = input_allocate_device();
if (!pwrbtn_dev)
return -ENOMEM;
pwrbtn_dev->evbit[0] = BIT(EV_KEY);
pwrbtn_dev->keybit[LONG(KEY_POWER)] = BIT(KEY_POWER);
pwrbtn_dev->name = "retu-pwrbutton";
input_register_device(pwrbtn_dev);
return 0;
}
/**
* Cleanup function which is called when driver is unloaded
*/
static void __exit retubutton_exit(void)
{
retu_free_irq(RETU_INT_PWR);
del_timer_sync(&pwrbtn_timer);
input_unregister_device(pwrbtn_dev);
}
module_init(retubutton_init);
module_exit(retubutton_exit);
MODULE_DESCRIPTION("Retu Power Button");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Ari Saastamoinen");
This diff is collapsed.
/**
* drivers/cbus/retu-user.c
*
* Retu user space interface functions
*
* Copyright (C) 2004, 2005 Nokia Corporation
*
* Written by Mikko Ylinen <mikko.k.ylinen@nokia.com>
*
* This file is subject to the terms and conditions of the GNU General
* Public License. See the file "COPYING" in the main directory of this
* archive for more details.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/poll.h>
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/mutex.h>
#include <asm/uaccess.h>
#include "retu.h"
#include "user_retu_tahvo.h"
/* Maximum size of IRQ node buffer/pool */
#define RETU_MAX_IRQ_BUF_LEN 16
#define PFX "retu-user: "
/* Bitmap for marking the interrupt sources as having the handlers */
static u32 retu_irq_bits;
/* For allowing only one user process to subscribe to the retu interrupts */
static struct file *retu_irq_subscr = NULL;
/* For poll and IRQ passing */
struct retu_irq {
u32 id;
struct list_head node;
};
static spinlock_t retu_irqs_lock;
static struct retu_irq *retu_irq_block;
static LIST_HEAD(retu_irqs);
static LIST_HEAD(retu_irqs_reserve);
/* Wait queue - used when user wants to read the device */
DECLARE_WAIT_QUEUE_HEAD(retu_user_waitqueue);
/* Semaphore to protect irq subscription sequence */
static struct mutex retu_mutex;
/* This array specifies RETU register types (read/write/toggle) */
static const u8 retu_access_bits[] = {
1,
4,
3,
3,
1,
3,
3,
0,
3,
3,
3,
3,
3,
3,
3,
4,
4,
3,
0,
0,
0,
0,
1,
3,
3,
3,
3,
3,
3,
3,
3,
3
};
/*
* The handler for all RETU interrupts.
*
* arg is the interrupt source in RETU.
*/
static void retu_user_irq_handler(unsigned long arg)
{
struct retu_irq *irq;
retu_ack_irq(arg);
spin_lock(&retu_irqs_lock);
if (list_empty(&retu_irqs_reserve)) {
spin_unlock(&retu_irqs_lock);
return;
}
irq = list_entry((&retu_irqs_reserve)->next, struct retu_irq, node);
irq->id = arg;
list_move_tail(&irq->node, &retu_irqs);
spin_unlock(&retu_irqs_lock);
/* wake up waiting thread */
wake_up(&retu_user_waitqueue);
}
/*
* This routine sets up the interrupt handler and marks an interrupt source
* in RETU as a candidate for signal delivery to the user process.
*/
static int retu_user_subscribe_to_irq(int id, struct file *filp)
{
int ret;
mutex_lock(&retu_mutex);
if ((retu_irq_subscr != NULL) && (retu_irq_subscr != filp)) {
mutex_unlock(&retu_mutex);
return -EBUSY;
}
/* Store the file pointer of the first user process registering IRQs */
retu_irq_subscr = filp;
mutex_unlock(&retu_mutex);
if (retu_irq_bits & (1 << id))
return 0;
ret = retu_request_irq(id, retu_user_irq_handler, id, "");
if (ret < 0)
return ret;
/* Mark that this interrupt has a handler */
retu_irq_bits |= 1 << id;
return 0;
}
/*
* Unregisters all RETU interrupt handlers.
*/
static void retu_unreg_irq_handlers(void)
{
int id;
if (!retu_irq_bits)
return;
for (id = 0; id < MAX_RETU_IRQ_HANDLERS; id++)
if (retu_irq_bits & (1 << id))
retu_free_irq(id);
retu_irq_bits = 0;
}
/*
* Write to RETU register.
* Returns 0 upon success, a negative error value otherwise.
*/
static int retu_user_write_with_mask(u32 field, u16 value)
{
u32 mask;
u32 reg;
u_short tmp;
unsigned long flags;
mask = MASK(field);
reg = REG(field);
/* Detect bad mask and reg */
if (mask == 0 || reg > RETU_REG_MAX ||
retu_access_bits[reg] == READ_ONLY) {
printk(KERN_ERR PFX "invalid arguments (reg=%#x, mask=%#x)\n",
reg, mask);
return -EINVAL;
}
/* Justify value according to mask */
while (!(mask & 1)) {
value = value << 1;
mask = mask >> 1;
}
spin_lock_irqsave(&retu_lock, flags);
if (retu_access_bits[reg] == TOGGLE) {
/* No need to detect previous content of register */
tmp = 0;
} else {
/* Read current value of register */
tmp = retu_read_reg(reg);
}
/* Generate new value */
tmp = (tmp & ~MASK(field)) | (value & MASK(field));
/* Write data to RETU */
retu_write_reg(reg, tmp);
spin_unlock_irqrestore(&retu_lock, flags);
return 0;
}
/*
* Read RETU register.
*/
static u32 retu_user_read_with_mask(u32 field)
{
u_short value;
u32 mask, reg;
mask = MASK(field);
reg = REG(field);
/* Detect bad mask and reg */
if (mask == 0 || reg > RETU_REG_MAX) {
printk(KERN_ERR PFX "invalid arguments (reg=%#x, mask=%#x)\n",
reg, mask);
return -EINVAL;
}
/* Read the register */
value = retu_read_reg(reg) & mask;
/* Right justify value */
while (!(mask & 1)) {
value = value >> 1;
mask = mask >> 1;
}
return value;
}
/*
* Close device
*/
static int retu_close(struct inode *inode, struct file *filp)
{
/* Unregister all interrupts that have been registered */
if (retu_irq_subscr == filp) {
retu_unreg_irq_handlers();
retu_irq_subscr = NULL;
}
return 0;
}
/*
* Device control (ioctl)
*/
static int retu_ioctl(struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg)
{
struct retu_tahvo_write_parms par;
int ret;
switch (cmd) {
case URT_IOCT_IRQ_SUBSCR:
return retu_user_subscribe_to_irq(arg, filp);
case RETU_IOCH_READ:
return retu_user_read_with_mask(arg);
case RETU_IOCX_WRITE:
ret = copy_from_user(&par, (void __user *) arg, sizeof(par));
if (ret)
printk(KERN_ERR "copy_from_user failed: %d\n", ret);
par.result = retu_user_write_with_mask(par.field, par.value);
ret = copy_to_user((void __user *) arg, &par, sizeof(par));
if (ret)
printk(KERN_ERR "copy_to_user failed: %d\n", ret);
break;
case RETU_IOCH_ADC_READ:
return retu_read_adc(arg);
default:
return -ENOIOCTLCMD;
}
return 0;
}
/*
* Read from device
*/
static ssize_t retu_read(struct file *filp, char *buf, size_t count,
loff_t * offp)
{
struct retu_irq *irq;
u32 nr, i;
/* read not permitted if neither filp nor anyone has registered IRQs */
if (retu_irq_subscr != filp)
return -EPERM;
if ((count < sizeof(u32)) || ((count % sizeof(u32)) != 0))
return -EINVAL;
nr = count / sizeof(u32);
for (i = 0; i < nr; i++) {
unsigned long flags;
u32 irq_id;
int ret;
ret = wait_event_interruptible(retu_user_waitqueue,
!list_empty(&retu_irqs));
if (ret < 0)
return ret;
spin_lock_irqsave(&retu_irqs_lock, flags);
irq = list_entry((&retu_irqs)->next, struct retu_irq, node);
irq_id = irq->id;
list_move(&irq->node, &retu_irqs_reserve);
spin_unlock_irqrestore(&retu_irqs_lock, flags);
ret = copy_to_user(buf + i * sizeof(irq_id), &irq_id,
sizeof(irq_id));
if (ret)
printk(KERN_ERR "copy_to_user failed: %d\n", ret);
}
return count;
}
/*
* Poll method
*/
static unsigned retu_poll(struct file *filp, struct poll_table_struct *pt)
{
if (!list_empty(&retu_irqs))
return POLLIN;
poll_wait(filp, &retu_user_waitqueue, pt);
if (!list_empty(&retu_irqs))
return POLLIN;
else
return 0;
}
static struct file_operations retu_user_fileops = {
.owner = THIS_MODULE,
.ioctl = retu_ioctl,
.read = retu_read,
.release = retu_close,
.poll = retu_poll
};
static struct miscdevice retu_device = {
.minor = MISC_DYNAMIC_MINOR,
.name = "retu",
.fops = &retu_user_fileops
};
/*
* Initialization
*
* @return 0 if successful, error value otherwise.
*/
int retu_user_init(void)
{
struct retu_irq *irq;
int res, i;
irq = kmalloc(sizeof(*irq) * RETU_MAX_IRQ_BUF_LEN, GFP_KERNEL);
if (irq == NULL) {
printk(KERN_ERR PFX "kmalloc failed\n");
return -ENOMEM;
}
memset(irq, 0, sizeof(*irq) * RETU_MAX_IRQ_BUF_LEN);
for (i = 0; i < RETU_MAX_IRQ_BUF_LEN; i++)
list_add(&irq[i].node, &retu_irqs_reserve);
retu_irq_block = irq;
spin_lock_init(&retu_irqs_lock);
mutex_init(&retu_mutex);
/* Request a misc device */
res = misc_register(&retu_device);
if (res < 0) {
printk(KERN_ERR PFX "unable to register misc device for %s\n",
retu_device.name);
kfree(irq);
return res;
}
return 0;
}
/*
* Cleanup.
*/
void retu_user_cleanup(void)
{
/* Unregister our misc device */
misc_deregister(&retu_device);
/* Unregister and disable all RETU interrupts used by this module */
retu_unreg_irq_handlers();
kfree(retu_irq_block);
}
MODULE_DESCRIPTION("Retu ASIC user space functions");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Mikko Ylinen");
/**
* drivers/cbus/retu-wdt.c
*
* Driver for Retu watchdog
*
* Copyright (C) 2004, 2005 Nokia Corporation
*
* Written by Amit Kucheria <amit.kucheria@nokia.com>
*
* This file is subject to the terms and conditions of the GNU General
* Public License. See the file "COPYING" in the main directory of this
* archive for more details.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/completion.h>
#include <linux/errno.h>
#include <linux/moduleparam.h>
#include <linux/platform_device.h>
#include "cbus.h"
#include "retu.h"
/* Watchdog timeout in seconds */
#define RETU_WDT_MIN_TIMER 0
#define RETU_WDT_DEFAULT_TIMER 32
#define RETU_WDT_MAX_TIMER 63
static struct completion retu_wdt_completion;
static DECLARE_MUTEX(retu_wdt_mutex); /* Avoid simultaneous writes to watchdog register */
static unsigned int period_val = RETU_WDT_DEFAULT_TIMER; /* Current period of watchdog */
static int counter_param = RETU_WDT_MAX_TIMER;
static int retu_modify_counter(unsigned int new)
{
int ret = 0;
if (new < RETU_WDT_MIN_TIMER || new > RETU_WDT_MAX_TIMER)
return -EINVAL;
down_interruptible(&retu_wdt_mutex);
period_val = new;
retu_write_reg(RETU_REG_WATCHDOG, (u16)period_val);
up(&retu_wdt_mutex);
return ret;
}
static ssize_t retu_wdt_period_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
/* Show current max counter */
return sprintf(buf, "%u\n", (u16)period_val);
}
static ssize_t retu_wdt_period_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
unsigned int new_period;
int ret;
if (sscanf(buf, "%u", &new_period) != 1) {
printk(KERN_ALERT "retu_wdt_period_store: Invalid input\n");
return -EINVAL;
}
ret = retu_modify_counter(new_period);
if (ret < 0)
return ret;
return strnlen(buf, count);
}
static ssize_t retu_wdt_counter_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
u16 counter;
/* Show current value in watchdog counter */
counter = retu_read_reg(RETU_REG_WATCHDOG);
/* Only the 5 LSB are important */
return snprintf(buf, PAGE_SIZE, "%u\n", (counter & 0x3F));
}
static DEVICE_ATTR(period, S_IRUGO | S_IWUSR, retu_wdt_period_show, \
retu_wdt_period_store);
static DEVICE_ATTR(counter, S_IRUGO, retu_wdt_counter_show, NULL);
static int __devinit retu_wdt_probe(struct device *dev)
{
int ret;
ret = device_create_file(dev, &dev_attr_period);
if (ret) {
printk(KERN_ERR "retu_wdt_probe: Error creating sys device file: period\n");
return ret;
}
ret = device_create_file(dev, &dev_attr_counter);
if (ret) {
device_remove_file(dev, &dev_attr_period);
printk(KERN_ERR "retu_wdt_probe: Error creating sys device file: counter\n");
}
return ret;
}
static int __devexit retu_wdt_remove(struct device *dev)
{
device_remove_file(dev, &dev_attr_period);
device_remove_file(dev, &dev_attr_counter);
return 0;
}
static void retu_wdt_device_release(struct device *dev)
{
complete(&retu_wdt_completion);
}
static struct platform_device retu_wdt_device = {
.name = "retu-watchdog",
.id = -1,
.dev = {
.release = retu_wdt_device_release,
},
};
static struct device_driver retu_wdt_driver = {
.name = "retu-watchdog",
.bus = &platform_bus_type,
.probe = retu_wdt_probe,
.remove = __devexit_p(retu_wdt_remove),
};
static int __init retu_wdt_init(void)
{
int ret;
init_completion(&retu_wdt_completion);
ret = driver_register(&retu_wdt_driver);
if (ret)
return ret;
ret = platform_device_register(&retu_wdt_device);
if (ret)
goto exit1;
/* passed as module parameter? */
ret = retu_modify_counter(counter_param);
if (ret == -EINVAL) {
ret = retu_modify_counter(RETU_WDT_DEFAULT_TIMER);
printk(KERN_INFO
"retu_wdt_init: Intializing to default value\n");
}
printk(KERN_INFO "Retu watchdog driver initialized\n");
return ret;
exit1:
driver_unregister(&retu_wdt_driver);
wait_for_completion(&retu_wdt_completion);
return ret;
}
static void __exit retu_wdt_exit(void)
{
platform_device_unregister(&retu_wdt_device);
driver_unregister(&retu_wdt_driver);
wait_for_completion(&retu_wdt_completion);
}
module_init(retu_wdt_init);
module_exit(retu_wdt_exit);
module_param(counter_param, int, 0);
MODULE_DESCRIPTION("Retu WatchDog");
MODULE_AUTHOR("Amit Kucheria");
MODULE_LICENSE("GPL");
This diff is collapsed.
/**
* drivers/cbus/retu.h
*
* Copyright (C) 2004, 2005 Nokia Corporation
*
* Written by Juha Yrjölä <juha.yrjola@nokia.com> and
* David Weinehall <david.weinehall@nokia.com>
*
* This file is subject to the terms and conditions of the GNU General
* Public License. See the file "COPYING" in the main directory of this
* archive for more details.
*
* 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.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __DRIVERS_CBUS_RETU_H
#define __DRIVERS_CBUS_RETU_H
#include <linux/types.h>
/* Registers */
#define RETU_REG_ASICR 0x00 /* ASIC ID & revision */
#define RETU_REG_IDR 0x01 /* Interrupt ID */
#define RETU_REG_IMR 0x02 /* Interrupt mask */
#define RETU_REG_RTCDSR 0x03 /* RTC seconds register */
#define RETU_REG_RTCHMR 0x04 /* RTC hours and minutes register */
#define RETU_REG_RTCHMAR 0x05 /* RTC hours and minutes alarm and time set register */
#define RETU_REG_RTCCALR 0x06 /* RTC calibration register */
#define RETU_REG_ADCR 0x08 /* ADC result */
#define RETU_REG_CC1 0x0d /* Common control register 1 */
#define RETU_REG_CC2 0x0e /* Common control register 2 */
#define RETU_REG_CTRL_CLR 0x0f /* Regulator clear register */
#define RETU_REG_CTRL_SET 0x10 /* Regulator set register */
#define RETU_REG_STATUS 0x16 /* Status register */
#define RETU_REG_WATCHDOG 0x17 /* Watchdog register */
#define RETU_REG_MAX 0x1f
/* Interrupt sources */
#define RETU_INT_PWR 0
#define RETU_INT_CHAR 1
#define RETU_INT_RTCS 2
#define RETU_INT_RTCM 3
#define RETU_INT_RTCD 4
#define RETU_INT_RTCA 5
#define RETU_INT_ADCS 8
#define MAX_RETU_IRQ_HANDLERS 16
int retu_read_reg(int reg);
void retu_write_reg(int reg, u16 val);
void retu_set_clear_reg_bits(int reg, u16 set, u16 clear);
int retu_read_adc(int channel);
int retu_request_irq(int id, void *irq_handler, unsigned long arg, char *name);
void retu_free_irq(int id);
void retu_enable_irq(int id);
void retu_disable_irq(int id);
void retu_ack_irq(int id);
#ifdef CONFIG_CBUS_RETU_USER
int retu_user_init(void);
void retu_user_cleanup(void);
#endif
extern spinlock_t retu_lock;
#endif /* __DRIVERS_CBUS_RETU_H */
This diff is collapsed.
/**
* drivers/cbus/tahvo-user.c
*
* Tahvo user space interface functions
*
* Copyright (C) 2004, 2005 Nokia Corporation
*
* Written by Mikko Ylinen <mikko.k.ylinen@nokia.com>
*
* This file is subject to the terms and conditions of the GNU General
* Public License. See the file "COPYING" in the main directory of this
* archive for more details.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/poll.h>
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/mutex.h>
#include <asm/uaccess.h>
#include "tahvo.h"
#include "user_retu_tahvo.h"
/* Maximum size of IRQ node buffer/pool */
#define TAHVO_MAX_IRQ_BUF_LEN 16
#define PFX "tahvo-user: "
/* Bitmap for marking the interrupt sources as having the handlers */
static u32 tahvo_irq_bits;
/* For allowing only one user process to subscribe to the tahvo interrupts */
static struct file *tahvo_irq_subscr = NULL;
/* For poll and IRQ passing */
struct tahvo_irq {
u32 id;
struct list_head node;
};
static spinlock_t tahvo_irqs_lock;
static struct tahvo_irq *tahvo_irq_block;
static LIST_HEAD(tahvo_irqs);
static LIST_HEAD(tahvo_irqs_reserve);
/* Wait queue - used when user wants to read the device */
DECLARE_WAIT_QUEUE_HEAD(tahvo_user_waitqueue);
/* Semaphore to protect irq subscription sequence */
static struct mutex tahvo_mutex;
/* This array specifies TAHVO register types (read/write/toggle) */
static const u8 tahvo_access_bits[] = {
1,
4,
1,
3,
3,
3,
3,
3,
3,
3,
3,
3,
3,
1
};
/*
* The handler for all TAHVO interrupts.
*
* arg is the interrupt source in TAHVO.
*/
static void tahvo_user_irq_handler(unsigned long arg)
{
struct tahvo_irq *irq;
/* user has to re-enable the interrupt once ready
* for receiving them again */
tahvo_disable_irq(arg);
tahvo_ack_irq(arg);
spin_lock(&tahvo_irqs_lock);
if (list_empty(&tahvo_irqs_reserve)) {
spin_unlock(&tahvo_irqs_lock);
return;
}
irq = list_entry((&tahvo_irqs_reserve)->next, struct tahvo_irq, node);
irq->id = arg;
list_move_tail(&irq->node, &tahvo_irqs);
spin_unlock(&tahvo_irqs_lock);
/* wake up waiting thread */
wake_up(&tahvo_user_waitqueue);
}
/*
* This routine sets up the interrupt handler and marks an interrupt source
* in TAHVO as a candidate for signal delivery to the user process.
*/
static int tahvo_user_subscribe_to_irq(int id, struct file *filp)
{
int ret;
mutex_lock(&tahvo_mutex);
if ((tahvo_irq_subscr != NULL) && (tahvo_irq_subscr != filp)) {
mutex_unlock(&tahvo_mutex);
return -EBUSY;
}
/* Store the file pointer of the first user process registering IRQs */
tahvo_irq_subscr = filp;
mutex_unlock(&tahvo_mutex);
if (tahvo_irq_bits & (1 << id))
return 0;
ret = tahvo_request_irq(id, tahvo_user_irq_handler, id, "");
if (ret < 0)
return ret;
/* Mark that this interrupt has a handler */
tahvo_irq_bits |= 1 << id;
return 0;
}
/*
* Unregister all TAHVO interrupt handlers
*/
static void tahvo_unreg_irq_handlers(void)
{
int id;
if (!tahvo_irq_bits)
return;
for (id = 0; id < MAX_TAHVO_IRQ_HANDLERS; id++)
if (tahvo_irq_bits & (1 << id))
tahvo_free_irq(id);
tahvo_irq_bits = 0;
}
/*
* Write to TAHVO register.
* Returns 0 upon success, a negative error value otherwise.
*/
static int tahvo_user_write_with_mask(u32 field, u16 value)
{
u32 mask;
u32 reg;
u_short tmp;
unsigned long flags;
mask = MASK(field);
reg = REG(field);
/* Detect bad mask and reg */
if (mask == 0 || reg > TAHVO_REG_MAX ||
tahvo_access_bits[reg] == READ_ONLY) {
printk(KERN_ERR PFX "invalid arguments (reg=%#x, mask=%#x)\n",
reg, mask);
return -EINVAL;
}
/* Justify value according to mask */
while (!(mask & 1)) {
value = value << 1;
mask = mask >> 1;
}
spin_lock_irqsave(&tahvo_lock, flags);
if (tahvo_access_bits[reg] == TOGGLE) {
/* No need to detect previous content of register */
tmp = 0;
} else {
/* Read current value of register */
tmp = tahvo_read_reg(reg);
}
/* Generate a new value */
tmp = (tmp & ~MASK(field)) | (value & MASK(field));
/* Write data to TAHVO */
tahvo_write_reg(reg, tmp);
spin_unlock_irqrestore(&tahvo_lock, flags);
return 0;
}
/*
* Read TAHVO register.
*/
static u32 tahvo_user_read_with_mask(u32 field)
{
u_short value;
u32 mask, reg;
mask = MASK(field);
reg = REG(field);
/* Detect bad mask and reg */
if (mask == 0 || reg > TAHVO_REG_MAX) {
printk(KERN_ERR PFX "invalid arguments (reg=%#x, mask=%#x)\n",
reg, mask);
return -EINVAL;
}
/* Read the register */
value = tahvo_read_reg(reg) & mask;
/* Right justify value */
while (!(mask & 1)) {
value = value >> 1;
mask = mask >> 1;
}
return value;
}
/*
* Close device
*/
static int tahvo_close(struct inode *inode, struct file *filp)
{
/* Unregister all interrupts that have been registered */
if (tahvo_irq_subscr == filp) {
tahvo_unreg_irq_handlers();
tahvo_irq_subscr = NULL;
}
return 0;
}
/*
* Device control (ioctl)
*/
static int tahvo_ioctl(struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg)
{
struct retu_tahvo_write_parms par;
int ret;
switch (cmd) {
case URT_IOCT_IRQ_SUBSCR:
return tahvo_user_subscribe_to_irq(arg, filp);
case TAHVO_IOCH_READ:
return tahvo_user_read_with_mask(arg);
case TAHVO_IOCX_WRITE:
ret = copy_from_user(&par, (void __user *) arg, sizeof(par));
if (ret)
printk(KERN_ERR "copy_from_user failed: %d\n", ret);
par.result = tahvo_user_write_with_mask(par.field, par.value);
ret = copy_to_user((void __user *) arg, &par, sizeof(par));
if (ret)
printk(KERN_ERR "copy_to_user failed: %d\n", ret);
break;
default:
return -ENOIOCTLCMD;
}
return 0;
}
/*
* Read from device
*/
static ssize_t tahvo_read(struct file *filp, char *buf, size_t count,
loff_t * offp)
{
struct tahvo_irq *irq;
u32 nr, i;
/* read not permitted if neither filp nor anyone has registered IRQs */
if (tahvo_irq_subscr != filp)
return -EPERM;
if ((count < sizeof(u32)) || ((count % sizeof(u32)) != 0))
return -EINVAL;
nr = count / sizeof(u32);
for (i = 0; i < nr; i++) {
unsigned long flags;
u32 irq_id;
int ret;
ret = wait_event_interruptible(tahvo_user_waitqueue,
!list_empty(&tahvo_irqs));
if (ret < 0)
return ret;
spin_lock_irqsave(&tahvo_irqs_lock, flags);
irq = list_entry((&tahvo_irqs)->next, struct tahvo_irq, node);
irq_id = irq->id;
list_move(&irq->node, &tahvo_irqs_reserve);
spin_unlock_irqrestore(&tahvo_irqs_lock, flags);
ret = copy_to_user(buf + i * sizeof(irq_id), &irq_id,
sizeof(irq_id));
if (ret)
printk(KERN_ERR "copy_to_user failed: %d\n", ret);
}
return count;
}
/*
* Poll method
*/
static unsigned tahvo_poll(struct file *filp, struct poll_table_struct *pt)
{
if (!list_empty(&tahvo_irqs))
return POLLIN;
poll_wait(filp, &tahvo_user_waitqueue, pt);
if (!list_empty(&tahvo_irqs))
return POLLIN;
else
return 0;
}
static struct file_operations tahvo_user_fileops = {
.owner = THIS_MODULE,
.ioctl = tahvo_ioctl,
.read = tahvo_read,
.release = tahvo_close,
.poll = tahvo_poll
};
static struct miscdevice tahvo_device = {
.minor = MISC_DYNAMIC_MINOR,
.name = "tahvo",
.fops = &tahvo_user_fileops
};
/*
* Initialization
*
* @return 0 if successful, error value otherwise.
*/
int tahvo_user_init(void)
{
struct tahvo_irq *irq;
int res, i;
irq = kmalloc(sizeof(*irq) * TAHVO_MAX_IRQ_BUF_LEN, GFP_KERNEL);
if (irq == NULL) {
printk(KERN_ERR PFX "kmalloc failed\n");
return -ENOMEM;
}
memset(irq, 0, sizeof(*irq) * TAHVO_MAX_IRQ_BUF_LEN);
for (i = 0; i < TAHVO_MAX_IRQ_BUF_LEN; i++)
list_add(&irq[i].node, &tahvo_irqs_reserve);
tahvo_irq_block = irq;
spin_lock_init(&tahvo_irqs_lock);
mutex_init(&tahvo_mutex);
/* Request a misc device */
res = misc_register(&tahvo_device);
if (res < 0) {
printk(KERN_ERR PFX "unable to register misc device for %s\n",
tahvo_device.name);
kfree(irq);
return res;
}
return 0;
}
/*
* Cleanup.
*/
void tahvo_user_cleanup(void)
{
/* Unregister our misc device */
misc_deregister(&tahvo_device);
/* Unregister and disable all TAHVO interrupts */
tahvo_unreg_irq_handlers();
kfree(tahvo_irq_block);
}
MODULE_DESCRIPTION("Tahvo ASIC user space functions");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Mikko Ylinen");
This diff is collapsed.
/*
* drivers/cbus/tahvo.h
*
* Copyright (C) 2004, 2005 Nokia Corporation
*
* Written by Juha Yrjl <juha.yrjola@nokia.com> and
* David Weinehall <david.weinehall@nokia.com>
*
* This file is subject to the terms and conditions of the GNU General
* Public License. See the file "COPYING" in the main directory of this
* archive for more details.
*
* 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.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __DRIVERS_CBUS_TAHVO_H
#define __DRIVERS_CBUS_TAHVO_H
#include <linux/types.h>
/* Registers */
#define TAHVO_REG_ASICR 0x00 /* ASIC ID & revision */
#define TAHVO_REG_IDR 0x01 /* Interrupt ID */
#define TAHVO_REG_IDSR 0x02 /* Interrupt status */
#define TAHVO_REG_IMR 0x03 /* Interrupt mask */
#define TAHVO_REG_LEDPWMR 0x05 /* LED PWM */
#define TAHVO_REG_USBR 0x06 /* USB control */
#define TAHVO_REG_MAX 0x0d
/* Interrupt sources */
#define TAHVO_INT_VBUSON 0
#define MAX_TAHVO_IRQ_HANDLERS 8
int tahvo_read_reg(int reg);
void tahvo_write_reg(int reg, u16 val);
void tahvo_set_clear_reg_bits(int reg, u16 set, u16 clear);
int tahvo_request_irq(int id, void *irq_handler, unsigned long arg, char *name);
void tahvo_free_irq(int id);
void tahvo_enable_irq(int id);
void tahvo_disable_irq(int id);
void tahvo_ack_irq(int id);
int tahvo_get_backlight_level(void);
int tahvo_get_max_backlight_level(void);
void tahvo_set_backlight_level(int level);
#ifdef CONFIG_CBUS_TAHVO_USER
int tahvo_user_init(void);
void tahvo_user_cleanup(void);
#endif
extern spinlock_t tahvo_lock;
#endif /* __DRIVERS_CBUS_TAHVO_H */
/**
* drivers/cbus/user_retu_tahvo.h
*
* Copyright (C) 2004, 2005 Nokia Corporation
*
* Written by Mikko Ylinen <mikko.k.ylinen@nokia.com>
*
* Definitions and types used by both retu-user and tahvo-user.
*
* This file is subject to the terms and conditions of the GNU General
* Public License. See the file "COPYING" in the main directory of this
* archive for more details.
*
* 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.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _USER_RETU_TAHVO_H
#define _USER_RETU_TAHVO_H
/* Chip IDs */
#define CHIP_RETU 1
#define CHIP_TAHVO 2
/* Register access type bits */
#define READ_ONLY 1
#define WRITE_ONLY 2
#define READ_WRITE 3
#define TOGGLE 4
#define MASK(field) ((u16)(field & 0xFFFF))
#define REG(field) ((u16)((field >> 16) & 0x3F))
/*** IOCTL definitions. These should be kept in sync with user space **********/
#define URT_IOC_MAGIC '`'
/*
* IOCTL function naming conventions:
* ==================================
* 0 -- No argument and return value
* S -- Set through a pointer
* T -- Tell directly with the argument value
* G -- Reply by setting through a pointer
* Q -- response is on the return value
* X -- S and G atomically
* H -- T and Q atomically
*/
/* General */
#define URT_IOCT_IRQ_SUBSCR _IO(URT_IOC_MAGIC, 0)
/* RETU */
#define RETU_IOCH_READ _IO(URT_IOC_MAGIC, 1)
#define RETU_IOCX_WRITE _IO(URT_IOC_MAGIC, 2)
#define RETU_IOCH_ADC_READ _IO(URT_IOC_MAGIC, 3)
/* TAHVO */
#define TAHVO_IOCH_READ _IO(URT_IOC_MAGIC, 4)
#define TAHVO_IOCX_WRITE _IO(URT_IOC_MAGIC, 5)
/* This structure is used for writing RETU/TAHVO registers */
struct retu_tahvo_write_parms {
u32 field;
u16 value;
u8 result;
};
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment