forked from luck/tmp_suning_uos_patched
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
|
||||
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.
|
||||
|
||||
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,
|
||||
CONFIG_DEBUG_SLAB, CONFIG_DEBUG_PAGEALLOC, CONFIG_DEBUG_MUTEXES,
|
||||
CONFIG_DEBUG_SPINLOCK, CONFIG_DEBUG_ATOMIC_SLEEP all simultaneously
|
||||
enabled.
|
||||
CONFIG_DEBUG_SPINLOCK, CONFIG_DEBUG_ATOMIC_SLEEP, CONFIG_PROVE_RCU
|
||||
and CONFIG_DEBUG_OBJECTS_RCU_HEAD all simultaneously enabled.
|
||||
|
||||
13: Has been build- and runtime tested with and without CONFIG_SMP and
|
||||
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
|
||||
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.
|
||||
|
||||
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
|
||||
When: 2012
|
||||
Why: superseded by xt_addrtype
|
||||
|
|
|
@ -3012,7 +3012,7 @@ F: kernel/hrtimer.c
|
|||
F: kernel/time/clockevents.c
|
||||
F: kernel/time/tick*.*
|
||||
F: kernel/time/timer_*.c
|
||||
F: include/linux/clockevents.h
|
||||
F: include/linux/clockchips.h
|
||||
F: include/linux/hrtimer.h
|
||||
|
||||
HIGH-SPEED SCC DRIVER FOR AX.25
|
||||
|
|
|
@ -200,7 +200,6 @@ show_regs(struct pt_regs *regs)
|
|||
void
|
||||
start_thread(struct pt_regs * regs, unsigned long pc, unsigned long sp)
|
||||
{
|
||||
set_fs(USER_DS);
|
||||
regs->pc = pc;
|
||||
regs->ps = 8;
|
||||
wrusp(sp);
|
||||
|
|
|
@ -10,16 +10,97 @@
|
|||
#include <linux/amba/bus.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/i2c.h>
|
||||
|
||||
#include <asm/mach/arch.h>
|
||||
#include <asm/mach-types.h>
|
||||
|
||||
#include <plat/pincfg.h>
|
||||
#include <plat/i2c.h>
|
||||
|
||||
#include <mach/hardware.h>
|
||||
#include <mach/devices.h>
|
||||
#include <mach/setup.h>
|
||||
|
||||
#include "pins-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)
|
||||
{
|
||||
db5500_add_uart0(NULL);
|
||||
|
@ -30,7 +111,8 @@ static void __init u5500_uart_init(void)
|
|||
static void __init u5500_init_machine(void)
|
||||
{
|
||||
u5500_init_devices();
|
||||
|
||||
nmk_config_pins(u5500_pins, ARRAY_SIZE(u5500_pins));
|
||||
u5500_i2c_init();
|
||||
u5500_sdi_init();
|
||||
u5500_uart_init();
|
||||
}
|
||||
|
|
|
@ -85,6 +85,8 @@ crisv32_pinmux_alloc_fixed(enum fixed_function function)
|
|||
int ret = -EINVAL;
|
||||
char saved[sizeof pins];
|
||||
unsigned long flags;
|
||||
reg_pinmux_rw_hwprot hwprot;
|
||||
reg_clkgen_rw_clk_ctrl clk_ctrl;
|
||||
|
||||
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 */
|
||||
|
||||
reg_pinmux_rw_hwprot hwprot = REG_RD(pinmux, regi_pinmux, rw_hwprot);
|
||||
reg_clkgen_rw_clk_ctrl clk_ctrl = REG_RD(clkgen, regi_clkgen,
|
||||
rw_clk_ctrl);
|
||||
hwprot = REG_RD(pinmux, regi_pinmux, rw_hwprot);
|
||||
clk_ctrl = REG_RD(clkgen, regi_clkgen, rw_clk_ctrl);
|
||||
|
||||
switch (function) {
|
||||
case pinmux_eth:
|
||||
|
@ -262,6 +263,7 @@ crisv32_pinmux_dealloc_fixed(enum fixed_function function)
|
|||
int ret = -EINVAL;
|
||||
char saved[sizeof pins];
|
||||
unsigned long flags;
|
||||
reg_pinmux_rw_hwprot hwprot;
|
||||
|
||||
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 */
|
||||
|
||||
reg_pinmux_rw_hwprot hwprot = REG_RD(pinmux, regi_pinmux, rw_hwprot);
|
||||
hwprot = REG_RD(pinmux, regi_pinmux, rw_hwprot);
|
||||
|
||||
switch (function) {
|
||||
case pinmux_eth:
|
||||
|
|
|
@ -53,7 +53,6 @@ struct thread_struct {
|
|||
*/
|
||||
|
||||
#define start_thread(regs, ip, usp) do { \
|
||||
set_fs(USER_DS); \
|
||||
regs->irp = ip; \
|
||||
regs->dccr |= 1 << U_DCCR_BITNR; \
|
||||
wrusp(usp); \
|
||||
|
|
|
@ -47,7 +47,6 @@ struct thread_struct {
|
|||
*/
|
||||
#define start_thread(regs, ip, usp) \
|
||||
do { \
|
||||
set_fs(USER_DS); \
|
||||
regs->erp = ip; \
|
||||
regs->ccs |= 1 << (U_CCS_BITNR + CCS_SHIFT); \
|
||||
wrusp(usp); \
|
||||
|
|
|
@ -81,7 +81,6 @@ struct thread_struct {
|
|||
#if defined(__H8300H__)
|
||||
#define start_thread(_regs, _pc, _usp) \
|
||||
do { \
|
||||
set_fs(USER_DS); /* reads from user space */ \
|
||||
(_regs)->pc = (_pc); \
|
||||
(_regs)->ccr = 0x00; /* clear all flags */ \
|
||||
(_regs)->er5 = current->mm->start_data; /* GOT base */ \
|
||||
|
@ -91,7 +90,6 @@ do { \
|
|||
#if defined(__H8300S__)
|
||||
#define start_thread(_regs, _pc, _usp) \
|
||||
do { \
|
||||
set_fs(USER_DS); /* reads from user space */ \
|
||||
(_regs)->pc = (_pc); \
|
||||
(_regs)->ccr = 0x00; /* clear kernel flag */ \
|
||||
(_regs)->exr = 0x78; /* enable all interrupts */ \
|
||||
|
|
|
@ -106,7 +106,6 @@ struct thread_struct {
|
|||
|
||||
#define start_thread(regs, new_pc, new_spu) \
|
||||
do { \
|
||||
set_fs(USER_DS); \
|
||||
regs->psw = (regs->psw | USERPS_BPSW) & 0x0000FFFFUL; \
|
||||
regs->bpc = new_pc; \
|
||||
regs->spu = new_spu; \
|
||||
|
|
|
@ -105,9 +105,6 @@ struct thread_struct {
|
|||
static inline void start_thread(struct pt_regs * regs, unsigned long pc,
|
||||
unsigned long usp)
|
||||
{
|
||||
/* reads from user space */
|
||||
set_fs(USER_DS);
|
||||
|
||||
regs->pc = pc;
|
||||
regs->sr &= ~0x2000;
|
||||
wrusp(usp);
|
||||
|
@ -129,7 +126,6 @@ extern int handle_kernel_fault(struct pt_regs *regs);
|
|||
|
||||
#define start_thread(_regs, _pc, _usp) \
|
||||
do { \
|
||||
set_fs(USER_DS); /* reads from user space */ \
|
||||
(_regs)->pc = (_pc); \
|
||||
((struct switch_stack *)(_regs))[-1].a6 = 0; \
|
||||
reformat(_regs); \
|
||||
|
|
|
@ -185,7 +185,7 @@ EXPORT_SYMBOL(kernel_thread);
|
|||
void flush_thread(void)
|
||||
{
|
||||
unsigned long zero = 0;
|
||||
set_fs(USER_DS);
|
||||
|
||||
current->thread.fs = __USER_DS;
|
||||
if (!FPU_IS_EMU)
|
||||
asm volatile (".chip 68k/68881\n\t"
|
||||
|
|
|
@ -158,7 +158,7 @@ void flush_thread(void)
|
|||
#ifdef CONFIG_FPU
|
||||
unsigned long zero = 0;
|
||||
#endif
|
||||
set_fs(USER_DS);
|
||||
|
||||
current->thread.fs = __USER_DS;
|
||||
#ifdef CONFIG_FPU
|
||||
if (!FPU_IS_EMU)
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include <linux/cpumask.h>
|
||||
#include <linux/memblock.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/reboot.h>
|
||||
|
||||
#include <asm/prom.h>
|
||||
#include <asm/rtas.h>
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <asm/delay.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/rtas.h>
|
||||
|
|
|
@ -41,6 +41,7 @@ config SPARC64
|
|||
select HAVE_FUNCTION_TRACE_MCOUNT_TEST
|
||||
select HAVE_KRETPROBES
|
||||
select HAVE_KPROBES
|
||||
select HAVE_RCU_TABLE_FREE if SMP
|
||||
select HAVE_MEMBLOCK
|
||||
select HAVE_SYSCALL_WRAPPERS
|
||||
select HAVE_DYNAMIC_FTRACE
|
||||
|
@ -81,10 +82,6 @@ config IOMMU_HELPER
|
|||
bool
|
||||
default y if SPARC64
|
||||
|
||||
config QUICKLIST
|
||||
bool
|
||||
default y if SPARC64
|
||||
|
||||
config STACKTRACE_SUPPORT
|
||||
bool
|
||||
default y if SPARC64
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
#include <linux/sched.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/quicklist.h>
|
||||
|
||||
#include <asm/spitfire.h>
|
||||
#include <asm/cpudata.h>
|
||||
|
@ -14,71 +13,114 @@
|
|||
|
||||
/* Page table allocation/freeing. */
|
||||
|
||||
extern struct kmem_cache *pgtable_cache;
|
||||
|
||||
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)
|
||||
{
|
||||
quicklist_free(0, NULL, pgd);
|
||||
kmem_cache_free(pgtable_cache, pgd);
|
||||
}
|
||||
|
||||
#define pud_populate(MM, PUD, PMD) pud_set(PUD, PMD)
|
||||
|
||||
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)
|
||||
{
|
||||
quicklist_free(0, NULL, pmd);
|
||||
kmem_cache_free(pgtable_cache, pmd);
|
||||
}
|
||||
|
||||
static inline pte_t *pte_alloc_one_kernel(struct mm_struct *mm,
|
||||
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,
|
||||
unsigned long address)
|
||||
{
|
||||
struct page *page;
|
||||
void *pg;
|
||||
pte_t *pte;
|
||||
|
||||
pg = quicklist_alloc(0, GFP_KERNEL, NULL);
|
||||
if (!pg)
|
||||
pte = pte_alloc_one_kernel(mm, address);
|
||||
if (!pte)
|
||||
return NULL;
|
||||
page = virt_to_page(pg);
|
||||
page = virt_to_page(pte);
|
||||
pgtable_page_ctor(page);
|
||||
return page;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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(MM,PMD,PTE_PAGE) \
|
||||
pmd_populate_kernel(MM,PMD,page_address(PTE_PAGE))
|
||||
#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)
|
||||
#define __pmd_free_tlb(tlb, pmd, addr) pmd_free((tlb)->mm, pmd)
|
||||
#ifdef CONFIG_SMP
|
||||
|
||||
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 */
|
||||
|
|
|
@ -95,6 +95,10 @@
|
|||
/* PTE bits which are the same in SUN4U and SUN4V format. */
|
||||
#define _PAGE_VALID _AC(0x8000000000000000,UL) /* Valid TTE */
|
||||
#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... */
|
||||
#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_IE_4U _AC(0x0800000000000000,UL) /* Invert Endianness */
|
||||
#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_SZ32MB_4U _AC(0x0001000000000000,UL) /* (Panther) 32MB 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_READ_4V _AC(0x0800000000000000,UL) /* Readable 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_IE_4V _AC(0x0000000000001000,UL) /* Invert Endianness */
|
||||
#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)
|
||||
: "i" (_PAGE_PADDR_4U | _PAGE_MODIFIED_4U | _PAGE_ACCESSED_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 |
|
||||
_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));
|
||||
}
|
||||
|
@ -502,6 +508,7 @@ static inline pte_t pte_mkyoung(pte_t pte)
|
|||
|
||||
static inline pte_t pte_mkspecial(pte_t pte)
|
||||
{
|
||||
pte_val(pte) |= _PAGE_SPECIAL;
|
||||
return pte;
|
||||
}
|
||||
|
||||
|
@ -607,9 +614,9 @@ static inline unsigned long pte_present(pte_t pte)
|
|||
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) \
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
asflags-y := -ansi
|
||||
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 += init_$(BITS).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 const char *tsb_cache_names[8] = {
|
||||
|
@ -253,6 +255,15 @@ void __init pgtable_cache_init(void)
|
|||
{
|
||||
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++) {
|
||||
unsigned long size = 8192 << 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"
|
||||
|
||||
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)
|
||||
{
|
||||
struct list_head *chans = &line->chan_list;
|
||||
struct chan *new, *chan;
|
||||
struct chan *new;
|
||||
char *in, *out;
|
||||
|
||||
if (!list_empty(chans)) {
|
||||
chan = list_entry(chans->next, struct chan, list);
|
||||
free_chan(chans, 0);
|
||||
INIT_LIST_HEAD(chans);
|
||||
}
|
||||
|
|
|
@ -186,7 +186,11 @@ static int absolutize(char *to, int size, char *from)
|
|||
strcat(to, "/");
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ int start_watchdog(int *in_fd_ret, int *out_fd_ret, char *sock)
|
|||
{
|
||||
struct dog_data data;
|
||||
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 *mconsole_args[] = { "/usr/bin/uml_watchdog", "-mconsole", NULL,
|
||||
NULL };
|
||||
|
|
|
@ -176,10 +176,9 @@ void line_flush_buffer(struct tty_struct *tty)
|
|||
{
|
||||
struct line *line = tty->driver_data;
|
||||
unsigned long flags;
|
||||
int err;
|
||||
|
||||
spin_lock_irqsave(&line->lock, flags);
|
||||
err = flush_buffer(line);
|
||||
flush_buffer(line);
|
||||
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;
|
||||
}
|
||||
|
||||
#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,
|
||||
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_change_mtu = uml_net_change_mtu,
|
||||
.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");
|
||||
|
||||
pid = change_tramp(argv, output, output_len);
|
||||
if (pid < 0) return;
|
||||
if (pid < 0) {
|
||||
kfree(output);
|
||||
return;
|
||||
}
|
||||
|
||||
if (output != NULL) {
|
||||
printk("%s", output);
|
||||
|
|
|
@ -102,7 +102,7 @@ static int slip_tramp(char **argv, int fd)
|
|||
"buffer\n");
|
||||
os_kill_process(pid, 1);
|
||||
err = -ENOMEM;
|
||||
goto out_free;
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
close(fds[1]);
|
||||
|
@ -112,7 +112,6 @@ static int slip_tramp(char **argv, int fd)
|
|||
err = helper_wait(pid);
|
||||
close(fds[0]);
|
||||
|
||||
out_free:
|
||||
kfree(output);
|
||||
return err;
|
||||
|
||||
|
|
|
@ -1,20 +1,18 @@
|
|||
#ifndef __UM_DELAY_H
|
||||
#define __UM_DELAY_H
|
||||
|
||||
#define MILLION 1000000
|
||||
|
||||
/* Undefined on purpose */
|
||||
extern void __bad_udelay(void);
|
||||
extern void __bad_ndelay(void);
|
||||
|
||||
extern void __udelay(unsigned long usecs);
|
||||
extern void __ndelay(unsigned long usecs);
|
||||
extern void __delay(unsigned long loops);
|
||||
|
||||
#define udelay(n) ((__builtin_constant_p(n) && (n) > 20000) ? \
|
||||
__bad_udelay() : __udelay(n))
|
||||
|
||||
/* It appears that ndelay is not used at all for UML, and has never been
|
||||
* implemented. */
|
||||
extern void __unimplemented_ndelay(void);
|
||||
#define ndelay(n) __unimplemented_ndelay()
|
||||
#define ndelay(n) ((__builtin_constant_p(n) && (n) > 20000) ? \
|
||||
__bad_ndelay() : __ndelay(n))
|
||||
|
||||
#endif
|
||||
|
|
|
@ -38,7 +38,6 @@ void flush_thread(void)
|
|||
|
||||
void start_thread(struct pt_regs *regs, unsigned long eip, unsigned long esp)
|
||||
{
|
||||
set_fs(USER_DS);
|
||||
PT_REGS_IP(regs) = eip;
|
||||
PT_REGS_SP(regs) = esp;
|
||||
}
|
||||
|
|
|
@ -20,9 +20,8 @@ static void kill_off_processes(void)
|
|||
os_kill_ptraced_process(userspace_pid[0], 1);
|
||||
else {
|
||||
struct task_struct *p;
|
||||
int pid, me;
|
||||
int pid;
|
||||
|
||||
me = os_getpid();
|
||||
for_each_process(p) {
|
||||
if (p->mm == NULL)
|
||||
continue;
|
||||
|
|
|
@ -3,10 +3,12 @@
|
|||
# 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 \
|
||||
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 \
|
||||
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
|
||||
|
|
|
@ -14,16 +14,11 @@
|
|||
#include "mem_user.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;
|
||||
#else
|
||||
typedef Elf64_auxv_t elf_auxv_t;
|
||||
#endif
|
||||
|
||||
/* These are initialized very early in boot and never changed */
|
||||
char * elf_aux_platform;
|
||||
long elf_aux_hwcap;
|
||||
extern long elf_aux_hwcap;
|
||||
unsigned long vsyscall_ehdr;
|
||||
unsigned long vsyscall_end;
|
||||
unsigned long __kernel_vsyscall;
|
||||
|
|
|
@ -28,14 +28,14 @@ static int helper_child(void *arg)
|
|||
{
|
||||
struct helper_data *data = arg;
|
||||
char **argv = data->argv;
|
||||
int err;
|
||||
int err, ret;
|
||||
|
||||
if (data->pre_exec != NULL)
|
||||
(*data->pre_exec)(data->pre_data);
|
||||
err = execvp_noalloc(data->buf, argv[0], argv);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
#define STACKSIZE (8 * 1024 * 1024)
|
||||
#define THREAD_NAME_LEN (256)
|
||||
|
||||
long elf_aux_hwcap;
|
||||
|
||||
static void set_stklim(void)
|
||||
{
|
||||
struct rlimit lim;
|
||||
|
@ -143,7 +145,9 @@ int __init main(int argc, char **argv, char **envp)
|
|||
install_fatal_handler(SIGINT);
|
||||
install_fatal_handler(SIGTERM);
|
||||
|
||||
#ifdef CONFIG_ARCH_REUSE_HOST_VSYSCALL_AREA
|
||||
scan_elf_aux(envp);
|
||||
#endif
|
||||
|
||||
do_uml_initcalls();
|
||||
ret = linux_main(argc, argv);
|
||||
|
|
|
@ -176,7 +176,7 @@ static int __init make_tempfile(const char *template, char **out_tempname,
|
|||
|
||||
find_tempdir();
|
||||
if ((tempdir == NULL) || (strlen(tempdir) >= MAXPATHLEN))
|
||||
return -1;
|
||||
goto out;
|
||||
|
||||
if (template[0] != '/')
|
||||
strcpy(tempname, tempdir);
|
||||
|
@ -191,13 +191,15 @@ static int __init make_tempfile(const char *template, char **out_tempname,
|
|||
}
|
||||
if (do_unlink && (unlink(tempname) < 0)) {
|
||||
perror("unlink");
|
||||
goto out;
|
||||
goto close;
|
||||
}
|
||||
if (out_tempname) {
|
||||
*out_tempname = tempname;
|
||||
} else
|
||||
free(tempname);
|
||||
return fd;
|
||||
close:
|
||||
close(fd);
|
||||
out:
|
||||
free(tempname);
|
||||
return -1;
|
||||
|
|
|
@ -113,3 +113,8 @@ EXPORT_SYMBOL(__stack_smash_handler);
|
|||
|
||||
extern long __guard __attribute__((weak));
|
||||
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 \
|
||||
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
|
||||
|
||||
|
|
|
@ -105,6 +105,8 @@ extern unsigned long __kernel_vsyscall;
|
|||
#define FIXADDR_USER_START VSYSCALL_BASE
|
||||
#define FIXADDR_USER_END VSYSCALL_END
|
||||
|
||||
#define __HAVE_ARCH_GATE_AREA 1
|
||||
|
||||
/*
|
||||
* Architecture-neutral AT_ values in 0-17, leave some room
|
||||
* 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/kernel.h>
|
||||
#include <linux/delay.h>
|
||||
#include <asm/param.h>
|
||||
|
||||
void __delay(unsigned long time)
|
||||
void __delay(unsigned long loops)
|
||||
{
|
||||
/* Stolen from the i386 __loop_delay */
|
||||
int d0;
|
||||
__asm__ __volatile__(
|
||||
"\tjmp 1f\n"
|
||||
asm volatile(
|
||||
"test %0,%0\n"
|
||||
"jz 3f\n"
|
||||
"jmp 1f\n"
|
||||
|
||||
".align 16\n"
|
||||
"1:\tjmp 2f\n"
|
||||
"1: jmp 2f\n"
|
||||
|
||||
".align 16\n"
|
||||
"2:\tdecl %0\n\tjns 2b"
|
||||
:"=&a" (d0)
|
||||
:"0" (time));
|
||||
"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)
|
||||
{
|
||||
int i, n;
|
||||
|
||||
n = (loops_per_jiffy * HZ * usecs) / MILLION;
|
||||
for(i=0;i<n;i++)
|
||||
cpu_relax();
|
||||
__const_udelay(usecs * 0x000010c7); /* 2**32 / 1000000 (rounded up) */
|
||||
}
|
||||
|
||||
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
|
||||
#
|
||||
|
||||
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 \
|
||||
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 \
|
||||
lib/rwsem.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 __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
|
||||
|
|
|
@ -1,30 +1,60 @@
|
|||
/*
|
||||
* Copyright 2003 PathScale, Inc.
|
||||
* Copied from arch/x86_64
|
||||
* Copyright (C) 2011 Richard Weinberger <richrd@nod.at>
|
||||
* 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/kernel.h>
|
||||
#include <linux/delay.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/param.h>
|
||||
|
||||
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++)
|
||||
cpu_relax();
|
||||
".align 16\n"
|
||||
"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)
|
||||
{
|
||||
unsigned long i, n;
|
||||
|
||||
n = (loops_per_jiffy * HZ * usecs) / MILLION;
|
||||
for(i=0;i<n;i++)
|
||||
cpu_relax();
|
||||
__const_udelay(usecs * 0x000010c7); /* 2**32 / 1000000 (rounded up) */
|
||||
}
|
||||
|
||||
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 "asm/page.h"
|
||||
#include "asm/mman.h"
|
||||
|
||||
unsigned long vm_stack_flags = __VM_STACK_FLAGS;
|
||||
unsigned long vm_stack_flags32 = __VM_STACK_FLAGS;
|
||||
unsigned long vm_data_default_flags = __VM_DATA_DEFAULT_FLAGS;
|
||||
unsigned long vm_data_default_flags32 = __VM_DATA_DEFAULT_FLAGS;
|
||||
unsigned long vm_force_exec32 = PROT_EXEC;
|
||||
const char *arch_vma_name(struct vm_area_struct *vma)
|
||||
{
|
||||
if (vma->vm_mm && vma->vm_start == um_vdso_addr)
|
||||
return "[vdso]";
|
||||
|
||||
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
|
||||
#define __VM_FLAGS_X86_64_H
|
||||
|
||||
#define __VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | VM_EXEC | \
|
||||
VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC)
|
||||
#define __VM_STACK_FLAGS (VM_GROWSDOWN | VM_READ | VM_WRITE | \
|
||||
VM_EXEC | VM_MAYREAD | VM_MAYWRITE | \
|
||||
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
|
||||
#define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | VM_EXEC | \
|
||||
VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC)
|
||||
#define VM_STACK_DEFAULT_FLAGS (VM_GROWSDOWN | VM_READ | VM_WRITE | \
|
||||
VM_EXEC | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC)
|
||||
|
||||
#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
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/prefetch.h>
|
||||
#include <asm/types.h>
|
||||
|
||||
#define VERIFY_READ 0
|
||||
|
|
|
@ -147,6 +147,9 @@ int ptrace_setxregs(struct task_struct *child, void __user *uregs)
|
|||
elf_xtregs_t *xtregs = uregs;
|
||||
int ret = 0;
|
||||
|
||||
if (!access_ok(VERIFY_READ, uregs, sizeof(elf_xtregs_t)))
|
||||
return -EFAULT;
|
||||
|
||||
#if XTENSA_HAVE_COPROCESSORS
|
||||
/* Flush all coprocessors before we overwrite them. */
|
||||
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 };
|
||||
|
||||
/* Allocate DWC SATA device */
|
||||
hsdev = kmalloc(sizeof(*hsdev), GFP_KERNEL);
|
||||
hsdev = kzalloc(sizeof(*hsdev), GFP_KERNEL);
|
||||
if (hsdev == NULL) {
|
||||
dev_err(&ofdev->dev, "kmalloc failed for hsdev\n");
|
||||
err = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
memset(hsdev, 0, sizeof(*hsdev));
|
||||
|
||||
/* Ioremap SATA registers */
|
||||
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 timespec ts;
|
||||
__u8 buffer[CN_PROC_MSG_SIZE];
|
||||
struct task_struct *tracer;
|
||||
|
||||
if (atomic_read(&proc_event_num_listeners) < 1)
|
||||
return;
|
||||
|
|
|
@ -45,13 +45,13 @@ static int __init pci_eisa_init(struct pci_dev *pdev,
|
|||
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_CLASS_BRIDGE_EISA << 8, 0xffff00, 0 },
|
||||
{ 0, }
|
||||
};
|
||||
|
||||
static struct pci_driver pci_eisa_driver = {
|
||||
static struct pci_driver __initdata pci_eisa_driver = {
|
||||
.name = "pci_eisa",
|
||||
.id_table = pci_eisa_pci_tbl,
|
||||
.probe = pci_eisa_init,
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <linux/firmware.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/sigma.h>
|
||||
|
||||
/* 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;
|
||||
}
|
||||
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)
|
||||
return -EINVAL;
|
||||
|
||||
entry = kmalloc(sizeof(*entry), GFP_KERNEL);
|
||||
entry = kzalloc(sizeof(*entry), GFP_KERNEL);
|
||||
if (!entry)
|
||||
return -ENOMEM;
|
||||
|
||||
memset(entry, 0, sizeof(*entry));
|
||||
pages = (request->size + PAGE_SIZE - 1) / PAGE_SIZE;
|
||||
DRM_DEBUG("size=%ld pages=%ld\n", request->size, 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) {
|
||||
kfree(entry);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
memset(entry->pagelist, 0, pages * sizeof(*entry->pagelist));
|
||||
|
||||
entry->busaddr = kmalloc(pages * sizeof(*entry->busaddr), GFP_KERNEL);
|
||||
entry->busaddr = kcalloc(pages, sizeof(*entry->busaddr), GFP_KERNEL);
|
||||
if (!entry->busaddr) {
|
||||
kfree(entry->pagelist);
|
||||
kfree(entry);
|
||||
return -ENOMEM;
|
||||
}
|
||||
memset((void *)entry->busaddr, 0, pages * sizeof(*entry->busaddr));
|
||||
|
||||
entry->virtual = drm_vmalloc_dma(pages << PAGE_SHIFT);
|
||||
if (!entry->virtual) {
|
||||
|
|
|
@ -139,7 +139,7 @@ static int init_heap(struct mem_block **heap, int start, int size)
|
|||
if (!blocks)
|
||||
return -ENOMEM;
|
||||
|
||||
*heap = kmalloc(sizeof(**heap), GFP_KERNEL);
|
||||
*heap = kzalloc(sizeof(**heap), GFP_KERNEL);
|
||||
if (!*heap) {
|
||||
kfree(blocks);
|
||||
return -ENOMEM;
|
||||
|
@ -150,7 +150,6 @@ static int init_heap(struct mem_block **heap, int start, int size)
|
|||
blocks->file_priv = NULL;
|
||||
blocks->next = blocks->prev = *heap;
|
||||
|
||||
memset(*heap, 0, sizeof(**heap));
|
||||
(*heap)->file_priv = (struct drm_file *) - 1;
|
||||
(*heap)->next = (*heap)->prev = blocks;
|
||||
return 0;
|
||||
|
|
|
@ -585,11 +585,10 @@ int vmw_overlay_init(struct vmw_private *dev_priv)
|
|||
return -ENOSYS;
|
||||
}
|
||||
|
||||
overlay = kmalloc(sizeof(*overlay), GFP_KERNEL);
|
||||
overlay = kzalloc(sizeof(*overlay), GFP_KERNEL);
|
||||
if (!overlay)
|
||||
return -ENOMEM;
|
||||
|
||||
memset(overlay, 0, sizeof(*overlay));
|
||||
mutex_init(&overlay->mutex);
|
||||
for (i = 0; i < VMW_MAX_NUM_STREAMS; i++) {
|
||||
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->format == SVGA3D_A8R8G8B8) {
|
||||
|
||||
srf->snooper.image = kmalloc(64 * 64 * 4, GFP_KERNEL);
|
||||
/* clear the image */
|
||||
if (srf->snooper.image) {
|
||||
memset(srf->snooper.image, 0x00, 64 * 64 * 4);
|
||||
} else {
|
||||
/* allocate image area and clear it */
|
||||
srf->snooper.image = kzalloc(64 * 64 * 4, GFP_KERNEL);
|
||||
if (!srf->snooper.image) {
|
||||
DRM_ERROR("Failed to allocate cursor_image\n");
|
||||
ret = -ENOMEM;
|
||||
goto out_err1;
|
||||
|
|
|
@ -1171,10 +1171,9 @@ static int vga_arb_open(struct inode *inode, struct file *file)
|
|||
|
||||
pr_debug("%s\n", __func__);
|
||||
|
||||
priv = kmalloc(sizeof(struct vga_arb_private), GFP_KERNEL);
|
||||
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
|
||||
if (priv == NULL)
|
||||
return -ENOMEM;
|
||||
memset(priv, 0, sizeof(*priv));
|
||||
spin_lock_init(&priv->lock);
|
||||
file->private_data = priv;
|
||||
|
||||
|
|
|
@ -365,6 +365,7 @@ config LEDS_NS2
|
|||
config LEDS_NETXBIG
|
||||
tristate "LED support for Big Network series LEDs"
|
||||
depends on MACH_NET2BIG_V2 || MACH_NET5BIG_V2
|
||||
depends on LEDS_CLASS
|
||||
default y
|
||||
help
|
||||
This option enable support for LEDs found on the LaCie 2Big
|
||||
|
|
|
@ -68,17 +68,16 @@
|
|||
#define LM3530_ALS2_IMP_SHIFT (4)
|
||||
|
||||
/* Zone Boundary Register defaults */
|
||||
#define LM3530_DEF_ZB_0 (0x33)
|
||||
#define LM3530_DEF_ZB_1 (0x66)
|
||||
#define LM3530_DEF_ZB_2 (0x99)
|
||||
#define LM3530_DEF_ZB_3 (0xCC)
|
||||
#define LM3530_ALS_ZB_MAX (4)
|
||||
#define LM3530_ALS_WINDOW_mV (1000)
|
||||
#define LM3530_ALS_OFFSET_mV (4)
|
||||
|
||||
/* Zone Target Register defaults */
|
||||
#define LM3530_DEF_ZT_0 (0x19)
|
||||
#define LM3530_DEF_ZT_1 (0x33)
|
||||
#define LM3530_DEF_ZT_0 (0x7F)
|
||||
#define LM3530_DEF_ZT_1 (0x66)
|
||||
#define LM3530_DEF_ZT_2 (0x4C)
|
||||
#define LM3530_DEF_ZT_3 (0x66)
|
||||
#define LM3530_DEF_ZT_4 (0x7F)
|
||||
#define LM3530_DEF_ZT_3 (0x33)
|
||||
#define LM3530_DEF_ZT_4 (0x19)
|
||||
|
||||
struct lm3530_mode_map {
|
||||
const char *mode;
|
||||
|
@ -150,6 +149,8 @@ static int lm3530_init_registers(struct lm3530_data *drvdata)
|
|||
u8 als_imp_sel = 0;
|
||||
u8 brightness;
|
||||
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 i2c_client *client = drvdata->client;
|
||||
|
||||
|
@ -161,6 +162,26 @@ static int lm3530_init_registers(struct lm3530_data *drvdata)
|
|||
gen_config |= (LM3530_ENABLE_I2C);
|
||||
|
||||
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 =
|
||||
(pltfm->als_avrg_time << LM3530_ALS_AVG_TIME_SHIFT) |
|
||||
(LM3530_ENABLE_ALS) |
|
||||
|
@ -169,6 +190,7 @@ static int lm3530_init_registers(struct lm3530_data *drvdata)
|
|||
als_imp_sel =
|
||||
(pltfm->als1_resistor_sel << LM3530_ALS1_IMP_SHIFT) |
|
||||
(pltfm->als2_resistor_sel << LM3530_ALS2_IMP_SHIFT);
|
||||
|
||||
}
|
||||
|
||||
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[4] = als_imp_sel; /* LM3530_ALS_IMP_SELECT */
|
||||
reg_val[5] = brightness; /* LM3530_BRT_CTRL_REG */
|
||||
reg_val[6] = LM3530_DEF_ZB_0; /* LM3530_ALS_ZB0_REG */
|
||||
reg_val[7] = LM3530_DEF_ZB_1; /* LM3530_ALS_ZB1_REG */
|
||||
reg_val[8] = LM3530_DEF_ZB_2; /* LM3530_ALS_ZB2_REG */
|
||||
reg_val[9] = LM3530_DEF_ZB_3; /* LM3530_ALS_ZB3_REG */
|
||||
reg_val[6] = zones[0]; /* LM3530_ALS_ZB0_REG */
|
||||
reg_val[7] = zones[1]; /* LM3530_ALS_ZB1_REG */
|
||||
reg_val[8] = zones[2]; /* LM3530_ALS_ZB2_REG */
|
||||
reg_val[9] = zones[3]; /* LM3530_ALS_ZB3_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[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
|
||||
*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);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(mode, 0644, NULL, lm3530_mode_set);
|
||||
static DEVICE_ATTR(mode, 0644, lm3530_mode_get, lm3530_mode_set);
|
||||
|
||||
static int __devinit lm3530_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
|
|
|
@ -744,7 +744,7 @@ static int __devinit lp5521_probe(struct i2c_client *client,
|
|||
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);
|
||||
int i;
|
||||
|
@ -775,7 +775,7 @@ static struct i2c_driver lp5521_driver = {
|
|||
.name = "lp5521",
|
||||
},
|
||||
.probe = lp5521_probe,
|
||||
.remove = lp5521_remove,
|
||||
.remove = __devexit_p(lp5521_remove),
|
||||
.id_table = lp5521_id,
|
||||
};
|
||||
|
||||
|
|
|
@ -127,17 +127,19 @@ static int __devinit sunfire_led_generic_probe(struct platform_device *pdev,
|
|||
struct led_type *types)
|
||||
{
|
||||
struct sunfire_drvdata *p;
|
||||
int i, err = -EINVAL;
|
||||
int i, err;
|
||||
|
||||
if (pdev->num_resources != 1) {
|
||||
printk(KERN_ERR PFX "Wrong number of resources %d, should be 1\n",
|
||||
pdev->num_resources);
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
p = kzalloc(sizeof(*p), GFP_KERNEL);
|
||||
if (!p) {
|
||||
printk(KERN_ERR PFX "Could not allocate struct sunfire_drvdata\n");
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
@ -160,14 +162,14 @@ static int __devinit sunfire_led_generic_probe(struct platform_device *pdev,
|
|||
|
||||
dev_set_drvdata(&pdev->dev, p);
|
||||
|
||||
err = 0;
|
||||
out:
|
||||
return err;
|
||||
return 0;
|
||||
|
||||
out_unregister_led_cdevs:
|
||||
for (i--; i >= 0; i--)
|
||||
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)
|
||||
|
|
|
@ -245,8 +245,7 @@ config SGI_XP
|
|||
|
||||
config CS5535_MFGPT
|
||||
tristate "CS5535/CS5536 Geode Multi-Function General Purpose Timer (MFGPT) support"
|
||||
depends on PCI
|
||||
depends on X86
|
||||
depends on PCI && X86 && MFD_CS5535
|
||||
default n
|
||||
help
|
||||
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
|
||||
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/eeprom/Kconfig"
|
||||
source "drivers/misc/cb710/Kconfig"
|
||||
|
|
|
@ -46,3 +46,4 @@ obj-y += ti-st/
|
|||
obj-$(CONFIG_AB8500_PWM) += ab8500-pwm.o
|
||||
obj-y += lis3lv02d/
|
||||
obj-y += carma/
|
||||
obj-$(CONFIG_USB_SWITCH_FSA9480) += fsa9480.o
|
||||
|
|
|
@ -70,4 +70,29 @@ config EEPROM_93CX6
|
|||
|
||||
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
|
||||
|
|
|
@ -3,3 +3,5 @@ obj-$(CONFIG_EEPROM_AT25) += at25.o
|
|||
obj-$(CONFIG_EEPROM_LEGACY) += eeprom.o
|
||||
obj-$(CONFIG_EEPROM_MAX6875) += max6875.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 */
|
||||
const char *board_name;
|
||||
|
||||
retval = sysfs_create_file(&pdev->dev.kobj,
|
||||
&dev_attr_pch_mac.attr);
|
||||
if (retval)
|
||||
|
@ -701,7 +703,8 @@ static int __devinit pch_phub_probe(struct pci_dev *pdev,
|
|||
CLKCFG_CANCLK_MASK);
|
||||
|
||||
/* 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,
|
||||
(unsigned int)CLKCFG_REG_OFFSET,
|
||||
CLKCFG_UART_48MHZ | CLKCFG_BAUDDIV |
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <linux/log2.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/suspend.h>
|
||||
|
||||
#include <linux/mmc/card.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)
|
||||
{
|
||||
int i;
|
||||
unsigned char map = 0, flags;
|
||||
unsigned char map = 0, flags = 0;
|
||||
|
||||
if (p->channel_count == 0)
|
||||
return;
|
||||
flags |= IORESOURCE_DISABLED;
|
||||
|
||||
for (i = 0; i < p->channel_count; 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);
|
||||
}
|
||||
|
||||
|
@ -527,17 +527,17 @@ static __init void pnpacpi_parse_irq_option(struct pnp_dev *dev,
|
|||
{
|
||||
int i;
|
||||
pnp_irq_mask_t map;
|
||||
unsigned char flags;
|
||||
unsigned char flags = 0;
|
||||
|
||||
if (p->interrupt_count == 0)
|
||||
return;
|
||||
flags |= IORESOURCE_DISABLED;
|
||||
|
||||
bitmap_zero(map.bits, PNP_IRQ_NR);
|
||||
for (i = 0; i < p->interrupt_count; i++)
|
||||
if (p->interrupts[i])
|
||||
__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);
|
||||
}
|
||||
|
||||
|
@ -547,10 +547,10 @@ static __init void pnpacpi_parse_ext_irq_option(struct pnp_dev *dev,
|
|||
{
|
||||
int i;
|
||||
pnp_irq_mask_t map;
|
||||
unsigned char flags;
|
||||
unsigned char flags = 0;
|
||||
|
||||
if (p->interrupt_count == 0)
|
||||
return;
|
||||
flags |= IORESOURCE_DISABLED;
|
||||
|
||||
bitmap_zero(map.bits, PNP_IRQ_NR);
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -575,10 +575,10 @@ static __init void pnpacpi_parse_port_option(struct pnp_dev *dev,
|
|||
unsigned char flags = 0;
|
||||
|
||||
if (io->address_length == 0)
|
||||
return;
|
||||
flags |= IORESOURCE_DISABLED;
|
||||
|
||||
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,
|
||||
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,
|
||||
struct acpi_resource_fixed_io *io)
|
||||
{
|
||||
unsigned char flags = 0;
|
||||
|
||||
if (io->address_length == 0)
|
||||
return;
|
||||
flags |= IORESOURCE_DISABLED;
|
||||
|
||||
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,
|
||||
|
@ -601,10 +603,10 @@ static __init void pnpacpi_parse_mem24_option(struct pnp_dev *dev,
|
|||
unsigned char flags = 0;
|
||||
|
||||
if (p->address_length == 0)
|
||||
return;
|
||||
flags |= IORESOURCE_DISABLED;
|
||||
|
||||
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,
|
||||
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;
|
||||
|
||||
if (p->address_length == 0)
|
||||
return;
|
||||
flags |= IORESOURCE_DISABLED;
|
||||
|
||||
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,
|
||||
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;
|
||||
|
||||
if (p->address_length == 0)
|
||||
return;
|
||||
flags |= IORESOURCE_DISABLED;
|
||||
|
||||
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,
|
||||
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)
|
||||
return;
|
||||
flags |= IORESOURCE_DISABLED;
|
||||
|
||||
if (p->resource_type == ACPI_MEMORY_RANGE) {
|
||||
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,
|
||||
p->minimum, 0, p->address_length,
|
||||
flags);
|
||||
} else if (p->resource_type == ACPI_IO_RANGE)
|
||||
pnp_register_port_resource(dev, option_flags, p->minimum,
|
||||
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,
|
||||
|
@ -677,18 +679,18 @@ static __init void pnpacpi_parse_ext_address_option(struct pnp_dev *dev,
|
|||
unsigned char flags = 0;
|
||||
|
||||
if (p->address_length == 0)
|
||||
return;
|
||||
flags |= IORESOURCE_DISABLED;
|
||||
|
||||
if (p->resource_type == ACPI_MEMORY_RANGE) {
|
||||
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,
|
||||
p->minimum, 0, p->address_length,
|
||||
flags);
|
||||
} else if (p->resource_type == ACPI_IO_RANGE)
|
||||
pnp_register_port_resource(dev, option_flags, p->minimum,
|
||||
p->minimum, 0, p->address_length,
|
||||
IORESOURCE_IO_FIXED);
|
||||
flags | IORESOURCE_IO_FIXED);
|
||||
}
|
||||
|
||||
struct acpipnp_parse_option_s {
|
||||
|
|
|
@ -1006,10 +1006,10 @@ config RTC_DRV_MC13XXX
|
|||
|
||||
config RTC_DRV_MPC5121
|
||||
tristate "Freescale MPC5121 built-in RTC"
|
||||
depends on PPC_MPC512x && RTC_CLASS
|
||||
depends on PPC_MPC512x || PPC_MPC52xx
|
||||
help
|
||||
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
|
||||
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
|
||||
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
|
||||
tristate "NVIDIA Tegra Internal RTC driver"
|
||||
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_PL030) += rtc-pl030.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_PUV3) += rtc-puv3.o
|
||||
obj-$(CONFIG_RTC_DRV_PXA) += rtc-pxa.o
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
*
|
||||
* Copyright 2007, Domen Puncer <domen.puncer@telargo.com>
|
||||
* 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
|
||||
* 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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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,
|
||||
};
|
||||
|
||||
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)
|
||||
{
|
||||
struct mpc5121_rtc_data *rtc;
|
||||
int err = 0;
|
||||
u32 ka;
|
||||
|
||||
rtc = kzalloc(sizeof(*rtc), GFP_KERNEL);
|
||||
if (!rtc)
|
||||
|
@ -287,15 +344,22 @@ static int __devinit mpc5121_rtc_probe(struct platform_device *op)
|
|||
goto out_dispose2;
|
||||
}
|
||||
|
||||
ka = in_be32(&rtc->regs->keep_alive);
|
||||
if (ka & 0x02) {
|
||||
dev_warn(&op->dev,
|
||||
"mpc5121-rtc: Battery or oscillator failure!\n");
|
||||
out_be32(&rtc->regs->keep_alive, ka);
|
||||
if (of_device_is_compatible(op->dev.of_node, "fsl,mpc5121-rtc")) {
|
||||
u32 ka;
|
||||
ka = in_be32(&rtc->regs->keep_alive);
|
||||
if (ka & 0x02) {
|
||||
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)) {
|
||||
err = PTR_ERR(rtc->rtc);
|
||||
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 = {
|
||||
{ .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;
|
||||
|
||||
clk_enable(rtc_clk);
|
||||
rtc_update_irq(rdev, 1, RTC_AF | RTC_IRQF);
|
||||
|
||||
if (s3c_rtc_cpu_type == TYPE_S3C64XX)
|
||||
writeb(S3C2410_INTP_ALM, s3c_rtc_base + S3C2410_INTP);
|
||||
|
||||
clk_disable(rtc_clk);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
@ -69,11 +71,13 @@ static irqreturn_t s3c_rtc_tickirq(int irq, void *id)
|
|||
{
|
||||
struct rtc_device *rdev = id;
|
||||
|
||||
clk_enable(rtc_clk);
|
||||
rtc_update_irq(rdev, 1, RTC_PF | RTC_IRQF);
|
||||
|
||||
if (s3c_rtc_cpu_type == TYPE_S3C64XX)
|
||||
writeb(S3C2410_INTP_TIC, s3c_rtc_base + S3C2410_INTP);
|
||||
|
||||
clk_disable(rtc_clk);
|
||||
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);
|
||||
|
||||
clk_enable(rtc_clk);
|
||||
tmp = readb(s3c_rtc_base + S3C2410_RTCALM) & ~S3C2410_RTCALM_ALMEN;
|
||||
|
||||
if (enabled)
|
||||
tmp |= S3C2410_RTCALM_ALMEN;
|
||||
|
||||
writeb(tmp, s3c_rtc_base + S3C2410_RTCALM);
|
||||
clk_disable(rtc_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -103,6 +109,7 @@ static int s3c_rtc_setfreq(struct device *dev, int freq)
|
|||
if (!is_power_of_2(freq))
|
||||
return -EINVAL;
|
||||
|
||||
clk_enable(rtc_clk);
|
||||
spin_lock_irq(&s3c_rtc_pie_lock);
|
||||
|
||||
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);
|
||||
spin_unlock_irq(&s3c_rtc_pie_lock);
|
||||
clk_disable(rtc_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -125,6 +133,7 @@ static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
|
|||
unsigned int have_retried = 0;
|
||||
void __iomem *base = s3c_rtc_base;
|
||||
|
||||
clk_enable(rtc_clk);
|
||||
retry_get_time:
|
||||
rtc_tm->tm_min = readb(base + S3C2410_RTCMIN);
|
||||
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_mon -= 1;
|
||||
|
||||
clk_disable(rtc_clk);
|
||||
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;
|
||||
int year = tm->tm_year - 100;
|
||||
|
||||
clk_enable(rtc_clk);
|
||||
pr_debug("set time %04d.%02d.%02d %02d:%02d:%02d\n",
|
||||
1900 + tm->tm_year, tm->tm_mon, tm->tm_mday,
|
||||
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_mon + 1), base + S3C2410_RTCMON);
|
||||
writeb(bin2bcd(year), base + S3C2410_RTCYEAR);
|
||||
clk_disable(rtc_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -192,6 +204,7 @@ static int s3c_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm)
|
|||
void __iomem *base = s3c_rtc_base;
|
||||
unsigned int alm_en;
|
||||
|
||||
clk_enable(rtc_clk);
|
||||
alm_tm->tm_sec = readb(base + S3C2410_ALMSEC);
|
||||
alm_tm->tm_min = readb(base + S3C2410_ALMMIN);
|
||||
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
|
||||
alm_tm->tm_year = -1;
|
||||
|
||||
clk_disable(rtc_clk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -252,6 +266,7 @@ static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
|
|||
void __iomem *base = s3c_rtc_base;
|
||||
unsigned int alrm_en;
|
||||
|
||||
clk_enable(rtc_clk);
|
||||
pr_debug("s3c_rtc_setalarm: %d, %04d.%02d.%02d %02d:%02d:%02d\n",
|
||||
alrm->enabled,
|
||||
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);
|
||||
|
||||
clk_disable(rtc_clk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -289,6 +305,7 @@ static int s3c_rtc_proc(struct device *dev, struct seq_file *seq)
|
|||
{
|
||||
unsigned int ticnt;
|
||||
|
||||
clk_enable(rtc_clk);
|
||||
if (s3c_rtc_cpu_type == TYPE_S3C64XX) {
|
||||
ticnt = readw(s3c_rtc_base + S3C2410_RTCCON);
|
||||
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");
|
||||
clk_disable(rtc_clk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -360,6 +378,7 @@ static void s3c_rtc_enable(struct platform_device *pdev, int en)
|
|||
if (s3c_rtc_base == NULL)
|
||||
return;
|
||||
|
||||
clk_enable(rtc_clk);
|
||||
if (!en) {
|
||||
tmp = readw(base + S3C2410_RTCCON);
|
||||
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);
|
||||
}
|
||||
}
|
||||
clk_disable(rtc_clk);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
clk_disable(rtc_clk);
|
||||
clk_put(rtc_clk);
|
||||
rtc_clk = NULL;
|
||||
|
||||
|
@ -529,6 +548,8 @@ static int __devinit s3c_rtc_probe(struct platform_device *pdev)
|
|||
|
||||
s3c_rtc_setfreq(&pdev->dev, 1);
|
||||
|
||||
clk_disable(rtc_clk);
|
||||
|
||||
return 0;
|
||||
|
||||
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)
|
||||
{
|
||||
clk_enable(rtc_clk);
|
||||
/* save TICNT for anyone using periodic interrupts */
|
||||
ticnt_save = readb(s3c_rtc_base + S3C2410_TICNT);
|
||||
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
|
||||
dev_err(&pdev->dev, "enable_irq_wake failed\n");
|
||||
}
|
||||
clk_disable(rtc_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -576,6 +599,7 @@ static int s3c_rtc_resume(struct platform_device *pdev)
|
|||
{
|
||||
unsigned int tmp;
|
||||
|
||||
clk_enable(rtc_clk);
|
||||
s3c_rtc_enable(pdev, 1);
|
||||
writeb(ticnt_save, s3c_rtc_base + S3C2410_TICNT);
|
||||
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);
|
||||
wake_en = false;
|
||||
}
|
||||
clk_disable(rtc_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -343,7 +343,7 @@ static int __devinit tegra_rtc_probe(struct platform_device *pdev)
|
|||
|
||||
/* set context info. */
|
||||
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);
|
||||
|
||||
|
|
|
@ -275,7 +275,7 @@ static int twl_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
|||
goto out;
|
||||
|
||||
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)
|
||||
goto out;
|
||||
|
||||
|
|
|
@ -117,6 +117,14 @@ config LCD_LD9040
|
|||
If you have an LD9040 Panel, say Y to enable its
|
||||
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
|
||||
|
||||
#
|
||||
|
|
|
@ -13,6 +13,7 @@ obj-$(CONFIG_LCD_TDO24M) += tdo24m.o
|
|||
obj-$(CONFIG_LCD_TOSA) += tosa_lcd.o
|
||||
obj-$(CONFIG_LCD_S6E63M0) += s6e63m0.o
|
||||
obj-$(CONFIG_LCD_LD9040) += ld9040.o
|
||||
obj-$(CONFIG_LCD_AMS369FG06) += ams369fg06.o
|
||||
|
||||
obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) += backlight.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;
|
||||
}
|
||||
|
||||
bl->props.max_brightness =
|
||||
bl->props.brightness = ADP8860_MAX_BRIGHTNESS;
|
||||
bl->props.brightness = ADP8860_MAX_BRIGHTNESS;
|
||||
|
||||
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 lcd_device *ld = NULL;
|
||||
struct backlight_device *bd = NULL;
|
||||
struct backlight_properties props;
|
||||
|
||||
lcd = kzalloc(sizeof(struct ld9040), GFP_KERNEL);
|
||||
if (!lcd)
|
||||
|
@ -699,14 +700,17 @@ static int ld9040_probe(struct spi_device *spi)
|
|||
|
||||
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,
|
||||
lcd, &ld9040_backlight_ops, NULL);
|
||||
if (IS_ERR(ld)) {
|
||||
ret = PTR_ERR(ld);
|
||||
goto out_free_lcd;
|
||||
lcd, &ld9040_backlight_ops, &props);
|
||||
if (IS_ERR(bd)) {
|
||||
ret = PTR_ERR(bd);
|
||||
goto out_unregister_lcd;
|
||||
}
|
||||
|
||||
bd->props.max_brightness = MAX_BRIGHTNESS;
|
||||
bd->props.brightness = MAX_BRIGHTNESS;
|
||||
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");
|
||||
return 0;
|
||||
|
||||
out_unregister_lcd:
|
||||
lcd_device_unregister(lcd->ld);
|
||||
out_free_lcd:
|
||||
kfree(lcd);
|
||||
return ret;
|
||||
|
@ -741,6 +747,7 @@ static int __devexit ld9040_remove(struct spi_device *spi)
|
|||
struct ld9040 *lcd = dev_get_drvdata(&spi->dev);
|
||||
|
||||
ld9040_power(lcd, FB_BLANK_POWERDOWN);
|
||||
backlight_device_unregister(lcd->bd);
|
||||
lcd_device_unregister(lcd->ld);
|
||||
kfree(lcd);
|
||||
|
||||
|
|
|
@ -738,6 +738,7 @@ static int __devinit s6e63m0_probe(struct spi_device *spi)
|
|||
struct s6e63m0 *lcd = NULL;
|
||||
struct lcd_device *ld = NULL;
|
||||
struct backlight_device *bd = NULL;
|
||||
struct backlight_properties props;
|
||||
|
||||
lcd = kzalloc(sizeof(struct s6e63m0), GFP_KERNEL);
|
||||
if (!lcd)
|
||||
|
@ -769,16 +770,18 @@ static int __devinit s6e63m0_probe(struct spi_device *spi)
|
|||
|
||||
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,
|
||||
&s6e63m0_backlight_ops, NULL);
|
||||
&s6e63m0_backlight_ops, &props);
|
||||
if (IS_ERR(bd)) {
|
||||
ret = PTR_ERR(bd);
|
||||
goto out_lcd_unregister;
|
||||
}
|
||||
|
||||
bd->props.max_brightness = MAX_BRIGHTNESS;
|
||||
bd->props.brightness = MAX_BRIGHTNESS;
|
||||
bd->props.type = BACKLIGHT_RAW;
|
||||
lcd->bd = bd;
|
||||
|
||||
/*
|
||||
|
@ -840,7 +843,7 @@ static int __devexit s6e63m0_remove(struct spi_device *spi)
|
|||
}
|
||||
|
||||
#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)
|
||||
{
|
||||
|
|
|
@ -26,6 +26,36 @@ config XEN_SELFBALLOONING
|
|||
kernel boot parameter. Note that systems without a sufficiently
|
||||
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
|
||||
bool "Scrub pages before returning them to system"
|
||||
depends on XEN_BALLOON
|
||||
|
|
|
@ -4,6 +4,12 @@
|
|||
* Copyright (c) 2003, B Dragovic
|
||||
* Copyright (c) 2003-2004, M Williamson, K Fraser
|
||||
* 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
|
||||
* modify it under the terms of the GNU General Public License version 2
|
||||
|
@ -40,6 +46,9 @@
|
|||
#include <linux/mutex.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/memory.h>
|
||||
#include <linux/memory_hotplug.h>
|
||||
|
||||
#include <asm/page.h>
|
||||
#include <asm/pgalloc.h>
|
||||
|
@ -194,6 +203,87 @@ static enum bp_state update_schedule(enum bp_state state)
|
|||
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)
|
||||
{
|
||||
unsigned long target = balloon_stats.target_pages;
|
||||
|
@ -206,6 +296,21 @@ static long current_credit(void)
|
|||
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)
|
||||
{
|
||||
int rc;
|
||||
|
@ -217,6 +322,15 @@ static enum bp_state increase_reservation(unsigned long nr_pages)
|
|||
.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))
|
||||
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
|
||||
};
|
||||
|
||||
#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))
|
||||
nr_pages = ARRAY_SIZE(frame_list);
|
||||
|
||||
|
@ -340,8 +463,12 @@ static void balloon_process(struct work_struct *work)
|
|||
do {
|
||||
credit = current_credit();
|
||||
|
||||
if (credit > 0)
|
||||
state = increase_reservation(credit);
|
||||
if (credit > 0) {
|
||||
if (balloon_is_inflated())
|
||||
state = increase_reservation(credit);
|
||||
else
|
||||
state = reserve_additional_memory(credit);
|
||||
}
|
||||
|
||||
if (credit < 0)
|
||||
state = decrease_reservation(-credit, GFP_BALLOON);
|
||||
|
@ -448,6 +575,14 @@ static int __init balloon_init(void)
|
|||
balloon_stats.retry_count = 1;
|
||||
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
|
||||
* 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_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;
|
||||
|
||||
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
|
||||
* Documentation/feature-removal-schedule.txt.
|
||||
*/
|
||||
printk_once(KERN_WARNING "%s (%d): /proc/%d/oom_adj is deprecated, "
|
||||
"please use /proc/%d/oom_score_adj instead.\n",
|
||||
current->comm, task_pid_nr(current),
|
||||
task_pid_nr(task), task_pid_nr(task));
|
||||
WARN_ONCE(1, "%s (%d): /proc/%d/oom_adj is deprecated, please use /proc/%d/oom_score_adj instead.\n",
|
||||
current->comm, task_pid_nr(current), task_pid_nr(task),
|
||||
task_pid_nr(task));
|
||||
task->signal->oom_adj = oom_adjust;
|
||||
/*
|
||||
* 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 ... */
|
||||
while (--i >= *beg)
|
||||
reiserfs_test_and_clear_le_bit
|
||||
reiserfs_clear_le_bit
|
||||
(i, bh->b_data);
|
||||
reiserfs_restore_prepared_buffer(s, bh);
|
||||
*beg = org;
|
||||
|
@ -1222,15 +1222,11 @@ void reiserfs_cache_bitmap_metadata(struct super_block *sb,
|
|||
info->free_count = 0;
|
||||
|
||||
while (--cur >= (unsigned long *)bh->b_data) {
|
||||
int i;
|
||||
|
||||
/* 0 and ~0 are special, we can optimize for them */
|
||||
if (*cur == 0)
|
||||
info->free_count += BITS_PER_LONG;
|
||||
else if (*cur != ~0L) /* A mix, investigate */
|
||||
for (i = BITS_PER_LONG - 1; i >= 0; i--)
|
||||
if (!reiserfs_test_le_bit(i, cur))
|
||||
info->free_count++;
|
||||
info->free_count += BITS_PER_LONG - hweight_long(*cur);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user