mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2025-01-17 02:36:21 +00:00
Merge 'akpm' patch series
* Merge akpm patch series: (122 commits) drivers/connector/cn_proc.c: remove unused local Documentation/SubmitChecklist: add RCU debug config options reiserfs: use hweight_long() reiserfs: use proper little-endian bitops pnpacpi: register disabled resources drivers/rtc/rtc-tegra.c: properly initialize spinlock drivers/rtc/rtc-twl.c: check return value of twl_rtc_write_u8() in twl_rtc_set_time() drivers/rtc: add support for Qualcomm PMIC8xxx RTC drivers/rtc/rtc-s3c.c: support clock gating drivers/rtc/rtc-mpc5121.c: add support for RTC on MPC5200 init: skip calibration delay if previously done misc/eeprom: add eeprom access driver for digsy_mtc board misc/eeprom: add driver for microwire 93xx46 EEPROMs checkpatch.pl: update $logFunctions checkpatch: make utf-8 test --strict checkpatch.pl: add ability to ignore various messages checkpatch: add a "prefer __aligned" check checkpatch: validate signature styles and To: and Cc: lines checkpatch: add __rcu as a sparse modifier checkpatch: suggest using min_t or max_t ... Did this as a merge because of (trivial) conflicts in - Documentation/feature-removal-schedule.txt - arch/xtensa/include/asm/uaccess.h that were just easier to fix up in the merge than in the patch series.
This commit is contained in:
commit
45b583b10a
@ -14,7 +14,7 @@ Why: /proc/<pid>/oom_adj allows userspace to influence the oom killer's
|
|||||||
|
|
||||||
A much more powerful interface, /proc/<pid>/oom_score_adj, was
|
A much more powerful interface, /proc/<pid>/oom_score_adj, was
|
||||||
introduced with the oom killer rewrite that allows users to increase or
|
introduced with the oom killer rewrite that allows users to increase or
|
||||||
decrease the badness() score linearly. This interface will replace
|
decrease the badness score linearly. This interface will replace
|
||||||
/proc/<pid>/oom_adj.
|
/proc/<pid>/oom_adj.
|
||||||
|
|
||||||
A warning will be emitted to the kernel log if an application uses this
|
A warning will be emitted to the kernel log if an application uses this
|
||||||
|
21
Documentation/ABI/testing/sysfs-bus-i2c-devices-fsa9480
Normal file
21
Documentation/ABI/testing/sysfs-bus-i2c-devices-fsa9480
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
What: /sys/bus/i2c/devices/.../device
|
||||||
|
Date: February 2011
|
||||||
|
Contact: Minkyu Kang <mk7.kang@samsung.com>
|
||||||
|
Description:
|
||||||
|
show what device is attached
|
||||||
|
NONE - no device
|
||||||
|
USB - USB device is attached
|
||||||
|
UART - UART is attached
|
||||||
|
CHARGER - Charger is attaced
|
||||||
|
JIG - JIG is attached
|
||||||
|
|
||||||
|
What: /sys/bus/i2c/devices/.../switch
|
||||||
|
Date: February 2011
|
||||||
|
Contact: Minkyu Kang <mk7.kang@samsung.com>
|
||||||
|
Description:
|
||||||
|
show or set the state of manual switch
|
||||||
|
VAUDIO - switch to VAUDIO path
|
||||||
|
UART - switch to UART path
|
||||||
|
AUDIO - switch to AUDIO path
|
||||||
|
DHOST - switch to DHOST path
|
||||||
|
AUTO - switch automatically by device
|
@ -53,8 +53,8 @@ kernel patches.
|
|||||||
|
|
||||||
12: Has been tested with CONFIG_PREEMPT, CONFIG_DEBUG_PREEMPT,
|
12: Has been tested with CONFIG_PREEMPT, CONFIG_DEBUG_PREEMPT,
|
||||||
CONFIG_DEBUG_SLAB, CONFIG_DEBUG_PAGEALLOC, CONFIG_DEBUG_MUTEXES,
|
CONFIG_DEBUG_SLAB, CONFIG_DEBUG_PAGEALLOC, CONFIG_DEBUG_MUTEXES,
|
||||||
CONFIG_DEBUG_SPINLOCK, CONFIG_DEBUG_ATOMIC_SLEEP all simultaneously
|
CONFIG_DEBUG_SPINLOCK, CONFIG_DEBUG_ATOMIC_SLEEP, CONFIG_PROVE_RCU
|
||||||
enabled.
|
and CONFIG_DEBUG_OBJECTS_RCU_HEAD all simultaneously enabled.
|
||||||
|
|
||||||
13: Has been build- and runtime tested with and without CONFIG_SMP and
|
13: Has been build- and runtime tested with and without CONFIG_SMP and
|
||||||
CONFIG_PREEMPT.
|
CONFIG_PREEMPT.
|
||||||
|
@ -184,7 +184,7 @@ Why: /proc/<pid>/oom_adj allows userspace to influence the oom killer's
|
|||||||
|
|
||||||
A much more powerful interface, /proc/<pid>/oom_score_adj, was
|
A much more powerful interface, /proc/<pid>/oom_score_adj, was
|
||||||
introduced with the oom killer rewrite that allows users to increase or
|
introduced with the oom killer rewrite that allows users to increase or
|
||||||
decrease the badness() score linearly. This interface will replace
|
decrease the badness score linearly. This interface will replace
|
||||||
/proc/<pid>/oom_adj.
|
/proc/<pid>/oom_adj.
|
||||||
|
|
||||||
A warning will be emitted to the kernel log if an application uses this
|
A warning will be emitted to the kernel log if an application uses this
|
||||||
@ -518,22 +518,6 @@ Files: net/netfilter/xt_connlimit.c
|
|||||||
|
|
||||||
----------------------------
|
----------------------------
|
||||||
|
|
||||||
What: noswapaccount kernel command line parameter
|
|
||||||
When: 3.0
|
|
||||||
Why: The original implementation of memsw feature enabled by
|
|
||||||
CONFIG_CGROUP_MEM_RES_CTLR_SWAP could be disabled by the noswapaccount
|
|
||||||
kernel parameter (introduced in 2.6.29-rc1). Later on, this decision
|
|
||||||
turned out to be not ideal because we cannot have the feature compiled
|
|
||||||
in and disabled by default and let only interested to enable it
|
|
||||||
(e.g. general distribution kernels might need it). Therefore we have
|
|
||||||
added swapaccount[=0|1] parameter (introduced in 2.6.37) which provides
|
|
||||||
the both possibilities. If we remove noswapaccount we will have
|
|
||||||
less command line parameters with the same functionality and we
|
|
||||||
can also cleanup the parameter handling a bit ().
|
|
||||||
Who: Michal Hocko <mhocko@suse.cz>
|
|
||||||
|
|
||||||
----------------------------
|
|
||||||
|
|
||||||
What: ipt_addrtype match include file
|
What: ipt_addrtype match include file
|
||||||
When: 2012
|
When: 2012
|
||||||
Why: superseded by xt_addrtype
|
Why: superseded by xt_addrtype
|
||||||
|
@ -3012,7 +3012,7 @@ F: kernel/hrtimer.c
|
|||||||
F: kernel/time/clockevents.c
|
F: kernel/time/clockevents.c
|
||||||
F: kernel/time/tick*.*
|
F: kernel/time/tick*.*
|
||||||
F: kernel/time/timer_*.c
|
F: kernel/time/timer_*.c
|
||||||
F: include/linux/clockevents.h
|
F: include/linux/clockchips.h
|
||||||
F: include/linux/hrtimer.h
|
F: include/linux/hrtimer.h
|
||||||
|
|
||||||
HIGH-SPEED SCC DRIVER FOR AX.25
|
HIGH-SPEED SCC DRIVER FOR AX.25
|
||||||
|
@ -200,7 +200,6 @@ show_regs(struct pt_regs *regs)
|
|||||||
void
|
void
|
||||||
start_thread(struct pt_regs * regs, unsigned long pc, unsigned long sp)
|
start_thread(struct pt_regs * regs, unsigned long pc, unsigned long sp)
|
||||||
{
|
{
|
||||||
set_fs(USER_DS);
|
|
||||||
regs->pc = pc;
|
regs->pc = pc;
|
||||||
regs->ps = 8;
|
regs->ps = 8;
|
||||||
wrusp(sp);
|
wrusp(sp);
|
||||||
|
@ -10,16 +10,97 @@
|
|||||||
#include <linux/amba/bus.h>
|
#include <linux/amba/bus.h>
|
||||||
#include <linux/gpio.h>
|
#include <linux/gpio.h>
|
||||||
#include <linux/irq.h>
|
#include <linux/irq.h>
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
|
||||||
#include <asm/mach/arch.h>
|
#include <asm/mach/arch.h>
|
||||||
#include <asm/mach-types.h>
|
#include <asm/mach-types.h>
|
||||||
|
|
||||||
|
#include <plat/pincfg.h>
|
||||||
|
#include <plat/i2c.h>
|
||||||
|
|
||||||
#include <mach/hardware.h>
|
#include <mach/hardware.h>
|
||||||
#include <mach/devices.h>
|
#include <mach/devices.h>
|
||||||
#include <mach/setup.h>
|
#include <mach/setup.h>
|
||||||
|
|
||||||
|
#include "pins-db5500.h"
|
||||||
#include "devices-db5500.h"
|
#include "devices-db5500.h"
|
||||||
|
#include <linux/led-lm3530.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* GPIO
|
||||||
|
*/
|
||||||
|
|
||||||
|
static pin_cfg_t u5500_pins[] = {
|
||||||
|
/* I2C */
|
||||||
|
GPIO218_I2C2_SCL | PIN_INPUT_PULLUP,
|
||||||
|
GPIO219_I2C2_SDA | PIN_INPUT_PULLUP,
|
||||||
|
|
||||||
|
/* DISPLAY_ENABLE */
|
||||||
|
GPIO226_GPIO | PIN_OUTPUT_LOW,
|
||||||
|
|
||||||
|
/* Backlight Enbale */
|
||||||
|
GPIO224_GPIO | PIN_OUTPUT_HIGH,
|
||||||
|
};
|
||||||
|
/*
|
||||||
|
* I2C
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define U5500_I2C_CONTROLLER(id, _slsu, _tft, _rft, clk, _sm) \
|
||||||
|
static struct nmk_i2c_controller u5500_i2c##id##_data = { \
|
||||||
|
/* \
|
||||||
|
* slave data setup time, which is \
|
||||||
|
* 250 ns,100ns,10ns which is 14,6,2 \
|
||||||
|
* respectively for a 48 Mhz \
|
||||||
|
* i2c clock \
|
||||||
|
*/ \
|
||||||
|
.slsu = _slsu, \
|
||||||
|
/* Tx FIFO threshold */ \
|
||||||
|
.tft = _tft, \
|
||||||
|
/* Rx FIFO threshold */ \
|
||||||
|
.rft = _rft, \
|
||||||
|
/* std. mode operation */ \
|
||||||
|
.clk_freq = clk, \
|
||||||
|
.sm = _sm, \
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* The board uses TODO <3> i2c controllers, initialize all of
|
||||||
|
* them with slave data setup time of 250 ns,
|
||||||
|
* Tx & Rx FIFO threshold values as 1 and standard
|
||||||
|
* mode of operation
|
||||||
|
*/
|
||||||
|
|
||||||
|
U5500_I2C_CONTROLLER(2, 0xe, 1, 1, 400000, I2C_FREQ_MODE_FAST);
|
||||||
|
|
||||||
|
static struct lm3530_platform_data u5500_als_platform_data = {
|
||||||
|
.mode = LM3530_BL_MODE_MANUAL,
|
||||||
|
.als_input_mode = LM3530_INPUT_ALS1,
|
||||||
|
.max_current = LM3530_FS_CURR_26mA,
|
||||||
|
.pwm_pol_hi = true,
|
||||||
|
.als_avrg_time = LM3530_ALS_AVRG_TIME_512ms,
|
||||||
|
.brt_ramp_law = 1, /* Linear */
|
||||||
|
.brt_ramp_fall = LM3530_RAMP_TIME_8s,
|
||||||
|
.brt_ramp_rise = LM3530_RAMP_TIME_8s,
|
||||||
|
.als1_resistor_sel = LM3530_ALS_IMPD_13_53kOhm,
|
||||||
|
.als2_resistor_sel = LM3530_ALS_IMPD_Z,
|
||||||
|
.als_vmin = 730, /* mV */
|
||||||
|
.als_vmax = 1020, /* mV */
|
||||||
|
.brt_val = 0x7F, /* Max brightness */
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static struct i2c_board_info __initdata u5500_i2c2_devices[] = {
|
||||||
|
{
|
||||||
|
/* Backlight */
|
||||||
|
I2C_BOARD_INFO("lm3530-led", 0x36),
|
||||||
|
.platform_data = &u5500_als_platform_data,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static void __init u5500_i2c_init(void)
|
||||||
|
{
|
||||||
|
db5500_add_i2c2(&u5500_i2c2_data);
|
||||||
|
i2c_register_board_info(2, ARRAY_AND_SIZE(u5500_i2c2_devices));
|
||||||
|
}
|
||||||
static void __init u5500_uart_init(void)
|
static void __init u5500_uart_init(void)
|
||||||
{
|
{
|
||||||
db5500_add_uart0(NULL);
|
db5500_add_uart0(NULL);
|
||||||
@ -30,7 +111,8 @@ static void __init u5500_uart_init(void)
|
|||||||
static void __init u5500_init_machine(void)
|
static void __init u5500_init_machine(void)
|
||||||
{
|
{
|
||||||
u5500_init_devices();
|
u5500_init_devices();
|
||||||
|
nmk_config_pins(u5500_pins, ARRAY_SIZE(u5500_pins));
|
||||||
|
u5500_i2c_init();
|
||||||
u5500_sdi_init();
|
u5500_sdi_init();
|
||||||
u5500_uart_init();
|
u5500_uart_init();
|
||||||
}
|
}
|
||||||
|
@ -85,6 +85,8 @@ crisv32_pinmux_alloc_fixed(enum fixed_function function)
|
|||||||
int ret = -EINVAL;
|
int ret = -EINVAL;
|
||||||
char saved[sizeof pins];
|
char saved[sizeof pins];
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
reg_pinmux_rw_hwprot hwprot;
|
||||||
|
reg_clkgen_rw_clk_ctrl clk_ctrl;
|
||||||
|
|
||||||
spin_lock_irqsave(&pinmux_lock, flags);
|
spin_lock_irqsave(&pinmux_lock, flags);
|
||||||
|
|
||||||
@ -93,9 +95,8 @@ crisv32_pinmux_alloc_fixed(enum fixed_function function)
|
|||||||
|
|
||||||
crisv32_pinmux_init(); /* must be done before we read rw_hwprot */
|
crisv32_pinmux_init(); /* must be done before we read rw_hwprot */
|
||||||
|
|
||||||
reg_pinmux_rw_hwprot hwprot = REG_RD(pinmux, regi_pinmux, rw_hwprot);
|
hwprot = REG_RD(pinmux, regi_pinmux, rw_hwprot);
|
||||||
reg_clkgen_rw_clk_ctrl clk_ctrl = REG_RD(clkgen, regi_clkgen,
|
clk_ctrl = REG_RD(clkgen, regi_clkgen, rw_clk_ctrl);
|
||||||
rw_clk_ctrl);
|
|
||||||
|
|
||||||
switch (function) {
|
switch (function) {
|
||||||
case pinmux_eth:
|
case pinmux_eth:
|
||||||
@ -262,6 +263,7 @@ crisv32_pinmux_dealloc_fixed(enum fixed_function function)
|
|||||||
int ret = -EINVAL;
|
int ret = -EINVAL;
|
||||||
char saved[sizeof pins];
|
char saved[sizeof pins];
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
reg_pinmux_rw_hwprot hwprot;
|
||||||
|
|
||||||
spin_lock_irqsave(&pinmux_lock, flags);
|
spin_lock_irqsave(&pinmux_lock, flags);
|
||||||
|
|
||||||
@ -270,7 +272,7 @@ crisv32_pinmux_dealloc_fixed(enum fixed_function function)
|
|||||||
|
|
||||||
crisv32_pinmux_init(); /* must be done before we read rw_hwprot */
|
crisv32_pinmux_init(); /* must be done before we read rw_hwprot */
|
||||||
|
|
||||||
reg_pinmux_rw_hwprot hwprot = REG_RD(pinmux, regi_pinmux, rw_hwprot);
|
hwprot = REG_RD(pinmux, regi_pinmux, rw_hwprot);
|
||||||
|
|
||||||
switch (function) {
|
switch (function) {
|
||||||
case pinmux_eth:
|
case pinmux_eth:
|
||||||
|
@ -53,7 +53,6 @@ struct thread_struct {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#define start_thread(regs, ip, usp) do { \
|
#define start_thread(regs, ip, usp) do { \
|
||||||
set_fs(USER_DS); \
|
|
||||||
regs->irp = ip; \
|
regs->irp = ip; \
|
||||||
regs->dccr |= 1 << U_DCCR_BITNR; \
|
regs->dccr |= 1 << U_DCCR_BITNR; \
|
||||||
wrusp(usp); \
|
wrusp(usp); \
|
||||||
|
@ -47,7 +47,6 @@ struct thread_struct {
|
|||||||
*/
|
*/
|
||||||
#define start_thread(regs, ip, usp) \
|
#define start_thread(regs, ip, usp) \
|
||||||
do { \
|
do { \
|
||||||
set_fs(USER_DS); \
|
|
||||||
regs->erp = ip; \
|
regs->erp = ip; \
|
||||||
regs->ccs |= 1 << (U_CCS_BITNR + CCS_SHIFT); \
|
regs->ccs |= 1 << (U_CCS_BITNR + CCS_SHIFT); \
|
||||||
wrusp(usp); \
|
wrusp(usp); \
|
||||||
|
@ -81,7 +81,6 @@ struct thread_struct {
|
|||||||
#if defined(__H8300H__)
|
#if defined(__H8300H__)
|
||||||
#define start_thread(_regs, _pc, _usp) \
|
#define start_thread(_regs, _pc, _usp) \
|
||||||
do { \
|
do { \
|
||||||
set_fs(USER_DS); /* reads from user space */ \
|
|
||||||
(_regs)->pc = (_pc); \
|
(_regs)->pc = (_pc); \
|
||||||
(_regs)->ccr = 0x00; /* clear all flags */ \
|
(_regs)->ccr = 0x00; /* clear all flags */ \
|
||||||
(_regs)->er5 = current->mm->start_data; /* GOT base */ \
|
(_regs)->er5 = current->mm->start_data; /* GOT base */ \
|
||||||
@ -91,7 +90,6 @@ do { \
|
|||||||
#if defined(__H8300S__)
|
#if defined(__H8300S__)
|
||||||
#define start_thread(_regs, _pc, _usp) \
|
#define start_thread(_regs, _pc, _usp) \
|
||||||
do { \
|
do { \
|
||||||
set_fs(USER_DS); /* reads from user space */ \
|
|
||||||
(_regs)->pc = (_pc); \
|
(_regs)->pc = (_pc); \
|
||||||
(_regs)->ccr = 0x00; /* clear kernel flag */ \
|
(_regs)->ccr = 0x00; /* clear kernel flag */ \
|
||||||
(_regs)->exr = 0x78; /* enable all interrupts */ \
|
(_regs)->exr = 0x78; /* enable all interrupts */ \
|
||||||
|
@ -106,7 +106,6 @@ struct thread_struct {
|
|||||||
|
|
||||||
#define start_thread(regs, new_pc, new_spu) \
|
#define start_thread(regs, new_pc, new_spu) \
|
||||||
do { \
|
do { \
|
||||||
set_fs(USER_DS); \
|
|
||||||
regs->psw = (regs->psw | USERPS_BPSW) & 0x0000FFFFUL; \
|
regs->psw = (regs->psw | USERPS_BPSW) & 0x0000FFFFUL; \
|
||||||
regs->bpc = new_pc; \
|
regs->bpc = new_pc; \
|
||||||
regs->spu = new_spu; \
|
regs->spu = new_spu; \
|
||||||
|
@ -105,9 +105,6 @@ struct thread_struct {
|
|||||||
static inline void start_thread(struct pt_regs * regs, unsigned long pc,
|
static inline void start_thread(struct pt_regs * regs, unsigned long pc,
|
||||||
unsigned long usp)
|
unsigned long usp)
|
||||||
{
|
{
|
||||||
/* reads from user space */
|
|
||||||
set_fs(USER_DS);
|
|
||||||
|
|
||||||
regs->pc = pc;
|
regs->pc = pc;
|
||||||
regs->sr &= ~0x2000;
|
regs->sr &= ~0x2000;
|
||||||
wrusp(usp);
|
wrusp(usp);
|
||||||
@ -129,7 +126,6 @@ extern int handle_kernel_fault(struct pt_regs *regs);
|
|||||||
|
|
||||||
#define start_thread(_regs, _pc, _usp) \
|
#define start_thread(_regs, _pc, _usp) \
|
||||||
do { \
|
do { \
|
||||||
set_fs(USER_DS); /* reads from user space */ \
|
|
||||||
(_regs)->pc = (_pc); \
|
(_regs)->pc = (_pc); \
|
||||||
((struct switch_stack *)(_regs))[-1].a6 = 0; \
|
((struct switch_stack *)(_regs))[-1].a6 = 0; \
|
||||||
reformat(_regs); \
|
reformat(_regs); \
|
||||||
|
@ -185,7 +185,7 @@ EXPORT_SYMBOL(kernel_thread);
|
|||||||
void flush_thread(void)
|
void flush_thread(void)
|
||||||
{
|
{
|
||||||
unsigned long zero = 0;
|
unsigned long zero = 0;
|
||||||
set_fs(USER_DS);
|
|
||||||
current->thread.fs = __USER_DS;
|
current->thread.fs = __USER_DS;
|
||||||
if (!FPU_IS_EMU)
|
if (!FPU_IS_EMU)
|
||||||
asm volatile (".chip 68k/68881\n\t"
|
asm volatile (".chip 68k/68881\n\t"
|
||||||
|
@ -158,7 +158,7 @@ void flush_thread(void)
|
|||||||
#ifdef CONFIG_FPU
|
#ifdef CONFIG_FPU
|
||||||
unsigned long zero = 0;
|
unsigned long zero = 0;
|
||||||
#endif
|
#endif
|
||||||
set_fs(USER_DS);
|
|
||||||
current->thread.fs = __USER_DS;
|
current->thread.fs = __USER_DS;
|
||||||
#ifdef CONFIG_FPU
|
#ifdef CONFIG_FPU
|
||||||
if (!FPU_IS_EMU)
|
if (!FPU_IS_EMU)
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
#include <linux/cpumask.h>
|
#include <linux/cpumask.h>
|
||||||
#include <linux/memblock.h>
|
#include <linux/memblock.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
#include <linux/reboot.h>
|
||||||
|
|
||||||
#include <asm/prom.h>
|
#include <asm/prom.h>
|
||||||
#include <asm/rtas.h>
|
#include <asm/rtas.h>
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/proc_fs.h>
|
#include <linux/proc_fs.h>
|
||||||
|
#include <linux/reboot.h>
|
||||||
#include <asm/delay.h>
|
#include <asm/delay.h>
|
||||||
#include <asm/uaccess.h>
|
#include <asm/uaccess.h>
|
||||||
#include <asm/rtas.h>
|
#include <asm/rtas.h>
|
||||||
|
@ -41,6 +41,7 @@ config SPARC64
|
|||||||
select HAVE_FUNCTION_TRACE_MCOUNT_TEST
|
select HAVE_FUNCTION_TRACE_MCOUNT_TEST
|
||||||
select HAVE_KRETPROBES
|
select HAVE_KRETPROBES
|
||||||
select HAVE_KPROBES
|
select HAVE_KPROBES
|
||||||
|
select HAVE_RCU_TABLE_FREE if SMP
|
||||||
select HAVE_MEMBLOCK
|
select HAVE_MEMBLOCK
|
||||||
select HAVE_SYSCALL_WRAPPERS
|
select HAVE_SYSCALL_WRAPPERS
|
||||||
select HAVE_DYNAMIC_FTRACE
|
select HAVE_DYNAMIC_FTRACE
|
||||||
@ -81,10 +82,6 @@ config IOMMU_HELPER
|
|||||||
bool
|
bool
|
||||||
default y if SPARC64
|
default y if SPARC64
|
||||||
|
|
||||||
config QUICKLIST
|
|
||||||
bool
|
|
||||||
default y if SPARC64
|
|
||||||
|
|
||||||
config STACKTRACE_SUPPORT
|
config STACKTRACE_SUPPORT
|
||||||
bool
|
bool
|
||||||
default y if SPARC64
|
default y if SPARC64
|
||||||
|
@ -5,7 +5,6 @@
|
|||||||
#include <linux/sched.h>
|
#include <linux/sched.h>
|
||||||
#include <linux/mm.h>
|
#include <linux/mm.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/quicklist.h>
|
|
||||||
|
|
||||||
#include <asm/spitfire.h>
|
#include <asm/spitfire.h>
|
||||||
#include <asm/cpudata.h>
|
#include <asm/cpudata.h>
|
||||||
@ -14,71 +13,114 @@
|
|||||||
|
|
||||||
/* Page table allocation/freeing. */
|
/* Page table allocation/freeing. */
|
||||||
|
|
||||||
|
extern struct kmem_cache *pgtable_cache;
|
||||||
|
|
||||||
static inline pgd_t *pgd_alloc(struct mm_struct *mm)
|
static inline pgd_t *pgd_alloc(struct mm_struct *mm)
|
||||||
{
|
{
|
||||||
return quicklist_alloc(0, GFP_KERNEL, NULL);
|
return kmem_cache_alloc(pgtable_cache, GFP_KERNEL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void pgd_free(struct mm_struct *mm, pgd_t *pgd)
|
static inline void pgd_free(struct mm_struct *mm, pgd_t *pgd)
|
||||||
{
|
{
|
||||||
quicklist_free(0, NULL, pgd);
|
kmem_cache_free(pgtable_cache, pgd);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define pud_populate(MM, PUD, PMD) pud_set(PUD, PMD)
|
#define pud_populate(MM, PUD, PMD) pud_set(PUD, PMD)
|
||||||
|
|
||||||
static inline pmd_t *pmd_alloc_one(struct mm_struct *mm, unsigned long addr)
|
static inline pmd_t *pmd_alloc_one(struct mm_struct *mm, unsigned long addr)
|
||||||
{
|
{
|
||||||
return quicklist_alloc(0, GFP_KERNEL, NULL);
|
return kmem_cache_alloc(pgtable_cache,
|
||||||
|
GFP_KERNEL|__GFP_REPEAT);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void pmd_free(struct mm_struct *mm, pmd_t *pmd)
|
static inline void pmd_free(struct mm_struct *mm, pmd_t *pmd)
|
||||||
{
|
{
|
||||||
quicklist_free(0, NULL, pmd);
|
kmem_cache_free(pgtable_cache, pmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline pte_t *pte_alloc_one_kernel(struct mm_struct *mm,
|
static inline pte_t *pte_alloc_one_kernel(struct mm_struct *mm,
|
||||||
unsigned long address)
|
unsigned long address)
|
||||||
{
|
{
|
||||||
return quicklist_alloc(0, GFP_KERNEL, NULL);
|
return (pte_t *)__get_free_page(GFP_KERNEL | __GFP_REPEAT | __GFP_ZERO);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline pgtable_t pte_alloc_one(struct mm_struct *mm,
|
static inline pgtable_t pte_alloc_one(struct mm_struct *mm,
|
||||||
unsigned long address)
|
unsigned long address)
|
||||||
{
|
{
|
||||||
struct page *page;
|
struct page *page;
|
||||||
void *pg;
|
pte_t *pte;
|
||||||
|
|
||||||
pg = quicklist_alloc(0, GFP_KERNEL, NULL);
|
pte = pte_alloc_one_kernel(mm, address);
|
||||||
if (!pg)
|
if (!pte)
|
||||||
return NULL;
|
return NULL;
|
||||||
page = virt_to_page(pg);
|
page = virt_to_page(pte);
|
||||||
pgtable_page_ctor(page);
|
pgtable_page_ctor(page);
|
||||||
return page;
|
return page;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void pte_free_kernel(struct mm_struct *mm, pte_t *pte)
|
static inline void pte_free_kernel(struct mm_struct *mm, pte_t *pte)
|
||||||
{
|
{
|
||||||
quicklist_free(0, NULL, pte);
|
free_page((unsigned long)pte);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void pte_free(struct mm_struct *mm, pgtable_t ptepage)
|
static inline void pte_free(struct mm_struct *mm, pgtable_t ptepage)
|
||||||
{
|
{
|
||||||
pgtable_page_dtor(ptepage);
|
pgtable_page_dtor(ptepage);
|
||||||
quicklist_free_page(0, NULL, ptepage);
|
__free_page(ptepage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#define pmd_populate_kernel(MM, PMD, PTE) pmd_set(PMD, PTE)
|
#define pmd_populate_kernel(MM, PMD, PTE) pmd_set(PMD, PTE)
|
||||||
#define pmd_populate(MM,PMD,PTE_PAGE) \
|
#define pmd_populate(MM,PMD,PTE_PAGE) \
|
||||||
pmd_populate_kernel(MM,PMD,page_address(PTE_PAGE))
|
pmd_populate_kernel(MM,PMD,page_address(PTE_PAGE))
|
||||||
#define pmd_pgtable(pmd) pmd_page(pmd)
|
#define pmd_pgtable(pmd) pmd_page(pmd)
|
||||||
|
|
||||||
static inline void check_pgt_cache(void)
|
#define check_pgt_cache() do { } while (0)
|
||||||
|
|
||||||
|
static inline void pgtable_free(void *table, bool is_page)
|
||||||
{
|
{
|
||||||
quicklist_trim(0, NULL, 25, 16);
|
if (is_page)
|
||||||
|
free_page((unsigned long)table);
|
||||||
|
else
|
||||||
|
kmem_cache_free(pgtable_cache, table);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define __pte_free_tlb(tlb, pte, addr) pte_free((tlb)->mm, pte)
|
#ifdef CONFIG_SMP
|
||||||
#define __pmd_free_tlb(tlb, pmd, addr) pmd_free((tlb)->mm, pmd)
|
|
||||||
|
struct mmu_gather;
|
||||||
|
extern void tlb_remove_table(struct mmu_gather *, void *);
|
||||||
|
|
||||||
|
static inline void pgtable_free_tlb(struct mmu_gather *tlb, void *table, bool is_page)
|
||||||
|
{
|
||||||
|
unsigned long pgf = (unsigned long)table;
|
||||||
|
if (is_page)
|
||||||
|
pgf |= 0x1UL;
|
||||||
|
tlb_remove_table(tlb, (void *)pgf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void __tlb_remove_table(void *_table)
|
||||||
|
{
|
||||||
|
void *table = (void *)((unsigned long)_table & ~0x1UL);
|
||||||
|
bool is_page = false;
|
||||||
|
|
||||||
|
if ((unsigned long)_table & 0x1UL)
|
||||||
|
is_page = true;
|
||||||
|
pgtable_free(table, is_page);
|
||||||
|
}
|
||||||
|
#else /* CONFIG_SMP */
|
||||||
|
static inline void pgtable_free_tlb(struct mmu_gather *tlb, void *table, bool is_page)
|
||||||
|
{
|
||||||
|
pgtable_free(table, is_page);
|
||||||
|
}
|
||||||
|
#endif /* !CONFIG_SMP */
|
||||||
|
|
||||||
|
static inline void __pte_free_tlb(struct mmu_gather *tlb, struct page *ptepage,
|
||||||
|
unsigned long address)
|
||||||
|
{
|
||||||
|
pgtable_page_dtor(ptepage);
|
||||||
|
pgtable_free_tlb(tlb, page_address(ptepage), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define __pmd_free_tlb(tlb, pmd, addr) \
|
||||||
|
pgtable_free_tlb(tlb, pmd, false)
|
||||||
|
|
||||||
#endif /* _SPARC64_PGALLOC_H */
|
#endif /* _SPARC64_PGALLOC_H */
|
||||||
|
@ -95,6 +95,10 @@
|
|||||||
/* PTE bits which are the same in SUN4U and SUN4V format. */
|
/* PTE bits which are the same in SUN4U and SUN4V format. */
|
||||||
#define _PAGE_VALID _AC(0x8000000000000000,UL) /* Valid TTE */
|
#define _PAGE_VALID _AC(0x8000000000000000,UL) /* Valid TTE */
|
||||||
#define _PAGE_R _AC(0x8000000000000000,UL) /* Keep ref bit uptodate*/
|
#define _PAGE_R _AC(0x8000000000000000,UL) /* Keep ref bit uptodate*/
|
||||||
|
#define _PAGE_SPECIAL _AC(0x0200000000000000,UL) /* Special page */
|
||||||
|
|
||||||
|
/* Advertise support for _PAGE_SPECIAL */
|
||||||
|
#define __HAVE_ARCH_PTE_SPECIAL
|
||||||
|
|
||||||
/* SUN4U pte bits... */
|
/* SUN4U pte bits... */
|
||||||
#define _PAGE_SZ4MB_4U _AC(0x6000000000000000,UL) /* 4MB Page */
|
#define _PAGE_SZ4MB_4U _AC(0x6000000000000000,UL) /* 4MB Page */
|
||||||
@ -104,6 +108,7 @@
|
|||||||
#define _PAGE_NFO_4U _AC(0x1000000000000000,UL) /* No Fault Only */
|
#define _PAGE_NFO_4U _AC(0x1000000000000000,UL) /* No Fault Only */
|
||||||
#define _PAGE_IE_4U _AC(0x0800000000000000,UL) /* Invert Endianness */
|
#define _PAGE_IE_4U _AC(0x0800000000000000,UL) /* Invert Endianness */
|
||||||
#define _PAGE_SOFT2_4U _AC(0x07FC000000000000,UL) /* Software bits, set 2 */
|
#define _PAGE_SOFT2_4U _AC(0x07FC000000000000,UL) /* Software bits, set 2 */
|
||||||
|
#define _PAGE_SPECIAL_4U _AC(0x0200000000000000,UL) /* Special page */
|
||||||
#define _PAGE_RES1_4U _AC(0x0002000000000000,UL) /* Reserved */
|
#define _PAGE_RES1_4U _AC(0x0002000000000000,UL) /* Reserved */
|
||||||
#define _PAGE_SZ32MB_4U _AC(0x0001000000000000,UL) /* (Panther) 32MB page */
|
#define _PAGE_SZ32MB_4U _AC(0x0001000000000000,UL) /* (Panther) 32MB page */
|
||||||
#define _PAGE_SZ256MB_4U _AC(0x2001000000000000,UL) /* (Panther) 256MB page */
|
#define _PAGE_SZ256MB_4U _AC(0x2001000000000000,UL) /* (Panther) 256MB page */
|
||||||
@ -133,6 +138,7 @@
|
|||||||
#define _PAGE_ACCESSED_4V _AC(0x1000000000000000,UL) /* Accessed (ref'd) */
|
#define _PAGE_ACCESSED_4V _AC(0x1000000000000000,UL) /* Accessed (ref'd) */
|
||||||
#define _PAGE_READ_4V _AC(0x0800000000000000,UL) /* Readable SW Bit */
|
#define _PAGE_READ_4V _AC(0x0800000000000000,UL) /* Readable SW Bit */
|
||||||
#define _PAGE_WRITE_4V _AC(0x0400000000000000,UL) /* Writable SW Bit */
|
#define _PAGE_WRITE_4V _AC(0x0400000000000000,UL) /* Writable SW Bit */
|
||||||
|
#define _PAGE_SPECIAL_4V _AC(0x0200000000000000,UL) /* Special page */
|
||||||
#define _PAGE_PADDR_4V _AC(0x00FFFFFFFFFFE000,UL) /* paddr[55:13] */
|
#define _PAGE_PADDR_4V _AC(0x00FFFFFFFFFFE000,UL) /* paddr[55:13] */
|
||||||
#define _PAGE_IE_4V _AC(0x0000000000001000,UL) /* Invert Endianness */
|
#define _PAGE_IE_4V _AC(0x0000000000001000,UL) /* Invert Endianness */
|
||||||
#define _PAGE_E_4V _AC(0x0000000000000800,UL) /* side-Effect */
|
#define _PAGE_E_4V _AC(0x0000000000000800,UL) /* side-Effect */
|
||||||
@ -302,10 +308,10 @@ static inline pte_t pte_modify(pte_t pte, pgprot_t prot)
|
|||||||
: "=r" (mask), "=r" (tmp)
|
: "=r" (mask), "=r" (tmp)
|
||||||
: "i" (_PAGE_PADDR_4U | _PAGE_MODIFIED_4U | _PAGE_ACCESSED_4U |
|
: "i" (_PAGE_PADDR_4U | _PAGE_MODIFIED_4U | _PAGE_ACCESSED_4U |
|
||||||
_PAGE_CP_4U | _PAGE_CV_4U | _PAGE_E_4U | _PAGE_PRESENT_4U |
|
_PAGE_CP_4U | _PAGE_CV_4U | _PAGE_E_4U | _PAGE_PRESENT_4U |
|
||||||
_PAGE_SZBITS_4U),
|
_PAGE_SZBITS_4U | _PAGE_SPECIAL),
|
||||||
"i" (_PAGE_PADDR_4V | _PAGE_MODIFIED_4V | _PAGE_ACCESSED_4V |
|
"i" (_PAGE_PADDR_4V | _PAGE_MODIFIED_4V | _PAGE_ACCESSED_4V |
|
||||||
_PAGE_CP_4V | _PAGE_CV_4V | _PAGE_E_4V | _PAGE_PRESENT_4V |
|
_PAGE_CP_4V | _PAGE_CV_4V | _PAGE_E_4V | _PAGE_PRESENT_4V |
|
||||||
_PAGE_SZBITS_4V));
|
_PAGE_SZBITS_4V | _PAGE_SPECIAL));
|
||||||
|
|
||||||
return __pte((pte_val(pte) & mask) | (pgprot_val(prot) & ~mask));
|
return __pte((pte_val(pte) & mask) | (pgprot_val(prot) & ~mask));
|
||||||
}
|
}
|
||||||
@ -502,6 +508,7 @@ static inline pte_t pte_mkyoung(pte_t pte)
|
|||||||
|
|
||||||
static inline pte_t pte_mkspecial(pte_t pte)
|
static inline pte_t pte_mkspecial(pte_t pte)
|
||||||
{
|
{
|
||||||
|
pte_val(pte) |= _PAGE_SPECIAL;
|
||||||
return pte;
|
return pte;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -607,9 +614,9 @@ static inline unsigned long pte_present(pte_t pte)
|
|||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int pte_special(pte_t pte)
|
static inline unsigned long pte_special(pte_t pte)
|
||||||
{
|
{
|
||||||
return 0;
|
return pte_val(pte) & _PAGE_SPECIAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define pmd_set(pmdp, ptep) \
|
#define pmd_set(pmdp, ptep) \
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
asflags-y := -ansi
|
asflags-y := -ansi
|
||||||
ccflags-y := -Werror
|
ccflags-y := -Werror
|
||||||
|
|
||||||
obj-$(CONFIG_SPARC64) += ultra.o tlb.o tsb.o
|
obj-$(CONFIG_SPARC64) += ultra.o tlb.o tsb.o gup.o
|
||||||
obj-y += fault_$(BITS).o
|
obj-y += fault_$(BITS).o
|
||||||
obj-y += init_$(BITS).o
|
obj-y += init_$(BITS).o
|
||||||
obj-$(CONFIG_SPARC32) += loadmmu.o
|
obj-$(CONFIG_SPARC32) += loadmmu.o
|
||||||
|
181
arch/sparc/mm/gup.c
Normal file
181
arch/sparc/mm/gup.c
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
/*
|
||||||
|
* Lockless get_user_pages_fast for sparc, cribbed from powerpc
|
||||||
|
*
|
||||||
|
* Copyright (C) 2008 Nick Piggin
|
||||||
|
* Copyright (C) 2008 Novell Inc.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <linux/mm.h>
|
||||||
|
#include <linux/vmstat.h>
|
||||||
|
#include <linux/pagemap.h>
|
||||||
|
#include <linux/rwsem.h>
|
||||||
|
#include <asm/pgtable.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The performance critical leaf functions are made noinline otherwise gcc
|
||||||
|
* inlines everything into a single function which results in too much
|
||||||
|
* register pressure.
|
||||||
|
*/
|
||||||
|
static noinline int gup_pte_range(pmd_t pmd, unsigned long addr,
|
||||||
|
unsigned long end, int write, struct page **pages, int *nr)
|
||||||
|
{
|
||||||
|
unsigned long mask, result;
|
||||||
|
pte_t *ptep;
|
||||||
|
|
||||||
|
if (tlb_type == hypervisor) {
|
||||||
|
result = _PAGE_PRESENT_4V|_PAGE_P_4V;
|
||||||
|
if (write)
|
||||||
|
result |= _PAGE_WRITE_4V;
|
||||||
|
} else {
|
||||||
|
result = _PAGE_PRESENT_4U|_PAGE_P_4U;
|
||||||
|
if (write)
|
||||||
|
result |= _PAGE_WRITE_4U;
|
||||||
|
}
|
||||||
|
mask = result | _PAGE_SPECIAL;
|
||||||
|
|
||||||
|
ptep = pte_offset_kernel(&pmd, addr);
|
||||||
|
do {
|
||||||
|
struct page *page, *head;
|
||||||
|
pte_t pte = *ptep;
|
||||||
|
|
||||||
|
if ((pte_val(pte) & mask) != result)
|
||||||
|
return 0;
|
||||||
|
VM_BUG_ON(!pfn_valid(pte_pfn(pte)));
|
||||||
|
|
||||||
|
/* The hugepage case is simplified on sparc64 because
|
||||||
|
* we encode the sub-page pfn offsets into the
|
||||||
|
* hugepage PTEs. We could optimize this in the future
|
||||||
|
* use page_cache_add_speculative() for the hugepage case.
|
||||||
|
*/
|
||||||
|
page = pte_page(pte);
|
||||||
|
head = compound_head(page);
|
||||||
|
if (!page_cache_get_speculative(head))
|
||||||
|
return 0;
|
||||||
|
if (unlikely(pte_val(pte) != pte_val(*ptep))) {
|
||||||
|
put_page(head);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pages[*nr] = page;
|
||||||
|
(*nr)++;
|
||||||
|
} while (ptep++, addr += PAGE_SIZE, addr != end);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int gup_pmd_range(pud_t pud, unsigned long addr, unsigned long end,
|
||||||
|
int write, struct page **pages, int *nr)
|
||||||
|
{
|
||||||
|
unsigned long next;
|
||||||
|
pmd_t *pmdp;
|
||||||
|
|
||||||
|
pmdp = pmd_offset(&pud, addr);
|
||||||
|
do {
|
||||||
|
pmd_t pmd = *pmdp;
|
||||||
|
|
||||||
|
next = pmd_addr_end(addr, end);
|
||||||
|
if (pmd_none(pmd))
|
||||||
|
return 0;
|
||||||
|
if (!gup_pte_range(pmd, addr, next, write, pages, nr))
|
||||||
|
return 0;
|
||||||
|
} while (pmdp++, addr = next, addr != end);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int gup_pud_range(pgd_t pgd, unsigned long addr, unsigned long end,
|
||||||
|
int write, struct page **pages, int *nr)
|
||||||
|
{
|
||||||
|
unsigned long next;
|
||||||
|
pud_t *pudp;
|
||||||
|
|
||||||
|
pudp = pud_offset(&pgd, addr);
|
||||||
|
do {
|
||||||
|
pud_t pud = *pudp;
|
||||||
|
|
||||||
|
next = pud_addr_end(addr, end);
|
||||||
|
if (pud_none(pud))
|
||||||
|
return 0;
|
||||||
|
if (!gup_pmd_range(pud, addr, next, write, pages, nr))
|
||||||
|
return 0;
|
||||||
|
} while (pudp++, addr = next, addr != end);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_user_pages_fast(unsigned long start, int nr_pages, int write,
|
||||||
|
struct page **pages)
|
||||||
|
{
|
||||||
|
struct mm_struct *mm = current->mm;
|
||||||
|
unsigned long addr, len, end;
|
||||||
|
unsigned long next;
|
||||||
|
pgd_t *pgdp;
|
||||||
|
int nr = 0;
|
||||||
|
|
||||||
|
start &= PAGE_MASK;
|
||||||
|
addr = start;
|
||||||
|
len = (unsigned long) nr_pages << PAGE_SHIFT;
|
||||||
|
end = start + len;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* XXX: batch / limit 'nr', to avoid large irq off latency
|
||||||
|
* needs some instrumenting to determine the common sizes used by
|
||||||
|
* important workloads (eg. DB2), and whether limiting the batch size
|
||||||
|
* will decrease performance.
|
||||||
|
*
|
||||||
|
* It seems like we're in the clear for the moment. Direct-IO is
|
||||||
|
* the main guy that batches up lots of get_user_pages, and even
|
||||||
|
* they are limited to 64-at-a-time which is not so many.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* This doesn't prevent pagetable teardown, but does prevent
|
||||||
|
* the pagetables from being freed on sparc.
|
||||||
|
*
|
||||||
|
* So long as we atomically load page table pointers versus teardown,
|
||||||
|
* we can follow the address down to the the page and take a ref on it.
|
||||||
|
*/
|
||||||
|
local_irq_disable();
|
||||||
|
|
||||||
|
pgdp = pgd_offset(mm, addr);
|
||||||
|
do {
|
||||||
|
pgd_t pgd = *pgdp;
|
||||||
|
|
||||||
|
next = pgd_addr_end(addr, end);
|
||||||
|
if (pgd_none(pgd))
|
||||||
|
goto slow;
|
||||||
|
if (!gup_pud_range(pgd, addr, next, write, pages, &nr))
|
||||||
|
goto slow;
|
||||||
|
} while (pgdp++, addr = next, addr != end);
|
||||||
|
|
||||||
|
local_irq_enable();
|
||||||
|
|
||||||
|
VM_BUG_ON(nr != (end - start) >> PAGE_SHIFT);
|
||||||
|
return nr;
|
||||||
|
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
slow:
|
||||||
|
local_irq_enable();
|
||||||
|
|
||||||
|
/* Try to get the remaining pages with get_user_pages */
|
||||||
|
start += nr << PAGE_SHIFT;
|
||||||
|
pages += nr;
|
||||||
|
|
||||||
|
down_read(&mm->mmap_sem);
|
||||||
|
ret = get_user_pages(current, mm, start,
|
||||||
|
(end - start) >> PAGE_SHIFT, write, 0, pages, NULL);
|
||||||
|
up_read(&mm->mmap_sem);
|
||||||
|
|
||||||
|
/* Have to be a bit careful with return values */
|
||||||
|
if (nr > 0) {
|
||||||
|
if (ret < 0)
|
||||||
|
ret = nr;
|
||||||
|
else
|
||||||
|
ret += nr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
@ -236,6 +236,8 @@ static void setup_tsb_params(struct mm_struct *mm, unsigned long tsb_idx, unsign
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct kmem_cache *pgtable_cache __read_mostly;
|
||||||
|
|
||||||
static struct kmem_cache *tsb_caches[8] __read_mostly;
|
static struct kmem_cache *tsb_caches[8] __read_mostly;
|
||||||
|
|
||||||
static const char *tsb_cache_names[8] = {
|
static const char *tsb_cache_names[8] = {
|
||||||
@ -253,6 +255,15 @@ void __init pgtable_cache_init(void)
|
|||||||
{
|
{
|
||||||
unsigned long i;
|
unsigned long i;
|
||||||
|
|
||||||
|
pgtable_cache = kmem_cache_create("pgtable_cache",
|
||||||
|
PAGE_SIZE, PAGE_SIZE,
|
||||||
|
0,
|
||||||
|
_clear_page);
|
||||||
|
if (!pgtable_cache) {
|
||||||
|
prom_printf("pgtable_cache_init(): Could not create!\n");
|
||||||
|
prom_halt();
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0; i < 8; i++) {
|
for (i = 0; i < 8; i++) {
|
||||||
unsigned long size = 8192 << i;
|
unsigned long size = 8192 << i;
|
||||||
const char *name = tsb_cache_names[i];
|
const char *name = tsb_cache_names[i];
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
mainmenu "User Mode Linux/$SUBARCH $KERNELVERSION Kernel Configuration"
|
||||||
|
|
||||||
source "arch/um/Kconfig.common"
|
source "arch/um/Kconfig.common"
|
||||||
|
|
||||||
menu "UML-specific options"
|
menu "UML-specific options"
|
||||||
|
@ -543,11 +543,10 @@ int parse_chan_pair(char *str, struct line *line, int device,
|
|||||||
const struct chan_opts *opts, char **error_out)
|
const struct chan_opts *opts, char **error_out)
|
||||||
{
|
{
|
||||||
struct list_head *chans = &line->chan_list;
|
struct list_head *chans = &line->chan_list;
|
||||||
struct chan *new, *chan;
|
struct chan *new;
|
||||||
char *in, *out;
|
char *in, *out;
|
||||||
|
|
||||||
if (!list_empty(chans)) {
|
if (!list_empty(chans)) {
|
||||||
chan = list_entry(chans->next, struct chan, list);
|
|
||||||
free_chan(chans, 0);
|
free_chan(chans, 0);
|
||||||
INIT_LIST_HEAD(chans);
|
INIT_LIST_HEAD(chans);
|
||||||
}
|
}
|
||||||
|
@ -186,7 +186,11 @@ static int absolutize(char *to, int size, char *from)
|
|||||||
strcat(to, "/");
|
strcat(to, "/");
|
||||||
strcat(to, from);
|
strcat(to, from);
|
||||||
}
|
}
|
||||||
chdir(save_cwd);
|
if (chdir(save_cwd)) {
|
||||||
|
cow_printf("absolutize : Can't cd to '%s' - "
|
||||||
|
"errno = %d\n", save_cwd, errno);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ int start_watchdog(int *in_fd_ret, int *out_fd_ret, char *sock)
|
|||||||
{
|
{
|
||||||
struct dog_data data;
|
struct dog_data data;
|
||||||
int in_fds[2], out_fds[2], pid, n, err;
|
int in_fds[2], out_fds[2], pid, n, err;
|
||||||
char pid_buf[sizeof("nnnnn\0")], c;
|
char pid_buf[sizeof("nnnnnnn\0")], c;
|
||||||
char *pid_args[] = { "/usr/bin/uml_watchdog", "-pid", pid_buf, NULL };
|
char *pid_args[] = { "/usr/bin/uml_watchdog", "-pid", pid_buf, NULL };
|
||||||
char *mconsole_args[] = { "/usr/bin/uml_watchdog", "-mconsole", NULL,
|
char *mconsole_args[] = { "/usr/bin/uml_watchdog", "-mconsole", NULL,
|
||||||
NULL };
|
NULL };
|
||||||
|
@ -176,10 +176,9 @@ void line_flush_buffer(struct tty_struct *tty)
|
|||||||
{
|
{
|
||||||
struct line *line = tty->driver_data;
|
struct line *line = tty->driver_data;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
int err;
|
|
||||||
|
|
||||||
spin_lock_irqsave(&line->lock, flags);
|
spin_lock_irqsave(&line->lock, flags);
|
||||||
err = flush_buffer(line);
|
flush_buffer(line);
|
||||||
spin_unlock_irqrestore(&line->lock, flags);
|
spin_unlock_irqrestore(&line->lock, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -262,6 +262,15 @@ static int uml_net_change_mtu(struct net_device *dev, int new_mtu)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_NET_POLL_CONTROLLER
|
||||||
|
static void uml_net_poll_controller(struct net_device *dev)
|
||||||
|
{
|
||||||
|
disable_irq(dev->irq);
|
||||||
|
uml_net_interrupt(dev->irq, dev);
|
||||||
|
enable_irq(dev->irq);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static void uml_net_get_drvinfo(struct net_device *dev,
|
static void uml_net_get_drvinfo(struct net_device *dev,
|
||||||
struct ethtool_drvinfo *info)
|
struct ethtool_drvinfo *info)
|
||||||
{
|
{
|
||||||
@ -364,6 +373,9 @@ static const struct net_device_ops uml_netdev_ops = {
|
|||||||
.ndo_set_mac_address = eth_mac_addr,
|
.ndo_set_mac_address = eth_mac_addr,
|
||||||
.ndo_change_mtu = uml_net_change_mtu,
|
.ndo_change_mtu = uml_net_change_mtu,
|
||||||
.ndo_validate_addr = eth_validate_addr,
|
.ndo_validate_addr = eth_validate_addr,
|
||||||
|
#ifdef CONFIG_NET_POLL_CONTROLLER
|
||||||
|
.ndo_poll_controller = uml_net_poll_controller,
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -228,7 +228,10 @@ static void change(char *dev, char *what, unsigned char *addr,
|
|||||||
"buffer\n");
|
"buffer\n");
|
||||||
|
|
||||||
pid = change_tramp(argv, output, output_len);
|
pid = change_tramp(argv, output, output_len);
|
||||||
if (pid < 0) return;
|
if (pid < 0) {
|
||||||
|
kfree(output);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (output != NULL) {
|
if (output != NULL) {
|
||||||
printk("%s", output);
|
printk("%s", output);
|
||||||
|
@ -102,7 +102,7 @@ static int slip_tramp(char **argv, int fd)
|
|||||||
"buffer\n");
|
"buffer\n");
|
||||||
os_kill_process(pid, 1);
|
os_kill_process(pid, 1);
|
||||||
err = -ENOMEM;
|
err = -ENOMEM;
|
||||||
goto out_free;
|
goto out_close;
|
||||||
}
|
}
|
||||||
|
|
||||||
close(fds[1]);
|
close(fds[1]);
|
||||||
@ -112,7 +112,6 @@ static int slip_tramp(char **argv, int fd)
|
|||||||
err = helper_wait(pid);
|
err = helper_wait(pid);
|
||||||
close(fds[0]);
|
close(fds[0]);
|
||||||
|
|
||||||
out_free:
|
|
||||||
kfree(output);
|
kfree(output);
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
|
@ -1,20 +1,18 @@
|
|||||||
#ifndef __UM_DELAY_H
|
#ifndef __UM_DELAY_H
|
||||||
#define __UM_DELAY_H
|
#define __UM_DELAY_H
|
||||||
|
|
||||||
#define MILLION 1000000
|
|
||||||
|
|
||||||
/* Undefined on purpose */
|
/* Undefined on purpose */
|
||||||
extern void __bad_udelay(void);
|
extern void __bad_udelay(void);
|
||||||
|
extern void __bad_ndelay(void);
|
||||||
|
|
||||||
extern void __udelay(unsigned long usecs);
|
extern void __udelay(unsigned long usecs);
|
||||||
|
extern void __ndelay(unsigned long usecs);
|
||||||
extern void __delay(unsigned long loops);
|
extern void __delay(unsigned long loops);
|
||||||
|
|
||||||
#define udelay(n) ((__builtin_constant_p(n) && (n) > 20000) ? \
|
#define udelay(n) ((__builtin_constant_p(n) && (n) > 20000) ? \
|
||||||
__bad_udelay() : __udelay(n))
|
__bad_udelay() : __udelay(n))
|
||||||
|
|
||||||
/* It appears that ndelay is not used at all for UML, and has never been
|
#define ndelay(n) ((__builtin_constant_p(n) && (n) > 20000) ? \
|
||||||
* implemented. */
|
__bad_ndelay() : __ndelay(n))
|
||||||
extern void __unimplemented_ndelay(void);
|
|
||||||
#define ndelay(n) __unimplemented_ndelay()
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -38,7 +38,6 @@ void flush_thread(void)
|
|||||||
|
|
||||||
void start_thread(struct pt_regs *regs, unsigned long eip, unsigned long esp)
|
void start_thread(struct pt_regs *regs, unsigned long eip, unsigned long esp)
|
||||||
{
|
{
|
||||||
set_fs(USER_DS);
|
|
||||||
PT_REGS_IP(regs) = eip;
|
PT_REGS_IP(regs) = eip;
|
||||||
PT_REGS_SP(regs) = esp;
|
PT_REGS_SP(regs) = esp;
|
||||||
}
|
}
|
||||||
|
@ -20,9 +20,8 @@ static void kill_off_processes(void)
|
|||||||
os_kill_ptraced_process(userspace_pid[0], 1);
|
os_kill_ptraced_process(userspace_pid[0], 1);
|
||||||
else {
|
else {
|
||||||
struct task_struct *p;
|
struct task_struct *p;
|
||||||
int pid, me;
|
int pid;
|
||||||
|
|
||||||
me = os_getpid();
|
|
||||||
for_each_process(p) {
|
for_each_process(p) {
|
||||||
if (p->mm == NULL)
|
if (p->mm == NULL)
|
||||||
continue;
|
continue;
|
||||||
|
@ -3,10 +3,12 @@
|
|||||||
# Licensed under the GPL
|
# Licensed under the GPL
|
||||||
#
|
#
|
||||||
|
|
||||||
obj-y = aio.o elf_aux.o execvp.o file.o helper.o irq.o main.o mem.o process.o \
|
obj-y = aio.o execvp.o file.o helper.o irq.o main.o mem.o process.o \
|
||||||
registers.o sigio.o signal.o start_up.o time.o tty.o uaccess.o \
|
registers.o sigio.o signal.o start_up.o time.o tty.o uaccess.o \
|
||||||
umid.o tls.o user_syms.o util.o drivers/ sys-$(SUBARCH)/ skas/
|
umid.o tls.o user_syms.o util.o drivers/ sys-$(SUBARCH)/ skas/
|
||||||
|
|
||||||
|
obj-$(CONFIG_ARCH_REUSE_HOST_VSYSCALL_AREA) += elf_aux.o
|
||||||
|
|
||||||
USER_OBJS := $(user-objs-y) aio.o elf_aux.o execvp.o file.o helper.o irq.o \
|
USER_OBJS := $(user-objs-y) aio.o elf_aux.o execvp.o file.o helper.o irq.o \
|
||||||
main.o mem.o process.o registers.o sigio.o signal.o start_up.o time.o \
|
main.o mem.o process.o registers.o sigio.o signal.o start_up.o time.o \
|
||||||
tty.o tls.o uaccess.o umid.o util.o
|
tty.o tls.o uaccess.o umid.o util.o
|
||||||
|
@ -14,16 +14,11 @@
|
|||||||
#include "mem_user.h"
|
#include "mem_user.h"
|
||||||
#include <kern_constants.h>
|
#include <kern_constants.h>
|
||||||
|
|
||||||
/* Use the one from the kernel - the host may miss it, if having old headers. */
|
|
||||||
#if UM_ELF_CLASS == UM_ELFCLASS32
|
|
||||||
typedef Elf32_auxv_t elf_auxv_t;
|
typedef Elf32_auxv_t elf_auxv_t;
|
||||||
#else
|
|
||||||
typedef Elf64_auxv_t elf_auxv_t;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* These are initialized very early in boot and never changed */
|
/* These are initialized very early in boot and never changed */
|
||||||
char * elf_aux_platform;
|
char * elf_aux_platform;
|
||||||
long elf_aux_hwcap;
|
extern long elf_aux_hwcap;
|
||||||
unsigned long vsyscall_ehdr;
|
unsigned long vsyscall_ehdr;
|
||||||
unsigned long vsyscall_end;
|
unsigned long vsyscall_end;
|
||||||
unsigned long __kernel_vsyscall;
|
unsigned long __kernel_vsyscall;
|
||||||
|
@ -28,14 +28,14 @@ static int helper_child(void *arg)
|
|||||||
{
|
{
|
||||||
struct helper_data *data = arg;
|
struct helper_data *data = arg;
|
||||||
char **argv = data->argv;
|
char **argv = data->argv;
|
||||||
int err;
|
int err, ret;
|
||||||
|
|
||||||
if (data->pre_exec != NULL)
|
if (data->pre_exec != NULL)
|
||||||
(*data->pre_exec)(data->pre_data);
|
(*data->pre_exec)(data->pre_data);
|
||||||
err = execvp_noalloc(data->buf, argv[0], argv);
|
err = execvp_noalloc(data->buf, argv[0], argv);
|
||||||
|
|
||||||
/* If the exec succeeds, we don't get here */
|
/* If the exec succeeds, we don't get here */
|
||||||
write(data->fd, &err, sizeof(err));
|
CATCH_EINTR(ret = write(data->fd, &err, sizeof(err)));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,8 @@
|
|||||||
#define STACKSIZE (8 * 1024 * 1024)
|
#define STACKSIZE (8 * 1024 * 1024)
|
||||||
#define THREAD_NAME_LEN (256)
|
#define THREAD_NAME_LEN (256)
|
||||||
|
|
||||||
|
long elf_aux_hwcap;
|
||||||
|
|
||||||
static void set_stklim(void)
|
static void set_stklim(void)
|
||||||
{
|
{
|
||||||
struct rlimit lim;
|
struct rlimit lim;
|
||||||
@ -143,7 +145,9 @@ int __init main(int argc, char **argv, char **envp)
|
|||||||
install_fatal_handler(SIGINT);
|
install_fatal_handler(SIGINT);
|
||||||
install_fatal_handler(SIGTERM);
|
install_fatal_handler(SIGTERM);
|
||||||
|
|
||||||
|
#ifdef CONFIG_ARCH_REUSE_HOST_VSYSCALL_AREA
|
||||||
scan_elf_aux(envp);
|
scan_elf_aux(envp);
|
||||||
|
#endif
|
||||||
|
|
||||||
do_uml_initcalls();
|
do_uml_initcalls();
|
||||||
ret = linux_main(argc, argv);
|
ret = linux_main(argc, argv);
|
||||||
|
@ -176,7 +176,7 @@ static int __init make_tempfile(const char *template, char **out_tempname,
|
|||||||
|
|
||||||
find_tempdir();
|
find_tempdir();
|
||||||
if ((tempdir == NULL) || (strlen(tempdir) >= MAXPATHLEN))
|
if ((tempdir == NULL) || (strlen(tempdir) >= MAXPATHLEN))
|
||||||
return -1;
|
goto out;
|
||||||
|
|
||||||
if (template[0] != '/')
|
if (template[0] != '/')
|
||||||
strcpy(tempname, tempdir);
|
strcpy(tempname, tempdir);
|
||||||
@ -191,13 +191,15 @@ static int __init make_tempfile(const char *template, char **out_tempname,
|
|||||||
}
|
}
|
||||||
if (do_unlink && (unlink(tempname) < 0)) {
|
if (do_unlink && (unlink(tempname) < 0)) {
|
||||||
perror("unlink");
|
perror("unlink");
|
||||||
goto out;
|
goto close;
|
||||||
}
|
}
|
||||||
if (out_tempname) {
|
if (out_tempname) {
|
||||||
*out_tempname = tempname;
|
*out_tempname = tempname;
|
||||||
} else
|
} else
|
||||||
free(tempname);
|
free(tempname);
|
||||||
return fd;
|
return fd;
|
||||||
|
close:
|
||||||
|
close(fd);
|
||||||
out:
|
out:
|
||||||
free(tempname);
|
free(tempname);
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -113,3 +113,8 @@ EXPORT_SYMBOL(__stack_smash_handler);
|
|||||||
|
|
||||||
extern long __guard __attribute__((weak));
|
extern long __guard __attribute__((weak));
|
||||||
EXPORT_SYMBOL(__guard);
|
EXPORT_SYMBOL(__guard);
|
||||||
|
|
||||||
|
#ifdef _FORTIFY_SOURCE
|
||||||
|
extern int __sprintf_chk(char *str, int flag, size_t strlen, const char *format);
|
||||||
|
EXPORT_SYMBOL(__sprintf_chk);
|
||||||
|
#endif
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
obj-y = bug.o bugs.o checksum.o delay.o fault.o ksyms.o ldt.o ptrace.o \
|
obj-y = bug.o bugs.o checksum.o delay.o fault.o ksyms.o ldt.o ptrace.o \
|
||||||
ptrace_user.o setjmp.o signal.o stub.o stub_segv.o syscalls.o sysrq.o \
|
ptrace_user.o setjmp.o signal.o stub.o stub_segv.o syscalls.o sysrq.o \
|
||||||
sys_call_table.o tls.o atomic64_cx8_32.o
|
sys_call_table.o tls.o atomic64_cx8_32.o mem.o
|
||||||
|
|
||||||
obj-$(CONFIG_BINFMT_ELF) += elfcore.o
|
obj-$(CONFIG_BINFMT_ELF) += elfcore.o
|
||||||
|
|
||||||
|
@ -105,6 +105,8 @@ extern unsigned long __kernel_vsyscall;
|
|||||||
#define FIXADDR_USER_START VSYSCALL_BASE
|
#define FIXADDR_USER_START VSYSCALL_BASE
|
||||||
#define FIXADDR_USER_END VSYSCALL_END
|
#define FIXADDR_USER_END VSYSCALL_END
|
||||||
|
|
||||||
|
#define __HAVE_ARCH_GATE_AREA 1
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Architecture-neutral AT_ values in 0-17, leave some room
|
* Architecture-neutral AT_ values in 0-17, leave some room
|
||||||
* for more of them, start the x86-specific ones at 32.
|
* for more of them, start the x86-specific ones at 32.
|
||||||
|
@ -1,29 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 Richard Weinberger <richrd@nod.at>
|
||||||
|
* Mostly copied from arch/x86/lib/delay.c
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <asm/param.h>
|
#include <asm/param.h>
|
||||||
|
|
||||||
void __delay(unsigned long time)
|
void __delay(unsigned long loops)
|
||||||
{
|
{
|
||||||
/* Stolen from the i386 __loop_delay */
|
asm volatile(
|
||||||
int d0;
|
"test %0,%0\n"
|
||||||
__asm__ __volatile__(
|
"jz 3f\n"
|
||||||
"\tjmp 1f\n"
|
"jmp 1f\n"
|
||||||
|
|
||||||
".align 16\n"
|
".align 16\n"
|
||||||
"1:\tjmp 2f\n"
|
"1: jmp 2f\n"
|
||||||
|
|
||||||
".align 16\n"
|
".align 16\n"
|
||||||
"2:\tdecl %0\n\tjns 2b"
|
"2: dec %0\n"
|
||||||
:"=&a" (d0)
|
" jnz 2b\n"
|
||||||
:"0" (time));
|
"3: dec %0\n"
|
||||||
|
|
||||||
|
: /* we don't need output */
|
||||||
|
: "a" (loops)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL(__delay);
|
||||||
|
|
||||||
|
inline void __const_udelay(unsigned long xloops)
|
||||||
|
{
|
||||||
|
int d0;
|
||||||
|
|
||||||
|
xloops *= 4;
|
||||||
|
asm("mull %%edx"
|
||||||
|
: "=d" (xloops), "=&a" (d0)
|
||||||
|
: "1" (xloops), "0"
|
||||||
|
(loops_per_jiffy * (HZ/4)));
|
||||||
|
|
||||||
|
__delay(++xloops);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(__const_udelay);
|
||||||
|
|
||||||
void __udelay(unsigned long usecs)
|
void __udelay(unsigned long usecs)
|
||||||
{
|
{
|
||||||
int i, n;
|
__const_udelay(usecs * 0x000010c7); /* 2**32 / 1000000 (rounded up) */
|
||||||
|
|
||||||
n = (loops_per_jiffy * HZ * usecs) / MILLION;
|
|
||||||
for(i=0;i<n;i++)
|
|
||||||
cpu_relax();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPORT_SYMBOL(__udelay);
|
EXPORT_SYMBOL(__udelay);
|
||||||
|
|
||||||
|
void __ndelay(unsigned long nsecs)
|
||||||
|
{
|
||||||
|
__const_udelay(nsecs * 0x00005); /* 2**32 / 1000000000 (rounded up) */
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(__ndelay);
|
||||||
|
62
arch/um/sys-i386/mem.c
Normal file
62
arch/um/sys-i386/mem.c
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 Richard Weinberger <richrd@nod.at>
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/mm.h>
|
||||||
|
#include <asm/page.h>
|
||||||
|
#include <asm/mman.h>
|
||||||
|
|
||||||
|
static struct vm_area_struct gate_vma;
|
||||||
|
|
||||||
|
static int __init gate_vma_init(void)
|
||||||
|
{
|
||||||
|
if (!FIXADDR_USER_START)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
gate_vma.vm_mm = NULL;
|
||||||
|
gate_vma.vm_start = FIXADDR_USER_START;
|
||||||
|
gate_vma.vm_end = FIXADDR_USER_END;
|
||||||
|
gate_vma.vm_flags = VM_READ | VM_MAYREAD | VM_EXEC | VM_MAYEXEC;
|
||||||
|
gate_vma.vm_page_prot = __P101;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make sure the vDSO gets into every core dump.
|
||||||
|
* Dumping its contents makes post-mortem fully interpretable later
|
||||||
|
* without matching up the same kernel and hardware config to see
|
||||||
|
* what PC values meant.
|
||||||
|
*/
|
||||||
|
gate_vma.vm_flags |= VM_ALWAYSDUMP;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
__initcall(gate_vma_init);
|
||||||
|
|
||||||
|
struct vm_area_struct *get_gate_vma(struct mm_struct *mm)
|
||||||
|
{
|
||||||
|
return FIXADDR_USER_START ? &gate_vma : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int in_gate_area_no_mm(unsigned long addr)
|
||||||
|
{
|
||||||
|
if (!FIXADDR_USER_START)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if ((addr >= FIXADDR_USER_START) && (addr < FIXADDR_USER_END))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int in_gate_area(struct mm_struct *mm, unsigned long addr)
|
||||||
|
{
|
||||||
|
struct vm_area_struct *vma = get_gate_vma(mm);
|
||||||
|
|
||||||
|
if (!vma)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return (addr >= vma->vm_start) && (addr < vma->vm_end);
|
||||||
|
}
|
@ -4,10 +4,12 @@
|
|||||||
# Licensed under the GPL
|
# Licensed under the GPL
|
||||||
#
|
#
|
||||||
|
|
||||||
obj-y = bug.o bugs.o delay.o fault.o ldt.o mem.o ptrace.o ptrace_user.o \
|
obj-y = bug.o bugs.o delay.o fault.o ldt.o ptrace.o ptrace_user.o mem.o \
|
||||||
setjmp.o signal.o stub.o stub_segv.o syscalls.o syscall_table.o \
|
setjmp.o signal.o stub.o stub_segv.o syscalls.o syscall_table.o \
|
||||||
sysrq.o ksyms.o tls.o
|
sysrq.o ksyms.o tls.o
|
||||||
|
|
||||||
|
obj-y += vdso/
|
||||||
|
|
||||||
subarch-obj-y = lib/csum-partial_64.o lib/memcpy_64.o lib/thunk_64.o \
|
subarch-obj-y = lib/csum-partial_64.o lib/memcpy_64.o lib/thunk_64.o \
|
||||||
lib/rwsem.o
|
lib/rwsem.o
|
||||||
subarch-obj-$(CONFIG_MODULES) += kernel/module.o
|
subarch-obj-$(CONFIG_MODULES) += kernel/module.o
|
||||||
|
@ -119,4 +119,14 @@ extern long elf_aux_hwcap;
|
|||||||
|
|
||||||
#define SET_PERSONALITY(ex) do ; while(0)
|
#define SET_PERSONALITY(ex) do ; while(0)
|
||||||
|
|
||||||
|
#define __HAVE_ARCH_GATE_AREA 1
|
||||||
|
#define ARCH_HAS_SETUP_ADDITIONAL_PAGES 1
|
||||||
|
struct linux_binprm;
|
||||||
|
extern int arch_setup_additional_pages(struct linux_binprm *bprm,
|
||||||
|
int uses_interp);
|
||||||
|
|
||||||
|
extern unsigned long um_vdso_addr;
|
||||||
|
#define AT_SYSINFO_EHDR 33
|
||||||
|
#define ARCH_DLINFO NEW_AUX_ENT(AT_SYSINFO_EHDR, um_vdso_addr)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -1,30 +1,60 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2003 PathScale, Inc.
|
* Copyright (C) 2011 Richard Weinberger <richrd@nod.at>
|
||||||
* Copied from arch/x86_64
|
* Mostly copied from arch/x86/lib/delay.c
|
||||||
*
|
*
|
||||||
* Licensed under the GPL
|
* 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.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <asm/processor.h>
|
|
||||||
#include <asm/param.h>
|
#include <asm/param.h>
|
||||||
|
|
||||||
void __delay(unsigned long loops)
|
void __delay(unsigned long loops)
|
||||||
{
|
{
|
||||||
unsigned long i;
|
asm volatile(
|
||||||
|
"test %0,%0\n"
|
||||||
|
"jz 3f\n"
|
||||||
|
"jmp 1f\n"
|
||||||
|
|
||||||
for(i = 0; i < loops; i++)
|
".align 16\n"
|
||||||
cpu_relax();
|
"1: jmp 2f\n"
|
||||||
|
|
||||||
|
".align 16\n"
|
||||||
|
"2: dec %0\n"
|
||||||
|
" jnz 2b\n"
|
||||||
|
"3: dec %0\n"
|
||||||
|
|
||||||
|
: /* we don't need output */
|
||||||
|
: "a" (loops)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL(__delay);
|
||||||
|
|
||||||
|
inline void __const_udelay(unsigned long xloops)
|
||||||
|
{
|
||||||
|
int d0;
|
||||||
|
|
||||||
|
xloops *= 4;
|
||||||
|
asm("mull %%edx"
|
||||||
|
: "=d" (xloops), "=&a" (d0)
|
||||||
|
: "1" (xloops), "0"
|
||||||
|
(loops_per_jiffy * (HZ/4)));
|
||||||
|
|
||||||
|
__delay(++xloops);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(__const_udelay);
|
||||||
|
|
||||||
void __udelay(unsigned long usecs)
|
void __udelay(unsigned long usecs)
|
||||||
{
|
{
|
||||||
unsigned long i, n;
|
__const_udelay(usecs * 0x000010c7); /* 2**32 / 1000000 (rounded up) */
|
||||||
|
|
||||||
n = (loops_per_jiffy * HZ * usecs) / MILLION;
|
|
||||||
for(i=0;i<n;i++)
|
|
||||||
cpu_relax();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPORT_SYMBOL(__udelay);
|
EXPORT_SYMBOL(__udelay);
|
||||||
|
|
||||||
|
void __ndelay(unsigned long nsecs)
|
||||||
|
{
|
||||||
|
__const_udelay(nsecs * 0x00005); /* 2**32 / 1000000000 (rounded up) */
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(__ndelay);
|
||||||
|
@ -1,16 +1,26 @@
|
|||||||
/*
|
|
||||||
* Copyright 2003 PathScale, Inc.
|
|
||||||
*
|
|
||||||
* Licensed under the GPL
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "linux/mm.h"
|
#include "linux/mm.h"
|
||||||
#include "asm/page.h"
|
#include "asm/page.h"
|
||||||
#include "asm/mman.h"
|
#include "asm/mman.h"
|
||||||
|
|
||||||
unsigned long vm_stack_flags = __VM_STACK_FLAGS;
|
const char *arch_vma_name(struct vm_area_struct *vma)
|
||||||
unsigned long vm_stack_flags32 = __VM_STACK_FLAGS;
|
{
|
||||||
unsigned long vm_data_default_flags = __VM_DATA_DEFAULT_FLAGS;
|
if (vma->vm_mm && vma->vm_start == um_vdso_addr)
|
||||||
unsigned long vm_data_default_flags32 = __VM_DATA_DEFAULT_FLAGS;
|
return "[vdso]";
|
||||||
unsigned long vm_force_exec32 = PROT_EXEC;
|
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct vm_area_struct *get_gate_vma(struct mm_struct *mm)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int in_gate_area(struct mm_struct *mm, unsigned long addr)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int in_gate_area_no_mm(unsigned long addr)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
@ -7,27 +7,9 @@
|
|||||||
#ifndef __VM_FLAGS_X86_64_H
|
#ifndef __VM_FLAGS_X86_64_H
|
||||||
#define __VM_FLAGS_X86_64_H
|
#define __VM_FLAGS_X86_64_H
|
||||||
|
|
||||||
#define __VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | VM_EXEC | \
|
#define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | VM_EXEC | \
|
||||||
VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC)
|
VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC)
|
||||||
#define __VM_STACK_FLAGS (VM_GROWSDOWN | VM_READ | VM_WRITE | \
|
#define VM_STACK_DEFAULT_FLAGS (VM_GROWSDOWN | VM_READ | VM_WRITE | \
|
||||||
VM_EXEC | VM_MAYREAD | VM_MAYWRITE | \
|
VM_EXEC | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC)
|
||||||
VM_MAYEXEC)
|
|
||||||
|
|
||||||
extern unsigned long vm_stack_flags, vm_stack_flags32;
|
|
||||||
extern unsigned long vm_data_default_flags, vm_data_default_flags32;
|
|
||||||
extern unsigned long vm_force_exec32;
|
|
||||||
|
|
||||||
#ifdef TIF_IA32
|
|
||||||
#define VM_DATA_DEFAULT_FLAGS \
|
|
||||||
(test_thread_flag(TIF_IA32) ? vm_data_default_flags32 : \
|
|
||||||
vm_data_default_flags)
|
|
||||||
|
|
||||||
#define VM_STACK_DEFAULT_FLAGS \
|
|
||||||
(test_thread_flag(TIF_IA32) ? vm_stack_flags32 : vm_stack_flags)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define VM_DATA_DEFAULT_FLAGS vm_data_default_flags
|
|
||||||
|
|
||||||
#define VM_STACK_DEFAULT_FLAGS vm_stack_flags
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
90
arch/um/sys-x86_64/vdso/Makefile
Normal file
90
arch/um/sys-x86_64/vdso/Makefile
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
#
|
||||||
|
# Building vDSO images for x86.
|
||||||
|
#
|
||||||
|
|
||||||
|
VDSO64-y := y
|
||||||
|
|
||||||
|
vdso-install-$(VDSO64-y) += vdso.so
|
||||||
|
|
||||||
|
|
||||||
|
# files to link into the vdso
|
||||||
|
vobjs-y := vdso-note.o um_vdso.o
|
||||||
|
|
||||||
|
# files to link into kernel
|
||||||
|
obj-$(VDSO64-y) += vdso.o vma.o
|
||||||
|
|
||||||
|
vobjs := $(foreach F,$(vobjs-y),$(obj)/$F)
|
||||||
|
|
||||||
|
$(obj)/vdso.o: $(obj)/vdso.so
|
||||||
|
|
||||||
|
targets += vdso.so vdso.so.dbg vdso.lds $(vobjs-y)
|
||||||
|
|
||||||
|
export CPPFLAGS_vdso.lds += -P -C
|
||||||
|
|
||||||
|
VDSO_LDFLAGS_vdso.lds = -m64 -Wl,-soname=linux-vdso.so.1 \
|
||||||
|
-Wl,-z,max-page-size=4096 -Wl,-z,common-page-size=4096
|
||||||
|
|
||||||
|
$(obj)/vdso.o: $(src)/vdso.S $(obj)/vdso.so
|
||||||
|
|
||||||
|
$(obj)/vdso.so.dbg: $(src)/vdso.lds $(vobjs) FORCE
|
||||||
|
$(call if_changed,vdso)
|
||||||
|
|
||||||
|
$(obj)/%.so: OBJCOPYFLAGS := -S
|
||||||
|
$(obj)/%.so: $(obj)/%.so.dbg FORCE
|
||||||
|
$(call if_changed,objcopy)
|
||||||
|
|
||||||
|
#
|
||||||
|
# Don't omit frame pointers for ease of userspace debugging, but do
|
||||||
|
# optimize sibling calls.
|
||||||
|
#
|
||||||
|
CFL := $(PROFILING) -mcmodel=small -fPIC -O2 -fasynchronous-unwind-tables -m64 \
|
||||||
|
$(filter -g%,$(KBUILD_CFLAGS)) $(call cc-option, -fno-stack-protector) \
|
||||||
|
-fno-omit-frame-pointer -foptimize-sibling-calls
|
||||||
|
|
||||||
|
$(vobjs): KBUILD_CFLAGS += $(CFL)
|
||||||
|
|
||||||
|
#
|
||||||
|
# vDSO code runs in userspace and -pg doesn't help with profiling anyway.
|
||||||
|
#
|
||||||
|
CFLAGS_REMOVE_vdso-note.o = -pg
|
||||||
|
CFLAGS_REMOVE_um_vdso.o = -pg
|
||||||
|
|
||||||
|
targets += vdso-syms.lds
|
||||||
|
obj-$(VDSO64-y) += vdso-syms.lds
|
||||||
|
|
||||||
|
#
|
||||||
|
# Match symbols in the DSO that look like VDSO*; produce a file of constants.
|
||||||
|
#
|
||||||
|
sed-vdsosym := -e 's/^00*/0/' \
|
||||||
|
-e 's/^\([0-9a-fA-F]*\) . \(VDSO[a-zA-Z0-9_]*\)$$/\2 = 0x\1;/p'
|
||||||
|
quiet_cmd_vdsosym = VDSOSYM $@
|
||||||
|
define cmd_vdsosym
|
||||||
|
$(NM) $< | LC_ALL=C sed -n $(sed-vdsosym) | LC_ALL=C sort > $@
|
||||||
|
endef
|
||||||
|
|
||||||
|
$(obj)/%-syms.lds: $(obj)/%.so.dbg FORCE
|
||||||
|
$(call if_changed,vdsosym)
|
||||||
|
|
||||||
|
#
|
||||||
|
# The DSO images are built using a special linker script.
|
||||||
|
#
|
||||||
|
quiet_cmd_vdso = VDSO $@
|
||||||
|
cmd_vdso = $(CC) -nostdlib -o $@ \
|
||||||
|
$(VDSO_LDFLAGS) $(VDSO_LDFLAGS_$(filter %.lds,$(^F))) \
|
||||||
|
-Wl,-T,$(filter %.lds,$^) $(filter %.o,$^) && \
|
||||||
|
sh $(srctree)/$(src)/checkundef.sh '$(NM)' '$@'
|
||||||
|
|
||||||
|
VDSO_LDFLAGS = -fPIC -shared $(call cc-ldoption, -Wl$(comma)--hash-style=sysv)
|
||||||
|
GCOV_PROFILE := n
|
||||||
|
|
||||||
|
#
|
||||||
|
# Install the unstripped copy of vdso*.so listed in $(vdso-install-y).
|
||||||
|
#
|
||||||
|
quiet_cmd_vdso_install = INSTALL $@
|
||||||
|
cmd_vdso_install = cp $(obj)/$@.dbg $(MODLIB)/vdso/$@
|
||||||
|
$(vdso-install-y): %.so: $(obj)/%.so.dbg FORCE
|
||||||
|
@mkdir -p $(MODLIB)/vdso
|
||||||
|
$(call cmd,vdso_install)
|
||||||
|
|
||||||
|
PHONY += vdso_install $(vdso-install-y)
|
||||||
|
vdso_install: $(vdso-install-y)
|
10
arch/um/sys-x86_64/vdso/checkundef.sh
Normal file
10
arch/um/sys-x86_64/vdso/checkundef.sh
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
nm="$1"
|
||||||
|
file="$2"
|
||||||
|
$nm "$file" | grep '^ *U' > /dev/null 2>&1
|
||||||
|
if [ $? -eq 1 ]; then
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
echo "$file: undefined symbols found" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
71
arch/um/sys-x86_64/vdso/um_vdso.c
Normal file
71
arch/um/sys-x86_64/vdso/um_vdso.c
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 Richard Weinberger <richrd@nod.at>
|
||||||
|
*
|
||||||
|
* 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 vDSO turns all calls into a syscall so that UML can trap them.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/* Disable profiling for userspace code */
|
||||||
|
#define DISABLE_BRANCH_PROFILING
|
||||||
|
|
||||||
|
#include <linux/time.h>
|
||||||
|
#include <linux/getcpu.h>
|
||||||
|
#include <asm/unistd.h>
|
||||||
|
|
||||||
|
int __vdso_clock_gettime(clockid_t clock, struct timespec *ts)
|
||||||
|
{
|
||||||
|
long ret;
|
||||||
|
|
||||||
|
asm("syscall" : "=a" (ret) :
|
||||||
|
"0" (__NR_clock_gettime), "D" (clock), "S" (ts) : "memory");
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
int clock_gettime(clockid_t, struct timespec *)
|
||||||
|
__attribute__((weak, alias("__vdso_clock_gettime")));
|
||||||
|
|
||||||
|
int __vdso_gettimeofday(struct timeval *tv, struct timezone *tz)
|
||||||
|
{
|
||||||
|
long ret;
|
||||||
|
|
||||||
|
asm("syscall" : "=a" (ret) :
|
||||||
|
"0" (__NR_gettimeofday), "D" (tv), "S" (tz) : "memory");
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
int gettimeofday(struct timeval *, struct timezone *)
|
||||||
|
__attribute__((weak, alias("__vdso_gettimeofday")));
|
||||||
|
|
||||||
|
time_t __vdso_time(time_t *t)
|
||||||
|
{
|
||||||
|
long secs;
|
||||||
|
|
||||||
|
asm volatile("syscall"
|
||||||
|
: "=a" (secs)
|
||||||
|
: "0" (__NR_time), "D" (t) : "cc", "r11", "cx", "memory");
|
||||||
|
|
||||||
|
return secs;
|
||||||
|
}
|
||||||
|
int time(time_t *t) __attribute__((weak, alias("__vdso_time")));
|
||||||
|
|
||||||
|
long
|
||||||
|
__vdso_getcpu(unsigned *cpu, unsigned *node, struct getcpu_cache *unused)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* UML does not support SMP, we can cheat here. :)
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (cpu)
|
||||||
|
*cpu = 0;
|
||||||
|
if (node)
|
||||||
|
*node = 0;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
long getcpu(unsigned *cpu, unsigned *node, struct getcpu_cache *tcache)
|
||||||
|
__attribute__((weak, alias("__vdso_getcpu")));
|
64
arch/um/sys-x86_64/vdso/vdso-layout.lds.S
Normal file
64
arch/um/sys-x86_64/vdso/vdso-layout.lds.S
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
* Linker script for vDSO. This is an ELF shared object prelinked to
|
||||||
|
* its virtual address, and with only one read-only segment.
|
||||||
|
* This script controls its layout.
|
||||||
|
*/
|
||||||
|
|
||||||
|
SECTIONS
|
||||||
|
{
|
||||||
|
. = VDSO_PRELINK + SIZEOF_HEADERS;
|
||||||
|
|
||||||
|
.hash : { *(.hash) } :text
|
||||||
|
.gnu.hash : { *(.gnu.hash) }
|
||||||
|
.dynsym : { *(.dynsym) }
|
||||||
|
.dynstr : { *(.dynstr) }
|
||||||
|
.gnu.version : { *(.gnu.version) }
|
||||||
|
.gnu.version_d : { *(.gnu.version_d) }
|
||||||
|
.gnu.version_r : { *(.gnu.version_r) }
|
||||||
|
|
||||||
|
.note : { *(.note.*) } :text :note
|
||||||
|
|
||||||
|
.eh_frame_hdr : { *(.eh_frame_hdr) } :text :eh_frame_hdr
|
||||||
|
.eh_frame : { KEEP (*(.eh_frame)) } :text
|
||||||
|
|
||||||
|
.dynamic : { *(.dynamic) } :text :dynamic
|
||||||
|
|
||||||
|
.rodata : { *(.rodata*) } :text
|
||||||
|
.data : {
|
||||||
|
*(.data*)
|
||||||
|
*(.sdata*)
|
||||||
|
*(.got.plt) *(.got)
|
||||||
|
*(.gnu.linkonce.d.*)
|
||||||
|
*(.bss*)
|
||||||
|
*(.dynbss*)
|
||||||
|
*(.gnu.linkonce.b.*)
|
||||||
|
}
|
||||||
|
|
||||||
|
.altinstructions : { *(.altinstructions) }
|
||||||
|
.altinstr_replacement : { *(.altinstr_replacement) }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Align the actual code well away from the non-instruction data.
|
||||||
|
* This is the best thing for the I-cache.
|
||||||
|
*/
|
||||||
|
. = ALIGN(0x100);
|
||||||
|
|
||||||
|
.text : { *(.text*) } :text =0x90909090
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Very old versions of ld do not recognize this name token; use the constant.
|
||||||
|
*/
|
||||||
|
#define PT_GNU_EH_FRAME 0x6474e550
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We must supply the ELF program headers explicitly to get just one
|
||||||
|
* PT_LOAD segment, and set the flags explicitly to make segments read-only.
|
||||||
|
*/
|
||||||
|
PHDRS
|
||||||
|
{
|
||||||
|
text PT_LOAD FLAGS(5) FILEHDR PHDRS; /* PF_R|PF_X */
|
||||||
|
dynamic PT_DYNAMIC FLAGS(4); /* PF_R */
|
||||||
|
note PT_NOTE FLAGS(4); /* PF_R */
|
||||||
|
eh_frame_hdr PT_GNU_EH_FRAME;
|
||||||
|
}
|
12
arch/um/sys-x86_64/vdso/vdso-note.S
Normal file
12
arch/um/sys-x86_64/vdso/vdso-note.S
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
/*
|
||||||
|
* This supplies .note.* sections to go into the PT_NOTE inside the vDSO text.
|
||||||
|
* Here we can supply some information useful to userland.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/uts.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
#include <linux/elfnote.h>
|
||||||
|
|
||||||
|
ELFNOTE_START(Linux, 0, "a")
|
||||||
|
.long LINUX_VERSION_CODE
|
||||||
|
ELFNOTE_END
|
10
arch/um/sys-x86_64/vdso/vdso.S
Normal file
10
arch/um/sys-x86_64/vdso/vdso.S
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#include <linux/init.h>
|
||||||
|
|
||||||
|
__INITDATA
|
||||||
|
|
||||||
|
.globl vdso_start, vdso_end
|
||||||
|
vdso_start:
|
||||||
|
.incbin "arch/um/sys-x86_64/vdso/vdso.so"
|
||||||
|
vdso_end:
|
||||||
|
|
||||||
|
__FINIT
|
32
arch/um/sys-x86_64/vdso/vdso.lds.S
Normal file
32
arch/um/sys-x86_64/vdso/vdso.lds.S
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Linker script for 64-bit vDSO.
|
||||||
|
* We #include the file to define the layout details.
|
||||||
|
* Here we only choose the prelinked virtual address.
|
||||||
|
*
|
||||||
|
* This file defines the version script giving the user-exported symbols in
|
||||||
|
* the DSO. We can define local symbols here called VDSO* to make their
|
||||||
|
* values visible using the asm-x86/vdso.h macros from the kernel proper.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define VDSO_PRELINK 0xffffffffff700000
|
||||||
|
#include "vdso-layout.lds.S"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This controls what userland symbols we export from the vDSO.
|
||||||
|
*/
|
||||||
|
VERSION {
|
||||||
|
LINUX_2.6 {
|
||||||
|
global:
|
||||||
|
clock_gettime;
|
||||||
|
__vdso_clock_gettime;
|
||||||
|
gettimeofday;
|
||||||
|
__vdso_gettimeofday;
|
||||||
|
getcpu;
|
||||||
|
__vdso_getcpu;
|
||||||
|
time;
|
||||||
|
__vdso_time;
|
||||||
|
local: *;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
VDSO64_PRELINK = VDSO_PRELINK;
|
74
arch/um/sys-x86_64/vdso/vma.c
Normal file
74
arch/um/sys-x86_64/vdso/vma.c
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 Richard Weinberger <richrd@nod.at>
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <linux/mm.h>
|
||||||
|
#include <asm/page.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
|
||||||
|
unsigned int __read_mostly vdso_enabled = 1;
|
||||||
|
unsigned long um_vdso_addr;
|
||||||
|
|
||||||
|
extern unsigned long task_size;
|
||||||
|
extern char vdso_start[], vdso_end[];
|
||||||
|
|
||||||
|
static struct page **vdsop;
|
||||||
|
|
||||||
|
static int __init init_vdso(void)
|
||||||
|
{
|
||||||
|
struct page *um_vdso;
|
||||||
|
|
||||||
|
BUG_ON(vdso_end - vdso_start > PAGE_SIZE);
|
||||||
|
|
||||||
|
um_vdso_addr = task_size - PAGE_SIZE;
|
||||||
|
|
||||||
|
vdsop = kmalloc(GFP_KERNEL, sizeof(struct page *));
|
||||||
|
if (!vdsop)
|
||||||
|
goto oom;
|
||||||
|
|
||||||
|
um_vdso = alloc_page(GFP_KERNEL);
|
||||||
|
if (!um_vdso) {
|
||||||
|
kfree(vdsop);
|
||||||
|
|
||||||
|
goto oom;
|
||||||
|
}
|
||||||
|
|
||||||
|
copy_page(page_address(um_vdso), vdso_start);
|
||||||
|
*vdsop = um_vdso;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
oom:
|
||||||
|
printk(KERN_ERR "Cannot allocate vdso\n");
|
||||||
|
vdso_enabled = 0;
|
||||||
|
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
subsys_initcall(init_vdso);
|
||||||
|
|
||||||
|
int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
struct mm_struct *mm = current->mm;
|
||||||
|
|
||||||
|
if (!vdso_enabled)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
down_write(&mm->mmap_sem);
|
||||||
|
|
||||||
|
err = install_special_mapping(mm, um_vdso_addr, PAGE_SIZE,
|
||||||
|
VM_READ|VM_EXEC|
|
||||||
|
VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC|
|
||||||
|
VM_ALWAYSDUMP,
|
||||||
|
vdsop);
|
||||||
|
|
||||||
|
up_write(&mm->mmap_sem);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
@ -17,6 +17,7 @@
|
|||||||
#define _XTENSA_UACCESS_H
|
#define _XTENSA_UACCESS_H
|
||||||
|
|
||||||
#include <linux/errno.h>
|
#include <linux/errno.h>
|
||||||
|
#include <linux/prefetch.h>
|
||||||
#include <asm/types.h>
|
#include <asm/types.h>
|
||||||
|
|
||||||
#define VERIFY_READ 0
|
#define VERIFY_READ 0
|
||||||
|
@ -147,6 +147,9 @@ int ptrace_setxregs(struct task_struct *child, void __user *uregs)
|
|||||||
elf_xtregs_t *xtregs = uregs;
|
elf_xtregs_t *xtregs = uregs;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
|
if (!access_ok(VERIFY_READ, uregs, sizeof(elf_xtregs_t)))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
#if XTENSA_HAVE_COPROCESSORS
|
#if XTENSA_HAVE_COPROCESSORS
|
||||||
/* Flush all coprocessors before we overwrite them. */
|
/* Flush all coprocessors before we overwrite them. */
|
||||||
coprocessor_flush_all(ti);
|
coprocessor_flush_all(ti);
|
||||||
|
@ -1642,13 +1642,12 @@ static int sata_dwc_probe(struct platform_device *ofdev)
|
|||||||
const struct ata_port_info *ppi[] = { &pi, NULL };
|
const struct ata_port_info *ppi[] = { &pi, NULL };
|
||||||
|
|
||||||
/* Allocate DWC SATA device */
|
/* Allocate DWC SATA device */
|
||||||
hsdev = kmalloc(sizeof(*hsdev), GFP_KERNEL);
|
hsdev = kzalloc(sizeof(*hsdev), GFP_KERNEL);
|
||||||
if (hsdev == NULL) {
|
if (hsdev == NULL) {
|
||||||
dev_err(&ofdev->dev, "kmalloc failed for hsdev\n");
|
dev_err(&ofdev->dev, "kmalloc failed for hsdev\n");
|
||||||
err = -ENOMEM;
|
err = -ENOMEM;
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
memset(hsdev, 0, sizeof(*hsdev));
|
|
||||||
|
|
||||||
/* Ioremap SATA registers */
|
/* Ioremap SATA registers */
|
||||||
base = of_iomap(ofdev->dev.of_node, 0);
|
base = of_iomap(ofdev->dev.of_node, 0);
|
||||||
|
@ -173,7 +173,6 @@ void proc_ptrace_connector(struct task_struct *task, int ptrace_id)
|
|||||||
struct proc_event *ev;
|
struct proc_event *ev;
|
||||||
struct timespec ts;
|
struct timespec ts;
|
||||||
__u8 buffer[CN_PROC_MSG_SIZE];
|
__u8 buffer[CN_PROC_MSG_SIZE];
|
||||||
struct task_struct *tracer;
|
|
||||||
|
|
||||||
if (atomic_read(&proc_event_num_listeners) < 1)
|
if (atomic_read(&proc_event_num_listeners) < 1)
|
||||||
return;
|
return;
|
||||||
|
@ -45,13 +45,13 @@ static int __init pci_eisa_init(struct pci_dev *pdev,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct pci_device_id pci_eisa_pci_tbl[] = {
|
static struct pci_device_id __initdata pci_eisa_pci_tbl[] = {
|
||||||
{ PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
|
{ PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
|
||||||
PCI_CLASS_BRIDGE_EISA << 8, 0xffff00, 0 },
|
PCI_CLASS_BRIDGE_EISA << 8, 0xffff00, 0 },
|
||||||
{ 0, }
|
{ 0, }
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct pci_driver pci_eisa_driver = {
|
static struct pci_driver __initdata pci_eisa_driver = {
|
||||||
.name = "pci_eisa",
|
.name = "pci_eisa",
|
||||||
.id_table = pci_eisa_pci_tbl,
|
.id_table = pci_eisa_pci_tbl,
|
||||||
.probe = pci_eisa_init,
|
.probe = pci_eisa_init,
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#include <linux/firmware.h>
|
#include <linux/firmware.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/module.h>
|
||||||
#include <linux/sigma.h>
|
#include <linux/sigma.h>
|
||||||
|
|
||||||
/* Return: 0==OK, <0==error, =1 ==no more actions */
|
/* Return: 0==OK, <0==error, =1 ==no more actions */
|
||||||
@ -113,3 +114,5 @@ int process_sigma_firmware(struct i2c_client *client, const char *name)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(process_sigma_firmware);
|
EXPORT_SYMBOL(process_sigma_firmware);
|
||||||
|
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
@ -83,30 +83,26 @@ int drm_sg_alloc(struct drm_device *dev, struct drm_scatter_gather * request)
|
|||||||
if (dev->sg)
|
if (dev->sg)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
entry = kmalloc(sizeof(*entry), GFP_KERNEL);
|
entry = kzalloc(sizeof(*entry), GFP_KERNEL);
|
||||||
if (!entry)
|
if (!entry)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
memset(entry, 0, sizeof(*entry));
|
|
||||||
pages = (request->size + PAGE_SIZE - 1) / PAGE_SIZE;
|
pages = (request->size + PAGE_SIZE - 1) / PAGE_SIZE;
|
||||||
DRM_DEBUG("size=%ld pages=%ld\n", request->size, pages);
|
DRM_DEBUG("size=%ld pages=%ld\n", request->size, pages);
|
||||||
|
|
||||||
entry->pages = pages;
|
entry->pages = pages;
|
||||||
entry->pagelist = kmalloc(pages * sizeof(*entry->pagelist), GFP_KERNEL);
|
entry->pagelist = kcalloc(pages, sizeof(*entry->pagelist), GFP_KERNEL);
|
||||||
if (!entry->pagelist) {
|
if (!entry->pagelist) {
|
||||||
kfree(entry);
|
kfree(entry);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(entry->pagelist, 0, pages * sizeof(*entry->pagelist));
|
entry->busaddr = kcalloc(pages, sizeof(*entry->busaddr), GFP_KERNEL);
|
||||||
|
|
||||||
entry->busaddr = kmalloc(pages * sizeof(*entry->busaddr), GFP_KERNEL);
|
|
||||||
if (!entry->busaddr) {
|
if (!entry->busaddr) {
|
||||||
kfree(entry->pagelist);
|
kfree(entry->pagelist);
|
||||||
kfree(entry);
|
kfree(entry);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
memset((void *)entry->busaddr, 0, pages * sizeof(*entry->busaddr));
|
|
||||||
|
|
||||||
entry->virtual = drm_vmalloc_dma(pages << PAGE_SHIFT);
|
entry->virtual = drm_vmalloc_dma(pages << PAGE_SHIFT);
|
||||||
if (!entry->virtual) {
|
if (!entry->virtual) {
|
||||||
|
@ -139,7 +139,7 @@ static int init_heap(struct mem_block **heap, int start, int size)
|
|||||||
if (!blocks)
|
if (!blocks)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
*heap = kmalloc(sizeof(**heap), GFP_KERNEL);
|
*heap = kzalloc(sizeof(**heap), GFP_KERNEL);
|
||||||
if (!*heap) {
|
if (!*heap) {
|
||||||
kfree(blocks);
|
kfree(blocks);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
@ -150,7 +150,6 @@ static int init_heap(struct mem_block **heap, int start, int size)
|
|||||||
blocks->file_priv = NULL;
|
blocks->file_priv = NULL;
|
||||||
blocks->next = blocks->prev = *heap;
|
blocks->next = blocks->prev = *heap;
|
||||||
|
|
||||||
memset(*heap, 0, sizeof(**heap));
|
|
||||||
(*heap)->file_priv = (struct drm_file *) - 1;
|
(*heap)->file_priv = (struct drm_file *) - 1;
|
||||||
(*heap)->next = (*heap)->prev = blocks;
|
(*heap)->next = (*heap)->prev = blocks;
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -585,11 +585,10 @@ int vmw_overlay_init(struct vmw_private *dev_priv)
|
|||||||
return -ENOSYS;
|
return -ENOSYS;
|
||||||
}
|
}
|
||||||
|
|
||||||
overlay = kmalloc(sizeof(*overlay), GFP_KERNEL);
|
overlay = kzalloc(sizeof(*overlay), GFP_KERNEL);
|
||||||
if (!overlay)
|
if (!overlay)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
memset(overlay, 0, sizeof(*overlay));
|
|
||||||
mutex_init(&overlay->mutex);
|
mutex_init(&overlay->mutex);
|
||||||
for (i = 0; i < VMW_MAX_NUM_STREAMS; i++) {
|
for (i = 0; i < VMW_MAX_NUM_STREAMS; i++) {
|
||||||
overlay->stream[i].buf = NULL;
|
overlay->stream[i].buf = NULL;
|
||||||
|
@ -612,11 +612,9 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data,
|
|||||||
srf->sizes[0].height == 64 &&
|
srf->sizes[0].height == 64 &&
|
||||||
srf->format == SVGA3D_A8R8G8B8) {
|
srf->format == SVGA3D_A8R8G8B8) {
|
||||||
|
|
||||||
srf->snooper.image = kmalloc(64 * 64 * 4, GFP_KERNEL);
|
/* allocate image area and clear it */
|
||||||
/* clear the image */
|
srf->snooper.image = kzalloc(64 * 64 * 4, GFP_KERNEL);
|
||||||
if (srf->snooper.image) {
|
if (!srf->snooper.image) {
|
||||||
memset(srf->snooper.image, 0x00, 64 * 64 * 4);
|
|
||||||
} else {
|
|
||||||
DRM_ERROR("Failed to allocate cursor_image\n");
|
DRM_ERROR("Failed to allocate cursor_image\n");
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto out_err1;
|
goto out_err1;
|
||||||
|
@ -1171,10 +1171,9 @@ static int vga_arb_open(struct inode *inode, struct file *file)
|
|||||||
|
|
||||||
pr_debug("%s\n", __func__);
|
pr_debug("%s\n", __func__);
|
||||||
|
|
||||||
priv = kmalloc(sizeof(struct vga_arb_private), GFP_KERNEL);
|
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
|
||||||
if (priv == NULL)
|
if (priv == NULL)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
memset(priv, 0, sizeof(*priv));
|
|
||||||
spin_lock_init(&priv->lock);
|
spin_lock_init(&priv->lock);
|
||||||
file->private_data = priv;
|
file->private_data = priv;
|
||||||
|
|
||||||
|
@ -365,6 +365,7 @@ config LEDS_NS2
|
|||||||
config LEDS_NETXBIG
|
config LEDS_NETXBIG
|
||||||
tristate "LED support for Big Network series LEDs"
|
tristate "LED support for Big Network series LEDs"
|
||||||
depends on MACH_NET2BIG_V2 || MACH_NET5BIG_V2
|
depends on MACH_NET2BIG_V2 || MACH_NET5BIG_V2
|
||||||
|
depends on LEDS_CLASS
|
||||||
default y
|
default y
|
||||||
help
|
help
|
||||||
This option enable support for LEDs found on the LaCie 2Big
|
This option enable support for LEDs found on the LaCie 2Big
|
||||||
|
@ -68,17 +68,16 @@
|
|||||||
#define LM3530_ALS2_IMP_SHIFT (4)
|
#define LM3530_ALS2_IMP_SHIFT (4)
|
||||||
|
|
||||||
/* Zone Boundary Register defaults */
|
/* Zone Boundary Register defaults */
|
||||||
#define LM3530_DEF_ZB_0 (0x33)
|
#define LM3530_ALS_ZB_MAX (4)
|
||||||
#define LM3530_DEF_ZB_1 (0x66)
|
#define LM3530_ALS_WINDOW_mV (1000)
|
||||||
#define LM3530_DEF_ZB_2 (0x99)
|
#define LM3530_ALS_OFFSET_mV (4)
|
||||||
#define LM3530_DEF_ZB_3 (0xCC)
|
|
||||||
|
|
||||||
/* Zone Target Register defaults */
|
/* Zone Target Register defaults */
|
||||||
#define LM3530_DEF_ZT_0 (0x19)
|
#define LM3530_DEF_ZT_0 (0x7F)
|
||||||
#define LM3530_DEF_ZT_1 (0x33)
|
#define LM3530_DEF_ZT_1 (0x66)
|
||||||
#define LM3530_DEF_ZT_2 (0x4C)
|
#define LM3530_DEF_ZT_2 (0x4C)
|
||||||
#define LM3530_DEF_ZT_3 (0x66)
|
#define LM3530_DEF_ZT_3 (0x33)
|
||||||
#define LM3530_DEF_ZT_4 (0x7F)
|
#define LM3530_DEF_ZT_4 (0x19)
|
||||||
|
|
||||||
struct lm3530_mode_map {
|
struct lm3530_mode_map {
|
||||||
const char *mode;
|
const char *mode;
|
||||||
@ -150,6 +149,8 @@ static int lm3530_init_registers(struct lm3530_data *drvdata)
|
|||||||
u8 als_imp_sel = 0;
|
u8 als_imp_sel = 0;
|
||||||
u8 brightness;
|
u8 brightness;
|
||||||
u8 reg_val[LM3530_REG_MAX];
|
u8 reg_val[LM3530_REG_MAX];
|
||||||
|
u8 zones[LM3530_ALS_ZB_MAX];
|
||||||
|
u32 als_vmin, als_vmax, als_vstep;
|
||||||
struct lm3530_platform_data *pltfm = drvdata->pdata;
|
struct lm3530_platform_data *pltfm = drvdata->pdata;
|
||||||
struct i2c_client *client = drvdata->client;
|
struct i2c_client *client = drvdata->client;
|
||||||
|
|
||||||
@ -161,6 +162,26 @@ static int lm3530_init_registers(struct lm3530_data *drvdata)
|
|||||||
gen_config |= (LM3530_ENABLE_I2C);
|
gen_config |= (LM3530_ENABLE_I2C);
|
||||||
|
|
||||||
if (drvdata->mode == LM3530_BL_MODE_ALS) {
|
if (drvdata->mode == LM3530_BL_MODE_ALS) {
|
||||||
|
if (pltfm->als_vmax == 0) {
|
||||||
|
pltfm->als_vmin = als_vmin = 0;
|
||||||
|
pltfm->als_vmin = als_vmax = LM3530_ALS_WINDOW_mV;
|
||||||
|
}
|
||||||
|
|
||||||
|
als_vmin = pltfm->als_vmin;
|
||||||
|
als_vmax = pltfm->als_vmax;
|
||||||
|
|
||||||
|
if ((als_vmax - als_vmin) > LM3530_ALS_WINDOW_mV)
|
||||||
|
pltfm->als_vmax = als_vmax =
|
||||||
|
als_vmin + LM3530_ALS_WINDOW_mV;
|
||||||
|
|
||||||
|
/* n zone boundary makes n+1 zones */
|
||||||
|
als_vstep = (als_vmax - als_vmin) / (LM3530_ALS_ZB_MAX + 1);
|
||||||
|
|
||||||
|
for (i = 0; i < LM3530_ALS_ZB_MAX; i++)
|
||||||
|
zones[i] = (((als_vmin + LM3530_ALS_OFFSET_mV) +
|
||||||
|
als_vstep + (i * als_vstep)) * LED_FULL)
|
||||||
|
/ 1000;
|
||||||
|
|
||||||
als_config =
|
als_config =
|
||||||
(pltfm->als_avrg_time << LM3530_ALS_AVG_TIME_SHIFT) |
|
(pltfm->als_avrg_time << LM3530_ALS_AVG_TIME_SHIFT) |
|
||||||
(LM3530_ENABLE_ALS) |
|
(LM3530_ENABLE_ALS) |
|
||||||
@ -169,6 +190,7 @@ static int lm3530_init_registers(struct lm3530_data *drvdata)
|
|||||||
als_imp_sel =
|
als_imp_sel =
|
||||||
(pltfm->als1_resistor_sel << LM3530_ALS1_IMP_SHIFT) |
|
(pltfm->als1_resistor_sel << LM3530_ALS1_IMP_SHIFT) |
|
||||||
(pltfm->als2_resistor_sel << LM3530_ALS2_IMP_SHIFT);
|
(pltfm->als2_resistor_sel << LM3530_ALS2_IMP_SHIFT);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (drvdata->mode == LM3530_BL_MODE_PWM)
|
if (drvdata->mode == LM3530_BL_MODE_PWM)
|
||||||
@ -190,10 +212,10 @@ static int lm3530_init_registers(struct lm3530_data *drvdata)
|
|||||||
reg_val[3] = 0x00; /* LM3530_ALS_ZONE_REG */
|
reg_val[3] = 0x00; /* LM3530_ALS_ZONE_REG */
|
||||||
reg_val[4] = als_imp_sel; /* LM3530_ALS_IMP_SELECT */
|
reg_val[4] = als_imp_sel; /* LM3530_ALS_IMP_SELECT */
|
||||||
reg_val[5] = brightness; /* LM3530_BRT_CTRL_REG */
|
reg_val[5] = brightness; /* LM3530_BRT_CTRL_REG */
|
||||||
reg_val[6] = LM3530_DEF_ZB_0; /* LM3530_ALS_ZB0_REG */
|
reg_val[6] = zones[0]; /* LM3530_ALS_ZB0_REG */
|
||||||
reg_val[7] = LM3530_DEF_ZB_1; /* LM3530_ALS_ZB1_REG */
|
reg_val[7] = zones[1]; /* LM3530_ALS_ZB1_REG */
|
||||||
reg_val[8] = LM3530_DEF_ZB_2; /* LM3530_ALS_ZB2_REG */
|
reg_val[8] = zones[2]; /* LM3530_ALS_ZB2_REG */
|
||||||
reg_val[9] = LM3530_DEF_ZB_3; /* LM3530_ALS_ZB3_REG */
|
reg_val[9] = zones[3]; /* LM3530_ALS_ZB3_REG */
|
||||||
reg_val[10] = LM3530_DEF_ZT_0; /* LM3530_ALS_Z0T_REG */
|
reg_val[10] = LM3530_DEF_ZT_0; /* LM3530_ALS_Z0T_REG */
|
||||||
reg_val[11] = LM3530_DEF_ZT_1; /* LM3530_ALS_Z1T_REG */
|
reg_val[11] = LM3530_DEF_ZT_1; /* LM3530_ALS_Z1T_REG */
|
||||||
reg_val[12] = LM3530_DEF_ZT_2; /* LM3530_ALS_Z2T_REG */
|
reg_val[12] = LM3530_DEF_ZT_2; /* LM3530_ALS_Z2T_REG */
|
||||||
@ -265,6 +287,24 @@ static void lm3530_brightness_set(struct led_classdev *led_cdev,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static ssize_t lm3530_mode_get(struct device *dev,
|
||||||
|
struct device_attribute *attr, char *buf)
|
||||||
|
{
|
||||||
|
struct i2c_client *client = container_of(
|
||||||
|
dev->parent, struct i2c_client, dev);
|
||||||
|
struct lm3530_data *drvdata = i2c_get_clientdata(client);
|
||||||
|
int i, len = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(mode_map); i++)
|
||||||
|
if (drvdata->mode == mode_map[i].mode_val)
|
||||||
|
len += sprintf(buf + len, "[%s] ", mode_map[i].mode);
|
||||||
|
else
|
||||||
|
len += sprintf(buf + len, "%s ", mode_map[i].mode);
|
||||||
|
|
||||||
|
len += sprintf(buf + len, "\n");
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
static ssize_t lm3530_mode_set(struct device *dev, struct device_attribute
|
static ssize_t lm3530_mode_set(struct device *dev, struct device_attribute
|
||||||
*attr, const char *buf, size_t size)
|
*attr, const char *buf, size_t size)
|
||||||
@ -298,8 +338,7 @@ static ssize_t lm3530_mode_set(struct device *dev, struct device_attribute
|
|||||||
|
|
||||||
return sizeof(drvdata->mode);
|
return sizeof(drvdata->mode);
|
||||||
}
|
}
|
||||||
|
static DEVICE_ATTR(mode, 0644, lm3530_mode_get, lm3530_mode_set);
|
||||||
static DEVICE_ATTR(mode, 0644, NULL, lm3530_mode_set);
|
|
||||||
|
|
||||||
static int __devinit lm3530_probe(struct i2c_client *client,
|
static int __devinit lm3530_probe(struct i2c_client *client,
|
||||||
const struct i2c_device_id *id)
|
const struct i2c_device_id *id)
|
||||||
|
@ -744,7 +744,7 @@ fail1:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int lp5521_remove(struct i2c_client *client)
|
static int __devexit lp5521_remove(struct i2c_client *client)
|
||||||
{
|
{
|
||||||
struct lp5521_chip *chip = i2c_get_clientdata(client);
|
struct lp5521_chip *chip = i2c_get_clientdata(client);
|
||||||
int i;
|
int i;
|
||||||
@ -775,7 +775,7 @@ static struct i2c_driver lp5521_driver = {
|
|||||||
.name = "lp5521",
|
.name = "lp5521",
|
||||||
},
|
},
|
||||||
.probe = lp5521_probe,
|
.probe = lp5521_probe,
|
||||||
.remove = lp5521_remove,
|
.remove = __devexit_p(lp5521_remove),
|
||||||
.id_table = lp5521_id,
|
.id_table = lp5521_id,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -127,17 +127,19 @@ static int __devinit sunfire_led_generic_probe(struct platform_device *pdev,
|
|||||||
struct led_type *types)
|
struct led_type *types)
|
||||||
{
|
{
|
||||||
struct sunfire_drvdata *p;
|
struct sunfire_drvdata *p;
|
||||||
int i, err = -EINVAL;
|
int i, err;
|
||||||
|
|
||||||
if (pdev->num_resources != 1) {
|
if (pdev->num_resources != 1) {
|
||||||
printk(KERN_ERR PFX "Wrong number of resources %d, should be 1\n",
|
printk(KERN_ERR PFX "Wrong number of resources %d, should be 1\n",
|
||||||
pdev->num_resources);
|
pdev->num_resources);
|
||||||
|
err = -EINVAL;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
p = kzalloc(sizeof(*p), GFP_KERNEL);
|
p = kzalloc(sizeof(*p), GFP_KERNEL);
|
||||||
if (!p) {
|
if (!p) {
|
||||||
printk(KERN_ERR PFX "Could not allocate struct sunfire_drvdata\n");
|
printk(KERN_ERR PFX "Could not allocate struct sunfire_drvdata\n");
|
||||||
|
err = -ENOMEM;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,14 +162,14 @@ static int __devinit sunfire_led_generic_probe(struct platform_device *pdev,
|
|||||||
|
|
||||||
dev_set_drvdata(&pdev->dev, p);
|
dev_set_drvdata(&pdev->dev, p);
|
||||||
|
|
||||||
err = 0;
|
return 0;
|
||||||
out:
|
|
||||||
return err;
|
|
||||||
|
|
||||||
out_unregister_led_cdevs:
|
out_unregister_led_cdevs:
|
||||||
for (i--; i >= 0; i--)
|
for (i--; i >= 0; i--)
|
||||||
led_classdev_unregister(&p->leds[i].led_cdev);
|
led_classdev_unregister(&p->leds[i].led_cdev);
|
||||||
goto out;
|
kfree(p);
|
||||||
|
out:
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __devexit sunfire_led_generic_remove(struct platform_device *pdev)
|
static int __devexit sunfire_led_generic_remove(struct platform_device *pdev)
|
||||||
|
@ -245,8 +245,7 @@ config SGI_XP
|
|||||||
|
|
||||||
config CS5535_MFGPT
|
config CS5535_MFGPT
|
||||||
tristate "CS5535/CS5536 Geode Multi-Function General Purpose Timer (MFGPT) support"
|
tristate "CS5535/CS5536 Geode Multi-Function General Purpose Timer (MFGPT) support"
|
||||||
depends on PCI
|
depends on PCI && X86 && MFD_CS5535
|
||||||
depends on X86
|
|
||||||
default n
|
default n
|
||||||
help
|
help
|
||||||
This driver provides access to MFGPT functionality for other
|
This driver provides access to MFGPT functionality for other
|
||||||
@ -490,6 +489,15 @@ config PCH_PHUB
|
|||||||
To compile this driver as a module, choose M here: the module will
|
To compile this driver as a module, choose M here: the module will
|
||||||
be called pch_phub.
|
be called pch_phub.
|
||||||
|
|
||||||
|
config USB_SWITCH_FSA9480
|
||||||
|
tristate "FSA9480 USB Switch"
|
||||||
|
depends on I2C
|
||||||
|
help
|
||||||
|
The FSA9480 is a USB port accessory detector and switch.
|
||||||
|
The FSA9480 is fully controlled using I2C and enables USB data,
|
||||||
|
stereo and mono audio, video, microphone and UART data to use
|
||||||
|
a common connector port.
|
||||||
|
|
||||||
source "drivers/misc/c2port/Kconfig"
|
source "drivers/misc/c2port/Kconfig"
|
||||||
source "drivers/misc/eeprom/Kconfig"
|
source "drivers/misc/eeprom/Kconfig"
|
||||||
source "drivers/misc/cb710/Kconfig"
|
source "drivers/misc/cb710/Kconfig"
|
||||||
|
@ -46,3 +46,4 @@ obj-y += ti-st/
|
|||||||
obj-$(CONFIG_AB8500_PWM) += ab8500-pwm.o
|
obj-$(CONFIG_AB8500_PWM) += ab8500-pwm.o
|
||||||
obj-y += lis3lv02d/
|
obj-y += lis3lv02d/
|
||||||
obj-y += carma/
|
obj-y += carma/
|
||||||
|
obj-$(CONFIG_USB_SWITCH_FSA9480) += fsa9480.o
|
||||||
|
@ -70,4 +70,29 @@ config EEPROM_93CX6
|
|||||||
|
|
||||||
If unsure, say N.
|
If unsure, say N.
|
||||||
|
|
||||||
|
config EEPROM_93XX46
|
||||||
|
tristate "Microwire EEPROM 93XX46 support"
|
||||||
|
depends on SPI && SYSFS
|
||||||
|
help
|
||||||
|
Driver for the microwire EEPROM chipsets 93xx46x. The driver
|
||||||
|
supports both read and write commands and also the command to
|
||||||
|
erase the whole EEPROM.
|
||||||
|
|
||||||
|
This driver can also be built as a module. If so, the module
|
||||||
|
will be called eeprom_93xx46.
|
||||||
|
|
||||||
|
If unsure, say N.
|
||||||
|
|
||||||
|
config EEPROM_DIGSY_MTC_CFG
|
||||||
|
bool "DigsyMTC display configuration EEPROMs device"
|
||||||
|
depends on PPC_MPC5200_GPIO && GPIOLIB && SPI_GPIO
|
||||||
|
help
|
||||||
|
This option enables access to display configuration EEPROMs
|
||||||
|
on digsy_mtc board. You have to additionally select Microwire
|
||||||
|
EEPROM 93XX46 driver. sysfs entries will be created for that
|
||||||
|
EEPROM allowing to read/write the configuration data or to
|
||||||
|
erase the whole EEPROM.
|
||||||
|
|
||||||
|
If unsure, say N.
|
||||||
|
|
||||||
endmenu
|
endmenu
|
||||||
|
@ -3,3 +3,5 @@ obj-$(CONFIG_EEPROM_AT25) += at25.o
|
|||||||
obj-$(CONFIG_EEPROM_LEGACY) += eeprom.o
|
obj-$(CONFIG_EEPROM_LEGACY) += eeprom.o
|
||||||
obj-$(CONFIG_EEPROM_MAX6875) += max6875.o
|
obj-$(CONFIG_EEPROM_MAX6875) += max6875.o
|
||||||
obj-$(CONFIG_EEPROM_93CX6) += eeprom_93cx6.o
|
obj-$(CONFIG_EEPROM_93CX6) += eeprom_93cx6.o
|
||||||
|
obj-$(CONFIG_EEPROM_93XX46) += eeprom_93xx46.o
|
||||||
|
obj-$(CONFIG_EEPROM_DIGSY_MTC_CFG) += digsy_mtc_eeprom.o
|
||||||
|
85
drivers/misc/eeprom/digsy_mtc_eeprom.c
Normal file
85
drivers/misc/eeprom/digsy_mtc_eeprom.c
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
* EEPROMs access control driver for display configuration EEPROMs
|
||||||
|
* on DigsyMTC board.
|
||||||
|
*
|
||||||
|
* (C) 2011 DENX Software Engineering, Anatolij Gustschin <agust@denx.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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/gpio.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/spi/spi.h>
|
||||||
|
#include <linux/spi/spi_gpio.h>
|
||||||
|
#include <linux/eeprom_93xx46.h>
|
||||||
|
|
||||||
|
#define GPIO_EEPROM_CLK 216
|
||||||
|
#define GPIO_EEPROM_CS 210
|
||||||
|
#define GPIO_EEPROM_DI 217
|
||||||
|
#define GPIO_EEPROM_DO 249
|
||||||
|
#define GPIO_EEPROM_OE 255
|
||||||
|
#define EE_SPI_BUS_NUM 1
|
||||||
|
|
||||||
|
static void digsy_mtc_op_prepare(void *p)
|
||||||
|
{
|
||||||
|
/* enable */
|
||||||
|
gpio_set_value(GPIO_EEPROM_OE, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void digsy_mtc_op_finish(void *p)
|
||||||
|
{
|
||||||
|
/* disable */
|
||||||
|
gpio_set_value(GPIO_EEPROM_OE, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct eeprom_93xx46_platform_data digsy_mtc_eeprom_data = {
|
||||||
|
.flags = EE_ADDR8,
|
||||||
|
.prepare = digsy_mtc_op_prepare,
|
||||||
|
.finish = digsy_mtc_op_finish,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct spi_gpio_platform_data eeprom_spi_gpio_data = {
|
||||||
|
.sck = GPIO_EEPROM_CLK,
|
||||||
|
.mosi = GPIO_EEPROM_DI,
|
||||||
|
.miso = GPIO_EEPROM_DO,
|
||||||
|
.num_chipselect = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct platform_device digsy_mtc_eeprom = {
|
||||||
|
.name = "spi_gpio",
|
||||||
|
.id = EE_SPI_BUS_NUM,
|
||||||
|
.dev = {
|
||||||
|
.platform_data = &eeprom_spi_gpio_data,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct spi_board_info digsy_mtc_eeprom_info[] __initdata = {
|
||||||
|
{
|
||||||
|
.modalias = "93xx46",
|
||||||
|
.max_speed_hz = 1000000,
|
||||||
|
.bus_num = EE_SPI_BUS_NUM,
|
||||||
|
.chip_select = 0,
|
||||||
|
.mode = SPI_MODE_0,
|
||||||
|
.controller_data = (void *)GPIO_EEPROM_CS,
|
||||||
|
.platform_data = &digsy_mtc_eeprom_data,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init digsy_mtc_eeprom_devices_init(void)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = gpio_request_one(GPIO_EEPROM_OE, GPIOF_OUT_INIT_HIGH,
|
||||||
|
"93xx46 EEPROMs OE");
|
||||||
|
if (ret) {
|
||||||
|
pr_err("can't request gpio %d\n", GPIO_EEPROM_OE);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
spi_register_board_info(digsy_mtc_eeprom_info,
|
||||||
|
ARRAY_SIZE(digsy_mtc_eeprom_info));
|
||||||
|
return platform_device_register(&digsy_mtc_eeprom);
|
||||||
|
}
|
||||||
|
device_initcall(digsy_mtc_eeprom_devices_init);
|
410
drivers/misc/eeprom/eeprom_93xx46.c
Normal file
410
drivers/misc/eeprom/eeprom_93xx46.c
Normal file
@ -0,0 +1,410 @@
|
|||||||
|
/*
|
||||||
|
* Driver for 93xx46 EEPROMs
|
||||||
|
*
|
||||||
|
* (C) 2011 DENX Software Engineering, Anatolij Gustschin <agust@denx.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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/device.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/mutex.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/spi/spi.h>
|
||||||
|
#include <linux/sysfs.h>
|
||||||
|
#include <linux/eeprom_93xx46.h>
|
||||||
|
|
||||||
|
#define OP_START 0x4
|
||||||
|
#define OP_WRITE (OP_START | 0x1)
|
||||||
|
#define OP_READ (OP_START | 0x2)
|
||||||
|
#define ADDR_EWDS 0x00
|
||||||
|
#define ADDR_ERAL 0x20
|
||||||
|
#define ADDR_EWEN 0x30
|
||||||
|
|
||||||
|
struct eeprom_93xx46_dev {
|
||||||
|
struct spi_device *spi;
|
||||||
|
struct eeprom_93xx46_platform_data *pdata;
|
||||||
|
struct bin_attribute bin;
|
||||||
|
struct mutex lock;
|
||||||
|
int addrlen;
|
||||||
|
};
|
||||||
|
|
||||||
|
static ssize_t
|
||||||
|
eeprom_93xx46_bin_read(struct file *filp, struct kobject *kobj,
|
||||||
|
struct bin_attribute *bin_attr,
|
||||||
|
char *buf, loff_t off, size_t count)
|
||||||
|
{
|
||||||
|
struct eeprom_93xx46_dev *edev;
|
||||||
|
struct device *dev;
|
||||||
|
struct spi_message m;
|
||||||
|
struct spi_transfer t[2];
|
||||||
|
int bits, ret;
|
||||||
|
u16 cmd_addr;
|
||||||
|
|
||||||
|
dev = container_of(kobj, struct device, kobj);
|
||||||
|
edev = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
if (unlikely(off >= edev->bin.size))
|
||||||
|
return 0;
|
||||||
|
if ((off + count) > edev->bin.size)
|
||||||
|
count = edev->bin.size - off;
|
||||||
|
if (unlikely(!count))
|
||||||
|
return count;
|
||||||
|
|
||||||
|
cmd_addr = OP_READ << edev->addrlen;
|
||||||
|
|
||||||
|
if (edev->addrlen == 7) {
|
||||||
|
cmd_addr |= off & 0x7f;
|
||||||
|
bits = 10;
|
||||||
|
} else {
|
||||||
|
cmd_addr |= off & 0x3f;
|
||||||
|
bits = 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_dbg(&edev->spi->dev, "read cmd 0x%x, %d Hz\n",
|
||||||
|
cmd_addr, edev->spi->max_speed_hz);
|
||||||
|
|
||||||
|
spi_message_init(&m);
|
||||||
|
memset(t, 0, sizeof(t));
|
||||||
|
|
||||||
|
t[0].tx_buf = (char *)&cmd_addr;
|
||||||
|
t[0].len = 2;
|
||||||
|
t[0].bits_per_word = bits;
|
||||||
|
spi_message_add_tail(&t[0], &m);
|
||||||
|
|
||||||
|
t[1].rx_buf = buf;
|
||||||
|
t[1].len = count;
|
||||||
|
t[1].bits_per_word = 8;
|
||||||
|
spi_message_add_tail(&t[1], &m);
|
||||||
|
|
||||||
|
mutex_lock(&edev->lock);
|
||||||
|
|
||||||
|
if (edev->pdata->prepare)
|
||||||
|
edev->pdata->prepare(edev);
|
||||||
|
|
||||||
|
ret = spi_sync(edev->spi, &m);
|
||||||
|
/* have to wait at least Tcsl ns */
|
||||||
|
ndelay(250);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&edev->spi->dev, "read %zu bytes at %d: err. %d\n",
|
||||||
|
count, (int)off, ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (edev->pdata->finish)
|
||||||
|
edev->pdata->finish(edev);
|
||||||
|
|
||||||
|
mutex_unlock(&edev->lock);
|
||||||
|
return ret ? : count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int eeprom_93xx46_ew(struct eeprom_93xx46_dev *edev, int is_on)
|
||||||
|
{
|
||||||
|
struct spi_message m;
|
||||||
|
struct spi_transfer t;
|
||||||
|
int bits, ret;
|
||||||
|
u16 cmd_addr;
|
||||||
|
|
||||||
|
cmd_addr = OP_START << edev->addrlen;
|
||||||
|
if (edev->addrlen == 7) {
|
||||||
|
cmd_addr |= (is_on ? ADDR_EWEN : ADDR_EWDS) << 1;
|
||||||
|
bits = 10;
|
||||||
|
} else {
|
||||||
|
cmd_addr |= (is_on ? ADDR_EWEN : ADDR_EWDS);
|
||||||
|
bits = 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_dbg(&edev->spi->dev, "ew cmd 0x%04x\n", cmd_addr);
|
||||||
|
|
||||||
|
spi_message_init(&m);
|
||||||
|
memset(&t, 0, sizeof(t));
|
||||||
|
|
||||||
|
t.tx_buf = &cmd_addr;
|
||||||
|
t.len = 2;
|
||||||
|
t.bits_per_word = bits;
|
||||||
|
spi_message_add_tail(&t, &m);
|
||||||
|
|
||||||
|
mutex_lock(&edev->lock);
|
||||||
|
|
||||||
|
if (edev->pdata->prepare)
|
||||||
|
edev->pdata->prepare(edev);
|
||||||
|
|
||||||
|
ret = spi_sync(edev->spi, &m);
|
||||||
|
/* have to wait at least Tcsl ns */
|
||||||
|
ndelay(250);
|
||||||
|
if (ret)
|
||||||
|
dev_err(&edev->spi->dev, "erase/write %sable error %d\n",
|
||||||
|
is_on ? "en" : "dis", ret);
|
||||||
|
|
||||||
|
if (edev->pdata->finish)
|
||||||
|
edev->pdata->finish(edev);
|
||||||
|
|
||||||
|
mutex_unlock(&edev->lock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t
|
||||||
|
eeprom_93xx46_write_word(struct eeprom_93xx46_dev *edev,
|
||||||
|
const char *buf, unsigned off)
|
||||||
|
{
|
||||||
|
struct spi_message m;
|
||||||
|
struct spi_transfer t[2];
|
||||||
|
int bits, data_len, ret;
|
||||||
|
u16 cmd_addr;
|
||||||
|
|
||||||
|
cmd_addr = OP_WRITE << edev->addrlen;
|
||||||
|
|
||||||
|
if (edev->addrlen == 7) {
|
||||||
|
cmd_addr |= off & 0x7f;
|
||||||
|
bits = 10;
|
||||||
|
data_len = 1;
|
||||||
|
} else {
|
||||||
|
cmd_addr |= off & 0x3f;
|
||||||
|
bits = 9;
|
||||||
|
data_len = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_dbg(&edev->spi->dev, "write cmd 0x%x\n", cmd_addr);
|
||||||
|
|
||||||
|
spi_message_init(&m);
|
||||||
|
memset(t, 0, sizeof(t));
|
||||||
|
|
||||||
|
t[0].tx_buf = (char *)&cmd_addr;
|
||||||
|
t[0].len = 2;
|
||||||
|
t[0].bits_per_word = bits;
|
||||||
|
spi_message_add_tail(&t[0], &m);
|
||||||
|
|
||||||
|
t[1].tx_buf = buf;
|
||||||
|
t[1].len = data_len;
|
||||||
|
t[1].bits_per_word = 8;
|
||||||
|
spi_message_add_tail(&t[1], &m);
|
||||||
|
|
||||||
|
ret = spi_sync(edev->spi, &m);
|
||||||
|
/* have to wait program cycle time Twc ms */
|
||||||
|
mdelay(6);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t
|
||||||
|
eeprom_93xx46_bin_write(struct file *filp, struct kobject *kobj,
|
||||||
|
struct bin_attribute *bin_attr,
|
||||||
|
char *buf, loff_t off, size_t count)
|
||||||
|
{
|
||||||
|
struct eeprom_93xx46_dev *edev;
|
||||||
|
struct device *dev;
|
||||||
|
int i, ret, step = 1;
|
||||||
|
|
||||||
|
dev = container_of(kobj, struct device, kobj);
|
||||||
|
edev = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
if (unlikely(off >= edev->bin.size))
|
||||||
|
return 0;
|
||||||
|
if ((off + count) > edev->bin.size)
|
||||||
|
count = edev->bin.size - off;
|
||||||
|
if (unlikely(!count))
|
||||||
|
return count;
|
||||||
|
|
||||||
|
/* only write even number of bytes on 16-bit devices */
|
||||||
|
if (edev->addrlen == 6) {
|
||||||
|
step = 2;
|
||||||
|
count &= ~1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* erase/write enable */
|
||||||
|
ret = eeprom_93xx46_ew(edev, 1);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
mutex_lock(&edev->lock);
|
||||||
|
|
||||||
|
if (edev->pdata->prepare)
|
||||||
|
edev->pdata->prepare(edev);
|
||||||
|
|
||||||
|
for (i = 0; i < count; i += step) {
|
||||||
|
ret = eeprom_93xx46_write_word(edev, &buf[i], off + i);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&edev->spi->dev, "write failed at %d: %d\n",
|
||||||
|
(int)off + i, ret);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (edev->pdata->finish)
|
||||||
|
edev->pdata->finish(edev);
|
||||||
|
|
||||||
|
mutex_unlock(&edev->lock);
|
||||||
|
|
||||||
|
/* erase/write disable */
|
||||||
|
eeprom_93xx46_ew(edev, 0);
|
||||||
|
return ret ? : count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int eeprom_93xx46_eral(struct eeprom_93xx46_dev *edev)
|
||||||
|
{
|
||||||
|
struct eeprom_93xx46_platform_data *pd = edev->pdata;
|
||||||
|
struct spi_message m;
|
||||||
|
struct spi_transfer t;
|
||||||
|
int bits, ret;
|
||||||
|
u16 cmd_addr;
|
||||||
|
|
||||||
|
cmd_addr = OP_START << edev->addrlen;
|
||||||
|
if (edev->addrlen == 7) {
|
||||||
|
cmd_addr |= ADDR_ERAL << 1;
|
||||||
|
bits = 10;
|
||||||
|
} else {
|
||||||
|
cmd_addr |= ADDR_ERAL;
|
||||||
|
bits = 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
spi_message_init(&m);
|
||||||
|
memset(&t, 0, sizeof(t));
|
||||||
|
|
||||||
|
t.tx_buf = &cmd_addr;
|
||||||
|
t.len = 2;
|
||||||
|
t.bits_per_word = bits;
|
||||||
|
spi_message_add_tail(&t, &m);
|
||||||
|
|
||||||
|
mutex_lock(&edev->lock);
|
||||||
|
|
||||||
|
if (edev->pdata->prepare)
|
||||||
|
edev->pdata->prepare(edev);
|
||||||
|
|
||||||
|
ret = spi_sync(edev->spi, &m);
|
||||||
|
if (ret)
|
||||||
|
dev_err(&edev->spi->dev, "erase error %d\n", ret);
|
||||||
|
/* have to wait erase cycle time Tec ms */
|
||||||
|
mdelay(6);
|
||||||
|
|
||||||
|
if (pd->finish)
|
||||||
|
pd->finish(edev);
|
||||||
|
|
||||||
|
mutex_unlock(&edev->lock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t eeprom_93xx46_store_erase(struct device *dev,
|
||||||
|
struct device_attribute *attr,
|
||||||
|
const char *buf, size_t count)
|
||||||
|
{
|
||||||
|
struct eeprom_93xx46_dev *edev = dev_get_drvdata(dev);
|
||||||
|
int erase = 0, ret;
|
||||||
|
|
||||||
|
sscanf(buf, "%d", &erase);
|
||||||
|
if (erase) {
|
||||||
|
ret = eeprom_93xx46_ew(edev, 1);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
ret = eeprom_93xx46_eral(edev);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
ret = eeprom_93xx46_ew(edev, 0);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
static DEVICE_ATTR(erase, S_IWUSR, NULL, eeprom_93xx46_store_erase);
|
||||||
|
|
||||||
|
static int __devinit eeprom_93xx46_probe(struct spi_device *spi)
|
||||||
|
{
|
||||||
|
struct eeprom_93xx46_platform_data *pd;
|
||||||
|
struct eeprom_93xx46_dev *edev;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
pd = spi->dev.platform_data;
|
||||||
|
if (!pd) {
|
||||||
|
dev_err(&spi->dev, "missing platform data\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
edev = kzalloc(sizeof(*edev), GFP_KERNEL);
|
||||||
|
if (!edev)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
if (pd->flags & EE_ADDR8)
|
||||||
|
edev->addrlen = 7;
|
||||||
|
else if (pd->flags & EE_ADDR16)
|
||||||
|
edev->addrlen = 6;
|
||||||
|
else {
|
||||||
|
dev_err(&spi->dev, "unspecified address type\n");
|
||||||
|
err = -EINVAL;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_init(&edev->lock);
|
||||||
|
|
||||||
|
edev->spi = spi_dev_get(spi);
|
||||||
|
edev->pdata = pd;
|
||||||
|
|
||||||
|
sysfs_bin_attr_init(&edev->bin);
|
||||||
|
edev->bin.attr.name = "eeprom";
|
||||||
|
edev->bin.attr.mode = S_IRUSR;
|
||||||
|
edev->bin.read = eeprom_93xx46_bin_read;
|
||||||
|
edev->bin.size = 128;
|
||||||
|
if (!(pd->flags & EE_READONLY)) {
|
||||||
|
edev->bin.write = eeprom_93xx46_bin_write;
|
||||||
|
edev->bin.attr.mode |= S_IWUSR;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = sysfs_create_bin_file(&spi->dev.kobj, &edev->bin);
|
||||||
|
if (err)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
dev_info(&spi->dev, "%d-bit eeprom %s\n",
|
||||||
|
(pd->flags & EE_ADDR8) ? 8 : 16,
|
||||||
|
(pd->flags & EE_READONLY) ? "(readonly)" : "");
|
||||||
|
|
||||||
|
if (!(pd->flags & EE_READONLY)) {
|
||||||
|
if (device_create_file(&spi->dev, &dev_attr_erase))
|
||||||
|
dev_err(&spi->dev, "can't create erase interface\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_set_drvdata(&spi->dev, edev);
|
||||||
|
return 0;
|
||||||
|
fail:
|
||||||
|
kfree(edev);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __devexit eeprom_93xx46_remove(struct spi_device *spi)
|
||||||
|
{
|
||||||
|
struct eeprom_93xx46_dev *edev = dev_get_drvdata(&spi->dev);
|
||||||
|
|
||||||
|
if (!(edev->pdata->flags & EE_READONLY))
|
||||||
|
device_remove_file(&spi->dev, &dev_attr_erase);
|
||||||
|
|
||||||
|
sysfs_remove_bin_file(&spi->dev.kobj, &edev->bin);
|
||||||
|
dev_set_drvdata(&spi->dev, NULL);
|
||||||
|
kfree(edev);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct spi_driver eeprom_93xx46_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = "93xx46",
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
},
|
||||||
|
.probe = eeprom_93xx46_probe,
|
||||||
|
.remove = __devexit_p(eeprom_93xx46_remove),
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init eeprom_93xx46_init(void)
|
||||||
|
{
|
||||||
|
return spi_register_driver(&eeprom_93xx46_driver);
|
||||||
|
}
|
||||||
|
module_init(eeprom_93xx46_init);
|
||||||
|
|
||||||
|
static void __exit eeprom_93xx46_exit(void)
|
||||||
|
{
|
||||||
|
spi_unregister_driver(&eeprom_93xx46_driver);
|
||||||
|
}
|
||||||
|
module_exit(eeprom_93xx46_exit);
|
||||||
|
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
MODULE_DESCRIPTION("Driver for 93xx46 EEPROMs");
|
||||||
|
MODULE_AUTHOR("Anatolij Gustschin <agust@denx.de>");
|
||||||
|
MODULE_ALIAS("spi:93xx46");
|
557
drivers/misc/fsa9480.c
Normal file
557
drivers/misc/fsa9480.c
Normal file
@ -0,0 +1,557 @@
|
|||||||
|
/*
|
||||||
|
* fsa9480.c - FSA9480 micro USB switch device driver
|
||||||
|
*
|
||||||
|
* Copyright (C) 2010 Samsung Electronics
|
||||||
|
* Minkyu Kang <mk7.kang@samsung.com>
|
||||||
|
* Wonguk Jeong <wonguk.jeong@samsung.com>
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/err.h>
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/platform_data/fsa9480.h>
|
||||||
|
#include <linux/irq.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/workqueue.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/pm_runtime.h>
|
||||||
|
|
||||||
|
/* FSA9480 I2C registers */
|
||||||
|
#define FSA9480_REG_DEVID 0x01
|
||||||
|
#define FSA9480_REG_CTRL 0x02
|
||||||
|
#define FSA9480_REG_INT1 0x03
|
||||||
|
#define FSA9480_REG_INT2 0x04
|
||||||
|
#define FSA9480_REG_INT1_MASK 0x05
|
||||||
|
#define FSA9480_REG_INT2_MASK 0x06
|
||||||
|
#define FSA9480_REG_ADC 0x07
|
||||||
|
#define FSA9480_REG_TIMING1 0x08
|
||||||
|
#define FSA9480_REG_TIMING2 0x09
|
||||||
|
#define FSA9480_REG_DEV_T1 0x0a
|
||||||
|
#define FSA9480_REG_DEV_T2 0x0b
|
||||||
|
#define FSA9480_REG_BTN1 0x0c
|
||||||
|
#define FSA9480_REG_BTN2 0x0d
|
||||||
|
#define FSA9480_REG_CK 0x0e
|
||||||
|
#define FSA9480_REG_CK_INT1 0x0f
|
||||||
|
#define FSA9480_REG_CK_INT2 0x10
|
||||||
|
#define FSA9480_REG_CK_INTMASK1 0x11
|
||||||
|
#define FSA9480_REG_CK_INTMASK2 0x12
|
||||||
|
#define FSA9480_REG_MANSW1 0x13
|
||||||
|
#define FSA9480_REG_MANSW2 0x14
|
||||||
|
|
||||||
|
/* Control */
|
||||||
|
#define CON_SWITCH_OPEN (1 << 4)
|
||||||
|
#define CON_RAW_DATA (1 << 3)
|
||||||
|
#define CON_MANUAL_SW (1 << 2)
|
||||||
|
#define CON_WAIT (1 << 1)
|
||||||
|
#define CON_INT_MASK (1 << 0)
|
||||||
|
#define CON_MASK (CON_SWITCH_OPEN | CON_RAW_DATA | \
|
||||||
|
CON_MANUAL_SW | CON_WAIT)
|
||||||
|
|
||||||
|
/* Device Type 1 */
|
||||||
|
#define DEV_USB_OTG (1 << 7)
|
||||||
|
#define DEV_DEDICATED_CHG (1 << 6)
|
||||||
|
#define DEV_USB_CHG (1 << 5)
|
||||||
|
#define DEV_CAR_KIT (1 << 4)
|
||||||
|
#define DEV_UART (1 << 3)
|
||||||
|
#define DEV_USB (1 << 2)
|
||||||
|
#define DEV_AUDIO_2 (1 << 1)
|
||||||
|
#define DEV_AUDIO_1 (1 << 0)
|
||||||
|
|
||||||
|
#define DEV_T1_USB_MASK (DEV_USB_OTG | DEV_USB)
|
||||||
|
#define DEV_T1_UART_MASK (DEV_UART)
|
||||||
|
#define DEV_T1_CHARGER_MASK (DEV_DEDICATED_CHG | DEV_USB_CHG)
|
||||||
|
|
||||||
|
/* Device Type 2 */
|
||||||
|
#define DEV_AV (1 << 6)
|
||||||
|
#define DEV_TTY (1 << 5)
|
||||||
|
#define DEV_PPD (1 << 4)
|
||||||
|
#define DEV_JIG_UART_OFF (1 << 3)
|
||||||
|
#define DEV_JIG_UART_ON (1 << 2)
|
||||||
|
#define DEV_JIG_USB_OFF (1 << 1)
|
||||||
|
#define DEV_JIG_USB_ON (1 << 0)
|
||||||
|
|
||||||
|
#define DEV_T2_USB_MASK (DEV_JIG_USB_OFF | DEV_JIG_USB_ON)
|
||||||
|
#define DEV_T2_UART_MASK (DEV_JIG_UART_OFF | DEV_JIG_UART_ON)
|
||||||
|
#define DEV_T2_JIG_MASK (DEV_JIG_USB_OFF | DEV_JIG_USB_ON | \
|
||||||
|
DEV_JIG_UART_OFF | DEV_JIG_UART_ON)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Manual Switch
|
||||||
|
* D- [7:5] / D+ [4:2]
|
||||||
|
* 000: Open all / 001: USB / 010: AUDIO / 011: UART / 100: V_AUDIO
|
||||||
|
*/
|
||||||
|
#define SW_VAUDIO ((4 << 5) | (4 << 2))
|
||||||
|
#define SW_UART ((3 << 5) | (3 << 2))
|
||||||
|
#define SW_AUDIO ((2 << 5) | (2 << 2))
|
||||||
|
#define SW_DHOST ((1 << 5) | (1 << 2))
|
||||||
|
#define SW_AUTO ((0 << 5) | (0 << 2))
|
||||||
|
|
||||||
|
/* Interrupt 1 */
|
||||||
|
#define INT_DETACH (1 << 1)
|
||||||
|
#define INT_ATTACH (1 << 0)
|
||||||
|
|
||||||
|
struct fsa9480_usbsw {
|
||||||
|
struct i2c_client *client;
|
||||||
|
struct fsa9480_platform_data *pdata;
|
||||||
|
int dev1;
|
||||||
|
int dev2;
|
||||||
|
int mansw;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct fsa9480_usbsw *chip;
|
||||||
|
|
||||||
|
static int fsa9480_write_reg(struct i2c_client *client,
|
||||||
|
int reg, int value)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = i2c_smbus_write_byte_data(client, reg, value);
|
||||||
|
|
||||||
|
if (ret < 0)
|
||||||
|
dev_err(&client->dev, "%s: err %d\n", __func__, ret);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fsa9480_read_reg(struct i2c_client *client, int reg)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = i2c_smbus_read_byte_data(client, reg);
|
||||||
|
|
||||||
|
if (ret < 0)
|
||||||
|
dev_err(&client->dev, "%s: err %d\n", __func__, ret);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fsa9480_read_irq(struct i2c_client *client, int *value)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = i2c_smbus_read_i2c_block_data(client,
|
||||||
|
FSA9480_REG_INT1, 2, (u8 *)value);
|
||||||
|
*value &= 0xffff;
|
||||||
|
|
||||||
|
if (ret < 0)
|
||||||
|
dev_err(&client->dev, "%s: err %d\n", __func__, ret);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fsa9480_set_switch(const char *buf)
|
||||||
|
{
|
||||||
|
struct fsa9480_usbsw *usbsw = chip;
|
||||||
|
struct i2c_client *client = usbsw->client;
|
||||||
|
unsigned int value;
|
||||||
|
unsigned int path = 0;
|
||||||
|
|
||||||
|
value = fsa9480_read_reg(client, FSA9480_REG_CTRL);
|
||||||
|
|
||||||
|
if (!strncmp(buf, "VAUDIO", 6)) {
|
||||||
|
path = SW_VAUDIO;
|
||||||
|
value &= ~CON_MANUAL_SW;
|
||||||
|
} else if (!strncmp(buf, "UART", 4)) {
|
||||||
|
path = SW_UART;
|
||||||
|
value &= ~CON_MANUAL_SW;
|
||||||
|
} else if (!strncmp(buf, "AUDIO", 5)) {
|
||||||
|
path = SW_AUDIO;
|
||||||
|
value &= ~CON_MANUAL_SW;
|
||||||
|
} else if (!strncmp(buf, "DHOST", 5)) {
|
||||||
|
path = SW_DHOST;
|
||||||
|
value &= ~CON_MANUAL_SW;
|
||||||
|
} else if (!strncmp(buf, "AUTO", 4)) {
|
||||||
|
path = SW_AUTO;
|
||||||
|
value |= CON_MANUAL_SW;
|
||||||
|
} else {
|
||||||
|
printk(KERN_ERR "Wrong command\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
usbsw->mansw = path;
|
||||||
|
fsa9480_write_reg(client, FSA9480_REG_MANSW1, path);
|
||||||
|
fsa9480_write_reg(client, FSA9480_REG_CTRL, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t fsa9480_get_switch(char *buf)
|
||||||
|
{
|
||||||
|
struct fsa9480_usbsw *usbsw = chip;
|
||||||
|
struct i2c_client *client = usbsw->client;
|
||||||
|
unsigned int value;
|
||||||
|
|
||||||
|
value = fsa9480_read_reg(client, FSA9480_REG_MANSW1);
|
||||||
|
|
||||||
|
if (value == SW_VAUDIO)
|
||||||
|
return sprintf(buf, "VAUDIO\n");
|
||||||
|
else if (value == SW_UART)
|
||||||
|
return sprintf(buf, "UART\n");
|
||||||
|
else if (value == SW_AUDIO)
|
||||||
|
return sprintf(buf, "AUDIO\n");
|
||||||
|
else if (value == SW_DHOST)
|
||||||
|
return sprintf(buf, "DHOST\n");
|
||||||
|
else if (value == SW_AUTO)
|
||||||
|
return sprintf(buf, "AUTO\n");
|
||||||
|
else
|
||||||
|
return sprintf(buf, "%x", value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t fsa9480_show_device(struct device *dev,
|
||||||
|
struct device_attribute *attr,
|
||||||
|
char *buf)
|
||||||
|
{
|
||||||
|
struct fsa9480_usbsw *usbsw = dev_get_drvdata(dev);
|
||||||
|
struct i2c_client *client = usbsw->client;
|
||||||
|
int dev1, dev2;
|
||||||
|
|
||||||
|
dev1 = fsa9480_read_reg(client, FSA9480_REG_DEV_T1);
|
||||||
|
dev2 = fsa9480_read_reg(client, FSA9480_REG_DEV_T2);
|
||||||
|
|
||||||
|
if (!dev1 && !dev2)
|
||||||
|
return sprintf(buf, "NONE\n");
|
||||||
|
|
||||||
|
/* USB */
|
||||||
|
if (dev1 & DEV_T1_USB_MASK || dev2 & DEV_T2_USB_MASK)
|
||||||
|
return sprintf(buf, "USB\n");
|
||||||
|
|
||||||
|
/* UART */
|
||||||
|
if (dev1 & DEV_T1_UART_MASK || dev2 & DEV_T2_UART_MASK)
|
||||||
|
return sprintf(buf, "UART\n");
|
||||||
|
|
||||||
|
/* CHARGER */
|
||||||
|
if (dev1 & DEV_T1_CHARGER_MASK)
|
||||||
|
return sprintf(buf, "CHARGER\n");
|
||||||
|
|
||||||
|
/* JIG */
|
||||||
|
if (dev2 & DEV_T2_JIG_MASK)
|
||||||
|
return sprintf(buf, "JIG\n");
|
||||||
|
|
||||||
|
return sprintf(buf, "UNKNOWN\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t fsa9480_show_manualsw(struct device *dev,
|
||||||
|
struct device_attribute *attr, char *buf)
|
||||||
|
{
|
||||||
|
return fsa9480_get_switch(buf);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t fsa9480_set_manualsw(struct device *dev,
|
||||||
|
struct device_attribute *attr,
|
||||||
|
const char *buf, size_t count)
|
||||||
|
{
|
||||||
|
fsa9480_set_switch(buf);
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DEVICE_ATTR(device, S_IRUGO, fsa9480_show_device, NULL);
|
||||||
|
static DEVICE_ATTR(switch, S_IRUGO | S_IWUSR,
|
||||||
|
fsa9480_show_manualsw, fsa9480_set_manualsw);
|
||||||
|
|
||||||
|
static struct attribute *fsa9480_attributes[] = {
|
||||||
|
&dev_attr_device.attr,
|
||||||
|
&dev_attr_switch.attr,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct attribute_group fsa9480_group = {
|
||||||
|
.attrs = fsa9480_attributes,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void fsa9480_detect_dev(struct fsa9480_usbsw *usbsw, int intr)
|
||||||
|
{
|
||||||
|
int val1, val2, ctrl;
|
||||||
|
struct fsa9480_platform_data *pdata = usbsw->pdata;
|
||||||
|
struct i2c_client *client = usbsw->client;
|
||||||
|
|
||||||
|
val1 = fsa9480_read_reg(client, FSA9480_REG_DEV_T1);
|
||||||
|
val2 = fsa9480_read_reg(client, FSA9480_REG_DEV_T2);
|
||||||
|
ctrl = fsa9480_read_reg(client, FSA9480_REG_CTRL);
|
||||||
|
|
||||||
|
dev_info(&client->dev, "intr: 0x%x, dev1: 0x%x, dev2: 0x%x\n",
|
||||||
|
intr, val1, val2);
|
||||||
|
|
||||||
|
if (!intr)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
if (intr & INT_ATTACH) { /* Attached */
|
||||||
|
/* USB */
|
||||||
|
if (val1 & DEV_T1_USB_MASK || val2 & DEV_T2_USB_MASK) {
|
||||||
|
if (pdata->usb_cb)
|
||||||
|
pdata->usb_cb(FSA9480_ATTACHED);
|
||||||
|
|
||||||
|
if (usbsw->mansw) {
|
||||||
|
fsa9480_write_reg(client,
|
||||||
|
FSA9480_REG_MANSW1, usbsw->mansw);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* UART */
|
||||||
|
if (val1 & DEV_T1_UART_MASK || val2 & DEV_T2_UART_MASK) {
|
||||||
|
if (pdata->uart_cb)
|
||||||
|
pdata->uart_cb(FSA9480_ATTACHED);
|
||||||
|
|
||||||
|
if (!(ctrl & CON_MANUAL_SW)) {
|
||||||
|
fsa9480_write_reg(client,
|
||||||
|
FSA9480_REG_MANSW1, SW_UART);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* CHARGER */
|
||||||
|
if (val1 & DEV_T1_CHARGER_MASK) {
|
||||||
|
if (pdata->charger_cb)
|
||||||
|
pdata->charger_cb(FSA9480_ATTACHED);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* JIG */
|
||||||
|
if (val2 & DEV_T2_JIG_MASK) {
|
||||||
|
if (pdata->jig_cb)
|
||||||
|
pdata->jig_cb(FSA9480_ATTACHED);
|
||||||
|
}
|
||||||
|
} else if (intr & INT_DETACH) { /* Detached */
|
||||||
|
/* USB */
|
||||||
|
if (usbsw->dev1 & DEV_T1_USB_MASK ||
|
||||||
|
usbsw->dev2 & DEV_T2_USB_MASK) {
|
||||||
|
if (pdata->usb_cb)
|
||||||
|
pdata->usb_cb(FSA9480_DETACHED);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* UART */
|
||||||
|
if (usbsw->dev1 & DEV_T1_UART_MASK ||
|
||||||
|
usbsw->dev2 & DEV_T2_UART_MASK) {
|
||||||
|
if (pdata->uart_cb)
|
||||||
|
pdata->uart_cb(FSA9480_DETACHED);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* CHARGER */
|
||||||
|
if (usbsw->dev1 & DEV_T1_CHARGER_MASK) {
|
||||||
|
if (pdata->charger_cb)
|
||||||
|
pdata->charger_cb(FSA9480_DETACHED);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* JIG */
|
||||||
|
if (usbsw->dev2 & DEV_T2_JIG_MASK) {
|
||||||
|
if (pdata->jig_cb)
|
||||||
|
pdata->jig_cb(FSA9480_DETACHED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
usbsw->dev1 = val1;
|
||||||
|
usbsw->dev2 = val2;
|
||||||
|
|
||||||
|
out:
|
||||||
|
ctrl &= ~CON_INT_MASK;
|
||||||
|
fsa9480_write_reg(client, FSA9480_REG_CTRL, ctrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
static irqreturn_t fsa9480_irq_handler(int irq, void *data)
|
||||||
|
{
|
||||||
|
struct fsa9480_usbsw *usbsw = data;
|
||||||
|
struct i2c_client *client = usbsw->client;
|
||||||
|
int intr;
|
||||||
|
|
||||||
|
/* clear interrupt */
|
||||||
|
fsa9480_read_irq(client, &intr);
|
||||||
|
|
||||||
|
/* device detection */
|
||||||
|
fsa9480_detect_dev(usbsw, intr);
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fsa9480_irq_init(struct fsa9480_usbsw *usbsw)
|
||||||
|
{
|
||||||
|
struct fsa9480_platform_data *pdata = usbsw->pdata;
|
||||||
|
struct i2c_client *client = usbsw->client;
|
||||||
|
int ret;
|
||||||
|
int intr;
|
||||||
|
unsigned int ctrl = CON_MASK;
|
||||||
|
|
||||||
|
/* clear interrupt */
|
||||||
|
fsa9480_read_irq(client, &intr);
|
||||||
|
|
||||||
|
/* unmask interrupt (attach/detach only) */
|
||||||
|
fsa9480_write_reg(client, FSA9480_REG_INT1_MASK, 0xfc);
|
||||||
|
fsa9480_write_reg(client, FSA9480_REG_INT2_MASK, 0x1f);
|
||||||
|
|
||||||
|
usbsw->mansw = fsa9480_read_reg(client, FSA9480_REG_MANSW1);
|
||||||
|
|
||||||
|
if (usbsw->mansw)
|
||||||
|
ctrl &= ~CON_MANUAL_SW; /* Manual Switching Mode */
|
||||||
|
|
||||||
|
fsa9480_write_reg(client, FSA9480_REG_CTRL, ctrl);
|
||||||
|
|
||||||
|
if (pdata && pdata->cfg_gpio)
|
||||||
|
pdata->cfg_gpio();
|
||||||
|
|
||||||
|
if (client->irq) {
|
||||||
|
ret = request_threaded_irq(client->irq, NULL,
|
||||||
|
fsa9480_irq_handler,
|
||||||
|
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||||
|
"fsa9480 micro USB", usbsw);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&client->dev, "failed to reqeust IRQ\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
device_init_wakeup(&client->dev, pdata->wakeup);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __devinit fsa9480_probe(struct i2c_client *client,
|
||||||
|
const struct i2c_device_id *id)
|
||||||
|
{
|
||||||
|
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
|
||||||
|
struct fsa9480_usbsw *usbsw;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
usbsw = kzalloc(sizeof(struct fsa9480_usbsw), GFP_KERNEL);
|
||||||
|
if (!usbsw) {
|
||||||
|
dev_err(&client->dev, "failed to allocate driver data\n");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
usbsw->client = client;
|
||||||
|
usbsw->pdata = client->dev.platform_data;
|
||||||
|
|
||||||
|
chip = usbsw;
|
||||||
|
|
||||||
|
i2c_set_clientdata(client, usbsw);
|
||||||
|
|
||||||
|
ret = fsa9480_irq_init(usbsw);
|
||||||
|
if (ret)
|
||||||
|
goto fail1;
|
||||||
|
|
||||||
|
ret = sysfs_create_group(&client->dev.kobj, &fsa9480_group);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&client->dev,
|
||||||
|
"failed to create fsa9480 attribute group\n");
|
||||||
|
goto fail2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ADC Detect Time: 500ms */
|
||||||
|
fsa9480_write_reg(client, FSA9480_REG_TIMING1, 0x6);
|
||||||
|
|
||||||
|
if (chip->pdata->reset_cb)
|
||||||
|
chip->pdata->reset_cb();
|
||||||
|
|
||||||
|
/* device detection */
|
||||||
|
fsa9480_detect_dev(usbsw, INT_ATTACH);
|
||||||
|
|
||||||
|
pm_runtime_set_active(&client->dev);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
fail2:
|
||||||
|
if (client->irq)
|
||||||
|
free_irq(client->irq, NULL);
|
||||||
|
fail1:
|
||||||
|
i2c_set_clientdata(client, NULL);
|
||||||
|
kfree(usbsw);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __devexit fsa9480_remove(struct i2c_client *client)
|
||||||
|
{
|
||||||
|
struct fsa9480_usbsw *usbsw = i2c_get_clientdata(client);
|
||||||
|
if (client->irq)
|
||||||
|
free_irq(client->irq, NULL);
|
||||||
|
i2c_set_clientdata(client, NULL);
|
||||||
|
|
||||||
|
sysfs_remove_group(&client->dev.kobj, &fsa9480_group);
|
||||||
|
device_init_wakeup(&client->dev, 0);
|
||||||
|
kfree(usbsw);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
|
||||||
|
static int fsa9480_suspend(struct i2c_client *client, pm_message_t state)
|
||||||
|
{
|
||||||
|
struct fsa9480_usbsw *usbsw = i2c_get_clientdata(client);
|
||||||
|
struct fsa9480_platform_data *pdata = usbsw->pdata;
|
||||||
|
|
||||||
|
if (device_may_wakeup(&client->dev) && client->irq)
|
||||||
|
enable_irq_wake(client->irq);
|
||||||
|
|
||||||
|
if (pdata->usb_power)
|
||||||
|
pdata->usb_power(0);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fsa9480_resume(struct i2c_client *client)
|
||||||
|
{
|
||||||
|
struct fsa9480_usbsw *usbsw = i2c_get_clientdata(client);
|
||||||
|
int dev1, dev2;
|
||||||
|
|
||||||
|
if (device_may_wakeup(&client->dev) && client->irq)
|
||||||
|
disable_irq_wake(client->irq);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Clear Pending interrupt. Note that detect_dev does what
|
||||||
|
* the interrupt handler does. So, we don't miss pending and
|
||||||
|
* we reenable interrupt if there is one.
|
||||||
|
*/
|
||||||
|
fsa9480_read_reg(client, FSA9480_REG_INT1);
|
||||||
|
fsa9480_read_reg(client, FSA9480_REG_INT2);
|
||||||
|
|
||||||
|
dev1 = fsa9480_read_reg(client, FSA9480_REG_DEV_T1);
|
||||||
|
dev2 = fsa9480_read_reg(client, FSA9480_REG_DEV_T2);
|
||||||
|
|
||||||
|
/* device detection */
|
||||||
|
fsa9480_detect_dev(usbsw, (dev1 || dev2) ? INT_ATTACH : INT_DETACH);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define fsa9480_suspend NULL
|
||||||
|
#define fsa9480_resume NULL
|
||||||
|
|
||||||
|
#endif /* CONFIG_PM */
|
||||||
|
|
||||||
|
static const struct i2c_device_id fsa9480_id[] = {
|
||||||
|
{"fsa9480", 0},
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(i2c, fsa9480_id);
|
||||||
|
|
||||||
|
static struct i2c_driver fsa9480_i2c_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = "fsa9480",
|
||||||
|
},
|
||||||
|
.probe = fsa9480_probe,
|
||||||
|
.remove = __devexit_p(fsa9480_remove),
|
||||||
|
.resume = fsa9480_resume,
|
||||||
|
.suspend = fsa9480_suspend,
|
||||||
|
.id_table = fsa9480_id,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init fsa9480_init(void)
|
||||||
|
{
|
||||||
|
return i2c_add_driver(&fsa9480_i2c_driver);
|
||||||
|
}
|
||||||
|
module_init(fsa9480_init);
|
||||||
|
|
||||||
|
static void __exit fsa9480_exit(void)
|
||||||
|
{
|
||||||
|
i2c_del_driver(&fsa9480_i2c_driver);
|
||||||
|
}
|
||||||
|
module_exit(fsa9480_exit);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Minkyu Kang <mk7.kang@samsung.com>");
|
||||||
|
MODULE_DESCRIPTION("FSA9480 USB Switch driver");
|
||||||
|
MODULE_LICENSE("GPL");
|
@ -686,6 +686,8 @@ static int __devinit pch_phub_probe(struct pci_dev *pdev,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (id->driver_data == 1) { /* EG20T PCH */
|
if (id->driver_data == 1) { /* EG20T PCH */
|
||||||
|
const char *board_name;
|
||||||
|
|
||||||
retval = sysfs_create_file(&pdev->dev.kobj,
|
retval = sysfs_create_file(&pdev->dev.kobj,
|
||||||
&dev_attr_pch_mac.attr);
|
&dev_attr_pch_mac.attr);
|
||||||
if (retval)
|
if (retval)
|
||||||
@ -701,7 +703,8 @@ static int __devinit pch_phub_probe(struct pci_dev *pdev,
|
|||||||
CLKCFG_CANCLK_MASK);
|
CLKCFG_CANCLK_MASK);
|
||||||
|
|
||||||
/* quirk for CM-iTC board */
|
/* quirk for CM-iTC board */
|
||||||
if (strstr(dmi_get_system_info(DMI_BOARD_NAME), "CM-iTC"))
|
board_name = dmi_get_system_info(DMI_BOARD_NAME);
|
||||||
|
if (board_name && strstr(board_name, "CM-iTC"))
|
||||||
pch_phub_read_modify_write_reg(chip,
|
pch_phub_read_modify_write_reg(chip,
|
||||||
(unsigned int)CLKCFG_REG_OFFSET,
|
(unsigned int)CLKCFG_REG_OFFSET,
|
||||||
CLKCFG_UART_48MHZ | CLKCFG_BAUDDIV |
|
CLKCFG_UART_48MHZ | CLKCFG_BAUDDIV |
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
#include <linux/log2.h>
|
#include <linux/log2.h>
|
||||||
#include <linux/regulator/consumer.h>
|
#include <linux/regulator/consumer.h>
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
|
#include <linux/suspend.h>
|
||||||
|
|
||||||
#include <linux/mmc/card.h>
|
#include <linux/mmc/card.h>
|
||||||
#include <linux/mmc/host.h>
|
#include <linux/mmc/host.h>
|
||||||
|
@ -509,15 +509,15 @@ static __init void pnpacpi_parse_dma_option(struct pnp_dev *dev,
|
|||||||
struct acpi_resource_dma *p)
|
struct acpi_resource_dma *p)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
unsigned char map = 0, flags;
|
unsigned char map = 0, flags = 0;
|
||||||
|
|
||||||
if (p->channel_count == 0)
|
if (p->channel_count == 0)
|
||||||
return;
|
flags |= IORESOURCE_DISABLED;
|
||||||
|
|
||||||
for (i = 0; i < p->channel_count; i++)
|
for (i = 0; i < p->channel_count; i++)
|
||||||
map |= 1 << p->channels[i];
|
map |= 1 << p->channels[i];
|
||||||
|
|
||||||
flags = dma_flags(dev, p->type, p->bus_master, p->transfer);
|
flags |= dma_flags(dev, p->type, p->bus_master, p->transfer);
|
||||||
pnp_register_dma_resource(dev, option_flags, map, flags);
|
pnp_register_dma_resource(dev, option_flags, map, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -527,17 +527,17 @@ static __init void pnpacpi_parse_irq_option(struct pnp_dev *dev,
|
|||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
pnp_irq_mask_t map;
|
pnp_irq_mask_t map;
|
||||||
unsigned char flags;
|
unsigned char flags = 0;
|
||||||
|
|
||||||
if (p->interrupt_count == 0)
|
if (p->interrupt_count == 0)
|
||||||
return;
|
flags |= IORESOURCE_DISABLED;
|
||||||
|
|
||||||
bitmap_zero(map.bits, PNP_IRQ_NR);
|
bitmap_zero(map.bits, PNP_IRQ_NR);
|
||||||
for (i = 0; i < p->interrupt_count; i++)
|
for (i = 0; i < p->interrupt_count; i++)
|
||||||
if (p->interrupts[i])
|
if (p->interrupts[i])
|
||||||
__set_bit(p->interrupts[i], map.bits);
|
__set_bit(p->interrupts[i], map.bits);
|
||||||
|
|
||||||
flags = irq_flags(p->triggering, p->polarity, p->sharable);
|
flags |= irq_flags(p->triggering, p->polarity, p->sharable);
|
||||||
pnp_register_irq_resource(dev, option_flags, &map, flags);
|
pnp_register_irq_resource(dev, option_flags, &map, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -547,10 +547,10 @@ static __init void pnpacpi_parse_ext_irq_option(struct pnp_dev *dev,
|
|||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
pnp_irq_mask_t map;
|
pnp_irq_mask_t map;
|
||||||
unsigned char flags;
|
unsigned char flags = 0;
|
||||||
|
|
||||||
if (p->interrupt_count == 0)
|
if (p->interrupt_count == 0)
|
||||||
return;
|
flags |= IORESOURCE_DISABLED;
|
||||||
|
|
||||||
bitmap_zero(map.bits, PNP_IRQ_NR);
|
bitmap_zero(map.bits, PNP_IRQ_NR);
|
||||||
for (i = 0; i < p->interrupt_count; i++) {
|
for (i = 0; i < p->interrupt_count; i++) {
|
||||||
@ -564,7 +564,7 @@ static __init void pnpacpi_parse_ext_irq_option(struct pnp_dev *dev,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
flags = irq_flags(p->triggering, p->polarity, p->sharable);
|
flags |= irq_flags(p->triggering, p->polarity, p->sharable);
|
||||||
pnp_register_irq_resource(dev, option_flags, &map, flags);
|
pnp_register_irq_resource(dev, option_flags, &map, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -575,10 +575,10 @@ static __init void pnpacpi_parse_port_option(struct pnp_dev *dev,
|
|||||||
unsigned char flags = 0;
|
unsigned char flags = 0;
|
||||||
|
|
||||||
if (io->address_length == 0)
|
if (io->address_length == 0)
|
||||||
return;
|
flags |= IORESOURCE_DISABLED;
|
||||||
|
|
||||||
if (io->io_decode == ACPI_DECODE_16)
|
if (io->io_decode == ACPI_DECODE_16)
|
||||||
flags = IORESOURCE_IO_16BIT_ADDR;
|
flags |= IORESOURCE_IO_16BIT_ADDR;
|
||||||
pnp_register_port_resource(dev, option_flags, io->minimum, io->maximum,
|
pnp_register_port_resource(dev, option_flags, io->minimum, io->maximum,
|
||||||
io->alignment, io->address_length, flags);
|
io->alignment, io->address_length, flags);
|
||||||
}
|
}
|
||||||
@ -587,11 +587,13 @@ static __init void pnpacpi_parse_fixed_port_option(struct pnp_dev *dev,
|
|||||||
unsigned int option_flags,
|
unsigned int option_flags,
|
||||||
struct acpi_resource_fixed_io *io)
|
struct acpi_resource_fixed_io *io)
|
||||||
{
|
{
|
||||||
|
unsigned char flags = 0;
|
||||||
|
|
||||||
if (io->address_length == 0)
|
if (io->address_length == 0)
|
||||||
return;
|
flags |= IORESOURCE_DISABLED;
|
||||||
|
|
||||||
pnp_register_port_resource(dev, option_flags, io->address, io->address,
|
pnp_register_port_resource(dev, option_flags, io->address, io->address,
|
||||||
0, io->address_length, IORESOURCE_IO_FIXED);
|
0, io->address_length, flags | IORESOURCE_IO_FIXED);
|
||||||
}
|
}
|
||||||
|
|
||||||
static __init void pnpacpi_parse_mem24_option(struct pnp_dev *dev,
|
static __init void pnpacpi_parse_mem24_option(struct pnp_dev *dev,
|
||||||
@ -601,10 +603,10 @@ static __init void pnpacpi_parse_mem24_option(struct pnp_dev *dev,
|
|||||||
unsigned char flags = 0;
|
unsigned char flags = 0;
|
||||||
|
|
||||||
if (p->address_length == 0)
|
if (p->address_length == 0)
|
||||||
return;
|
flags |= IORESOURCE_DISABLED;
|
||||||
|
|
||||||
if (p->write_protect == ACPI_READ_WRITE_MEMORY)
|
if (p->write_protect == ACPI_READ_WRITE_MEMORY)
|
||||||
flags = IORESOURCE_MEM_WRITEABLE;
|
flags |= IORESOURCE_MEM_WRITEABLE;
|
||||||
pnp_register_mem_resource(dev, option_flags, p->minimum, p->maximum,
|
pnp_register_mem_resource(dev, option_flags, p->minimum, p->maximum,
|
||||||
p->alignment, p->address_length, flags);
|
p->alignment, p->address_length, flags);
|
||||||
}
|
}
|
||||||
@ -616,10 +618,10 @@ static __init void pnpacpi_parse_mem32_option(struct pnp_dev *dev,
|
|||||||
unsigned char flags = 0;
|
unsigned char flags = 0;
|
||||||
|
|
||||||
if (p->address_length == 0)
|
if (p->address_length == 0)
|
||||||
return;
|
flags |= IORESOURCE_DISABLED;
|
||||||
|
|
||||||
if (p->write_protect == ACPI_READ_WRITE_MEMORY)
|
if (p->write_protect == ACPI_READ_WRITE_MEMORY)
|
||||||
flags = IORESOURCE_MEM_WRITEABLE;
|
flags |= IORESOURCE_MEM_WRITEABLE;
|
||||||
pnp_register_mem_resource(dev, option_flags, p->minimum, p->maximum,
|
pnp_register_mem_resource(dev, option_flags, p->minimum, p->maximum,
|
||||||
p->alignment, p->address_length, flags);
|
p->alignment, p->address_length, flags);
|
||||||
}
|
}
|
||||||
@ -631,10 +633,10 @@ static __init void pnpacpi_parse_fixed_mem32_option(struct pnp_dev *dev,
|
|||||||
unsigned char flags = 0;
|
unsigned char flags = 0;
|
||||||
|
|
||||||
if (p->address_length == 0)
|
if (p->address_length == 0)
|
||||||
return;
|
flags |= IORESOURCE_DISABLED;
|
||||||
|
|
||||||
if (p->write_protect == ACPI_READ_WRITE_MEMORY)
|
if (p->write_protect == ACPI_READ_WRITE_MEMORY)
|
||||||
flags = IORESOURCE_MEM_WRITEABLE;
|
flags |= IORESOURCE_MEM_WRITEABLE;
|
||||||
pnp_register_mem_resource(dev, option_flags, p->address, p->address,
|
pnp_register_mem_resource(dev, option_flags, p->address, p->address,
|
||||||
0, p->address_length, flags);
|
0, p->address_length, flags);
|
||||||
}
|
}
|
||||||
@ -655,18 +657,18 @@ static __init void pnpacpi_parse_address_option(struct pnp_dev *dev,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (p->address_length == 0)
|
if (p->address_length == 0)
|
||||||
return;
|
flags |= IORESOURCE_DISABLED;
|
||||||
|
|
||||||
if (p->resource_type == ACPI_MEMORY_RANGE) {
|
if (p->resource_type == ACPI_MEMORY_RANGE) {
|
||||||
if (p->info.mem.write_protect == ACPI_READ_WRITE_MEMORY)
|
if (p->info.mem.write_protect == ACPI_READ_WRITE_MEMORY)
|
||||||
flags = IORESOURCE_MEM_WRITEABLE;
|
flags |= IORESOURCE_MEM_WRITEABLE;
|
||||||
pnp_register_mem_resource(dev, option_flags, p->minimum,
|
pnp_register_mem_resource(dev, option_flags, p->minimum,
|
||||||
p->minimum, 0, p->address_length,
|
p->minimum, 0, p->address_length,
|
||||||
flags);
|
flags);
|
||||||
} else if (p->resource_type == ACPI_IO_RANGE)
|
} else if (p->resource_type == ACPI_IO_RANGE)
|
||||||
pnp_register_port_resource(dev, option_flags, p->minimum,
|
pnp_register_port_resource(dev, option_flags, p->minimum,
|
||||||
p->minimum, 0, p->address_length,
|
p->minimum, 0, p->address_length,
|
||||||
IORESOURCE_IO_FIXED);
|
flags | IORESOURCE_IO_FIXED);
|
||||||
}
|
}
|
||||||
|
|
||||||
static __init void pnpacpi_parse_ext_address_option(struct pnp_dev *dev,
|
static __init void pnpacpi_parse_ext_address_option(struct pnp_dev *dev,
|
||||||
@ -677,18 +679,18 @@ static __init void pnpacpi_parse_ext_address_option(struct pnp_dev *dev,
|
|||||||
unsigned char flags = 0;
|
unsigned char flags = 0;
|
||||||
|
|
||||||
if (p->address_length == 0)
|
if (p->address_length == 0)
|
||||||
return;
|
flags |= IORESOURCE_DISABLED;
|
||||||
|
|
||||||
if (p->resource_type == ACPI_MEMORY_RANGE) {
|
if (p->resource_type == ACPI_MEMORY_RANGE) {
|
||||||
if (p->info.mem.write_protect == ACPI_READ_WRITE_MEMORY)
|
if (p->info.mem.write_protect == ACPI_READ_WRITE_MEMORY)
|
||||||
flags = IORESOURCE_MEM_WRITEABLE;
|
flags |= IORESOURCE_MEM_WRITEABLE;
|
||||||
pnp_register_mem_resource(dev, option_flags, p->minimum,
|
pnp_register_mem_resource(dev, option_flags, p->minimum,
|
||||||
p->minimum, 0, p->address_length,
|
p->minimum, 0, p->address_length,
|
||||||
flags);
|
flags);
|
||||||
} else if (p->resource_type == ACPI_IO_RANGE)
|
} else if (p->resource_type == ACPI_IO_RANGE)
|
||||||
pnp_register_port_resource(dev, option_flags, p->minimum,
|
pnp_register_port_resource(dev, option_flags, p->minimum,
|
||||||
p->minimum, 0, p->address_length,
|
p->minimum, 0, p->address_length,
|
||||||
IORESOURCE_IO_FIXED);
|
flags | IORESOURCE_IO_FIXED);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct acpipnp_parse_option_s {
|
struct acpipnp_parse_option_s {
|
||||||
|
@ -1006,10 +1006,10 @@ config RTC_DRV_MC13XXX
|
|||||||
|
|
||||||
config RTC_DRV_MPC5121
|
config RTC_DRV_MPC5121
|
||||||
tristate "Freescale MPC5121 built-in RTC"
|
tristate "Freescale MPC5121 built-in RTC"
|
||||||
depends on PPC_MPC512x && RTC_CLASS
|
depends on PPC_MPC512x || PPC_MPC52xx
|
||||||
help
|
help
|
||||||
If you say yes here you will get support for the
|
If you say yes here you will get support for the
|
||||||
built-in RTC MPC5121.
|
built-in RTC on MPC5121 or on MPC5200.
|
||||||
|
|
||||||
This driver can also be built as a module. If so, the module
|
This driver can also be built as a module. If so, the module
|
||||||
will be called rtc-mpc5121.
|
will be called rtc-mpc5121.
|
||||||
@ -1034,6 +1034,16 @@ config RTC_DRV_LPC32XX
|
|||||||
This driver can also be buillt as a module. If so, the module
|
This driver can also be buillt as a module. If so, the module
|
||||||
will be called rtc-lpc32xx.
|
will be called rtc-lpc32xx.
|
||||||
|
|
||||||
|
config RTC_DRV_PM8XXX
|
||||||
|
tristate "Qualcomm PMIC8XXX RTC"
|
||||||
|
depends on MFD_PM8XXX
|
||||||
|
help
|
||||||
|
If you say yes here you get support for the
|
||||||
|
Qualcomm PMIC8XXX RTC.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the
|
||||||
|
module will be called rtc-pm8xxx.
|
||||||
|
|
||||||
config RTC_DRV_TEGRA
|
config RTC_DRV_TEGRA
|
||||||
tristate "NVIDIA Tegra Internal RTC driver"
|
tristate "NVIDIA Tegra Internal RTC driver"
|
||||||
depends on RTC_CLASS && ARCH_TEGRA
|
depends on RTC_CLASS && ARCH_TEGRA
|
||||||
|
@ -77,6 +77,7 @@ obj-$(CONFIG_RTC_DRV_PCF2123) += rtc-pcf2123.o
|
|||||||
obj-$(CONFIG_RTC_DRV_PCF50633) += rtc-pcf50633.o
|
obj-$(CONFIG_RTC_DRV_PCF50633) += rtc-pcf50633.o
|
||||||
obj-$(CONFIG_RTC_DRV_PL030) += rtc-pl030.o
|
obj-$(CONFIG_RTC_DRV_PL030) += rtc-pl030.o
|
||||||
obj-$(CONFIG_RTC_DRV_PL031) += rtc-pl031.o
|
obj-$(CONFIG_RTC_DRV_PL031) += rtc-pl031.o
|
||||||
|
obj-$(CONFIG_RTC_DRV_PM8XXX) += rtc-pm8xxx.o
|
||||||
obj-$(CONFIG_RTC_DRV_PS3) += rtc-ps3.o
|
obj-$(CONFIG_RTC_DRV_PS3) += rtc-ps3.o
|
||||||
obj-$(CONFIG_RTC_DRV_PUV3) += rtc-puv3.o
|
obj-$(CONFIG_RTC_DRV_PUV3) += rtc-puv3.o
|
||||||
obj-$(CONFIG_RTC_DRV_PXA) += rtc-pxa.o
|
obj-$(CONFIG_RTC_DRV_PXA) += rtc-pxa.o
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
*
|
*
|
||||||
* Copyright 2007, Domen Puncer <domen.puncer@telargo.com>
|
* Copyright 2007, Domen Puncer <domen.puncer@telargo.com>
|
||||||
* Copyright 2008, Freescale Semiconductor, Inc. All rights reserved.
|
* Copyright 2008, Freescale Semiconductor, Inc. All rights reserved.
|
||||||
|
* Copyright 2011, Dmitry Eremin-Solenikov
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
@ -145,6 +146,55 @@ static int mpc5121_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int mpc5200_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||||
|
{
|
||||||
|
struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev);
|
||||||
|
struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
|
||||||
|
int tmp;
|
||||||
|
|
||||||
|
tm->tm_sec = in_8(®s->second);
|
||||||
|
tm->tm_min = in_8(®s->minute);
|
||||||
|
|
||||||
|
/* 12 hour format? */
|
||||||
|
if (in_8(®s->hour) & 0x20)
|
||||||
|
tm->tm_hour = (in_8(®s->hour) >> 1) +
|
||||||
|
(in_8(®s->hour) & 1 ? 12 : 0);
|
||||||
|
else
|
||||||
|
tm->tm_hour = in_8(®s->hour);
|
||||||
|
|
||||||
|
tmp = in_8(®s->wday_mday);
|
||||||
|
tm->tm_mday = tmp & 0x1f;
|
||||||
|
tm->tm_mon = in_8(®s->month) - 1;
|
||||||
|
tm->tm_year = in_be16(®s->year) - 1900;
|
||||||
|
tm->tm_wday = (tmp >> 5) % 7;
|
||||||
|
tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year);
|
||||||
|
tm->tm_isdst = 0;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mpc5200_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||||
|
{
|
||||||
|
struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev);
|
||||||
|
struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
|
||||||
|
|
||||||
|
mpc5121_rtc_update_smh(regs, tm);
|
||||||
|
|
||||||
|
/* date */
|
||||||
|
out_8(®s->month_set, tm->tm_mon + 1);
|
||||||
|
out_8(®s->weekday_set, tm->tm_wday ? tm->tm_wday : 7);
|
||||||
|
out_8(®s->date_set, tm->tm_mday);
|
||||||
|
out_be16(®s->year_set, tm->tm_year + 1900);
|
||||||
|
|
||||||
|
/* set date sequence */
|
||||||
|
out_8(®s->set_date, 0x1);
|
||||||
|
out_8(®s->set_date, 0x3);
|
||||||
|
out_8(®s->set_date, 0x1);
|
||||||
|
out_8(®s->set_date, 0x0);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int mpc5121_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
|
static int mpc5121_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
|
||||||
{
|
{
|
||||||
struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev);
|
struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev);
|
||||||
@ -248,11 +298,18 @@ static const struct rtc_class_ops mpc5121_rtc_ops = {
|
|||||||
.alarm_irq_enable = mpc5121_rtc_alarm_irq_enable,
|
.alarm_irq_enable = mpc5121_rtc_alarm_irq_enable,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct rtc_class_ops mpc5200_rtc_ops = {
|
||||||
|
.read_time = mpc5200_rtc_read_time,
|
||||||
|
.set_time = mpc5200_rtc_set_time,
|
||||||
|
.read_alarm = mpc5121_rtc_read_alarm,
|
||||||
|
.set_alarm = mpc5121_rtc_set_alarm,
|
||||||
|
.alarm_irq_enable = mpc5121_rtc_alarm_irq_enable,
|
||||||
|
};
|
||||||
|
|
||||||
static int __devinit mpc5121_rtc_probe(struct platform_device *op)
|
static int __devinit mpc5121_rtc_probe(struct platform_device *op)
|
||||||
{
|
{
|
||||||
struct mpc5121_rtc_data *rtc;
|
struct mpc5121_rtc_data *rtc;
|
||||||
int err = 0;
|
int err = 0;
|
||||||
u32 ka;
|
|
||||||
|
|
||||||
rtc = kzalloc(sizeof(*rtc), GFP_KERNEL);
|
rtc = kzalloc(sizeof(*rtc), GFP_KERNEL);
|
||||||
if (!rtc)
|
if (!rtc)
|
||||||
@ -287,15 +344,22 @@ static int __devinit mpc5121_rtc_probe(struct platform_device *op)
|
|||||||
goto out_dispose2;
|
goto out_dispose2;
|
||||||
}
|
}
|
||||||
|
|
||||||
ka = in_be32(&rtc->regs->keep_alive);
|
if (of_device_is_compatible(op->dev.of_node, "fsl,mpc5121-rtc")) {
|
||||||
if (ka & 0x02) {
|
u32 ka;
|
||||||
dev_warn(&op->dev,
|
ka = in_be32(&rtc->regs->keep_alive);
|
||||||
"mpc5121-rtc: Battery or oscillator failure!\n");
|
if (ka & 0x02) {
|
||||||
out_be32(&rtc->regs->keep_alive, ka);
|
dev_warn(&op->dev,
|
||||||
|
"mpc5121-rtc: Battery or oscillator failure!\n");
|
||||||
|
out_be32(&rtc->regs->keep_alive, ka);
|
||||||
|
}
|
||||||
|
|
||||||
|
rtc->rtc = rtc_device_register("mpc5121-rtc", &op->dev,
|
||||||
|
&mpc5121_rtc_ops, THIS_MODULE);
|
||||||
|
} else {
|
||||||
|
rtc->rtc = rtc_device_register("mpc5200-rtc", &op->dev,
|
||||||
|
&mpc5200_rtc_ops, THIS_MODULE);
|
||||||
}
|
}
|
||||||
|
|
||||||
rtc->rtc = rtc_device_register("mpc5121-rtc", &op->dev,
|
|
||||||
&mpc5121_rtc_ops, THIS_MODULE);
|
|
||||||
if (IS_ERR(rtc->rtc)) {
|
if (IS_ERR(rtc->rtc)) {
|
||||||
err = PTR_ERR(rtc->rtc);
|
err = PTR_ERR(rtc->rtc);
|
||||||
goto out_free_irq;
|
goto out_free_irq;
|
||||||
@ -340,6 +404,7 @@ static int __devexit mpc5121_rtc_remove(struct platform_device *op)
|
|||||||
|
|
||||||
static struct of_device_id mpc5121_rtc_match[] __devinitdata = {
|
static struct of_device_id mpc5121_rtc_match[] __devinitdata = {
|
||||||
{ .compatible = "fsl,mpc5121-rtc", },
|
{ .compatible = "fsl,mpc5121-rtc", },
|
||||||
|
{ .compatible = "fsl,mpc5200-rtc", },
|
||||||
{},
|
{},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
550
drivers/rtc/rtc-pm8xxx.c
Normal file
550
drivers/rtc/rtc-pm8xxx.c
Normal file
@ -0,0 +1,550 @@
|
|||||||
|
/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 and
|
||||||
|
* only version 2 as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/rtc.h>
|
||||||
|
#include <linux/pm.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/spinlock.h>
|
||||||
|
|
||||||
|
#include <linux/mfd/pm8xxx/core.h>
|
||||||
|
#include <linux/mfd/pm8xxx/rtc.h>
|
||||||
|
|
||||||
|
|
||||||
|
/* RTC Register offsets from RTC CTRL REG */
|
||||||
|
#define PM8XXX_ALARM_CTRL_OFFSET 0x01
|
||||||
|
#define PM8XXX_RTC_WRITE_OFFSET 0x02
|
||||||
|
#define PM8XXX_RTC_READ_OFFSET 0x06
|
||||||
|
#define PM8XXX_ALARM_RW_OFFSET 0x0A
|
||||||
|
|
||||||
|
/* RTC_CTRL register bit fields */
|
||||||
|
#define PM8xxx_RTC_ENABLE BIT(7)
|
||||||
|
#define PM8xxx_RTC_ALARM_ENABLE BIT(1)
|
||||||
|
#define PM8xxx_RTC_ALARM_CLEAR BIT(0)
|
||||||
|
|
||||||
|
#define NUM_8_BIT_RTC_REGS 0x4
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct pm8xxx_rtc - rtc driver internal structure
|
||||||
|
* @rtc: rtc device for this driver.
|
||||||
|
* @rtc_alarm_irq: rtc alarm irq number.
|
||||||
|
* @rtc_base: address of rtc control register.
|
||||||
|
* @rtc_read_base: base address of read registers.
|
||||||
|
* @rtc_write_base: base address of write registers.
|
||||||
|
* @alarm_rw_base: base address of alarm registers.
|
||||||
|
* @ctrl_reg: rtc control register.
|
||||||
|
* @rtc_dev: device structure.
|
||||||
|
* @ctrl_reg_lock: spinlock protecting access to ctrl_reg.
|
||||||
|
*/
|
||||||
|
struct pm8xxx_rtc {
|
||||||
|
struct rtc_device *rtc;
|
||||||
|
int rtc_alarm_irq;
|
||||||
|
int rtc_base;
|
||||||
|
int rtc_read_base;
|
||||||
|
int rtc_write_base;
|
||||||
|
int alarm_rw_base;
|
||||||
|
u8 ctrl_reg;
|
||||||
|
struct device *rtc_dev;
|
||||||
|
spinlock_t ctrl_reg_lock;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The RTC registers need to be read/written one byte at a time. This is a
|
||||||
|
* hardware limitation.
|
||||||
|
*/
|
||||||
|
static int pm8xxx_read_wrapper(struct pm8xxx_rtc *rtc_dd, u8 *rtc_val,
|
||||||
|
int base, int count)
|
||||||
|
{
|
||||||
|
int i, rc;
|
||||||
|
struct device *parent = rtc_dd->rtc_dev->parent;
|
||||||
|
|
||||||
|
for (i = 0; i < count; i++) {
|
||||||
|
rc = pm8xxx_readb(parent, base + i, &rtc_val[i]);
|
||||||
|
if (rc < 0) {
|
||||||
|
dev_err(rtc_dd->rtc_dev, "PMIC read failed\n");
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pm8xxx_write_wrapper(struct pm8xxx_rtc *rtc_dd, u8 *rtc_val,
|
||||||
|
int base, int count)
|
||||||
|
{
|
||||||
|
int i, rc;
|
||||||
|
struct device *parent = rtc_dd->rtc_dev->parent;
|
||||||
|
|
||||||
|
for (i = 0; i < count; i++) {
|
||||||
|
rc = pm8xxx_writeb(parent, base + i, rtc_val[i]);
|
||||||
|
if (rc < 0) {
|
||||||
|
dev_err(rtc_dd->rtc_dev, "PMIC write failed\n");
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Steps to write the RTC registers.
|
||||||
|
* 1. Disable alarm if enabled.
|
||||||
|
* 2. Write 0x00 to LSB.
|
||||||
|
* 3. Write Byte[1], Byte[2], Byte[3] then Byte[0].
|
||||||
|
* 4. Enable alarm if disabled in step 1.
|
||||||
|
*/
|
||||||
|
static int pm8xxx_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||||
|
{
|
||||||
|
int rc, i;
|
||||||
|
unsigned long secs, irq_flags;
|
||||||
|
u8 value[NUM_8_BIT_RTC_REGS], reg = 0, alarm_enabled = 0, ctrl_reg;
|
||||||
|
struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
rtc_tm_to_time(tm, &secs);
|
||||||
|
|
||||||
|
for (i = 0; i < NUM_8_BIT_RTC_REGS; i++) {
|
||||||
|
value[i] = secs & 0xFF;
|
||||||
|
secs >>= 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_dbg(dev, "Seconds value to be written to RTC = %lu\n", secs);
|
||||||
|
|
||||||
|
spin_lock_irqsave(&rtc_dd->ctrl_reg_lock, irq_flags);
|
||||||
|
ctrl_reg = rtc_dd->ctrl_reg;
|
||||||
|
|
||||||
|
if (ctrl_reg & PM8xxx_RTC_ALARM_ENABLE) {
|
||||||
|
alarm_enabled = 1;
|
||||||
|
ctrl_reg &= ~PM8xxx_RTC_ALARM_ENABLE;
|
||||||
|
rc = pm8xxx_write_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base,
|
||||||
|
1);
|
||||||
|
if (rc < 0) {
|
||||||
|
dev_err(dev, "Write to RTC control register "
|
||||||
|
"failed\n");
|
||||||
|
goto rtc_rw_fail;
|
||||||
|
}
|
||||||
|
rtc_dd->ctrl_reg = ctrl_reg;
|
||||||
|
} else
|
||||||
|
spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags);
|
||||||
|
|
||||||
|
/* Write 0 to Byte[0] */
|
||||||
|
reg = 0;
|
||||||
|
rc = pm8xxx_write_wrapper(rtc_dd, ®, rtc_dd->rtc_write_base, 1);
|
||||||
|
if (rc < 0) {
|
||||||
|
dev_err(dev, "Write to RTC write data register failed\n");
|
||||||
|
goto rtc_rw_fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Write Byte[1], Byte[2], Byte[3] */
|
||||||
|
rc = pm8xxx_write_wrapper(rtc_dd, value + 1,
|
||||||
|
rtc_dd->rtc_write_base + 1, 3);
|
||||||
|
if (rc < 0) {
|
||||||
|
dev_err(dev, "Write to RTC write data register failed\n");
|
||||||
|
goto rtc_rw_fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Write Byte[0] */
|
||||||
|
rc = pm8xxx_write_wrapper(rtc_dd, value, rtc_dd->rtc_write_base, 1);
|
||||||
|
if (rc < 0) {
|
||||||
|
dev_err(dev, "Write to RTC write data register failed\n");
|
||||||
|
goto rtc_rw_fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (alarm_enabled) {
|
||||||
|
ctrl_reg |= PM8xxx_RTC_ALARM_ENABLE;
|
||||||
|
rc = pm8xxx_write_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base,
|
||||||
|
1);
|
||||||
|
if (rc < 0) {
|
||||||
|
dev_err(dev, "Write to RTC control register "
|
||||||
|
"failed\n");
|
||||||
|
goto rtc_rw_fail;
|
||||||
|
}
|
||||||
|
rtc_dd->ctrl_reg = ctrl_reg;
|
||||||
|
}
|
||||||
|
|
||||||
|
rtc_rw_fail:
|
||||||
|
if (alarm_enabled)
|
||||||
|
spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pm8xxx_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
u8 value[NUM_8_BIT_RTC_REGS], reg;
|
||||||
|
unsigned long secs;
|
||||||
|
struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
rc = pm8xxx_read_wrapper(rtc_dd, value, rtc_dd->rtc_read_base,
|
||||||
|
NUM_8_BIT_RTC_REGS);
|
||||||
|
if (rc < 0) {
|
||||||
|
dev_err(dev, "RTC read data register failed\n");
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read the LSB again and check if there has been a carry over.
|
||||||
|
* If there is, redo the read operation.
|
||||||
|
*/
|
||||||
|
rc = pm8xxx_read_wrapper(rtc_dd, ®, rtc_dd->rtc_read_base, 1);
|
||||||
|
if (rc < 0) {
|
||||||
|
dev_err(dev, "RTC read data register failed\n");
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unlikely(reg < value[0])) {
|
||||||
|
rc = pm8xxx_read_wrapper(rtc_dd, value,
|
||||||
|
rtc_dd->rtc_read_base, NUM_8_BIT_RTC_REGS);
|
||||||
|
if (rc < 0) {
|
||||||
|
dev_err(dev, "RTC read data register failed\n");
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
secs = value[0] | (value[1] << 8) | (value[2] << 16) | (value[3] << 24);
|
||||||
|
|
||||||
|
rtc_time_to_tm(secs, tm);
|
||||||
|
|
||||||
|
rc = rtc_valid_tm(tm);
|
||||||
|
if (rc < 0) {
|
||||||
|
dev_err(dev, "Invalid time read from RTC\n");
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_dbg(dev, "secs = %lu, h:m:s == %d:%d:%d, d/m/y = %d/%d/%d\n",
|
||||||
|
secs, tm->tm_hour, tm->tm_min, tm->tm_sec,
|
||||||
|
tm->tm_mday, tm->tm_mon, tm->tm_year);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pm8xxx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
|
||||||
|
{
|
||||||
|
int rc, i;
|
||||||
|
u8 value[NUM_8_BIT_RTC_REGS], ctrl_reg;
|
||||||
|
unsigned long secs, irq_flags;
|
||||||
|
struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
rtc_tm_to_time(&alarm->time, &secs);
|
||||||
|
|
||||||
|
for (i = 0; i < NUM_8_BIT_RTC_REGS; i++) {
|
||||||
|
value[i] = secs & 0xFF;
|
||||||
|
secs >>= 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_lock_irqsave(&rtc_dd->ctrl_reg_lock, irq_flags);
|
||||||
|
|
||||||
|
rc = pm8xxx_write_wrapper(rtc_dd, value, rtc_dd->alarm_rw_base,
|
||||||
|
NUM_8_BIT_RTC_REGS);
|
||||||
|
if (rc < 0) {
|
||||||
|
dev_err(dev, "Write to RTC ALARM register failed\n");
|
||||||
|
goto rtc_rw_fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctrl_reg = rtc_dd->ctrl_reg;
|
||||||
|
ctrl_reg = alarm->enabled ? (ctrl_reg | PM8xxx_RTC_ALARM_ENABLE) :
|
||||||
|
(ctrl_reg & ~PM8xxx_RTC_ALARM_ENABLE);
|
||||||
|
|
||||||
|
rc = pm8xxx_write_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base, 1);
|
||||||
|
if (rc < 0) {
|
||||||
|
dev_err(dev, "Write to RTC control register failed\n");
|
||||||
|
goto rtc_rw_fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
rtc_dd->ctrl_reg = ctrl_reg;
|
||||||
|
|
||||||
|
dev_dbg(dev, "Alarm Set for h:r:s=%d:%d:%d, d/m/y=%d/%d/%d\n",
|
||||||
|
alarm->time.tm_hour, alarm->time.tm_min,
|
||||||
|
alarm->time.tm_sec, alarm->time.tm_mday,
|
||||||
|
alarm->time.tm_mon, alarm->time.tm_year);
|
||||||
|
rtc_rw_fail:
|
||||||
|
spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pm8xxx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
u8 value[NUM_8_BIT_RTC_REGS];
|
||||||
|
unsigned long secs;
|
||||||
|
struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
rc = pm8xxx_read_wrapper(rtc_dd, value, rtc_dd->alarm_rw_base,
|
||||||
|
NUM_8_BIT_RTC_REGS);
|
||||||
|
if (rc < 0) {
|
||||||
|
dev_err(dev, "RTC alarm time read failed\n");
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
secs = value[0] | (value[1] << 8) | (value[2] << 16) | (value[3] << 24);
|
||||||
|
|
||||||
|
rtc_time_to_tm(secs, &alarm->time);
|
||||||
|
|
||||||
|
rc = rtc_valid_tm(&alarm->time);
|
||||||
|
if (rc < 0) {
|
||||||
|
dev_err(dev, "Invalid alarm time read from RTC\n");
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_dbg(dev, "Alarm set for - h:r:s=%d:%d:%d, d/m/y=%d/%d/%d\n",
|
||||||
|
alarm->time.tm_hour, alarm->time.tm_min,
|
||||||
|
alarm->time.tm_sec, alarm->time.tm_mday,
|
||||||
|
alarm->time.tm_mon, alarm->time.tm_year);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pm8xxx_rtc_alarm_irq_enable(struct device *dev, unsigned int enable)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
unsigned long irq_flags;
|
||||||
|
struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev);
|
||||||
|
u8 ctrl_reg;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&rtc_dd->ctrl_reg_lock, irq_flags);
|
||||||
|
ctrl_reg = rtc_dd->ctrl_reg;
|
||||||
|
ctrl_reg = (enable) ? (ctrl_reg | PM8xxx_RTC_ALARM_ENABLE) :
|
||||||
|
(ctrl_reg & ~PM8xxx_RTC_ALARM_ENABLE);
|
||||||
|
|
||||||
|
rc = pm8xxx_write_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base, 1);
|
||||||
|
if (rc < 0) {
|
||||||
|
dev_err(dev, "Write to RTC control register failed\n");
|
||||||
|
goto rtc_rw_fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
rtc_dd->ctrl_reg = ctrl_reg;
|
||||||
|
|
||||||
|
rtc_rw_fail:
|
||||||
|
spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct rtc_class_ops pm8xxx_rtc_ops = {
|
||||||
|
.read_time = pm8xxx_rtc_read_time,
|
||||||
|
.set_alarm = pm8xxx_rtc_set_alarm,
|
||||||
|
.read_alarm = pm8xxx_rtc_read_alarm,
|
||||||
|
.alarm_irq_enable = pm8xxx_rtc_alarm_irq_enable,
|
||||||
|
};
|
||||||
|
|
||||||
|
static irqreturn_t pm8xxx_alarm_trigger(int irq, void *dev_id)
|
||||||
|
{
|
||||||
|
struct pm8xxx_rtc *rtc_dd = dev_id;
|
||||||
|
u8 ctrl_reg;
|
||||||
|
int rc;
|
||||||
|
unsigned long irq_flags;
|
||||||
|
|
||||||
|
rtc_update_irq(rtc_dd->rtc, 1, RTC_IRQF | RTC_AF);
|
||||||
|
|
||||||
|
spin_lock_irqsave(&rtc_dd->ctrl_reg_lock, irq_flags);
|
||||||
|
|
||||||
|
/* Clear the alarm enable bit */
|
||||||
|
ctrl_reg = rtc_dd->ctrl_reg;
|
||||||
|
ctrl_reg &= ~PM8xxx_RTC_ALARM_ENABLE;
|
||||||
|
|
||||||
|
rc = pm8xxx_write_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base, 1);
|
||||||
|
if (rc < 0) {
|
||||||
|
spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags);
|
||||||
|
dev_err(rtc_dd->rtc_dev, "Write to RTC control register "
|
||||||
|
"failed\n");
|
||||||
|
goto rtc_alarm_handled;
|
||||||
|
}
|
||||||
|
|
||||||
|
rtc_dd->ctrl_reg = ctrl_reg;
|
||||||
|
spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags);
|
||||||
|
|
||||||
|
/* Clear RTC alarm register */
|
||||||
|
rc = pm8xxx_read_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base +
|
||||||
|
PM8XXX_ALARM_CTRL_OFFSET, 1);
|
||||||
|
if (rc < 0) {
|
||||||
|
dev_err(rtc_dd->rtc_dev, "RTC Alarm control register read "
|
||||||
|
"failed\n");
|
||||||
|
goto rtc_alarm_handled;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctrl_reg &= ~PM8xxx_RTC_ALARM_CLEAR;
|
||||||
|
rc = pm8xxx_write_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base +
|
||||||
|
PM8XXX_ALARM_CTRL_OFFSET, 1);
|
||||||
|
if (rc < 0)
|
||||||
|
dev_err(rtc_dd->rtc_dev, "Write to RTC Alarm control register"
|
||||||
|
" failed\n");
|
||||||
|
|
||||||
|
rtc_alarm_handled:
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __devinit pm8xxx_rtc_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
u8 ctrl_reg;
|
||||||
|
bool rtc_write_enable = false;
|
||||||
|
struct pm8xxx_rtc *rtc_dd;
|
||||||
|
struct resource *rtc_resource;
|
||||||
|
const struct pm8xxx_rtc_platform_data *pdata =
|
||||||
|
dev_get_platdata(&pdev->dev);
|
||||||
|
|
||||||
|
if (pdata != NULL)
|
||||||
|
rtc_write_enable = pdata->rtc_write_enable;
|
||||||
|
|
||||||
|
rtc_dd = kzalloc(sizeof(*rtc_dd), GFP_KERNEL);
|
||||||
|
if (rtc_dd == NULL) {
|
||||||
|
dev_err(&pdev->dev, "Unable to allocate memory!\n");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialise spinlock to protect RTC control register */
|
||||||
|
spin_lock_init(&rtc_dd->ctrl_reg_lock);
|
||||||
|
|
||||||
|
rtc_dd->rtc_alarm_irq = platform_get_irq(pdev, 0);
|
||||||
|
if (rtc_dd->rtc_alarm_irq < 0) {
|
||||||
|
dev_err(&pdev->dev, "Alarm IRQ resource absent!\n");
|
||||||
|
rc = -ENXIO;
|
||||||
|
goto fail_rtc_enable;
|
||||||
|
}
|
||||||
|
|
||||||
|
rtc_resource = platform_get_resource_byname(pdev, IORESOURCE_IO,
|
||||||
|
"pmic_rtc_base");
|
||||||
|
if (!(rtc_resource && rtc_resource->start)) {
|
||||||
|
dev_err(&pdev->dev, "RTC IO resource absent!\n");
|
||||||
|
rc = -ENXIO;
|
||||||
|
goto fail_rtc_enable;
|
||||||
|
}
|
||||||
|
|
||||||
|
rtc_dd->rtc_base = rtc_resource->start;
|
||||||
|
|
||||||
|
/* Setup RTC register addresses */
|
||||||
|
rtc_dd->rtc_write_base = rtc_dd->rtc_base + PM8XXX_RTC_WRITE_OFFSET;
|
||||||
|
rtc_dd->rtc_read_base = rtc_dd->rtc_base + PM8XXX_RTC_READ_OFFSET;
|
||||||
|
rtc_dd->alarm_rw_base = rtc_dd->rtc_base + PM8XXX_ALARM_RW_OFFSET;
|
||||||
|
|
||||||
|
rtc_dd->rtc_dev = &pdev->dev;
|
||||||
|
|
||||||
|
/* Check if the RTC is on, else turn it on */
|
||||||
|
rc = pm8xxx_read_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base, 1);
|
||||||
|
if (rc < 0) {
|
||||||
|
dev_err(&pdev->dev, "RTC control register read failed!\n");
|
||||||
|
goto fail_rtc_enable;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(ctrl_reg & PM8xxx_RTC_ENABLE)) {
|
||||||
|
ctrl_reg |= PM8xxx_RTC_ENABLE;
|
||||||
|
rc = pm8xxx_write_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base,
|
||||||
|
1);
|
||||||
|
if (rc < 0) {
|
||||||
|
dev_err(&pdev->dev, "Write to RTC control register "
|
||||||
|
"failed\n");
|
||||||
|
goto fail_rtc_enable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rtc_dd->ctrl_reg = ctrl_reg;
|
||||||
|
if (rtc_write_enable == true)
|
||||||
|
pm8xxx_rtc_ops.set_time = pm8xxx_rtc_set_time;
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, rtc_dd);
|
||||||
|
|
||||||
|
/* Register the RTC device */
|
||||||
|
rtc_dd->rtc = rtc_device_register("pm8xxx_rtc", &pdev->dev,
|
||||||
|
&pm8xxx_rtc_ops, THIS_MODULE);
|
||||||
|
if (IS_ERR(rtc_dd->rtc)) {
|
||||||
|
dev_err(&pdev->dev, "%s: RTC registration failed (%ld)\n",
|
||||||
|
__func__, PTR_ERR(rtc_dd->rtc));
|
||||||
|
rc = PTR_ERR(rtc_dd->rtc);
|
||||||
|
goto fail_rtc_enable;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Request the alarm IRQ */
|
||||||
|
rc = request_any_context_irq(rtc_dd->rtc_alarm_irq,
|
||||||
|
pm8xxx_alarm_trigger, IRQF_TRIGGER_RISING,
|
||||||
|
"pm8xxx_rtc_alarm", rtc_dd);
|
||||||
|
if (rc < 0) {
|
||||||
|
dev_err(&pdev->dev, "Request IRQ failed (%d)\n", rc);
|
||||||
|
goto fail_req_irq;
|
||||||
|
}
|
||||||
|
|
||||||
|
device_init_wakeup(&pdev->dev, 1);
|
||||||
|
|
||||||
|
dev_dbg(&pdev->dev, "Probe success !!\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
fail_req_irq:
|
||||||
|
rtc_device_unregister(rtc_dd->rtc);
|
||||||
|
fail_rtc_enable:
|
||||||
|
platform_set_drvdata(pdev, NULL);
|
||||||
|
kfree(rtc_dd);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __devexit pm8xxx_rtc_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct pm8xxx_rtc *rtc_dd = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
|
device_init_wakeup(&pdev->dev, 0);
|
||||||
|
free_irq(rtc_dd->rtc_alarm_irq, rtc_dd);
|
||||||
|
rtc_device_unregister(rtc_dd->rtc);
|
||||||
|
platform_set_drvdata(pdev, NULL);
|
||||||
|
kfree(rtc_dd);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM_SLEEP
|
||||||
|
static int pm8xxx_rtc_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
if (device_may_wakeup(dev))
|
||||||
|
disable_irq_wake(rtc_dd->rtc_alarm_irq);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pm8xxx_rtc_suspend(struct device *dev)
|
||||||
|
{
|
||||||
|
struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
if (device_may_wakeup(dev))
|
||||||
|
enable_irq_wake(rtc_dd->rtc_alarm_irq);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
SIMPLE_DEV_PM_OPS(pm8xxx_rtc_pm_ops, pm8xxx_rtc_suspend, pm8xxx_rtc_resume);
|
||||||
|
|
||||||
|
static struct platform_driver pm8xxx_rtc_driver = {
|
||||||
|
.probe = pm8xxx_rtc_probe,
|
||||||
|
.remove = __devexit_p(pm8xxx_rtc_remove),
|
||||||
|
.driver = {
|
||||||
|
.name = PM8XXX_RTC_DEV_NAME,
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
.pm = &pm8xxx_rtc_pm_ops,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init pm8xxx_rtc_init(void)
|
||||||
|
{
|
||||||
|
return platform_driver_register(&pm8xxx_rtc_driver);
|
||||||
|
}
|
||||||
|
module_init(pm8xxx_rtc_init);
|
||||||
|
|
||||||
|
static void __exit pm8xxx_rtc_exit(void)
|
||||||
|
{
|
||||||
|
platform_driver_unregister(&pm8xxx_rtc_driver);
|
||||||
|
}
|
||||||
|
module_exit(pm8xxx_rtc_exit);
|
||||||
|
|
||||||
|
MODULE_ALIAS("platform:rtc-pm8xxx");
|
||||||
|
MODULE_DESCRIPTION("PMIC8xxx RTC driver");
|
||||||
|
MODULE_LICENSE("GPL v2");
|
||||||
|
MODULE_AUTHOR("Anirudh Ghayal <aghayal@codeaurora.org>");
|
@ -57,11 +57,13 @@ static irqreturn_t s3c_rtc_alarmirq(int irq, void *id)
|
|||||||
{
|
{
|
||||||
struct rtc_device *rdev = id;
|
struct rtc_device *rdev = id;
|
||||||
|
|
||||||
|
clk_enable(rtc_clk);
|
||||||
rtc_update_irq(rdev, 1, RTC_AF | RTC_IRQF);
|
rtc_update_irq(rdev, 1, RTC_AF | RTC_IRQF);
|
||||||
|
|
||||||
if (s3c_rtc_cpu_type == TYPE_S3C64XX)
|
if (s3c_rtc_cpu_type == TYPE_S3C64XX)
|
||||||
writeb(S3C2410_INTP_ALM, s3c_rtc_base + S3C2410_INTP);
|
writeb(S3C2410_INTP_ALM, s3c_rtc_base + S3C2410_INTP);
|
||||||
|
|
||||||
|
clk_disable(rtc_clk);
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,11 +71,13 @@ static irqreturn_t s3c_rtc_tickirq(int irq, void *id)
|
|||||||
{
|
{
|
||||||
struct rtc_device *rdev = id;
|
struct rtc_device *rdev = id;
|
||||||
|
|
||||||
|
clk_enable(rtc_clk);
|
||||||
rtc_update_irq(rdev, 1, RTC_PF | RTC_IRQF);
|
rtc_update_irq(rdev, 1, RTC_PF | RTC_IRQF);
|
||||||
|
|
||||||
if (s3c_rtc_cpu_type == TYPE_S3C64XX)
|
if (s3c_rtc_cpu_type == TYPE_S3C64XX)
|
||||||
writeb(S3C2410_INTP_TIC, s3c_rtc_base + S3C2410_INTP);
|
writeb(S3C2410_INTP_TIC, s3c_rtc_base + S3C2410_INTP);
|
||||||
|
|
||||||
|
clk_disable(rtc_clk);
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,12 +88,14 @@ static int s3c_rtc_setaie(struct device *dev, unsigned int enabled)
|
|||||||
|
|
||||||
pr_debug("%s: aie=%d\n", __func__, enabled);
|
pr_debug("%s: aie=%d\n", __func__, enabled);
|
||||||
|
|
||||||
|
clk_enable(rtc_clk);
|
||||||
tmp = readb(s3c_rtc_base + S3C2410_RTCALM) & ~S3C2410_RTCALM_ALMEN;
|
tmp = readb(s3c_rtc_base + S3C2410_RTCALM) & ~S3C2410_RTCALM_ALMEN;
|
||||||
|
|
||||||
if (enabled)
|
if (enabled)
|
||||||
tmp |= S3C2410_RTCALM_ALMEN;
|
tmp |= S3C2410_RTCALM_ALMEN;
|
||||||
|
|
||||||
writeb(tmp, s3c_rtc_base + S3C2410_RTCALM);
|
writeb(tmp, s3c_rtc_base + S3C2410_RTCALM);
|
||||||
|
clk_disable(rtc_clk);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -103,6 +109,7 @@ static int s3c_rtc_setfreq(struct device *dev, int freq)
|
|||||||
if (!is_power_of_2(freq))
|
if (!is_power_of_2(freq))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
clk_enable(rtc_clk);
|
||||||
spin_lock_irq(&s3c_rtc_pie_lock);
|
spin_lock_irq(&s3c_rtc_pie_lock);
|
||||||
|
|
||||||
if (s3c_rtc_cpu_type == TYPE_S3C2410) {
|
if (s3c_rtc_cpu_type == TYPE_S3C2410) {
|
||||||
@ -114,6 +121,7 @@ static int s3c_rtc_setfreq(struct device *dev, int freq)
|
|||||||
|
|
||||||
writel(tmp, s3c_rtc_base + S3C2410_TICNT);
|
writel(tmp, s3c_rtc_base + S3C2410_TICNT);
|
||||||
spin_unlock_irq(&s3c_rtc_pie_lock);
|
spin_unlock_irq(&s3c_rtc_pie_lock);
|
||||||
|
clk_disable(rtc_clk);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -125,6 +133,7 @@ static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
|
|||||||
unsigned int have_retried = 0;
|
unsigned int have_retried = 0;
|
||||||
void __iomem *base = s3c_rtc_base;
|
void __iomem *base = s3c_rtc_base;
|
||||||
|
|
||||||
|
clk_enable(rtc_clk);
|
||||||
retry_get_time:
|
retry_get_time:
|
||||||
rtc_tm->tm_min = readb(base + S3C2410_RTCMIN);
|
rtc_tm->tm_min = readb(base + S3C2410_RTCMIN);
|
||||||
rtc_tm->tm_hour = readb(base + S3C2410_RTCHOUR);
|
rtc_tm->tm_hour = readb(base + S3C2410_RTCHOUR);
|
||||||
@ -157,6 +166,7 @@ static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
|
|||||||
rtc_tm->tm_year += 100;
|
rtc_tm->tm_year += 100;
|
||||||
rtc_tm->tm_mon -= 1;
|
rtc_tm->tm_mon -= 1;
|
||||||
|
|
||||||
|
clk_disable(rtc_clk);
|
||||||
return rtc_valid_tm(rtc_tm);
|
return rtc_valid_tm(rtc_tm);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -165,6 +175,7 @@ static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm)
|
|||||||
void __iomem *base = s3c_rtc_base;
|
void __iomem *base = s3c_rtc_base;
|
||||||
int year = tm->tm_year - 100;
|
int year = tm->tm_year - 100;
|
||||||
|
|
||||||
|
clk_enable(rtc_clk);
|
||||||
pr_debug("set time %04d.%02d.%02d %02d:%02d:%02d\n",
|
pr_debug("set time %04d.%02d.%02d %02d:%02d:%02d\n",
|
||||||
1900 + tm->tm_year, tm->tm_mon, tm->tm_mday,
|
1900 + tm->tm_year, tm->tm_mon, tm->tm_mday,
|
||||||
tm->tm_hour, tm->tm_min, tm->tm_sec);
|
tm->tm_hour, tm->tm_min, tm->tm_sec);
|
||||||
@ -182,6 +193,7 @@ static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm)
|
|||||||
writeb(bin2bcd(tm->tm_mday), base + S3C2410_RTCDATE);
|
writeb(bin2bcd(tm->tm_mday), base + S3C2410_RTCDATE);
|
||||||
writeb(bin2bcd(tm->tm_mon + 1), base + S3C2410_RTCMON);
|
writeb(bin2bcd(tm->tm_mon + 1), base + S3C2410_RTCMON);
|
||||||
writeb(bin2bcd(year), base + S3C2410_RTCYEAR);
|
writeb(bin2bcd(year), base + S3C2410_RTCYEAR);
|
||||||
|
clk_disable(rtc_clk);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -192,6 +204,7 @@ static int s3c_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm)
|
|||||||
void __iomem *base = s3c_rtc_base;
|
void __iomem *base = s3c_rtc_base;
|
||||||
unsigned int alm_en;
|
unsigned int alm_en;
|
||||||
|
|
||||||
|
clk_enable(rtc_clk);
|
||||||
alm_tm->tm_sec = readb(base + S3C2410_ALMSEC);
|
alm_tm->tm_sec = readb(base + S3C2410_ALMSEC);
|
||||||
alm_tm->tm_min = readb(base + S3C2410_ALMMIN);
|
alm_tm->tm_min = readb(base + S3C2410_ALMMIN);
|
||||||
alm_tm->tm_hour = readb(base + S3C2410_ALMHOUR);
|
alm_tm->tm_hour = readb(base + S3C2410_ALMHOUR);
|
||||||
@ -243,6 +256,7 @@ static int s3c_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm)
|
|||||||
else
|
else
|
||||||
alm_tm->tm_year = -1;
|
alm_tm->tm_year = -1;
|
||||||
|
|
||||||
|
clk_disable(rtc_clk);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -252,6 +266,7 @@ static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
|
|||||||
void __iomem *base = s3c_rtc_base;
|
void __iomem *base = s3c_rtc_base;
|
||||||
unsigned int alrm_en;
|
unsigned int alrm_en;
|
||||||
|
|
||||||
|
clk_enable(rtc_clk);
|
||||||
pr_debug("s3c_rtc_setalarm: %d, %04d.%02d.%02d %02d:%02d:%02d\n",
|
pr_debug("s3c_rtc_setalarm: %d, %04d.%02d.%02d %02d:%02d:%02d\n",
|
||||||
alrm->enabled,
|
alrm->enabled,
|
||||||
1900 + tm->tm_year, tm->tm_mon, tm->tm_mday,
|
1900 + tm->tm_year, tm->tm_mon, tm->tm_mday,
|
||||||
@ -282,6 +297,7 @@ static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
|
|||||||
|
|
||||||
s3c_rtc_setaie(dev, alrm->enabled);
|
s3c_rtc_setaie(dev, alrm->enabled);
|
||||||
|
|
||||||
|
clk_disable(rtc_clk);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -289,6 +305,7 @@ static int s3c_rtc_proc(struct device *dev, struct seq_file *seq)
|
|||||||
{
|
{
|
||||||
unsigned int ticnt;
|
unsigned int ticnt;
|
||||||
|
|
||||||
|
clk_enable(rtc_clk);
|
||||||
if (s3c_rtc_cpu_type == TYPE_S3C64XX) {
|
if (s3c_rtc_cpu_type == TYPE_S3C64XX) {
|
||||||
ticnt = readw(s3c_rtc_base + S3C2410_RTCCON);
|
ticnt = readw(s3c_rtc_base + S3C2410_RTCCON);
|
||||||
ticnt &= S3C64XX_RTCCON_TICEN;
|
ticnt &= S3C64XX_RTCCON_TICEN;
|
||||||
@ -298,6 +315,7 @@ static int s3c_rtc_proc(struct device *dev, struct seq_file *seq)
|
|||||||
}
|
}
|
||||||
|
|
||||||
seq_printf(seq, "periodic_IRQ\t: %s\n", ticnt ? "yes" : "no");
|
seq_printf(seq, "periodic_IRQ\t: %s\n", ticnt ? "yes" : "no");
|
||||||
|
clk_disable(rtc_clk);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -360,6 +378,7 @@ static void s3c_rtc_enable(struct platform_device *pdev, int en)
|
|||||||
if (s3c_rtc_base == NULL)
|
if (s3c_rtc_base == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
clk_enable(rtc_clk);
|
||||||
if (!en) {
|
if (!en) {
|
||||||
tmp = readw(base + S3C2410_RTCCON);
|
tmp = readw(base + S3C2410_RTCCON);
|
||||||
if (s3c_rtc_cpu_type == TYPE_S3C64XX)
|
if (s3c_rtc_cpu_type == TYPE_S3C64XX)
|
||||||
@ -399,6 +418,7 @@ static void s3c_rtc_enable(struct platform_device *pdev, int en)
|
|||||||
base + S3C2410_RTCCON);
|
base + S3C2410_RTCCON);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
clk_disable(rtc_clk);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __devexit s3c_rtc_remove(struct platform_device *dev)
|
static int __devexit s3c_rtc_remove(struct platform_device *dev)
|
||||||
@ -410,7 +430,6 @@ static int __devexit s3c_rtc_remove(struct platform_device *dev)
|
|||||||
|
|
||||||
s3c_rtc_setaie(&dev->dev, 0);
|
s3c_rtc_setaie(&dev->dev, 0);
|
||||||
|
|
||||||
clk_disable(rtc_clk);
|
|
||||||
clk_put(rtc_clk);
|
clk_put(rtc_clk);
|
||||||
rtc_clk = NULL;
|
rtc_clk = NULL;
|
||||||
|
|
||||||
@ -529,6 +548,8 @@ static int __devinit s3c_rtc_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
s3c_rtc_setfreq(&pdev->dev, 1);
|
s3c_rtc_setfreq(&pdev->dev, 1);
|
||||||
|
|
||||||
|
clk_disable(rtc_clk);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_nortc:
|
err_nortc:
|
||||||
@ -554,6 +575,7 @@ static int ticnt_save, ticnt_en_save;
|
|||||||
|
|
||||||
static int s3c_rtc_suspend(struct platform_device *pdev, pm_message_t state)
|
static int s3c_rtc_suspend(struct platform_device *pdev, pm_message_t state)
|
||||||
{
|
{
|
||||||
|
clk_enable(rtc_clk);
|
||||||
/* save TICNT for anyone using periodic interrupts */
|
/* save TICNT for anyone using periodic interrupts */
|
||||||
ticnt_save = readb(s3c_rtc_base + S3C2410_TICNT);
|
ticnt_save = readb(s3c_rtc_base + S3C2410_TICNT);
|
||||||
if (s3c_rtc_cpu_type == TYPE_S3C64XX) {
|
if (s3c_rtc_cpu_type == TYPE_S3C64XX) {
|
||||||
@ -568,6 +590,7 @@ static int s3c_rtc_suspend(struct platform_device *pdev, pm_message_t state)
|
|||||||
else
|
else
|
||||||
dev_err(&pdev->dev, "enable_irq_wake failed\n");
|
dev_err(&pdev->dev, "enable_irq_wake failed\n");
|
||||||
}
|
}
|
||||||
|
clk_disable(rtc_clk);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -576,6 +599,7 @@ static int s3c_rtc_resume(struct platform_device *pdev)
|
|||||||
{
|
{
|
||||||
unsigned int tmp;
|
unsigned int tmp;
|
||||||
|
|
||||||
|
clk_enable(rtc_clk);
|
||||||
s3c_rtc_enable(pdev, 1);
|
s3c_rtc_enable(pdev, 1);
|
||||||
writeb(ticnt_save, s3c_rtc_base + S3C2410_TICNT);
|
writeb(ticnt_save, s3c_rtc_base + S3C2410_TICNT);
|
||||||
if (s3c_rtc_cpu_type == TYPE_S3C64XX && ticnt_en_save) {
|
if (s3c_rtc_cpu_type == TYPE_S3C64XX && ticnt_en_save) {
|
||||||
@ -587,6 +611,7 @@ static int s3c_rtc_resume(struct platform_device *pdev)
|
|||||||
disable_irq_wake(s3c_rtc_alarmno);
|
disable_irq_wake(s3c_rtc_alarmno);
|
||||||
wake_en = false;
|
wake_en = false;
|
||||||
}
|
}
|
||||||
|
clk_disable(rtc_clk);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -343,7 +343,7 @@ static int __devinit tegra_rtc_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
/* set context info. */
|
/* set context info. */
|
||||||
info->pdev = pdev;
|
info->pdev = pdev;
|
||||||
info->tegra_rtc_lock = __SPIN_LOCK_UNLOCKED(info->tegra_rtc_lock);
|
spin_lock_init(&info->tegra_rtc_lock);
|
||||||
|
|
||||||
platform_set_drvdata(pdev, info);
|
platform_set_drvdata(pdev, info);
|
||||||
|
|
||||||
|
@ -275,7 +275,7 @@ static int twl_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
|||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
save_control &= ~BIT_RTC_CTRL_REG_STOP_RTC_M;
|
save_control &= ~BIT_RTC_CTRL_REG_STOP_RTC_M;
|
||||||
twl_rtc_write_u8(save_control, REG_RTC_CTRL_REG);
|
ret = twl_rtc_write_u8(save_control, REG_RTC_CTRL_REG);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
@ -117,6 +117,14 @@ config LCD_LD9040
|
|||||||
If you have an LD9040 Panel, say Y to enable its
|
If you have an LD9040 Panel, say Y to enable its
|
||||||
control driver.
|
control driver.
|
||||||
|
|
||||||
|
config LCD_AMS369FG06
|
||||||
|
tristate "AMS369FG06 AMOLED LCD Driver"
|
||||||
|
depends on SPI && BACKLIGHT_CLASS_DEVICE
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
If you have an AMS369FG06 AMOLED Panel, say Y to enable its
|
||||||
|
LCD control driver.
|
||||||
|
|
||||||
endif # LCD_CLASS_DEVICE
|
endif # LCD_CLASS_DEVICE
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -13,6 +13,7 @@ obj-$(CONFIG_LCD_TDO24M) += tdo24m.o
|
|||||||
obj-$(CONFIG_LCD_TOSA) += tosa_lcd.o
|
obj-$(CONFIG_LCD_TOSA) += tosa_lcd.o
|
||||||
obj-$(CONFIG_LCD_S6E63M0) += s6e63m0.o
|
obj-$(CONFIG_LCD_S6E63M0) += s6e63m0.o
|
||||||
obj-$(CONFIG_LCD_LD9040) += ld9040.o
|
obj-$(CONFIG_LCD_LD9040) += ld9040.o
|
||||||
|
obj-$(CONFIG_LCD_AMS369FG06) += ams369fg06.o
|
||||||
|
|
||||||
obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) += backlight.o
|
obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) += backlight.o
|
||||||
obj-$(CONFIG_BACKLIGHT_ATMEL_PWM) += atmel-pwm-bl.o
|
obj-$(CONFIG_BACKLIGHT_ATMEL_PWM) += atmel-pwm-bl.o
|
||||||
|
@ -722,8 +722,7 @@ static int __devinit adp8860_probe(struct i2c_client *client,
|
|||||||
goto out2;
|
goto out2;
|
||||||
}
|
}
|
||||||
|
|
||||||
bl->props.max_brightness =
|
bl->props.brightness = ADP8860_MAX_BRIGHTNESS;
|
||||||
bl->props.brightness = ADP8860_MAX_BRIGHTNESS;
|
|
||||||
|
|
||||||
data->bl = bl;
|
data->bl = bl;
|
||||||
|
|
||||||
|
646
drivers/video/backlight/ams369fg06.c
Normal file
646
drivers/video/backlight/ams369fg06.c
Normal file
@ -0,0 +1,646 @@
|
|||||||
|
/*
|
||||||
|
* ams369fg06 AMOLED LCD panel driver.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
|
||||||
|
* Author: Jingoo Han <jg1.han@samsung.com>
|
||||||
|
*
|
||||||
|
* Derived from drivers/video/s6e63m0.c
|
||||||
|
*
|
||||||
|
* 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.,
|
||||||
|
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/wait.h>
|
||||||
|
#include <linux/fb.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/gpio.h>
|
||||||
|
#include <linux/spi/spi.h>
|
||||||
|
#include <linux/lcd.h>
|
||||||
|
#include <linux/backlight.h>
|
||||||
|
|
||||||
|
#define SLEEPMSEC 0x1000
|
||||||
|
#define ENDDEF 0x2000
|
||||||
|
#define DEFMASK 0xFF00
|
||||||
|
#define COMMAND_ONLY 0xFE
|
||||||
|
#define DATA_ONLY 0xFF
|
||||||
|
|
||||||
|
#define MAX_GAMMA_LEVEL 5
|
||||||
|
#define GAMMA_TABLE_COUNT 21
|
||||||
|
|
||||||
|
#define MIN_BRIGHTNESS 0
|
||||||
|
#define MAX_BRIGHTNESS 255
|
||||||
|
#define DEFAULT_BRIGHTNESS 150
|
||||||
|
|
||||||
|
struct ams369fg06 {
|
||||||
|
struct device *dev;
|
||||||
|
struct spi_device *spi;
|
||||||
|
unsigned int power;
|
||||||
|
struct lcd_device *ld;
|
||||||
|
struct backlight_device *bd;
|
||||||
|
struct lcd_platform_data *lcd_pd;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const unsigned short seq_display_on[] = {
|
||||||
|
0x14, 0x03,
|
||||||
|
ENDDEF, 0x0000
|
||||||
|
};
|
||||||
|
|
||||||
|
static const unsigned short seq_display_off[] = {
|
||||||
|
0x14, 0x00,
|
||||||
|
ENDDEF, 0x0000
|
||||||
|
};
|
||||||
|
|
||||||
|
static const unsigned short seq_stand_by_on[] = {
|
||||||
|
0x1D, 0xA1,
|
||||||
|
SLEEPMSEC, 200,
|
||||||
|
ENDDEF, 0x0000
|
||||||
|
};
|
||||||
|
|
||||||
|
static const unsigned short seq_stand_by_off[] = {
|
||||||
|
0x1D, 0xA0,
|
||||||
|
SLEEPMSEC, 250,
|
||||||
|
ENDDEF, 0x0000
|
||||||
|
};
|
||||||
|
|
||||||
|
static const unsigned short seq_setting[] = {
|
||||||
|
0x31, 0x08,
|
||||||
|
0x32, 0x14,
|
||||||
|
0x30, 0x02,
|
||||||
|
0x27, 0x01,
|
||||||
|
0x12, 0x08,
|
||||||
|
0x13, 0x08,
|
||||||
|
0x15, 0x00,
|
||||||
|
0x16, 0x00,
|
||||||
|
|
||||||
|
0xef, 0xd0,
|
||||||
|
DATA_ONLY, 0xe8,
|
||||||
|
|
||||||
|
0x39, 0x44,
|
||||||
|
0x40, 0x00,
|
||||||
|
0x41, 0x3f,
|
||||||
|
0x42, 0x2a,
|
||||||
|
0x43, 0x27,
|
||||||
|
0x44, 0x27,
|
||||||
|
0x45, 0x1f,
|
||||||
|
0x46, 0x44,
|
||||||
|
0x50, 0x00,
|
||||||
|
0x51, 0x00,
|
||||||
|
0x52, 0x17,
|
||||||
|
0x53, 0x24,
|
||||||
|
0x54, 0x26,
|
||||||
|
0x55, 0x1f,
|
||||||
|
0x56, 0x43,
|
||||||
|
0x60, 0x00,
|
||||||
|
0x61, 0x3f,
|
||||||
|
0x62, 0x2a,
|
||||||
|
0x63, 0x25,
|
||||||
|
0x64, 0x24,
|
||||||
|
0x65, 0x1b,
|
||||||
|
0x66, 0x5c,
|
||||||
|
|
||||||
|
0x17, 0x22,
|
||||||
|
0x18, 0x33,
|
||||||
|
0x19, 0x03,
|
||||||
|
0x1a, 0x01,
|
||||||
|
0x22, 0xa4,
|
||||||
|
0x23, 0x00,
|
||||||
|
0x26, 0xa0,
|
||||||
|
|
||||||
|
0x1d, 0xa0,
|
||||||
|
SLEEPMSEC, 300,
|
||||||
|
|
||||||
|
0x14, 0x03,
|
||||||
|
|
||||||
|
ENDDEF, 0x0000
|
||||||
|
};
|
||||||
|
|
||||||
|
/* gamma value: 2.2 */
|
||||||
|
static const unsigned int ams369fg06_22_250[] = {
|
||||||
|
0x00, 0x3f, 0x2a, 0x27, 0x27, 0x1f, 0x44,
|
||||||
|
0x00, 0x00, 0x17, 0x24, 0x26, 0x1f, 0x43,
|
||||||
|
0x00, 0x3f, 0x2a, 0x25, 0x24, 0x1b, 0x5c,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const unsigned int ams369fg06_22_200[] = {
|
||||||
|
0x00, 0x3f, 0x28, 0x29, 0x27, 0x21, 0x3e,
|
||||||
|
0x00, 0x00, 0x10, 0x25, 0x27, 0x20, 0x3d,
|
||||||
|
0x00, 0x3f, 0x28, 0x27, 0x25, 0x1d, 0x53,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const unsigned int ams369fg06_22_150[] = {
|
||||||
|
0x00, 0x3f, 0x2d, 0x29, 0x28, 0x23, 0x37,
|
||||||
|
0x00, 0x00, 0x0b, 0x25, 0x28, 0x22, 0x36,
|
||||||
|
0x00, 0x3f, 0x2b, 0x28, 0x26, 0x1f, 0x4a,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const unsigned int ams369fg06_22_100[] = {
|
||||||
|
0x00, 0x3f, 0x30, 0x2a, 0x2b, 0x24, 0x2f,
|
||||||
|
0x00, 0x00, 0x00, 0x25, 0x29, 0x24, 0x2e,
|
||||||
|
0x00, 0x3f, 0x2f, 0x29, 0x29, 0x21, 0x3f,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const unsigned int ams369fg06_22_50[] = {
|
||||||
|
0x00, 0x3f, 0x3c, 0x2c, 0x2d, 0x27, 0x24,
|
||||||
|
0x00, 0x00, 0x00, 0x22, 0x2a, 0x27, 0x23,
|
||||||
|
0x00, 0x3f, 0x3b, 0x2c, 0x2b, 0x24, 0x31,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ams369fg06_gamma {
|
||||||
|
unsigned int *gamma_22_table[MAX_GAMMA_LEVEL];
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct ams369fg06_gamma gamma_table = {
|
||||||
|
.gamma_22_table[0] = (unsigned int *)&ams369fg06_22_50,
|
||||||
|
.gamma_22_table[1] = (unsigned int *)&ams369fg06_22_100,
|
||||||
|
.gamma_22_table[2] = (unsigned int *)&ams369fg06_22_150,
|
||||||
|
.gamma_22_table[3] = (unsigned int *)&ams369fg06_22_200,
|
||||||
|
.gamma_22_table[4] = (unsigned int *)&ams369fg06_22_250,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int ams369fg06_spi_write_byte(struct ams369fg06 *lcd, int addr, int data)
|
||||||
|
{
|
||||||
|
u16 buf[1];
|
||||||
|
struct spi_message msg;
|
||||||
|
|
||||||
|
struct spi_transfer xfer = {
|
||||||
|
.len = 2,
|
||||||
|
.tx_buf = buf,
|
||||||
|
};
|
||||||
|
|
||||||
|
buf[0] = (addr << 8) | data;
|
||||||
|
|
||||||
|
spi_message_init(&msg);
|
||||||
|
spi_message_add_tail(&xfer, &msg);
|
||||||
|
|
||||||
|
return spi_sync(lcd->spi, &msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ams369fg06_spi_write(struct ams369fg06 *lcd, unsigned char address,
|
||||||
|
unsigned char command)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (address != DATA_ONLY)
|
||||||
|
ret = ams369fg06_spi_write_byte(lcd, 0x70, address);
|
||||||
|
if (command != COMMAND_ONLY)
|
||||||
|
ret = ams369fg06_spi_write_byte(lcd, 0x72, command);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ams369fg06_panel_send_sequence(struct ams369fg06 *lcd,
|
||||||
|
const unsigned short *wbuf)
|
||||||
|
{
|
||||||
|
int ret = 0, i = 0;
|
||||||
|
|
||||||
|
while ((wbuf[i] & DEFMASK) != ENDDEF) {
|
||||||
|
if ((wbuf[i] & DEFMASK) != SLEEPMSEC) {
|
||||||
|
ret = ams369fg06_spi_write(lcd, wbuf[i], wbuf[i+1]);
|
||||||
|
if (ret)
|
||||||
|
break;
|
||||||
|
} else
|
||||||
|
mdelay(wbuf[i+1]);
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _ams369fg06_gamma_ctl(struct ams369fg06 *lcd,
|
||||||
|
const unsigned int *gamma)
|
||||||
|
{
|
||||||
|
unsigned int i = 0;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
for (i = 0 ; i < GAMMA_TABLE_COUNT / 3; i++) {
|
||||||
|
ret = ams369fg06_spi_write(lcd, 0x40 + i, gamma[i]);
|
||||||
|
ret = ams369fg06_spi_write(lcd, 0x50 + i, gamma[i+7*1]);
|
||||||
|
ret = ams369fg06_spi_write(lcd, 0x60 + i, gamma[i+7*2]);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(lcd->dev, "failed to set gamma table.\n");
|
||||||
|
goto gamma_err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gamma_err:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ams369fg06_gamma_ctl(struct ams369fg06 *lcd, int brightness)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
int gamma = 0;
|
||||||
|
|
||||||
|
if ((brightness >= 0) && (brightness <= 50))
|
||||||
|
gamma = 0;
|
||||||
|
else if ((brightness > 50) && (brightness <= 100))
|
||||||
|
gamma = 1;
|
||||||
|
else if ((brightness > 100) && (brightness <= 150))
|
||||||
|
gamma = 2;
|
||||||
|
else if ((brightness > 150) && (brightness <= 200))
|
||||||
|
gamma = 3;
|
||||||
|
else if ((brightness > 200) && (brightness <= 255))
|
||||||
|
gamma = 4;
|
||||||
|
|
||||||
|
ret = _ams369fg06_gamma_ctl(lcd, gamma_table.gamma_22_table[gamma]);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ams369fg06_ldi_init(struct ams369fg06 *lcd)
|
||||||
|
{
|
||||||
|
int ret, i;
|
||||||
|
static const unsigned short *init_seq[] = {
|
||||||
|
seq_setting,
|
||||||
|
seq_stand_by_off,
|
||||||
|
};
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(init_seq); i++) {
|
||||||
|
ret = ams369fg06_panel_send_sequence(lcd, init_seq[i]);
|
||||||
|
if (ret)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ams369fg06_ldi_enable(struct ams369fg06 *lcd)
|
||||||
|
{
|
||||||
|
int ret, i;
|
||||||
|
static const unsigned short *init_seq[] = {
|
||||||
|
seq_stand_by_off,
|
||||||
|
seq_display_on,
|
||||||
|
};
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(init_seq); i++) {
|
||||||
|
ret = ams369fg06_panel_send_sequence(lcd, init_seq[i]);
|
||||||
|
if (ret)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ams369fg06_ldi_disable(struct ams369fg06 *lcd)
|
||||||
|
{
|
||||||
|
int ret, i;
|
||||||
|
|
||||||
|
static const unsigned short *init_seq[] = {
|
||||||
|
seq_display_off,
|
||||||
|
seq_stand_by_on,
|
||||||
|
};
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(init_seq); i++) {
|
||||||
|
ret = ams369fg06_panel_send_sequence(lcd, init_seq[i]);
|
||||||
|
if (ret)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ams369fg06_power_is_on(int power)
|
||||||
|
{
|
||||||
|
return ((power) <= FB_BLANK_NORMAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ams369fg06_power_on(struct ams369fg06 *lcd)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
struct lcd_platform_data *pd = NULL;
|
||||||
|
struct backlight_device *bd = NULL;
|
||||||
|
|
||||||
|
pd = lcd->lcd_pd;
|
||||||
|
if (!pd) {
|
||||||
|
dev_err(lcd->dev, "platform data is NULL.\n");
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
bd = lcd->bd;
|
||||||
|
if (!bd) {
|
||||||
|
dev_err(lcd->dev, "backlight device is NULL.\n");
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pd->power_on) {
|
||||||
|
dev_err(lcd->dev, "power_on is NULL.\n");
|
||||||
|
return -EFAULT;
|
||||||
|
} else {
|
||||||
|
pd->power_on(lcd->ld, 1);
|
||||||
|
mdelay(pd->power_on_delay);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pd->reset) {
|
||||||
|
dev_err(lcd->dev, "reset is NULL.\n");
|
||||||
|
return -EFAULT;
|
||||||
|
} else {
|
||||||
|
pd->reset(lcd->ld);
|
||||||
|
mdelay(pd->reset_delay);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = ams369fg06_ldi_init(lcd);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(lcd->dev, "failed to initialize ldi.\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = ams369fg06_ldi_enable(lcd);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(lcd->dev, "failed to enable ldi.\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* set brightness to current value after power on or resume. */
|
||||||
|
ret = ams369fg06_gamma_ctl(lcd, bd->props.brightness);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(lcd->dev, "lcd gamma setting failed.\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ams369fg06_power_off(struct ams369fg06 *lcd)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
struct lcd_platform_data *pd = NULL;
|
||||||
|
|
||||||
|
pd = lcd->lcd_pd;
|
||||||
|
if (!pd) {
|
||||||
|
dev_err(lcd->dev, "platform data is NULL\n");
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = ams369fg06_ldi_disable(lcd);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(lcd->dev, "lcd setting failed.\n");
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
mdelay(pd->power_off_delay);
|
||||||
|
|
||||||
|
if (!pd->power_on) {
|
||||||
|
dev_err(lcd->dev, "power_on is NULL.\n");
|
||||||
|
return -EFAULT;
|
||||||
|
} else
|
||||||
|
pd->power_on(lcd->ld, 0);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ams369fg06_power(struct ams369fg06 *lcd, int power)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (ams369fg06_power_is_on(power) &&
|
||||||
|
!ams369fg06_power_is_on(lcd->power))
|
||||||
|
ret = ams369fg06_power_on(lcd);
|
||||||
|
else if (!ams369fg06_power_is_on(power) &&
|
||||||
|
ams369fg06_power_is_on(lcd->power))
|
||||||
|
ret = ams369fg06_power_off(lcd);
|
||||||
|
|
||||||
|
if (!ret)
|
||||||
|
lcd->power = power;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ams369fg06_get_power(struct lcd_device *ld)
|
||||||
|
{
|
||||||
|
struct ams369fg06 *lcd = lcd_get_data(ld);
|
||||||
|
|
||||||
|
return lcd->power;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ams369fg06_set_power(struct lcd_device *ld, int power)
|
||||||
|
{
|
||||||
|
struct ams369fg06 *lcd = lcd_get_data(ld);
|
||||||
|
|
||||||
|
if (power != FB_BLANK_UNBLANK && power != FB_BLANK_POWERDOWN &&
|
||||||
|
power != FB_BLANK_NORMAL) {
|
||||||
|
dev_err(lcd->dev, "power value should be 0, 1 or 4.\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ams369fg06_power(lcd, power);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ams369fg06_get_brightness(struct backlight_device *bd)
|
||||||
|
{
|
||||||
|
return bd->props.brightness;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ams369fg06_set_brightness(struct backlight_device *bd)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
int brightness = bd->props.brightness;
|
||||||
|
struct ams369fg06 *lcd = dev_get_drvdata(&bd->dev);
|
||||||
|
|
||||||
|
if (brightness < MIN_BRIGHTNESS ||
|
||||||
|
brightness > bd->props.max_brightness) {
|
||||||
|
dev_err(&bd->dev, "lcd brightness should be %d to %d.\n",
|
||||||
|
MIN_BRIGHTNESS, MAX_BRIGHTNESS);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = ams369fg06_gamma_ctl(lcd, bd->props.brightness);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&bd->dev, "lcd brightness setting failed.\n");
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct lcd_ops ams369fg06_lcd_ops = {
|
||||||
|
.get_power = ams369fg06_get_power,
|
||||||
|
.set_power = ams369fg06_set_power,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct backlight_ops ams369fg06_backlight_ops = {
|
||||||
|
.get_brightness = ams369fg06_get_brightness,
|
||||||
|
.update_status = ams369fg06_set_brightness,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __devinit ams369fg06_probe(struct spi_device *spi)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
struct ams369fg06 *lcd = NULL;
|
||||||
|
struct lcd_device *ld = NULL;
|
||||||
|
struct backlight_device *bd = NULL;
|
||||||
|
struct backlight_properties props;
|
||||||
|
|
||||||
|
lcd = kzalloc(sizeof(struct ams369fg06), GFP_KERNEL);
|
||||||
|
if (!lcd)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
/* ams369fg06 lcd panel uses 3-wire 16bits SPI Mode. */
|
||||||
|
spi->bits_per_word = 16;
|
||||||
|
|
||||||
|
ret = spi_setup(spi);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(&spi->dev, "spi setup failed.\n");
|
||||||
|
goto out_free_lcd;
|
||||||
|
}
|
||||||
|
|
||||||
|
lcd->spi = spi;
|
||||||
|
lcd->dev = &spi->dev;
|
||||||
|
|
||||||
|
lcd->lcd_pd = spi->dev.platform_data;
|
||||||
|
if (!lcd->lcd_pd) {
|
||||||
|
dev_err(&spi->dev, "platform data is NULL\n");
|
||||||
|
goto out_free_lcd;
|
||||||
|
}
|
||||||
|
|
||||||
|
ld = lcd_device_register("ams369fg06", &spi->dev, lcd,
|
||||||
|
&ams369fg06_lcd_ops);
|
||||||
|
if (IS_ERR(ld)) {
|
||||||
|
ret = PTR_ERR(ld);
|
||||||
|
goto out_free_lcd;
|
||||||
|
}
|
||||||
|
|
||||||
|
lcd->ld = ld;
|
||||||
|
|
||||||
|
memset(&props, 0, sizeof(struct backlight_properties));
|
||||||
|
props.type = BACKLIGHT_RAW;
|
||||||
|
props.max_brightness = MAX_BRIGHTNESS;
|
||||||
|
|
||||||
|
bd = backlight_device_register("ams369fg06-bl", &spi->dev, lcd,
|
||||||
|
&ams369fg06_backlight_ops, &props);
|
||||||
|
if (IS_ERR(bd)) {
|
||||||
|
ret = PTR_ERR(bd);
|
||||||
|
goto out_lcd_unregister;
|
||||||
|
}
|
||||||
|
|
||||||
|
bd->props.brightness = DEFAULT_BRIGHTNESS;
|
||||||
|
lcd->bd = bd;
|
||||||
|
|
||||||
|
if (!lcd->lcd_pd->lcd_enabled) {
|
||||||
|
/*
|
||||||
|
* if lcd panel was off from bootloader then
|
||||||
|
* current lcd status is powerdown and then
|
||||||
|
* it enables lcd panel.
|
||||||
|
*/
|
||||||
|
lcd->power = FB_BLANK_POWERDOWN;
|
||||||
|
|
||||||
|
ams369fg06_power(lcd, FB_BLANK_UNBLANK);
|
||||||
|
} else
|
||||||
|
lcd->power = FB_BLANK_UNBLANK;
|
||||||
|
|
||||||
|
dev_set_drvdata(&spi->dev, lcd);
|
||||||
|
|
||||||
|
dev_info(&spi->dev, "ams369fg06 panel driver has been probed.\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
out_lcd_unregister:
|
||||||
|
lcd_device_unregister(ld);
|
||||||
|
out_free_lcd:
|
||||||
|
kfree(lcd);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __devexit ams369fg06_remove(struct spi_device *spi)
|
||||||
|
{
|
||||||
|
struct ams369fg06 *lcd = dev_get_drvdata(&spi->dev);
|
||||||
|
|
||||||
|
ams369fg06_power(lcd, FB_BLANK_POWERDOWN);
|
||||||
|
backlight_device_unregister(lcd->bd);
|
||||||
|
lcd_device_unregister(lcd->ld);
|
||||||
|
kfree(lcd);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(CONFIG_PM)
|
||||||
|
static unsigned int before_power;
|
||||||
|
|
||||||
|
static int ams369fg06_suspend(struct spi_device *spi, pm_message_t mesg)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
struct ams369fg06 *lcd = dev_get_drvdata(&spi->dev);
|
||||||
|
|
||||||
|
dev_dbg(&spi->dev, "lcd->power = %d\n", lcd->power);
|
||||||
|
|
||||||
|
before_power = lcd->power;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* when lcd panel is suspend, lcd panel becomes off
|
||||||
|
* regardless of status.
|
||||||
|
*/
|
||||||
|
ret = ams369fg06_power(lcd, FB_BLANK_POWERDOWN);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ams369fg06_resume(struct spi_device *spi)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
struct ams369fg06 *lcd = dev_get_drvdata(&spi->dev);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* after suspended, if lcd panel status is FB_BLANK_UNBLANK
|
||||||
|
* (at that time, before_power is FB_BLANK_UNBLANK) then
|
||||||
|
* it changes that status to FB_BLANK_POWERDOWN to get lcd on.
|
||||||
|
*/
|
||||||
|
if (before_power == FB_BLANK_UNBLANK)
|
||||||
|
lcd->power = FB_BLANK_POWERDOWN;
|
||||||
|
|
||||||
|
dev_dbg(&spi->dev, "before_power = %d\n", before_power);
|
||||||
|
|
||||||
|
ret = ams369fg06_power(lcd, before_power);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#define ams369fg06_suspend NULL
|
||||||
|
#define ams369fg06_resume NULL
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void ams369fg06_shutdown(struct spi_device *spi)
|
||||||
|
{
|
||||||
|
struct ams369fg06 *lcd = dev_get_drvdata(&spi->dev);
|
||||||
|
|
||||||
|
ams369fg06_power(lcd, FB_BLANK_POWERDOWN);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct spi_driver ams369fg06_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = "ams369fg06",
|
||||||
|
.bus = &spi_bus_type,
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
},
|
||||||
|
.probe = ams369fg06_probe,
|
||||||
|
.remove = __devexit_p(ams369fg06_remove),
|
||||||
|
.shutdown = ams369fg06_shutdown,
|
||||||
|
.suspend = ams369fg06_suspend,
|
||||||
|
.resume = ams369fg06_resume,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init ams369fg06_init(void)
|
||||||
|
{
|
||||||
|
return spi_register_driver(&ams369fg06_driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit ams369fg06_exit(void)
|
||||||
|
{
|
||||||
|
spi_unregister_driver(&ams369fg06_driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
module_init(ams369fg06_init);
|
||||||
|
module_exit(ams369fg06_exit);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>");
|
||||||
|
MODULE_DESCRIPTION("ams369fg06 LCD Driver");
|
||||||
|
MODULE_LICENSE("GPL");
|
@ -668,6 +668,7 @@ static int ld9040_probe(struct spi_device *spi)
|
|||||||
struct ld9040 *lcd = NULL;
|
struct ld9040 *lcd = NULL;
|
||||||
struct lcd_device *ld = NULL;
|
struct lcd_device *ld = NULL;
|
||||||
struct backlight_device *bd = NULL;
|
struct backlight_device *bd = NULL;
|
||||||
|
struct backlight_properties props;
|
||||||
|
|
||||||
lcd = kzalloc(sizeof(struct ld9040), GFP_KERNEL);
|
lcd = kzalloc(sizeof(struct ld9040), GFP_KERNEL);
|
||||||
if (!lcd)
|
if (!lcd)
|
||||||
@ -699,14 +700,17 @@ static int ld9040_probe(struct spi_device *spi)
|
|||||||
|
|
||||||
lcd->ld = ld;
|
lcd->ld = ld;
|
||||||
|
|
||||||
|
memset(&props, 0, sizeof(struct backlight_properties));
|
||||||
|
props.type = BACKLIGHT_RAW;
|
||||||
|
props.max_brightness = MAX_BRIGHTNESS;
|
||||||
|
|
||||||
bd = backlight_device_register("ld9040-bl", &spi->dev,
|
bd = backlight_device_register("ld9040-bl", &spi->dev,
|
||||||
lcd, &ld9040_backlight_ops, NULL);
|
lcd, &ld9040_backlight_ops, &props);
|
||||||
if (IS_ERR(ld)) {
|
if (IS_ERR(bd)) {
|
||||||
ret = PTR_ERR(ld);
|
ret = PTR_ERR(bd);
|
||||||
goto out_free_lcd;
|
goto out_unregister_lcd;
|
||||||
}
|
}
|
||||||
|
|
||||||
bd->props.max_brightness = MAX_BRIGHTNESS;
|
|
||||||
bd->props.brightness = MAX_BRIGHTNESS;
|
bd->props.brightness = MAX_BRIGHTNESS;
|
||||||
lcd->bd = bd;
|
lcd->bd = bd;
|
||||||
|
|
||||||
@ -731,6 +735,8 @@ static int ld9040_probe(struct spi_device *spi)
|
|||||||
dev_info(&spi->dev, "ld9040 panel driver has been probed.\n");
|
dev_info(&spi->dev, "ld9040 panel driver has been probed.\n");
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
out_unregister_lcd:
|
||||||
|
lcd_device_unregister(lcd->ld);
|
||||||
out_free_lcd:
|
out_free_lcd:
|
||||||
kfree(lcd);
|
kfree(lcd);
|
||||||
return ret;
|
return ret;
|
||||||
@ -741,6 +747,7 @@ static int __devexit ld9040_remove(struct spi_device *spi)
|
|||||||
struct ld9040 *lcd = dev_get_drvdata(&spi->dev);
|
struct ld9040 *lcd = dev_get_drvdata(&spi->dev);
|
||||||
|
|
||||||
ld9040_power(lcd, FB_BLANK_POWERDOWN);
|
ld9040_power(lcd, FB_BLANK_POWERDOWN);
|
||||||
|
backlight_device_unregister(lcd->bd);
|
||||||
lcd_device_unregister(lcd->ld);
|
lcd_device_unregister(lcd->ld);
|
||||||
kfree(lcd);
|
kfree(lcd);
|
||||||
|
|
||||||
|
@ -738,6 +738,7 @@ static int __devinit s6e63m0_probe(struct spi_device *spi)
|
|||||||
struct s6e63m0 *lcd = NULL;
|
struct s6e63m0 *lcd = NULL;
|
||||||
struct lcd_device *ld = NULL;
|
struct lcd_device *ld = NULL;
|
||||||
struct backlight_device *bd = NULL;
|
struct backlight_device *bd = NULL;
|
||||||
|
struct backlight_properties props;
|
||||||
|
|
||||||
lcd = kzalloc(sizeof(struct s6e63m0), GFP_KERNEL);
|
lcd = kzalloc(sizeof(struct s6e63m0), GFP_KERNEL);
|
||||||
if (!lcd)
|
if (!lcd)
|
||||||
@ -769,16 +770,18 @@ static int __devinit s6e63m0_probe(struct spi_device *spi)
|
|||||||
|
|
||||||
lcd->ld = ld;
|
lcd->ld = ld;
|
||||||
|
|
||||||
|
memset(&props, 0, sizeof(struct backlight_properties));
|
||||||
|
props.type = BACKLIGHT_RAW;
|
||||||
|
props.max_brightness = MAX_BRIGHTNESS;
|
||||||
|
|
||||||
bd = backlight_device_register("s6e63m0bl-bl", &spi->dev, lcd,
|
bd = backlight_device_register("s6e63m0bl-bl", &spi->dev, lcd,
|
||||||
&s6e63m0_backlight_ops, NULL);
|
&s6e63m0_backlight_ops, &props);
|
||||||
if (IS_ERR(bd)) {
|
if (IS_ERR(bd)) {
|
||||||
ret = PTR_ERR(bd);
|
ret = PTR_ERR(bd);
|
||||||
goto out_lcd_unregister;
|
goto out_lcd_unregister;
|
||||||
}
|
}
|
||||||
|
|
||||||
bd->props.max_brightness = MAX_BRIGHTNESS;
|
|
||||||
bd->props.brightness = MAX_BRIGHTNESS;
|
bd->props.brightness = MAX_BRIGHTNESS;
|
||||||
bd->props.type = BACKLIGHT_RAW;
|
|
||||||
lcd->bd = bd;
|
lcd->bd = bd;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -840,7 +843,7 @@ static int __devexit s6e63m0_remove(struct spi_device *spi)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if defined(CONFIG_PM)
|
#if defined(CONFIG_PM)
|
||||||
unsigned int before_power;
|
static unsigned int before_power;
|
||||||
|
|
||||||
static int s6e63m0_suspend(struct spi_device *spi, pm_message_t mesg)
|
static int s6e63m0_suspend(struct spi_device *spi, pm_message_t mesg)
|
||||||
{
|
{
|
||||||
|
@ -26,6 +26,36 @@ config XEN_SELFBALLOONING
|
|||||||
kernel boot parameter. Note that systems without a sufficiently
|
kernel boot parameter. Note that systems without a sufficiently
|
||||||
large swap device should not enable self-ballooning.
|
large swap device should not enable self-ballooning.
|
||||||
|
|
||||||
|
config XEN_BALLOON_MEMORY_HOTPLUG
|
||||||
|
bool "Memory hotplug support for Xen balloon driver"
|
||||||
|
default n
|
||||||
|
depends on XEN_BALLOON && MEMORY_HOTPLUG
|
||||||
|
help
|
||||||
|
Memory hotplug support for Xen balloon driver allows expanding memory
|
||||||
|
available for the system above limit declared at system startup.
|
||||||
|
It is very useful on critical systems which require long
|
||||||
|
run without rebooting.
|
||||||
|
|
||||||
|
Memory could be hotplugged in following steps:
|
||||||
|
|
||||||
|
1) dom0: xl mem-max <domU> <maxmem>
|
||||||
|
where <maxmem> is >= requested memory size,
|
||||||
|
|
||||||
|
2) dom0: xl mem-set <domU> <memory>
|
||||||
|
where <memory> is requested memory size; alternatively memory
|
||||||
|
could be added by writing proper value to
|
||||||
|
/sys/devices/system/xen_memory/xen_memory0/target or
|
||||||
|
/sys/devices/system/xen_memory/xen_memory0/target_kb on dumU,
|
||||||
|
|
||||||
|
3) domU: for i in /sys/devices/system/memory/memory*/state; do \
|
||||||
|
[ "`cat "$i"`" = offline ] && echo online > "$i"; done
|
||||||
|
|
||||||
|
Memory could be onlined automatically on domU by adding following line to udev rules:
|
||||||
|
|
||||||
|
SUBSYSTEM=="memory", ACTION=="add", RUN+="/bin/sh -c '[ -f /sys$devpath/state ] && echo online > /sys$devpath/state'"
|
||||||
|
|
||||||
|
In that case step 3 should be omitted.
|
||||||
|
|
||||||
config XEN_SCRUB_PAGES
|
config XEN_SCRUB_PAGES
|
||||||
bool "Scrub pages before returning them to system"
|
bool "Scrub pages before returning them to system"
|
||||||
depends on XEN_BALLOON
|
depends on XEN_BALLOON
|
||||||
|
@ -4,6 +4,12 @@
|
|||||||
* Copyright (c) 2003, B Dragovic
|
* Copyright (c) 2003, B Dragovic
|
||||||
* Copyright (c) 2003-2004, M Williamson, K Fraser
|
* Copyright (c) 2003-2004, M Williamson, K Fraser
|
||||||
* Copyright (c) 2005 Dan M. Smith, IBM Corporation
|
* Copyright (c) 2005 Dan M. Smith, IBM Corporation
|
||||||
|
* Copyright (c) 2010 Daniel Kiper
|
||||||
|
*
|
||||||
|
* Memory hotplug support was written by Daniel Kiper. Work on
|
||||||
|
* it was sponsored by Google under Google Summer of Code 2010
|
||||||
|
* program. Jeremy Fitzhardinge from Citrix was the mentor for
|
||||||
|
* this project.
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License version 2
|
* modify it under the terms of the GNU General Public License version 2
|
||||||
@ -40,6 +46,9 @@
|
|||||||
#include <linux/mutex.h>
|
#include <linux/mutex.h>
|
||||||
#include <linux/list.h>
|
#include <linux/list.h>
|
||||||
#include <linux/gfp.h>
|
#include <linux/gfp.h>
|
||||||
|
#include <linux/notifier.h>
|
||||||
|
#include <linux/memory.h>
|
||||||
|
#include <linux/memory_hotplug.h>
|
||||||
|
|
||||||
#include <asm/page.h>
|
#include <asm/page.h>
|
||||||
#include <asm/pgalloc.h>
|
#include <asm/pgalloc.h>
|
||||||
@ -194,6 +203,87 @@ static enum bp_state update_schedule(enum bp_state state)
|
|||||||
return BP_EAGAIN;
|
return BP_EAGAIN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_XEN_BALLOON_MEMORY_HOTPLUG
|
||||||
|
static long current_credit(void)
|
||||||
|
{
|
||||||
|
return balloon_stats.target_pages - balloon_stats.current_pages -
|
||||||
|
balloon_stats.hotplug_pages;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool balloon_is_inflated(void)
|
||||||
|
{
|
||||||
|
if (balloon_stats.balloon_low || balloon_stats.balloon_high ||
|
||||||
|
balloon_stats.balloon_hotplug)
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* reserve_additional_memory() adds memory region of size >= credit above
|
||||||
|
* max_pfn. New region is section aligned and size is modified to be multiple
|
||||||
|
* of section size. Those features allow optimal use of address space and
|
||||||
|
* establish proper alignment when this function is called first time after
|
||||||
|
* boot (last section not fully populated at boot time contains unused memory
|
||||||
|
* pages with PG_reserved bit not set; online_pages_range() does not allow page
|
||||||
|
* onlining in whole range if first onlined page does not have PG_reserved
|
||||||
|
* bit set). Real size of added memory is established at page onlining stage.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static enum bp_state reserve_additional_memory(long credit)
|
||||||
|
{
|
||||||
|
int nid, rc;
|
||||||
|
u64 hotplug_start_paddr;
|
||||||
|
unsigned long balloon_hotplug = credit;
|
||||||
|
|
||||||
|
hotplug_start_paddr = PFN_PHYS(SECTION_ALIGN_UP(max_pfn));
|
||||||
|
balloon_hotplug = round_up(balloon_hotplug, PAGES_PER_SECTION);
|
||||||
|
nid = memory_add_physaddr_to_nid(hotplug_start_paddr);
|
||||||
|
|
||||||
|
rc = add_memory(nid, hotplug_start_paddr, balloon_hotplug << PAGE_SHIFT);
|
||||||
|
|
||||||
|
if (rc) {
|
||||||
|
pr_info("xen_balloon: %s: add_memory() failed: %i\n", __func__, rc);
|
||||||
|
return BP_EAGAIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
balloon_hotplug -= credit;
|
||||||
|
|
||||||
|
balloon_stats.hotplug_pages += credit;
|
||||||
|
balloon_stats.balloon_hotplug = balloon_hotplug;
|
||||||
|
|
||||||
|
return BP_DONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void xen_online_page(struct page *page)
|
||||||
|
{
|
||||||
|
__online_page_set_limits(page);
|
||||||
|
|
||||||
|
mutex_lock(&balloon_mutex);
|
||||||
|
|
||||||
|
__balloon_append(page);
|
||||||
|
|
||||||
|
if (balloon_stats.hotplug_pages)
|
||||||
|
--balloon_stats.hotplug_pages;
|
||||||
|
else
|
||||||
|
--balloon_stats.balloon_hotplug;
|
||||||
|
|
||||||
|
mutex_unlock(&balloon_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int xen_memory_notifier(struct notifier_block *nb, unsigned long val, void *v)
|
||||||
|
{
|
||||||
|
if (val == MEM_ONLINE)
|
||||||
|
schedule_delayed_work(&balloon_worker, 0);
|
||||||
|
|
||||||
|
return NOTIFY_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct notifier_block xen_memory_nb = {
|
||||||
|
.notifier_call = xen_memory_notifier,
|
||||||
|
.priority = 0
|
||||||
|
};
|
||||||
|
#else
|
||||||
static long current_credit(void)
|
static long current_credit(void)
|
||||||
{
|
{
|
||||||
unsigned long target = balloon_stats.target_pages;
|
unsigned long target = balloon_stats.target_pages;
|
||||||
@ -206,6 +296,21 @@ static long current_credit(void)
|
|||||||
return target - balloon_stats.current_pages;
|
return target - balloon_stats.current_pages;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool balloon_is_inflated(void)
|
||||||
|
{
|
||||||
|
if (balloon_stats.balloon_low || balloon_stats.balloon_high)
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum bp_state reserve_additional_memory(long credit)
|
||||||
|
{
|
||||||
|
balloon_stats.target_pages = balloon_stats.current_pages;
|
||||||
|
return BP_DONE;
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_XEN_BALLOON_MEMORY_HOTPLUG */
|
||||||
|
|
||||||
static enum bp_state increase_reservation(unsigned long nr_pages)
|
static enum bp_state increase_reservation(unsigned long nr_pages)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
@ -217,6 +322,15 @@ static enum bp_state increase_reservation(unsigned long nr_pages)
|
|||||||
.domid = DOMID_SELF
|
.domid = DOMID_SELF
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef CONFIG_XEN_BALLOON_MEMORY_HOTPLUG
|
||||||
|
if (!balloon_stats.balloon_low && !balloon_stats.balloon_high) {
|
||||||
|
nr_pages = min(nr_pages, balloon_stats.balloon_hotplug);
|
||||||
|
balloon_stats.hotplug_pages += nr_pages;
|
||||||
|
balloon_stats.balloon_hotplug -= nr_pages;
|
||||||
|
return BP_DONE;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (nr_pages > ARRAY_SIZE(frame_list))
|
if (nr_pages > ARRAY_SIZE(frame_list))
|
||||||
nr_pages = ARRAY_SIZE(frame_list);
|
nr_pages = ARRAY_SIZE(frame_list);
|
||||||
|
|
||||||
@ -279,6 +393,15 @@ static enum bp_state decrease_reservation(unsigned long nr_pages, gfp_t gfp)
|
|||||||
.domid = DOMID_SELF
|
.domid = DOMID_SELF
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef CONFIG_XEN_BALLOON_MEMORY_HOTPLUG
|
||||||
|
if (balloon_stats.hotplug_pages) {
|
||||||
|
nr_pages = min(nr_pages, balloon_stats.hotplug_pages);
|
||||||
|
balloon_stats.hotplug_pages -= nr_pages;
|
||||||
|
balloon_stats.balloon_hotplug += nr_pages;
|
||||||
|
return BP_DONE;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (nr_pages > ARRAY_SIZE(frame_list))
|
if (nr_pages > ARRAY_SIZE(frame_list))
|
||||||
nr_pages = ARRAY_SIZE(frame_list);
|
nr_pages = ARRAY_SIZE(frame_list);
|
||||||
|
|
||||||
@ -340,8 +463,12 @@ static void balloon_process(struct work_struct *work)
|
|||||||
do {
|
do {
|
||||||
credit = current_credit();
|
credit = current_credit();
|
||||||
|
|
||||||
if (credit > 0)
|
if (credit > 0) {
|
||||||
state = increase_reservation(credit);
|
if (balloon_is_inflated())
|
||||||
|
state = increase_reservation(credit);
|
||||||
|
else
|
||||||
|
state = reserve_additional_memory(credit);
|
||||||
|
}
|
||||||
|
|
||||||
if (credit < 0)
|
if (credit < 0)
|
||||||
state = decrease_reservation(-credit, GFP_BALLOON);
|
state = decrease_reservation(-credit, GFP_BALLOON);
|
||||||
@ -448,6 +575,14 @@ static int __init balloon_init(void)
|
|||||||
balloon_stats.retry_count = 1;
|
balloon_stats.retry_count = 1;
|
||||||
balloon_stats.max_retry_count = RETRY_UNLIMITED;
|
balloon_stats.max_retry_count = RETRY_UNLIMITED;
|
||||||
|
|
||||||
|
#ifdef CONFIG_XEN_BALLOON_MEMORY_HOTPLUG
|
||||||
|
balloon_stats.hotplug_pages = 0;
|
||||||
|
balloon_stats.balloon_hotplug = 0;
|
||||||
|
|
||||||
|
set_online_page_callback(&xen_online_page);
|
||||||
|
register_memory_notifier(&xen_memory_nb);
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialise the balloon with excess memory space. We need
|
* Initialise the balloon with excess memory space. We need
|
||||||
* to make sure we don't add memory which doesn't exist or
|
* to make sure we don't add memory which doesn't exist or
|
||||||
|
@ -94,7 +94,7 @@ static int hugetlbfs_file_mmap(struct file *file, struct vm_area_struct *vma)
|
|||||||
vma->vm_flags |= VM_HUGETLB | VM_RESERVED;
|
vma->vm_flags |= VM_HUGETLB | VM_RESERVED;
|
||||||
vma->vm_ops = &hugetlb_vm_ops;
|
vma->vm_ops = &hugetlb_vm_ops;
|
||||||
|
|
||||||
if (vma->vm_pgoff & ~(huge_page_mask(h) >> PAGE_SHIFT))
|
if (vma->vm_pgoff & (~huge_page_mask(h) >> PAGE_SHIFT))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
vma_len = (loff_t)(vma->vm_end - vma->vm_start);
|
vma_len = (loff_t)(vma->vm_end - vma->vm_start);
|
||||||
|
@ -1118,10 +1118,9 @@ static ssize_t oom_adjust_write(struct file *file, const char __user *buf,
|
|||||||
* Warn that /proc/pid/oom_adj is deprecated, see
|
* Warn that /proc/pid/oom_adj is deprecated, see
|
||||||
* Documentation/feature-removal-schedule.txt.
|
* Documentation/feature-removal-schedule.txt.
|
||||||
*/
|
*/
|
||||||
printk_once(KERN_WARNING "%s (%d): /proc/%d/oom_adj is deprecated, "
|
WARN_ONCE(1, "%s (%d): /proc/%d/oom_adj is deprecated, please use /proc/%d/oom_score_adj instead.\n",
|
||||||
"please use /proc/%d/oom_score_adj instead.\n",
|
current->comm, task_pid_nr(current), task_pid_nr(task),
|
||||||
current->comm, task_pid_nr(current),
|
task_pid_nr(task));
|
||||||
task_pid_nr(task), task_pid_nr(task));
|
|
||||||
task->signal->oom_adj = oom_adjust;
|
task->signal->oom_adj = oom_adjust;
|
||||||
/*
|
/*
|
||||||
* Scale /proc/pid/oom_score_adj appropriately ensuring that a maximum
|
* Scale /proc/pid/oom_score_adj appropriately ensuring that a maximum
|
||||||
|
@ -214,7 +214,7 @@ static int scan_bitmap_block(struct reiserfs_transaction_handle *th,
|
|||||||
}
|
}
|
||||||
/* otherwise we clear all bit were set ... */
|
/* otherwise we clear all bit were set ... */
|
||||||
while (--i >= *beg)
|
while (--i >= *beg)
|
||||||
reiserfs_test_and_clear_le_bit
|
reiserfs_clear_le_bit
|
||||||
(i, bh->b_data);
|
(i, bh->b_data);
|
||||||
reiserfs_restore_prepared_buffer(s, bh);
|
reiserfs_restore_prepared_buffer(s, bh);
|
||||||
*beg = org;
|
*beg = org;
|
||||||
@ -1222,15 +1222,11 @@ void reiserfs_cache_bitmap_metadata(struct super_block *sb,
|
|||||||
info->free_count = 0;
|
info->free_count = 0;
|
||||||
|
|
||||||
while (--cur >= (unsigned long *)bh->b_data) {
|
while (--cur >= (unsigned long *)bh->b_data) {
|
||||||
int i;
|
|
||||||
|
|
||||||
/* 0 and ~0 are special, we can optimize for them */
|
/* 0 and ~0 are special, we can optimize for them */
|
||||||
if (*cur == 0)
|
if (*cur == 0)
|
||||||
info->free_count += BITS_PER_LONG;
|
info->free_count += BITS_PER_LONG;
|
||||||
else if (*cur != ~0L) /* A mix, investigate */
|
else if (*cur != ~0L) /* A mix, investigate */
|
||||||
for (i = BITS_PER_LONG - 1; i >= 0; i--)
|
info->free_count += BITS_PER_LONG - hweight_long(*cur);
|
||||||
if (!reiserfs_test_le_bit(i, cur))
|
|
||||||
info->free_count++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user