mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-11 20:48:49 +08:00
staging driver merge for 3.4-rc1
Here is the big drivers/staging/ merge for 3.4-rc1 Lots of new driver updates here, with the addition of a few new ones, and only one moving out of the staging tree to the "real" part of the kernel (the hyperv scsi driver, acked by the scsi maintainer). There are also loads of cleanups, fixes, and other minor things in here, all self-contained in the drivers/staging/ tree. Overall we reversed the recent trend by adding more lines than we removed: 379 files changed, 37952 insertions(+), 14153 deletions(-) Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.18 (GNU/Linux) iEYEABECAAYFAk9necMACgkQMUfUDdst+ykn0gCgvUcAu8khJuLXKArhcyrIb7XH qVgAoIp4/Ntb+zNSI3UGX03VLx15VSdi =z2fR -----END PGP SIGNATURE----- Merge tag 'staging-3.3' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging Pull big staging driver updates from Greg KH: "Here is the big drivers/staging/ merge for 3.4-rc1 Lots of new driver updates here, with the addition of a few new ones, and only one moving out of the staging tree to the "real" part of the kernel (the hyperv scsi driver, acked by the scsi maintainer). There are also loads of cleanups, fixes, and other minor things in here, all self-contained in the drivers/staging/ tree. Overall we reversed the recent trend by adding more lines than we removed: 379 files changed, 37952 insertions(+), 14153 deletions(-)" * tag 'staging-3.3' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging: (360 commits) staging/zmem: Use lockdep_assert_held instead of spin_is_locked Staging: rtl8187se: r8180_wx.c: Cleaned up comments Staging: rtl8187se: r8180_wx.c: Removed old comments Staging: rtl8187se: r8180_dm.c: Removed old comments Staging: android: ram_console.c: Staging: rtl8187se: r8180_dm.c: Fix comments Staging: rtl8187se: r8180_dm.c: Fix spacing issues Staging: rtl8187se: r8180_dm.c Fixed indentation issues Staging: rtl8187se: r8180_dm.c: Fix brackets Staging: rtl8187se: r8180_dm.c: Removed spaces before tab stop staging: vme: fix section mismatches in linux-next 20120314 Staging: rtl8187se: r8180_core.c: Fix some long line issues Staging: rtl8187se: r8180_core.c: Fix some spacing issues Staging: rtl8187se: r8180_core.c: Removed trailing spaces staging: mei: remove driver internal versioning Staging: rtl8187se: r8180_core.c: Cleaned up if statement staging: ozwpan depends on NET staging: ozwpan: added maintainer for ozwpan driver staging/mei: propagate error codes up in the write flow drivers:staging:mei Fix some typos in staging/mei ...
This commit is contained in:
commit
71e7ff2578
@ -6395,6 +6395,11 @@ W: http://wiki.laptop.org/go/DCON
|
||||
S: Odd Fixes
|
||||
F: drivers/staging/olpc_dcon/
|
||||
|
||||
STAGING - OZMO DEVICES USB OVER WIFI DRIVER
|
||||
M: Chris Kelly <ckelly@ozmodevices.com>
|
||||
S: Maintained
|
||||
F: drivers/staging/ozwpan/
|
||||
|
||||
STAGING - PARALLEL LCD/KEYPAD PANEL DRIVER
|
||||
M: Willy Tarreau <willy@meta-x.org>
|
||||
S: Odd Fixes
|
||||
|
@ -40,8 +40,6 @@ source "drivers/net/Kconfig"
|
||||
|
||||
source "drivers/isdn/Kconfig"
|
||||
|
||||
source "drivers/telephony/Kconfig"
|
||||
|
||||
# input before char - char/joystick depends on it. As does USB.
|
||||
|
||||
source "drivers/input/Kconfig"
|
||||
|
@ -86,7 +86,6 @@ obj-$(CONFIG_POWER_SUPPLY) += power/
|
||||
obj-$(CONFIG_HWMON) += hwmon/
|
||||
obj-$(CONFIG_THERMAL) += thermal/
|
||||
obj-$(CONFIG_WATCHDOG) += watchdog/
|
||||
obj-$(CONFIG_PHONE) += telephony/
|
||||
obj-$(CONFIG_MD) += md/
|
||||
obj-$(CONFIG_BT) += bluetooth/
|
||||
obj-$(CONFIG_ACCESSIBILITY) += accessibility/
|
||||
|
@ -662,6 +662,13 @@ config VMWARE_PVSCSI
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called vmw_pvscsi.
|
||||
|
||||
config HYPERV_STORAGE
|
||||
tristate "Microsoft Hyper-V virtual storage driver"
|
||||
depends on SCSI && HYPERV
|
||||
default HYPERV
|
||||
help
|
||||
Select this option to enable the Hyper-V virtual storage driver.
|
||||
|
||||
config LIBFC
|
||||
tristate "LibFC module"
|
||||
select SCSI_FC_ATTRS
|
||||
|
@ -142,6 +142,7 @@ obj-$(CONFIG_SCSI_BNX2_ISCSI) += libiscsi.o bnx2i/
|
||||
obj-$(CONFIG_BE2ISCSI) += libiscsi.o be2iscsi/
|
||||
obj-$(CONFIG_SCSI_PMCRAID) += pmcraid.o
|
||||
obj-$(CONFIG_VMWARE_PVSCSI) += vmw_pvscsi.o
|
||||
obj-$(CONFIG_HYPERV_STORAGE) += hv_storvsc.o
|
||||
|
||||
obj-$(CONFIG_ARM) += arm/
|
||||
|
||||
@ -170,6 +171,8 @@ scsi_mod-$(CONFIG_SCSI_PROC_FS) += scsi_proc.o
|
||||
scsi_mod-y += scsi_trace.o
|
||||
scsi_mod-$(CONFIG_PM) += scsi_pm.o
|
||||
|
||||
hv_storvsc-y := storvsc_drv.o
|
||||
|
||||
scsi_tgt-y += scsi_tgt_lib.o scsi_tgt_if.o
|
||||
|
||||
sd_mod-objs := sd.o
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -76,8 +76,6 @@ source "drivers/staging/vt6655/Kconfig"
|
||||
|
||||
source "drivers/staging/vt6656/Kconfig"
|
||||
|
||||
source "drivers/staging/hv/Kconfig"
|
||||
|
||||
source "drivers/staging/vme/Kconfig"
|
||||
|
||||
source "drivers/staging/sep/Kconfig"
|
||||
@ -88,6 +86,8 @@ source "drivers/staging/zram/Kconfig"
|
||||
|
||||
source "drivers/staging/zcache/Kconfig"
|
||||
|
||||
source "drivers/staging/zsmalloc/Kconfig"
|
||||
|
||||
source "drivers/staging/wlags49_h2/Kconfig"
|
||||
|
||||
source "drivers/staging/wlags49_h25/Kconfig"
|
||||
@ -128,4 +128,10 @@ source "drivers/staging/omapdrm/Kconfig"
|
||||
|
||||
source "drivers/staging/android/Kconfig"
|
||||
|
||||
source "drivers/staging/telephony/Kconfig"
|
||||
|
||||
source "drivers/staging/ramster/Kconfig"
|
||||
|
||||
source "drivers/staging/ozwpan/Kconfig"
|
||||
|
||||
endif # STAGING
|
||||
|
@ -29,13 +29,12 @@ obj-$(CONFIG_USB_SERIAL_QUATECH_USB2) += quatech_usb2/
|
||||
obj-$(CONFIG_OCTEON_ETHERNET) += octeon/
|
||||
obj-$(CONFIG_VT6655) += vt6655/
|
||||
obj-$(CONFIG_VT6656) += vt6656/
|
||||
obj-$(CONFIG_HYPERV) += hv/
|
||||
obj-$(CONFIG_VME_BUS) += vme/
|
||||
obj-$(CONFIG_DX_SEP) += sep/
|
||||
obj-$(CONFIG_IIO) += iio/
|
||||
obj-$(CONFIG_ZRAM) += zram/
|
||||
obj-$(CONFIG_XVMALLOC) += zram/
|
||||
obj-$(CONFIG_ZCACHE) += zcache/
|
||||
obj-$(CONFIG_ZSMALLOC) += zsmalloc/
|
||||
obj-$(CONFIG_WLAGS49_H2) += wlags49_h2/
|
||||
obj-$(CONFIG_WLAGS49_H25) += wlags49_h25/
|
||||
obj-$(CONFIG_FB_SM7XX) += sm7xx/
|
||||
@ -55,3 +54,6 @@ obj-$(CONFIG_INTEL_MEI) += mei/
|
||||
obj-$(CONFIG_MFD_NVEC) += nvec/
|
||||
obj-$(CONFIG_DRM_OMAP) += omapdrm/
|
||||
obj-$(CONFIG_ANDROID) += android/
|
||||
obj-$(CONFIG_PHONE) += telephony/
|
||||
obj-$(CONFIG_RAMSTER) += ramster/
|
||||
obj-$(CONFIG_USB_WPAN_HCD) += ozwpan/
|
||||
|
@ -25,65 +25,17 @@ config ANDROID_LOGGER
|
||||
tristate "Android log driver"
|
||||
default n
|
||||
|
||||
config ANDROID_RAM_CONSOLE
|
||||
bool "Android RAM buffer console"
|
||||
depends on !S390 && !UML
|
||||
default n
|
||||
|
||||
config ANDROID_RAM_CONSOLE_ENABLE_VERBOSE
|
||||
bool "Enable verbose console messages on Android RAM console"
|
||||
default y
|
||||
depends on ANDROID_RAM_CONSOLE
|
||||
|
||||
menuconfig ANDROID_RAM_CONSOLE_ERROR_CORRECTION
|
||||
bool "Android RAM Console Enable error correction"
|
||||
default n
|
||||
depends on ANDROID_RAM_CONSOLE
|
||||
depends on !ANDROID_RAM_CONSOLE_EARLY_INIT
|
||||
config ANDROID_PERSISTENT_RAM
|
||||
bool
|
||||
select REED_SOLOMON
|
||||
select REED_SOLOMON_ENC8
|
||||
select REED_SOLOMON_DEC8
|
||||
|
||||
if ANDROID_RAM_CONSOLE_ERROR_CORRECTION
|
||||
|
||||
config ANDROID_RAM_CONSOLE_ERROR_CORRECTION_DATA_SIZE
|
||||
int "Android RAM Console Data data size"
|
||||
default 128
|
||||
help
|
||||
Must be a power of 2.
|
||||
|
||||
config ANDROID_RAM_CONSOLE_ERROR_CORRECTION_ECC_SIZE
|
||||
int "Android RAM Console ECC size"
|
||||
default 16
|
||||
|
||||
config ANDROID_RAM_CONSOLE_ERROR_CORRECTION_SYMBOL_SIZE
|
||||
int "Android RAM Console Symbol size"
|
||||
default 8
|
||||
|
||||
config ANDROID_RAM_CONSOLE_ERROR_CORRECTION_POLYNOMIAL
|
||||
hex "Android RAM Console Polynomial"
|
||||
default 0x19 if (ANDROID_RAM_CONSOLE_ERROR_CORRECTION_SYMBOL_SIZE = 4)
|
||||
default 0x29 if (ANDROID_RAM_CONSOLE_ERROR_CORRECTION_SYMBOL_SIZE = 5)
|
||||
default 0x61 if (ANDROID_RAM_CONSOLE_ERROR_CORRECTION_SYMBOL_SIZE = 6)
|
||||
default 0x89 if (ANDROID_RAM_CONSOLE_ERROR_CORRECTION_SYMBOL_SIZE = 7)
|
||||
default 0x11d if (ANDROID_RAM_CONSOLE_ERROR_CORRECTION_SYMBOL_SIZE = 8)
|
||||
|
||||
endif # ANDROID_RAM_CONSOLE_ERROR_CORRECTION
|
||||
|
||||
config ANDROID_RAM_CONSOLE_EARLY_INIT
|
||||
bool "Start Android RAM console early"
|
||||
config ANDROID_RAM_CONSOLE
|
||||
bool "Android RAM buffer console"
|
||||
depends on !S390 && !UML
|
||||
select ANDROID_PERSISTENT_RAM
|
||||
default n
|
||||
depends on ANDROID_RAM_CONSOLE
|
||||
|
||||
config ANDROID_RAM_CONSOLE_EARLY_ADDR
|
||||
hex "Android RAM console virtual address"
|
||||
default 0
|
||||
depends on ANDROID_RAM_CONSOLE_EARLY_INIT
|
||||
|
||||
config ANDROID_RAM_CONSOLE_EARLY_SIZE
|
||||
hex "Android RAM console buffer size"
|
||||
default 0
|
||||
depends on ANDROID_RAM_CONSOLE_EARLY_INIT
|
||||
|
||||
config ANDROID_TIMED_OUTPUT
|
||||
bool "Timed output class driver"
|
||||
@ -102,6 +54,32 @@ config ANDROID_LOW_MEMORY_KILLER
|
||||
|
||||
source "drivers/staging/android/switch/Kconfig"
|
||||
|
||||
config ANDROID_INTF_ALARM
|
||||
bool "Android alarm driver"
|
||||
depends on RTC_CLASS
|
||||
default n
|
||||
help
|
||||
Provides non-wakeup and rtc backed wakeup alarms based on rtc or
|
||||
elapsed realtime, and a non-wakeup alarm on the monotonic clock.
|
||||
Also provides an interface to set the wall time which must be used
|
||||
for elapsed realtime to work.
|
||||
|
||||
config ANDROID_INTF_ALARM_DEV
|
||||
bool "Android alarm device"
|
||||
depends on ANDROID_INTF_ALARM
|
||||
default y
|
||||
help
|
||||
Exports the alarm interface to user-space.
|
||||
|
||||
config ANDROID_ALARM_OLDDRV_COMPAT
|
||||
bool "Android Alarm compatability with old drivers"
|
||||
depends on ANDROID_INTF_ALARM
|
||||
default n
|
||||
help
|
||||
Provides preprocessor alias to aid compatability with
|
||||
older out-of-tree drivers that use the Android Alarm
|
||||
in-kernel API. This will be removed eventually.
|
||||
|
||||
endif # if ANDROID
|
||||
|
||||
endmenu
|
||||
|
@ -1,8 +1,11 @@
|
||||
obj-$(CONFIG_ANDROID_BINDER_IPC) += binder.o
|
||||
obj-$(CONFIG_ASHMEM) += ashmem.o
|
||||
obj-$(CONFIG_ANDROID_LOGGER) += logger.o
|
||||
obj-$(CONFIG_ANDROID_PERSISTENT_RAM) += persistent_ram.o
|
||||
obj-$(CONFIG_ANDROID_RAM_CONSOLE) += ram_console.o
|
||||
obj-$(CONFIG_ANDROID_TIMED_OUTPUT) += timed_output.o
|
||||
obj-$(CONFIG_ANDROID_TIMED_GPIO) += timed_gpio.o
|
||||
obj-$(CONFIG_ANDROID_LOW_MEMORY_KILLER) += lowmemorykiller.o
|
||||
obj-$(CONFIG_ANDROID_SWITCH) += switch/
|
||||
obj-$(CONFIG_ANDROID_INTF_ALARM) += alarm.o
|
||||
obj-$(CONFIG_ANDROID_INTF_ALARM_DEV) += alarm-dev.o
|
||||
|
@ -3,7 +3,7 @@ TODO:
|
||||
- sparse fixes
|
||||
- rename files to be not so "generic"
|
||||
- make sure things build as modules properly
|
||||
- add proper arch dependancies as needed
|
||||
- add proper arch dependencies as needed
|
||||
- audit userspace interfaces to make sure they are sane
|
||||
|
||||
Please send patches to Greg Kroah-Hartman <greg@kroah.com> and Cc:
|
||||
|
297
drivers/staging/android/alarm-dev.c
Normal file
297
drivers/staging/android/alarm-dev.c
Normal file
@ -0,0 +1,297 @@
|
||||
/* drivers/rtc/alarm-dev.c
|
||||
*
|
||||
* Copyright (C) 2007-2009 Google, Inc.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/time.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include "android_alarm.h"
|
||||
|
||||
/* XXX - Hack out wakelocks, while they are out of tree */
|
||||
struct wake_lock {
|
||||
int i;
|
||||
};
|
||||
#define wake_lock(x)
|
||||
#define wake_lock_timeout(x, y)
|
||||
#define wake_unlock(x)
|
||||
#define WAKE_LOCK_SUSPEND 0
|
||||
#define wake_lock_init(x, y, z) ((x)->i = 1)
|
||||
#define wake_lock_destroy(x)
|
||||
|
||||
#define ANDROID_ALARM_PRINT_INFO (1U << 0)
|
||||
#define ANDROID_ALARM_PRINT_IO (1U << 1)
|
||||
#define ANDROID_ALARM_PRINT_INT (1U << 2)
|
||||
|
||||
|
||||
static int debug_mask = ANDROID_ALARM_PRINT_INFO;
|
||||
module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP);
|
||||
|
||||
#define pr_alarm(debug_level_mask, args...) \
|
||||
do { \
|
||||
if (debug_mask & ANDROID_ALARM_PRINT_##debug_level_mask) { \
|
||||
pr_info(args); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define ANDROID_ALARM_WAKEUP_MASK ( \
|
||||
ANDROID_ALARM_RTC_WAKEUP_MASK | \
|
||||
ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP_MASK)
|
||||
|
||||
/* support old usespace code */
|
||||
#define ANDROID_ALARM_SET_OLD _IOW('a', 2, time_t) /* set alarm */
|
||||
#define ANDROID_ALARM_SET_AND_WAIT_OLD _IOW('a', 3, time_t)
|
||||
|
||||
static int alarm_opened;
|
||||
static DEFINE_SPINLOCK(alarm_slock);
|
||||
static struct wake_lock alarm_wake_lock;
|
||||
static DECLARE_WAIT_QUEUE_HEAD(alarm_wait_queue);
|
||||
static uint32_t alarm_pending;
|
||||
static uint32_t alarm_enabled;
|
||||
static uint32_t wait_pending;
|
||||
|
||||
static struct android_alarm alarms[ANDROID_ALARM_TYPE_COUNT];
|
||||
|
||||
static long alarm_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
int rv = 0;
|
||||
unsigned long flags;
|
||||
struct timespec new_alarm_time;
|
||||
struct timespec new_rtc_time;
|
||||
struct timespec tmp_time;
|
||||
enum android_alarm_type alarm_type = ANDROID_ALARM_IOCTL_TO_TYPE(cmd);
|
||||
uint32_t alarm_type_mask = 1U << alarm_type;
|
||||
|
||||
if (alarm_type >= ANDROID_ALARM_TYPE_COUNT)
|
||||
return -EINVAL;
|
||||
|
||||
if (ANDROID_ALARM_BASE_CMD(cmd) != ANDROID_ALARM_GET_TIME(0)) {
|
||||
if ((file->f_flags & O_ACCMODE) == O_RDONLY)
|
||||
return -EPERM;
|
||||
if (file->private_data == NULL &&
|
||||
cmd != ANDROID_ALARM_SET_RTC) {
|
||||
spin_lock_irqsave(&alarm_slock, flags);
|
||||
if (alarm_opened) {
|
||||
spin_unlock_irqrestore(&alarm_slock, flags);
|
||||
return -EBUSY;
|
||||
}
|
||||
alarm_opened = 1;
|
||||
file->private_data = (void *)1;
|
||||
spin_unlock_irqrestore(&alarm_slock, flags);
|
||||
}
|
||||
}
|
||||
|
||||
switch (ANDROID_ALARM_BASE_CMD(cmd)) {
|
||||
case ANDROID_ALARM_CLEAR(0):
|
||||
spin_lock_irqsave(&alarm_slock, flags);
|
||||
pr_alarm(IO, "alarm %d clear\n", alarm_type);
|
||||
android_alarm_try_to_cancel(&alarms[alarm_type]);
|
||||
if (alarm_pending) {
|
||||
alarm_pending &= ~alarm_type_mask;
|
||||
if (!alarm_pending && !wait_pending)
|
||||
wake_unlock(&alarm_wake_lock);
|
||||
}
|
||||
alarm_enabled &= ~alarm_type_mask;
|
||||
spin_unlock_irqrestore(&alarm_slock, flags);
|
||||
break;
|
||||
|
||||
case ANDROID_ALARM_SET_OLD:
|
||||
case ANDROID_ALARM_SET_AND_WAIT_OLD:
|
||||
if (get_user(new_alarm_time.tv_sec, (int __user *)arg)) {
|
||||
rv = -EFAULT;
|
||||
goto err1;
|
||||
}
|
||||
new_alarm_time.tv_nsec = 0;
|
||||
goto from_old_alarm_set;
|
||||
|
||||
case ANDROID_ALARM_SET_AND_WAIT(0):
|
||||
case ANDROID_ALARM_SET(0):
|
||||
if (copy_from_user(&new_alarm_time, (void __user *)arg,
|
||||
sizeof(new_alarm_time))) {
|
||||
rv = -EFAULT;
|
||||
goto err1;
|
||||
}
|
||||
from_old_alarm_set:
|
||||
spin_lock_irqsave(&alarm_slock, flags);
|
||||
pr_alarm(IO, "alarm %d set %ld.%09ld\n", alarm_type,
|
||||
new_alarm_time.tv_sec, new_alarm_time.tv_nsec);
|
||||
alarm_enabled |= alarm_type_mask;
|
||||
android_alarm_start_range(&alarms[alarm_type],
|
||||
timespec_to_ktime(new_alarm_time),
|
||||
timespec_to_ktime(new_alarm_time));
|
||||
spin_unlock_irqrestore(&alarm_slock, flags);
|
||||
if (ANDROID_ALARM_BASE_CMD(cmd) != ANDROID_ALARM_SET_AND_WAIT(0)
|
||||
&& cmd != ANDROID_ALARM_SET_AND_WAIT_OLD)
|
||||
break;
|
||||
/* fall though */
|
||||
case ANDROID_ALARM_WAIT:
|
||||
spin_lock_irqsave(&alarm_slock, flags);
|
||||
pr_alarm(IO, "alarm wait\n");
|
||||
if (!alarm_pending && wait_pending) {
|
||||
wake_unlock(&alarm_wake_lock);
|
||||
wait_pending = 0;
|
||||
}
|
||||
spin_unlock_irqrestore(&alarm_slock, flags);
|
||||
rv = wait_event_interruptible(alarm_wait_queue, alarm_pending);
|
||||
if (rv)
|
||||
goto err1;
|
||||
spin_lock_irqsave(&alarm_slock, flags);
|
||||
rv = alarm_pending;
|
||||
wait_pending = 1;
|
||||
alarm_pending = 0;
|
||||
spin_unlock_irqrestore(&alarm_slock, flags);
|
||||
break;
|
||||
case ANDROID_ALARM_SET_RTC:
|
||||
if (copy_from_user(&new_rtc_time, (void __user *)arg,
|
||||
sizeof(new_rtc_time))) {
|
||||
rv = -EFAULT;
|
||||
goto err1;
|
||||
}
|
||||
rv = android_alarm_set_rtc(new_rtc_time);
|
||||
spin_lock_irqsave(&alarm_slock, flags);
|
||||
alarm_pending |= ANDROID_ALARM_TIME_CHANGE_MASK;
|
||||
wake_up(&alarm_wait_queue);
|
||||
spin_unlock_irqrestore(&alarm_slock, flags);
|
||||
if (rv < 0)
|
||||
goto err1;
|
||||
break;
|
||||
case ANDROID_ALARM_GET_TIME(0):
|
||||
switch (alarm_type) {
|
||||
case ANDROID_ALARM_RTC_WAKEUP:
|
||||
case ANDROID_ALARM_RTC:
|
||||
getnstimeofday(&tmp_time);
|
||||
break;
|
||||
case ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP:
|
||||
case ANDROID_ALARM_ELAPSED_REALTIME:
|
||||
tmp_time =
|
||||
ktime_to_timespec(alarm_get_elapsed_realtime());
|
||||
break;
|
||||
case ANDROID_ALARM_TYPE_COUNT:
|
||||
case ANDROID_ALARM_SYSTEMTIME:
|
||||
ktime_get_ts(&tmp_time);
|
||||
break;
|
||||
}
|
||||
if (copy_to_user((void __user *)arg, &tmp_time,
|
||||
sizeof(tmp_time))) {
|
||||
rv = -EFAULT;
|
||||
goto err1;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
rv = -EINVAL;
|
||||
goto err1;
|
||||
}
|
||||
err1:
|
||||
return rv;
|
||||
}
|
||||
|
||||
static int alarm_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
file->private_data = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int alarm_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
int i;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&alarm_slock, flags);
|
||||
if (file->private_data != 0) {
|
||||
for (i = 0; i < ANDROID_ALARM_TYPE_COUNT; i++) {
|
||||
uint32_t alarm_type_mask = 1U << i;
|
||||
if (alarm_enabled & alarm_type_mask) {
|
||||
pr_alarm(INFO, "alarm_release: clear alarm, "
|
||||
"pending %d\n",
|
||||
!!(alarm_pending & alarm_type_mask));
|
||||
alarm_enabled &= ~alarm_type_mask;
|
||||
}
|
||||
spin_unlock_irqrestore(&alarm_slock, flags);
|
||||
android_alarm_cancel(&alarms[i]);
|
||||
spin_lock_irqsave(&alarm_slock, flags);
|
||||
}
|
||||
if (alarm_pending | wait_pending) {
|
||||
if (alarm_pending)
|
||||
pr_alarm(INFO, "alarm_release: clear "
|
||||
"pending alarms %x\n", alarm_pending);
|
||||
wake_unlock(&alarm_wake_lock);
|
||||
wait_pending = 0;
|
||||
alarm_pending = 0;
|
||||
}
|
||||
alarm_opened = 0;
|
||||
}
|
||||
spin_unlock_irqrestore(&alarm_slock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void alarm_triggered(struct android_alarm *alarm)
|
||||
{
|
||||
unsigned long flags;
|
||||
uint32_t alarm_type_mask = 1U << alarm->type;
|
||||
|
||||
pr_alarm(INT, "alarm_triggered type %d\n", alarm->type);
|
||||
spin_lock_irqsave(&alarm_slock, flags);
|
||||
if (alarm_enabled & alarm_type_mask) {
|
||||
wake_lock_timeout(&alarm_wake_lock, 5 * HZ);
|
||||
alarm_enabled &= ~alarm_type_mask;
|
||||
alarm_pending |= alarm_type_mask;
|
||||
wake_up(&alarm_wait_queue);
|
||||
}
|
||||
spin_unlock_irqrestore(&alarm_slock, flags);
|
||||
}
|
||||
|
||||
static const struct file_operations alarm_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.unlocked_ioctl = alarm_ioctl,
|
||||
.open = alarm_open,
|
||||
.release = alarm_release,
|
||||
};
|
||||
|
||||
static struct miscdevice alarm_device = {
|
||||
.minor = MISC_DYNAMIC_MINOR,
|
||||
.name = "alarm",
|
||||
.fops = &alarm_fops,
|
||||
};
|
||||
|
||||
static int __init alarm_dev_init(void)
|
||||
{
|
||||
int err;
|
||||
int i;
|
||||
|
||||
err = misc_register(&alarm_device);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
for (i = 0; i < ANDROID_ALARM_TYPE_COUNT; i++)
|
||||
android_alarm_init(&alarms[i], i, alarm_triggered);
|
||||
wake_lock_init(&alarm_wake_lock, WAKE_LOCK_SUSPEND, "alarm");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit alarm_dev_exit(void)
|
||||
{
|
||||
misc_deregister(&alarm_device);
|
||||
wake_lock_destroy(&alarm_wake_lock);
|
||||
}
|
||||
|
||||
module_init(alarm_dev_init);
|
||||
module_exit(alarm_dev_exit);
|
||||
|
601
drivers/staging/android/alarm.c
Normal file
601
drivers/staging/android/alarm.c
Normal file
@ -0,0 +1,601 @@
|
||||
/* drivers/rtc/alarm.c
|
||||
*
|
||||
* Copyright (C) 2007-2009 Google, Inc.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/time.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/rtc.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include "android_alarm.h"
|
||||
|
||||
/* XXX - Hack out wakelocks, while they are out of tree */
|
||||
struct wake_lock {
|
||||
int i;
|
||||
};
|
||||
#define wake_lock(x)
|
||||
#define wake_lock_timeout(x, y)
|
||||
#define wake_unlock(x)
|
||||
#define WAKE_LOCK_SUSPEND 0
|
||||
#define wake_lock_init(x, y, z) ((x)->i = 1)
|
||||
#define wake_lock_destroy(x)
|
||||
|
||||
#define ANDROID_ALARM_PRINT_ERROR (1U << 0)
|
||||
#define ANDROID_ALARM_PRINT_INIT_STATUS (1U << 1)
|
||||
#define ANDROID_ALARM_PRINT_TSET (1U << 2)
|
||||
#define ANDROID_ALARM_PRINT_CALL (1U << 3)
|
||||
#define ANDROID_ALARM_PRINT_SUSPEND (1U << 4)
|
||||
#define ANDROID_ALARM_PRINT_INT (1U << 5)
|
||||
#define ANDROID_ALARM_PRINT_FLOW (1U << 6)
|
||||
|
||||
static int debug_mask = ANDROID_ALARM_PRINT_ERROR | \
|
||||
ANDROID_ALARM_PRINT_INIT_STATUS;
|
||||
module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP);
|
||||
|
||||
#define pr_alarm(debug_level_mask, args...) \
|
||||
do { \
|
||||
if (debug_mask & ANDROID_ALARM_PRINT_##debug_level_mask) { \
|
||||
pr_info(args); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define ANDROID_ALARM_WAKEUP_MASK ( \
|
||||
ANDROID_ALARM_RTC_WAKEUP_MASK | \
|
||||
ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP_MASK)
|
||||
|
||||
/* support old usespace code */
|
||||
#define ANDROID_ALARM_SET_OLD _IOW('a', 2, time_t) /* set alarm */
|
||||
#define ANDROID_ALARM_SET_AND_WAIT_OLD _IOW('a', 3, time_t)
|
||||
|
||||
struct alarm_queue {
|
||||
struct rb_root alarms;
|
||||
struct rb_node *first;
|
||||
struct hrtimer timer;
|
||||
ktime_t delta;
|
||||
bool stopped;
|
||||
ktime_t stopped_time;
|
||||
};
|
||||
|
||||
static struct rtc_device *alarm_rtc_dev;
|
||||
static DEFINE_SPINLOCK(alarm_slock);
|
||||
static DEFINE_MUTEX(alarm_setrtc_mutex);
|
||||
static struct wake_lock alarm_rtc_wake_lock;
|
||||
static struct platform_device *alarm_platform_dev;
|
||||
struct alarm_queue alarms[ANDROID_ALARM_TYPE_COUNT];
|
||||
static bool suspended;
|
||||
|
||||
static void update_timer_locked(struct alarm_queue *base, bool head_removed)
|
||||
{
|
||||
struct android_alarm *alarm;
|
||||
bool is_wakeup = base == &alarms[ANDROID_ALARM_RTC_WAKEUP] ||
|
||||
base == &alarms[ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP];
|
||||
|
||||
if (base->stopped) {
|
||||
pr_alarm(FLOW, "changed alarm while setting the wall time\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_wakeup && !suspended && head_removed)
|
||||
wake_unlock(&alarm_rtc_wake_lock);
|
||||
|
||||
if (!base->first)
|
||||
return;
|
||||
|
||||
alarm = container_of(base->first, struct android_alarm, node);
|
||||
|
||||
pr_alarm(FLOW, "selected alarm, type %d, func %pF at %lld\n",
|
||||
alarm->type, alarm->function, ktime_to_ns(alarm->expires));
|
||||
|
||||
if (is_wakeup && suspended) {
|
||||
pr_alarm(FLOW, "changed alarm while suspened\n");
|
||||
wake_lock_timeout(&alarm_rtc_wake_lock, 1 * HZ);
|
||||
return;
|
||||
}
|
||||
|
||||
hrtimer_try_to_cancel(&base->timer);
|
||||
base->timer.node.expires = ktime_add(base->delta, alarm->expires);
|
||||
base->timer._softexpires = ktime_add(base->delta, alarm->softexpires);
|
||||
hrtimer_start_expires(&base->timer, HRTIMER_MODE_ABS);
|
||||
}
|
||||
|
||||
static void alarm_enqueue_locked(struct android_alarm *alarm)
|
||||
{
|
||||
struct alarm_queue *base = &alarms[alarm->type];
|
||||
struct rb_node **link = &base->alarms.rb_node;
|
||||
struct rb_node *parent = NULL;
|
||||
struct android_alarm *entry;
|
||||
int leftmost = 1;
|
||||
bool was_first = false;
|
||||
|
||||
pr_alarm(FLOW, "added alarm, type %d, func %pF at %lld\n",
|
||||
alarm->type, alarm->function, ktime_to_ns(alarm->expires));
|
||||
|
||||
if (base->first == &alarm->node) {
|
||||
base->first = rb_next(&alarm->node);
|
||||
was_first = true;
|
||||
}
|
||||
if (!RB_EMPTY_NODE(&alarm->node)) {
|
||||
rb_erase(&alarm->node, &base->alarms);
|
||||
RB_CLEAR_NODE(&alarm->node);
|
||||
}
|
||||
|
||||
while (*link) {
|
||||
parent = *link;
|
||||
entry = rb_entry(parent, struct android_alarm, node);
|
||||
/*
|
||||
* We dont care about collisions. Nodes with
|
||||
* the same expiry time stay together.
|
||||
*/
|
||||
if (alarm->expires.tv64 < entry->expires.tv64) {
|
||||
link = &(*link)->rb_left;
|
||||
} else {
|
||||
link = &(*link)->rb_right;
|
||||
leftmost = 0;
|
||||
}
|
||||
}
|
||||
if (leftmost)
|
||||
base->first = &alarm->node;
|
||||
if (leftmost || was_first)
|
||||
update_timer_locked(base, was_first);
|
||||
|
||||
rb_link_node(&alarm->node, parent, link);
|
||||
rb_insert_color(&alarm->node, &base->alarms);
|
||||
}
|
||||
|
||||
/**
|
||||
* android_alarm_init - initialize an alarm
|
||||
* @alarm: the alarm to be initialized
|
||||
* @type: the alarm type to be used
|
||||
* @function: alarm callback function
|
||||
*/
|
||||
void android_alarm_init(struct android_alarm *alarm,
|
||||
enum android_alarm_type type, void (*function)(struct android_alarm *))
|
||||
{
|
||||
RB_CLEAR_NODE(&alarm->node);
|
||||
alarm->type = type;
|
||||
alarm->function = function;
|
||||
|
||||
pr_alarm(FLOW, "created alarm, type %d, func %pF\n", type, function);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* android_alarm_start_range - (re)start an alarm
|
||||
* @alarm: the alarm to be added
|
||||
* @start: earliest expiry time
|
||||
* @end: expiry time
|
||||
*/
|
||||
void android_alarm_start_range(struct android_alarm *alarm, ktime_t start,
|
||||
ktime_t end)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&alarm_slock, flags);
|
||||
alarm->softexpires = start;
|
||||
alarm->expires = end;
|
||||
alarm_enqueue_locked(alarm);
|
||||
spin_unlock_irqrestore(&alarm_slock, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* android_alarm_try_to_cancel - try to deactivate an alarm
|
||||
* @alarm: alarm to stop
|
||||
*
|
||||
* Returns:
|
||||
* 0 when the alarm was not active
|
||||
* 1 when the alarm was active
|
||||
* -1 when the alarm may currently be excuting the callback function and
|
||||
* cannot be stopped (it may also be inactive)
|
||||
*/
|
||||
int android_alarm_try_to_cancel(struct android_alarm *alarm)
|
||||
{
|
||||
struct alarm_queue *base = &alarms[alarm->type];
|
||||
unsigned long flags;
|
||||
bool first = false;
|
||||
int ret = 0;
|
||||
|
||||
spin_lock_irqsave(&alarm_slock, flags);
|
||||
if (!RB_EMPTY_NODE(&alarm->node)) {
|
||||
pr_alarm(FLOW, "canceled alarm, type %d, func %pF at %lld\n",
|
||||
alarm->type, alarm->function,
|
||||
ktime_to_ns(alarm->expires));
|
||||
ret = 1;
|
||||
if (base->first == &alarm->node) {
|
||||
base->first = rb_next(&alarm->node);
|
||||
first = true;
|
||||
}
|
||||
rb_erase(&alarm->node, &base->alarms);
|
||||
RB_CLEAR_NODE(&alarm->node);
|
||||
if (first)
|
||||
update_timer_locked(base, true);
|
||||
} else
|
||||
pr_alarm(FLOW, "tried to cancel alarm, type %d, func %pF\n",
|
||||
alarm->type, alarm->function);
|
||||
spin_unlock_irqrestore(&alarm_slock, flags);
|
||||
if (!ret && hrtimer_callback_running(&base->timer))
|
||||
ret = -1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* android_alarm_cancel - cancel an alarm and wait for the handler to finish.
|
||||
* @alarm: the alarm to be cancelled
|
||||
*
|
||||
* Returns:
|
||||
* 0 when the alarm was not active
|
||||
* 1 when the alarm was active
|
||||
*/
|
||||
int android_alarm_cancel(struct android_alarm *alarm)
|
||||
{
|
||||
for (;;) {
|
||||
int ret = android_alarm_try_to_cancel(alarm);
|
||||
if (ret >= 0)
|
||||
return ret;
|
||||
cpu_relax();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* alarm_set_rtc - set the kernel and rtc walltime
|
||||
* @new_time: timespec value containing the new time
|
||||
*/
|
||||
int android_alarm_set_rtc(struct timespec new_time)
|
||||
{
|
||||
int i;
|
||||
int ret;
|
||||
unsigned long flags;
|
||||
struct rtc_time rtc_new_rtc_time;
|
||||
struct timespec tmp_time;
|
||||
|
||||
rtc_time_to_tm(new_time.tv_sec, &rtc_new_rtc_time);
|
||||
|
||||
pr_alarm(TSET, "set rtc %ld %ld - rtc %02d:%02d:%02d %02d/%02d/%04d\n",
|
||||
new_time.tv_sec, new_time.tv_nsec,
|
||||
rtc_new_rtc_time.tm_hour, rtc_new_rtc_time.tm_min,
|
||||
rtc_new_rtc_time.tm_sec, rtc_new_rtc_time.tm_mon + 1,
|
||||
rtc_new_rtc_time.tm_mday,
|
||||
rtc_new_rtc_time.tm_year + 1900);
|
||||
|
||||
mutex_lock(&alarm_setrtc_mutex);
|
||||
spin_lock_irqsave(&alarm_slock, flags);
|
||||
wake_lock(&alarm_rtc_wake_lock);
|
||||
getnstimeofday(&tmp_time);
|
||||
for (i = 0; i < ANDROID_ALARM_SYSTEMTIME; i++) {
|
||||
hrtimer_try_to_cancel(&alarms[i].timer);
|
||||
alarms[i].stopped = true;
|
||||
alarms[i].stopped_time = timespec_to_ktime(tmp_time);
|
||||
}
|
||||
alarms[ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP].delta =
|
||||
alarms[ANDROID_ALARM_ELAPSED_REALTIME].delta =
|
||||
ktime_sub(alarms[ANDROID_ALARM_ELAPSED_REALTIME].delta,
|
||||
timespec_to_ktime(timespec_sub(tmp_time, new_time)));
|
||||
spin_unlock_irqrestore(&alarm_slock, flags);
|
||||
ret = do_settimeofday(&new_time);
|
||||
spin_lock_irqsave(&alarm_slock, flags);
|
||||
for (i = 0; i < ANDROID_ALARM_SYSTEMTIME; i++) {
|
||||
alarms[i].stopped = false;
|
||||
update_timer_locked(&alarms[i], false);
|
||||
}
|
||||
spin_unlock_irqrestore(&alarm_slock, flags);
|
||||
if (ret < 0) {
|
||||
pr_alarm(ERROR, "alarm_set_rtc: Failed to set time\n");
|
||||
goto err;
|
||||
}
|
||||
if (!alarm_rtc_dev) {
|
||||
pr_alarm(ERROR,
|
||||
"alarm_set_rtc: no RTC, time will be lost on reboot\n");
|
||||
goto err;
|
||||
}
|
||||
ret = rtc_set_time(alarm_rtc_dev, &rtc_new_rtc_time);
|
||||
if (ret < 0)
|
||||
pr_alarm(ERROR, "alarm_set_rtc: "
|
||||
"Failed to set RTC, time will be lost on reboot\n");
|
||||
err:
|
||||
wake_unlock(&alarm_rtc_wake_lock);
|
||||
mutex_unlock(&alarm_setrtc_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* alarm_get_elapsed_realtime - get the elapsed real time in ktime_t format
|
||||
*
|
||||
* returns the time in ktime_t format
|
||||
*/
|
||||
ktime_t alarm_get_elapsed_realtime(void)
|
||||
{
|
||||
ktime_t now;
|
||||
unsigned long flags;
|
||||
struct alarm_queue *base = &alarms[ANDROID_ALARM_ELAPSED_REALTIME];
|
||||
|
||||
spin_lock_irqsave(&alarm_slock, flags);
|
||||
now = base->stopped ? base->stopped_time : ktime_get_real();
|
||||
now = ktime_sub(now, base->delta);
|
||||
spin_unlock_irqrestore(&alarm_slock, flags);
|
||||
return now;
|
||||
}
|
||||
|
||||
static enum hrtimer_restart alarm_timer_triggered(struct hrtimer *timer)
|
||||
{
|
||||
struct alarm_queue *base;
|
||||
struct android_alarm *alarm;
|
||||
unsigned long flags;
|
||||
ktime_t now;
|
||||
|
||||
spin_lock_irqsave(&alarm_slock, flags);
|
||||
|
||||
base = container_of(timer, struct alarm_queue, timer);
|
||||
now = base->stopped ? base->stopped_time : hrtimer_cb_get_time(timer);
|
||||
now = ktime_sub(now, base->delta);
|
||||
|
||||
pr_alarm(INT, "alarm_timer_triggered type %ld at %lld\n",
|
||||
base - alarms, ktime_to_ns(now));
|
||||
|
||||
while (base->first) {
|
||||
alarm = container_of(base->first, struct android_alarm, node);
|
||||
if (alarm->softexpires.tv64 > now.tv64) {
|
||||
pr_alarm(FLOW, "don't call alarm, %pF, %lld (s %lld)\n",
|
||||
alarm->function, ktime_to_ns(alarm->expires),
|
||||
ktime_to_ns(alarm->softexpires));
|
||||
break;
|
||||
}
|
||||
base->first = rb_next(&alarm->node);
|
||||
rb_erase(&alarm->node, &base->alarms);
|
||||
RB_CLEAR_NODE(&alarm->node);
|
||||
pr_alarm(CALL, "call alarm, type %d, func %pF, %lld (s %lld)\n",
|
||||
alarm->type, alarm->function,
|
||||
ktime_to_ns(alarm->expires),
|
||||
ktime_to_ns(alarm->softexpires));
|
||||
spin_unlock_irqrestore(&alarm_slock, flags);
|
||||
alarm->function(alarm);
|
||||
spin_lock_irqsave(&alarm_slock, flags);
|
||||
}
|
||||
if (!base->first)
|
||||
pr_alarm(FLOW, "no more alarms of type %ld\n", base - alarms);
|
||||
update_timer_locked(base, true);
|
||||
spin_unlock_irqrestore(&alarm_slock, flags);
|
||||
return HRTIMER_NORESTART;
|
||||
}
|
||||
|
||||
static void alarm_triggered_func(void *p)
|
||||
{
|
||||
struct rtc_device *rtc = alarm_rtc_dev;
|
||||
if (!(rtc->irq_data & RTC_AF))
|
||||
return;
|
||||
pr_alarm(INT, "rtc alarm triggered\n");
|
||||
wake_lock_timeout(&alarm_rtc_wake_lock, 1 * HZ);
|
||||
}
|
||||
|
||||
static int alarm_suspend(struct platform_device *pdev, pm_message_t state)
|
||||
{
|
||||
int err = 0;
|
||||
unsigned long flags;
|
||||
struct rtc_wkalrm rtc_alarm;
|
||||
struct rtc_time rtc_current_rtc_time;
|
||||
unsigned long rtc_current_time;
|
||||
unsigned long rtc_alarm_time;
|
||||
struct timespec rtc_delta;
|
||||
struct timespec wall_time;
|
||||
struct alarm_queue *wakeup_queue = NULL;
|
||||
struct alarm_queue *tmp_queue = NULL;
|
||||
|
||||
pr_alarm(SUSPEND, "alarm_suspend(%p, %d)\n", pdev, state.event);
|
||||
|
||||
spin_lock_irqsave(&alarm_slock, flags);
|
||||
suspended = true;
|
||||
spin_unlock_irqrestore(&alarm_slock, flags);
|
||||
|
||||
hrtimer_cancel(&alarms[ANDROID_ALARM_RTC_WAKEUP].timer);
|
||||
hrtimer_cancel(&alarms[
|
||||
ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP].timer);
|
||||
|
||||
tmp_queue = &alarms[ANDROID_ALARM_RTC_WAKEUP];
|
||||
if (tmp_queue->first)
|
||||
wakeup_queue = tmp_queue;
|
||||
tmp_queue = &alarms[ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP];
|
||||
if (tmp_queue->first && (!wakeup_queue ||
|
||||
hrtimer_get_expires(&tmp_queue->timer).tv64 <
|
||||
hrtimer_get_expires(&wakeup_queue->timer).tv64))
|
||||
wakeup_queue = tmp_queue;
|
||||
if (wakeup_queue) {
|
||||
rtc_read_time(alarm_rtc_dev, &rtc_current_rtc_time);
|
||||
getnstimeofday(&wall_time);
|
||||
rtc_tm_to_time(&rtc_current_rtc_time, &rtc_current_time);
|
||||
set_normalized_timespec(&rtc_delta,
|
||||
wall_time.tv_sec - rtc_current_time,
|
||||
wall_time.tv_nsec);
|
||||
|
||||
rtc_alarm_time = timespec_sub(ktime_to_timespec(
|
||||
hrtimer_get_expires(&wakeup_queue->timer)),
|
||||
rtc_delta).tv_sec;
|
||||
|
||||
rtc_time_to_tm(rtc_alarm_time, &rtc_alarm.time);
|
||||
rtc_alarm.enabled = 1;
|
||||
rtc_set_alarm(alarm_rtc_dev, &rtc_alarm);
|
||||
rtc_read_time(alarm_rtc_dev, &rtc_current_rtc_time);
|
||||
rtc_tm_to_time(&rtc_current_rtc_time, &rtc_current_time);
|
||||
pr_alarm(SUSPEND,
|
||||
"rtc alarm set at %ld, now %ld, rtc delta %ld.%09ld\n",
|
||||
rtc_alarm_time, rtc_current_time,
|
||||
rtc_delta.tv_sec, rtc_delta.tv_nsec);
|
||||
if (rtc_current_time + 1 >= rtc_alarm_time) {
|
||||
pr_alarm(SUSPEND, "alarm about to go off\n");
|
||||
memset(&rtc_alarm, 0, sizeof(rtc_alarm));
|
||||
rtc_alarm.enabled = 0;
|
||||
rtc_set_alarm(alarm_rtc_dev, &rtc_alarm);
|
||||
|
||||
spin_lock_irqsave(&alarm_slock, flags);
|
||||
suspended = false;
|
||||
wake_lock_timeout(&alarm_rtc_wake_lock, 2 * HZ);
|
||||
update_timer_locked(&alarms[ANDROID_ALARM_RTC_WAKEUP],
|
||||
false);
|
||||
update_timer_locked(&alarms[
|
||||
ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP], false);
|
||||
err = -EBUSY;
|
||||
spin_unlock_irqrestore(&alarm_slock, flags);
|
||||
}
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static int alarm_resume(struct platform_device *pdev)
|
||||
{
|
||||
struct rtc_wkalrm alarm;
|
||||
unsigned long flags;
|
||||
|
||||
pr_alarm(SUSPEND, "alarm_resume(%p)\n", pdev);
|
||||
|
||||
memset(&alarm, 0, sizeof(alarm));
|
||||
alarm.enabled = 0;
|
||||
rtc_set_alarm(alarm_rtc_dev, &alarm);
|
||||
|
||||
spin_lock_irqsave(&alarm_slock, flags);
|
||||
suspended = false;
|
||||
update_timer_locked(&alarms[ANDROID_ALARM_RTC_WAKEUP], false);
|
||||
update_timer_locked(&alarms[ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP],
|
||||
false);
|
||||
spin_unlock_irqrestore(&alarm_slock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct rtc_task alarm_rtc_task = {
|
||||
.func = alarm_triggered_func
|
||||
};
|
||||
|
||||
static int rtc_alarm_add_device(struct device *dev,
|
||||
struct class_interface *class_intf)
|
||||
{
|
||||
int err;
|
||||
struct rtc_device *rtc = to_rtc_device(dev);
|
||||
|
||||
mutex_lock(&alarm_setrtc_mutex);
|
||||
|
||||
if (alarm_rtc_dev) {
|
||||
err = -EBUSY;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
alarm_platform_dev =
|
||||
platform_device_register_simple("alarm", -1, NULL, 0);
|
||||
if (IS_ERR(alarm_platform_dev)) {
|
||||
err = PTR_ERR(alarm_platform_dev);
|
||||
goto err2;
|
||||
}
|
||||
err = rtc_irq_register(rtc, &alarm_rtc_task);
|
||||
if (err)
|
||||
goto err3;
|
||||
alarm_rtc_dev = rtc;
|
||||
pr_alarm(INIT_STATUS, "using rtc device, %s, for alarms", rtc->name);
|
||||
mutex_unlock(&alarm_setrtc_mutex);
|
||||
|
||||
return 0;
|
||||
|
||||
err3:
|
||||
platform_device_unregister(alarm_platform_dev);
|
||||
err2:
|
||||
err1:
|
||||
mutex_unlock(&alarm_setrtc_mutex);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void rtc_alarm_remove_device(struct device *dev,
|
||||
struct class_interface *class_intf)
|
||||
{
|
||||
if (dev == &alarm_rtc_dev->dev) {
|
||||
pr_alarm(INIT_STATUS, "lost rtc device for alarms");
|
||||
rtc_irq_unregister(alarm_rtc_dev, &alarm_rtc_task);
|
||||
platform_device_unregister(alarm_platform_dev);
|
||||
alarm_rtc_dev = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static struct class_interface rtc_alarm_interface = {
|
||||
.add_dev = &rtc_alarm_add_device,
|
||||
.remove_dev = &rtc_alarm_remove_device,
|
||||
};
|
||||
|
||||
static struct platform_driver alarm_driver = {
|
||||
.suspend = alarm_suspend,
|
||||
.resume = alarm_resume,
|
||||
.driver = {
|
||||
.name = "alarm"
|
||||
}
|
||||
};
|
||||
|
||||
static int __init alarm_late_init(void)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct timespec tmp_time, system_time;
|
||||
|
||||
/* this needs to run after the rtc is read at boot */
|
||||
spin_lock_irqsave(&alarm_slock, flags);
|
||||
/* We read the current rtc and system time so we can later calulate
|
||||
* elasped realtime to be (boot_systemtime + rtc - boot_rtc) ==
|
||||
* (rtc - (boot_rtc - boot_systemtime))
|
||||
*/
|
||||
getnstimeofday(&tmp_time);
|
||||
ktime_get_ts(&system_time);
|
||||
alarms[ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP].delta =
|
||||
alarms[ANDROID_ALARM_ELAPSED_REALTIME].delta =
|
||||
timespec_to_ktime(timespec_sub(tmp_time, system_time));
|
||||
|
||||
spin_unlock_irqrestore(&alarm_slock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init alarm_driver_init(void)
|
||||
{
|
||||
int err;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ANDROID_ALARM_SYSTEMTIME; i++) {
|
||||
hrtimer_init(&alarms[i].timer,
|
||||
CLOCK_REALTIME, HRTIMER_MODE_ABS);
|
||||
alarms[i].timer.function = alarm_timer_triggered;
|
||||
}
|
||||
hrtimer_init(&alarms[ANDROID_ALARM_SYSTEMTIME].timer,
|
||||
CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
|
||||
alarms[ANDROID_ALARM_SYSTEMTIME].timer.function = alarm_timer_triggered;
|
||||
err = platform_driver_register(&alarm_driver);
|
||||
if (err < 0)
|
||||
goto err1;
|
||||
wake_lock_init(&alarm_rtc_wake_lock, WAKE_LOCK_SUSPEND, "alarm_rtc");
|
||||
rtc_alarm_interface.class = rtc_class;
|
||||
err = class_interface_register(&rtc_alarm_interface);
|
||||
if (err < 0)
|
||||
goto err2;
|
||||
|
||||
return 0;
|
||||
|
||||
err2:
|
||||
wake_lock_destroy(&alarm_rtc_wake_lock);
|
||||
platform_driver_unregister(&alarm_driver);
|
||||
err1:
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __exit alarm_exit(void)
|
||||
{
|
||||
class_interface_unregister(&rtc_alarm_interface);
|
||||
wake_lock_destroy(&alarm_rtc_wake_lock);
|
||||
platform_driver_unregister(&alarm_driver);
|
||||
}
|
||||
|
||||
late_initcall(alarm_late_init);
|
||||
module_init(alarm_driver_init);
|
||||
module_exit(alarm_exit);
|
||||
|
121
drivers/staging/android/android_alarm.h
Normal file
121
drivers/staging/android/android_alarm.h
Normal file
@ -0,0 +1,121 @@
|
||||
/* include/linux/android_alarm.h
|
||||
*
|
||||
* Copyright (C) 2006-2007 Google, Inc.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_ANDROID_ALARM_H
|
||||
#define _LINUX_ANDROID_ALARM_H
|
||||
|
||||
#include <linux/ioctl.h>
|
||||
#include <linux/time.h>
|
||||
|
||||
enum android_alarm_type {
|
||||
/* return code bit numbers or set alarm arg */
|
||||
ANDROID_ALARM_RTC_WAKEUP,
|
||||
ANDROID_ALARM_RTC,
|
||||
ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP,
|
||||
ANDROID_ALARM_ELAPSED_REALTIME,
|
||||
ANDROID_ALARM_SYSTEMTIME,
|
||||
|
||||
ANDROID_ALARM_TYPE_COUNT,
|
||||
|
||||
/* return code bit numbers */
|
||||
/* ANDROID_ALARM_TIME_CHANGE = 16 */
|
||||
};
|
||||
|
||||
#ifdef __KERNEL__
|
||||
|
||||
#include <linux/ktime.h>
|
||||
#include <linux/rbtree.h>
|
||||
|
||||
/*
|
||||
* The alarm interface is similar to the hrtimer interface but adds support
|
||||
* for wakeup from suspend. It also adds an elapsed realtime clock that can
|
||||
* be used for periodic timers that need to keep runing while the system is
|
||||
* suspended and not be disrupted when the wall time is set.
|
||||
*/
|
||||
|
||||
/**
|
||||
* struct alarm - the basic alarm structure
|
||||
* @node: red black tree node for time ordered insertion
|
||||
* @type: alarm type. rtc/elapsed-realtime/systemtime, wakeup/non-wakeup.
|
||||
* @softexpires: the absolute earliest expiry time of the alarm.
|
||||
* @expires: the absolute expiry time.
|
||||
* @function: alarm expiry callback function
|
||||
*
|
||||
* The alarm structure must be initialized by alarm_init()
|
||||
*
|
||||
*/
|
||||
|
||||
struct android_alarm {
|
||||
struct rb_node node;
|
||||
enum android_alarm_type type;
|
||||
ktime_t softexpires;
|
||||
ktime_t expires;
|
||||
void (*function)(struct android_alarm *);
|
||||
};
|
||||
|
||||
void android_alarm_init(struct android_alarm *alarm,
|
||||
enum android_alarm_type type, void (*function)(struct android_alarm *));
|
||||
void android_alarm_start_range(struct android_alarm *alarm, ktime_t start,
|
||||
ktime_t end);
|
||||
int android_alarm_try_to_cancel(struct android_alarm *alarm);
|
||||
int android_alarm_cancel(struct android_alarm *alarm);
|
||||
ktime_t alarm_get_elapsed_realtime(void);
|
||||
|
||||
/* set rtc while preserving elapsed realtime */
|
||||
int android_alarm_set_rtc(const struct timespec ts);
|
||||
|
||||
#ifdef CONFIG_ANDROID_ALARM_OLDDRV_COMPAT
|
||||
/*
|
||||
* Some older drivers depend on the old API,
|
||||
* so provide compatability macros for now.
|
||||
*/
|
||||
#define alarm android_alarm
|
||||
#define alarm_init(x, y, z) android_alarm_init(x, y, z)
|
||||
#define alarm_start_range(x, y, z) android_alarm_start_range(x, y, z)
|
||||
#define alarm_try_to_cancel(x) android_alarm_try_to_cancel(x)
|
||||
#define alarm_cancel(x) android_alarm_cancel(x)
|
||||
#define alarm_set_rtc(x) android_alarm_set_rtc(x)
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
enum android_alarm_return_flags {
|
||||
ANDROID_ALARM_RTC_WAKEUP_MASK = 1U << ANDROID_ALARM_RTC_WAKEUP,
|
||||
ANDROID_ALARM_RTC_MASK = 1U << ANDROID_ALARM_RTC,
|
||||
ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP_MASK =
|
||||
1U << ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP,
|
||||
ANDROID_ALARM_ELAPSED_REALTIME_MASK =
|
||||
1U << ANDROID_ALARM_ELAPSED_REALTIME,
|
||||
ANDROID_ALARM_SYSTEMTIME_MASK = 1U << ANDROID_ALARM_SYSTEMTIME,
|
||||
ANDROID_ALARM_TIME_CHANGE_MASK = 1U << 16
|
||||
};
|
||||
|
||||
/* Disable alarm */
|
||||
#define ANDROID_ALARM_CLEAR(type) _IO('a', 0 | ((type) << 4))
|
||||
|
||||
/* Ack last alarm and wait for next */
|
||||
#define ANDROID_ALARM_WAIT _IO('a', 1)
|
||||
|
||||
#define ALARM_IOW(c, type, size) _IOW('a', (c) | ((type) << 4), size)
|
||||
/* Set alarm */
|
||||
#define ANDROID_ALARM_SET(type) ALARM_IOW(2, type, struct timespec)
|
||||
#define ANDROID_ALARM_SET_AND_WAIT(type) ALARM_IOW(3, type, struct timespec)
|
||||
#define ANDROID_ALARM_GET_TIME(type) ALARM_IOW(4, type, struct timespec)
|
||||
#define ANDROID_ALARM_SET_RTC _IOW('a', 5, struct timespec)
|
||||
#define ANDROID_ALARM_BASE_CMD(cmd) (cmd & ~(_IOC(0, 0, 0xf0, 0)))
|
||||
#define ANDROID_ALARM_IOCTL_TO_TYPE(cmd) (_IOC_NR(cmd) >> 4)
|
||||
|
||||
#endif
|
@ -315,7 +315,7 @@ static int ashmem_mmap(struct file *file, struct vm_area_struct *vma)
|
||||
get_file(asma->file);
|
||||
|
||||
/*
|
||||
* XXX - Reworked to use shmem_zero_setup() instead of
|
||||
* XXX - Reworked to use shmem_zero_setup() instead of
|
||||
* shmem_set_file while we're in staging. -jstultz
|
||||
*/
|
||||
if (vma->vm_flags & VM_SHARED) {
|
||||
@ -680,7 +680,7 @@ static long ashmem_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct file_operations ashmem_fops = {
|
||||
static const struct file_operations ashmem_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = ashmem_open,
|
||||
.release = ashmem_release,
|
||||
|
@ -103,7 +103,7 @@ static uint32_t binder_debug_mask = BINDER_DEBUG_USER_ERROR |
|
||||
BINDER_DEBUG_FAILED_TRANSACTION | BINDER_DEBUG_DEAD_TRANSACTION;
|
||||
module_param_named(debug_mask, binder_debug_mask, uint, S_IWUSR | S_IRUGO);
|
||||
|
||||
static int binder_debug_no_lock;
|
||||
static bool binder_debug_no_lock;
|
||||
module_param_named(proc_no_lock, binder_debug_no_lock, bool, S_IWUSR | S_IRUGO);
|
||||
|
||||
static DECLARE_WAIT_QUEUE_HEAD(binder_user_error_wait);
|
||||
@ -258,7 +258,7 @@ struct binder_ref {
|
||||
};
|
||||
|
||||
struct binder_buffer {
|
||||
struct list_head entry; /* free and allocated entries by addesss */
|
||||
struct list_head entry; /* free and allocated entries by address */
|
||||
struct rb_node rb_node; /* free entry by size or allocated entry */
|
||||
/* by address */
|
||||
unsigned free:1;
|
||||
@ -288,6 +288,7 @@ struct binder_proc {
|
||||
struct rb_root refs_by_node;
|
||||
int pid;
|
||||
struct vm_area_struct *vma;
|
||||
struct mm_struct *vma_vm_mm;
|
||||
struct task_struct *tsk;
|
||||
struct files_struct *files;
|
||||
struct hlist_node deferred_work_node;
|
||||
@ -633,7 +634,7 @@ static int binder_update_page_range(struct binder_proc *proc, int allocate,
|
||||
if (mm) {
|
||||
down_write(&mm->mmap_sem);
|
||||
vma = proc->vma;
|
||||
if (vma && mm != vma->vm_mm) {
|
||||
if (vma && mm != proc->vma_vm_mm) {
|
||||
pr_err("binder: %d: vma mm and task mm mismatch\n",
|
||||
proc->pid);
|
||||
vma = NULL;
|
||||
@ -2776,6 +2777,7 @@ static void binder_vma_close(struct vm_area_struct *vma)
|
||||
(vma->vm_end - vma->vm_start) / SZ_1K, vma->vm_flags,
|
||||
(unsigned long)pgprot_val(vma->vm_page_prot));
|
||||
proc->vma = NULL;
|
||||
proc->vma_vm_mm = NULL;
|
||||
binder_defer_work(proc, BINDER_DEFERRED_PUT_FILES);
|
||||
}
|
||||
|
||||
@ -2858,6 +2860,7 @@ static int binder_mmap(struct file *filp, struct vm_area_struct *vma)
|
||||
barrier();
|
||||
proc->files = get_files_struct(proc->tsk);
|
||||
proc->vma = vma;
|
||||
proc->vma_vm_mm = vma->vm_mm;
|
||||
|
||||
/*printk(KERN_INFO "binder_mmap: %d %lx-%lx maps %p\n",
|
||||
proc->pid, vma->vm_start, vma->vm_end, proc->buffer);*/
|
||||
|
@ -60,7 +60,11 @@ struct logger_reader {
|
||||
};
|
||||
|
||||
/* logger_offset - returns index 'n' into the log via (optimized) modulus */
|
||||
#define logger_offset(n) ((n) & (log->size - 1))
|
||||
size_t logger_offset(struct logger_log *log, size_t n)
|
||||
{
|
||||
return n & (log->size-1);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* file_get_log - Given a file structure, return the associated log
|
||||
@ -89,20 +93,24 @@ static inline struct logger_log *file_get_log(struct file *file)
|
||||
* get_entry_len - Grabs the length of the payload of the next entry starting
|
||||
* from 'off'.
|
||||
*
|
||||
* An entry length is 2 bytes (16 bits) in host endian order.
|
||||
* In the log, the length does not include the size of the log entry structure.
|
||||
* This function returns the size including the log entry structure.
|
||||
*
|
||||
* Caller needs to hold log->mutex.
|
||||
*/
|
||||
static __u32 get_entry_len(struct logger_log *log, size_t off)
|
||||
{
|
||||
__u16 val;
|
||||
|
||||
switch (log->size - off) {
|
||||
case 1:
|
||||
memcpy(&val, log->buffer + off, 1);
|
||||
memcpy(((char *) &val) + 1, log->buffer, 1);
|
||||
break;
|
||||
default:
|
||||
memcpy(&val, log->buffer + off, 2);
|
||||
}
|
||||
/* copy 2 bytes from buffer, in memcpy order, */
|
||||
/* handling possible wrap at end of buffer */
|
||||
|
||||
((__u8 *)&val)[0] = log->buffer[off];
|
||||
if (likely(off+1 < log->size))
|
||||
((__u8 *)&val)[1] = log->buffer[off+1];
|
||||
else
|
||||
((__u8 *)&val)[1] = log->buffer[0];
|
||||
|
||||
return sizeof(struct logger_entry) + val;
|
||||
}
|
||||
@ -137,7 +145,7 @@ static ssize_t do_read_log_to_user(struct logger_log *log,
|
||||
if (copy_to_user(buf + len, log->buffer, count - len))
|
||||
return -EFAULT;
|
||||
|
||||
reader->r_off = logger_offset(reader->r_off + count);
|
||||
reader->r_off = logger_offset(log, reader->r_off + count);
|
||||
|
||||
return count;
|
||||
}
|
||||
@ -164,9 +172,10 @@ static ssize_t logger_read(struct file *file, char __user *buf,
|
||||
|
||||
start:
|
||||
while (1) {
|
||||
mutex_lock(&log->mutex);
|
||||
|
||||
prepare_to_wait(&log->wq, &wait, TASK_INTERRUPTIBLE);
|
||||
|
||||
mutex_lock(&log->mutex);
|
||||
ret = (log->w_off == reader->r_off);
|
||||
mutex_unlock(&log->mutex);
|
||||
if (!ret)
|
||||
@ -225,7 +234,7 @@ static size_t get_next_entry(struct logger_log *log, size_t off, size_t len)
|
||||
|
||||
do {
|
||||
size_t nr = get_entry_len(log, off);
|
||||
off = logger_offset(off + nr);
|
||||
off = logger_offset(log, off + nr);
|
||||
count += nr;
|
||||
} while (count < len);
|
||||
|
||||
@ -233,16 +242,28 @@ static size_t get_next_entry(struct logger_log *log, size_t off, size_t len)
|
||||
}
|
||||
|
||||
/*
|
||||
* clock_interval - is a < c < b in mod-space? Put another way, does the line
|
||||
* from a to b cross c?
|
||||
* is_between - is a < c < b, accounting for wrapping of a, b, and c
|
||||
* positions in the buffer
|
||||
*
|
||||
* That is, if a<b, check for c between a and b
|
||||
* and if a>b, check for c outside (not between) a and b
|
||||
*
|
||||
* |------- a xxxxxxxx b --------|
|
||||
* c^
|
||||
*
|
||||
* |xxxxx b --------- a xxxxxxxxx|
|
||||
* c^
|
||||
* or c^
|
||||
*/
|
||||
static inline int clock_interval(size_t a, size_t b, size_t c)
|
||||
static inline int is_between(size_t a, size_t b, size_t c)
|
||||
{
|
||||
if (b < a) {
|
||||
if (a < c || b >= c)
|
||||
if (a < b) {
|
||||
/* is c between a and b? */
|
||||
if (a < c && c <= b)
|
||||
return 1;
|
||||
} else {
|
||||
if (a < c && b >= c)
|
||||
/* is c outside of b through a? */
|
||||
if (c <= b || a < c)
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -260,14 +281,14 @@ static inline int clock_interval(size_t a, size_t b, size_t c)
|
||||
static void fix_up_readers(struct logger_log *log, size_t len)
|
||||
{
|
||||
size_t old = log->w_off;
|
||||
size_t new = logger_offset(old + len);
|
||||
size_t new = logger_offset(log, old + len);
|
||||
struct logger_reader *reader;
|
||||
|
||||
if (clock_interval(old, new, log->head))
|
||||
if (is_between(old, new, log->head))
|
||||
log->head = get_next_entry(log, log->head, len);
|
||||
|
||||
list_for_each_entry(reader, &log->readers, list)
|
||||
if (clock_interval(old, new, reader->r_off))
|
||||
if (is_between(old, new, reader->r_off))
|
||||
reader->r_off = get_next_entry(log, reader->r_off, len);
|
||||
}
|
||||
|
||||
@ -286,7 +307,7 @@ static void do_write_log(struct logger_log *log, const void *buf, size_t count)
|
||||
if (count != len)
|
||||
memcpy(log->buffer, buf + len, count - len);
|
||||
|
||||
log->w_off = logger_offset(log->w_off + count);
|
||||
log->w_off = logger_offset(log, log->w_off + count);
|
||||
|
||||
}
|
||||
|
||||
@ -309,9 +330,15 @@ static ssize_t do_write_log_from_user(struct logger_log *log,
|
||||
|
||||
if (count != len)
|
||||
if (copy_from_user(log->buffer, buf + len, count - len))
|
||||
/*
|
||||
* Note that by not updating w_off, this abandons the
|
||||
* portion of the new entry that *was* successfully
|
||||
* copied, just above. This is intentional to avoid
|
||||
* message corruption from missing fragments.
|
||||
*/
|
||||
return -EFAULT;
|
||||
|
||||
log->w_off = logger_offset(log->w_off + count);
|
||||
log->w_off = logger_offset(log, log->w_off + count);
|
||||
|
||||
return count;
|
||||
}
|
||||
@ -432,7 +459,12 @@ static int logger_release(struct inode *ignored, struct file *file)
|
||||
{
|
||||
if (file->f_mode & FMODE_READ) {
|
||||
struct logger_reader *reader = file->private_data;
|
||||
struct logger_log *log = reader->log;
|
||||
|
||||
mutex_lock(&log->mutex);
|
||||
list_del(&reader->list);
|
||||
mutex_unlock(&log->mutex);
|
||||
|
||||
kfree(reader);
|
||||
}
|
||||
|
||||
|
@ -1,16 +1,17 @@
|
||||
/* drivers/misc/lowmemorykiller.c
|
||||
*
|
||||
* The lowmemorykiller driver lets user-space specify a set of memory thresholds
|
||||
* where processes with a range of oom_adj values will get killed. Specify the
|
||||
* minimum oom_adj values in /sys/module/lowmemorykiller/parameters/adj and the
|
||||
* number of free pages in /sys/module/lowmemorykiller/parameters/minfree. Both
|
||||
* files take a comma separated list of numbers in ascending order.
|
||||
* where processes with a range of oom_score_adj values will get killed. Specify
|
||||
* the minimum oom_score_adj values in
|
||||
* /sys/module/lowmemorykiller/parameters/adj and the number of free pages in
|
||||
* /sys/module/lowmemorykiller/parameters/minfree. Both files take a comma
|
||||
* separated list of numbers in ascending order.
|
||||
*
|
||||
* For example, write "0,8" to /sys/module/lowmemorykiller/parameters/adj and
|
||||
* "1024,4096" to /sys/module/lowmemorykiller/parameters/minfree to kill
|
||||
* processes with a oom_adj value of 8 or higher when the free memory drops
|
||||
* below 4096 pages and kill processes with a oom_adj value of 0 or higher
|
||||
* when the free memory drops below 1024 pages.
|
||||
* processes with a oom_score_adj value of 8 or higher when the free memory
|
||||
* drops below 4096 pages and kill processes with a oom_score_adj value of 0 or
|
||||
* higher when the free memory drops below 1024 pages.
|
||||
*
|
||||
* The driver considers memory used for caches to be free, but if a large
|
||||
* percentage of the cached memory is locked this can be very inaccurate
|
||||
@ -34,6 +35,7 @@
|
||||
#include <linux/mm.h>
|
||||
#include <linux/oom.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/rcupdate.h>
|
||||
#include <linux/profile.h>
|
||||
#include <linux/notifier.h>
|
||||
|
||||
@ -45,7 +47,7 @@ static int lowmem_adj[6] = {
|
||||
12,
|
||||
};
|
||||
static int lowmem_adj_size = 4;
|
||||
static size_t lowmem_minfree[6] = {
|
||||
static int lowmem_minfree[6] = {
|
||||
3 * 512, /* 6MB */
|
||||
2 * 1024, /* 8MB */
|
||||
4 * 1024, /* 16MB */
|
||||
@ -73,23 +75,23 @@ static int
|
||||
task_notify_func(struct notifier_block *self, unsigned long val, void *data)
|
||||
{
|
||||
struct task_struct *task = data;
|
||||
if (task == lowmem_deathpending) {
|
||||
|
||||
if (task == lowmem_deathpending)
|
||||
lowmem_deathpending = NULL;
|
||||
task_handoff_unregister(&task_nb);
|
||||
}
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc)
|
||||
{
|
||||
struct task_struct *p;
|
||||
struct task_struct *tsk;
|
||||
struct task_struct *selected = NULL;
|
||||
int rem = 0;
|
||||
int tasksize;
|
||||
int i;
|
||||
int min_adj = OOM_ADJUST_MAX + 1;
|
||||
int min_score_adj = OOM_SCORE_ADJ_MAX + 1;
|
||||
int selected_tasksize = 0;
|
||||
int selected_oom_adj;
|
||||
int selected_oom_score_adj;
|
||||
int array_size = ARRAY_SIZE(lowmem_adj);
|
||||
int other_free = global_page_state(NR_FREE_PAGES);
|
||||
int other_file = global_page_state(NR_FILE_PAGES) -
|
||||
@ -115,80 +117,77 @@ static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc)
|
||||
for (i = 0; i < array_size; i++) {
|
||||
if (other_free < lowmem_minfree[i] &&
|
||||
other_file < lowmem_minfree[i]) {
|
||||
min_adj = lowmem_adj[i];
|
||||
min_score_adj = lowmem_adj[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (sc->nr_to_scan > 0)
|
||||
lowmem_print(3, "lowmem_shrink %lu, %x, ofree %d %d, ma %d\n",
|
||||
sc->nr_to_scan, sc->gfp_mask, other_free,
|
||||
other_file, min_adj);
|
||||
other_file, min_score_adj);
|
||||
rem = global_page_state(NR_ACTIVE_ANON) +
|
||||
global_page_state(NR_ACTIVE_FILE) +
|
||||
global_page_state(NR_INACTIVE_ANON) +
|
||||
global_page_state(NR_INACTIVE_FILE);
|
||||
if (sc->nr_to_scan <= 0 || min_adj == OOM_ADJUST_MAX + 1) {
|
||||
if (sc->nr_to_scan <= 0 || min_score_adj == OOM_SCORE_ADJ_MAX + 1) {
|
||||
lowmem_print(5, "lowmem_shrink %lu, %x, return %d\n",
|
||||
sc->nr_to_scan, sc->gfp_mask, rem);
|
||||
return rem;
|
||||
}
|
||||
selected_oom_adj = min_adj;
|
||||
selected_oom_score_adj = min_score_adj;
|
||||
|
||||
read_lock(&tasklist_lock);
|
||||
for_each_process(p) {
|
||||
struct mm_struct *mm;
|
||||
struct signal_struct *sig;
|
||||
int oom_adj;
|
||||
rcu_read_lock();
|
||||
for_each_process(tsk) {
|
||||
struct task_struct *p;
|
||||
int oom_score_adj;
|
||||
|
||||
task_lock(p);
|
||||
mm = p->mm;
|
||||
sig = p->signal;
|
||||
if (!mm || !sig) {
|
||||
if (tsk->flags & PF_KTHREAD)
|
||||
continue;
|
||||
|
||||
p = find_lock_task_mm(tsk);
|
||||
if (!p)
|
||||
continue;
|
||||
|
||||
oom_score_adj = p->signal->oom_score_adj;
|
||||
if (oom_score_adj < min_score_adj) {
|
||||
task_unlock(p);
|
||||
continue;
|
||||
}
|
||||
oom_adj = sig->oom_adj;
|
||||
if (oom_adj < min_adj) {
|
||||
task_unlock(p);
|
||||
continue;
|
||||
}
|
||||
tasksize = get_mm_rss(mm);
|
||||
tasksize = get_mm_rss(p->mm);
|
||||
task_unlock(p);
|
||||
if (tasksize <= 0)
|
||||
continue;
|
||||
if (selected) {
|
||||
if (oom_adj < selected_oom_adj)
|
||||
if (oom_score_adj < selected_oom_score_adj)
|
||||
continue;
|
||||
if (oom_adj == selected_oom_adj &&
|
||||
if (oom_score_adj == selected_oom_score_adj &&
|
||||
tasksize <= selected_tasksize)
|
||||
continue;
|
||||
}
|
||||
selected = p;
|
||||
selected_tasksize = tasksize;
|
||||
selected_oom_adj = oom_adj;
|
||||
selected_oom_score_adj = oom_score_adj;
|
||||
lowmem_print(2, "select %d (%s), adj %d, size %d, to kill\n",
|
||||
p->pid, p->comm, oom_adj, tasksize);
|
||||
p->pid, p->comm, oom_score_adj, tasksize);
|
||||
}
|
||||
if (selected) {
|
||||
lowmem_print(1, "send sigkill to %d (%s), adj %d, size %d\n",
|
||||
selected->pid, selected->comm,
|
||||
selected_oom_adj, selected_tasksize);
|
||||
selected_oom_score_adj, selected_tasksize);
|
||||
/*
|
||||
* If CONFIG_PROFILING is off, then task_handoff_register()
|
||||
* is a nop. In that case we don't want to stall the killer
|
||||
* by setting lowmem_deathpending.
|
||||
* If CONFIG_PROFILING is off, then we don't want to stall
|
||||
* the killer by setting lowmem_deathpending.
|
||||
*/
|
||||
#ifdef CONFIG_PROFILING
|
||||
lowmem_deathpending = selected;
|
||||
lowmem_deathpending_timeout = jiffies + HZ;
|
||||
task_handoff_register(&task_nb);
|
||||
#endif
|
||||
force_sig(SIGKILL, selected);
|
||||
send_sig(SIGKILL, selected, 0);
|
||||
rem -= selected_tasksize;
|
||||
}
|
||||
lowmem_print(4, "lowmem_shrink %lu, %x, return %d\n",
|
||||
sc->nr_to_scan, sc->gfp_mask, rem);
|
||||
read_unlock(&tasklist_lock);
|
||||
rcu_read_unlock();
|
||||
return rem;
|
||||
}
|
||||
|
||||
@ -199,6 +198,7 @@ static struct shrinker lowmem_shrinker = {
|
||||
|
||||
static int __init lowmem_init(void)
|
||||
{
|
||||
task_handoff_register(&task_nb);
|
||||
register_shrinker(&lowmem_shrinker);
|
||||
return 0;
|
||||
}
|
||||
@ -206,6 +206,7 @@ static int __init lowmem_init(void)
|
||||
static void __exit lowmem_exit(void)
|
||||
{
|
||||
unregister_shrinker(&lowmem_shrinker);
|
||||
task_handoff_unregister(&task_nb);
|
||||
}
|
||||
|
||||
module_param_named(cost, lowmem_shrinker.seeks, int, S_IRUGO | S_IWUSR);
|
||||
|
470
drivers/staging/android/persistent_ram.c
Normal file
470
drivers/staging/android/persistent_ram.c
Normal file
@ -0,0 +1,470 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Google, Inc.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/memblock.h>
|
||||
#include <linux/rslib.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include "persistent_ram.h"
|
||||
|
||||
struct persistent_ram_buffer {
|
||||
uint32_t sig;
|
||||
atomic_t start;
|
||||
atomic_t size;
|
||||
uint8_t data[0];
|
||||
};
|
||||
|
||||
#define PERSISTENT_RAM_SIG (0x43474244) /* DBGC */
|
||||
|
||||
static __initdata LIST_HEAD(persistent_ram_list);
|
||||
|
||||
static inline size_t buffer_size(struct persistent_ram_zone *prz)
|
||||
{
|
||||
return atomic_read(&prz->buffer->size);
|
||||
}
|
||||
|
||||
static inline size_t buffer_start(struct persistent_ram_zone *prz)
|
||||
{
|
||||
return atomic_read(&prz->buffer->start);
|
||||
}
|
||||
|
||||
/* increase and wrap the start pointer, returning the old value */
|
||||
static inline size_t buffer_start_add(struct persistent_ram_zone *prz, size_t a)
|
||||
{
|
||||
int old;
|
||||
int new;
|
||||
|
||||
do {
|
||||
old = atomic_read(&prz->buffer->start);
|
||||
new = old + a;
|
||||
while (unlikely(new > prz->buffer_size))
|
||||
new -= prz->buffer_size;
|
||||
} while (atomic_cmpxchg(&prz->buffer->start, old, new) != old);
|
||||
|
||||
return old;
|
||||
}
|
||||
|
||||
/* increase the size counter until it hits the max size */
|
||||
static inline void buffer_size_add(struct persistent_ram_zone *prz, size_t a)
|
||||
{
|
||||
size_t old;
|
||||
size_t new;
|
||||
|
||||
if (atomic_read(&prz->buffer->size) == prz->buffer_size)
|
||||
return;
|
||||
|
||||
do {
|
||||
old = atomic_read(&prz->buffer->size);
|
||||
new = old + a;
|
||||
if (new > prz->buffer_size)
|
||||
new = prz->buffer_size;
|
||||
} while (atomic_cmpxchg(&prz->buffer->size, old, new) != old);
|
||||
}
|
||||
|
||||
/* increase the size counter, retuning an error if it hits the max size */
|
||||
static inline ssize_t buffer_size_add_clamp(struct persistent_ram_zone *prz,
|
||||
size_t a)
|
||||
{
|
||||
size_t old;
|
||||
size_t new;
|
||||
|
||||
do {
|
||||
old = atomic_read(&prz->buffer->size);
|
||||
new = old + a;
|
||||
if (new > prz->buffer_size)
|
||||
return -ENOMEM;
|
||||
} while (atomic_cmpxchg(&prz->buffer->size, old, new) != old);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void notrace persistent_ram_encode_rs8(struct persistent_ram_zone *prz,
|
||||
uint8_t *data, size_t len, uint8_t *ecc)
|
||||
{
|
||||
int i;
|
||||
uint16_t par[prz->ecc_size];
|
||||
|
||||
/* Initialize the parity buffer */
|
||||
memset(par, 0, sizeof(par));
|
||||
encode_rs8(prz->rs_decoder, data, len, par, 0);
|
||||
for (i = 0; i < prz->ecc_size; i++)
|
||||
ecc[i] = par[i];
|
||||
}
|
||||
|
||||
static int persistent_ram_decode_rs8(struct persistent_ram_zone *prz,
|
||||
void *data, size_t len, uint8_t *ecc)
|
||||
{
|
||||
int i;
|
||||
uint16_t par[prz->ecc_size];
|
||||
|
||||
for (i = 0; i < prz->ecc_size; i++)
|
||||
par[i] = ecc[i];
|
||||
return decode_rs8(prz->rs_decoder, data, par, len,
|
||||
NULL, 0, NULL, 0, NULL);
|
||||
}
|
||||
|
||||
static void notrace persistent_ram_update_ecc(struct persistent_ram_zone *prz,
|
||||
unsigned int start, unsigned int count)
|
||||
{
|
||||
struct persistent_ram_buffer *buffer = prz->buffer;
|
||||
uint8_t *buffer_end = buffer->data + prz->buffer_size;
|
||||
uint8_t *block;
|
||||
uint8_t *par;
|
||||
int ecc_block_size = prz->ecc_block_size;
|
||||
int ecc_size = prz->ecc_size;
|
||||
int size = prz->ecc_block_size;
|
||||
|
||||
if (!prz->ecc)
|
||||
return;
|
||||
|
||||
block = buffer->data + (start & ~(ecc_block_size - 1));
|
||||
par = prz->par_buffer + (start / ecc_block_size) * prz->ecc_size;
|
||||
|
||||
do {
|
||||
if (block + ecc_block_size > buffer_end)
|
||||
size = buffer_end - block;
|
||||
persistent_ram_encode_rs8(prz, block, size, par);
|
||||
block += ecc_block_size;
|
||||
par += ecc_size;
|
||||
} while (block < buffer->data + start + count);
|
||||
}
|
||||
|
||||
static void persistent_ram_update_header_ecc(struct persistent_ram_zone *prz)
|
||||
{
|
||||
struct persistent_ram_buffer *buffer = prz->buffer;
|
||||
|
||||
if (!prz->ecc)
|
||||
return;
|
||||
|
||||
persistent_ram_encode_rs8(prz, (uint8_t *)buffer, sizeof(*buffer),
|
||||
prz->par_header);
|
||||
}
|
||||
|
||||
static void persistent_ram_ecc_old(struct persistent_ram_zone *prz)
|
||||
{
|
||||
struct persistent_ram_buffer *buffer = prz->buffer;
|
||||
uint8_t *block;
|
||||
uint8_t *par;
|
||||
|
||||
if (!prz->ecc)
|
||||
return;
|
||||
|
||||
block = buffer->data;
|
||||
par = prz->par_buffer;
|
||||
while (block < buffer->data + buffer_size(prz)) {
|
||||
int numerr;
|
||||
int size = prz->ecc_block_size;
|
||||
if (block + size > buffer->data + prz->buffer_size)
|
||||
size = buffer->data + prz->buffer_size - block;
|
||||
numerr = persistent_ram_decode_rs8(prz, block, size, par);
|
||||
if (numerr > 0) {
|
||||
pr_devel("persistent_ram: error in block %p, %d\n",
|
||||
block, numerr);
|
||||
prz->corrected_bytes += numerr;
|
||||
} else if (numerr < 0) {
|
||||
pr_devel("persistent_ram: uncorrectable error in block %p\n",
|
||||
block);
|
||||
prz->bad_blocks++;
|
||||
}
|
||||
block += prz->ecc_block_size;
|
||||
par += prz->ecc_size;
|
||||
}
|
||||
}
|
||||
|
||||
static int persistent_ram_init_ecc(struct persistent_ram_zone *prz,
|
||||
size_t buffer_size)
|
||||
{
|
||||
int numerr;
|
||||
struct persistent_ram_buffer *buffer = prz->buffer;
|
||||
int ecc_blocks;
|
||||
|
||||
if (!prz->ecc)
|
||||
return 0;
|
||||
|
||||
prz->ecc_block_size = 128;
|
||||
prz->ecc_size = 16;
|
||||
prz->ecc_symsize = 8;
|
||||
prz->ecc_poly = 0x11d;
|
||||
|
||||
ecc_blocks = DIV_ROUND_UP(prz->buffer_size, prz->ecc_block_size);
|
||||
prz->buffer_size -= (ecc_blocks + 1) * prz->ecc_size;
|
||||
|
||||
if (prz->buffer_size > buffer_size) {
|
||||
pr_err("persistent_ram: invalid size %zu, non-ecc datasize %zu\n",
|
||||
buffer_size, prz->buffer_size);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
prz->par_buffer = buffer->data + prz->buffer_size;
|
||||
prz->par_header = prz->par_buffer + ecc_blocks * prz->ecc_size;
|
||||
|
||||
/*
|
||||
* first consecutive root is 0
|
||||
* primitive element to generate roots = 1
|
||||
*/
|
||||
prz->rs_decoder = init_rs(prz->ecc_symsize, prz->ecc_poly, 0, 1,
|
||||
prz->ecc_size);
|
||||
if (prz->rs_decoder == NULL) {
|
||||
pr_info("persistent_ram: init_rs failed\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
prz->corrected_bytes = 0;
|
||||
prz->bad_blocks = 0;
|
||||
|
||||
numerr = persistent_ram_decode_rs8(prz, buffer, sizeof(*buffer),
|
||||
prz->par_header);
|
||||
if (numerr > 0) {
|
||||
pr_info("persistent_ram: error in header, %d\n", numerr);
|
||||
prz->corrected_bytes += numerr;
|
||||
} else if (numerr < 0) {
|
||||
pr_info("persistent_ram: uncorrectable error in header\n");
|
||||
prz->bad_blocks++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ssize_t persistent_ram_ecc_string(struct persistent_ram_zone *prz,
|
||||
char *str, size_t len)
|
||||
{
|
||||
ssize_t ret;
|
||||
|
||||
if (prz->corrected_bytes || prz->bad_blocks)
|
||||
ret = snprintf(str, len, ""
|
||||
"\n%d Corrected bytes, %d unrecoverable blocks\n",
|
||||
prz->corrected_bytes, prz->bad_blocks);
|
||||
else
|
||||
ret = snprintf(str, len, "\nNo errors detected\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void notrace persistent_ram_update(struct persistent_ram_zone *prz,
|
||||
const void *s, unsigned int start, unsigned int count)
|
||||
{
|
||||
struct persistent_ram_buffer *buffer = prz->buffer;
|
||||
memcpy(buffer->data + start, s, count);
|
||||
persistent_ram_update_ecc(prz, start, count);
|
||||
}
|
||||
|
||||
static void __init
|
||||
persistent_ram_save_old(struct persistent_ram_zone *prz)
|
||||
{
|
||||
struct persistent_ram_buffer *buffer = prz->buffer;
|
||||
size_t size = buffer_size(prz);
|
||||
size_t start = buffer_start(prz);
|
||||
char *dest;
|
||||
|
||||
persistent_ram_ecc_old(prz);
|
||||
|
||||
dest = kmalloc(size, GFP_KERNEL);
|
||||
if (dest == NULL) {
|
||||
pr_err("persistent_ram: failed to allocate buffer\n");
|
||||
return;
|
||||
}
|
||||
|
||||
prz->old_log = dest;
|
||||
prz->old_log_size = size;
|
||||
memcpy(prz->old_log, &buffer->data[start], size - start);
|
||||
memcpy(prz->old_log + size - start, &buffer->data[0], start);
|
||||
}
|
||||
|
||||
int notrace persistent_ram_write(struct persistent_ram_zone *prz,
|
||||
const void *s, unsigned int count)
|
||||
{
|
||||
int rem;
|
||||
int c = count;
|
||||
size_t start;
|
||||
|
||||
if (unlikely(c > prz->buffer_size)) {
|
||||
s += c - prz->buffer_size;
|
||||
c = prz->buffer_size;
|
||||
}
|
||||
|
||||
buffer_size_add_clamp(prz, c);
|
||||
|
||||
start = buffer_start_add(prz, c);
|
||||
|
||||
rem = prz->buffer_size - start;
|
||||
if (unlikely(rem < c)) {
|
||||
persistent_ram_update(prz, s, start, rem);
|
||||
s += rem;
|
||||
c -= rem;
|
||||
start = 0;
|
||||
}
|
||||
persistent_ram_update(prz, s, start, c);
|
||||
|
||||
persistent_ram_update_header_ecc(prz);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
size_t persistent_ram_old_size(struct persistent_ram_zone *prz)
|
||||
{
|
||||
return prz->old_log_size;
|
||||
}
|
||||
|
||||
void *persistent_ram_old(struct persistent_ram_zone *prz)
|
||||
{
|
||||
return prz->old_log;
|
||||
}
|
||||
|
||||
void persistent_ram_free_old(struct persistent_ram_zone *prz)
|
||||
{
|
||||
kfree(prz->old_log);
|
||||
prz->old_log = NULL;
|
||||
prz->old_log_size = 0;
|
||||
}
|
||||
|
||||
static int persistent_ram_buffer_map(phys_addr_t start, phys_addr_t size,
|
||||
struct persistent_ram_zone *prz)
|
||||
{
|
||||
struct page **pages;
|
||||
phys_addr_t page_start;
|
||||
unsigned int page_count;
|
||||
pgprot_t prot;
|
||||
unsigned int i;
|
||||
|
||||
page_start = start - offset_in_page(start);
|
||||
page_count = DIV_ROUND_UP(size + offset_in_page(start), PAGE_SIZE);
|
||||
|
||||
prot = pgprot_noncached(PAGE_KERNEL);
|
||||
|
||||
pages = kmalloc(sizeof(struct page *) * page_count, GFP_KERNEL);
|
||||
if (!pages) {
|
||||
pr_err("%s: Failed to allocate array for %u pages\n", __func__,
|
||||
page_count);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for (i = 0; i < page_count; i++) {
|
||||
phys_addr_t addr = page_start + i * PAGE_SIZE;
|
||||
pages[i] = pfn_to_page(addr >> PAGE_SHIFT);
|
||||
}
|
||||
prz->vaddr = vmap(pages, page_count, VM_MAP, prot);
|
||||
kfree(pages);
|
||||
if (!prz->vaddr) {
|
||||
pr_err("%s: Failed to map %u pages\n", __func__, page_count);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
prz->buffer = prz->vaddr + offset_in_page(start);
|
||||
prz->buffer_size = size - sizeof(struct persistent_ram_buffer);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init persistent_ram_buffer_init(const char *name,
|
||||
struct persistent_ram_zone *prz)
|
||||
{
|
||||
int i;
|
||||
struct persistent_ram *ram;
|
||||
struct persistent_ram_descriptor *desc;
|
||||
phys_addr_t start;
|
||||
|
||||
list_for_each_entry(ram, &persistent_ram_list, node) {
|
||||
start = ram->start;
|
||||
for (i = 0; i < ram->num_descs; i++) {
|
||||
desc = &ram->descs[i];
|
||||
if (!strcmp(desc->name, name))
|
||||
return persistent_ram_buffer_map(start,
|
||||
desc->size, prz);
|
||||
start += desc->size;
|
||||
}
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static __init
|
||||
struct persistent_ram_zone *__persistent_ram_init(struct device *dev, bool ecc)
|
||||
{
|
||||
struct persistent_ram_zone *prz;
|
||||
int ret;
|
||||
|
||||
prz = kzalloc(sizeof(struct persistent_ram_zone), GFP_KERNEL);
|
||||
if (!prz) {
|
||||
pr_err("persistent_ram: failed to allocate persistent ram zone\n");
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&prz->node);
|
||||
|
||||
ret = persistent_ram_buffer_init(dev_name(dev), prz);
|
||||
if (ret) {
|
||||
pr_err("persistent_ram: failed to initialize buffer\n");
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
prz->ecc = ecc;
|
||||
ret = persistent_ram_init_ecc(prz, prz->buffer_size);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
if (prz->buffer->sig == PERSISTENT_RAM_SIG) {
|
||||
if (buffer_size(prz) > prz->buffer_size ||
|
||||
buffer_start(prz) > buffer_size(prz))
|
||||
pr_info("persistent_ram: found existing invalid buffer,"
|
||||
" size %ld, start %ld\n",
|
||||
buffer_size(prz), buffer_start(prz));
|
||||
else {
|
||||
pr_info("persistent_ram: found existing buffer,"
|
||||
" size %ld, start %ld\n",
|
||||
buffer_size(prz), buffer_start(prz));
|
||||
persistent_ram_save_old(prz);
|
||||
}
|
||||
} else {
|
||||
pr_info("persistent_ram: no valid data in buffer"
|
||||
" (sig = 0x%08x)\n", prz->buffer->sig);
|
||||
}
|
||||
|
||||
prz->buffer->sig = PERSISTENT_RAM_SIG;
|
||||
atomic_set(&prz->buffer->start, 0);
|
||||
atomic_set(&prz->buffer->size, 0);
|
||||
|
||||
return prz;
|
||||
}
|
||||
|
||||
struct persistent_ram_zone * __init
|
||||
persistent_ram_init_ringbuffer(struct device *dev, bool ecc)
|
||||
{
|
||||
return __persistent_ram_init(dev, ecc);
|
||||
}
|
||||
|
||||
int __init persistent_ram_early_init(struct persistent_ram *ram)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = memblock_reserve(ram->start, ram->size);
|
||||
if (ret) {
|
||||
pr_err("Failed to reserve persistent memory from %08lx-%08lx\n",
|
||||
(long)ram->start, (long)(ram->start + ram->size - 1));
|
||||
return ret;
|
||||
}
|
||||
|
||||
list_add_tail(&ram->node, &persistent_ram_list);
|
||||
|
||||
pr_info("Initialized persistent memory from %08lx-%08lx\n",
|
||||
(long)ram->start, (long)(ram->start + ram->size - 1));
|
||||
|
||||
return 0;
|
||||
}
|
78
drivers/staging/android/persistent_ram.h
Normal file
78
drivers/staging/android/persistent_ram.h
Normal file
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (C) 2011 Google, Inc.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_PERSISTENT_RAM_H__
|
||||
#define __LINUX_PERSISTENT_RAM_H__
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
struct persistent_ram_buffer;
|
||||
|
||||
struct persistent_ram_descriptor {
|
||||
const char *name;
|
||||
phys_addr_t size;
|
||||
};
|
||||
|
||||
struct persistent_ram {
|
||||
phys_addr_t start;
|
||||
phys_addr_t size;
|
||||
|
||||
int num_descs;
|
||||
struct persistent_ram_descriptor *descs;
|
||||
|
||||
struct list_head node;
|
||||
};
|
||||
|
||||
struct persistent_ram_zone {
|
||||
struct list_head node;
|
||||
void *vaddr;
|
||||
struct persistent_ram_buffer *buffer;
|
||||
size_t buffer_size;
|
||||
|
||||
/* ECC correction */
|
||||
bool ecc;
|
||||
char *par_buffer;
|
||||
char *par_header;
|
||||
struct rs_control *rs_decoder;
|
||||
int corrected_bytes;
|
||||
int bad_blocks;
|
||||
int ecc_block_size;
|
||||
int ecc_size;
|
||||
int ecc_symsize;
|
||||
int ecc_poly;
|
||||
|
||||
char *old_log;
|
||||
size_t old_log_size;
|
||||
size_t old_log_footer_size;
|
||||
bool early;
|
||||
};
|
||||
|
||||
int persistent_ram_early_init(struct persistent_ram *ram);
|
||||
|
||||
struct persistent_ram_zone *persistent_ram_init_ringbuffer(struct device *dev,
|
||||
bool ecc);
|
||||
|
||||
int persistent_ram_write(struct persistent_ram_zone *prz, const void *s,
|
||||
unsigned int count);
|
||||
|
||||
size_t persistent_ram_old_size(struct persistent_ram_zone *prz);
|
||||
void *persistent_ram_old(struct persistent_ram_zone *prz);
|
||||
void persistent_ram_free_old(struct persistent_ram_zone *prz);
|
||||
ssize_t persistent_ram_ecc_string(struct persistent_ram_zone *prz,
|
||||
char *str, size_t len);
|
||||
|
||||
#endif
|
@ -21,129 +21,24 @@
|
||||
#include <linux/string.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/io.h>
|
||||
#include "persistent_ram.h"
|
||||
#include "ram_console.h"
|
||||
|
||||
#ifdef CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION
|
||||
#include <linux/rslib.h>
|
||||
#endif
|
||||
|
||||
struct ram_console_buffer {
|
||||
uint32_t sig;
|
||||
uint32_t start;
|
||||
uint32_t size;
|
||||
uint8_t data[0];
|
||||
};
|
||||
|
||||
#define RAM_CONSOLE_SIG (0x43474244) /* DBGC */
|
||||
|
||||
#ifdef CONFIG_ANDROID_RAM_CONSOLE_EARLY_INIT
|
||||
static char __initdata
|
||||
ram_console_old_log_init_buffer[CONFIG_ANDROID_RAM_CONSOLE_EARLY_SIZE];
|
||||
#endif
|
||||
static char *ram_console_old_log;
|
||||
static size_t ram_console_old_log_size;
|
||||
|
||||
static struct ram_console_buffer *ram_console_buffer;
|
||||
static size_t ram_console_buffer_size;
|
||||
#ifdef CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION
|
||||
static char *ram_console_par_buffer;
|
||||
static struct rs_control *ram_console_rs_decoder;
|
||||
static int ram_console_corrected_bytes;
|
||||
static int ram_console_bad_blocks;
|
||||
#define ECC_BLOCK_SIZE CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_DATA_SIZE
|
||||
#define ECC_SIZE CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_ECC_SIZE
|
||||
#define ECC_SYMSIZE CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_SYMBOL_SIZE
|
||||
#define ECC_POLY CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_POLYNOMIAL
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION
|
||||
static void ram_console_encode_rs8(uint8_t *data, size_t len, uint8_t *ecc)
|
||||
{
|
||||
int i;
|
||||
uint16_t par[ECC_SIZE];
|
||||
/* Initialize the parity buffer */
|
||||
memset(par, 0, sizeof(par));
|
||||
encode_rs8(ram_console_rs_decoder, data, len, par, 0);
|
||||
for (i = 0; i < ECC_SIZE; i++)
|
||||
ecc[i] = par[i];
|
||||
}
|
||||
|
||||
static int ram_console_decode_rs8(void *data, size_t len, uint8_t *ecc)
|
||||
{
|
||||
int i;
|
||||
uint16_t par[ECC_SIZE];
|
||||
for (i = 0; i < ECC_SIZE; i++)
|
||||
par[i] = ecc[i];
|
||||
return decode_rs8(ram_console_rs_decoder, data, par, len,
|
||||
NULL, 0, NULL, 0, NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void ram_console_update(const char *s, unsigned int count)
|
||||
{
|
||||
struct ram_console_buffer *buffer = ram_console_buffer;
|
||||
#ifdef CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION
|
||||
uint8_t *buffer_end = buffer->data + ram_console_buffer_size;
|
||||
uint8_t *block;
|
||||
uint8_t *par;
|
||||
int size = ECC_BLOCK_SIZE;
|
||||
#endif
|
||||
memcpy(buffer->data + buffer->start, s, count);
|
||||
#ifdef CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION
|
||||
block = buffer->data + (buffer->start & ~(ECC_BLOCK_SIZE - 1));
|
||||
par = ram_console_par_buffer +
|
||||
(buffer->start / ECC_BLOCK_SIZE) * ECC_SIZE;
|
||||
do {
|
||||
if (block + ECC_BLOCK_SIZE > buffer_end)
|
||||
size = buffer_end - block;
|
||||
ram_console_encode_rs8(block, size, par);
|
||||
block += ECC_BLOCK_SIZE;
|
||||
par += ECC_SIZE;
|
||||
} while (block < buffer->data + buffer->start + count);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void ram_console_update_header(void)
|
||||
{
|
||||
#ifdef CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION
|
||||
struct ram_console_buffer *buffer = ram_console_buffer;
|
||||
uint8_t *par;
|
||||
par = ram_console_par_buffer +
|
||||
DIV_ROUND_UP(ram_console_buffer_size, ECC_BLOCK_SIZE) * ECC_SIZE;
|
||||
ram_console_encode_rs8((uint8_t *)buffer, sizeof(*buffer), par);
|
||||
#endif
|
||||
}
|
||||
static struct persistent_ram_zone *ram_console_zone;
|
||||
static const char *bootinfo;
|
||||
static size_t bootinfo_size;
|
||||
|
||||
static void
|
||||
ram_console_write(struct console *console, const char *s, unsigned int count)
|
||||
{
|
||||
int rem;
|
||||
struct ram_console_buffer *buffer = ram_console_buffer;
|
||||
|
||||
if (count > ram_console_buffer_size) {
|
||||
s += count - ram_console_buffer_size;
|
||||
count = ram_console_buffer_size;
|
||||
}
|
||||
rem = ram_console_buffer_size - buffer->start;
|
||||
if (rem < count) {
|
||||
ram_console_update(s, rem);
|
||||
s += rem;
|
||||
count -= rem;
|
||||
buffer->start = 0;
|
||||
buffer->size = ram_console_buffer_size;
|
||||
}
|
||||
ram_console_update(s, count);
|
||||
|
||||
buffer->start += count;
|
||||
if (buffer->size < ram_console_buffer_size)
|
||||
buffer->size += count;
|
||||
ram_console_update_header();
|
||||
struct persistent_ram_zone *prz = console->data;
|
||||
persistent_ram_write(prz, s, count);
|
||||
}
|
||||
|
||||
static struct console ram_console = {
|
||||
.name = "ram",
|
||||
.write = ram_console_write,
|
||||
.flags = CON_PRINTBUFFER | CON_ENABLED,
|
||||
.flags = CON_PRINTBUFFER | CON_ENABLED | CON_ANYTIME,
|
||||
.index = -1,
|
||||
};
|
||||
|
||||
@ -155,220 +50,31 @@ void ram_console_enable_console(int enabled)
|
||||
ram_console.flags &= ~CON_ENABLED;
|
||||
}
|
||||
|
||||
static void __init
|
||||
ram_console_save_old(struct ram_console_buffer *buffer, const char *bootinfo,
|
||||
char *dest)
|
||||
static int __init ram_console_probe(struct platform_device *pdev)
|
||||
{
|
||||
size_t old_log_size = buffer->size;
|
||||
size_t bootinfo_size = 0;
|
||||
size_t total_size = old_log_size;
|
||||
char *ptr;
|
||||
const char *bootinfo_label = "Boot info:\n";
|
||||
struct ram_console_platform_data *pdata = pdev->dev.platform_data;
|
||||
struct persistent_ram_zone *prz;
|
||||
|
||||
#ifdef CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION
|
||||
uint8_t *block;
|
||||
uint8_t *par;
|
||||
char strbuf[80];
|
||||
int strbuf_len = 0;
|
||||
prz = persistent_ram_init_ringbuffer(&pdev->dev, true);
|
||||
if (IS_ERR(prz))
|
||||
return PTR_ERR(prz);
|
||||
|
||||
block = buffer->data;
|
||||
par = ram_console_par_buffer;
|
||||
while (block < buffer->data + buffer->size) {
|
||||
int numerr;
|
||||
int size = ECC_BLOCK_SIZE;
|
||||
if (block + size > buffer->data + ram_console_buffer_size)
|
||||
size = buffer->data + ram_console_buffer_size - block;
|
||||
numerr = ram_console_decode_rs8(block, size, par);
|
||||
if (numerr > 0) {
|
||||
#if 0
|
||||
printk(KERN_INFO "ram_console: error in block %p, %d\n",
|
||||
block, numerr);
|
||||
#endif
|
||||
ram_console_corrected_bytes += numerr;
|
||||
} else if (numerr < 0) {
|
||||
#if 0
|
||||
printk(KERN_INFO "ram_console: uncorrectable error in "
|
||||
"block %p\n", block);
|
||||
#endif
|
||||
ram_console_bad_blocks++;
|
||||
}
|
||||
block += ECC_BLOCK_SIZE;
|
||||
par += ECC_SIZE;
|
||||
}
|
||||
if (ram_console_corrected_bytes || ram_console_bad_blocks)
|
||||
strbuf_len = snprintf(strbuf, sizeof(strbuf),
|
||||
"\n%d Corrected bytes, %d unrecoverable blocks\n",
|
||||
ram_console_corrected_bytes, ram_console_bad_blocks);
|
||||
else
|
||||
strbuf_len = snprintf(strbuf, sizeof(strbuf),
|
||||
"\nNo errors detected\n");
|
||||
if (strbuf_len >= sizeof(strbuf))
|
||||
strbuf_len = sizeof(strbuf) - 1;
|
||||
total_size += strbuf_len;
|
||||
#endif
|
||||
|
||||
if (bootinfo)
|
||||
bootinfo_size = strlen(bootinfo) + strlen(bootinfo_label);
|
||||
total_size += bootinfo_size;
|
||||
|
||||
if (dest == NULL) {
|
||||
dest = kmalloc(total_size, GFP_KERNEL);
|
||||
if (dest == NULL) {
|
||||
printk(KERN_ERR
|
||||
"ram_console: failed to allocate buffer\n");
|
||||
return;
|
||||
}
|
||||
if (pdata) {
|
||||
bootinfo = kstrdup(pdata->bootinfo, GFP_KERNEL);
|
||||
if (bootinfo)
|
||||
bootinfo_size = strlen(bootinfo);
|
||||
}
|
||||
|
||||
ram_console_old_log = dest;
|
||||
ram_console_old_log_size = total_size;
|
||||
memcpy(ram_console_old_log,
|
||||
&buffer->data[buffer->start], buffer->size - buffer->start);
|
||||
memcpy(ram_console_old_log + buffer->size - buffer->start,
|
||||
&buffer->data[0], buffer->start);
|
||||
ptr = ram_console_old_log + old_log_size;
|
||||
#ifdef CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION
|
||||
memcpy(ptr, strbuf, strbuf_len);
|
||||
ptr += strbuf_len;
|
||||
#endif
|
||||
if (bootinfo) {
|
||||
memcpy(ptr, bootinfo_label, strlen(bootinfo_label));
|
||||
ptr += strlen(bootinfo_label);
|
||||
memcpy(ptr, bootinfo, bootinfo_size);
|
||||
ptr += bootinfo_size;
|
||||
}
|
||||
}
|
||||
|
||||
static int __init ram_console_init(struct ram_console_buffer *buffer,
|
||||
size_t buffer_size, const char *bootinfo,
|
||||
char *old_buf)
|
||||
{
|
||||
#ifdef CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION
|
||||
int numerr;
|
||||
uint8_t *par;
|
||||
#endif
|
||||
ram_console_buffer = buffer;
|
||||
ram_console_buffer_size =
|
||||
buffer_size - sizeof(struct ram_console_buffer);
|
||||
|
||||
if (ram_console_buffer_size > buffer_size) {
|
||||
pr_err("ram_console: buffer %p, invalid size %zu, "
|
||||
"datasize %zu\n", buffer, buffer_size,
|
||||
ram_console_buffer_size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION
|
||||
ram_console_buffer_size -= (DIV_ROUND_UP(ram_console_buffer_size,
|
||||
ECC_BLOCK_SIZE) + 1) * ECC_SIZE;
|
||||
|
||||
if (ram_console_buffer_size > buffer_size) {
|
||||
pr_err("ram_console: buffer %p, invalid size %zu, "
|
||||
"non-ecc datasize %zu\n",
|
||||
buffer, buffer_size, ram_console_buffer_size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ram_console_par_buffer = buffer->data + ram_console_buffer_size;
|
||||
|
||||
|
||||
/* first consecutive root is 0
|
||||
* primitive element to generate roots = 1
|
||||
*/
|
||||
ram_console_rs_decoder = init_rs(ECC_SYMSIZE, ECC_POLY, 0, 1, ECC_SIZE);
|
||||
if (ram_console_rs_decoder == NULL) {
|
||||
printk(KERN_INFO "ram_console: init_rs failed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ram_console_corrected_bytes = 0;
|
||||
ram_console_bad_blocks = 0;
|
||||
|
||||
par = ram_console_par_buffer +
|
||||
DIV_ROUND_UP(ram_console_buffer_size, ECC_BLOCK_SIZE) * ECC_SIZE;
|
||||
|
||||
numerr = ram_console_decode_rs8(buffer, sizeof(*buffer), par);
|
||||
if (numerr > 0) {
|
||||
printk(KERN_INFO "ram_console: error in header, %d\n", numerr);
|
||||
ram_console_corrected_bytes += numerr;
|
||||
} else if (numerr < 0) {
|
||||
printk(KERN_INFO
|
||||
"ram_console: uncorrectable error in header\n");
|
||||
ram_console_bad_blocks++;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (buffer->sig == RAM_CONSOLE_SIG) {
|
||||
if (buffer->size > ram_console_buffer_size
|
||||
|| buffer->start > buffer->size)
|
||||
printk(KERN_INFO "ram_console: found existing invalid "
|
||||
"buffer, size %d, start %d\n",
|
||||
buffer->size, buffer->start);
|
||||
else {
|
||||
printk(KERN_INFO "ram_console: found existing buffer, "
|
||||
"size %d, start %d\n",
|
||||
buffer->size, buffer->start);
|
||||
ram_console_save_old(buffer, bootinfo, old_buf);
|
||||
}
|
||||
} else {
|
||||
printk(KERN_INFO "ram_console: no valid data in buffer "
|
||||
"(sig = 0x%08x)\n", buffer->sig);
|
||||
}
|
||||
|
||||
buffer->sig = RAM_CONSOLE_SIG;
|
||||
buffer->start = 0;
|
||||
buffer->size = 0;
|
||||
ram_console_zone = prz;
|
||||
ram_console.data = prz;
|
||||
|
||||
register_console(&ram_console);
|
||||
#ifdef CONFIG_ANDROID_RAM_CONSOLE_ENABLE_VERBOSE
|
||||
console_verbose();
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ANDROID_RAM_CONSOLE_EARLY_INIT
|
||||
static int __init ram_console_early_init(void)
|
||||
{
|
||||
return ram_console_init((struct ram_console_buffer *)
|
||||
CONFIG_ANDROID_RAM_CONSOLE_EARLY_ADDR,
|
||||
CONFIG_ANDROID_RAM_CONSOLE_EARLY_SIZE,
|
||||
NULL,
|
||||
ram_console_old_log_init_buffer);
|
||||
}
|
||||
#else
|
||||
static int ram_console_driver_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res = pdev->resource;
|
||||
size_t start;
|
||||
size_t buffer_size;
|
||||
void *buffer;
|
||||
const char *bootinfo = NULL;
|
||||
struct ram_console_platform_data *pdata = pdev->dev.platform_data;
|
||||
|
||||
if (res == NULL || pdev->num_resources != 1 ||
|
||||
!(res->flags & IORESOURCE_MEM)) {
|
||||
printk(KERN_ERR "ram_console: invalid resource, %p %d flags "
|
||||
"%lx\n", res, pdev->num_resources, res ? res->flags : 0);
|
||||
return -ENXIO;
|
||||
}
|
||||
buffer_size = res->end - res->start + 1;
|
||||
start = res->start;
|
||||
printk(KERN_INFO "ram_console: got buffer at %zx, size %zx\n",
|
||||
start, buffer_size);
|
||||
buffer = ioremap(res->start, buffer_size);
|
||||
if (buffer == NULL) {
|
||||
printk(KERN_ERR "ram_console: failed to map memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (pdata)
|
||||
bootinfo = pdata->bootinfo;
|
||||
|
||||
return ram_console_init(buffer, buffer_size, bootinfo, NULL/* allocate */);
|
||||
}
|
||||
|
||||
static struct platform_driver ram_console_driver = {
|
||||
.probe = ram_console_driver_probe,
|
||||
.driver = {
|
||||
.name = "ram_console",
|
||||
},
|
||||
@ -376,10 +82,11 @@ static struct platform_driver ram_console_driver = {
|
||||
|
||||
static int __init ram_console_module_init(void)
|
||||
{
|
||||
int err;
|
||||
err = platform_driver_register(&ram_console_driver);
|
||||
return err;
|
||||
return platform_driver_probe(&ram_console_driver, ram_console_probe);
|
||||
}
|
||||
|
||||
#ifndef CONFIG_PRINTK
|
||||
#define dmesg_restrict 0
|
||||
#endif
|
||||
|
||||
static ssize_t ram_console_read_old(struct file *file, char __user *buf,
|
||||
@ -387,14 +94,52 @@ static ssize_t ram_console_read_old(struct file *file, char __user *buf,
|
||||
{
|
||||
loff_t pos = *offset;
|
||||
ssize_t count;
|
||||
struct persistent_ram_zone *prz = ram_console_zone;
|
||||
size_t old_log_size = persistent_ram_old_size(prz);
|
||||
const char *old_log = persistent_ram_old(prz);
|
||||
char *str;
|
||||
int ret;
|
||||
|
||||
if (pos >= ram_console_old_log_size)
|
||||
return 0;
|
||||
if (dmesg_restrict && !capable(CAP_SYSLOG))
|
||||
return -EPERM;
|
||||
|
||||
count = min(len, (size_t)(ram_console_old_log_size - pos));
|
||||
if (copy_to_user(buf, ram_console_old_log + pos, count))
|
||||
return -EFAULT;
|
||||
/* Main last_kmsg log */
|
||||
if (pos < old_log_size) {
|
||||
count = min(len, (size_t)(old_log_size - pos));
|
||||
if (copy_to_user(buf, old_log + pos, count))
|
||||
return -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* ECC correction notice */
|
||||
pos -= old_log_size;
|
||||
count = persistent_ram_ecc_string(prz, NULL, 0);
|
||||
if (pos < count) {
|
||||
str = kmalloc(count, GFP_KERNEL);
|
||||
if (!str)
|
||||
return -ENOMEM;
|
||||
persistent_ram_ecc_string(prz, str, count + 1);
|
||||
count = min(len, (size_t)(count - pos));
|
||||
ret = copy_to_user(buf, str + pos, count);
|
||||
kfree(str);
|
||||
if (ret)
|
||||
return -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Boot info passed through pdata */
|
||||
pos -= count;
|
||||
if (pos < bootinfo_size) {
|
||||
count = min(len, (size_t)(bootinfo_size - pos));
|
||||
if (copy_to_user(buf, bootinfo + pos, count))
|
||||
return -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* EOF */
|
||||
return 0;
|
||||
|
||||
out:
|
||||
*offset += count;
|
||||
return count;
|
||||
}
|
||||
@ -407,37 +152,28 @@ static const struct file_operations ram_console_file_ops = {
|
||||
static int __init ram_console_late_init(void)
|
||||
{
|
||||
struct proc_dir_entry *entry;
|
||||
struct persistent_ram_zone *prz = ram_console_zone;
|
||||
|
||||
if (ram_console_old_log == NULL)
|
||||
if (!prz)
|
||||
return 0;
|
||||
#ifdef CONFIG_ANDROID_RAM_CONSOLE_EARLY_INIT
|
||||
ram_console_old_log = kmalloc(ram_console_old_log_size, GFP_KERNEL);
|
||||
if (ram_console_old_log == NULL) {
|
||||
printk(KERN_ERR
|
||||
"ram_console: failed to allocate buffer for old log\n");
|
||||
ram_console_old_log_size = 0;
|
||||
|
||||
if (persistent_ram_old_size(prz) == 0)
|
||||
return 0;
|
||||
}
|
||||
memcpy(ram_console_old_log,
|
||||
ram_console_old_log_init_buffer, ram_console_old_log_size);
|
||||
#endif
|
||||
|
||||
entry = create_proc_entry("last_kmsg", S_IFREG | S_IRUGO, NULL);
|
||||
if (!entry) {
|
||||
printk(KERN_ERR "ram_console: failed to create proc entry\n");
|
||||
kfree(ram_console_old_log);
|
||||
ram_console_old_log = NULL;
|
||||
persistent_ram_free_old(prz);
|
||||
return 0;
|
||||
}
|
||||
|
||||
entry->proc_fops = &ram_console_file_ops;
|
||||
entry->size = ram_console_old_log_size;
|
||||
entry->size = persistent_ram_old_size(prz) +
|
||||
persistent_ram_ecc_string(prz, NULL, 0) +
|
||||
bootinfo_size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ANDROID_RAM_CONSOLE_EARLY_INIT
|
||||
console_initcall(ram_console_early_init);
|
||||
#else
|
||||
postcore_initcall(ram_console_module_init);
|
||||
#endif
|
||||
late_initcall(ram_console_late_init);
|
||||
|
||||
postcore_initcall(ram_console_module_init);
|
||||
|
@ -29,9 +29,9 @@ struct timed_gpio_data {
|
||||
struct timed_output_dev dev;
|
||||
struct hrtimer timer;
|
||||
spinlock_t lock;
|
||||
unsigned gpio;
|
||||
int max_timeout;
|
||||
u8 active_low;
|
||||
unsigned gpio;
|
||||
int max_timeout;
|
||||
u8 active_low;
|
||||
};
|
||||
|
||||
static enum hrtimer_restart gpio_timer_func(struct hrtimer *timer)
|
||||
|
@ -20,13 +20,13 @@
|
||||
|
||||
struct timed_gpio {
|
||||
const char *name;
|
||||
unsigned gpio;
|
||||
unsigned gpio;
|
||||
int max_timeout;
|
||||
u8 active_low;
|
||||
u8 active_low;
|
||||
};
|
||||
|
||||
struct timed_gpio_platform_data {
|
||||
int num_gpios;
|
||||
int num_gpios;
|
||||
struct timed_gpio *gpios;
|
||||
};
|
||||
|
||||
|
@ -159,7 +159,6 @@ static void setup_packet_header(struct asus_oled_packet *packet, char flags,
|
||||
|
||||
static void enable_oled(struct asus_oled_dev *odev, uint8_t enabl)
|
||||
{
|
||||
int a;
|
||||
int retval;
|
||||
int act_len;
|
||||
struct asus_oled_packet *packet;
|
||||
@ -178,17 +177,15 @@ static void enable_oled(struct asus_oled_dev *odev, uint8_t enabl)
|
||||
else
|
||||
packet->bitmap[0] = 0xae;
|
||||
|
||||
for (a = 0; a < 1; a++) {
|
||||
retval = usb_bulk_msg(odev->udev,
|
||||
usb_sndbulkpipe(odev->udev, 2),
|
||||
packet,
|
||||
sizeof(struct asus_oled_header) + 1,
|
||||
&act_len,
|
||||
-1);
|
||||
retval = usb_bulk_msg(odev->udev,
|
||||
usb_sndbulkpipe(odev->udev, 2),
|
||||
packet,
|
||||
sizeof(struct asus_oled_header) + 1,
|
||||
&act_len,
|
||||
-1);
|
||||
|
||||
if (retval)
|
||||
dev_dbg(&odev->udev->dev, "retval = %d\n", retval);
|
||||
}
|
||||
if (retval)
|
||||
dev_dbg(&odev->udev->dev, "retval = %d\n", retval);
|
||||
|
||||
odev->enabled = enabl;
|
||||
|
||||
|
@ -728,14 +728,10 @@ static long bcm_char_ioctl(struct file *filp, UINT cmd, ULONG arg)
|
||||
if (IoBuffer.InputLength > MAX_CNTL_PKT_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
pvBuffer = kmalloc(IoBuffer.InputLength, GFP_KERNEL);
|
||||
if (!pvBuffer)
|
||||
return -ENOMEM;
|
||||
|
||||
if (copy_from_user(pvBuffer, IoBuffer.InputBuffer, IoBuffer.InputLength)) {
|
||||
kfree(pvBuffer);
|
||||
return -EFAULT;
|
||||
}
|
||||
pvBuffer = memdup_user(IoBuffer.InputBuffer,
|
||||
IoBuffer.InputLength);
|
||||
if (IS_ERR(pvBuffer))
|
||||
return PTR_ERR(pvBuffer);
|
||||
|
||||
down(&Adapter->LowPowerModeSync);
|
||||
Status = wait_event_interruptible_timeout(Adapter->lowpower_mode_wait_queue,
|
||||
@ -1140,15 +1136,10 @@ cntrlEnd:
|
||||
if (IoBuffer.InputLength < sizeof(ULONG) * 2)
|
||||
return -EINVAL;
|
||||
|
||||
pvBuffer = kmalloc(IoBuffer.InputLength, GFP_KERNEL);
|
||||
if (!pvBuffer)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Get WrmBuffer structure */
|
||||
if (copy_from_user(pvBuffer, IoBuffer.InputBuffer, IoBuffer.InputLength)) {
|
||||
kfree(pvBuffer);
|
||||
return -EFAULT;
|
||||
}
|
||||
pvBuffer = memdup_user(IoBuffer.InputBuffer,
|
||||
IoBuffer.InputLength);
|
||||
if (IS_ERR(pvBuffer))
|
||||
return PTR_ERR(pvBuffer);
|
||||
|
||||
pBulkBuffer = (PBULKWRM_BUFFER)pvBuffer;
|
||||
|
||||
@ -1302,20 +1293,18 @@ cntrlEnd:
|
||||
/*
|
||||
* Deny the access if the offset crosses the cal area limit.
|
||||
*/
|
||||
if (stNVMReadWrite.uiNumBytes > Adapter->uiNVMDSDSize)
|
||||
return STATUS_FAILURE;
|
||||
|
||||
if ((stNVMReadWrite.uiOffset + stNVMReadWrite.uiNumBytes) > Adapter->uiNVMDSDSize) {
|
||||
if (stNVMReadWrite.uiOffset > Adapter->uiNVMDSDSize - stNVMReadWrite.uiNumBytes) {
|
||||
/* BCM_DEBUG_PRINT(Adapter,DBG_TYPE_PRINTK, 0, 0,"Can't allow access beyond NVM Size: 0x%x 0x%x\n", stNVMReadWrite.uiOffset, stNVMReadWrite.uiNumBytes); */
|
||||
return STATUS_FAILURE;
|
||||
}
|
||||
|
||||
pReadData = kzalloc(stNVMReadWrite.uiNumBytes, GFP_KERNEL);
|
||||
if (!pReadData)
|
||||
return -ENOMEM;
|
||||
|
||||
if (copy_from_user(pReadData, stNVMReadWrite.pBuffer, stNVMReadWrite.uiNumBytes)) {
|
||||
kfree(pReadData);
|
||||
return -EFAULT;
|
||||
}
|
||||
pReadData = memdup_user(stNVMReadWrite.pBuffer,
|
||||
stNVMReadWrite.uiNumBytes);
|
||||
if (IS_ERR(pReadData))
|
||||
return PTR_ERR(pReadData);
|
||||
|
||||
do_gettimeofday(&tv0);
|
||||
if (IOCTL_BCM_NVM_READ == cmd) {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -4,11 +4,11 @@
|
||||
/*************************TYPE DEF**********************/
|
||||
#define NUM_OF_LEDS 4
|
||||
|
||||
#define DSD_START_OFFSET 0x0200
|
||||
#define EEPROM_VERSION_OFFSET 0x020E
|
||||
#define EEPROM_HW_PARAM_POINTER_ADDRESS 0x0218
|
||||
#define EEPROM_HW_PARAM_POINTER_ADDRRES_MAP5 0x0220
|
||||
#define GPIO_SECTION_START_OFFSET 0x03
|
||||
#define DSD_START_OFFSET 0x0200
|
||||
#define EEPROM_VERSION_OFFSET 0x020E
|
||||
#define EEPROM_HW_PARAM_POINTER_ADDRESS 0x0218
|
||||
#define EEPROM_HW_PARAM_POINTER_ADDRRES_MAP5 0x0220
|
||||
#define GPIO_SECTION_START_OFFSET 0x03
|
||||
|
||||
#define COMPATIBILITY_SECTION_LENGTH 42
|
||||
#define COMPATIBILITY_SECTION_LENGTH_MAP5 84
|
||||
@ -18,27 +18,27 @@
|
||||
#define EEPROM_MAP5_MINORVERSION 0
|
||||
|
||||
|
||||
#define MAX_NUM_OF_BLINKS 10
|
||||
#define NUM_OF_GPIO_PINS 16
|
||||
#define MAX_NUM_OF_BLINKS 10
|
||||
#define NUM_OF_GPIO_PINS 16
|
||||
|
||||
#define DISABLE_GPIO_NUM 0xFF
|
||||
#define EVENT_SIGNALED 1
|
||||
#define DISABLE_GPIO_NUM 0xFF
|
||||
#define EVENT_SIGNALED 1
|
||||
|
||||
#define MAX_FILE_NAME_BUFFER_SIZE 100
|
||||
#define MAX_FILE_NAME_BUFFER_SIZE 100
|
||||
|
||||
#define TURN_ON_LED(GPIO, index) do{ \
|
||||
#define TURN_ON_LED(GPIO, index) do { \
|
||||
UINT gpio_val = GPIO; \
|
||||
(Adapter->LEDInfo.LEDState[index].BitPolarity == 1) ? \
|
||||
wrmaltWithLock(Adapter,BCM_GPIO_OUTPUT_SET_REG, &gpio_val ,sizeof(gpio_val)) : \
|
||||
wrmaltWithLock(Adapter,BCM_GPIO_OUTPUT_CLR_REG, &gpio_val, sizeof(gpio_val)); \
|
||||
}while(0);
|
||||
wrmaltWithLock(Adapter, BCM_GPIO_OUTPUT_SET_REG, &gpio_val, sizeof(gpio_val)) : \
|
||||
wrmaltWithLock(Adapter, BCM_GPIO_OUTPUT_CLR_REG, &gpio_val, sizeof(gpio_val)); \
|
||||
} while (0);
|
||||
|
||||
#define TURN_OFF_LED(GPIO, index) do { \
|
||||
UINT gpio_val = GPIO; \
|
||||
(Adapter->LEDInfo.LEDState[index].BitPolarity == 1) ? \
|
||||
wrmaltWithLock(Adapter,BCM_GPIO_OUTPUT_CLR_REG,&gpio_val ,sizeof(gpio_val)) : \
|
||||
wrmaltWithLock(Adapter,BCM_GPIO_OUTPUT_SET_REG,&gpio_val ,sizeof(gpio_val)); \
|
||||
}while(0);
|
||||
wrmaltWithLock(Adapter, BCM_GPIO_OUTPUT_CLR_REG, &gpio_val, sizeof(gpio_val)) : \
|
||||
wrmaltWithLock(Adapter, BCM_GPIO_OUTPUT_SET_REG, &gpio_val, sizeof(gpio_val)); \
|
||||
} while (0);
|
||||
|
||||
#define B_ULONG32 unsigned long
|
||||
|
||||
@ -50,7 +50,7 @@ typedef enum _LEDColors{
|
||||
BLUE_LED = 2,
|
||||
YELLOW_LED = 3,
|
||||
GREEN_LED = 4
|
||||
} LEDColors; /*Enumerated values of different LED types*/
|
||||
} LEDColors; /*Enumerated values of different LED types*/
|
||||
|
||||
typedef enum LedEvents {
|
||||
SHUTDOWN_EXIT = 0x00,
|
||||
@ -62,43 +62,39 @@ typedef enum LedEvents {
|
||||
LOWPOWER_MODE_ENTER = 0x20,
|
||||
IDLEMODE_CONTINUE = 0x40,
|
||||
IDLEMODE_EXIT = 0x80,
|
||||
LED_THREAD_INACTIVE = 0x100, //Makes the LED thread Inactivce. It wil be equivallent to putting the thread on hold.
|
||||
LED_THREAD_ACTIVE = 0x200 //Makes the LED Thread Active back.
|
||||
} LedEventInfo_t; /*Enumerated values of different driver states*/
|
||||
LED_THREAD_INACTIVE = 0x100, /* Makes the LED thread Inactivce. It wil be equivallent to putting the thread on hold. */
|
||||
LED_THREAD_ACTIVE = 0x200, /* Makes the LED Thread Active back. */
|
||||
DRIVER_HALT = 0xff
|
||||
} LedEventInfo_t; /* Enumerated values of different driver states */
|
||||
|
||||
#define DRIVER_HALT 0xff
|
||||
|
||||
|
||||
/*Structure which stores the information of different LED types
|
||||
* and corresponding LED state information of driver states*/
|
||||
typedef struct LedStateInfo_t
|
||||
{
|
||||
/*
|
||||
* Structure which stores the information of different LED types
|
||||
* and corresponding LED state information of driver states
|
||||
*/
|
||||
typedef struct LedStateInfo_t {
|
||||
UCHAR LED_Type; /* specify GPIO number - use 0xFF if not used */
|
||||
UCHAR LED_On_State; /* Bits set or reset for different states */
|
||||
UCHAR LED_Blink_State; /* Bits set or reset for blinking LEDs for different states */
|
||||
UCHAR GPIO_Num;
|
||||
UCHAR BitPolarity; /*To represent whether H/W is normal polarity or reverse
|
||||
polarity*/
|
||||
}LEDStateInfo, *pLEDStateInfo;
|
||||
UCHAR BitPolarity; /* To represent whether H/W is normal polarity or reverse polarity */
|
||||
} LEDStateInfo, *pLEDStateInfo;
|
||||
|
||||
|
||||
typedef struct _LED_INFO_STRUCT
|
||||
{
|
||||
typedef struct _LED_INFO_STRUCT {
|
||||
LEDStateInfo LEDState[NUM_OF_LEDS];
|
||||
BOOLEAN bIdleMode_tx_from_host; /*Variable to notify whether driver came out
|
||||
from idlemode due to Host or target*/
|
||||
BOOLEAN bIdleMode_tx_from_host; /* Variable to notify whether driver came out from idlemode due to Host or target*/
|
||||
BOOLEAN bIdle_led_off;
|
||||
wait_queue_head_t notify_led_event;
|
||||
wait_queue_head_t idleModeSyncEvent;
|
||||
struct task_struct *led_cntrl_threadid;
|
||||
int led_thread_running;
|
||||
struct task_struct *led_cntrl_threadid;
|
||||
int led_thread_running;
|
||||
BOOLEAN bLedInitDone;
|
||||
|
||||
} LED_INFO_STRUCT, *PLED_INFO_STRUCT;
|
||||
//LED Thread state.
|
||||
#define BCM_LED_THREAD_DISABLED 0 //LED Thread is not running.
|
||||
#define BCM_LED_THREAD_RUNNING_ACTIVELY 1 //LED thread is running.
|
||||
#define BCM_LED_THREAD_RUNNING_INACTIVELY 2 //LED thread has been put on hold
|
||||
/* LED Thread state. */
|
||||
#define BCM_LED_THREAD_DISABLED 0 /* LED Thread is not running. */
|
||||
#define BCM_LED_THREAD_RUNNING_ACTIVELY 1 /* LED thread is running. */
|
||||
#define BCM_LED_THREAD_RUNNING_INACTIVELY 2 /*LED thread has been put on hold*/
|
||||
|
||||
|
||||
|
||||
|
@ -765,8 +765,9 @@ config COMEDI_ADV_PCI_DIO
|
||||
default N
|
||||
---help---
|
||||
Enable support for Advantech PCI DIO cards
|
||||
PCI-1730, PCI-1733, PCI-1734, PCI-1736UP, PCI-1750, PCI-1751,
|
||||
PCI-1752, PCI-1753/E, PCI-1754, PCI-1756 and PCI-1762
|
||||
PCI-1730, PCI-1733, PCI-1734, PCI-1735U, PCI-1736UP, PCI-1739U,
|
||||
PCI-1750, PCI-1751, PCI-1752, PCI-1753/E, PCI-1754, PCI-1756,
|
||||
PCI-1760 and PCI-1762
|
||||
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called adv_pci_dio.
|
||||
|
@ -8,16 +8,16 @@
|
||||
/*
|
||||
Driver: adv_pci_dio
|
||||
Description: Advantech PCI-1730, PCI-1733, PCI-1734, PCI-1735U,
|
||||
PCI-1736UP, PCI-1750, PCI-1751, PCI-1752, PCI-1753/E,
|
||||
PCI-1754, PCI-1756, PCI-1762
|
||||
PCI-1736UP, PCI-1739U, PCI-1750, PCI-1751, PCI-1752,
|
||||
PCI-1753/E, PCI-1754, PCI-1756, PCI-1760, PCI-1762
|
||||
Author: Michal Dobes <dobes@tesnet.cz>
|
||||
Devices: [Advantech] PCI-1730 (adv_pci_dio), PCI-1733,
|
||||
PCI-1734, PCI-1735U, PCI-1736UP, PCI-1750,
|
||||
PCI-1734, PCI-1735U, PCI-1736UP, PCI-1739U, PCI-1750,
|
||||
PCI-1751, PCI-1752, PCI-1753,
|
||||
PCI-1753+PCI-1753E, PCI-1754, PCI-1756,
|
||||
PCI-1760, PCI-1762
|
||||
Status: untested
|
||||
Updated: Tue, 04 May 2010 13:00:00 +0000
|
||||
Updated: Mon, 09 Jan 2012 12:40:46 +0000
|
||||
|
||||
This driver supports now only insn interface for DI/DO/DIO.
|
||||
|
||||
@ -51,6 +51,7 @@ Configuration options:
|
||||
/* hardware types of the cards */
|
||||
enum hw_cards_id {
|
||||
TYPE_PCI1730, TYPE_PCI1733, TYPE_PCI1734, TYPE_PCI1735, TYPE_PCI1736,
|
||||
TYPE_PCI1739,
|
||||
TYPE_PCI1750,
|
||||
TYPE_PCI1751,
|
||||
TYPE_PCI1752,
|
||||
@ -109,6 +110,12 @@ enum hw_io_access {
|
||||
#define PCI1736_BOARDID 4 /* R: Board I/D switch for 1736UP */
|
||||
#define PCI1736_MAINREG 0 /* Normal register (2) doesn't work */
|
||||
|
||||
/* Advantech PCI-1739U */
|
||||
#define PCI1739_DIO 0 /* R/W: begin of 8255 registers block */
|
||||
#define PCI1739_ICR 32 /* W: Interrupt control register */
|
||||
#define PCI1739_ISR 32 /* R: Interrupt status register */
|
||||
#define PCI1739_BOARDID 8 /* R: Board I/D switch for 1739U */
|
||||
|
||||
/* Advantech PCI-1750 */
|
||||
#define PCI1750_IDI 0 /* R: Isolated digital input 0-15 */
|
||||
#define PCI1750_IDO 0 /* W: Isolated digital output 0-15 */
|
||||
@ -262,6 +269,7 @@ static DEFINE_PCI_DEVICE_TABLE(pci_dio_pci_table) = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_ADVANTECH, 0x1734) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_ADVANTECH, 0x1735) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_ADVANTECH, 0x1736) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_ADVANTECH, 0x1739) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_ADVANTECH, 0x1750) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_ADVANTECH, 0x1751) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_ADVANTECH, 0x1752) },
|
||||
@ -316,6 +324,14 @@ static const struct dio_boardtype boardtypes[] = {
|
||||
{4, PCI1736_BOARDID, 1, SDF_INTERNAL},
|
||||
{ {0, 0, 0, 0} },
|
||||
IO_8b},
|
||||
{"pci1739", PCI_VENDOR_ID_ADVANTECH, 0x1739, PCIDIO_MAINREG,
|
||||
TYPE_PCI1739,
|
||||
{ {0, 0, 0, 0}, {0, 0, 0, 0} },
|
||||
{ {0, 0, 0, 0}, {0, 0, 0, 0} },
|
||||
{ {48, PCI1739_DIO, 2, 0}, {0, 0, 0, 0} },
|
||||
{0, 0, 0, 0},
|
||||
{ {0, 0, 0, 0} },
|
||||
IO_8b},
|
||||
{"pci1750", PCI_VENDOR_ID_ADVANTECH, 0x1750, PCIDIO_MAINREG,
|
||||
TYPE_PCI1750,
|
||||
{ {0, 0, 0, 0}, {16, PCI1750_IDI, 2, 0} },
|
||||
@ -883,6 +899,11 @@ static int pci_dio_reset(struct comedi_device *dev)
|
||||
outb(0, dev->iobase + PCI1736_3_INT_RF);
|
||||
break;
|
||||
|
||||
case TYPE_PCI1739:
|
||||
/* disable & clear interrupts */
|
||||
outb(0x88, dev->iobase + PCI1739_ICR);
|
||||
break;
|
||||
|
||||
case TYPE_PCI1750:
|
||||
case TYPE_PCI1751:
|
||||
/* disable & clear interrupts */
|
||||
|
@ -720,12 +720,20 @@ static int dt2801_dio_insn_config(struct comedi_device *dev,
|
||||
which = 1;
|
||||
|
||||
/* configure */
|
||||
if (data[0]) {
|
||||
switch (data[0]) {
|
||||
case INSN_CONFIG_DIO_OUTPUT:
|
||||
s->io_bits = 0xff;
|
||||
dt2801_writecmd(dev, DT_C_SET_DIGOUT);
|
||||
} else {
|
||||
break;
|
||||
case INSN_CONFIG_DIO_INPUT:
|
||||
s->io_bits = 0;
|
||||
dt2801_writecmd(dev, DT_C_SET_DIGIN);
|
||||
break;
|
||||
case INSN_CONFIG_DIO_QUERY:
|
||||
data[1] = s->io_bits ? COMEDI_OUTPUT : COMEDI_INPUT;
|
||||
return insn->n;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
dt2801_writedata(dev, which);
|
||||
|
||||
|
@ -527,7 +527,7 @@ static void dt9812_configure_gain(struct usb_dt9812 *dev,
|
||||
* 11x -> Gain = 0.5
|
||||
*/
|
||||
case DT9812_GAIN_0PT5:
|
||||
rmw->or_value = F020_MASK_ADC0CF_AMP0GN2 ||
|
||||
rmw->or_value = F020_MASK_ADC0CF_AMP0GN2 |
|
||||
F020_MASK_ADC0CF_AMP0GN1;
|
||||
break;
|
||||
case DT9812_GAIN_1:
|
||||
@ -540,7 +540,7 @@ static void dt9812_configure_gain(struct usb_dt9812 *dev,
|
||||
rmw->or_value = F020_MASK_ADC0CF_AMP0GN1;
|
||||
break;
|
||||
case DT9812_GAIN_8:
|
||||
rmw->or_value = F020_MASK_ADC0CF_AMP0GN1 ||
|
||||
rmw->or_value = F020_MASK_ADC0CF_AMP0GN1 |
|
||||
F020_MASK_ADC0CF_AMP0GN0;
|
||||
break;
|
||||
case DT9812_GAIN_16:
|
||||
|
@ -2098,23 +2098,29 @@ static int me4000_dio_insn_config(struct comedi_device *dev,
|
||||
|
||||
CALL_PDEBUG("In me4000_dio_insn_config()\n");
|
||||
|
||||
if (data[0] == INSN_CONFIG_DIO_QUERY) {
|
||||
switch (data[0]) {
|
||||
default:
|
||||
return -EINVAL;
|
||||
case INSN_CONFIG_DIO_QUERY:
|
||||
data[1] =
|
||||
(s->io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
|
||||
return insn->n;
|
||||
case INSN_CONFIG_DIO_INPUT:
|
||||
case INSN_CONFIG_DIO_OUTPUT:
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* The input or output configuration of each digital line is
|
||||
* configured by a special insn_config instruction. chanspec
|
||||
* contains the channel to be changed, and data[0] contains the
|
||||
* value COMEDI_INPUT or COMEDI_OUTPUT.
|
||||
* value INSN_CONFIG_DIO_INPUT or INSN_CONFIG_DIO_OUTPUT.
|
||||
* On the ME-4000 it is only possible to switch port wise (8 bit)
|
||||
*/
|
||||
|
||||
tmp = me4000_inl(dev, info->dio_context.ctrl_reg);
|
||||
|
||||
if (data[0] == COMEDI_OUTPUT) {
|
||||
if (data[0] == INSN_CONFIG_DIO_OUTPUT) {
|
||||
if (chan < 8) {
|
||||
s->io_bits |= 0xFF;
|
||||
tmp &= ~(ME4000_DIO_CTRL_BIT_MODE_0 |
|
||||
|
@ -30,7 +30,7 @@ Status: works
|
||||
Devices: [National Instruments] PCI-DIO-32HS (ni_pcidio), PXI-6533,
|
||||
PCI-DIO-96, PCI-DIO-96B, PXI-6508, PCI-6503, PCI-6503B, PCI-6503X,
|
||||
PXI-6503, PCI-6533, PCI-6534
|
||||
Updated: Sun, 21 Apr 2002 21:03:38 -0700
|
||||
Updated: Mon, 09 Jan 2012 14:27:23 +0000
|
||||
|
||||
The DIO-96 appears as four 8255 subdevices. See the 8255
|
||||
driver notes for details.
|
||||
@ -42,6 +42,11 @@ supports simple digital I/O; no handshaking is supported.
|
||||
|
||||
DMA mostly works for the PCI-DIO32HS, but only in timed input mode.
|
||||
|
||||
The PCI-DIO-32HS/PCI-6533 has a configurable external trigger. Setting
|
||||
scan_begin_arg to 0 or CR_EDGE triggers on the leading edge. Setting
|
||||
scan_begin_arg to CR_INVERT or (CR_EDGE | CR_INVERT) triggers on the
|
||||
trailing edge.
|
||||
|
||||
This driver could be easily modified to support AT-MIO32HS and
|
||||
AT-MIO96.
|
||||
|
||||
@ -436,6 +441,7 @@ static int ni_pcidio_request_di_mite_channel(struct comedi_device *dev)
|
||||
comedi_error(dev, "failed to reserve mite dma channel.");
|
||||
return -EBUSY;
|
||||
}
|
||||
devpriv->di_mite_chan->dir = COMEDI_INPUT;
|
||||
writeb(primary_DMAChannel_bits(devpriv->di_mite_chan->channel) |
|
||||
secondary_DMAChannel_bits(devpriv->di_mite_chan->channel),
|
||||
devpriv->mite->daq_io_addr + DMA_Line_Control_Group1);
|
||||
@ -482,6 +488,21 @@ void ni_pcidio_event(struct comedi_device *dev, struct comedi_subdevice *s)
|
||||
comedi_event(dev, s);
|
||||
}
|
||||
|
||||
static int ni_pcidio_poll(struct comedi_device *dev, struct comedi_subdevice *s)
|
||||
{
|
||||
unsigned long irq_flags;
|
||||
int count;
|
||||
|
||||
spin_lock_irqsave(&dev->spinlock, irq_flags);
|
||||
spin_lock(&devpriv->mite_channel_lock);
|
||||
if (devpriv->di_mite_chan)
|
||||
mite_sync_input_dma(devpriv->di_mite_chan, s->async);
|
||||
spin_unlock(&devpriv->mite_channel_lock);
|
||||
count = s->async->buf_write_count - s->async->buf_read_count;
|
||||
spin_unlock_irqrestore(&dev->spinlock, irq_flags);
|
||||
return count;
|
||||
}
|
||||
|
||||
static irqreturn_t nidio_interrupt(int irq, void *d)
|
||||
{
|
||||
struct comedi_device *dev = d;
|
||||
@ -497,7 +518,6 @@ static irqreturn_t nidio_interrupt(int irq, void *d)
|
||||
int status;
|
||||
int work = 0;
|
||||
unsigned int m_status = 0;
|
||||
unsigned long irq_flags;
|
||||
|
||||
/* interrupcions parasites */
|
||||
if (dev->attached == 0) {
|
||||
@ -505,6 +525,9 @@ static irqreturn_t nidio_interrupt(int irq, void *d)
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
/* Lock to avoid race with comedi_poll */
|
||||
spin_lock(&dev->spinlock);
|
||||
|
||||
status = readb(devpriv->mite->daq_io_addr +
|
||||
Interrupt_And_Window_Status);
|
||||
flags = readb(devpriv->mite->daq_io_addr + Group_1_Flags);
|
||||
@ -518,7 +541,7 @@ static irqreturn_t nidio_interrupt(int irq, void *d)
|
||||
/* printk("buf[4096]=%08x\n",
|
||||
*(unsigned int *)(async->prealloc_buf+4096)); */
|
||||
|
||||
spin_lock_irqsave(&devpriv->mite_channel_lock, irq_flags);
|
||||
spin_lock(&devpriv->mite_channel_lock);
|
||||
if (devpriv->di_mite_chan)
|
||||
m_status = mite_get_status(devpriv->di_mite_chan);
|
||||
#ifdef MITE_DEBUG
|
||||
@ -543,7 +566,7 @@ static irqreturn_t nidio_interrupt(int irq, void *d)
|
||||
disable_irq(dev->irq);
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&devpriv->mite_channel_lock, irq_flags);
|
||||
spin_unlock(&devpriv->mite_channel_lock);
|
||||
|
||||
while (status & DataLeft) {
|
||||
work++;
|
||||
@ -645,6 +668,8 @@ out:
|
||||
Master_DMA_And_Interrupt_Control);
|
||||
}
|
||||
#endif
|
||||
|
||||
spin_unlock(&dev->spinlock);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
@ -825,8 +850,8 @@ static int ni_pcidio_cmdtest(struct comedi_device *dev,
|
||||
} else {
|
||||
/* TRIG_EXT */
|
||||
/* should be level/edge, hi/lo specification here */
|
||||
if (cmd->scan_begin_arg != 0) {
|
||||
cmd->scan_begin_arg = 0;
|
||||
if ((cmd->scan_begin_arg & ~(CR_EDGE | CR_INVERT)) != 0) {
|
||||
cmd->scan_begin_arg &= (CR_EDGE | CR_INVERT);
|
||||
err++;
|
||||
}
|
||||
}
|
||||
@ -941,7 +966,13 @@ static int ni_pcidio_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
|
||||
writeb(0, devpriv->mite->daq_io_addr + Sequence);
|
||||
writeb(0x00, devpriv->mite->daq_io_addr + ReqReg);
|
||||
writeb(4, devpriv->mite->daq_io_addr + BlockMode);
|
||||
writeb(0, devpriv->mite->daq_io_addr + LinePolarities);
|
||||
if (!(cmd->scan_begin_arg & CR_INVERT)) {
|
||||
/* Leading Edge pulse mode */
|
||||
writeb(0, devpriv->mite->daq_io_addr + LinePolarities);
|
||||
} else {
|
||||
/* Trailing Edge pulse mode */
|
||||
writeb(2, devpriv->mite->daq_io_addr + LinePolarities);
|
||||
}
|
||||
writeb(0x00, devpriv->mite->daq_io_addr + AckSer);
|
||||
writel(1, devpriv->mite->daq_io_addr + StartDelay);
|
||||
writeb(1, devpriv->mite->daq_io_addr + ReqDelay);
|
||||
@ -1005,17 +1036,24 @@ static int ni_pcidio_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
|
||||
static int setup_mite_dma(struct comedi_device *dev, struct comedi_subdevice *s)
|
||||
{
|
||||
int retval;
|
||||
unsigned long flags;
|
||||
|
||||
retval = ni_pcidio_request_di_mite_channel(dev);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
devpriv->di_mite_chan->dir = COMEDI_INPUT;
|
||||
/* write alloc the entire buffer */
|
||||
comedi_buf_write_alloc(s->async, s->async->prealloc_bufsz);
|
||||
|
||||
mite_prep_dma(devpriv->di_mite_chan, 32, 32);
|
||||
spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
|
||||
if (devpriv->di_mite_chan) {
|
||||
mite_prep_dma(devpriv->di_mite_chan, 32, 32);
|
||||
mite_dma_arm(devpriv->di_mite_chan);
|
||||
} else
|
||||
retval = -EIO;
|
||||
spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
|
||||
|
||||
mite_dma_arm(devpriv->di_mite_chan);
|
||||
return 0;
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int ni_pcidio_inttrig(struct comedi_device *dev,
|
||||
@ -1244,6 +1282,7 @@ static int nidio_attach(struct comedi_device *dev, struct comedi_devconfig *it)
|
||||
s->len_chanlist = 32; /* XXX */
|
||||
s->buf_change = &ni_pcidio_change;
|
||||
s->async_dma_dir = DMA_BIDIRECTIONAL;
|
||||
s->poll = &ni_pcidio_poll;
|
||||
|
||||
writel(0, devpriv->mite->daq_io_addr + Port_IO(0));
|
||||
writel(0, devpriv->mite->daq_io_addr + Port_Pin_Directions(0));
|
||||
|
@ -29,14 +29,15 @@ Devices: [National Instruments] PCI-MIO-16XE-50 (ni_pcimio),
|
||||
PCI-MIO-16XE-10, PXI-6030E, PCI-MIO-16E-1, PCI-MIO-16E-4, PCI-6014, PCI-6040E,
|
||||
PXI-6040E, PCI-6030E, PCI-6031E, PCI-6032E, PCI-6033E, PCI-6071E, PCI-6023E,
|
||||
PCI-6024E, PCI-6025E, PXI-6025E, PCI-6034E, PCI-6035E, PCI-6052E,
|
||||
PCI-6110, PCI-6111, PCI-6220, PCI-6221, PCI-6224, PXI-6224, PCI-6225, PXI-6225,
|
||||
PCI-6229, PCI-6250, PCI-6251, PCIe-6251, PCI-6254, PCI-6259, PCIe-6259,
|
||||
PCI-6110, PCI-6111, PCI-6220, PCI-6221, PCI-6224, PXI-6224,
|
||||
PCI-6225, PXI-6225, PCI-6229, PCI-6250, PCI-6251, PCIe-6251, PXIe-6251,
|
||||
PCI-6254, PCI-6259, PCIe-6259,
|
||||
PCI-6280, PCI-6281, PXI-6281, PCI-6284, PCI-6289,
|
||||
PCI-6711, PXI-6711, PCI-6713, PXI-6713,
|
||||
PXI-6071E, PCI-6070E, PXI-6070E,
|
||||
PXI-6052E, PCI-6036E, PCI-6731, PCI-6733, PXI-6733,
|
||||
PCI-6143, PXI-6143
|
||||
Updated: Wed, 03 Dec 2008 10:51:47 +0000
|
||||
Updated: Mon, 09 Jan 2012 14:52:48 +0000
|
||||
|
||||
These boards are almost identical to the AT-MIO E series, except that
|
||||
they use the PCI bus instead of ISA (i.e., AT). See the notes for
|
||||
@ -182,6 +183,7 @@ static DEFINE_PCI_DEVICE_TABLE(ni_pci_table) = {
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_NI, 0x717f)},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_NI, 0x71bc)},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_NI, 0x717d)},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_NI, 0x72e8)},
|
||||
{0}
|
||||
};
|
||||
|
||||
@ -1045,6 +1047,25 @@ static const struct ni_board_struct ni_boards[] = {
|
||||
.caldac = {caldac_none},
|
||||
.has_8255 = 0,
|
||||
},
|
||||
{
|
||||
.device_id = 0x72e8,
|
||||
.name = "pxie-6251",
|
||||
.n_adchan = 16,
|
||||
.adbits = 16,
|
||||
.ai_fifo_depth = 4095,
|
||||
.gainlkup = ai_gain_628x,
|
||||
.ai_speed = 800,
|
||||
.n_aochan = 2,
|
||||
.aobits = 16,
|
||||
.ao_fifo_depth = 8191,
|
||||
.ao_range_table = &range_ni_M_625x_ao,
|
||||
.reg_type = ni_reg_625x,
|
||||
.ao_unipolar = 0,
|
||||
.ao_speed = 357,
|
||||
.num_p0_dio_channels = 8,
|
||||
.caldac = {caldac_none},
|
||||
.has_8255 = 0,
|
||||
},
|
||||
{
|
||||
.device_id = 0x70b7,
|
||||
.name = "pci-6254",
|
||||
|
@ -306,7 +306,7 @@ static int __unioxx5_subdev_init(struct comedi_subdevice *subdev,
|
||||
usp = kzalloc(sizeof(*usp), GFP_KERNEL);
|
||||
|
||||
if (usp == NULL) {
|
||||
printk(KERN_ERR "comedi%d: erorr! --> out of memory!\n", minor);
|
||||
printk(KERN_ERR "comedi%d: error! --> out of memory!\n", minor);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -39,7 +39,7 @@ Status: testing
|
||||
*
|
||||
*
|
||||
* Revision history:
|
||||
* 0.1: inital version
|
||||
* 0.1: initial version
|
||||
* 0.2: all basic functions implemented, digital I/O only for one port
|
||||
* 0.3: proper vendor ID and driver name
|
||||
* 0.4: fixed D/A voltage range
|
||||
@ -235,16 +235,16 @@ struct usbduxsub {
|
||||
short int ao_cmd_running;
|
||||
/* pwm is running */
|
||||
short int pwm_cmd_running;
|
||||
/* continous aquisition */
|
||||
short int ai_continous;
|
||||
short int ao_continous;
|
||||
/* continuous acquisition */
|
||||
short int ai_continuous;
|
||||
short int ao_continuous;
|
||||
/* number of samples to acquire */
|
||||
int ai_sample_count;
|
||||
int ao_sample_count;
|
||||
/* time between samples in units of the timer */
|
||||
unsigned int ai_timer;
|
||||
unsigned int ao_timer;
|
||||
/* counter between aquisitions */
|
||||
/* counter between acquisitions */
|
||||
unsigned int ai_counter;
|
||||
unsigned int ao_counter;
|
||||
/* interval in frames/uframes */
|
||||
@ -455,8 +455,8 @@ static void usbduxsub_ai_IsocIrq(struct urb *urb)
|
||||
this_usbduxsub->ai_counter = this_usbduxsub->ai_timer;
|
||||
|
||||
/* test, if we transmit only a fixed number of samples */
|
||||
if (!(this_usbduxsub->ai_continous)) {
|
||||
/* not continous, fixed number of samples */
|
||||
if (!(this_usbduxsub->ai_continuous)) {
|
||||
/* not continuous, fixed number of samples */
|
||||
this_usbduxsub->ai_sample_count--;
|
||||
/* all samples received? */
|
||||
if (this_usbduxsub->ai_sample_count < 0) {
|
||||
@ -607,8 +607,8 @@ static void usbduxsub_ao_IsocIrq(struct urb *urb)
|
||||
/* timer zero */
|
||||
this_usbduxsub->ao_counter = this_usbduxsub->ao_timer;
|
||||
|
||||
/* handle non continous aquisition */
|
||||
if (!(this_usbduxsub->ao_continous)) {
|
||||
/* handle non continuous acquisition */
|
||||
if (!(this_usbduxsub->ao_continuous)) {
|
||||
/* fixed number of samples */
|
||||
this_usbduxsub->ao_sample_count--;
|
||||
if (this_usbduxsub->ao_sample_count < 0) {
|
||||
@ -925,7 +925,7 @@ static int usbdux_ai_cmdtest(struct comedi_device *dev,
|
||||
if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
|
||||
err++;
|
||||
|
||||
/* scanning is continous */
|
||||
/* scanning is continuous */
|
||||
tmp = cmd->convert_src;
|
||||
cmd->convert_src &= TRIG_NOW;
|
||||
if (!cmd->convert_src || tmp != cmd->convert_src)
|
||||
@ -1193,7 +1193,7 @@ static int usbdux_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
|
||||
up(&this_usbduxsub->sem);
|
||||
return -EBUSY;
|
||||
}
|
||||
/* set current channel of the running aquisition to zero */
|
||||
/* set current channel of the running acquisition to zero */
|
||||
s->async->cur_chan = 0;
|
||||
|
||||
/* first the number of channels per time step */
|
||||
@ -1261,10 +1261,10 @@ static int usbdux_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
|
||||
if (cmd->stop_src == TRIG_COUNT) {
|
||||
/* data arrives as one packet */
|
||||
this_usbduxsub->ai_sample_count = cmd->stop_arg;
|
||||
this_usbduxsub->ai_continous = 0;
|
||||
this_usbduxsub->ai_continuous = 0;
|
||||
} else {
|
||||
/* continous aquisition */
|
||||
this_usbduxsub->ai_continous = 1;
|
||||
/* continuous acquisition */
|
||||
this_usbduxsub->ai_continuous = 1;
|
||||
this_usbduxsub->ai_sample_count = 0;
|
||||
}
|
||||
|
||||
@ -1586,7 +1586,7 @@ static int usbdux_ao_cmdtest(struct comedi_device *dev,
|
||||
/* just now we scan also in the high speed mode every frame */
|
||||
/* this is due to ehci driver limitations */
|
||||
if (0) { /* (this_usbduxsub->high_speed) */
|
||||
/* start immidiately a new scan */
|
||||
/* start immediately a new scan */
|
||||
/* the sampling rate is set by the coversion rate */
|
||||
cmd->scan_begin_src &= TRIG_FOLLOW;
|
||||
} else {
|
||||
@ -1596,7 +1596,7 @@ static int usbdux_ao_cmdtest(struct comedi_device *dev,
|
||||
if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
|
||||
err++;
|
||||
|
||||
/* scanning is continous */
|
||||
/* scanning is continuous */
|
||||
tmp = cmd->convert_src;
|
||||
|
||||
/* all conversion events happen simultaneously */
|
||||
@ -1710,7 +1710,7 @@ static int usbdux_ao_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
|
||||
dev_dbg(&this_usbduxsub->interface->dev,
|
||||
"comedi%d: %s\n", dev->minor, __func__);
|
||||
|
||||
/* set current channel of the running aquisition to zero */
|
||||
/* set current channel of the running acquisition to zero */
|
||||
s->async->cur_chan = 0;
|
||||
for (i = 0; i < cmd->chanlist_len; ++i) {
|
||||
chan = CR_CHAN(cmd->chanlist[i]);
|
||||
@ -1759,7 +1759,7 @@ static int usbdux_ao_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
|
||||
this_usbduxsub->ao_counter = this_usbduxsub->ao_timer;
|
||||
|
||||
if (cmd->stop_src == TRIG_COUNT) {
|
||||
/* not continous */
|
||||
/* not continuous */
|
||||
/* counter */
|
||||
/* high speed also scans everything at once */
|
||||
if (0) { /* (this_usbduxsub->high_speed) */
|
||||
@ -1771,10 +1771,10 @@ static int usbdux_ao_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
|
||||
/* data arrives as one packet */
|
||||
this_usbduxsub->ao_sample_count = cmd->stop_arg;
|
||||
}
|
||||
this_usbduxsub->ao_continous = 0;
|
||||
this_usbduxsub->ao_continuous = 0;
|
||||
} else {
|
||||
/* continous aquisition */
|
||||
this_usbduxsub->ao_continous = 1;
|
||||
/* continuous acquisition */
|
||||
this_usbduxsub->ao_continuous = 1;
|
||||
this_usbduxsub->ao_sample_count = 0;
|
||||
}
|
||||
|
||||
|
@ -48,8 +48,7 @@
|
||||
|
||||
#endif
|
||||
|
||||
#include "bc_dts_defs.h"
|
||||
#include "bcm_70012_regs.h" /* Link Register defs */
|
||||
#include "crystalhd.h"
|
||||
|
||||
#define CRYSTALHD_API_NAME "crystalhd"
|
||||
#define CRYSTALHD_API_DEV_NAME "/dev/crystalhd"
|
||||
|
@ -1,40 +0,0 @@
|
||||
/********************************************************************
|
||||
* Copyright(c) 2006-2009 Broadcom Corporation.
|
||||
*
|
||||
* Name: bc_dts_types.h
|
||||
*
|
||||
* Description: Data types
|
||||
*
|
||||
* AU
|
||||
*
|
||||
* HISTORY:
|
||||
*
|
||||
********************************************************************
|
||||
* This header is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published
|
||||
* by the Free Software Foundation, either version 2.1 of the License.
|
||||
*
|
||||
* This header 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 Lesser General Public License for more details.
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this header. If not, see <http://www.gnu.org/licenses/>.
|
||||
*******************************************************************/
|
||||
|
||||
#ifndef _BC_DTS_TYPES_H_
|
||||
#define _BC_DTS_TYPES_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifndef TRUE
|
||||
#define TRUE 1
|
||||
#endif
|
||||
|
||||
#ifndef FALSE
|
||||
#define FALSE 0
|
||||
#endif
|
||||
|
||||
#define TEXT
|
||||
|
||||
#endif
|
14
drivers/staging/crystalhd/crystalhd.h
Normal file
14
drivers/staging/crystalhd/crystalhd.h
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef _CRYSTALHD_H_
|
||||
#define _CRYSTALHD_H_
|
||||
|
||||
#include <asm/system.h>
|
||||
#include "bc_dts_defs.h"
|
||||
#include "crystalhd_misc.h"
|
||||
#include "bc_dts_glob_lnx.h"
|
||||
#include "crystalhd_hw.h"
|
||||
#include "crystalhd_cmds.h"
|
||||
#include "crystalhd_lnx.h"
|
||||
#include "bcm_70012_regs.h"
|
||||
#include "crystalhd_fw_if.h"
|
||||
|
||||
#endif
|
@ -24,8 +24,7 @@
|
||||
* along with this driver. If not, see <http://www.gnu.org/licenses/>.
|
||||
**********************************************************************/
|
||||
|
||||
#include "crystalhd_cmds.h"
|
||||
#include "crystalhd_hw.h"
|
||||
#include "crystalhd.h"
|
||||
|
||||
static struct crystalhd_user *bc_cproc_get_uid(struct crystalhd_cmd *ctx)
|
||||
{
|
||||
|
@ -33,8 +33,8 @@
|
||||
* from _dts_glob and dts_defs etc.. which are defined for
|
||||
* windows.
|
||||
*/
|
||||
#include "crystalhd_misc.h"
|
||||
#include "crystalhd_hw.h"
|
||||
|
||||
#include "crystalhd.h"
|
||||
|
||||
enum crystalhd_state {
|
||||
BC_LINK_INVALID = 0x00,
|
||||
|
@ -22,10 +22,11 @@
|
||||
* along with this driver. If not, see <http://www.gnu.org/licenses/>.
|
||||
**********************************************************************/
|
||||
|
||||
#include "crystalhd.h"
|
||||
|
||||
#include <linux/pci.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/delay.h>
|
||||
#include "crystalhd_hw.h"
|
||||
|
||||
/* Functions internal to this file */
|
||||
|
||||
@ -766,7 +767,7 @@ static enum BC_STATUS crystalhd_hw_fill_desc(struct crystalhd_dio_req *ioreq,
|
||||
crystalhd_hw_dump_desc(desc, last_desc_ix, 1);
|
||||
|
||||
if (count != xfr_sz) {
|
||||
BCMLOG_ERR("interal error sz curr:%x exp:%x\n", count, xfr_sz);
|
||||
BCMLOG_ERR("internal error sz curr:%x exp:%x\n", count, xfr_sz);
|
||||
return BC_STS_ERROR;
|
||||
}
|
||||
|
||||
@ -868,8 +869,7 @@ static enum BC_STATUS crystalhd_stop_tx_dma_engine(struct crystalhd_hw *hw)
|
||||
|
||||
BCMLOG(BCMLOG_DBG, "Stopping TX DMA Engine..\n");
|
||||
|
||||
/* FIXME: jarod: invert dma_ctrl and check bit? or are there missing parens? */
|
||||
if (!dma_cntrl & DMA_START_BIT) {
|
||||
if (!(dma_cntrl & DMA_START_BIT)) {
|
||||
BCMLOG(BCMLOG_DBG, "Already Stopped\n");
|
||||
return BC_STS_SUCCESS;
|
||||
}
|
||||
@ -1628,7 +1628,6 @@ enum BC_STATUS crystalhd_download_fw(struct crystalhd_adp *adp, void *buffer, ui
|
||||
uint32_t fw_sig_len = 36;
|
||||
uint32_t dram_offset = BC_FWIMG_ST_ADDR, sig_reg;
|
||||
|
||||
BCMLOG_ENTER;
|
||||
|
||||
if (!adp || !buffer || !sz) {
|
||||
BCMLOG_ERR("Invalid Params.\n");
|
||||
@ -1725,8 +1724,6 @@ enum BC_STATUS crystalhd_do_fw_cmd(struct crystalhd_hw *hw,
|
||||
|
||||
crystalhd_create_event(&fw_cmd_event);
|
||||
|
||||
BCMLOG_ENTER;
|
||||
|
||||
if (!hw || !fw_cmd) {
|
||||
BCMLOG_ERR("Invalid Arguments\n");
|
||||
return BC_STS_INV_ARG;
|
||||
|
@ -27,8 +27,7 @@
|
||||
#ifndef _CRYSTALHD_HW_H_
|
||||
#define _CRYSTALHD_HW_H_
|
||||
|
||||
#include "crystalhd_misc.h"
|
||||
#include "crystalhd_fw_if.h"
|
||||
#include "crystalhd.h"
|
||||
|
||||
/* HW constants..*/
|
||||
#define DMA_ENGINE_CNT 2
|
||||
|
@ -15,10 +15,11 @@
|
||||
along with this driver. If not, see <http://www.gnu.org/licenses/>.
|
||||
***************************************************************************/
|
||||
|
||||
#include "crystalhd.h"
|
||||
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "crystalhd_lnx.h"
|
||||
|
||||
static DEFINE_MUTEX(chd_dec_mutex);
|
||||
static struct class *crystalhd_class;
|
||||
@ -298,7 +299,6 @@ static int chd_dec_open(struct inode *in, struct file *fd)
|
||||
enum BC_STATUS sts = BC_STS_SUCCESS;
|
||||
struct crystalhd_user *uc = NULL;
|
||||
|
||||
BCMLOG_ENTER;
|
||||
if (!adp) {
|
||||
BCMLOG_ERR("Invalid adp\n");
|
||||
return -EINVAL;
|
||||
@ -327,7 +327,6 @@ static int chd_dec_close(struct inode *in, struct file *fd)
|
||||
struct crystalhd_adp *adp = chd_get_adp();
|
||||
struct crystalhd_user *uc;
|
||||
|
||||
BCMLOG_ENTER;
|
||||
if (!adp) {
|
||||
BCMLOG_ERR("Invalid adp\n");
|
||||
return -EINVAL;
|
||||
@ -513,8 +512,6 @@ static void __devexit chd_dec_pci_remove(struct pci_dev *pdev)
|
||||
struct crystalhd_adp *pinfo;
|
||||
enum BC_STATUS sts = BC_STS_SUCCESS;
|
||||
|
||||
BCMLOG_ENTER;
|
||||
|
||||
pinfo = pci_get_drvdata(pdev);
|
||||
if (!pinfo) {
|
||||
BCMLOG_ERR("could not get adp\n");
|
||||
|
@ -1,7 +1,7 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2005-2009, Broadcom Corporation.
|
||||
*
|
||||
* Name: crystalhd_lnx . c
|
||||
* Name: crystalhd_lnx . h
|
||||
*
|
||||
* Description:
|
||||
* BCM70012 Linux driver
|
||||
@ -48,11 +48,10 @@
|
||||
#include <asm/system.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include "crystalhd_cmds.h"
|
||||
#include "crystalhd.h"
|
||||
|
||||
#define CRYSTAL_HD_NAME "Broadcom Crystal HD Decoder (BCM70012) Driver"
|
||||
|
||||
|
||||
/* OS specific PCI information structure and adapter information. */
|
||||
struct crystalhd_adp {
|
||||
/* Hardware borad/PCI specifics */
|
||||
|
@ -24,10 +24,9 @@
|
||||
* along with this driver. If not, see <http://www.gnu.org/licenses/>.
|
||||
**********************************************************************/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include "crystalhd.h"
|
||||
|
||||
#include "crystalhd_misc.h"
|
||||
#include "crystalhd_lnx.h"
|
||||
#include <linux/slab.h>
|
||||
|
||||
uint32_t g_linklog_level;
|
||||
|
||||
|
@ -28,6 +28,8 @@
|
||||
#ifndef _CRYSTALHD_MISC_H_
|
||||
#define _CRYSTALHD_MISC_H_
|
||||
|
||||
#include "crystalhd.h"
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
@ -35,8 +37,6 @@
|
||||
#include <linux/ioctl.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/sched.h>
|
||||
#include <asm/system.h>
|
||||
#include "bc_dts_glob_lnx.h"
|
||||
|
||||
/* Global log level variable defined in crystal_misc.c file */
|
||||
extern uint32_t g_linklog_level;
|
||||
@ -200,29 +200,21 @@ enum _chd_log_levels {
|
||||
BCMLOG_INFO = 0x00000001, /* Generic informational */
|
||||
BCMLOG_DBG = 0x00000002, /* First level Debug info */
|
||||
BCMLOG_SSTEP = 0x00000004, /* Stepping information */
|
||||
BCMLOG_ENTER_LEAVE = 0x00000008, /* stack tracking */
|
||||
};
|
||||
|
||||
#define BCMLOG_ENTER \
|
||||
if (g_linklog_level & BCMLOG_ENTER_LEAVE) { \
|
||||
printk(KERN_DEBUG "Entered %s\n", __func__); \
|
||||
}
|
||||
|
||||
#define BCMLOG_LEAVE \
|
||||
if (g_linklog_level & BCMLOG_ENTER_LEAVE) { \
|
||||
printk(KERN_DEBUG "Leaving %s\n", __func__); \
|
||||
}
|
||||
#define BCMLOG(trace, fmt, args...) \
|
||||
do { \
|
||||
if (g_linklog_level & trace) \
|
||||
printk(fmt, ##args); \
|
||||
} while (0)
|
||||
|
||||
#define BCMLOG(trace, fmt, args...) \
|
||||
if (g_linklog_level & trace) { \
|
||||
printk(fmt, ##args); \
|
||||
}
|
||||
|
||||
#define BCMLOG_ERR(fmt, args...) \
|
||||
do { \
|
||||
if (g_linklog_level & BCMLOG_ERROR) { \
|
||||
printk(KERN_ERR "*ERR*:%s:%d: "fmt, __FILE__, __LINE__, ##args); \
|
||||
} \
|
||||
} while (0);
|
||||
#define BCMLOG_ERR(fmt, args...) \
|
||||
do { \
|
||||
if (g_linklog_level & BCMLOG_ERROR) \
|
||||
printk(KERN_ERR "*ERR*:%s:%d: "fmt, \
|
||||
__FILE__, __LINE__, ##args); \
|
||||
} while (0)
|
||||
|
||||
#endif
|
||||
|
@ -11,6 +11,6 @@ TODO:
|
||||
- Use of kmem_cache seems a bit unusual
|
||||
|
||||
Please send patches to:
|
||||
Greg Kroah-Hartman <gregkh@suse.de>
|
||||
Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
||||
Mark Einon <mark.einon@gmail.com>
|
||||
|
||||
|
@ -802,7 +802,7 @@ static int et131x_init_eeprom(struct et131x_adapter *adapter)
|
||||
/* THIS IS A WORKAROUND:
|
||||
* I need to call this function twice to get my card in a
|
||||
* LG M1 Express Dual running. I tried also a msleep before this
|
||||
* function, because I thougth there could be some time condidions
|
||||
* function, because I thought there could be some time condidions
|
||||
* but it didn't work. Call the whole function twice also work.
|
||||
*/
|
||||
if (pci_read_config_byte(pdev, ET1310_PCI_EEPROM_STATUS, &eestatus)) {
|
||||
@ -987,7 +987,7 @@ static void et1310_config_mac_regs1(struct et131x_adapter *adapter)
|
||||
writel(station1, ¯egs->station_addr_1);
|
||||
writel(station2, ¯egs->station_addr_2);
|
||||
|
||||
/* Max ethernet packet in bytes that will passed by the mac without
|
||||
/* Max ethernet packet in bytes that will be passed by the mac without
|
||||
* being truncated. Allow the MAC to pass 4 more than our max packet
|
||||
* size. This is 4 for the Ethernet CRC.
|
||||
*
|
||||
@ -3109,7 +3109,7 @@ static struct rfd *nic_rx_pkts(struct et131x_adapter *adapter)
|
||||
skb->protocol = eth_type_trans(skb, adapter->netdev);
|
||||
skb->ip_summed = CHECKSUM_NONE;
|
||||
|
||||
netif_rx(skb);
|
||||
netif_rx_ni(skb);
|
||||
} else {
|
||||
rfd->len = 0;
|
||||
}
|
||||
@ -4413,7 +4413,7 @@ static void et131x_up(struct net_device *netdev)
|
||||
|
||||
/**
|
||||
* et131x_down - Bring down the device
|
||||
* @netdev: device to be broght down
|
||||
* @netdev: device to be brought down
|
||||
*/
|
||||
static void et131x_down(struct net_device *netdev)
|
||||
{
|
||||
@ -5177,7 +5177,7 @@ static int et131x_set_mac_addr(struct net_device *netdev, void *new_mac)
|
||||
|
||||
/* Make sure the requested MAC is valid */
|
||||
if (!is_valid_ether_addr(address->sa_data))
|
||||
return -EINVAL;
|
||||
return -EADDRNOTAVAIL;
|
||||
|
||||
et131x_disable_txrx(netdev);
|
||||
et131x_handle_send_interrupt(adapter);
|
||||
|
@ -596,7 +596,7 @@ struct rxdma_regs { /* Location: */
|
||||
* structure for tx test reg in txmac address map
|
||||
* located at address 0x3014
|
||||
* 31-17: unused
|
||||
* 16: reserved1
|
||||
* 16: reserved
|
||||
* 15: txtest_en
|
||||
* 14-11: unused
|
||||
* 10-0: txq test pointer
|
||||
@ -1485,7 +1485,7 @@ struct address_map {
|
||||
* 3: reserved
|
||||
* 2: ignore_10g_fr
|
||||
* 1: reserved
|
||||
* 0: preamble_supress_en
|
||||
* 0: preamble_suppress_en
|
||||
*/
|
||||
|
||||
/* MI Register 22: PHY Configuration Reg(0x16)
|
||||
|
@ -450,7 +450,7 @@ exit:
|
||||
/**
|
||||
* usb_alphatrack_poll
|
||||
*/
|
||||
static unsigned int usb_alphatrack_poll(struct file *file, poll_table * wait)
|
||||
static unsigned int usb_alphatrack_poll(struct file *file, poll_table *wait)
|
||||
{
|
||||
struct usb_alphatrack *dev;
|
||||
unsigned int mask = 0;
|
||||
|
@ -471,7 +471,7 @@ exit:
|
||||
/**
|
||||
* usb_tranzport_poll
|
||||
*/
|
||||
static unsigned int usb_tranzport_poll(struct file *file, poll_table * wait)
|
||||
static unsigned int usb_tranzport_poll(struct file *file, poll_table *wait)
|
||||
{
|
||||
struct usb_tranzport *dev;
|
||||
unsigned int mask = 0;
|
||||
|
@ -29,10 +29,10 @@
|
||||
#define FT1000_PROC "ft1000"
|
||||
#define MAX_FILE_LEN 255
|
||||
|
||||
#define PUTM_TO_PAGE(len,page,args...) \
|
||||
#define PUTM_TO_PAGE(len, page, args...) \
|
||||
len += snprintf(page+len, PAGE_SIZE - len, args)
|
||||
|
||||
#define PUTX_TO_PAGE(len,page,message,size,var) \
|
||||
#define PUTX_TO_PAGE(len, page, message, size, var) \
|
||||
len += snprintf(page+len, PAGE_SIZE - len, message); \
|
||||
for(i = 0; i < (size - 1); i++) \
|
||||
{ \
|
||||
@ -40,7 +40,7 @@
|
||||
} \
|
||||
len += snprintf(page+len, PAGE_SIZE - len, "%02x\n", var[i])
|
||||
|
||||
#define PUTD_TO_PAGE(len,page,message,size,var) \
|
||||
#define PUTD_TO_PAGE(len, page, message, size, var) \
|
||||
len += snprintf(page+len, PAGE_SIZE - len, message); \
|
||||
for(i = 0; i < (size - 1); i++) \
|
||||
{ \
|
||||
|
@ -1,5 +0,0 @@
|
||||
config HYPERV_STORAGE
|
||||
tristate "Microsoft Hyper-V virtual storage driver"
|
||||
depends on HYPERV && SCSI
|
||||
help
|
||||
Select this option to enable the Hyper-V virtual storage driver.
|
@ -1,3 +0,0 @@
|
||||
obj-$(CONFIG_HYPERV_STORAGE) += hv_storvsc.o
|
||||
|
||||
hv_storvsc-y := storvsc_drv.o
|
@ -1,5 +0,0 @@
|
||||
TODO:
|
||||
- audit the scsi driver
|
||||
|
||||
Please send patches for this code to Greg Kroah-Hartman <gregkh@suse.de>,
|
||||
Haiyang Zhang <haiyangz@microsoft.com>, and K. Y. Srinivasan <kys@microsoft.com>
|
@ -62,7 +62,7 @@ Then fill in the following:
|
||||
An optional associated buffer.
|
||||
- indio_dev->pollfunc:
|
||||
Poll function related elements. This controls what occurs when a trigger
|
||||
to which this device is attached sends and event.
|
||||
to which this device is attached sends an event.
|
||||
- indio_dev->channels:
|
||||
Specification of device channels. Most attributes etc are built
|
||||
form this spec.
|
||||
|
241
drivers/staging/iio/Documentation/iio_event_monitor.c
Normal file
241
drivers/staging/iio/Documentation/iio_event_monitor.c
Normal file
@ -0,0 +1,241 @@
|
||||
/* Industrialio event test code.
|
||||
*
|
||||
* Copyright (c) 2011-2012 Lars-Peter Clausen <lars@metafoo.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*
|
||||
* This program is primarily intended as an example application.
|
||||
* Reads the current buffer setup from sysfs and starts a short capture
|
||||
* from the specified device, pretty printing the result after appropriate
|
||||
* conversion.
|
||||
*
|
||||
* Usage:
|
||||
* iio_event_monitor <device_name>
|
||||
*
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <poll.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include "iio_utils.h"
|
||||
#include "../events.h"
|
||||
|
||||
static const char * const iio_chan_type_name_spec[] = {
|
||||
[IIO_VOLTAGE] = "voltage",
|
||||
[IIO_CURRENT] = "current",
|
||||
[IIO_POWER] = "power",
|
||||
[IIO_ACCEL] = "accel",
|
||||
[IIO_ANGL_VEL] = "anglvel",
|
||||
[IIO_MAGN] = "magn",
|
||||
[IIO_LIGHT] = "illuminance",
|
||||
[IIO_INTENSITY] = "intensity",
|
||||
[IIO_PROXIMITY] = "proximity",
|
||||
[IIO_TEMP] = "temp",
|
||||
[IIO_INCLI] = "incli",
|
||||
[IIO_ROT] = "rot",
|
||||
[IIO_ANGL] = "angl",
|
||||
[IIO_TIMESTAMP] = "timestamp",
|
||||
[IIO_CAPACITANCE] = "capacitance",
|
||||
};
|
||||
|
||||
static const char * const iio_ev_type_text[] = {
|
||||
[IIO_EV_TYPE_THRESH] = "thresh",
|
||||
[IIO_EV_TYPE_MAG] = "mag",
|
||||
[IIO_EV_TYPE_ROC] = "roc",
|
||||
[IIO_EV_TYPE_THRESH_ADAPTIVE] = "thresh_adaptive",
|
||||
[IIO_EV_TYPE_MAG_ADAPTIVE] = "mag_adaptive",
|
||||
};
|
||||
|
||||
static const char * const iio_ev_dir_text[] = {
|
||||
[IIO_EV_DIR_EITHER] = "either",
|
||||
[IIO_EV_DIR_RISING] = "rising",
|
||||
[IIO_EV_DIR_FALLING] = "falling"
|
||||
};
|
||||
|
||||
static const char * const iio_modifier_names[] = {
|
||||
[IIO_MOD_X] = "x",
|
||||
[IIO_MOD_Y] = "y",
|
||||
[IIO_MOD_Z] = "z",
|
||||
[IIO_MOD_LIGHT_BOTH] = "both",
|
||||
[IIO_MOD_LIGHT_IR] = "ir",
|
||||
};
|
||||
|
||||
static bool event_is_known(struct iio_event_data *event)
|
||||
{
|
||||
enum iio_chan_type type = IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event->id);
|
||||
enum iio_modifier mod = IIO_EVENT_CODE_EXTRACT_MODIFIER(event->id);
|
||||
enum iio_event_type ev_type = IIO_EVENT_CODE_EXTRACT_TYPE(event->id);
|
||||
enum iio_event_direction dir = IIO_EVENT_CODE_EXTRACT_DIR(event->id);
|
||||
|
||||
switch (type) {
|
||||
case IIO_VOLTAGE:
|
||||
case IIO_CURRENT:
|
||||
case IIO_POWER:
|
||||
case IIO_ACCEL:
|
||||
case IIO_ANGL_VEL:
|
||||
case IIO_MAGN:
|
||||
case IIO_LIGHT:
|
||||
case IIO_INTENSITY:
|
||||
case IIO_PROXIMITY:
|
||||
case IIO_TEMP:
|
||||
case IIO_INCLI:
|
||||
case IIO_ROT:
|
||||
case IIO_ANGL:
|
||||
case IIO_TIMESTAMP:
|
||||
case IIO_CAPACITANCE:
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (mod) {
|
||||
case IIO_NO_MOD:
|
||||
case IIO_MOD_X:
|
||||
case IIO_MOD_Y:
|
||||
case IIO_MOD_Z:
|
||||
case IIO_MOD_LIGHT_BOTH:
|
||||
case IIO_MOD_LIGHT_IR:
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (ev_type) {
|
||||
case IIO_EV_TYPE_THRESH:
|
||||
case IIO_EV_TYPE_MAG:
|
||||
case IIO_EV_TYPE_ROC:
|
||||
case IIO_EV_TYPE_THRESH_ADAPTIVE:
|
||||
case IIO_EV_TYPE_MAG_ADAPTIVE:
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (dir) {
|
||||
case IIO_EV_DIR_EITHER:
|
||||
case IIO_EV_DIR_RISING:
|
||||
case IIO_EV_DIR_FALLING:
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void print_event(struct iio_event_data *event)
|
||||
{
|
||||
enum iio_chan_type type = IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event->id);
|
||||
enum iio_modifier mod = IIO_EVENT_CODE_EXTRACT_MODIFIER(event->id);
|
||||
enum iio_event_type ev_type = IIO_EVENT_CODE_EXTRACT_TYPE(event->id);
|
||||
enum iio_event_direction dir = IIO_EVENT_CODE_EXTRACT_DIR(event->id);
|
||||
int chan = IIO_EVENT_CODE_EXTRACT_CHAN(event->id);
|
||||
int chan2 = IIO_EVENT_CODE_EXTRACT_CHAN2(event->id);
|
||||
bool diff = IIO_EVENT_CODE_EXTRACT_DIFF(event->id);
|
||||
|
||||
if (!event_is_known(event)) {
|
||||
printf("Unknown event: time: %lld, id: %llx\n",
|
||||
event->timestamp, event->id);
|
||||
return;
|
||||
}
|
||||
|
||||
printf("Event: time: %lld, ", event->timestamp);
|
||||
|
||||
if (mod != IIO_NO_MOD) {
|
||||
printf("type: %s(%s), ",
|
||||
iio_chan_type_name_spec[type],
|
||||
iio_modifier_names[mod]);
|
||||
} else {
|
||||
printf("type: %s, ",
|
||||
iio_chan_type_name_spec[type]);
|
||||
}
|
||||
|
||||
if (diff && chan >= 0 && chan2 >= 0)
|
||||
printf("channel: %d-%d, ", chan, chan2);
|
||||
else if (chan >= 0)
|
||||
printf("channel: %d, ", chan);
|
||||
|
||||
printf("evtype: %s, direction: %s\n",
|
||||
iio_ev_type_text[ev_type],
|
||||
iio_ev_dir_text[dir]);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct iio_event_data event;
|
||||
const char *device_name;
|
||||
char *chrdev_name;
|
||||
int ret;
|
||||
int dev_num;
|
||||
int fd, event_fd;
|
||||
|
||||
if (argc <= 1) {
|
||||
printf("Usage: %s <device_name>\n", argv[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
device_name = argv[1];
|
||||
|
||||
dev_num = find_type_by_name(device_name, "iio:device");
|
||||
if (dev_num >= 0) {
|
||||
printf("Found IIO device with name %s with device number %d\n",
|
||||
device_name, dev_num);
|
||||
ret = asprintf(&chrdev_name, "/dev/iio:device%d", dev_num);
|
||||
if (ret < 0) {
|
||||
ret = -ENOMEM;
|
||||
goto error_ret;
|
||||
}
|
||||
} else {
|
||||
/* If we can't find a IIO device by name assume device_name is a
|
||||
IIO chrdev */
|
||||
chrdev_name = strdup(device_name);
|
||||
}
|
||||
|
||||
fd = open(chrdev_name, 0);
|
||||
if (fd == -1) {
|
||||
fprintf(stdout, "Failed to open %s\n", chrdev_name);
|
||||
ret = -errno;
|
||||
goto error_free_chrdev_name;
|
||||
}
|
||||
|
||||
ret = ioctl(fd, IIO_GET_EVENT_FD_IOCTL, &event_fd);
|
||||
|
||||
close(fd);
|
||||
|
||||
if (ret == -1 || event_fd == -1) {
|
||||
fprintf(stdout, "Failed to retrieve event fd\n");
|
||||
ret = -errno;
|
||||
goto error_free_chrdev_name;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
ret = read(event_fd, &event, sizeof(event));
|
||||
if (ret == -1) {
|
||||
if (errno == EAGAIN) {
|
||||
printf("nothing available\n");
|
||||
continue;
|
||||
} else {
|
||||
perror("Failed to read event from device");
|
||||
ret = -errno;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
print_event(&event);
|
||||
}
|
||||
|
||||
close(event_fd);
|
||||
error_free_chrdev_name:
|
||||
free(chrdev_name);
|
||||
error_ret:
|
||||
return ret;
|
||||
}
|
58
drivers/staging/iio/Documentation/inkernel.txt
Normal file
58
drivers/staging/iio/Documentation/inkernel.txt
Normal file
@ -0,0 +1,58 @@
|
||||
Industrial I/O Subsystem in kernel consumers.
|
||||
|
||||
The IIO subsystem can act as a layer under other elements of the kernel
|
||||
providing a means of obtaining ADC type readings or of driving DAC type
|
||||
signals. The functionality supported will grow as use cases arise.
|
||||
|
||||
Describing the channel mapping (iio/machine.h)
|
||||
|
||||
Channel associations are described using:
|
||||
|
||||
struct iio_map {
|
||||
const char *adc_channel_label;
|
||||
const char *consumer_dev_name;
|
||||
const char *consumer_channel;
|
||||
};
|
||||
|
||||
adc_channel_label identifies the channel on the IIO device by being
|
||||
matched against the datasheet_name field of the iio_chan_spec.
|
||||
|
||||
consumer_dev_name allows identification of the consumer device.
|
||||
This are then used to find the channel mapping from the consumer device (see
|
||||
below).
|
||||
|
||||
Finally consumer_channel is a string identifying the channel to the consumer.
|
||||
(Perhaps 'battery_voltage' or similar).
|
||||
|
||||
An array of these structures is then passed to the IIO driver.
|
||||
|
||||
Supporting in kernel interfaces in the driver (driver.h)
|
||||
|
||||
The driver must provide datasheet_name values for its channels and
|
||||
must pass the iio_map structures and a pointer to its own iio_dev structure
|
||||
on to the core via a call to iio_map_array_register. On removal,
|
||||
iio_map_array_unregister reverses this process.
|
||||
|
||||
The result of this is that the IIO core now has all the information needed
|
||||
to associate a given channel with the consumer requesting it.
|
||||
|
||||
Acting as an IIO consumer (consumer.h)
|
||||
|
||||
The consumer first has to obtain an iio_channel structure from the core
|
||||
by calling iio_channel_get(). The correct channel is identified by:
|
||||
|
||||
* matching dev or dev_name against consumer_dev and consumer_dev_name
|
||||
* matching consumer_channel against consumer_channel in the map
|
||||
|
||||
There are then a number of functions that can be used to get information
|
||||
about this channel such as it's current reading.
|
||||
|
||||
e.g.
|
||||
iio_st_read_channel_raw() - get a reading
|
||||
iio_st_read_channel_type() - get the type of channel
|
||||
|
||||
There is also provision for retrieving all of the channels associated
|
||||
with a given consumer. This is useful for generic drivers such as
|
||||
iio_hwmon where the number and naming of channels is not known by the
|
||||
consumer driver. To do this, use iio_st_channel_get_all.
|
||||
|
@ -11,6 +11,13 @@ menuconfig IIO
|
||||
number of different physical interfaces (i2c, spi, etc). See
|
||||
drivers/staging/iio/Documentation for more information.
|
||||
if IIO
|
||||
config IIO_ST_HWMON
|
||||
tristate "Hwmon driver that uses channels specified via iio maps"
|
||||
depends on HWMON
|
||||
help
|
||||
This is a platform driver that in combination with a suitable
|
||||
map allows IIO devices to provide basic hwmon functionality
|
||||
for those channels specified in the map.
|
||||
|
||||
config IIO_BUFFER
|
||||
bool "Enable buffer support within IIO"
|
||||
@ -79,7 +86,7 @@ config IIO_SIMPLE_DUMMY
|
||||
help
|
||||
Driver intended mainly as documentation for how to write
|
||||
a driver. May also be useful for testing userspace code
|
||||
without hardward.
|
||||
without hardware.
|
||||
|
||||
if IIO_SIMPLE_DUMMY
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
#
|
||||
|
||||
obj-$(CONFIG_IIO) += industrialio.o
|
||||
industrialio-y := industrialio-core.o
|
||||
industrialio-y := industrialio-core.o industrialio-event.o inkern.o
|
||||
industrialio-$(CONFIG_IIO_BUFFER) += industrialio-buffer.o
|
||||
industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o
|
||||
|
||||
@ -17,6 +17,8 @@ iio_dummy-$(CONFIG_IIO_SIMPLE_DUMMY_BUFFER) += iio_simple_dummy_buffer.o
|
||||
|
||||
obj-$(CONFIG_IIO_DUMMY_EVGEN) += iio_dummy_evgen.o
|
||||
|
||||
obj-$(CONFIG_IIO_ST_HWMON) += iio_hwmon.o
|
||||
|
||||
obj-y += accel/
|
||||
obj-y += adc/
|
||||
obj-y += addac/
|
||||
|
@ -115,9 +115,7 @@ int adis16201_configure_ring(struct iio_dev *indio_dev)
|
||||
return ret;
|
||||
}
|
||||
indio_dev->buffer = ring;
|
||||
/* Effectively select the ring buffer implementation */
|
||||
ring->scan_timestamp = true;
|
||||
ring->access = &ring_sw_access_funcs;
|
||||
indio_dev->setup_ops = &adis16201_ring_setup_ops;
|
||||
|
||||
indio_dev->pollfunc = iio_alloc_pollfunc(&iio_pollfunc_store_time,
|
||||
|
@ -117,9 +117,7 @@ int adis16203_configure_ring(struct iio_dev *indio_dev)
|
||||
return ret;
|
||||
}
|
||||
indio_dev->buffer = ring;
|
||||
/* Effectively select the ring buffer implementation */
|
||||
ring->scan_timestamp = true;
|
||||
ring->access = &ring_sw_access_funcs;
|
||||
indio_dev->setup_ops = &adis16203_ring_setup_ops;
|
||||
|
||||
indio_dev->pollfunc = iio_alloc_pollfunc(&iio_pollfunc_store_time,
|
||||
|
@ -112,8 +112,6 @@ int adis16204_configure_ring(struct iio_dev *indio_dev)
|
||||
return ret;
|
||||
}
|
||||
indio_dev->buffer = ring;
|
||||
/* Effectively select the ring buffer implementation */
|
||||
ring->access = &ring_sw_access_funcs;
|
||||
ring->scan_timestamp = true;
|
||||
indio_dev->setup_ops = &adis16204_ring_setup_ops;
|
||||
|
||||
|
@ -113,8 +113,6 @@ int adis16209_configure_ring(struct iio_dev *indio_dev)
|
||||
return ret;
|
||||
}
|
||||
indio_dev->buffer = ring;
|
||||
/* Effectively select the ring buffer implementation */
|
||||
ring->access = &ring_sw_access_funcs;
|
||||
ring->scan_timestamp = true;
|
||||
indio_dev->setup_ops = &adis16209_ring_setup_ops;
|
||||
|
||||
|
@ -110,8 +110,6 @@ int adis16240_configure_ring(struct iio_dev *indio_dev)
|
||||
return ret;
|
||||
}
|
||||
indio_dev->buffer = ring;
|
||||
/* Effectively select the ring buffer implementation */
|
||||
ring->access = &ring_sw_access_funcs;
|
||||
ring->scan_timestamp = true;
|
||||
indio_dev->setup_ops = &adis16240_ring_setup_ops;
|
||||
|
||||
|
@ -187,12 +187,10 @@ void lis3l02dq_unconfigure_buffer(struct iio_dev *indio_dev);
|
||||
#ifdef CONFIG_LIS3L02DQ_BUF_RING_SW
|
||||
#define lis3l02dq_free_buf iio_sw_rb_free
|
||||
#define lis3l02dq_alloc_buf iio_sw_rb_allocate
|
||||
#define lis3l02dq_access_funcs ring_sw_access_funcs
|
||||
#endif
|
||||
#ifdef CONFIG_LIS3L02DQ_BUF_KFIFO
|
||||
#define lis3l02dq_free_buf iio_kfifo_free
|
||||
#define lis3l02dq_alloc_buf iio_kfifo_allocate
|
||||
#define lis3l02dq_access_funcs kfifo_access_funcs
|
||||
#endif
|
||||
irqreturn_t lis3l02dq_data_rdy_trig_poll(int irq, void *private);
|
||||
#define lis3l02dq_th lis3l02dq_data_rdy_trig_poll
|
||||
|
@ -239,7 +239,7 @@ static int lis3l02dq_data_rdy_trigger_set_state(struct iio_trigger *trig,
|
||||
__lis3l02dq_write_data_ready_config(&indio_dev->dev, state);
|
||||
if (state == false) {
|
||||
/*
|
||||
* A possible quirk with teh handler is currently worked around
|
||||
* A possible quirk with the handler is currently worked around
|
||||
* by ensuring outstanding read events are cleared.
|
||||
*/
|
||||
ret = lis3l02dq_read_all(indio_dev, NULL);
|
||||
@ -406,8 +406,6 @@ int lis3l02dq_configure_buffer(struct iio_dev *indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
indio_dev->buffer = buffer;
|
||||
/* Effectively select the buffer implementation */
|
||||
indio_dev->buffer->access = &lis3l02dq_access_funcs;
|
||||
|
||||
buffer->scan_timestamp = true;
|
||||
indio_dev->setup_ops = &lis3l02dq_buffer_setup_ops;
|
||||
|
@ -136,7 +136,7 @@
|
||||
#define SCA3000_INT_MASK_ACTIVE_HIGH 0x01
|
||||
#define SCA3000_INT_MASK_ACTIVE_LOW 0x00
|
||||
|
||||
/* Values of mulipexed registers (write to ctrl_data after select) */
|
||||
/* Values of multiplexed registers (write to ctrl_data after select) */
|
||||
#define SCA3000_REG_ADDR_CTRL_DATA 0x22
|
||||
|
||||
/* Measurement modes available on some sca3000 series chips. Code assumes others
|
||||
|
@ -193,4 +193,13 @@ config MAX1363_RING_BUFFER
|
||||
Say yes here to include ring buffer support in the MAX1363
|
||||
ADC driver.
|
||||
|
||||
config LPC32XX_ADC
|
||||
tristate "NXP LPC32XX ADC"
|
||||
depends on ARCH_LPC32XX && !TOUCHSCREEN_LPC32XX
|
||||
help
|
||||
Say yes here to build support for the integrated ADC inside the
|
||||
LPC32XX SoC. Note that this feature uses the same hardware as the
|
||||
touchscreen driver, so you can only select one of the two drivers
|
||||
(lpc32xx_adc or lpc32xx_ts). Provides direct access via sysfs.
|
||||
|
||||
endmenu
|
||||
|
@ -37,3 +37,4 @@ obj-$(CONFIG_AD7192) += ad7192.o
|
||||
obj-$(CONFIG_ADT7310) += adt7310.o
|
||||
obj-$(CONFIG_ADT7410) += adt7410.o
|
||||
obj-$(CONFIG_AD7280) += ad7280a.o
|
||||
obj-$(CONFIG_LPC32XX_ADC) += lpc32xx_adc.o
|
||||
|
@ -561,8 +561,6 @@ static int ad7192_register_ring_funcs_and_init(struct iio_dev *indio_dev)
|
||||
ret = -ENOMEM;
|
||||
goto error_ret;
|
||||
}
|
||||
/* Effectively select the ring buffer implementation */
|
||||
indio_dev->buffer->access = &ring_sw_access_funcs;
|
||||
indio_dev->pollfunc = iio_alloc_pollfunc(&iio_pollfunc_store_time,
|
||||
&ad7192_trigger_handler,
|
||||
IRQF_ONESHOT,
|
||||
@ -824,25 +822,20 @@ static struct attribute *ad7192_attributes[] = {
|
||||
NULL
|
||||
};
|
||||
|
||||
static umode_t ad7192_attr_is_visible(struct kobject *kobj,
|
||||
struct attribute *attr, int n)
|
||||
{
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct ad7192_state *st = iio_priv(indio_dev);
|
||||
|
||||
umode_t mode = attr->mode;
|
||||
|
||||
if ((st->devid != ID_AD7195) &&
|
||||
(attr == &iio_dev_attr_ac_excitation_en.dev_attr.attr))
|
||||
mode = 0;
|
||||
|
||||
return mode;
|
||||
}
|
||||
|
||||
static const struct attribute_group ad7192_attribute_group = {
|
||||
.attrs = ad7192_attributes,
|
||||
.is_visible = ad7192_attr_is_visible,
|
||||
};
|
||||
|
||||
static struct attribute *ad7195_attributes[] = {
|
||||
&iio_dev_attr_sampling_frequency.dev_attr.attr,
|
||||
&iio_dev_attr_in_v_m_v_scale_available.dev_attr.attr,
|
||||
&iio_dev_attr_in_voltage_scale_available.dev_attr.attr,
|
||||
&iio_dev_attr_bridge_switch_en.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group ad7195_attribute_group = {
|
||||
.attrs = ad7195_attributes,
|
||||
};
|
||||
|
||||
static int ad7192_read_raw(struct iio_dev *indio_dev,
|
||||
@ -972,6 +965,15 @@ static const struct iio_info ad7192_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const struct iio_info ad7195_info = {
|
||||
.read_raw = &ad7192_read_raw,
|
||||
.write_raw = &ad7192_write_raw,
|
||||
.write_raw_get_fmt = &ad7192_write_raw_get_fmt,
|
||||
.attrs = &ad7195_attribute_group,
|
||||
.validate_trigger = ad7192_validate_trigger,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
#define AD7192_CHAN_DIFF(_chan, _chan2, _name, _address, _si) \
|
||||
{ .type = IIO_VOLTAGE, \
|
||||
.differential = 1, \
|
||||
@ -1064,7 +1066,10 @@ static int __devinit ad7192_probe(struct spi_device *spi)
|
||||
indio_dev->channels = ad7192_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(ad7192_channels);
|
||||
indio_dev->available_scan_masks = st->available_scan_masks;
|
||||
indio_dev->info = &ad7192_info;
|
||||
if (st->devid == ID_AD7195)
|
||||
indio_dev->info = &ad7195_info;
|
||||
else
|
||||
indio_dev->info = &ad7192_info;
|
||||
|
||||
for (i = 0; i < indio_dev->num_channels; i++)
|
||||
st->available_scan_masks[i] = (1 << i) | (1 <<
|
||||
|
@ -321,7 +321,7 @@ static int ad7291_read_event_value(struct iio_dev *indio_dev,
|
||||
|
||||
switch (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code)) {
|
||||
case IIO_VOLTAGE:
|
||||
reg = ad7291_limit_regs[IIO_EVENT_CODE_EXTRACT_NUM(event_code)]
|
||||
reg = ad7291_limit_regs[IIO_EVENT_CODE_EXTRACT_CHAN(event_code)]
|
||||
[!(IIO_EVENT_CODE_EXTRACT_DIR(event_code) ==
|
||||
IIO_EV_DIR_RISING)];
|
||||
|
||||
@ -359,7 +359,7 @@ static int ad7291_write_event_value(struct iio_dev *indio_dev,
|
||||
case IIO_VOLTAGE:
|
||||
if (val > AD7291_VALUE_MASK || val < 0)
|
||||
return -EINVAL;
|
||||
reg = ad7291_limit_regs[IIO_EVENT_CODE_EXTRACT_NUM(event_code)]
|
||||
reg = ad7291_limit_regs[IIO_EVENT_CODE_EXTRACT_CHAN(event_code)]
|
||||
[!(IIO_EVENT_CODE_EXTRACT_DIR(event_code) ==
|
||||
IIO_EV_DIR_RISING)];
|
||||
return ad7291_i2c_write(chip, reg, val);
|
||||
@ -386,7 +386,7 @@ static int ad7291_read_event_config(struct iio_dev *indio_dev,
|
||||
switch (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code)) {
|
||||
case IIO_VOLTAGE:
|
||||
if (chip->c_mask &
|
||||
(1 << (15 - IIO_EVENT_CODE_EXTRACT_NUM(event_code))))
|
||||
(1 << (15 - IIO_EVENT_CODE_EXTRACT_CHAN(event_code))))
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
@ -418,12 +418,12 @@ static int ad7291_write_event_config(struct iio_dev *indio_dev,
|
||||
switch (IIO_EVENT_CODE_EXTRACT_TYPE(event_code)) {
|
||||
case IIO_VOLTAGE:
|
||||
if ((!state) && (chip->c_mask & (1 << (15 -
|
||||
IIO_EVENT_CODE_EXTRACT_NUM(event_code)))))
|
||||
chip->c_mask &= ~(1 << (15 - IIO_EVENT_CODE_EXTRACT_NUM
|
||||
IIO_EVENT_CODE_EXTRACT_CHAN(event_code)))))
|
||||
chip->c_mask &= ~(1 << (15 - IIO_EVENT_CODE_EXTRACT_CHAN
|
||||
(event_code)));
|
||||
else if (state && (!(chip->c_mask & (1 << (15 -
|
||||
IIO_EVENT_CODE_EXTRACT_NUM(event_code))))))
|
||||
chip->c_mask |= (1 << (15 - IIO_EVENT_CODE_EXTRACT_NUM
|
||||
IIO_EVENT_CODE_EXTRACT_CHAN(event_code))))))
|
||||
chip->c_mask |= (1 << (15 - IIO_EVENT_CODE_EXTRACT_CHAN
|
||||
(event_code)));
|
||||
else
|
||||
break;
|
||||
|
@ -131,9 +131,6 @@ int ad7298_register_ring_funcs_and_init(struct iio_dev *indio_dev)
|
||||
ret = -ENOMEM;
|
||||
goto error_ret;
|
||||
}
|
||||
/* Effectively select the ring buffer implementation */
|
||||
indio_dev->buffer->access = &ring_sw_access_funcs;
|
||||
|
||||
indio_dev->pollfunc = iio_alloc_pollfunc(NULL,
|
||||
&ad7298_trigger_handler,
|
||||
IRQF_ONESHOT,
|
||||
|
@ -23,7 +23,7 @@
|
||||
/**
|
||||
* ad7476_ring_preenable() setup the parameters of the ring before enabling
|
||||
*
|
||||
* The complex nature of the setting of the nuber of bytes per datum is due
|
||||
* The complex nature of the setting of the number of bytes per datum is due
|
||||
* to this driver currently ensuring that the timestamp is stored at an 8
|
||||
* byte boundary.
|
||||
**/
|
||||
@ -98,8 +98,6 @@ int ad7476_register_ring_funcs_and_init(struct iio_dev *indio_dev)
|
||||
ret = -ENOMEM;
|
||||
goto error_ret;
|
||||
}
|
||||
/* Effectively select the ring buffer implementation */
|
||||
indio_dev->buffer->access = &ring_sw_access_funcs;
|
||||
indio_dev->pollfunc
|
||||
= iio_alloc_pollfunc(NULL,
|
||||
&ad7476_trigger_handler,
|
||||
|
@ -197,7 +197,7 @@ static IIO_DEVICE_ATTR(oversampling_ratio, S_IRUGO | S_IWUSR,
|
||||
ad7606_store_oversampling_ratio, 0);
|
||||
static IIO_CONST_ATTR(oversampling_ratio_available, "0 2 4 8 16 32 64");
|
||||
|
||||
static struct attribute *ad7606_attributes[] = {
|
||||
static struct attribute *ad7606_attributes_os_and_range[] = {
|
||||
&iio_dev_attr_in_voltage_range.dev_attr.attr,
|
||||
&iio_const_attr_in_voltage_range_available.dev_attr.attr,
|
||||
&iio_dev_attr_oversampling_ratio.dev_attr.attr,
|
||||
@ -205,34 +205,28 @@ static struct attribute *ad7606_attributes[] = {
|
||||
NULL,
|
||||
};
|
||||
|
||||
static umode_t ad7606_attr_is_visible(struct kobject *kobj,
|
||||
struct attribute *attr, int n)
|
||||
{
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct ad7606_state *st = iio_priv(indio_dev);
|
||||
static const struct attribute_group ad7606_attribute_group_os_and_range = {
|
||||
.attrs = ad7606_attributes_os_and_range,
|
||||
};
|
||||
|
||||
umode_t mode = attr->mode;
|
||||
static struct attribute *ad7606_attributes_os[] = {
|
||||
&iio_dev_attr_oversampling_ratio.dev_attr.attr,
|
||||
&iio_const_attr_oversampling_ratio_available.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
if (!(gpio_is_valid(st->pdata->gpio_os0) &&
|
||||
gpio_is_valid(st->pdata->gpio_os1) &&
|
||||
gpio_is_valid(st->pdata->gpio_os2)) &&
|
||||
(attr == &iio_dev_attr_oversampling_ratio.dev_attr.attr ||
|
||||
attr ==
|
||||
&iio_const_attr_oversampling_ratio_available.dev_attr.attr))
|
||||
mode = 0;
|
||||
else if (!gpio_is_valid(st->pdata->gpio_range) &&
|
||||
(attr == &iio_dev_attr_in_voltage_range.dev_attr.attr ||
|
||||
attr ==
|
||||
&iio_const_attr_in_voltage_range_available.dev_attr.attr))
|
||||
mode = 0;
|
||||
static const struct attribute_group ad7606_attribute_group_os = {
|
||||
.attrs = ad7606_attributes_os,
|
||||
};
|
||||
|
||||
return mode;
|
||||
}
|
||||
static struct attribute *ad7606_attributes_range[] = {
|
||||
&iio_dev_attr_in_voltage_range.dev_attr.attr,
|
||||
&iio_const_attr_in_voltage_range_available.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group ad7606_attribute_group = {
|
||||
.attrs = ad7606_attributes,
|
||||
.is_visible = ad7606_attr_is_visible,
|
||||
static const struct attribute_group ad7606_attribute_group_range = {
|
||||
.attrs = ad7606_attributes_range,
|
||||
};
|
||||
|
||||
#define AD7606_CHANNEL(num) \
|
||||
@ -435,10 +429,27 @@ static irqreturn_t ad7606_interrupt(int irq, void *dev_id)
|
||||
return IRQ_HANDLED;
|
||||
};
|
||||
|
||||
static const struct iio_info ad7606_info = {
|
||||
static const struct iio_info ad7606_info_no_os_or_range = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = &ad7606_read_raw,
|
||||
.attrs = &ad7606_attribute_group,
|
||||
};
|
||||
|
||||
static const struct iio_info ad7606_info_os_and_range = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = &ad7606_read_raw,
|
||||
.attrs = &ad7606_attribute_group_os_and_range,
|
||||
};
|
||||
|
||||
static const struct iio_info ad7606_info_os = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = &ad7606_read_raw,
|
||||
.attrs = &ad7606_attribute_group_os,
|
||||
};
|
||||
|
||||
static const struct iio_info ad7606_info_range = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = &ad7606_read_raw,
|
||||
.attrs = &ad7606_attribute_group_range,
|
||||
};
|
||||
|
||||
struct iio_dev *ad7606_probe(struct device *dev, int irq,
|
||||
@ -483,7 +494,19 @@ struct iio_dev *ad7606_probe(struct device *dev, int irq,
|
||||
st->chip_info = &ad7606_chip_info_tbl[id];
|
||||
|
||||
indio_dev->dev.parent = dev;
|
||||
indio_dev->info = &ad7606_info;
|
||||
if (gpio_is_valid(st->pdata->gpio_os0) &&
|
||||
gpio_is_valid(st->pdata->gpio_os1) &&
|
||||
gpio_is_valid(st->pdata->gpio_os2)) {
|
||||
if (gpio_is_valid(st->pdata->gpio_range))
|
||||
indio_dev->info = &ad7606_info_os_and_range;
|
||||
else
|
||||
indio_dev->info = &ad7606_info_os;
|
||||
} else {
|
||||
if (gpio_is_valid(st->pdata->gpio_range))
|
||||
indio_dev->info = &ad7606_info_range;
|
||||
else
|
||||
indio_dev->info = &ad7606_info_no_os_or_range;
|
||||
}
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->name = st->chip_info->name;
|
||||
indio_dev->channels = st->chip_info->channels;
|
||||
|
@ -173,18 +173,7 @@ static struct platform_driver ad7606_driver = {
|
||||
},
|
||||
};
|
||||
|
||||
static int __init ad7606_init(void)
|
||||
{
|
||||
return platform_driver_register(&ad7606_driver);
|
||||
}
|
||||
|
||||
static void __exit ad7606_cleanup(void)
|
||||
{
|
||||
platform_driver_unregister(&ad7606_driver);
|
||||
}
|
||||
|
||||
module_init(ad7606_init);
|
||||
module_exit(ad7606_cleanup);
|
||||
module_platform_driver(ad7606_driver);
|
||||
|
||||
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD7606 ADC");
|
||||
|
@ -110,8 +110,6 @@ int ad7606_register_ring_funcs_and_init(struct iio_dev *indio_dev)
|
||||
goto error_ret;
|
||||
}
|
||||
|
||||
/* Effectively select the ring buffer implementation */
|
||||
indio_dev->buffer->access = &ring_sw_access_funcs;
|
||||
indio_dev->pollfunc = iio_alloc_pollfunc(&ad7606_trigger_handler_th_bh,
|
||||
&ad7606_trigger_handler_th_bh,
|
||||
0,
|
||||
|
@ -427,8 +427,6 @@ static int ad7793_register_ring_funcs_and_init(struct iio_dev *indio_dev)
|
||||
ret = -ENOMEM;
|
||||
goto error_ret;
|
||||
}
|
||||
/* Effectively select the ring buffer implementation */
|
||||
indio_dev->buffer->access = &ring_sw_access_funcs;
|
||||
indio_dev->pollfunc = iio_alloc_pollfunc(&iio_pollfunc_store_time,
|
||||
&ad7793_trigger_handler,
|
||||
IRQF_ONESHOT,
|
||||
|
@ -131,8 +131,6 @@ int ad7887_register_ring_funcs_and_init(struct iio_dev *indio_dev)
|
||||
ret = -ENOMEM;
|
||||
goto error_ret;
|
||||
}
|
||||
/* Effectively select the ring buffer implementation */
|
||||
indio_dev->buffer->access = &ring_sw_access_funcs;
|
||||
indio_dev->pollfunc = iio_alloc_pollfunc(&iio_pollfunc_store_time,
|
||||
&ad7887_trigger_handler,
|
||||
IRQF_ONESHOT,
|
||||
|
@ -256,7 +256,7 @@ static int ad799x_write_event_value(struct iio_dev *indio_dev,
|
||||
struct ad799x_state *st = iio_priv(indio_dev);
|
||||
int direction = !!(IIO_EVENT_CODE_EXTRACT_DIR(event_code) ==
|
||||
IIO_EV_DIR_FALLING);
|
||||
int number = IIO_EVENT_CODE_EXTRACT_NUM(event_code);
|
||||
int number = IIO_EVENT_CODE_EXTRACT_CHAN(event_code);
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
ret = ad799x_i2c_write16(st,
|
||||
@ -275,7 +275,7 @@ static int ad799x_read_event_value(struct iio_dev *indio_dev,
|
||||
struct ad799x_state *st = iio_priv(indio_dev);
|
||||
int direction = !!(IIO_EVENT_CODE_EXTRACT_DIR(event_code) ==
|
||||
IIO_EV_DIR_FALLING);
|
||||
int number = IIO_EVENT_CODE_EXTRACT_NUM(event_code);
|
||||
int number = IIO_EVENT_CODE_EXTRACT_CHAN(event_code);
|
||||
u16 valin;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
|
@ -26,7 +26,7 @@
|
||||
/**
|
||||
* ad799x_ring_preenable() setup the parameters of the ring before enabling
|
||||
*
|
||||
* The complex nature of the setting of the nuber of bytes per datum is due
|
||||
* The complex nature of the setting of the number of bytes per datum is due
|
||||
* to this driver currently ensuring that the timestamp is stored at an 8
|
||||
* byte boundary.
|
||||
**/
|
||||
@ -141,8 +141,6 @@ int ad799x_register_ring_funcs_and_init(struct iio_dev *indio_dev)
|
||||
ret = -ENOMEM;
|
||||
goto error_ret;
|
||||
}
|
||||
/* Effectively select the ring buffer implementation */
|
||||
indio_dev->buffer->access = &ring_sw_access_funcs;
|
||||
indio_dev->pollfunc = iio_alloc_pollfunc(NULL,
|
||||
&ad799x_trigger_handler,
|
||||
IRQF_ONESHOT,
|
||||
|
@ -725,32 +725,19 @@ static struct attribute *adt7310_event_int_attributes[] = {
|
||||
&iio_dev_attr_fault_queue.dev_attr.attr,
|
||||
&iio_dev_attr_t_alarm_high.dev_attr.attr,
|
||||
&iio_dev_attr_t_alarm_low.dev_attr.attr,
|
||||
&iio_dev_attr_t_hyst.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute *adt7310_event_ct_attributes[] = {
|
||||
&iio_dev_attr_event_mode.dev_attr.attr,
|
||||
&iio_dev_attr_available_event_modes.dev_attr.attr,
|
||||
&iio_dev_attr_fault_queue.dev_attr.attr,
|
||||
&iio_dev_attr_t_crit.dev_attr.attr,
|
||||
&iio_dev_attr_t_hyst.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group adt7310_event_attribute_group[ADT7310_IRQS] = {
|
||||
{
|
||||
.attrs = adt7310_event_int_attributes,
|
||||
.name = "events",
|
||||
}, {
|
||||
.attrs = adt7310_event_ct_attributes,
|
||||
.name = "events",
|
||||
}
|
||||
static struct attribute_group adt7310_event_attribute_group = {
|
||||
.attrs = adt7310_event_int_attributes,
|
||||
.name = "events",
|
||||
};
|
||||
|
||||
static const struct iio_info adt7310_info = {
|
||||
.attrs = &adt7310_attribute_group,
|
||||
.event_attrs = adt7310_event_attribute_group,
|
||||
.event_attrs = &adt7310_event_attribute_group,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
|
@ -693,32 +693,19 @@ static struct attribute *adt7410_event_int_attributes[] = {
|
||||
&iio_dev_attr_fault_queue.dev_attr.attr,
|
||||
&iio_dev_attr_t_alarm_high.dev_attr.attr,
|
||||
&iio_dev_attr_t_alarm_low.dev_attr.attr,
|
||||
&iio_dev_attr_t_hyst.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute *adt7410_event_ct_attributes[] = {
|
||||
&iio_dev_attr_event_mode.dev_attr.attr,
|
||||
&iio_dev_attr_available_event_modes.dev_attr.attr,
|
||||
&iio_dev_attr_fault_queue.dev_attr.attr,
|
||||
&iio_dev_attr_t_crit.dev_attr.attr,
|
||||
&iio_dev_attr_t_hyst.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group adt7410_event_attribute_group[ADT7410_IRQS] = {
|
||||
{
|
||||
.attrs = adt7410_event_int_attributes,
|
||||
.name = "events",
|
||||
}, {
|
||||
.attrs = adt7410_event_ct_attributes,
|
||||
.name = "events",
|
||||
}
|
||||
static struct attribute_group adt7410_event_attribute_group = {
|
||||
.attrs = adt7410_event_int_attributes,
|
||||
.name = "events",
|
||||
};
|
||||
|
||||
static const struct iio_info adt7410_info = {
|
||||
.attrs = &adt7410_attribute_group,
|
||||
.event_attrs = adt7410_event_attribute_group,
|
||||
.event_attrs = &adt7410_event_attribute_group,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
|
237
drivers/staging/iio/adc/lpc32xx_adc.c
Normal file
237
drivers/staging/iio/adc/lpc32xx_adc.c
Normal file
@ -0,0 +1,237 @@
|
||||
/*
|
||||
* lpc32xx_adc.c - Support for ADC in LPC32XX
|
||||
*
|
||||
* 3-channel, 10-bit ADC
|
||||
*
|
||||
* Copyright (C) 2011, 2012 Roland Stigge <stigge@antcom.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/completion.h>
|
||||
|
||||
#include "../iio.h"
|
||||
#include "../sysfs.h"
|
||||
|
||||
/*
|
||||
* LPC32XX registers definitions
|
||||
*/
|
||||
#define LPC32XX_ADC_SELECT(x) ((x) + 0x04)
|
||||
#define LPC32XX_ADC_CTRL(x) ((x) + 0x08)
|
||||
#define LPC32XX_ADC_VALUE(x) ((x) + 0x48)
|
||||
|
||||
/* Bit definitions for LPC32XX_ADC_SELECT: */
|
||||
#define AD_REFm 0x00000200 /* constant, always write this value! */
|
||||
#define AD_REFp 0x00000080 /* constant, always write this value! */
|
||||
#define AD_IN 0x00000010 /* multiple of this is the */
|
||||
/* channel number: 0, 1, 2 */
|
||||
#define AD_INTERNAL 0x00000004 /* constant, always write this value! */
|
||||
|
||||
/* Bit definitions for LPC32XX_ADC_CTRL: */
|
||||
#define AD_STROBE 0x00000002
|
||||
#define AD_PDN_CTRL 0x00000004
|
||||
|
||||
/* Bit definitions for LPC32XX_ADC_VALUE: */
|
||||
#define ADC_VALUE_MASK 0x000003FF
|
||||
|
||||
#define MOD_NAME "lpc32xx-adc"
|
||||
|
||||
struct lpc32xx_adc_info {
|
||||
void __iomem *adc_base;
|
||||
struct clk *clk;
|
||||
struct completion completion;
|
||||
|
||||
u32 value;
|
||||
};
|
||||
|
||||
static int lpc32xx_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val,
|
||||
int *val2,
|
||||
long mask)
|
||||
{
|
||||
struct lpc32xx_adc_info *info = iio_priv(indio_dev);
|
||||
|
||||
if (mask == 0) {
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
clk_enable(info->clk);
|
||||
/* Measurement setup */
|
||||
__raw_writel(AD_INTERNAL | (chan->address) | AD_REFp | AD_REFm,
|
||||
LPC32XX_ADC_SELECT(info->adc_base));
|
||||
/* Trigger conversion */
|
||||
__raw_writel(AD_PDN_CTRL | AD_STROBE,
|
||||
LPC32XX_ADC_CTRL(info->adc_base));
|
||||
wait_for_completion(&info->completion); /* set by ISR */
|
||||
clk_disable(info->clk);
|
||||
*val = info->value;
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_info lpc32xx_adc_iio_info = {
|
||||
.read_raw = &lpc32xx_read_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
#define LPC32XX_ADC_CHANNEL(_index) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = _index, \
|
||||
.address = AD_IN * _index, \
|
||||
.scan_index = _index, \
|
||||
}
|
||||
|
||||
static struct iio_chan_spec lpc32xx_adc_iio_channels[] = {
|
||||
LPC32XX_ADC_CHANNEL(0),
|
||||
LPC32XX_ADC_CHANNEL(1),
|
||||
LPC32XX_ADC_CHANNEL(2),
|
||||
};
|
||||
|
||||
static irqreturn_t lpc32xx_adc_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct lpc32xx_adc_info *info = (struct lpc32xx_adc_info *) dev_id;
|
||||
|
||||
/* Read value and clear irq */
|
||||
info->value = __raw_readl(LPC32XX_ADC_VALUE(info->adc_base)) &
|
||||
ADC_VALUE_MASK;
|
||||
complete(&info->completion);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int __devinit lpc32xx_adc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct lpc32xx_adc_info *info = NULL;
|
||||
struct resource *res;
|
||||
int retval = -ENODEV;
|
||||
struct iio_dev *iodev = NULL;
|
||||
int irq;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "failed to get platform I/O memory\n");
|
||||
retval = -EBUSY;
|
||||
goto errout1;
|
||||
}
|
||||
|
||||
iodev = iio_allocate_device(sizeof(struct lpc32xx_adc_info));
|
||||
if (!iodev) {
|
||||
dev_err(&pdev->dev, "failed allocating iio device\n");
|
||||
retval = -ENOMEM;
|
||||
goto errout1;
|
||||
}
|
||||
|
||||
info = iio_priv(iodev);
|
||||
|
||||
info->adc_base = ioremap(res->start, res->end - res->start + 1);
|
||||
if (!info->adc_base) {
|
||||
dev_err(&pdev->dev, "failed mapping memory\n");
|
||||
retval = -EBUSY;
|
||||
goto errout2;
|
||||
}
|
||||
|
||||
info->clk = clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(info->clk)) {
|
||||
dev_err(&pdev->dev, "failed getting clock\n");
|
||||
goto errout3;
|
||||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if ((irq < 0) || (irq >= NR_IRQS)) {
|
||||
dev_err(&pdev->dev, "failed getting interrupt resource\n");
|
||||
retval = -EINVAL;
|
||||
goto errout4;
|
||||
}
|
||||
|
||||
retval = request_irq(irq, lpc32xx_adc_isr, 0, MOD_NAME, info);
|
||||
if (retval < 0) {
|
||||
dev_err(&pdev->dev, "failed requesting interrupt\n");
|
||||
goto errout4;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, iodev);
|
||||
|
||||
init_completion(&info->completion);
|
||||
|
||||
iodev->name = MOD_NAME;
|
||||
iodev->dev.parent = &pdev->dev;
|
||||
iodev->info = &lpc32xx_adc_iio_info;
|
||||
iodev->modes = INDIO_DIRECT_MODE;
|
||||
iodev->channels = lpc32xx_adc_iio_channels;
|
||||
iodev->num_channels = ARRAY_SIZE(lpc32xx_adc_iio_channels);
|
||||
|
||||
retval = iio_device_register(iodev);
|
||||
if (retval)
|
||||
goto errout5;
|
||||
|
||||
dev_info(&pdev->dev, "LPC32XX ADC driver loaded, IRQ %d\n", irq);
|
||||
|
||||
return 0;
|
||||
|
||||
errout5:
|
||||
free_irq(irq, iodev);
|
||||
errout4:
|
||||
clk_put(info->clk);
|
||||
errout3:
|
||||
iounmap(info->adc_base);
|
||||
errout2:
|
||||
iio_free_device(iodev);
|
||||
errout1:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int __devexit lpc32xx_adc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *iodev = platform_get_drvdata(pdev);
|
||||
struct lpc32xx_adc_info *info = iio_priv(iodev);
|
||||
int irq = platform_get_irq(pdev, 0);
|
||||
|
||||
iio_device_unregister(iodev);
|
||||
free_irq(irq, iodev);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
clk_put(info->clk);
|
||||
iounmap(info->adc_base);
|
||||
iio_free_device(iodev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver lpc32xx_adc_driver = {
|
||||
.probe = lpc32xx_adc_probe,
|
||||
.remove = __devexit_p(lpc32xx_adc_remove),
|
||||
.driver = {
|
||||
.name = MOD_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(lpc32xx_adc_driver);
|
||||
|
||||
MODULE_AUTHOR("Roland Stigge <stigge@antcom.de>");
|
||||
MODULE_DESCRIPTION("LPC32XX ADC driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -341,7 +341,7 @@ static struct iio_chan_spec max1361_channels[] =
|
||||
static struct iio_chan_spec max1363_channels[] =
|
||||
MAX1363_4X_CHANS(12, MAX1363_EV_M);
|
||||
|
||||
/* Appies to max1236, max1237 */
|
||||
/* Applies to max1236, max1237 */
|
||||
static const enum max1363_modes max1236_mode_list[] = {
|
||||
_s0, _s1, _s2, _s3,
|
||||
s0to1, s0to2, s0to3,
|
||||
@ -543,9 +543,9 @@ static int max1363_read_thresh(struct iio_dev *indio_dev,
|
||||
{
|
||||
struct max1363_state *st = iio_priv(indio_dev);
|
||||
if (IIO_EVENT_CODE_EXTRACT_DIR(event_code) == IIO_EV_DIR_FALLING)
|
||||
*val = st->thresh_low[IIO_EVENT_CODE_EXTRACT_NUM(event_code)];
|
||||
*val = st->thresh_low[IIO_EVENT_CODE_EXTRACT_CHAN(event_code)];
|
||||
else
|
||||
*val = st->thresh_high[IIO_EVENT_CODE_EXTRACT_NUM(event_code)];
|
||||
*val = st->thresh_high[IIO_EVENT_CODE_EXTRACT_CHAN(event_code)];
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -568,10 +568,10 @@ static int max1363_write_thresh(struct iio_dev *indio_dev,
|
||||
|
||||
switch (IIO_EVENT_CODE_EXTRACT_DIR(event_code)) {
|
||||
case IIO_EV_DIR_FALLING:
|
||||
st->thresh_low[IIO_EVENT_CODE_EXTRACT_NUM(event_code)] = val;
|
||||
st->thresh_low[IIO_EVENT_CODE_EXTRACT_CHAN(event_code)] = val;
|
||||
break;
|
||||
case IIO_EV_DIR_RISING:
|
||||
st->thresh_high[IIO_EVENT_CODE_EXTRACT_NUM(event_code)] = val;
|
||||
st->thresh_high[IIO_EVENT_CODE_EXTRACT_CHAN(event_code)] = val;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -622,7 +622,7 @@ static int max1363_read_event_config(struct iio_dev *indio_dev,
|
||||
struct max1363_state *st = iio_priv(indio_dev);
|
||||
|
||||
int val;
|
||||
int number = IIO_EVENT_CODE_EXTRACT_NUM(event_code);
|
||||
int number = IIO_EVENT_CODE_EXTRACT_CHAN(event_code);
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
if (IIO_EVENT_CODE_EXTRACT_DIR(event_code) == IIO_EV_DIR_FALLING)
|
||||
val = (1 << number) & st->mask_low;
|
||||
@ -775,7 +775,7 @@ static int max1363_write_event_config(struct iio_dev *indio_dev,
|
||||
int ret = 0;
|
||||
struct max1363_state *st = iio_priv(indio_dev);
|
||||
u16 unifiedmask;
|
||||
int number = IIO_EVENT_CODE_EXTRACT_NUM(event_code);
|
||||
int number = IIO_EVENT_CODE_EXTRACT_CHAN(event_code);
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
unifiedmask = st->mask_low | st->mask_high;
|
||||
@ -1245,10 +1245,31 @@ static int max1363_initial_setup(struct max1363_state *st)
|
||||
return max1363_set_scan_mode(st);
|
||||
}
|
||||
|
||||
static int __devinit max1363_alloc_scan_masks(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct max1363_state *st = iio_priv(indio_dev);
|
||||
unsigned long *masks;
|
||||
int i;
|
||||
|
||||
masks = kzalloc(BITS_TO_LONGS(MAX1363_MAX_CHANNELS)*sizeof(long)*
|
||||
(st->chip_info->num_modes + 1), GFP_KERNEL);
|
||||
if (!masks)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < st->chip_info->num_modes; i++)
|
||||
bitmap_copy(masks + BITS_TO_LONGS(MAX1363_MAX_CHANNELS)*i,
|
||||
max1363_mode_table[st->chip_info->mode_list[i]]
|
||||
.modemask, MAX1363_MAX_CHANNELS);
|
||||
|
||||
indio_dev->available_scan_masks = masks;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devinit max1363_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
int ret, i;
|
||||
int ret;
|
||||
struct max1363_state *st;
|
||||
struct iio_dev *indio_dev;
|
||||
struct regulator *reg;
|
||||
@ -1276,19 +1297,10 @@ static int __devinit max1363_probe(struct i2c_client *client,
|
||||
st->chip_info = &max1363_chip_info_tbl[id->driver_data];
|
||||
st->client = client;
|
||||
|
||||
indio_dev->available_scan_masks
|
||||
= kzalloc(BITS_TO_LONGS(MAX1363_MAX_CHANNELS)*sizeof(long)*
|
||||
(st->chip_info->num_modes + 1), GFP_KERNEL);
|
||||
if (!indio_dev->available_scan_masks) {
|
||||
ret = -ENOMEM;
|
||||
ret = max1363_alloc_scan_masks(indio_dev);
|
||||
if (ret)
|
||||
goto error_free_device;
|
||||
}
|
||||
|
||||
for (i = 0; i < st->chip_info->num_modes; i++)
|
||||
bitmap_copy(indio_dev->available_scan_masks +
|
||||
BITS_TO_LONGS(MAX1363_MAX_CHANNELS)*i,
|
||||
max1363_mode_table[st->chip_info->mode_list[i]]
|
||||
.modemask, MAX1363_MAX_CHANNELS);
|
||||
/* Estabilish that the iio_dev is a child of the i2c device */
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->name = id->name;
|
||||
|
@ -116,8 +116,6 @@ int max1363_register_ring_funcs_and_init(struct iio_dev *indio_dev)
|
||||
ret = -ENOMEM;
|
||||
goto error_deallocate_sw_rb;
|
||||
}
|
||||
/* Effectively select the ring buffer implementation */
|
||||
indio_dev->buffer->access = &ring_sw_access_funcs;
|
||||
/* Ring buffer functions - here trigger setup related */
|
||||
indio_dev->setup_ops = &max1363_ring_setup_ops;
|
||||
|
||||
|
@ -125,30 +125,14 @@ static const struct i2c_device_id adt7316_i2c_id[] = {
|
||||
|
||||
MODULE_DEVICE_TABLE(i2c, adt7316_i2c_id);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int adt7316_i2c_suspend(struct i2c_client *client, pm_message_t message)
|
||||
{
|
||||
return adt7316_disable(&client->dev);
|
||||
}
|
||||
|
||||
static int adt7316_i2c_resume(struct i2c_client *client)
|
||||
{
|
||||
return adt7316_enable(&client->dev);
|
||||
}
|
||||
#else
|
||||
# define adt7316_i2c_suspend NULL
|
||||
# define adt7316_i2c_resume NULL
|
||||
#endif
|
||||
|
||||
static struct i2c_driver adt7316_driver = {
|
||||
.driver = {
|
||||
.name = "adt7316",
|
||||
.pm = ADT7316_PM_OPS,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = adt7316_i2c_probe,
|
||||
.remove = __devexit_p(adt7316_i2c_remove),
|
||||
.suspend = adt7316_i2c_suspend,
|
||||
.resume = adt7316_i2c_resume,
|
||||
.id_table = adt7316_i2c_id,
|
||||
};
|
||||
module_i2c_driver(adt7316_driver);
|
||||
|
@ -133,30 +133,14 @@ static const struct spi_device_id adt7316_spi_id[] = {
|
||||
|
||||
MODULE_DEVICE_TABLE(spi, adt7316_spi_id);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int adt7316_spi_suspend(struct spi_device *spi_dev, pm_message_t message)
|
||||
{
|
||||
return adt7316_disable(&spi_dev->dev);
|
||||
}
|
||||
|
||||
static int adt7316_spi_resume(struct spi_device *spi_dev)
|
||||
{
|
||||
return adt7316_enable(&spi_dev->dev);
|
||||
}
|
||||
#else
|
||||
# define adt7316_spi_suspend NULL
|
||||
# define adt7316_spi_resume NULL
|
||||
#endif
|
||||
|
||||
static struct spi_driver adt7316_driver = {
|
||||
.driver = {
|
||||
.name = "adt7316",
|
||||
.pm = ADT7316_PM_OPS,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = adt7316_spi_probe,
|
||||
.remove = __devexit_p(adt7316_spi_remove),
|
||||
.suspend = adt7316_spi_suspend,
|
||||
.resume = adt7316_spi_resume,
|
||||
.id_table = adt7316_spi_id,
|
||||
};
|
||||
module_spi_driver(adt7316_driver);
|
||||
|
@ -2089,24 +2089,25 @@ static struct attribute_group adt7516_event_attribute_group = {
|
||||
.name = "events",
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
int adt7316_disable(struct device *dev)
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int adt7316_disable(struct device *dev)
|
||||
{
|
||||
struct iio_dev *dev_info = dev_get_drvdata(dev);
|
||||
struct adt7316_chip_info *chip = iio_priv(dev_info);
|
||||
|
||||
return _adt7316_store_enabled(chip, 0);
|
||||
}
|
||||
EXPORT_SYMBOL(adt7316_disable);
|
||||
|
||||
int adt7316_enable(struct device *dev)
|
||||
static int adt7316_enable(struct device *dev)
|
||||
{
|
||||
struct iio_dev *dev_info = dev_get_drvdata(dev);
|
||||
struct adt7316_chip_info *chip = iio_priv(dev_info);
|
||||
|
||||
return _adt7316_store_enabled(chip, 1);
|
||||
}
|
||||
EXPORT_SYMBOL(adt7316_enable);
|
||||
|
||||
SIMPLE_DEV_PM_OPS(adt7316_pm_ops, adt7316_disable, adt7316_enable);
|
||||
EXPORT_SYMBOL_GPL(adt7316_pm_ops);
|
||||
#endif
|
||||
|
||||
static const struct iio_info adt7316_info = {
|
||||
|
@ -10,6 +10,7 @@
|
||||
#define _ADT7316_H_
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/pm.h>
|
||||
|
||||
#define ADT7316_REG_MAX_ADDR 0x3F
|
||||
|
||||
@ -23,9 +24,11 @@ struct adt7316_bus {
|
||||
int (*multi_write) (void *client, u8 first_reg, u8 count, u8 *data);
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
int adt7316_disable(struct device *dev);
|
||||
int adt7316_enable(struct device *dev);
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
extern const struct dev_pm_ops adt7316_pm_ops;
|
||||
#define ADT7316_PM_OPS (&adt7316_pm_ops)
|
||||
#else
|
||||
#define ADT7316_PM_OPS NULL
|
||||
#endif
|
||||
int adt7316_probe(struct device *dev, struct adt7316_bus *bus, const char *name);
|
||||
int adt7316_remove(struct device *dev);
|
||||
|
@ -91,8 +91,6 @@ struct iio_buffer {
|
||||
**/
|
||||
void iio_buffer_init(struct iio_buffer *buffer);
|
||||
|
||||
void iio_buffer_deinit(struct iio_buffer *buffer);
|
||||
|
||||
/**
|
||||
* __iio_update_buffer() - update common elements of buffers
|
||||
* @buffer: buffer that is the event source
|
||||
|
@ -167,7 +167,7 @@ static int ad7150_write_event_params(struct iio_dev *indio_dev, u64 event_code)
|
||||
u16 value;
|
||||
u8 sens, timeout;
|
||||
struct ad7150_chip_info *chip = iio_priv(indio_dev);
|
||||
int chan = IIO_EVENT_CODE_EXTRACT_NUM(event_code);
|
||||
int chan = IIO_EVENT_CODE_EXTRACT_CHAN(event_code);
|
||||
int rising = !!(IIO_EVENT_CODE_EXTRACT_DIR(event_code) ==
|
||||
IIO_EV_DIR_RISING);
|
||||
|
||||
@ -279,7 +279,7 @@ static int ad7150_read_event_value(struct iio_dev *indio_dev,
|
||||
u64 event_code,
|
||||
int *val)
|
||||
{
|
||||
int chan = IIO_EVENT_CODE_EXTRACT_NUM(event_code);
|
||||
int chan = IIO_EVENT_CODE_EXTRACT_CHAN(event_code);
|
||||
struct ad7150_chip_info *chip = iio_priv(indio_dev);
|
||||
int rising = !!(IIO_EVENT_CODE_EXTRACT_DIR(event_code) ==
|
||||
IIO_EV_DIR_RISING);
|
||||
@ -309,7 +309,7 @@ static int ad7150_write_event_value(struct iio_dev *indio_dev,
|
||||
{
|
||||
int ret;
|
||||
struct ad7150_chip_info *chip = iio_priv(indio_dev);
|
||||
int chan = IIO_EVENT_CODE_EXTRACT_NUM(event_code);
|
||||
int chan = IIO_EVENT_CODE_EXTRACT_CHAN(event_code);
|
||||
int rising = !!(IIO_EVENT_CODE_EXTRACT_DIR(event_code) ==
|
||||
IIO_EV_DIR_RISING);
|
||||
|
||||
@ -347,7 +347,7 @@ static ssize_t ad7150_show_timeout(struct device *dev,
|
||||
u8 value;
|
||||
|
||||
/* use the event code for consistency reasons */
|
||||
int chan = IIO_EVENT_CODE_EXTRACT_NUM(this_attr->address);
|
||||
int chan = IIO_EVENT_CODE_EXTRACT_CHAN(this_attr->address);
|
||||
int rising = !!(IIO_EVENT_CODE_EXTRACT_DIR(this_attr->address)
|
||||
== IIO_EV_DIR_RISING);
|
||||
|
||||
@ -373,7 +373,7 @@ static ssize_t ad7150_store_timeout(struct device *dev,
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct ad7150_chip_info *chip = iio_priv(indio_dev);
|
||||
struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
|
||||
int chan = IIO_EVENT_CODE_EXTRACT_NUM(this_attr->address);
|
||||
int chan = IIO_EVENT_CODE_EXTRACT_CHAN(this_attr->address);
|
||||
int rising = !!(IIO_EVENT_CODE_EXTRACT_DIR(this_attr->address) ==
|
||||
IIO_EV_DIR_RISING);
|
||||
u8 data;
|
||||
|
96
drivers/staging/iio/consumer.h
Normal file
96
drivers/staging/iio/consumer.h
Normal file
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Industrial I/O in kernel consumer interface
|
||||
*
|
||||
* Copyright (c) 2011 Jonathan Cameron
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*/
|
||||
#ifndef _IIO_INKERN_CONSUMER_H_
|
||||
#define _IIO_INKERN_CONSUMER_H
|
||||
#include "types.h"
|
||||
|
||||
struct iio_dev;
|
||||
struct iio_chan_spec;
|
||||
|
||||
/**
|
||||
* struct iio_channel - everything needed for a consumer to use a channel
|
||||
* @indio_dev: Device on which the channel exists.
|
||||
* @channel: Full description of the channel.
|
||||
*/
|
||||
struct iio_channel {
|
||||
struct iio_dev *indio_dev;
|
||||
const struct iio_chan_spec *channel;
|
||||
};
|
||||
|
||||
/**
|
||||
* iio_channel_get() - get description of all that is needed to access channel.
|
||||
* @name: Unique name of the device as provided in the iio_map
|
||||
* with which the desired provider to consumer mapping
|
||||
* was registered.
|
||||
* @consumer_channel: Unique name to identify the channel on the consumer
|
||||
* side. This typically describes the channels use within
|
||||
* the consumer. E.g. 'battery_voltage'
|
||||
*/
|
||||
struct iio_channel *iio_st_channel_get(const char *name,
|
||||
const char *consumer_channel);
|
||||
|
||||
/**
|
||||
* iio_st_channel_release() - release channels obtained via iio_st_channel_get
|
||||
* @chan: The channel to be released.
|
||||
*/
|
||||
void iio_st_channel_release(struct iio_channel *chan);
|
||||
|
||||
/**
|
||||
* iio_st_channel_get_all() - get all channels associated with a client
|
||||
* @name: name of consumer device.
|
||||
*
|
||||
* Returns an array of iio_channel structures terminated with one with
|
||||
* null iio_dev pointer.
|
||||
* This function is used by fairly generic consumers to get all the
|
||||
* channels registered as having this consumer.
|
||||
*/
|
||||
struct iio_channel *iio_st_channel_get_all(const char *name);
|
||||
|
||||
/**
|
||||
* iio_st_channel_release_all() - reverse iio_st_get_all
|
||||
* @chan: Array of channels to be released.
|
||||
*/
|
||||
void iio_st_channel_release_all(struct iio_channel *chan);
|
||||
|
||||
/**
|
||||
* iio_st_read_channel_raw() - read from a given channel
|
||||
* @channel: The channel being queried.
|
||||
* @val: Value read back.
|
||||
*
|
||||
* Note raw reads from iio channels are in adc counts and hence
|
||||
* scale will need to be applied if standard units required.
|
||||
*/
|
||||
int iio_st_read_channel_raw(struct iio_channel *chan,
|
||||
int *val);
|
||||
|
||||
/**
|
||||
* iio_st_get_channel_type() - get the type of a channel
|
||||
* @channel: The channel being queried.
|
||||
* @type: The type of the channel.
|
||||
*
|
||||
* returns the enum iio_chan_type of the channel
|
||||
*/
|
||||
int iio_st_get_channel_type(struct iio_channel *channel,
|
||||
enum iio_chan_type *type);
|
||||
|
||||
/**
|
||||
* iio_st_read_channel_scale() - read the scale value for a channel
|
||||
* @channel: The channel being queried.
|
||||
* @val: First part of value read back.
|
||||
* @val2: Second part of value read back.
|
||||
*
|
||||
* Note returns a description of what is in val and val2, such
|
||||
* as IIO_VAL_INT_PLUS_MICRO telling us we have a value of val
|
||||
* + val2/1e6
|
||||
*/
|
||||
int iio_st_read_channel_scale(struct iio_channel *chan, int *val,
|
||||
int *val2);
|
||||
|
||||
#endif
|
@ -4,11 +4,12 @@
|
||||
menu "Digital to analog converters"
|
||||
|
||||
config AD5064
|
||||
tristate "Analog Devices AD5064/64-1/44/24 DAC driver"
|
||||
tristate "Analog Devices AD5064/64-1/65/44/45/24/25, AD5628/48/66/68 DAC driver"
|
||||
depends on SPI
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD5064, AD5064-1,
|
||||
AD5044, AD5024 Digital to Analog Converter.
|
||||
Say yes here to build support for Analog Devices AD5024, AD5025, AD5044,
|
||||
AD5045, AD5064, AD5064-1, AD5065, AD5628, AD5648, AD5666, AD5668 Digital
|
||||
to Analog Converter.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad5064.
|
||||
|
@ -1,5 +1,6 @@
|
||||
/*
|
||||
* AD5064, AD5064-1, AD5044, AD5024 Digital to analog converters driver
|
||||
* AD5024, AD5025, AD5044, AD5045, AD5064, AD5064-1, AD5065, AD5628, AD5648,
|
||||
* AD5666, AD5668 Digital to analog converters driver
|
||||
*
|
||||
* Copyright 2011 Analog Devices Inc.
|
||||
*
|
||||
@ -19,7 +20,8 @@
|
||||
#include "../sysfs.h"
|
||||
#include "dac.h"
|
||||
|
||||
#define AD5064_DAC_CHANNELS 4
|
||||
#define AD5064_MAX_DAC_CHANNELS 8
|
||||
#define AD5064_MAX_VREFS 4
|
||||
|
||||
#define AD5064_ADDR(x) ((x) << 20)
|
||||
#define AD5064_CMD(x) ((x) << 24)
|
||||
@ -35,7 +37,10 @@
|
||||
#define AD5064_CMD_CLEAR 0x5
|
||||
#define AD5064_CMD_LDAC_MASK 0x6
|
||||
#define AD5064_CMD_RESET 0x7
|
||||
#define AD5064_CMD_DAISY_CHAIN_ENABLE 0x8
|
||||
#define AD5064_CMD_CONFIG 0x8
|
||||
|
||||
#define AD5064_CONFIG_DAISY_CHAIN_ENABLE BIT(1)
|
||||
#define AD5064_CONFIG_INT_VREF_ENABLE BIT(0)
|
||||
|
||||
#define AD5064_LDAC_PWRDN_NONE 0x0
|
||||
#define AD5064_LDAC_PWRDN_1K 0x1
|
||||
@ -45,12 +50,17 @@
|
||||
/**
|
||||
* struct ad5064_chip_info - chip specific information
|
||||
* @shared_vref: whether the vref supply is shared between channels
|
||||
* @internal_vref: internal reference voltage. 0 if the chip has no internal
|
||||
* vref.
|
||||
* @channel: channel specification
|
||||
*/
|
||||
* @num_channels: number of channels
|
||||
*/
|
||||
|
||||
struct ad5064_chip_info {
|
||||
bool shared_vref;
|
||||
struct iio_chan_spec channel[AD5064_DAC_CHANNELS];
|
||||
unsigned long internal_vref;
|
||||
const struct iio_chan_spec *channels;
|
||||
unsigned int num_channels;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -61,16 +71,19 @@ struct ad5064_chip_info {
|
||||
* @pwr_down: whether channel is powered down
|
||||
* @pwr_down_mode: channel's current power down mode
|
||||
* @dac_cache: current DAC raw value (chip does not support readback)
|
||||
* @use_internal_vref: set to true if the internal reference voltage should be
|
||||
* used.
|
||||
* @data: spi transfer buffers
|
||||
*/
|
||||
|
||||
struct ad5064_state {
|
||||
struct spi_device *spi;
|
||||
const struct ad5064_chip_info *chip_info;
|
||||
struct regulator_bulk_data vref_reg[AD5064_DAC_CHANNELS];
|
||||
bool pwr_down[AD5064_DAC_CHANNELS];
|
||||
u8 pwr_down_mode[AD5064_DAC_CHANNELS];
|
||||
unsigned int dac_cache[AD5064_DAC_CHANNELS];
|
||||
struct regulator_bulk_data vref_reg[AD5064_MAX_VREFS];
|
||||
bool pwr_down[AD5064_MAX_DAC_CHANNELS];
|
||||
u8 pwr_down_mode[AD5064_MAX_DAC_CHANNELS];
|
||||
unsigned int dac_cache[AD5064_MAX_DAC_CHANNELS];
|
||||
bool use_internal_vref;
|
||||
|
||||
/*
|
||||
* DMA (thus cache coherency maintenance) requires the
|
||||
@ -81,50 +94,20 @@ struct ad5064_state {
|
||||
|
||||
enum ad5064_type {
|
||||
ID_AD5024,
|
||||
ID_AD5025,
|
||||
ID_AD5044,
|
||||
ID_AD5045,
|
||||
ID_AD5064,
|
||||
ID_AD5064_1,
|
||||
};
|
||||
|
||||
#define AD5064_CHANNEL(chan, bits) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.output = 1, \
|
||||
.channel = (chan), \
|
||||
.info_mask = IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \
|
||||
.address = AD5064_ADDR_DAC(chan), \
|
||||
.scan_type = IIO_ST('u', (bits), 16, 20 - (bits)) \
|
||||
}
|
||||
|
||||
static const struct ad5064_chip_info ad5064_chip_info_tbl[] = {
|
||||
[ID_AD5024] = {
|
||||
.shared_vref = false,
|
||||
.channel[0] = AD5064_CHANNEL(0, 12),
|
||||
.channel[1] = AD5064_CHANNEL(1, 12),
|
||||
.channel[2] = AD5064_CHANNEL(2, 12),
|
||||
.channel[3] = AD5064_CHANNEL(3, 12),
|
||||
},
|
||||
[ID_AD5044] = {
|
||||
.shared_vref = false,
|
||||
.channel[0] = AD5064_CHANNEL(0, 14),
|
||||
.channel[1] = AD5064_CHANNEL(1, 14),
|
||||
.channel[2] = AD5064_CHANNEL(2, 14),
|
||||
.channel[3] = AD5064_CHANNEL(3, 14),
|
||||
},
|
||||
[ID_AD5064] = {
|
||||
.shared_vref = false,
|
||||
.channel[0] = AD5064_CHANNEL(0, 16),
|
||||
.channel[1] = AD5064_CHANNEL(1, 16),
|
||||
.channel[2] = AD5064_CHANNEL(2, 16),
|
||||
.channel[3] = AD5064_CHANNEL(3, 16),
|
||||
},
|
||||
[ID_AD5064_1] = {
|
||||
.shared_vref = true,
|
||||
.channel[0] = AD5064_CHANNEL(0, 16),
|
||||
.channel[1] = AD5064_CHANNEL(1, 16),
|
||||
.channel[2] = AD5064_CHANNEL(2, 16),
|
||||
.channel[3] = AD5064_CHANNEL(3, 16),
|
||||
},
|
||||
ID_AD5065,
|
||||
ID_AD5628_1,
|
||||
ID_AD5628_2,
|
||||
ID_AD5648_1,
|
||||
ID_AD5648_2,
|
||||
ID_AD5666_1,
|
||||
ID_AD5666_2,
|
||||
ID_AD5668_1,
|
||||
ID_AD5668_2,
|
||||
};
|
||||
|
||||
static int ad5064_spi_write(struct ad5064_state *st, unsigned int cmd,
|
||||
@ -160,22 +143,25 @@ static const char ad5064_powerdown_modes[][15] = {
|
||||
[AD5064_LDAC_PWRDN_3STATE] = "three_state",
|
||||
};
|
||||
|
||||
static ssize_t ad5064_read_powerdown_mode(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
static ssize_t ad5064_read_powerdown_mode_available(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, char *buf)
|
||||
{
|
||||
return sprintf(buf, "%s %s %s\n", ad5064_powerdown_modes[1],
|
||||
ad5064_powerdown_modes[2], ad5064_powerdown_modes[3]);
|
||||
}
|
||||
|
||||
static ssize_t ad5064_read_powerdown_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, char *buf)
|
||||
{
|
||||
struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct ad5064_state *st = iio_priv(indio_dev);
|
||||
|
||||
return sprintf(buf, "%s\n",
|
||||
ad5064_powerdown_modes[st->pwr_down_mode[this_attr->address]]);
|
||||
ad5064_powerdown_modes[st->pwr_down_mode[chan->channel]]);
|
||||
}
|
||||
|
||||
static ssize_t ad5064_write_powerdown_mode(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t len)
|
||||
static ssize_t ad5064_write_powerdown_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, const char *buf, size_t len)
|
||||
{
|
||||
struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct ad5064_state *st = iio_priv(indio_dev);
|
||||
unsigned int mode, i;
|
||||
int ret;
|
||||
@ -192,31 +178,26 @@ static ssize_t ad5064_write_powerdown_mode(struct device *dev,
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
st->pwr_down_mode[this_attr->address] = mode;
|
||||
st->pwr_down_mode[chan->channel] = mode;
|
||||
|
||||
ret = ad5064_sync_powerdown_mode(st, this_attr->address);
|
||||
ret = ad5064_sync_powerdown_mode(st, chan->channel);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return ret ? ret : len;
|
||||
}
|
||||
|
||||
static ssize_t ad5064_read_dac_powerdown(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
static ssize_t ad5064_read_dac_powerdown(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, char *buf)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct ad5064_state *st = iio_priv(indio_dev);
|
||||
struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
|
||||
|
||||
return sprintf(buf, "%d\n", st->pwr_down[this_attr->address]);
|
||||
return sprintf(buf, "%d\n", st->pwr_down[chan->channel]);
|
||||
}
|
||||
|
||||
static ssize_t ad5064_write_dac_powerdown(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t len)
|
||||
static ssize_t ad5064_write_dac_powerdown(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, const char *buf, size_t len)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct ad5064_state *st = iio_priv(indio_dev);
|
||||
struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
|
||||
bool pwr_down;
|
||||
int ret;
|
||||
|
||||
@ -225,53 +206,24 @@ static ssize_t ad5064_write_dac_powerdown(struct device *dev,
|
||||
return ret;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
st->pwr_down[this_attr->address] = pwr_down;
|
||||
st->pwr_down[chan->channel] = pwr_down;
|
||||
|
||||
ret = ad5064_sync_powerdown_mode(st, this_attr->address);
|
||||
ret = ad5064_sync_powerdown_mode(st, chan->channel);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return ret ? ret : len;
|
||||
}
|
||||
|
||||
static IIO_CONST_ATTR(out_voltage_powerdown_mode_available,
|
||||
"1kohm_to_gnd 100kohm_to_gnd three_state");
|
||||
static int ad5064_get_vref(struct ad5064_state *st,
|
||||
struct iio_chan_spec const *chan)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
#define IIO_DEV_ATTR_DAC_POWERDOWN_MODE(_chan) \
|
||||
IIO_DEVICE_ATTR(out_voltage##_chan##_powerdown_mode, \
|
||||
S_IRUGO | S_IWUSR, \
|
||||
ad5064_read_powerdown_mode, \
|
||||
ad5064_write_powerdown_mode, _chan);
|
||||
if (st->use_internal_vref)
|
||||
return st->chip_info->internal_vref;
|
||||
|
||||
#define IIO_DEV_ATTR_DAC_POWERDOWN(_chan) \
|
||||
IIO_DEVICE_ATTR(out_voltage##_chan##_powerdown, \
|
||||
S_IRUGO | S_IWUSR, \
|
||||
ad5064_read_dac_powerdown, \
|
||||
ad5064_write_dac_powerdown, _chan)
|
||||
|
||||
static IIO_DEV_ATTR_DAC_POWERDOWN(0);
|
||||
static IIO_DEV_ATTR_DAC_POWERDOWN_MODE(0);
|
||||
static IIO_DEV_ATTR_DAC_POWERDOWN(1);
|
||||
static IIO_DEV_ATTR_DAC_POWERDOWN_MODE(1);
|
||||
static IIO_DEV_ATTR_DAC_POWERDOWN(2);
|
||||
static IIO_DEV_ATTR_DAC_POWERDOWN_MODE(2);
|
||||
static IIO_DEV_ATTR_DAC_POWERDOWN(3);
|
||||
static IIO_DEV_ATTR_DAC_POWERDOWN_MODE(3);
|
||||
|
||||
static struct attribute *ad5064_attributes[] = {
|
||||
&iio_dev_attr_out_voltage0_powerdown.dev_attr.attr,
|
||||
&iio_dev_attr_out_voltage1_powerdown.dev_attr.attr,
|
||||
&iio_dev_attr_out_voltage2_powerdown.dev_attr.attr,
|
||||
&iio_dev_attr_out_voltage3_powerdown.dev_attr.attr,
|
||||
&iio_dev_attr_out_voltage0_powerdown_mode.dev_attr.attr,
|
||||
&iio_dev_attr_out_voltage1_powerdown_mode.dev_attr.attr,
|
||||
&iio_dev_attr_out_voltage2_powerdown_mode.dev_attr.attr,
|
||||
&iio_dev_attr_out_voltage3_powerdown_mode.dev_attr.attr,
|
||||
&iio_const_attr_out_voltage_powerdown_mode_available.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group ad5064_attribute_group = {
|
||||
.attrs = ad5064_attributes,
|
||||
};
|
||||
i = st->chip_info->shared_vref ? 0 : chan->channel;
|
||||
return regulator_get_voltage(st->vref_reg[i].consumer);
|
||||
}
|
||||
|
||||
static int ad5064_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
@ -280,7 +232,6 @@ static int ad5064_read_raw(struct iio_dev *indio_dev,
|
||||
long m)
|
||||
{
|
||||
struct ad5064_state *st = iio_priv(indio_dev);
|
||||
unsigned int vref;
|
||||
int scale_uv;
|
||||
|
||||
switch (m) {
|
||||
@ -288,8 +239,7 @@ static int ad5064_read_raw(struct iio_dev *indio_dev,
|
||||
*val = st->dac_cache[chan->channel];
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
vref = st->chip_info->shared_vref ? 0 : chan->channel;
|
||||
scale_uv = regulator_get_voltage(st->vref_reg[vref].consumer);
|
||||
scale_uv = ad5064_get_vref(st, chan);
|
||||
if (scale_uv < 0)
|
||||
return scale_uv;
|
||||
|
||||
@ -331,13 +281,144 @@ static int ad5064_write_raw(struct iio_dev *indio_dev,
|
||||
static const struct iio_info ad5064_info = {
|
||||
.read_raw = ad5064_read_raw,
|
||||
.write_raw = ad5064_write_raw,
|
||||
.attrs = &ad5064_attribute_group,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static struct iio_chan_spec_ext_info ad5064_ext_info[] = {
|
||||
{
|
||||
.name = "powerdown",
|
||||
.read = ad5064_read_dac_powerdown,
|
||||
.write = ad5064_write_dac_powerdown,
|
||||
},
|
||||
{
|
||||
.name = "powerdown_mode",
|
||||
.read = ad5064_read_powerdown_mode,
|
||||
.write = ad5064_write_powerdown_mode,
|
||||
},
|
||||
{
|
||||
.name = "powerdown_mode_available",
|
||||
.shared = true,
|
||||
.read = ad5064_read_powerdown_mode_available,
|
||||
},
|
||||
{ },
|
||||
};
|
||||
|
||||
#define AD5064_CHANNEL(chan, bits) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.output = 1, \
|
||||
.channel = (chan), \
|
||||
.info_mask = IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \
|
||||
.address = AD5064_ADDR_DAC(chan), \
|
||||
.scan_type = IIO_ST('u', (bits), 16, 20 - (bits)), \
|
||||
.ext_info = ad5064_ext_info, \
|
||||
}
|
||||
|
||||
#define DECLARE_AD5064_CHANNELS(name, bits) \
|
||||
const struct iio_chan_spec name[] = { \
|
||||
AD5064_CHANNEL(0, bits), \
|
||||
AD5064_CHANNEL(1, bits), \
|
||||
AD5064_CHANNEL(2, bits), \
|
||||
AD5064_CHANNEL(3, bits), \
|
||||
AD5064_CHANNEL(4, bits), \
|
||||
AD5064_CHANNEL(5, bits), \
|
||||
AD5064_CHANNEL(6, bits), \
|
||||
AD5064_CHANNEL(7, bits), \
|
||||
}
|
||||
|
||||
static DECLARE_AD5064_CHANNELS(ad5024_channels, 12);
|
||||
static DECLARE_AD5064_CHANNELS(ad5044_channels, 14);
|
||||
static DECLARE_AD5064_CHANNELS(ad5064_channels, 16);
|
||||
|
||||
static const struct ad5064_chip_info ad5064_chip_info_tbl[] = {
|
||||
[ID_AD5024] = {
|
||||
.shared_vref = false,
|
||||
.channels = ad5024_channels,
|
||||
.num_channels = 4,
|
||||
},
|
||||
[ID_AD5025] = {
|
||||
.shared_vref = false,
|
||||
.channels = ad5024_channels,
|
||||
.num_channels = 2,
|
||||
},
|
||||
[ID_AD5044] = {
|
||||
.shared_vref = false,
|
||||
.channels = ad5044_channels,
|
||||
.num_channels = 4,
|
||||
},
|
||||
[ID_AD5045] = {
|
||||
.shared_vref = false,
|
||||
.channels = ad5044_channels,
|
||||
.num_channels = 2,
|
||||
},
|
||||
[ID_AD5064] = {
|
||||
.shared_vref = false,
|
||||
.channels = ad5064_channels,
|
||||
.num_channels = 4,
|
||||
},
|
||||
[ID_AD5064_1] = {
|
||||
.shared_vref = true,
|
||||
.channels = ad5064_channels,
|
||||
.num_channels = 4,
|
||||
},
|
||||
[ID_AD5065] = {
|
||||
.shared_vref = false,
|
||||
.channels = ad5064_channels,
|
||||
.num_channels = 2,
|
||||
},
|
||||
[ID_AD5628_1] = {
|
||||
.shared_vref = true,
|
||||
.internal_vref = 2500000,
|
||||
.channels = ad5024_channels,
|
||||
.num_channels = 8,
|
||||
},
|
||||
[ID_AD5628_2] = {
|
||||
.shared_vref = true,
|
||||
.internal_vref = 5000000,
|
||||
.channels = ad5024_channels,
|
||||
.num_channels = 8,
|
||||
},
|
||||
[ID_AD5648_1] = {
|
||||
.shared_vref = true,
|
||||
.internal_vref = 2500000,
|
||||
.channels = ad5044_channels,
|
||||
.num_channels = 8,
|
||||
},
|
||||
[ID_AD5648_2] = {
|
||||
.shared_vref = true,
|
||||
.internal_vref = 5000000,
|
||||
.channels = ad5044_channels,
|
||||
.num_channels = 8,
|
||||
},
|
||||
[ID_AD5666_1] = {
|
||||
.shared_vref = true,
|
||||
.internal_vref = 2500000,
|
||||
.channels = ad5064_channels,
|
||||
.num_channels = 4,
|
||||
},
|
||||
[ID_AD5666_2] = {
|
||||
.shared_vref = true,
|
||||
.internal_vref = 5000000,
|
||||
.channels = ad5064_channels,
|
||||
.num_channels = 4,
|
||||
},
|
||||
[ID_AD5668_1] = {
|
||||
.shared_vref = true,
|
||||
.internal_vref = 2500000,
|
||||
.channels = ad5064_channels,
|
||||
.num_channels = 8,
|
||||
},
|
||||
[ID_AD5668_2] = {
|
||||
.shared_vref = true,
|
||||
.internal_vref = 5000000,
|
||||
.channels = ad5064_channels,
|
||||
.num_channels = 8,
|
||||
},
|
||||
};
|
||||
|
||||
static inline unsigned int ad5064_num_vref(struct ad5064_state *st)
|
||||
{
|
||||
return st->chip_info->shared_vref ? 1 : AD5064_DAC_CHANNELS;
|
||||
return st->chip_info->shared_vref ? 1 : st->chip_info->num_channels;
|
||||
}
|
||||
|
||||
static const char * const ad5064_vref_names[] = {
|
||||
@ -376,14 +457,24 @@ static int __devinit ad5064_probe(struct spi_device *spi)
|
||||
|
||||
ret = regulator_bulk_get(&st->spi->dev, ad5064_num_vref(st),
|
||||
st->vref_reg);
|
||||
if (ret)
|
||||
goto error_free;
|
||||
if (ret) {
|
||||
if (!st->chip_info->internal_vref)
|
||||
goto error_free;
|
||||
st->use_internal_vref = true;
|
||||
ret = ad5064_spi_write(st, AD5064_CMD_CONFIG, 0,
|
||||
AD5064_CONFIG_INT_VREF_ENABLE, 0);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "Failed to enable internal vref: %d\n",
|
||||
ret);
|
||||
goto error_free;
|
||||
}
|
||||
} else {
|
||||
ret = regulator_bulk_enable(ad5064_num_vref(st), st->vref_reg);
|
||||
if (ret)
|
||||
goto error_free_reg;
|
||||
}
|
||||
|
||||
ret = regulator_bulk_enable(ad5064_num_vref(st), st->vref_reg);
|
||||
if (ret)
|
||||
goto error_free_reg;
|
||||
|
||||
for (i = 0; i < AD5064_DAC_CHANNELS; ++i) {
|
||||
for (i = 0; i < st->chip_info->num_channels; ++i) {
|
||||
st->pwr_down_mode[i] = AD5064_LDAC_PWRDN_1K;
|
||||
st->dac_cache[i] = 0x8000;
|
||||
}
|
||||
@ -392,8 +483,8 @@ static int __devinit ad5064_probe(struct spi_device *spi)
|
||||
indio_dev->name = spi_get_device_id(spi)->name;
|
||||
indio_dev->info = &ad5064_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = st->chip_info->channel;
|
||||
indio_dev->num_channels = AD5064_DAC_CHANNELS;
|
||||
indio_dev->channels = st->chip_info->channels;
|
||||
indio_dev->num_channels = st->chip_info->num_channels;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
@ -402,9 +493,11 @@ static int __devinit ad5064_probe(struct spi_device *spi)
|
||||
return 0;
|
||||
|
||||
error_disable_reg:
|
||||
regulator_bulk_disable(ad5064_num_vref(st), st->vref_reg);
|
||||
if (!st->use_internal_vref)
|
||||
regulator_bulk_disable(ad5064_num_vref(st), st->vref_reg);
|
||||
error_free_reg:
|
||||
regulator_bulk_free(ad5064_num_vref(st), st->vref_reg);
|
||||
if (!st->use_internal_vref)
|
||||
regulator_bulk_free(ad5064_num_vref(st), st->vref_reg);
|
||||
error_free:
|
||||
iio_free_device(indio_dev);
|
||||
|
||||
@ -419,8 +512,10 @@ static int __devexit ad5064_remove(struct spi_device *spi)
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
regulator_bulk_disable(ad5064_num_vref(st), st->vref_reg);
|
||||
regulator_bulk_free(ad5064_num_vref(st), st->vref_reg);
|
||||
if (!st->use_internal_vref) {
|
||||
regulator_bulk_disable(ad5064_num_vref(st), st->vref_reg);
|
||||
regulator_bulk_free(ad5064_num_vref(st), st->vref_reg);
|
||||
}
|
||||
|
||||
iio_free_device(indio_dev);
|
||||
|
||||
@ -429,9 +524,21 @@ static int __devexit ad5064_remove(struct spi_device *spi)
|
||||
|
||||
static const struct spi_device_id ad5064_id[] = {
|
||||
{"ad5024", ID_AD5024},
|
||||
{"ad5025", ID_AD5025},
|
||||
{"ad5044", ID_AD5044},
|
||||
{"ad5045", ID_AD5045},
|
||||
{"ad5064", ID_AD5064},
|
||||
{"ad5064-1", ID_AD5064_1},
|
||||
{"ad5065", ID_AD5065},
|
||||
{"ad5628-1", ID_AD5628_1},
|
||||
{"ad5628-2", ID_AD5628_2},
|
||||
{"ad5648-1", ID_AD5648_1},
|
||||
{"ad5648-2", ID_AD5648_2},
|
||||
{"ad5666-1", ID_AD5666_1},
|
||||
{"ad5666-2", ID_AD5666_2},
|
||||
{"ad5668-1", ID_AD5668_1},
|
||||
{"ad5668-2", ID_AD5668_2},
|
||||
{"ad5668-3", ID_AD5668_2}, /* similar enough to ad5668-2 */
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad5064_id);
|
||||
@ -448,5 +555,5 @@ static struct spi_driver ad5064_driver = {
|
||||
module_spi_driver(ad5064_driver);
|
||||
|
||||
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD5064/64-1/44/24 DAC");
|
||||
MODULE_DESCRIPTION("Analog Devices AD5024/25/44/45/64/64-1/65, AD5628/48/66/68 DAC");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@ -439,8 +439,8 @@ static int __devinit ad5360_alloc_channels(struct iio_dev *indio_dev)
|
||||
struct iio_chan_spec *channels;
|
||||
unsigned int i;
|
||||
|
||||
channels = kcalloc(sizeof(struct iio_chan_spec),
|
||||
st->chip_info->num_channels, GFP_KERNEL);
|
||||
channels = kcalloc(st->chip_info->num_channels,
|
||||
sizeof(struct iio_chan_spec), GFP_KERNEL);
|
||||
|
||||
if (!channels)
|
||||
return -ENOMEM;
|
||||
|
@ -363,8 +363,8 @@ static int __devinit ad5380_alloc_channels(struct iio_dev *indio_dev)
|
||||
struct iio_chan_spec *channels;
|
||||
unsigned int i;
|
||||
|
||||
channels = kcalloc(sizeof(struct iio_chan_spec),
|
||||
st->chip_info->num_channels, GFP_KERNEL);
|
||||
channels = kcalloc(st->chip_info->num_channels,
|
||||
sizeof(struct iio_chan_spec), GFP_KERNEL);
|
||||
|
||||
if (!channels)
|
||||
return -ENOMEM;
|
||||
|
@ -536,18 +536,7 @@ static struct spi_driver ad5421_driver = {
|
||||
.probe = ad5421_probe,
|
||||
.remove = __devexit_p(ad5421_remove),
|
||||
};
|
||||
|
||||
static __init int ad5421_init(void)
|
||||
{
|
||||
return spi_register_driver(&ad5421_driver);
|
||||
}
|
||||
module_init(ad5421_init);
|
||||
|
||||
static __exit void ad5421_exit(void)
|
||||
{
|
||||
spi_unregister_driver(&ad5421_driver);
|
||||
}
|
||||
module_exit(ad5421_exit);
|
||||
module_spi_driver(ad5421_driver);
|
||||
|
||||
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD5421 DAC");
|
||||
|
@ -149,30 +149,8 @@ static struct attribute *ad5446_attributes[] = {
|
||||
NULL,
|
||||
};
|
||||
|
||||
static umode_t ad5446_attr_is_visible(struct kobject *kobj,
|
||||
struct attribute *attr, int n)
|
||||
{
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct ad5446_state *st = iio_priv(indio_dev);
|
||||
|
||||
umode_t mode = attr->mode;
|
||||
|
||||
if (!st->chip_info->store_pwr_down &&
|
||||
(attr == &iio_dev_attr_out_voltage0_powerdown.dev_attr.attr ||
|
||||
attr == &iio_dev_attr_out_voltage_powerdown_mode.
|
||||
dev_attr.attr ||
|
||||
attr ==
|
||||
&iio_const_attr_out_voltage_powerdown_mode_available.
|
||||
dev_attr.attr))
|
||||
mode = 0;
|
||||
|
||||
return mode;
|
||||
}
|
||||
|
||||
static const struct attribute_group ad5446_attribute_group = {
|
||||
.attrs = ad5446_attributes,
|
||||
.is_visible = ad5446_attr_is_visible,
|
||||
};
|
||||
|
||||
#define AD5446_CHANNEL(bits, storage, shift) { \
|
||||
@ -321,6 +299,12 @@ static const struct iio_info ad5446_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const struct iio_info ad5446_info_no_pwr_down = {
|
||||
.read_raw = ad5446_read_raw,
|
||||
.write_raw = ad5446_write_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int __devinit ad5446_probe(struct spi_device *spi)
|
||||
{
|
||||
struct ad5446_state *st;
|
||||
@ -350,10 +334,13 @@ static int __devinit ad5446_probe(struct spi_device *spi)
|
||||
st->reg = reg;
|
||||
st->spi = spi;
|
||||
|
||||
/* Estabilish that the iio_dev is a child of the spi device */
|
||||
/* Establish that the iio_dev is a child of the spi device */
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->name = spi_get_device_id(spi)->name;
|
||||
indio_dev->info = &ad5446_info;
|
||||
if (st->chip_info->store_pwr_down)
|
||||
indio_dev->info = &ad5446_info;
|
||||
else
|
||||
indio_dev->info = &ad5446_info_no_pwr_down;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = &st->chip_info->channel;
|
||||
indio_dev->num_channels = 1;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user