Commit 19deb684 authored by Juha Yrjola's avatar Juha Yrjola

ARM: OMAP: 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.
parent 279a7045
...@@ -748,6 +748,10 @@ source "drivers/mmc/Kconfig" ...@@ -748,6 +748,10 @@ source "drivers/mmc/Kconfig"
source "drivers/ssi/Kconfig" source "drivers/ssi/Kconfig"
if ARCH_OMAP
source "drivers/cbus/Kconfig"
endif
endmenu endmenu
source "fs/Kconfig" source "fs/Kconfig"
......
...@@ -31,7 +31,7 @@ obj-y += serial/ ...@@ -31,7 +31,7 @@ obj-y += serial/
obj-$(CONFIG_PARPORT) += parport/ obj-$(CONFIG_PARPORT) += parport/
obj-y += base/ block/ misc/ mfd/ net/ obj-y += base/ block/ misc/ mfd/ net/
obj-$(CONFIG_I2C) += i2c/ obj-$(CONFIG_I2C) += i2c/
obj-y += media/ ssi/ obj-y += media/ ssi/ cbus/
obj-$(CONFIG_NUBUS) += nubus/ obj-$(CONFIG_NUBUS) += nubus/
obj-$(CONFIG_ATM) += atm/ obj-$(CONFIG_ATM) += atm/
obj-$(CONFIG_PPC_PMAC) += macintosh/ obj-$(CONFIG_PPC_PMAC) += 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;
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;
}
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/config.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.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 <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 semaphore retu_sem;
/* 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;
down(&retu_sem);
if ((retu_irq_subscr != NULL) && (retu_irq_subscr != filp)) {
up(&retu_sem);
return -EBUSY;
}
/* Store the file pointer of the first user process registering IRQs */
retu_irq_subscr = filp;
up(&retu_sem);
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;
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:
copy_from_user(&par, (void __user *) arg, sizeof(par));
par.result = retu_user_write_with_mask(par.field, par.value);
copy_to_user((void __user *) arg, &par, sizeof(par));
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);
copy_to_user(buf + i * sizeof(irq_id), &irq_id, sizeof(irq_id));
}
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);
sema_init(&retu_sem, 1);
/* 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/config.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");
/**
* drivers/cbus/retu.c
*
* Support functions for Retu ASIC
*
* 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/module.h>
#include <linux/init.h>
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/device.h>
#include <linux/miscdevice.h>
#include <linux/poll.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <asm/uaccess.h>
#include <asm/arch/mux.h>
#include <asm/arch/gpio.h>
#include <asm/arch/board.h>
#include "cbus.h"
#include "retu.h"
#define RETU_ID 0x01
#define PFX "retu: "
static int retu_initialized;
static int retu_irq_pin;
static struct tasklet_struct retu_tasklet;
spinlock_t retu_lock = SPIN_LOCK_UNLOCKED;
static struct completion device_release;
struct retu_irq_handler_desc {
int (*func)(unsigned long);
unsigned long arg;
char name[8];
};
static struct retu_irq_handler_desc retu_irq_handlers[MAX_RETU_IRQ_HANDLERS];
/**
* retu_read_reg - Read a value from a register in Retu
* @reg: the register to read from
*
* This function returns the contents of the specified register
*/
int retu_read_reg(int reg)
{
BUG_ON(!retu_initialized);
return cbus_read_reg(cbus_host, RETU_ID, reg);
}
/**
* retu_write_reg - Write a value to a register in Retu
* @reg: the register to write to
* @reg: the value to write to the register
*
* This function writes a value to the specified register
*/
void retu_write_reg(int reg, u16 val)
{
BUG_ON(!retu_initialized);
cbus_write_reg(cbus_host, RETU_ID, reg, val);
}
#define ADC_MAX_CHAN_NUMBER 13
int retu_read_adc(int channel)
{
unsigned long flags;
int res;
if (channel < 0 || channel > ADC_MAX_CHAN_NUMBER)
return -EINVAL;
spin_lock_irqsave(&retu_lock, flags);
/* Select the channel and read result */
retu_write_reg(RETU_REG_ADCR, channel << 10);
res = retu_read_reg(RETU_REG_ADCR) & 0x3ff;
/* Unlock retu */
spin_unlock_irqrestore(&retu_lock, flags);
return res;
}
static u16 retu_disable_bogus_irqs(u16 mask)
{
int i;
for (i = 0; i < MAX_RETU_IRQ_HANDLERS; i++) {
if (mask & (1 << i))
continue;
if (retu_irq_handlers[i].func != NULL)
continue;
/* an IRQ was enabled but we don't have a handler for it */
printk(KERN_INFO PFX "disabling bogus IRQ %d\n", i);
mask |= (1 << i);
}
return mask;
}
/*
* Disable given RETU interrupt
*/
void retu_disable_irq(int id)
{
unsigned long flags;
u16 mask;
spin_lock_irqsave(&retu_lock, flags);
mask = retu_read_reg(RETU_REG_IMR);
mask |= 1 << id;
mask = retu_disable_bogus_irqs(mask);
retu_write_reg(RETU_REG_IMR, mask);
spin_unlock_irqrestore(&retu_lock, flags);
}
/*
* Enable given RETU interrupt
*/
void retu_enable_irq(int id)
{
unsigned long flags;
u16 mask;
if (id == 3) {
printk("Enabling Retu IRQ %d\n", id);
dump_stack();
}
spin_lock_irqsave(&retu_lock, flags);
mask = retu_read_reg(RETU_REG_IMR);
mask &= ~(1 << id);
mask = retu_disable_bogus_irqs(mask);
retu_write_reg(RETU_REG_IMR, mask);
spin_unlock_irqrestore(&retu_lock, flags);
}
/*
* Acknowledge given RETU interrupt
*/
void retu_ack_irq(int id)
{
retu_write_reg(RETU_REG_IDR, 1 << id);
}
/*
* RETU interrupt handler. Only schedules the tasklet.
*/
static irqreturn_t retu_irq_handler(int irq, void *dev_id, struct pt_regs *regs)
{
tasklet_schedule(&retu_tasklet);
return IRQ_HANDLED;
}
/*
* Tasklet handler
*/
static void retu_tasklet_handler(unsigned long data)
{
struct retu_irq_handler_desc *hnd;
u16 id;
u16 im;
int i;
for (;;) {
id = retu_read_reg(RETU_REG_IDR);
im = ~retu_read_reg(RETU_REG_IMR);
id &= im;
if (!id)
break;
for (i = 0; id != 0; i++, id >>= 1) {
if (!(id & 1))
continue;
hnd = &retu_irq_handlers[i];
if (hnd->func == NULL) {
/* Spurious retu interrupt - disable and ack it */
printk(KERN_INFO "Spurious Retu interrupt "
"(id %d)\n", i);
retu_disable_irq(i);
retu_ack_irq(i);
continue;
}
hnd->func(hnd->arg);
/*
* Don't acknowledge the interrupt here
* It must be done explicitly
*/
}
}
}
/*
* Register the handler for a given RETU interrupt source.
*/
int retu_request_irq(int id, void *irq_handler, unsigned long arg, char *name)
{
struct retu_irq_handler_desc *hnd;
if (irq_handler == NULL || id >= MAX_RETU_IRQ_HANDLERS ||
name == NULL) {
printk(KERN_ERR PFX "Invalid arguments to %s\n",
__FUNCTION__);
return -EINVAL;
}
hnd = &retu_irq_handlers[id];
if (hnd->func != NULL) {
printk(KERN_ERR PFX "IRQ %d already reserved\n", id);
return -EBUSY;
}
printk(KERN_INFO PFX "Registering interrupt %d for device %s\n",
id, name);
hnd->func = irq_handler;
hnd->arg = arg;
strlcpy(hnd->name, name, sizeof(hnd->name));
retu_ack_irq(id);
retu_enable_irq(id);
return 0;
}
/*
* Unregister the handler for a given RETU interrupt source.
*/
void retu_free_irq(int id)
{
struct retu_irq_handler_desc *hnd;
if (id >= MAX_RETU_IRQ_HANDLERS) {
printk(KERN_ERR PFX "Invalid argument to %s\n",
__FUNCTION__);
return;
}
hnd = &retu_irq_handlers[id];
if (hnd->func == NULL) {
printk(KERN_ERR PFX "IRQ %d already freed\n", id);
return;
}
retu_disable_irq(id);
hnd->func = NULL;
}
/**
* retu_power_off - Shut down power to system
*
* This function puts the system in power off state
*/
static void retu_power_off(void)
{
/* Ignore power button state */
retu_write_reg(RETU_REG_CC1, retu_read_reg(RETU_REG_CC1) | 2);
/* Expire watchdog immediately */
retu_write_reg(RETU_REG_WATCHDOG, 0);
/* Wait for poweroff*/
for (;;);
}
/**
* retu_probe - Probe for Retu ASIC
* @dev: the Retu device
*
* Probe for the Retu ASIC and allocate memory
* for its device-struct if found
*/
static int __devinit retu_probe(struct device *dev)
{
const struct omap_em_asic_bb5_config * em_asic_config;
int ret;
/* Prepare tasklet */
tasklet_init(&retu_tasklet, retu_tasklet_handler, 0);
em_asic_config = omap_get_config(OMAP_TAG_EM_ASIC_BB5,
struct omap_em_asic_bb5_config);
if (em_asic_config == NULL) {
printk(KERN_ERR PFX "Unable to retrieve config data\n");
return -ENODATA;
}
retu_irq_pin = em_asic_config->retu_irq_gpio;
if ((ret = omap_request_gpio(retu_irq_pin)) < 0) {
printk(KERN_ERR PFX "Unable to reserve IRQ GPIO\n");
return ret;
}
/* Set the pin as input */
omap_set_gpio_direction(retu_irq_pin, 1);
/* Rising edge triggers the IRQ */
set_irq_type(OMAP_GPIO_IRQ(retu_irq_pin), IRQT_RISING);
retu_initialized = 1;
/* Mask all RETU interrupts */
retu_write_reg(RETU_REG_IMR, 0xffff);
ret = request_irq(OMAP_GPIO_IRQ(retu_irq_pin), retu_irq_handler, 0,
"retu", 0);
if (ret < 0) {
printk(KERN_ERR PFX "Unable to register IRQ handler\n");
omap_free_gpio(retu_irq_pin);
return ret;
}
/* Register power off function */
pm_power_off = retu_power_off;
#ifdef CONFIG_CBUS_RETU_USER
/* Initialize user-space interface */
if (retu_user_init() < 0) {
printk(KERN_ERR "Unable to initialize driver\n");
free_irq(OMAP_GPIO_IRQ(retu_irq_pin), 0);
omap_free_gpio(retu_irq_pin);
return ret;
}
#endif
return 0;
}
static int retu_remove(struct device *dev)
{
#ifdef CONFIG_CBUS_RETU_USER
retu_user_cleanup();
#endif
/* Mask all RETU interrupts */
retu_write_reg(RETU_REG_IMR, 0xffff);
free_irq(OMAP_GPIO_IRQ(retu_irq_pin), 0);
omap_free_gpio(retu_irq_pin);
tasklet_kill(&retu_tasklet);
return 0;
}
static void retu_device_release(struct device *dev)
{
complete(&device_release);
}
static struct device_driver retu_driver = {
.name = "retu",
.bus = &platform_bus_type,
.probe = retu_probe,
.remove = retu_remove,
};
static struct platform_device retu_device = {
.name = "retu",
.id = -1,
.dev = {
.release = retu_device_release,
}
};
/**
* retu_init - initialise Retu driver
*
* Initialise the Retu driver and return 0 if everything worked ok
*/
static int __init retu_init(void)
{
int ret = 0;
printk(KERN_INFO "Retu driver initialising\n");
init_completion(&device_release);
if ((ret = driver_register(&retu_driver)) < 0)
return ret;
if ((ret = platform_device_register(&retu_device)) < 0) {
driver_unregister(&retu_driver);
return ret;
}
return 0;
}
/*
* Cleanup
*/
static void __exit retu_exit(void)
{
platform_device_unregister(&retu_device);
driver_unregister(&retu_driver);
wait_for_completion(&device_release);
}
EXPORT_SYMBOL(retu_request_irq);
EXPORT_SYMBOL(retu_free_irq);
EXPORT_SYMBOL(retu_enable_irq);
EXPORT_SYMBOL(retu_disable_irq);
EXPORT_SYMBOL(retu_ack_irq);
EXPORT_SYMBOL(retu_read_reg);
EXPORT_SYMBOL(retu_write_reg);
subsys_initcall(retu_init);
module_exit(retu_exit);
MODULE_DESCRIPTION("Retu ASIC control");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Juha Yrjl, David Weinehall, and Mikko Ylinen");
/**
* 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_ASICID 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);
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 <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 semaphore tahvo_sem;
/* 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;
down(&tahvo_sem);
if ((tahvo_irq_subscr != NULL) && (tahvo_irq_subscr != filp)) {
up(&tahvo_sem);
return -EBUSY;
}
/* Store the file pointer of the first user process registering IRQs */
tahvo_irq_subscr = filp;
up(&tahvo_sem);
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;
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:
copy_from_user(&par, (void __user *) arg, sizeof(par));
par.result = tahvo_user_write_with_mask(par.field, par.value);
copy_to_user((void __user *) arg, &par, sizeof(par));
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);
copy_to_user(buf + i * sizeof(irq_id), &irq_id, sizeof(irq_id));
}
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);
sema_init(&tahvo_sem, 1);
/* 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");
/**
* drivers/cbus/tahvo.c
*
* Support functions for Tahvo ASIC
*
* 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/module.h>
#include <linux/init.h>
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/device.h>
#include <linux/miscdevice.h>
#include <linux/poll.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <asm/uaccess.h>
#include <asm/arch/mux.h>
#include <asm/arch/gpio.h>
#include <asm/arch/board.h>
#include "cbus.h"
#include "tahvo.h"
#define TAHVO_ID 0x02
#define PFX "tahvo: "
static int tahvo_initialized;
static int tahvo_irq_pin;
static struct tasklet_struct tahvo_tasklet;
spinlock_t tahvo_lock = SPIN_LOCK_UNLOCKED;
static struct completion device_release;
struct tahvo_irq_handler_desc {
int (*func)(unsigned long);
unsigned long arg;
char name[8];
};
static struct tahvo_irq_handler_desc tahvo_irq_handlers[MAX_TAHVO_IRQ_HANDLERS];
/**
* tahvo_read_reg - Read a value from a register in Tahvo
* @reg: the register to read from
*
* This function returns the contents of the specified register
*/
int tahvo_read_reg(int reg)
{
BUG_ON(!tahvo_initialized);
return cbus_read_reg(cbus_host, TAHVO_ID, reg);
}
/**
* tahvo_write_reg - Write a value to a register in Tahvo
* @reg: the register to write to
* @reg: the value to write to the register
*
* This function writes a value to the specified register
*/
void tahvo_write_reg(int reg, u16 val)
{
BUG_ON(!tahvo_initialized);
cbus_write_reg(cbus_host, TAHVO_ID, reg, val);
}
/*
* Disable given TAHVO interrupt
*/
void tahvo_disable_irq(int id)
{
unsigned long flags;
u16 mask;
spin_lock_irqsave(&tahvo_lock, flags);
mask = tahvo_read_reg(TAHVO_REG_IMR);
mask |= 1 << id;
tahvo_write_reg(TAHVO_REG_IMR, mask);
spin_unlock_irqrestore(&tahvo_lock, flags);
}
/*
* Enable given TAHVO interrupt
*/
void tahvo_enable_irq(int id)
{
unsigned long flags;
u16 mask;
spin_lock_irqsave(&tahvo_lock, flags);
mask = tahvo_read_reg(TAHVO_REG_IMR);
mask &= ~(1 << id);
tahvo_write_reg(TAHVO_REG_IMR, mask);
spin_unlock_irqrestore(&tahvo_lock, flags);
}
/*
* Acknowledge given TAHVO interrupt
*/
void tahvo_ack_irq(int id)
{
tahvo_write_reg(TAHVO_REG_IDR, 1 << id);
}
static int tahvo_7bit_backlight;
int tahvo_get_backlight_level(void)
{
int mask;
if (tahvo_7bit_backlight)
mask = 0x7f;
else
mask = 0x0f;
return tahvo_read_reg(TAHVO_REG_LEDPWMR) & mask;
}
int tahvo_get_max_backlight_level(void)
{
if (tahvo_7bit_backlight)
return 0x7f;
else
return 0x0f;
}
void tahvo_set_backlight_level(int level)
{
int max_level;
max_level = tahvo_get_max_backlight_level();
if (level > max_level)
level = max_level;
tahvo_write_reg(TAHVO_REG_LEDPWMR, level);
}
/*
* TAHVO interrupt handler. Only schedules the tasklet.
*/
static irqreturn_t tahvo_irq_handler(int irq, void *dev_id, struct pt_regs *regs)
{
tasklet_schedule(&tahvo_tasklet);
return IRQ_HANDLED;
}
/*
* Tasklet handler
*/
static void tahvo_tasklet_handler(unsigned long data)
{
struct tahvo_irq_handler_desc *hnd;
u16 id;
u16 im;
int i;
for (;;) {
id = tahvo_read_reg(TAHVO_REG_IDR);
im = ~tahvo_read_reg(TAHVO_REG_IMR);
id &= im;
if (!id)
break;
for (i = 0; id != 0; i++, id >>= 1) {
if (!(id & 1))
continue;
hnd = &tahvo_irq_handlers[i];
if (hnd->func == NULL) {
/* Spurious tahvo interrupt - just ack it */
printk(KERN_INFO "Spurious Tahvo interrupt "
"(id %d)\n", i);
tahvo_disable_irq(i);
tahvo_ack_irq(i);
continue;
}
hnd->func(hnd->arg);
/*
* Don't acknowledge the interrupt here
* It must be done explicitly
*/
}
}
}
/*
* Register the handler for a given TAHVO interrupt source.
*/
int tahvo_request_irq(int id, void *irq_handler, unsigned long arg, char *name)
{
struct tahvo_irq_handler_desc *hnd;
if (irq_handler == NULL || id >= MAX_TAHVO_IRQ_HANDLERS ||
name == NULL) {
printk(KERN_ERR PFX "Invalid arguments to %s\n",
__FUNCTION__);
return -EINVAL;
}
hnd = &tahvo_irq_handlers[id];
if (hnd->func != NULL) {
printk(KERN_ERR PFX "IRQ %d already reserved\n", id);
return -EBUSY;
}
printk(KERN_INFO PFX "Registering interrupt %d for device %s\n",
id, name);
hnd->func = irq_handler;
hnd->arg = arg;
strlcpy(hnd->name, name, sizeof(hnd->name));
tahvo_ack_irq(id);
tahvo_enable_irq(id);
return 0;
}
/*
* Unregister the handler for a given TAHVO interrupt source.
*/
void tahvo_free_irq(int id)
{
struct tahvo_irq_handler_desc *hnd;
if (id >= MAX_TAHVO_IRQ_HANDLERS) {
printk(KERN_ERR PFX "Invalid argument to %s\n",
__FUNCTION__);
return;
}
hnd = &tahvo_irq_handlers[id];
if (hnd->func == NULL) {
printk(KERN_ERR PFX "IRQ %d already freed\n", id);
return;
}
tahvo_disable_irq(id);
hnd->func = NULL;
}
/**
* tahvo_probe - Probe for Tahvo ASIC
* @dev: the Tahvo device
*
* Probe for the Tahvo ASIC and allocate memory
* for its device-struct if found
*/
static int __devinit tahvo_probe(struct device *dev)
{
const struct omap_em_asic_bb5_config * em_asic_config;
int rev, ret;
/* Prepare tasklet */
tasklet_init(&tahvo_tasklet, tahvo_tasklet_handler, 0);
em_asic_config = omap_get_config(OMAP_TAG_EM_ASIC_BB5,
struct omap_em_asic_bb5_config);
if (em_asic_config == NULL) {
printk(KERN_ERR PFX "Unable to retrieve config data\n");
return -ENODATA;
}
tahvo_initialized = 1;
rev = tahvo_read_reg(TAHVO_REG_ASICR);
if (((rev >> 8) & 0x0f) != 0x03) {
printk(KERN_ERR PFX "Tahvo chip not found\n");
return -ENODEV;
}
rev &= 0xff;
if (rev >= 0x50)
tahvo_7bit_backlight = 1;
printk(KERN_INFO "Tahvo v%d.%d found\n", rev >> 4, rev & 0x0f);
tahvo_irq_pin = em_asic_config->tahvo_irq_gpio;
if ((ret = omap_request_gpio(tahvo_irq_pin)) < 0) {
printk(KERN_ERR PFX "Unable to reserve IRQ GPIO\n");
return ret;
}
/* Set the pin as input */
omap_set_gpio_direction(tahvo_irq_pin, 1);
/* Rising edge triggers the IRQ */
set_irq_type(OMAP_GPIO_IRQ(tahvo_irq_pin), IRQT_RISING);
/* Mask all TAHVO interrupts */
tahvo_write_reg(TAHVO_REG_IMR, 0xffff);
ret = request_irq(OMAP_GPIO_IRQ(tahvo_irq_pin), tahvo_irq_handler, 0,
"tahvo", 0);
if (ret < 0) {
printk(KERN_ERR PFX "Unable to register IRQ handler\n");
omap_free_gpio(tahvo_irq_pin);
return ret;
}
#ifdef CONFIG_CBUS_TAHVO_USER
/* Initialize user-space interface */
if (tahvo_user_init() < 0) {
printk(KERN_ERR "Unable to initialize driver\n");
free_irq(OMAP_GPIO_IRQ(tahvo_irq_pin), 0);
omap_free_gpio(tahvo_irq_pin);
return ret;
}
#endif
return 0;
}
static int tahvo_remove(struct device *dev)
{
#ifdef CONFIG_CBUS_TAHVO_USER
tahvo_user_cleanup();
#endif
/* Mask all TAHVO interrupts */
tahvo_write_reg(TAHVO_REG_IMR, 0xffff);
free_irq(OMAP_GPIO_IRQ(tahvo_irq_pin), 0);
omap_free_gpio(tahvo_irq_pin);
tasklet_kill(&tahvo_tasklet);
return 0;
}
static void tahvo_device_release(struct device *dev)
{
complete(&device_release);
}
static struct device_driver tahvo_driver = {
.name = "tahvo",
.bus = &platform_bus_type,
.probe = tahvo_probe,
.remove = tahvo_remove,
};
static struct platform_device tahvo_device = {
.name = "tahvo",
.id = -1,
.dev = {
.release = tahvo_device_release,
}
};
/**
* tahvo_init - initialise Tahvo driver
*
* Initialise the Tahvo driver and return 0 if everything worked ok
*/
static int __init tahvo_init(void)
{
int ret = 0;
printk(KERN_INFO "Tahvo driver initialising\n");
init_completion(&device_release);
if ((ret = driver_register(&tahvo_driver)) < 0)
return ret;
if ((ret = platform_device_register(&tahvo_device)) < 0) {
driver_unregister(&tahvo_driver);
return ret;
}
return 0;
}
/*
* Cleanup
*/
static void __exit tahvo_exit(void)
{
platform_device_unregister(&tahvo_device);
driver_unregister(&tahvo_driver);
wait_for_completion(&device_release);
}
EXPORT_SYMBOL(tahvo_request_irq);
EXPORT_SYMBOL(tahvo_free_irq);
EXPORT_SYMBOL(tahvo_enable_irq);
EXPORT_SYMBOL(tahvo_disable_irq);
EXPORT_SYMBOL(tahvo_ack_irq);
EXPORT_SYMBOL(tahvo_read_reg);
EXPORT_SYMBOL(tahvo_write_reg);
EXPORT_SYMBOL(tahvo_get_backlight_level);
EXPORT_SYMBOL(tahvo_get_max_backlight_level);
EXPORT_SYMBOL(tahvo_set_backlight_level);
subsys_initcall(tahvo_init);
module_exit(tahvo_exit);
MODULE_DESCRIPTION("Tahvo ASIC control");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Juha Yrjl, David Weinehall, and Mikko Ylinen");
/*
* 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);
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
/*
* linux/include/asm-arm/arch-omap/board-nokia.h
*
* Information structures for Nokia-specific board config data
*
* Copyright (C) 2005 Nokia Corporation
*/
#ifndef _OMAP_BOARD_NOKIA_H
#define _OMAP_BOARD_NOKIA_H
#include <linux/types.h>
#define OMAP_TAG_NOKIA_BT 0x4e01
#define OMAP_TAG_WLAN_CX3110X 0x4e02
#define OMAP_TAG_CBUS 0x4e03
#define OMAP_TAG_EM_ASIC_BB5 0x4e04
#define BT_CHIP_CSR 1
#define BT_CHIP_TI 2
#define BT_SYSCLK_12 1
#define BT_SYSCLK_38_4 2
struct omap_bluetooth_config {
u8 chip_type;
u8 bt_wakeup_gpio;
u8 host_wakeup_gpio;
u8 reset_gpio;
u8 bt_uart;
u8 bd_addr[6];
u8 bt_sysclk;
};
struct omap_wlan_cx3110x_config {
s16 antenna_sw_gpio;
s16 power_gpio;
s16 irq_gpio;
s16 spi_cs_gpio;
};
struct omap_cbus_config {
s16 clk_gpio;
s16 dat_gpio;
s16 sel_gpio;
};
struct omap_em_asic_bb5_config {
s16 retu_irq_gpio;
s16 tahvo_irq_gpio;
};
#endif
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#define OMAP_TAG_BOOT_REASON 0x4f80 #define OMAP_TAG_BOOT_REASON 0x4f80
#define OMAP_TAG_FLASH_PART 0x4f81 #define OMAP_TAG_FLASH_PART 0x4f81
#define OMAP_TAG_VERSION_STR 0x4f82
struct omap_clock_config { struct omap_clock_config {
/* 0 for 12 MHz, 1 for 13 MHz and 2 for 19.2 MHz */ /* 0 for 12 MHz, 1 for 13 MHz and 2 for 19.2 MHz */
...@@ -106,6 +107,12 @@ struct omap_gpio_switch_config { ...@@ -106,6 +107,12 @@ struct omap_gpio_switch_config {
int key_code:24; /* Linux key code */ int key_code:24; /* Linux key code */
}; };
struct omap_uart_config {
/* Bit field of UARTs present; bit 0 --> UART1 */
unsigned int enabled_uarts;
};
struct omap_flash_part_config { struct omap_flash_part_config {
char part_table[0]; char part_table[0];
}; };
...@@ -114,11 +121,14 @@ struct omap_boot_reason_config { ...@@ -114,11 +121,14 @@ struct omap_boot_reason_config {
char reason_str[12]; char reason_str[12];
}; };
struct omap_uart_config { struct omap_version_config {
/* Bit field of UARTs present; bit 0 --> UART1 */ char component[12];
unsigned int enabled_uarts; char version[12];
}; };
#include <asm-arm/arch-omap/board-nokia.h>
struct omap_board_config_entry { struct omap_board_config_entry {
u16 tag; u16 tag;
u16 len; u16 len;
......
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