From 9321d526dd731588f09508af48de50041785a26a Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Thu, 4 Oct 2012 17:11:11 -0700 Subject: audit.h: replace defines with C stubs Replace the #defines used when CONFIG_AUDIT or CONFIG_AUDIT_SYSCALLS are disabled so we get type checking during those builds. Suggested-by: Andrew Morton Signed-off-by: Kees Cook Cc: Al Viro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/audit.h | 206 +++++++++++++++++++++++++++++++++++++------------- 1 file changed, 153 insertions(+), 53 deletions(-) (limited to 'include/linux') diff --git a/include/linux/audit.h b/include/linux/audit.h index e7c836d961e..2c83e5f7edb 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -527,9 +527,18 @@ static inline void audit_ptrace(struct task_struct *t) extern unsigned int audit_serial(void); extern int auditsc_get_stamp(struct audit_context *ctx, struct timespec *t, unsigned int *serial); -extern int audit_set_loginuid(kuid_t loginuid); -#define audit_get_loginuid(t) ((t)->loginuid) -#define audit_get_sessionid(t) ((t)->sessionid) +extern int audit_set_loginuid(kuid_t loginuid); + +static inline kuid_t audit_get_loginuid(struct task_struct *tsk) +{ + return tsk->loginuid; +} + +static inline int audit_get_sessionid(struct task_struct *tsk) +{ + return tsk->sessionid; +} + extern void audit_log_task_context(struct audit_buffer *ab); extern void audit_log_task_info(struct audit_buffer *ab, struct task_struct *tsk); extern void __audit_ipc_obj(struct kern_ipc_perm *ipcp); @@ -626,38 +635,101 @@ static inline void audit_mmap_fd(int fd, int flags) extern int audit_n_rules; extern int audit_signals; #else /* CONFIG_AUDITSYSCALL */ -#define audit_alloc(t) ({ 0; }) -#define audit_free(t) do { ; } while (0) -#define audit_syscall_entry(ta,a,b,c,d,e) do { ; } while (0) -#define audit_syscall_exit(r) do { ; } while (0) -#define audit_dummy_context() 1 -#define audit_getname(n) do { ; } while (0) -#define audit_putname(n) do { ; } while (0) -#define __audit_inode(n,d) do { ; } while (0) -#define __audit_inode_child(i,p) do { ; } while (0) -#define audit_inode(n,d) do { (void)(d); } while (0) -#define audit_inode_child(i,p) do { ; } while (0) -#define audit_core_dumps(i) do { ; } while (0) -#define audit_seccomp(i,s,c) do { ; } while (0) -#define auditsc_get_stamp(c,t,s) (0) -#define audit_get_loginuid(t) (INVALID_UID) -#define audit_get_sessionid(t) (-1) -#define audit_log_task_context(b) do { ; } while (0) -#define audit_log_task_info(b, t) do { ; } while (0) -#define audit_ipc_obj(i) ((void)0) -#define audit_ipc_set_perm(q,u,g,m) ((void)0) -#define audit_bprm(p) ({ 0; }) -#define audit_socketcall(n,a) ((void)0) -#define audit_fd_pair(n,a) ((void)0) -#define audit_sockaddr(len, addr) ({ 0; }) -#define audit_mq_open(o,m,a) ((void)0) -#define audit_mq_sendrecv(d,l,p,t) ((void)0) -#define audit_mq_notify(d,n) ((void)0) -#define audit_mq_getsetattr(d,s) ((void)0) -#define audit_log_bprm_fcaps(b, ncr, ocr) ({ 0; }) -#define audit_log_capset(pid, ncr, ocr) ((void)0) -#define audit_mmap_fd(fd, flags) ((void)0) -#define audit_ptrace(t) ((void)0) +static inline int audit_alloc(struct task_struct *task) +{ + return 0; +} +static inline void audit_free(struct task_struct *task) +{ } +static inline void audit_syscall_entry(int arch, int major, unsigned long a0, + unsigned long a1, unsigned long a2, + unsigned long a3) +{ } +static inline void audit_syscall_exit(void *pt_regs) +{ } +static inline int audit_dummy_context(void) +{ + return 1; +} +static inline void audit_getname(const char *name) +{ } +static inline void audit_putname(const char *name) +{ } +static inline void __audit_inode(const char *name, const struct dentry *dentry) +{ } +static inline void __audit_inode_child(const struct dentry *dentry, + const struct inode *parent) +{ } +static inline void audit_inode(const char *name, const struct dentry *dentry) +{ } +static inline void audit_inode_child(const struct dentry *dentry, + const struct inode *parent) +{ } +static inline void audit_core_dumps(long signr) +{ } +static inline void __audit_seccomp(unsigned long syscall, long signr, int code) +{ } +static inline void audit_seccomp(unsigned long syscall, long signr, int code) +{ } +static inline int auditsc_get_stamp(struct audit_context *ctx, + struct timespec *t, unsigned int *serial) +{ + return 0; +} +static inline kuid_t audit_get_loginuid(struct task_struct *tsk) +{ + return INVALID_UID; +} +static inline int audit_get_sessionid(struct task_struct *tsk) +{ + return -1; +} +static inline void audit_log_task_context(struct audit_buffer *ab) +{ } +static inline void audit_log_task_info(struct audit_buffer *ab, + struct task_struct *tsk) +{ } +static inline void audit_ipc_obj(struct kern_ipc_perm *ipcp) +{ } +static inline void audit_ipc_set_perm(unsigned long qbytes, uid_t uid, + gid_t gid, umode_t mode) +{ } +static inline int audit_bprm(struct linux_binprm *bprm) +{ + return 0; +} +static inline void audit_socketcall(int nargs, unsigned long *args) +{ } +static inline void audit_fd_pair(int fd1, int fd2) +{ } +static inline int audit_sockaddr(int len, void *addr) +{ + return 0; +} +static inline void audit_mq_open(int oflag, umode_t mode, struct mq_attr *attr) +{ } +static inline void audit_mq_sendrecv(mqd_t mqdes, size_t msg_len, + unsigned int msg_prio, + const struct timespec *abs_timeout) +{ } +static inline void audit_mq_notify(mqd_t mqdes, + const struct sigevent *notification) +{ } +static inline void audit_mq_getsetattr(mqd_t mqdes, struct mq_attr *mqstat) +{ } +static inline int audit_log_bprm_fcaps(struct linux_binprm *bprm, + const struct cred *new, + const struct cred *old) +{ + return 0; +} +static inline void audit_log_capset(pid_t pid, const struct cred *new, + const struct cred *old) +{ } +static inline void audit_mmap_fd(int fd, int flags) +{ } +static inline void audit_ptrace(struct task_struct *t) +{ } #define audit_n_rules 0 #define audit_signals 0 #endif /* CONFIG_AUDITSYSCALL */ @@ -681,7 +753,6 @@ extern void audit_log_n_hex(struct audit_buffer *ab, extern void audit_log_n_string(struct audit_buffer *ab, const char *buf, size_t n); -#define audit_log_string(a,b) audit_log_n_string(a, b, strlen(b)); extern void audit_log_n_untrustedstring(struct audit_buffer *ab, const char *string, size_t n); @@ -698,7 +769,8 @@ extern void audit_log_lost(const char *message); #ifdef CONFIG_SECURITY extern void audit_log_secctx(struct audit_buffer *ab, u32 secid); #else -#define audit_log_secctx(b,s) do { ; } while (0) +static inline void audit_log_secctx(struct audit_buffer *ab, u32 secid) +{ } #endif extern int audit_update_lsm_rules(void); @@ -710,22 +782,50 @@ extern int audit_receive_filter(int type, int pid, int seq, void *data, size_t datasz, kuid_t loginuid, u32 sessionid, u32 sid); extern int audit_enabled; -#else -#define audit_log(c,g,t,f,...) do { ; } while (0) -#define audit_log_start(c,g,t) ({ NULL; }) -#define audit_log_vformat(b,f,a) do { ; } while (0) -#define audit_log_format(b,f,...) do { ; } while (0) -#define audit_log_end(b) do { ; } while (0) -#define audit_log_n_hex(a,b,l) do { ; } while (0) -#define audit_log_n_string(a,c,l) do { ; } while (0) -#define audit_log_string(a,c) do { ; } while (0) -#define audit_log_n_untrustedstring(a,n,s) do { ; } while (0) -#define audit_log_untrustedstring(a,s) do { ; } while (0) -#define audit_log_d_path(b, p, d) do { ; } while (0) -#define audit_log_key(b, k) do { ; } while (0) -#define audit_log_link_denied(o, l) do { ; } while (0) -#define audit_log_secctx(b,s) do { ; } while (0) +#else /* CONFIG_AUDIT */ +static inline __printf(4, 5) +void audit_log(struct audit_context *ctx, gfp_t gfp_mask, int type, + const char *fmt, ...) +{ } +static inline struct audit_buffer *audit_log_start(struct audit_context *ctx, + gfp_t gfp_mask, int type) +{ + return NULL; +} +static inline __printf(2, 3) +void audit_log_format(struct audit_buffer *ab, const char *fmt, ...) +{ } +static inline void audit_log_end(struct audit_buffer *ab) +{ } +static inline void audit_log_n_hex(struct audit_buffer *ab, + const unsigned char *buf, size_t len) +{ } +static inline void audit_log_n_string(struct audit_buffer *ab, + const char *buf, size_t n) +{ } +static inline void audit_log_n_untrustedstring(struct audit_buffer *ab, + const char *string, size_t n) +{ } +static inline void audit_log_untrustedstring(struct audit_buffer *ab, + const char *string) +{ } +static inline void audit_log_d_path(struct audit_buffer *ab, + const char *prefix, + const struct path *path) +{ } +static inline void audit_log_key(struct audit_buffer *ab, char *key) +{ } +static inline void audit_log_link_denied(const char *string, + const struct path *link) +{ } +static inline void audit_log_secctx(struct audit_buffer *ab, u32 secid) +{ } #define audit_enabled 0 -#endif +#endif /* CONFIG_AUDIT */ +static inline void audit_log_string(struct audit_buffer *ab, const char *buf) +{ + audit_log_n_string(ab, buf, strlen(buf)); +} + #endif #endif -- cgit v1.2.3-70-g09d2 From 754b7b63d112ecc771e376388dcefd7b79bf5c4b Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Thu, 4 Oct 2012 17:11:27 -0700 Subject: sections: disable const sections for PA-RISC v2 The PA-RISC tool chain seems to have some problem with correct read/write attributes on sections. This causes problems when the const sections are fixed up for other architecture to only contain truly read-only data. Disable const sections for PA-RISC This can cause a bit of noise with modpost. Signed-off-by: Andi Kleen Cc: "James E.J. Bottomley" Cc: Helge Deller Acked-by: Sam Ravnborg Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/parisc/Kconfig | 1 + include/linux/init.h | 27 +++++++++++++++++++-------- init/Kconfig | 6 ++++++ 3 files changed, 26 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/arch/parisc/Kconfig b/arch/parisc/Kconfig index 3ff21b536f2..b87438bb338 100644 --- a/arch/parisc/Kconfig +++ b/arch/parisc/Kconfig @@ -13,6 +13,7 @@ config PARISC select HAVE_PERF_EVENTS select GENERIC_ATOMIC64 if !64BIT select HAVE_GENERIC_HARDIRQS + select BROKEN_RODATA select GENERIC_IRQ_PROBE select GENERIC_PCI_IOMAP select IRQ_PER_CPU diff --git a/include/linux/init.h b/include/linux/init.h index 5e664f67161..e59041e21df 100644 --- a/include/linux/init.h +++ b/include/linux/init.h @@ -43,10 +43,21 @@ discard it in modules) */ #define __init __section(.init.text) __cold notrace #define __initdata __section(.init.data) -#define __initconst __section(.init.rodata) +#define __initconst __constsection(.init.rodata) #define __exitdata __section(.exit.data) #define __exit_call __used __section(.exitcall.exit) +/* + * Some architecture have tool chains which do not handle rodata attributes + * correctly. For those disable special sections for const, so that other + * architectures can annotate correctly. + */ +#ifdef CONFIG_BROKEN_RODATA +#define __constsection(x) +#else +#define __constsection(x) __section(x) +#endif + /* * modpost check for section mismatches during the kernel build. * A section mismatch happens when there are references from a @@ -66,7 +77,7 @@ */ #define __ref __section(.ref.text) noinline #define __refdata __section(.ref.data) -#define __refconst __section(.ref.rodata) +#define __refconst __constsection(.ref.rodata) /* compatibility defines */ #define __init_refok __ref @@ -85,26 +96,26 @@ /* Used for HOTPLUG */ #define __devinit __section(.devinit.text) __cold notrace #define __devinitdata __section(.devinit.data) -#define __devinitconst __section(.devinit.rodata) +#define __devinitconst __constsection(.devinit.rodata) #define __devexit __section(.devexit.text) __exitused __cold notrace #define __devexitdata __section(.devexit.data) -#define __devexitconst __section(.devexit.rodata) +#define __devexitconst __constsection(.devexit.rodata) /* Used for HOTPLUG_CPU */ #define __cpuinit __section(.cpuinit.text) __cold notrace #define __cpuinitdata __section(.cpuinit.data) -#define __cpuinitconst __section(.cpuinit.rodata) +#define __cpuinitconst __constsection(.cpuinit.rodata) #define __cpuexit __section(.cpuexit.text) __exitused __cold notrace #define __cpuexitdata __section(.cpuexit.data) -#define __cpuexitconst __section(.cpuexit.rodata) +#define __cpuexitconst __constsection(.cpuexit.rodata) /* Used for MEMORY_HOTPLUG */ #define __meminit __section(.meminit.text) __cold notrace #define __meminitdata __section(.meminit.data) -#define __meminitconst __section(.meminit.rodata) +#define __meminitconst __constsection(.meminit.rodata) #define __memexit __section(.memexit.text) __exitused __cold notrace #define __memexitdata __section(.memexit.data) -#define __memexitconst __section(.memexit.rodata) +#define __memexitconst __constsection(.memexit.rodata) /* For assembly routines */ #define __HEAD .section ".head.text","ax" diff --git a/init/Kconfig b/init/Kconfig index cb003a3c912..e82f289290f 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1581,4 +1581,10 @@ config PADATA depends on SMP bool +# Can be selected by architectures with broken toolchains +# that get confused by correct const<->read_only section +# mappings +config BROKEN_RODATA + bool + source "kernel/Kconfig.locks" -- cgit v1.2.3-70-g09d2 From 17f3609c21706b377ad80b5251558ed700c2af17 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Thu, 4 Oct 2012 17:12:07 -0700 Subject: sections: fix section conflicts in mm/percpu.c Signed-off-by: Andi Kleen Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/percpu.h | 2 +- mm/percpu.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/percpu.h b/include/linux/percpu.h index 2b9f82c037c..cc88172c7d9 100644 --- a/include/linux/percpu.h +++ b/include/linux/percpu.h @@ -107,7 +107,7 @@ enum pcpu_fc { PCPU_FC_NR, }; -extern const char *pcpu_fc_names[PCPU_FC_NR]; +extern const char * const pcpu_fc_names[PCPU_FC_NR]; extern enum pcpu_fc pcpu_chosen_fc; diff --git a/mm/percpu.c b/mm/percpu.c index bb4be7435ce..ddc5efb9c5b 100644 --- a/mm/percpu.c +++ b/mm/percpu.c @@ -1370,7 +1370,7 @@ int __init pcpu_setup_first_chunk(const struct pcpu_alloc_info *ai, #ifdef CONFIG_SMP -const char *pcpu_fc_names[PCPU_FC_NR] __initdata = { +const char * const pcpu_fc_names[PCPU_FC_NR] __initconst = { [PCPU_FC_AUTO] = "auto", [PCPU_FC_EMBED] = "embed", [PCPU_FC_PAGE] = "page", -- cgit v1.2.3-70-g09d2 From 6275ce9f7ffdccd414a92f6ec8fe0d7584037ee5 Mon Sep 17 00:00:00 2001 From: "Kim, Milo" Date: Thu, 4 Oct 2012 17:12:50 -0700 Subject: backlight: lp855x: add FAST bit description for LP8556 LP8556 backlight driver supports fast refresh mode when exiting the low power mode. This bit can be configurable in the platform side. Signed-off-by: Milo(Woogyom) Kim Cc: Richard Purdie Cc: Bryan Wu Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/platform_data/lp855x.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include/linux') diff --git a/include/linux/platform_data/lp855x.h b/include/linux/platform_data/lp855x.h index cc76f1f18f1..761f3175236 100644 --- a/include/linux/platform_data/lp855x.h +++ b/include/linux/platform_data/lp855x.h @@ -46,6 +46,8 @@ #define LP8556_I2C_CONFIG ((ENABLE_BL << BL_CTL_SHFT) | \ (LP8556_I2C_ONLY << BRT_MODE_SHFT)) #define LP8556_COMB2_CONFIG (LP8556_COMBINED2 << BRT_MODE_SHFT) +#define LP8556_FAST_CONFIG BIT(7) /* use it if EPROMs should be maintained + when exiting the low power mode */ enum lp855x_chip_id { LP8550, -- cgit v1.2.3-70-g09d2 From 0c2a665a648ea74879ce6333d3a31a96a7c361ce Mon Sep 17 00:00:00 2001 From: "G.Shark Jeong" Date: Thu, 4 Oct 2012 17:12:52 -0700 Subject: backlight: add Backlight driver for lm3630 chip This driver is a general version for LM3630 backlgiht driver chip of TI. LM3630 : The LM3630 is a current mode boost converter which supplies the power and controls the current in two strings of up to 10 LEDs per string. Programming is done over an I2C compatible interface. www.ti.com [akpm@linux-foundation.org: make bled_name[] static, a few coding style tuneups, create new set_intensity(), partly to avoid awkward layout gymnastics] Signed-off-by: G.Shark Jeong Cc: Richard Purdie Cc: Daniel Jeong Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/backlight/Kconfig | 7 + drivers/video/backlight/Makefile | 1 + drivers/video/backlight/lm3630_bl.c | 475 ++++++++++++++++++++++++++++++++ include/linux/platform_data/lm3630_bl.h | 57 ++++ 4 files changed, 540 insertions(+) create mode 100644 drivers/video/backlight/lm3630_bl.c create mode 100644 include/linux/platform_data/lm3630_bl.h (limited to 'include/linux') diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index cf282763a8d..b9008b54ac5 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -352,6 +352,13 @@ config BACKLIGHT_AAT2870 If you have a AnalogicTech AAT2870 say Y to enable the backlight driver. +config BACKLIGHT_LM3630 + tristate "Backlight Driver for LM3630" + depends on BACKLIGHT_CLASS_DEVICE && I2C + select REGMAP_I2C + help + This supports TI LM3630 Backlight Driver + config BACKLIGHT_LP855X tristate "Backlight driver for TI LP855X" depends on BACKLIGHT_CLASS_DEVICE && I2C diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile index a2ac9cfbaf6..b0725313cd4 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_BACKLIGHT_HP700) += jornada720_bl.o obj-$(CONFIG_BACKLIGHT_HP680) += hp680_bl.o obj-$(CONFIG_BACKLIGHT_LM3533) += lm3533_bl.o obj-$(CONFIG_BACKLIGHT_LOCOMO) += locomolcd.o +obj-$(CONFIG_BACKLIGHT_LM3630) += lm3630_bl.o obj-$(CONFIG_BACKLIGHT_LP855X) += lp855x_bl.o obj-$(CONFIG_BACKLIGHT_OMAP1) += omap1_bl.o obj-$(CONFIG_BACKLIGHT_PANDORA) += pandora_bl.o diff --git a/drivers/video/backlight/lm3630_bl.c b/drivers/video/backlight/lm3630_bl.c new file mode 100644 index 00000000000..dc191441796 --- /dev/null +++ b/drivers/video/backlight/lm3630_bl.c @@ -0,0 +1,475 @@ +/* +* Simple driver for Texas Instruments LM3630 Backlight driver chip +* Copyright (C) 2012 Texas Instruments +* +* 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define REG_CTRL 0x00 +#define REG_CONFIG 0x01 +#define REG_BRT_A 0x03 +#define REG_BRT_B 0x04 +#define REG_INT_STATUS 0x09 +#define REG_INT_EN 0x0A +#define REG_FAULT 0x0B +#define REG_PWM_OUTLOW 0x12 +#define REG_PWM_OUTHIGH 0x13 +#define REG_MAX 0x1F + +#define INT_DEBOUNCE_MSEC 10 + +enum lm3630_leds { + BLED_ALL = 0, + BLED_1, + BLED_2 +}; + +static const char *bled_name[] = { + [BLED_ALL] = "lm3630_bled", /*Bank1 controls all string */ + [BLED_1] = "lm3630_bled1", /*Bank1 controls bled1 */ + [BLED_2] = "lm3630_bled2", /*Bank1 or 2 controls bled2 */ +}; + +struct lm3630_chip_data { + struct device *dev; + struct delayed_work work; + int irq; + struct workqueue_struct *irqthread; + struct lm3630_platform_data *pdata; + struct backlight_device *bled1; + struct backlight_device *bled2; + struct regmap *regmap; +}; + +/* initialize chip */ +static int __devinit lm3630_chip_init(struct lm3630_chip_data *pchip) +{ + int ret; + unsigned int reg_val; + struct lm3630_platform_data *pdata = pchip->pdata; + + /*pwm control */ + reg_val = ((pdata->pwm_active & 0x01) << 2) | (pdata->pwm_ctrl & 0x03); + ret = regmap_update_bits(pchip->regmap, REG_CONFIG, 0x07, reg_val); + if (ret < 0) + goto out; + + /* bank control */ + reg_val = ((pdata->bank_b_ctrl & 0x01) << 1) | + (pdata->bank_a_ctrl & 0x07); + ret = regmap_update_bits(pchip->regmap, REG_CTRL, 0x07, reg_val); + if (ret < 0) + goto out; + + ret = regmap_update_bits(pchip->regmap, REG_CTRL, 0x80, 0x00); + if (ret < 0) + goto out; + + /* set initial brightness */ + if (pdata->bank_a_ctrl != BANK_A_CTRL_DISABLE) { + ret = regmap_write(pchip->regmap, + REG_BRT_A, pdata->init_brt_led1); + if (ret < 0) + goto out; + } + + if (pdata->bank_b_ctrl != BANK_B_CTRL_DISABLE) { + ret = regmap_write(pchip->regmap, + REG_BRT_B, pdata->init_brt_led2); + if (ret < 0) + goto out; + } + return ret; + +out: + dev_err(pchip->dev, "i2c failed to access register\n"); + return ret; +} + +/* interrupt handling */ +static void lm3630_delayed_func(struct work_struct *work) +{ + int ret; + unsigned int reg_val; + struct lm3630_chip_data *pchip; + + pchip = container_of(work, struct lm3630_chip_data, work.work); + + ret = regmap_read(pchip->regmap, REG_INT_STATUS, ®_val); + if (ret < 0) { + dev_err(pchip->dev, + "i2c failed to access REG_INT_STATUS Register\n"); + return; + } + + dev_info(pchip->dev, "REG_INT_STATUS Register is 0x%x\n", reg_val); +} + +static irqreturn_t lm3630_isr_func(int irq, void *chip) +{ + int ret; + struct lm3630_chip_data *pchip = chip; + unsigned long delay = msecs_to_jiffies(INT_DEBOUNCE_MSEC); + + queue_delayed_work(pchip->irqthread, &pchip->work, delay); + + ret = regmap_update_bits(pchip->regmap, REG_CTRL, 0x80, 0x00); + if (ret < 0) + goto out; + + return IRQ_HANDLED; +out: + dev_err(pchip->dev, "i2c failed to access register\n"); + return IRQ_HANDLED; +} + +static int lm3630_intr_config(struct lm3630_chip_data *pchip) +{ + INIT_DELAYED_WORK(&pchip->work, lm3630_delayed_func); + pchip->irqthread = create_singlethread_workqueue("lm3630-irqthd"); + if (!pchip->irqthread) { + dev_err(pchip->dev, "create irq thread fail...\n"); + return -1; + } + if (request_threaded_irq + (pchip->irq, NULL, lm3630_isr_func, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "lm3630_irq", pchip)) { + dev_err(pchip->dev, "request threaded irq fail..\n"); + return -1; + } + return 0; +} + +static bool +set_intensity(struct backlight_device *bl, struct lm3630_chip_data *pchip) +{ + if (!pchip->pdata->pwm_set_intensity) + return false; + pchip->pdata->pwm_set_intensity(bl->props.brightness - 1, + pchip->pdata->pwm_period); + return true; +} + +/* update and get brightness */ +static int lm3630_bank_a_update_status(struct backlight_device *bl) +{ + int ret; + struct lm3630_chip_data *pchip = bl_get_data(bl); + enum lm3630_pwm_ctrl pwm_ctrl = pchip->pdata->pwm_ctrl; + + /* brightness 0 means disable */ + if (!bl->props.brightness) { + ret = regmap_update_bits(pchip->regmap, REG_CTRL, 0x04, 0x00); + if (ret < 0) + goto out; + return bl->props.brightness; + } + + /* pwm control */ + if (pwm_ctrl == PWM_CTRL_BANK_A || pwm_ctrl == PWM_CTRL_BANK_ALL) { + if (!set_intensity(bl, pchip)) + dev_err(pchip->dev, "No pwm control func. in plat-data\n"); + } else { + + /* i2c control */ + ret = regmap_update_bits(pchip->regmap, REG_CTRL, 0x80, 0x00); + if (ret < 0) + goto out; + mdelay(1); + ret = regmap_write(pchip->regmap, + REG_BRT_A, bl->props.brightness - 1); + if (ret < 0) + goto out; + } + return bl->props.brightness; +out: + dev_err(pchip->dev, "i2c failed to access REG_CTRL\n"); + return bl->props.brightness; +} + +static int lm3630_bank_a_get_brightness(struct backlight_device *bl) +{ + unsigned int reg_val; + int brightness, ret; + struct lm3630_chip_data *pchip = bl_get_data(bl); + enum lm3630_pwm_ctrl pwm_ctrl = pchip->pdata->pwm_ctrl; + + if (pwm_ctrl == PWM_CTRL_BANK_A || pwm_ctrl == PWM_CTRL_BANK_ALL) { + ret = regmap_read(pchip->regmap, REG_PWM_OUTHIGH, ®_val); + if (ret < 0) + goto out; + brightness = reg_val & 0x01; + ret = regmap_read(pchip->regmap, REG_PWM_OUTLOW, ®_val); + if (ret < 0) + goto out; + brightness = ((brightness << 8) | reg_val) + 1; + } else { + ret = regmap_update_bits(pchip->regmap, REG_CTRL, 0x80, 0x00); + if (ret < 0) + goto out; + mdelay(1); + ret = regmap_read(pchip->regmap, REG_BRT_A, ®_val); + if (ret < 0) + goto out; + brightness = reg_val + 1; + } + bl->props.brightness = brightness; + return bl->props.brightness; +out: + dev_err(pchip->dev, "i2c failed to access register\n"); + return 0; +} + +static const struct backlight_ops lm3630_bank_a_ops = { + .options = BL_CORE_SUSPENDRESUME, + .update_status = lm3630_bank_a_update_status, + .get_brightness = lm3630_bank_a_get_brightness, +}; + +static int lm3630_bank_b_update_status(struct backlight_device *bl) +{ + int ret; + struct lm3630_chip_data *pchip = bl_get_data(bl); + enum lm3630_pwm_ctrl pwm_ctrl = pchip->pdata->pwm_ctrl; + + if (pwm_ctrl == PWM_CTRL_BANK_B || pwm_ctrl == PWM_CTRL_BANK_ALL) { + if (!set_intensity(bl, pchip)) + dev_err(pchip->dev, + "no pwm control func. in plat-data\n"); + } else { + ret = regmap_update_bits(pchip->regmap, REG_CTRL, 0x80, 0x00); + if (ret < 0) + goto out; + mdelay(1); + ret = regmap_write(pchip->regmap, + REG_BRT_B, bl->props.brightness - 1); + } + return bl->props.brightness; +out: + dev_err(pchip->dev, "i2c failed to access register\n"); + return bl->props.brightness; +} + +static int lm3630_bank_b_get_brightness(struct backlight_device *bl) +{ + unsigned int reg_val; + int brightness, ret; + struct lm3630_chip_data *pchip = bl_get_data(bl); + enum lm3630_pwm_ctrl pwm_ctrl = pchip->pdata->pwm_ctrl; + + if (pwm_ctrl == PWM_CTRL_BANK_B || pwm_ctrl == PWM_CTRL_BANK_ALL) { + ret = regmap_read(pchip->regmap, REG_PWM_OUTHIGH, ®_val); + if (ret < 0) + goto out; + brightness = reg_val & 0x01; + ret = regmap_read(pchip->regmap, REG_PWM_OUTLOW, ®_val); + if (ret < 0) + goto out; + brightness = ((brightness << 8) | reg_val) + 1; + } else { + ret = regmap_update_bits(pchip->regmap, REG_CTRL, 0x80, 0x00); + if (ret < 0) + goto out; + mdelay(1); + ret = regmap_read(pchip->regmap, REG_BRT_B, ®_val); + if (ret < 0) + goto out; + brightness = reg_val + 1; + } + bl->props.brightness = brightness; + + return bl->props.brightness; +out: + dev_err(pchip->dev, "i2c failed to access register\n"); + return bl->props.brightness; +} + +static const struct backlight_ops lm3630_bank_b_ops = { + .options = BL_CORE_SUSPENDRESUME, + .update_status = lm3630_bank_b_update_status, + .get_brightness = lm3630_bank_b_get_brightness, +}; + +static int lm3630_backlight_register(struct lm3630_chip_data *pchip, + enum lm3630_leds ledno) +{ + const char *name = bled_name[ledno]; + struct backlight_properties props; + struct lm3630_platform_data *pdata = pchip->pdata; + + props.type = BACKLIGHT_RAW; + switch (ledno) { + case BLED_1: + case BLED_ALL: + props.brightness = pdata->init_brt_led1; + props.max_brightness = pdata->max_brt_led1; + pchip->bled1 = + backlight_device_register(name, pchip->dev, pchip, + &lm3630_bank_a_ops, &props); + if (IS_ERR(pchip->bled1)) + return -EIO; + break; + case BLED_2: + props.brightness = pdata->init_brt_led2; + props.max_brightness = pdata->max_brt_led2; + pchip->bled2 = + backlight_device_register(name, pchip->dev, pchip, + &lm3630_bank_b_ops, &props); + if (IS_ERR(pchip->bled2)) + return -EIO; + break; + } + return 0; +} + +static void lm3630_backlight_unregister(struct lm3630_chip_data *pchip) +{ + if (pchip->bled1) + backlight_device_unregister(pchip->bled1); + if (pchip->bled2) + backlight_device_unregister(pchip->bled2); +} + +static const struct regmap_config lm3630_regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = REG_MAX, +}; + +static int __devinit lm3630_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct lm3630_platform_data *pdata = client->dev.platform_data; + struct lm3630_chip_data *pchip; + int ret; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "fail : i2c functionality check...\n"); + return -EOPNOTSUPP; + } + + if (pdata == NULL) { + dev_err(&client->dev, "fail : no platform data.\n"); + return -ENODATA; + } + + pchip = devm_kzalloc(&client->dev, sizeof(struct lm3630_chip_data), + GFP_KERNEL); + if (!pchip) + return -ENOMEM; + pchip->pdata = pdata; + pchip->dev = &client->dev; + + pchip->regmap = devm_regmap_init_i2c(client, &lm3630_regmap); + if (IS_ERR(pchip->regmap)) { + ret = PTR_ERR(pchip->regmap); + dev_err(&client->dev, "fail : allocate register map: %d\n", + ret); + return ret; + } + i2c_set_clientdata(client, pchip); + + /* chip initialize */ + ret = lm3630_chip_init(pchip); + if (ret < 0) { + dev_err(&client->dev, "fail : init chip\n"); + goto err_chip_init; + } + + switch (pdata->bank_a_ctrl) { + case BANK_A_CTRL_ALL: + ret = lm3630_backlight_register(pchip, BLED_ALL); + pdata->bank_b_ctrl = BANK_B_CTRL_DISABLE; + break; + case BANK_A_CTRL_LED1: + ret = lm3630_backlight_register(pchip, BLED_1); + break; + case BANK_A_CTRL_LED2: + ret = lm3630_backlight_register(pchip, BLED_2); + pdata->bank_b_ctrl = BANK_B_CTRL_DISABLE; + break; + default: + break; + } + + if (ret < 0) + goto err_bl_reg; + + if (pdata->bank_b_ctrl && pchip->bled2 == NULL) { + ret = lm3630_backlight_register(pchip, BLED_2); + if (ret < 0) + goto err_bl_reg; + } + + /* interrupt enable : irq 0 is not allowed for lm3630 */ + pchip->irq = client->irq; + if (pchip->irq) + lm3630_intr_config(pchip); + + dev_info(&client->dev, "LM3630 backlight register OK.\n"); + return 0; + +err_bl_reg: + dev_err(&client->dev, "fail : backlight register.\n"); + lm3630_backlight_unregister(pchip); +err_chip_init: + return ret; +} + +static int __devexit lm3630_remove(struct i2c_client *client) +{ + int ret; + struct lm3630_chip_data *pchip = i2c_get_clientdata(client); + + ret = regmap_write(pchip->regmap, REG_BRT_A, 0); + if (ret < 0) + dev_err(pchip->dev, "i2c failed to access register\n"); + + ret = regmap_write(pchip->regmap, REG_BRT_B, 0); + if (ret < 0) + dev_err(pchip->dev, "i2c failed to access register\n"); + + lm3630_backlight_unregister(pchip); + if (pchip->irq) { + free_irq(pchip->irq, pchip); + flush_workqueue(pchip->irqthread); + destroy_workqueue(pchip->irqthread); + } + return 0; +} + +static const struct i2c_device_id lm3630_id[] = { + {LM3630_NAME, 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, lm3630_id); + +static struct i2c_driver lm3630_i2c_driver = { + .driver = { + .name = LM3630_NAME, + }, + .probe = lm3630_probe, + .remove = __devexit_p(lm3630_remove), + .id_table = lm3630_id, +}; + +module_i2c_driver(lm3630_i2c_driver); + +MODULE_DESCRIPTION("Texas Instruments Backlight driver for LM3630"); +MODULE_AUTHOR("G.Shark Jeong "); +MODULE_AUTHOR("Daniel Jeong "); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/platform_data/lm3630_bl.h b/include/linux/platform_data/lm3630_bl.h new file mode 100644 index 00000000000..9176dd3f2d6 --- /dev/null +++ b/include/linux/platform_data/lm3630_bl.h @@ -0,0 +1,57 @@ +/* +* Simple driver for Texas Instruments LM3630 LED Flash driver chip +* Copyright (C) 2012 Texas Instruments +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +* +*/ + +#ifndef __LINUX_LM3630_H +#define __LINUX_LM3630_H + +#define LM3630_NAME "lm3630_bl" + +enum lm3630_pwm_ctrl { + PWM_CTRL_DISABLE = 0, + PWM_CTRL_BANK_A, + PWM_CTRL_BANK_B, + PWM_CTRL_BANK_ALL, +}; + +enum lm3630_pwm_active { + PWM_ACTIVE_HIGH = 0, + PWM_ACTIVE_LOW, +}; + +enum lm3630_bank_a_ctrl { + BANK_A_CTRL_DISABLE = 0x0, + BANK_A_CTRL_LED1 = 0x4, + BANK_A_CTRL_LED2 = 0x1, + BANK_A_CTRL_ALL = 0x5, +}; + +enum lm3630_bank_b_ctrl { + BANK_B_CTRL_DISABLE = 0, + BANK_B_CTRL_LED2, +}; + +struct lm3630_platform_data { + + /* maximum brightness */ + int max_brt_led1; + int max_brt_led2; + + /* initial on brightness */ + int init_brt_led1; + int init_brt_led2; + enum lm3630_pwm_ctrl pwm_ctrl; + enum lm3630_pwm_active pwm_active; + enum lm3630_bank_a_ctrl bank_a_ctrl; + enum lm3630_bank_b_ctrl bank_b_ctrl; + unsigned int pwm_period; + void (*pwm_set_intensity) (int brightness, int max_brightness); +}; + +#endif /* __LINUX_LM3630_H */ -- cgit v1.2.3-70-g09d2 From 0f59858d511960caefb42c4535dc73c2c5f3136c Mon Sep 17 00:00:00 2001 From: "G.Shark Jeong" Date: Thu, 4 Oct 2012 17:12:55 -0700 Subject: backlight: add new lm3639 backlight driver This driver is a general version for LM3639 backlgiht + flash driver chip of TI. LM3639: The LM3639 is a single chip LCD Display Backlight driver + white LED Camera driver. Programming is done over an I2C compatible interface. www.ti.com [akpm@linux-foundation.org: code layout tweaks] Signed-off-by: G.Shark Jeong Cc: Richard Purdie Cc: Daniel Jeong Cc: Randy Dunlap Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/backlight/Kconfig | 9 + drivers/video/backlight/Makefile | 1 + drivers/video/backlight/lm3639_bl.c | 437 ++++++++++++++++++++++++++++++++ include/linux/platform_data/lm3639_bl.h | 69 +++++ 4 files changed, 516 insertions(+) create mode 100644 drivers/video/backlight/lm3639_bl.c create mode 100644 include/linux/platform_data/lm3639_bl.h (limited to 'include/linux') diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index b9008b54ac5..2b98fb8e711 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -359,6 +359,15 @@ config BACKLIGHT_LM3630 help This supports TI LM3630 Backlight Driver +config BACKLIGHT_LM3639 + tristate "Backlight Driver for LM3639" + depends on BACKLIGHT_CLASS_DEVICE && I2C + select REGMAP_I2C + select NEW_LEDS + select LEDS_CLASS + help + This supports TI LM3639 Backlight + 1.5A Flash LED Driver + config BACKLIGHT_LP855X tristate "Backlight driver for TI LP855X" depends on BACKLIGHT_CLASS_DEVICE && I2C diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile index b0725313cd4..a151378da45 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_BACKLIGHT_HP680) += hp680_bl.o obj-$(CONFIG_BACKLIGHT_LM3533) += lm3533_bl.o obj-$(CONFIG_BACKLIGHT_LOCOMO) += locomolcd.o obj-$(CONFIG_BACKLIGHT_LM3630) += lm3630_bl.o +obj-$(CONFIG_BACKLIGHT_LM3639) += lm3639_bl.o obj-$(CONFIG_BACKLIGHT_LP855X) += lp855x_bl.o obj-$(CONFIG_BACKLIGHT_OMAP1) += omap1_bl.o obj-$(CONFIG_BACKLIGHT_PANDORA) += pandora_bl.o diff --git a/drivers/video/backlight/lm3639_bl.c b/drivers/video/backlight/lm3639_bl.c new file mode 100644 index 00000000000..c6915c6c3cd --- /dev/null +++ b/drivers/video/backlight/lm3639_bl.c @@ -0,0 +1,437 @@ +/* +* Simple driver for Texas Instruments LM3639 Backlight + Flash LED driver chip +* Copyright (C) 2012 Texas Instruments +* +* 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define REG_DEV_ID 0x00 +#define REG_CHECKSUM 0x01 +#define REG_BL_CONF_1 0x02 +#define REG_BL_CONF_2 0x03 +#define REG_BL_CONF_3 0x04 +#define REG_BL_CONF_4 0x05 +#define REG_FL_CONF_1 0x06 +#define REG_FL_CONF_2 0x07 +#define REG_FL_CONF_3 0x08 +#define REG_IO_CTRL 0x09 +#define REG_ENABLE 0x0A +#define REG_FLAG 0x0B +#define REG_MAX REG_FLAG + +struct lm3639_chip_data { + struct device *dev; + struct lm3639_platform_data *pdata; + + struct backlight_device *bled; + struct led_classdev cdev_flash; + struct led_classdev cdev_torch; + struct regmap *regmap; + + unsigned int bled_mode; + unsigned int bled_map; + unsigned int last_flag; +}; + +/* initialize chip */ +static int __devinit lm3639_chip_init(struct lm3639_chip_data *pchip) +{ + int ret; + unsigned int reg_val; + struct lm3639_platform_data *pdata = pchip->pdata; + + /* input pins config. */ + ret = + regmap_update_bits(pchip->regmap, REG_BL_CONF_1, 0x08, + pdata->pin_pwm); + if (ret < 0) + goto out; + + reg_val = (pdata->pin_pwm & 0x40) | pdata->pin_strobe | pdata->pin_tx; + ret = regmap_update_bits(pchip->regmap, REG_IO_CTRL, 0x7C, reg_val); + if (ret < 0) + goto out; + + /* init brightness */ + ret = regmap_write(pchip->regmap, REG_BL_CONF_4, pdata->init_brt_led); + if (ret < 0) + goto out; + + ret = regmap_write(pchip->regmap, REG_BL_CONF_3, pdata->init_brt_led); + if (ret < 0) + goto out; + + /* output pins config. */ + if (!pdata->init_brt_led) + reg_val = pdata->fled_pins | pdata->bled_pins; + else + reg_val = pdata->fled_pins | pdata->bled_pins | 0x01; + + ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x79, reg_val); + if (ret < 0) + goto out; + + return ret; +out: + dev_err(pchip->dev, "i2c failed to access register\n"); + return ret; +} + +/* update and get brightness */ +static int lm3639_bled_update_status(struct backlight_device *bl) +{ + int ret; + unsigned int reg_val; + struct lm3639_chip_data *pchip = bl_get_data(bl); + struct lm3639_platform_data *pdata = pchip->pdata; + + ret = regmap_read(pchip->regmap, REG_FLAG, ®_val); + if (ret < 0) + goto out; + + if (reg_val != 0) + dev_info(pchip->dev, "last flag is 0x%x\n", reg_val); + + /* pwm control */ + if (pdata->pin_pwm) { + if (pdata->pwm_set_intensity) + pdata->pwm_set_intensity(bl->props.brightness, + pdata->max_brt_led); + else + dev_err(pchip->dev, + "No pwm control func. in plat-data\n"); + return bl->props.brightness; + } + + /* i2c control and set brigtness */ + ret = regmap_write(pchip->regmap, REG_BL_CONF_4, bl->props.brightness); + if (ret < 0) + goto out; + ret = regmap_write(pchip->regmap, REG_BL_CONF_3, bl->props.brightness); + if (ret < 0) + goto out; + + if (!bl->props.brightness) + ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x01, 0x00); + else + ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x01, 0x01); + if (ret < 0) + goto out; + + return bl->props.brightness; +out: + dev_err(pchip->dev, "i2c failed to access registers\n"); + return bl->props.brightness; +} + +static int lm3639_bled_get_brightness(struct backlight_device *bl) +{ + int ret; + unsigned int reg_val; + struct lm3639_chip_data *pchip = bl_get_data(bl); + struct lm3639_platform_data *pdata = pchip->pdata; + + if (pdata->pin_pwm) { + if (pdata->pwm_get_intensity) + bl->props.brightness = pdata->pwm_get_intensity(); + else + dev_err(pchip->dev, + "No pwm control func. in plat-data\n"); + return bl->props.brightness; + } + + ret = regmap_read(pchip->regmap, REG_BL_CONF_1, ®_val); + if (ret < 0) + goto out; + if (reg_val & 0x10) + ret = regmap_read(pchip->regmap, REG_BL_CONF_4, ®_val); + else + ret = regmap_read(pchip->regmap, REG_BL_CONF_3, ®_val); + if (ret < 0) + goto out; + bl->props.brightness = reg_val; + + return bl->props.brightness; +out: + dev_err(pchip->dev, "i2c failed to access register\n"); + return bl->props.brightness; +} + +static const struct backlight_ops lm3639_bled_ops = { + .options = BL_CORE_SUSPENDRESUME, + .update_status = lm3639_bled_update_status, + .get_brightness = lm3639_bled_get_brightness, +}; + +/* backlight mapping mode */ +static ssize_t lm3639_bled_mode_store(struct device *dev, + struct device_attribute *devAttr, + const char *buf, size_t size) +{ + ssize_t ret; + struct lm3639_chip_data *pchip = dev_get_drvdata(dev); + unsigned int state; + + ret = kstrtouint(buf, 10, &state); + if (ret) + goto out_input; + + if (!state) + ret = + regmap_update_bits(pchip->regmap, REG_BL_CONF_1, 0x10, + 0x00); + else + ret = + regmap_update_bits(pchip->regmap, REG_BL_CONF_1, 0x10, + 0x10); + + if (ret < 0) + goto out; + + return size; + +out: + dev_err(pchip->dev, "%s:i2c access fail to register\n", __func__); + return size; + +out_input: + dev_err(pchip->dev, "%s:input conversion fail\n", __func__); + return size; + +} + +static DEVICE_ATTR(bled_mode, 0666, NULL, lm3639_bled_mode_store); + +/* torch */ +static void lm3639_torch_brightness_set(struct led_classdev *cdev, + enum led_brightness brightness) +{ + int ret; + unsigned int reg_val; + struct lm3639_chip_data *pchip; + + pchip = container_of(cdev, struct lm3639_chip_data, cdev_torch); + + ret = regmap_read(pchip->regmap, REG_FLAG, ®_val); + if (ret < 0) + goto out; + if (reg_val != 0) + dev_info(pchip->dev, "last flag is 0x%x\n", reg_val); + + /* brightness 0 means off state */ + if (!brightness) { + ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x06, 0x00); + if (ret < 0) + goto out; + return; + } + + ret = regmap_update_bits(pchip->regmap, + REG_FL_CONF_1, 0x70, (brightness - 1) << 4); + if (ret < 0) + goto out; + ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x06, 0x02); + if (ret < 0) + goto out; + + return; +out: + dev_err(pchip->dev, "i2c failed to access register\n"); + return; +} + +/* flash */ +static void lm3639_flash_brightness_set(struct led_classdev *cdev, + enum led_brightness brightness) +{ + int ret; + unsigned int reg_val; + struct lm3639_chip_data *pchip; + + pchip = container_of(cdev, struct lm3639_chip_data, cdev_flash); + + ret = regmap_read(pchip->regmap, REG_FLAG, ®_val); + if (ret < 0) + goto out; + if (reg_val != 0) + dev_info(pchip->dev, "last flag is 0x%x\n", reg_val); + + /* torch off before flash control */ + ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x06, 0x00); + if (ret < 0) + goto out; + + /* brightness 0 means off state */ + if (!brightness) + return; + + ret = regmap_update_bits(pchip->regmap, + REG_FL_CONF_1, 0x0F, brightness - 1); + if (ret < 0) + goto out; + ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x06, 0x06); + if (ret < 0) + goto out; + + return; +out: + dev_err(pchip->dev, "i2c failed to access register\n"); + return; +} + +static const struct regmap_config lm3639_regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = REG_MAX, +}; + +static int __devinit lm3639_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret; + struct lm3639_chip_data *pchip; + struct lm3639_platform_data *pdata = client->dev.platform_data; + struct backlight_properties props; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "i2c functionality check fail.\n"); + return -EOPNOTSUPP; + } + + if (pdata == NULL) { + dev_err(&client->dev, "Needs Platform Data.\n"); + return -ENODATA; + } + + pchip = devm_kzalloc(&client->dev, + sizeof(struct lm3639_chip_data), GFP_KERNEL); + if (!pchip) + return -ENOMEM; + + pchip->pdata = pdata; + pchip->dev = &client->dev; + + pchip->regmap = devm_regmap_init_i2c(client, &lm3639_regmap); + if (IS_ERR(pchip->regmap)) { + ret = PTR_ERR(pchip->regmap); + dev_err(&client->dev, "fail : allocate register map: %d\n", + ret); + return ret; + } + i2c_set_clientdata(client, pchip); + + /* chip initialize */ + ret = lm3639_chip_init(pchip); + if (ret < 0) { + dev_err(&client->dev, "fail : chip init\n"); + goto err_out; + } + + /* backlight */ + props.type = BACKLIGHT_RAW; + props.brightness = pdata->init_brt_led; + props.max_brightness = pdata->max_brt_led; + pchip->bled = + backlight_device_register("lm3639_bled", pchip->dev, pchip, + &lm3639_bled_ops, &props); + if (IS_ERR(pchip->bled)) { + dev_err(&client->dev, "fail : backlight register\n"); + ret = -EIO; + goto err_out; + } + + ret = device_create_file(&(pchip->bled->dev), &dev_attr_bled_mode); + if (ret < 0) { + dev_err(&client->dev, "failed : add sysfs entries\n"); + ret = -EIO; + goto err_bled_mode; + } + + /* flash */ + pchip->cdev_flash.name = "lm3639_flash"; + pchip->cdev_flash.max_brightness = 16; + pchip->cdev_flash.brightness_set = lm3639_flash_brightness_set; + ret = led_classdev_register((struct device *) + &client->dev, &pchip->cdev_flash); + if (ret < 0) { + dev_err(&client->dev, "fail : flash register\n"); + ret = -EIO; + goto err_flash; + } + + /* torch */ + pchip->cdev_torch.name = "lm3639_torch"; + pchip->cdev_torch.max_brightness = 8; + pchip->cdev_torch.brightness_set = lm3639_torch_brightness_set; + ret = led_classdev_register((struct device *) + &client->dev, &pchip->cdev_torch); + if (ret < 0) { + dev_err(&client->dev, "fail : torch register\n"); + ret = -EIO; + goto err_torch; + } + + return 0; + +err_torch: + led_classdev_unregister(&pchip->cdev_flash); +err_flash: + device_remove_file(&(pchip->bled->dev), &dev_attr_bled_mode); +err_bled_mode: + backlight_device_unregister(pchip->bled); +err_out: + return ret; +} + +static int __devexit lm3639_remove(struct i2c_client *client) +{ + struct lm3639_chip_data *pchip = i2c_get_clientdata(client); + + regmap_write(pchip->regmap, REG_ENABLE, 0x00); + + if (&pchip->cdev_torch) + led_classdev_unregister(&pchip->cdev_torch); + if (&pchip->cdev_flash) + led_classdev_unregister(&pchip->cdev_flash); + if (pchip->bled) { + device_remove_file(&(pchip->bled->dev), &dev_attr_bled_mode); + backlight_device_unregister(pchip->bled); + } + return 0; +} + +static const struct i2c_device_id lm3639_id[] = { + {LM3639_NAME, 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, lm3639_id); +static struct i2c_driver lm3639_i2c_driver = { + .driver = { + .name = LM3639_NAME, + }, + .probe = lm3639_probe, + .remove = __devexit_p(lm3639_remove), + .id_table = lm3639_id, +}; + +module_i2c_driver(lm3639_i2c_driver); + +MODULE_DESCRIPTION("Texas Instruments Backlight+Flash LED driver for LM3639"); +MODULE_AUTHOR("Daniel Jeong "); +MODULE_AUTHOR("G.Shark Jeong "); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/platform_data/lm3639_bl.h b/include/linux/platform_data/lm3639_bl.h new file mode 100644 index 00000000000..5234cd5ed16 --- /dev/null +++ b/include/linux/platform_data/lm3639_bl.h @@ -0,0 +1,69 @@ +/* +* Simple driver for Texas Instruments LM3630 LED Flash driver chip +* Copyright (C) 2012 Texas Instruments +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +* +*/ + +#ifndef __LINUX_LM3639_H +#define __LINUX_LM3639_H + +#define LM3639_NAME "lm3639_bl" + +enum lm3639_pwm { + LM3639_PWM_DISABLE = 0x00, + LM3639_PWM_EN_ACTLOW = 0x48, + LM3639_PWM_EN_ACTHIGH = 0x40, +}; + +enum lm3639_strobe { + LM3639_STROBE_DISABLE = 0x00, + LM3639_STROBE_EN_ACTLOW = 0x10, + LM3639_STROBE_EN_ACTHIGH = 0x30, +}; + +enum lm3639_txpin { + LM3639_TXPIN_DISABLE = 0x00, + LM3639_TXPIN_EN_ACTLOW = 0x04, + LM3639_TXPIN_EN_ACTHIGH = 0x0C, +}; + +enum lm3639_fleds { + LM3639_FLED_DIASBLE_ALL = 0x00, + LM3639_FLED_EN_1 = 0x40, + LM3639_FLED_EN_2 = 0x20, + LM3639_FLED_EN_ALL = 0x60, +}; + +enum lm3639_bleds { + LM3639_BLED_DIASBLE_ALL = 0x00, + LM3639_BLED_EN_1 = 0x10, + LM3639_BLED_EN_2 = 0x08, + LM3639_BLED_EN_ALL = 0x18, +}; +enum lm3639_bled_mode { + LM3639_BLED_MODE_EXPONETIAL = 0x00, + LM3639_BLED_MODE_LINEAR = 0x10, +}; + +struct lm3639_platform_data { + unsigned int max_brt_led; + unsigned int init_brt_led; + + /* input pins */ + enum lm3639_pwm pin_pwm; + enum lm3639_strobe pin_strobe; + enum lm3639_txpin pin_tx; + + /* output pins */ + enum lm3639_fleds fled_pins; + enum lm3639_bleds bled_pins; + enum lm3639_bled_mode bled_mode; + + void (*pwm_set_intensity) (int brightness, int max_brightness); + int (*pwm_get_intensity) (void); +}; +#endif /* __LINUX_LM3639_H */ -- cgit v1.2.3-70-g09d2 From 125c4c706b680c7831f0966ff873c1ad0354ec25 Mon Sep 17 00:00:00 2001 From: Fengguang Wu Date: Thu, 4 Oct 2012 17:13:15 -0700 Subject: idr: rename MAX_LEVEL to MAX_IDR_LEVEL To avoid name conflicts: drivers/video/riva/fbdev.c:281:9: sparse: preprocessor token MAX_LEVEL redefined While at it, also make the other names more consistent and add parentheses. [akpm@linux-foundation.org: repair fallout] [sfr@canb.auug.org.au: IB/mlx4: fix for MAX_ID_MASK to MAX_IDR_MASK name change] Signed-off-by: Fengguang Wu Cc: Bernd Petrovitsch Cc: walter harms Cc: Glauber Costa Signed-off-by: Stephen Rothwell Cc: Roland Dreier Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/i2c/i2c-core.c | 2 +- drivers/infiniband/core/cm.c | 2 +- drivers/infiniband/hw/mlx4/cm.c | 2 +- drivers/pps/pps.c | 2 +- drivers/thermal/thermal_sys.c | 2 +- fs/super.c | 2 +- include/linux/idr.h | 10 +++++----- lib/idr.c | 32 ++++++++++++++++---------------- 8 files changed, 27 insertions(+), 27 deletions(-) (limited to 'include/linux') diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 2091ae8f539..2421d95130d 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -982,7 +982,7 @@ int i2c_add_numbered_adapter(struct i2c_adapter *adap) if (adap->nr == -1) /* -1 means dynamically assign bus id */ return i2c_add_adapter(adap); - if (adap->nr & ~MAX_ID_MASK) + if (adap->nr & ~MAX_IDR_MASK) return -EINVAL; retry: diff --git a/drivers/infiniband/core/cm.c b/drivers/infiniband/core/cm.c index d67999f6e34..394fea2ba1b 100644 --- a/drivers/infiniband/core/cm.c +++ b/drivers/infiniband/core/cm.c @@ -390,7 +390,7 @@ static int cm_alloc_id(struct cm_id_private *cm_id_priv) ret = idr_get_new_above(&cm.local_id_table, cm_id_priv, next_id, &id); if (!ret) - next_id = ((unsigned) id + 1) & MAX_ID_MASK; + next_id = ((unsigned) id + 1) & MAX_IDR_MASK; spin_unlock_irqrestore(&cm.lock, flags); } while( (ret == -EAGAIN) && idr_pre_get(&cm.local_id_table, GFP_KERNEL) ); diff --git a/drivers/infiniband/hw/mlx4/cm.c b/drivers/infiniband/hw/mlx4/cm.c index e25e4dafb8a..80079e5a2e3 100644 --- a/drivers/infiniband/hw/mlx4/cm.c +++ b/drivers/infiniband/hw/mlx4/cm.c @@ -225,7 +225,7 @@ id_map_alloc(struct ib_device *ibdev, int slave_id, u32 sl_cm_id) ret = idr_get_new_above(&sriov->pv_id_table, ent, next_id, &id); if (!ret) { - next_id = ((unsigned) id + 1) & MAX_ID_MASK; + next_id = ((unsigned) id + 1) & MAX_IDR_MASK; ent->pv_cm_id = (u32)id; sl_id_map_add(ibdev, ent); } diff --git a/drivers/pps/pps.c b/drivers/pps/pps.c index e771487132f..2420d5af058 100644 --- a/drivers/pps/pps.c +++ b/drivers/pps/pps.c @@ -306,7 +306,7 @@ int pps_register_cdev(struct pps_device *pps) if (err < 0) return err; - pps->id &= MAX_ID_MASK; + pps->id &= MAX_IDR_MASK; if (pps->id >= PPS_MAX_SOURCES) { pr_err("%s: too many PPS sources in the system\n", pps->info.name); diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c index 67789b8345d..efd81bb25e0 100644 --- a/drivers/thermal/thermal_sys.c +++ b/drivers/thermal/thermal_sys.c @@ -78,7 +78,7 @@ again: else if (unlikely(err)) return err; - *id = *id & MAX_ID_MASK; + *id = *id & MAX_IDR_MASK; return 0; } diff --git a/fs/super.c b/fs/super.c index 5fdf7ff32c4..a3bc935069d 100644 --- a/fs/super.c +++ b/fs/super.c @@ -865,7 +865,7 @@ int get_anon_bdev(dev_t *p) else if (error) return -EAGAIN; - if ((dev & MAX_ID_MASK) == (1 << MINORBITS)) { + if ((dev & MAX_IDR_MASK) == (1 << MINORBITS)) { spin_lock(&unnamed_dev_lock); ida_remove(&unnamed_dev_ida, dev); if (unnamed_dev_start > dev) diff --git a/include/linux/idr.h b/include/linux/idr.h index 255491cf522..87259a44c25 100644 --- a/include/linux/idr.h +++ b/include/linux/idr.h @@ -38,15 +38,15 @@ #define IDR_SIZE (1 << IDR_BITS) #define IDR_MASK ((1 << IDR_BITS)-1) -#define MAX_ID_SHIFT (sizeof(int)*8 - 1) -#define MAX_ID_BIT (1U << MAX_ID_SHIFT) -#define MAX_ID_MASK (MAX_ID_BIT - 1) +#define MAX_IDR_SHIFT (sizeof(int)*8 - 1) +#define MAX_IDR_BIT (1U << MAX_IDR_SHIFT) +#define MAX_IDR_MASK (MAX_IDR_BIT - 1) /* Leave the possibility of an incomplete final layer */ -#define MAX_LEVEL (MAX_ID_SHIFT + IDR_BITS - 1) / IDR_BITS +#define MAX_IDR_LEVEL ((MAX_IDR_SHIFT + IDR_BITS - 1) / IDR_BITS) /* Number of id_layer structs to leave in free list */ -#define IDR_FREE_MAX MAX_LEVEL + MAX_LEVEL +#define MAX_IDR_FREE (MAX_IDR_LEVEL * 2) struct idr_layer { unsigned long bitmap; /* A zero bit means "space here" */ diff --git a/lib/idr.c b/lib/idr.c index 4046e29c0a9..648239079dd 100644 --- a/lib/idr.c +++ b/lib/idr.c @@ -20,7 +20,7 @@ * that id to this code and it returns your pointer. * You can release ids at any time. When all ids are released, most of - * the memory is returned (we keep IDR_FREE_MAX) in a local pool so we + * the memory is returned (we keep MAX_IDR_FREE) in a local pool so we * don't need to go to the memory "store" during an id allocate, just * so you don't need to be too concerned about locking and conflicts * with the slab allocator. @@ -122,7 +122,7 @@ static void idr_mark_full(struct idr_layer **pa, int id) */ int idr_pre_get(struct idr *idp, gfp_t gfp_mask) { - while (idp->id_free_cnt < IDR_FREE_MAX) { + while (idp->id_free_cnt < MAX_IDR_FREE) { struct idr_layer *new; new = kmem_cache_zalloc(idr_layer_cache, gfp_mask); if (new == NULL) @@ -179,7 +179,7 @@ static int sub_alloc(struct idr *idp, int *starting_id, struct idr_layer **pa) sh = IDR_BITS*l; id = ((id >> sh) ^ n ^ m) << sh; } - if ((id >= MAX_ID_BIT) || (id < 0)) + if ((id >= MAX_IDR_BIT) || (id < 0)) return IDR_NOMORE_SPACE; if (l == 0) break; @@ -223,7 +223,7 @@ build_up: * Add a new layer to the top of the tree if the requested * id is larger than the currently allocated space. */ - while ((layers < (MAX_LEVEL - 1)) && (id >= (1 << (layers*IDR_BITS)))) { + while ((layers < (MAX_IDR_LEVEL - 1)) && (id >= (1 << (layers*IDR_BITS)))) { layers++; if (!p->count) { /* special case: if the tree is currently empty, @@ -265,7 +265,7 @@ build_up: static int idr_get_new_above_int(struct idr *idp, void *ptr, int starting_id) { - struct idr_layer *pa[MAX_LEVEL]; + struct idr_layer *pa[MAX_IDR_LEVEL]; int id; id = idr_get_empty_slot(idp, starting_id, pa); @@ -357,7 +357,7 @@ static void idr_remove_warning(int id) static void sub_remove(struct idr *idp, int shift, int id) { struct idr_layer *p = idp->top; - struct idr_layer **pa[MAX_LEVEL]; + struct idr_layer **pa[MAX_IDR_LEVEL]; struct idr_layer ***paa = &pa[0]; struct idr_layer *to_free; int n; @@ -402,7 +402,7 @@ void idr_remove(struct idr *idp, int id) struct idr_layer *to_free; /* Mask off upper bits we don't use for the search. */ - id &= MAX_ID_MASK; + id &= MAX_IDR_MASK; sub_remove(idp, (idp->layers - 1) * IDR_BITS, id); if (idp->top && idp->top->count == 1 && (idp->layers > 1) && @@ -420,7 +420,7 @@ void idr_remove(struct idr *idp, int id) to_free->bitmap = to_free->count = 0; free_layer(to_free); } - while (idp->id_free_cnt >= IDR_FREE_MAX) { + while (idp->id_free_cnt >= MAX_IDR_FREE) { p = get_from_free_list(idp); /* * Note: we don't call the rcu callback here, since the only @@ -451,7 +451,7 @@ void idr_remove_all(struct idr *idp) int n, id, max; int bt_mask; struct idr_layer *p; - struct idr_layer *pa[MAX_LEVEL]; + struct idr_layer *pa[MAX_IDR_LEVEL]; struct idr_layer **paa = &pa[0]; n = idp->layers * IDR_BITS; @@ -517,7 +517,7 @@ void *idr_find(struct idr *idp, int id) n = (p->layer+1) * IDR_BITS; /* Mask off upper bits we don't use for the search. */ - id &= MAX_ID_MASK; + id &= MAX_IDR_MASK; if (id >= (1 << n)) return NULL; @@ -555,7 +555,7 @@ int idr_for_each(struct idr *idp, { int n, id, max, error = 0; struct idr_layer *p; - struct idr_layer *pa[MAX_LEVEL]; + struct idr_layer *pa[MAX_IDR_LEVEL]; struct idr_layer **paa = &pa[0]; n = idp->layers * IDR_BITS; @@ -601,7 +601,7 @@ EXPORT_SYMBOL(idr_for_each); */ void *idr_get_next(struct idr *idp, int *nextidp) { - struct idr_layer *p, *pa[MAX_LEVEL]; + struct idr_layer *p, *pa[MAX_IDR_LEVEL]; struct idr_layer **paa = &pa[0]; int id = *nextidp; int n, max; @@ -659,7 +659,7 @@ void *idr_replace(struct idr *idp, void *ptr, int id) n = (p->layer+1) * IDR_BITS; - id &= MAX_ID_MASK; + id &= MAX_IDR_MASK; if (id >= (1 << n)) return ERR_PTR(-EINVAL); @@ -780,7 +780,7 @@ EXPORT_SYMBOL(ida_pre_get); */ int ida_get_new_above(struct ida *ida, int starting_id, int *p_id) { - struct idr_layer *pa[MAX_LEVEL]; + struct idr_layer *pa[MAX_IDR_LEVEL]; struct ida_bitmap *bitmap; unsigned long flags; int idr_id = starting_id / IDA_BITMAP_BITS; @@ -793,7 +793,7 @@ int ida_get_new_above(struct ida *ida, int starting_id, int *p_id) if (t < 0) return _idr_rc_to_errno(t); - if (t * IDA_BITMAP_BITS >= MAX_ID_BIT) + if (t * IDA_BITMAP_BITS >= MAX_IDR_BIT) return -ENOSPC; if (t != idr_id) @@ -827,7 +827,7 @@ int ida_get_new_above(struct ida *ida, int starting_id, int *p_id) } id = idr_id * IDA_BITMAP_BITS + t; - if (id >= MAX_ID_BIT) + if (id >= MAX_IDR_BIT) return -ENOSPC; __set_bit(t, bitmap->bitmap); -- cgit v1.2.3-70-g09d2 From ca279cf1065fb689abea1dc7d8c11787729bb185 Mon Sep 17 00:00:00 2001 From: Benjamin Gaignard Date: Thu, 4 Oct 2012 17:13:20 -0700 Subject: genalloc: make it possible to use a custom allocation algorithm Premit use of another algorithm than the default first-fit one. For example a custom algorithm could be used to manage alignment requirements. As I can't predict all the possible requirements/needs for all allocation uses cases, I add a "free" field 'void *data' to pass any needed information to the allocation function. For example 'data' could be used to handle a structure where you store the alignment, the expected memory bank, the requester device, or any information that could influence the allocation algorithm. An usage example may look like this: struct my_pool_constraints { int align; int bank; ... }; unsigned long my_custom_algo(unsigned long *map, unsigned long size, unsigned long start, unsigned int nr, void *data) { struct my_pool_constraints *constraints = data; ... deal with allocation contraints ... return the index in bitmap where perform the allocation } void create_my_pool() { struct my_pool_constraints c; struct gen_pool *pool = gen_pool_create(...); gen_pool_add(pool, ...); gen_pool_set_algo(pool, my_custom_algo, &c); } Add of best-fit algorithm function: most of the time best-fit is slower then first-fit but memory fragmentation is lower. The random buffer allocation/free tests don't show any arithmetic relation between the allocation time and fragmentation but the best-fit algorithm is sometime able to perform the allocation when the first-fit can't. This new algorithm help to remove static allocations on ESRAM, a small but fast on-chip RAM of few KB, used for high-performance uses cases like DMA linked lists, graphic accelerators, encoders/decoders. On the Ux500 (in the ARM tree) we have define 5 ESRAM banks of 128 KB each and use of static allocations becomes unmaintainable: cd arch/arm/mach-ux500 && grep -r ESRAM . ./include/mach/db8500-regs.h:/* Base address and bank offsets for ESRAM */ ./include/mach/db8500-regs.h:#define U8500_ESRAM_BASE 0x40000000 ./include/mach/db8500-regs.h:#define U8500_ESRAM_BANK_SIZE 0x00020000 ./include/mach/db8500-regs.h:#define U8500_ESRAM_BANK0 U8500_ESRAM_BASE ./include/mach/db8500-regs.h:#define U8500_ESRAM_BANK1 (U8500_ESRAM_BASE + U8500_ESRAM_BANK_SIZE) ./include/mach/db8500-regs.h:#define U8500_ESRAM_BANK2 (U8500_ESRAM_BANK1 + U8500_ESRAM_BANK_SIZE) ./include/mach/db8500-regs.h:#define U8500_ESRAM_BANK3 (U8500_ESRAM_BANK2 + U8500_ESRAM_BANK_SIZE) ./include/mach/db8500-regs.h:#define U8500_ESRAM_BANK4 (U8500_ESRAM_BANK3 + U8500_ESRAM_BANK_SIZE) ./include/mach/db8500-regs.h:#define U8500_ESRAM_DMA_LCPA_OFFSET 0x10000 ./include/mach/db8500-regs.h:#define U8500_DMA_LCPA_BASE (U8500_ESRAM_BANK0 + U8500_ESRAM_DMA_LCPA_OFFSET) ./include/mach/db8500-regs.h:#define U8500_DMA_LCLA_BASE U8500_ESRAM_BANK4 I want to use genalloc to do dynamic allocations but I need to be able to fine tune the allocation algorithm. I my case best-fit algorithm give better results than first-fit, but it will not be true for every use case. Signed-off-by: Benjamin Gaignard Cc: Huang Ying Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/genalloc.h | 27 +++++++++++++++ lib/genalloc.c | 88 +++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 111 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/genalloc.h b/include/linux/genalloc.h index 5e98eeb2af3..dd7c569aaca 100644 --- a/include/linux/genalloc.h +++ b/include/linux/genalloc.h @@ -29,6 +29,20 @@ #ifndef __GENALLOC_H__ #define __GENALLOC_H__ +/** + * Allocation callback function type definition + * @map: Pointer to bitmap + * @size: The bitmap size in bits + * @start: The bitnumber to start searching at + * @nr: The number of zeroed bits we're looking for + * @data: optional additional data used by @genpool_algo_t + */ +typedef unsigned long (*genpool_algo_t)(unsigned long *map, + unsigned long size, + unsigned long start, + unsigned int nr, + void *data); + /* * General purpose special memory pool descriptor. */ @@ -36,6 +50,9 @@ struct gen_pool { spinlock_t lock; struct list_head chunks; /* list of chunks in this pool */ int min_alloc_order; /* minimum allocation order */ + + genpool_algo_t algo; /* allocation function */ + void *data; }; /* @@ -78,4 +95,14 @@ extern void gen_pool_for_each_chunk(struct gen_pool *, void (*)(struct gen_pool *, struct gen_pool_chunk *, void *), void *); extern size_t gen_pool_avail(struct gen_pool *); extern size_t gen_pool_size(struct gen_pool *); + +extern void gen_pool_set_algo(struct gen_pool *pool, genpool_algo_t algo, + void *data); + +extern unsigned long gen_pool_first_fit(unsigned long *map, unsigned long size, + unsigned long start, unsigned int nr, void *data); + +extern unsigned long gen_pool_best_fit(unsigned long *map, unsigned long size, + unsigned long start, unsigned int nr, void *data); + #endif /* __GENALLOC_H__ */ diff --git a/lib/genalloc.c b/lib/genalloc.c index 6bc04aab6ec..ca208a92628 100644 --- a/lib/genalloc.c +++ b/lib/genalloc.c @@ -152,6 +152,8 @@ struct gen_pool *gen_pool_create(int min_alloc_order, int nid) spin_lock_init(&pool->lock); INIT_LIST_HEAD(&pool->chunks); pool->min_alloc_order = min_alloc_order; + pool->algo = gen_pool_first_fit; + pool->data = NULL; } return pool; } @@ -255,8 +257,9 @@ EXPORT_SYMBOL(gen_pool_destroy); * @size: number of bytes to allocate from the pool * * Allocate the requested number of bytes from the specified pool. - * Uses a first-fit algorithm. Can not be used in NMI handler on - * architectures without NMI-safe cmpxchg implementation. + * Uses the pool allocation function (with first-fit algorithm by default). + * Can not be used in NMI handler on architectures without + * NMI-safe cmpxchg implementation. */ unsigned long gen_pool_alloc(struct gen_pool *pool, size_t size) { @@ -280,8 +283,8 @@ unsigned long gen_pool_alloc(struct gen_pool *pool, size_t size) end_bit = (chunk->end_addr - chunk->start_addr) >> order; retry: - start_bit = bitmap_find_next_zero_area(chunk->bits, end_bit, - start_bit, nbits, 0); + start_bit = pool->algo(chunk->bits, end_bit, start_bit, nbits, + pool->data); if (start_bit >= end_bit) continue; remain = bitmap_set_ll(chunk->bits, start_bit, nbits); @@ -400,3 +403,80 @@ size_t gen_pool_size(struct gen_pool *pool) return size; } EXPORT_SYMBOL_GPL(gen_pool_size); + +/** + * gen_pool_set_algo - set the allocation algorithm + * @pool: pool to change allocation algorithm + * @algo: custom algorithm function + * @data: additional data used by @algo + * + * Call @algo for each memory allocation in the pool. + * If @algo is NULL use gen_pool_first_fit as default + * memory allocation function. + */ +void gen_pool_set_algo(struct gen_pool *pool, genpool_algo_t algo, void *data) +{ + rcu_read_lock(); + + pool->algo = algo; + if (!pool->algo) + pool->algo = gen_pool_first_fit; + + pool->data = data; + + rcu_read_unlock(); +} +EXPORT_SYMBOL(gen_pool_set_algo); + +/** + * gen_pool_first_fit - find the first available region + * of memory matching the size requirement (no alignment constraint) + * @map: The address to base the search on + * @size: The bitmap size in bits + * @start: The bitnumber to start searching at + * @nr: The number of zeroed bits we're looking for + * @data: additional data - unused + */ +unsigned long gen_pool_first_fit(unsigned long *map, unsigned long size, + unsigned long start, unsigned int nr, void *data) +{ + return bitmap_find_next_zero_area(map, size, start, nr, 0); +} +EXPORT_SYMBOL(gen_pool_first_fit); + +/** + * gen_pool_best_fit - find the best fitting region of memory + * macthing the size requirement (no alignment constraint) + * @map: The address to base the search on + * @size: The bitmap size in bits + * @start: The bitnumber to start searching at + * @nr: The number of zeroed bits we're looking for + * @data: additional data - unused + * + * Iterate over the bitmap to find the smallest free region + * which we can allocate the memory. + */ +unsigned long gen_pool_best_fit(unsigned long *map, unsigned long size, + unsigned long start, unsigned int nr, void *data) +{ + unsigned long start_bit = size; + unsigned long len = size + 1; + unsigned long index; + + index = bitmap_find_next_zero_area(map, size, start, nr, 0); + + while (index < size) { + int next_bit = find_next_bit(map, size, index + nr); + if ((next_bit - index) < len) { + len = next_bit - index; + start_bit = index; + if (len == nr) + return start_bit; + } + index = bitmap_find_next_zero_area(map, size, + next_bit + 1, nr, 0); + } + + return start_bit; +} +EXPORT_SYMBOL(gen_pool_best_fit); -- cgit v1.2.3-70-g09d2 From 03a7beb55b9fad363f0dd33e72ccf2d3e1c2a406 Mon Sep 17 00:00:00 2001 From: "Paton J. Lewis" Date: Thu, 4 Oct 2012 17:13:39 -0700 Subject: epoll: support for disabling items, and a self-test app Enhanced epoll_ctl to support EPOLL_CTL_DISABLE, which disables an epoll item. If epoll_ctl doesn't return -EBUSY in this case, it is then safe to delete the epoll item in a multi-threaded environment. Also added a new test_epoll self- test app to both demonstrate the need for this feature and test it. Signed-off-by: Paton J. Lewis Cc: Alexander Viro Cc: Jason Baron Cc: Paul Holland Cc: Davide Libenzi Cc: Michael Kerrisk Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/eventpoll.c | 38 +++- include/linux/eventpoll.h | 1 + tools/testing/selftests/Makefile | 2 +- tools/testing/selftests/epoll/Makefile | 11 + tools/testing/selftests/epoll/test_epoll.c | 344 +++++++++++++++++++++++++++++ 5 files changed, 392 insertions(+), 4 deletions(-) create mode 100644 tools/testing/selftests/epoll/Makefile create mode 100644 tools/testing/selftests/epoll/test_epoll.c (limited to 'include/linux') diff --git a/fs/eventpoll.c b/fs/eventpoll.c index cd96649bfe6..da72250ddc1 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c @@ -346,7 +346,7 @@ static inline struct epitem *ep_item_from_epqueue(poll_table *p) /* Tells if the epoll_ctl(2) operation needs an event copy from userspace */ static inline int ep_op_has_event(int op) { - return op != EPOLL_CTL_DEL; + return op == EPOLL_CTL_ADD || op == EPOLL_CTL_MOD; } /* Initialize the poll safe wake up structure */ @@ -676,6 +676,34 @@ static int ep_remove(struct eventpoll *ep, struct epitem *epi) return 0; } +/* + * Disables a "struct epitem" in the eventpoll set. Returns -EBUSY if the item + * had no event flags set, indicating that another thread may be currently + * handling that item's events (in the case that EPOLLONESHOT was being + * used). Otherwise a zero result indicates that the item has been disabled + * from receiving events. A disabled item may be re-enabled via + * EPOLL_CTL_MOD. Must be called with "mtx" held. + */ +static int ep_disable(struct eventpoll *ep, struct epitem *epi) +{ + int result = 0; + unsigned long flags; + + spin_lock_irqsave(&ep->lock, flags); + if (epi->event.events & ~EP_PRIVATE_BITS) { + if (ep_is_linked(&epi->rdllink)) + list_del_init(&epi->rdllink); + /* Ensure ep_poll_callback will not add epi back onto ready + list: */ + epi->event.events &= EP_PRIVATE_BITS; + } + else + result = -EBUSY; + spin_unlock_irqrestore(&ep->lock, flags); + + return result; +} + static void ep_free(struct eventpoll *ep) { struct rb_node *rbp; @@ -1020,8 +1048,6 @@ static void ep_rbtree_insert(struct eventpoll *ep, struct epitem *epi) rb_insert_color(&epi->rbn, &ep->rbr); } - - #define PATH_ARR_SIZE 5 /* * These are the number paths of length 1 to 5, that we are allowing to emanate @@ -1787,6 +1813,12 @@ SYSCALL_DEFINE4(epoll_ctl, int, epfd, int, op, int, fd, } else error = -ENOENT; break; + case EPOLL_CTL_DISABLE: + if (epi) + error = ep_disable(ep, epi); + else + error = -ENOENT; + break; } mutex_unlock(&ep->mtx); diff --git a/include/linux/eventpoll.h b/include/linux/eventpoll.h index f4bb378ccf6..41085d0f395 100644 --- a/include/linux/eventpoll.h +++ b/include/linux/eventpoll.h @@ -25,6 +25,7 @@ #define EPOLL_CTL_ADD 1 #define EPOLL_CTL_DEL 2 #define EPOLL_CTL_MOD 3 +#define EPOLL_CTL_DISABLE 4 /* * Request the handling of system wakeup events so as to prevent system suspends diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 85baf11e2ac..43480149119 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -1,4 +1,4 @@ -TARGETS = breakpoints kcmp mqueue vm cpu-hotplug memory-hotplug +TARGETS = breakpoints kcmp mqueue vm cpu-hotplug memory-hotplug epoll all: for TARGET in $(TARGETS); do \ diff --git a/tools/testing/selftests/epoll/Makefile b/tools/testing/selftests/epoll/Makefile new file mode 100644 index 00000000000..19806ed62f5 --- /dev/null +++ b/tools/testing/selftests/epoll/Makefile @@ -0,0 +1,11 @@ +# Makefile for epoll selftests + +all: test_epoll +%: %.c + gcc -pthread -g -o $@ $^ + +run_tests: all + ./test_epoll + +clean: + $(RM) test_epoll diff --git a/tools/testing/selftests/epoll/test_epoll.c b/tools/testing/selftests/epoll/test_epoll.c new file mode 100644 index 00000000000..e0fcff1e833 --- /dev/null +++ b/tools/testing/selftests/epoll/test_epoll.c @@ -0,0 +1,344 @@ +/* + * tools/testing/selftests/epoll/test_epoll.c + * + * Copyright 2012 Adobe Systems Incorporated + * + * 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. + * + * Paton J. Lewis + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * A pointer to an epoll_item_private structure will be stored in the epoll + * item's event structure so that we can get access to the epoll_item_private + * data after calling epoll_wait: + */ +struct epoll_item_private { + int index; /* Position of this struct within the epoll_items array. */ + int fd; + uint32_t events; + pthread_mutex_t mutex; /* Guards the following variables... */ + int stop; + int status; /* Stores any error encountered while handling item. */ + /* The following variable allows us to test whether we have encountered + a problem while attempting to cancel and delete the associated + event. When the test program exits, 'deleted' should be exactly + one. If it is greater than one, then the failed test reflects a real + world situation where we would have tried to access the epoll item's + private data after deleting it: */ + int deleted; +}; + +struct epoll_item_private *epoll_items; + +/* + * Delete the specified item from the epoll set. In a real-world secneario this + * is where we would free the associated data structure, but in this testing + * environment we retain the structure so that we can test for double-deletion: + */ +void delete_item(int index) +{ + __sync_fetch_and_add(&epoll_items[index].deleted, 1); +} + +/* + * A pointer to a read_thread_data structure will be passed as the argument to + * each read thread: + */ +struct read_thread_data { + int stop; + int status; /* Indicates any error encountered by the read thread. */ + int epoll_set; +}; + +/* + * The function executed by the read threads: + */ +void *read_thread_function(void *function_data) +{ + struct read_thread_data *thread_data = + (struct read_thread_data *)function_data; + struct epoll_event event_data; + struct epoll_item_private *item_data; + char socket_data; + + /* Handle events until we encounter an error or this thread's 'stop' + condition is set: */ + while (1) { + int result = epoll_wait(thread_data->epoll_set, + &event_data, + 1, /* Number of desired events */ + 1000); /* Timeout in ms */ + if (result < 0) { + /* Breakpoints signal all threads. Ignore that while + debugging: */ + if (errno == EINTR) + continue; + thread_data->status = errno; + return 0; + } else if (thread_data->stop) + return 0; + else if (result == 0) /* Timeout */ + continue; + + /* We need the mutex here because checking for the stop + condition and re-enabling the epoll item need to be done + together as one atomic operation when EPOLL_CTL_DISABLE is + available: */ + item_data = (struct epoll_item_private *)event_data.data.ptr; + pthread_mutex_lock(&item_data->mutex); + + /* Remove the item from the epoll set if we want to stop + handling that event: */ + if (item_data->stop) + delete_item(item_data->index); + else { + /* Clear the data that was written to the other end of + our non-blocking socket: */ + do { + if (read(item_data->fd, &socket_data, 1) < 1) { + if ((errno == EAGAIN) || + (errno == EWOULDBLOCK)) + break; + else + goto error_unlock; + } + } while (item_data->events & EPOLLET); + + /* The item was one-shot, so re-enable it: */ + event_data.events = item_data->events; + if (epoll_ctl(thread_data->epoll_set, + EPOLL_CTL_MOD, + item_data->fd, + &event_data) < 0) + goto error_unlock; + } + + pthread_mutex_unlock(&item_data->mutex); + } + +error_unlock: + thread_data->status = item_data->status = errno; + pthread_mutex_unlock(&item_data->mutex); + return 0; +} + +/* + * A pointer to a write_thread_data structure will be passed as the argument to + * the write thread: + */ +struct write_thread_data { + int stop; + int status; /* Indicates any error encountered by the write thread. */ + int n_fds; + int *fds; +}; + +/* + * The function executed by the write thread. It writes a single byte to each + * socket in turn until the stop condition for this thread is set. If writing to + * a socket would block (i.e. errno was EAGAIN), we leave that socket alone for + * the moment and just move on to the next socket in the list. We don't care + * about the order in which we deliver events to the epoll set. In fact we don't + * care about the data we're writing to the pipes at all; we just want to + * trigger epoll events: + */ +void *write_thread_function(void *function_data) +{ + const char data = 'X'; + int index; + struct write_thread_data *thread_data = + (struct write_thread_data *)function_data; + while (!write_thread_data->stop) + for (index = 0; + !thread_data->stop && (index < thread_data->n_fds); + ++index) + if ((write(thread_data->fds[index], &data, 1) < 1) && + (errno != EAGAIN) && + (errno != EWOULDBLOCK)) { + write_thread_data->status = errno; + return; + } +} + +/* + * Arguments are currently ignored: + */ +int main(int argc, char **argv) +{ + const int n_read_threads = 100; + const int n_epoll_items = 500; + int index; + int epoll_set = epoll_create1(0); + struct write_thread_data write_thread_data = { + 0, 0, n_epoll_items, malloc(n_epoll_items * sizeof(int)) + }; + struct read_thread_data *read_thread_data = + malloc(n_read_threads * sizeof(struct read_thread_data)); + pthread_t *read_threads = malloc(n_read_threads * sizeof(pthread_t)); + pthread_t write_thread; + + printf("-----------------\n"); + printf("Runing test_epoll\n"); + printf("-----------------\n"); + + epoll_items = malloc(n_epoll_items * sizeof(struct epoll_item_private)); + + if (epoll_set < 0 || epoll_items == 0 || write_thread_data.fds == 0 || + read_thread_data == 0 || read_threads == 0) + goto error; + + if (sysconf(_SC_NPROCESSORS_ONLN) < 2) { + printf("Error: please run this test on a multi-core system.\n"); + goto error; + } + + /* Create the socket pairs and epoll items: */ + for (index = 0; index < n_epoll_items; ++index) { + int socket_pair[2]; + struct epoll_event event_data; + if (socketpair(AF_UNIX, + SOCK_STREAM | SOCK_NONBLOCK, + 0, + socket_pair) < 0) + goto error; + write_thread_data.fds[index] = socket_pair[0]; + epoll_items[index].index = index; + epoll_items[index].fd = socket_pair[1]; + if (pthread_mutex_init(&epoll_items[index].mutex, NULL) != 0) + goto error; + /* We always use EPOLLONESHOT because this test is currently + structured to demonstrate the need for EPOLL_CTL_DISABLE, + which only produces useful information in the EPOLLONESHOT + case (without EPOLLONESHOT, calling epoll_ctl with + EPOLL_CTL_DISABLE will never return EBUSY). If support for + testing events without EPOLLONESHOT is desired, it should + probably be implemented in a separate unit test. */ + epoll_items[index].events = EPOLLIN | EPOLLONESHOT; + if (index < n_epoll_items / 2) + epoll_items[index].events |= EPOLLET; + epoll_items[index].stop = 0; + epoll_items[index].status = 0; + epoll_items[index].deleted = 0; + event_data.events = epoll_items[index].events; + event_data.data.ptr = &epoll_items[index]; + if (epoll_ctl(epoll_set, + EPOLL_CTL_ADD, + epoll_items[index].fd, + &event_data) < 0) + goto error; + } + + /* Create and start the read threads: */ + for (index = 0; index < n_read_threads; ++index) { + read_thread_data[index].stop = 0; + read_thread_data[index].status = 0; + read_thread_data[index].epoll_set = epoll_set; + if (pthread_create(&read_threads[index], + NULL, + read_thread_function, + &read_thread_data[index]) != 0) + goto error; + } + + if (pthread_create(&write_thread, + NULL, + write_thread_function, + &write_thread_data) != 0) + goto error; + + /* Cancel all event pollers: */ +#ifdef EPOLL_CTL_DISABLE + for (index = 0; index < n_epoll_items; ++index) { + pthread_mutex_lock(&epoll_items[index].mutex); + ++epoll_items[index].stop; + if (epoll_ctl(epoll_set, + EPOLL_CTL_DISABLE, + epoll_items[index].fd, + NULL) == 0) + delete_item(index); + else if (errno != EBUSY) { + pthread_mutex_unlock(&epoll_items[index].mutex); + goto error; + } + /* EBUSY means events were being handled; allow the other thread + to delete the item. */ + pthread_mutex_unlock(&epoll_items[index].mutex); + } +#else + for (index = 0; index < n_epoll_items; ++index) { + pthread_mutex_lock(&epoll_items[index].mutex); + ++epoll_items[index].stop; + pthread_mutex_unlock(&epoll_items[index].mutex); + /* Wait in case a thread running read_thread_function is + currently executing code between epoll_wait and + pthread_mutex_lock with this item. Note that a longer delay + would make double-deletion less likely (at the expense of + performance), but there is no guarantee that any delay would + ever be sufficient. Note also that we delete all event + pollers at once for testing purposes, but in a real-world + environment we are likely to want to be able to cancel event + pollers at arbitrary times. Therefore we can't improve this + situation by just splitting this loop into two loops + (i.e. signal 'stop' for all items, sleep, and then delete all + items). We also can't fix the problem via EPOLL_CTL_DEL + because that command can't prevent the case where some other + thread is executing read_thread_function within the region + mentioned above: */ + usleep(1); + pthread_mutex_lock(&epoll_items[index].mutex); + if (!epoll_items[index].deleted) + delete_item(index); + pthread_mutex_unlock(&epoll_items[index].mutex); + } +#endif + + /* Shut down the read threads: */ + for (index = 0; index < n_read_threads; ++index) + __sync_fetch_and_add(&read_thread_data[index].stop, 1); + for (index = 0; index < n_read_threads; ++index) { + if (pthread_join(read_threads[index], NULL) != 0) + goto error; + if (read_thread_data[index].status) + goto error; + } + + /* Shut down the write thread: */ + __sync_fetch_and_add(&write_thread_data.stop, 1); + if ((pthread_join(write_thread, NULL) != 0) || write_thread_data.status) + goto error; + + /* Check for final error conditions: */ + for (index = 0; index < n_epoll_items; ++index) { + if (epoll_items[index].status != 0) + goto error; + if (pthread_mutex_destroy(&epoll_items[index].mutex) < 0) + goto error; + } + for (index = 0; index < n_epoll_items; ++index) + if (epoll_items[index].deleted != 1) { + printf("Error: item data deleted %1d times.\n", + epoll_items[index].deleted); + goto error; + } + + printf("[PASS]\n"); + return 0; + + error: + printf("[FAIL]\n"); + return errno; +} -- cgit v1.2.3-70-g09d2 From 7418a1198991beb5bf6656b2ac0bcbde378c5539 Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Thu, 4 Oct 2012 17:13:47 -0700 Subject: rtc: add Dallas DS2404 driver [akpm@linux-foundation.org: fix warning] Signed-off-by: Sven Schnelle Cc: Alessandro Zummo Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/Kconfig | 9 ++ drivers/rtc/Makefile | 1 + drivers/rtc/rtc-ds2404.c | 303 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/rtc-ds2404.h | 20 +++ 4 files changed, 333 insertions(+) create mode 100644 drivers/rtc/rtc-ds2404.c create mode 100644 include/linux/rtc-ds2404.h (limited to 'include/linux') diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index a4a805c273b..d4681c97f7c 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -660,6 +660,15 @@ config RTC_DRV_V3020 This driver can also be built as a module. If so, the module will be called rtc-v3020. +config RTC_DRV_DS2404 + tristate "Dallas DS2404" + help + If you say yes here you get support for the + Dallas DS2404 RTC chip. + + This driver can also be built as a module. If so, the module + will be called rtc-ds2404. + config RTC_DRV_WM831X tristate "Wolfson Microelectronics WM831x RTC" depends on MFD_WM831X diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 0d5b2b66f90..7ecdb2983dd 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -43,6 +43,7 @@ obj-$(CONFIG_RTC_DRV_DS1511) += rtc-ds1511.o obj-$(CONFIG_RTC_DRV_DS1553) += rtc-ds1553.o obj-$(CONFIG_RTC_DRV_DS1672) += rtc-ds1672.o obj-$(CONFIG_RTC_DRV_DS1742) += rtc-ds1742.o +obj-$(CONFIG_RTC_DRV_DS2404) += rtc-ds2404.o obj-$(CONFIG_RTC_DRV_DS3232) += rtc-ds3232.o obj-$(CONFIG_RTC_DRV_DS3234) += rtc-ds3234.o obj-$(CONFIG_RTC_DRV_EFI) += rtc-efi.o diff --git a/drivers/rtc/rtc-ds2404.c b/drivers/rtc/rtc-ds2404.c new file mode 100644 index 00000000000..5ea9df7c8c3 --- /dev/null +++ b/drivers/rtc/rtc-ds2404.c @@ -0,0 +1,303 @@ +/* + * Copyright (C) 2012 Sven Schnelle + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define DS2404_STATUS_REG 0x200 +#define DS2404_CONTROL_REG 0x201 +#define DS2404_RTC_REG 0x202 + +#define DS2404_WRITE_SCRATCHPAD_CMD 0x0f +#define DS2404_READ_SCRATCHPAD_CMD 0xaa +#define DS2404_COPY_SCRATCHPAD_CMD 0x55 +#define DS2404_READ_MEMORY_CMD 0xf0 + +struct ds2404; + +struct ds2404_chip_ops { + int (*map_io)(struct ds2404 *chip, struct platform_device *pdev, + struct ds2404_platform_data *pdata); + void (*unmap_io)(struct ds2404 *chip); +}; + +#define DS2404_RST 0 +#define DS2404_CLK 1 +#define DS2404_DQ 2 + +struct ds2404_gpio { + const char *name; + unsigned int gpio; +}; + +struct ds2404 { + struct ds2404_gpio *gpio; + struct ds2404_chip_ops *ops; + struct rtc_device *rtc; +}; + +static struct ds2404_gpio ds2404_gpio[] = { + { "RTC RST", 0 }, + { "RTC CLK", 0 }, + { "RTC DQ", 0 }, +}; + +static int ds2404_gpio_map(struct ds2404 *chip, struct platform_device *pdev, + struct ds2404_platform_data *pdata) +{ + int i, err; + + ds2404_gpio[DS2404_RST].gpio = pdata->gpio_rst; + ds2404_gpio[DS2404_CLK].gpio = pdata->gpio_clk; + ds2404_gpio[DS2404_DQ].gpio = pdata->gpio_dq; + + for (i = 0; i < ARRAY_SIZE(ds2404_gpio); i++) { + err = gpio_request(ds2404_gpio[i].gpio, ds2404_gpio[i].name); + if (err) { + printk(KERN_ERR "error mapping gpio %s: %d\n", + ds2404_gpio[i].name, err); + goto err_request; + } + if (i != DS2404_DQ) + gpio_direction_output(ds2404_gpio[i].gpio, 1); + } + + chip->gpio = ds2404_gpio; + return 0; + +err_request: + while (--i >= 0) + gpio_free(ds2404_gpio[i].gpio); + return err; +} + +static void ds2404_gpio_unmap(struct ds2404 *chip) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ds2404_gpio); i++) + gpio_free(ds2404_gpio[i].gpio); +} + +static struct ds2404_chip_ops ds2404_gpio_ops = { + .map_io = ds2404_gpio_map, + .unmap_io = ds2404_gpio_unmap, +}; + +static void ds2404_reset(struct device *dev) +{ + gpio_set_value(ds2404_gpio[DS2404_RST].gpio, 0); + udelay(1000); + gpio_set_value(ds2404_gpio[DS2404_RST].gpio, 1); + gpio_set_value(ds2404_gpio[DS2404_CLK].gpio, 0); + gpio_direction_output(ds2404_gpio[DS2404_DQ].gpio, 0); + udelay(10); +} + +static void ds2404_write_byte(struct device *dev, u8 byte) +{ + int i; + + gpio_direction_output(ds2404_gpio[DS2404_DQ].gpio, 1); + for (i = 0; i < 8; i++) { + gpio_set_value(ds2404_gpio[DS2404_DQ].gpio, byte & (1 << i)); + udelay(10); + gpio_set_value(ds2404_gpio[DS2404_CLK].gpio, 1); + udelay(10); + gpio_set_value(ds2404_gpio[DS2404_CLK].gpio, 0); + udelay(10); + } +} + +static u8 ds2404_read_byte(struct device *dev) +{ + int i; + u8 ret = 0; + + gpio_direction_input(ds2404_gpio[DS2404_DQ].gpio); + + for (i = 0; i < 8; i++) { + gpio_set_value(ds2404_gpio[DS2404_CLK].gpio, 0); + udelay(10); + if (gpio_get_value(ds2404_gpio[DS2404_DQ].gpio)) + ret |= 1 << i; + gpio_set_value(ds2404_gpio[DS2404_CLK].gpio, 1); + udelay(10); + } + return ret; +} + +static void ds2404_read_memory(struct device *dev, u16 offset, + int length, u8 *out) +{ + ds2404_reset(dev); + ds2404_write_byte(dev, DS2404_READ_MEMORY_CMD); + ds2404_write_byte(dev, offset & 0xff); + ds2404_write_byte(dev, (offset >> 8) & 0xff); + while (length--) + *out++ = ds2404_read_byte(dev); +} + +static void ds2404_write_memory(struct device *dev, u16 offset, + int length, u8 *out) +{ + int i; + u8 ta01, ta02, es; + + ds2404_reset(dev); + ds2404_write_byte(dev, DS2404_WRITE_SCRATCHPAD_CMD); + ds2404_write_byte(dev, offset & 0xff); + ds2404_write_byte(dev, (offset >> 8) & 0xff); + + for (i = 0; i < length; i++) + ds2404_write_byte(dev, out[i]); + + ds2404_reset(dev); + ds2404_write_byte(dev, DS2404_READ_SCRATCHPAD_CMD); + + ta01 = ds2404_read_byte(dev); + ta02 = ds2404_read_byte(dev); + es = ds2404_read_byte(dev); + + for (i = 0; i < length; i++) { + if (out[i] != ds2404_read_byte(dev)) { + printk(KERN_ERR "read invalid data\n"); + return; + } + } + + ds2404_reset(dev); + ds2404_write_byte(dev, DS2404_COPY_SCRATCHPAD_CMD); + ds2404_write_byte(dev, ta01); + ds2404_write_byte(dev, ta02); + ds2404_write_byte(dev, es); + + gpio_direction_input(ds2404_gpio[DS2404_DQ].gpio); + while (gpio_get_value(ds2404_gpio[DS2404_DQ].gpio)) + ; +} + +static void ds2404_enable_osc(struct device *dev) +{ + u8 in[1] = { 0x10 }; /* enable oscillator */ + ds2404_write_memory(dev, 0x201, 1, in); +} + +static int ds2404_read_time(struct device *dev, struct rtc_time *dt) +{ + unsigned long time = 0; + + ds2404_read_memory(dev, 0x203, 4, (u8 *)&time); + time = le32_to_cpu(time); + + rtc_time_to_tm(time, dt); + return rtc_valid_tm(dt); +} + +static int ds2404_set_mmss(struct device *dev, unsigned long secs) +{ + u32 time = cpu_to_le32(secs); + ds2404_write_memory(dev, 0x203, 4, (u8 *)&time); + return 0; +} + +static const struct rtc_class_ops ds2404_rtc_ops = { + .read_time = ds2404_read_time, + .set_mmss = ds2404_set_mmss, +}; + +static int rtc_probe(struct platform_device *pdev) +{ + struct ds2404_platform_data *pdata = pdev->dev.platform_data; + struct ds2404 *chip; + int retval = -EBUSY; + + chip = kzalloc(sizeof(struct ds2404), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->ops = &ds2404_gpio_ops; + + retval = chip->ops->map_io(chip, pdev, pdata); + if (retval) + goto err_chip; + + dev_info(&pdev->dev, "using GPIOs RST:%d, CLK:%d, DQ:%d\n", + chip->gpio[DS2404_RST].gpio, chip->gpio[DS2404_CLK].gpio, + chip->gpio[DS2404_DQ].gpio); + + platform_set_drvdata(pdev, chip); + + chip->rtc = rtc_device_register("ds2404", + &pdev->dev, &ds2404_rtc_ops, THIS_MODULE); + if (IS_ERR(chip->rtc)) { + retval = PTR_ERR(chip->rtc); + goto err_io; + } + + ds2404_enable_osc(&pdev->dev); + return 0; + +err_io: + chip->ops->unmap_io(chip); +err_chip: + kfree(chip); + return retval; +} + +static int rtc_remove(struct platform_device *dev) +{ + struct ds2404 *chip = platform_get_drvdata(dev); + struct rtc_device *rtc = chip->rtc; + + if (rtc) + rtc_device_unregister(rtc); + + chip->ops->unmap_io(chip); + kfree(chip); + + return 0; +} + +static struct platform_driver rtc_device_driver = { + .probe = rtc_probe, + .remove = rtc_remove, + .driver = { + .name = "ds2404", + .owner = THIS_MODULE, + }, +}; + +static __init int ds2404_init(void) +{ + return platform_driver_register(&rtc_device_driver); +} + +static __exit void ds2404_exit(void) +{ + platform_driver_unregister(&rtc_device_driver); +} + +module_init(ds2404_init); +module_exit(ds2404_exit); + +MODULE_DESCRIPTION("DS2404 RTC"); +MODULE_AUTHOR("Sven Schnelle"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:ds2404"); diff --git a/include/linux/rtc-ds2404.h b/include/linux/rtc-ds2404.h new file mode 100644 index 00000000000..22c53825528 --- /dev/null +++ b/include/linux/rtc-ds2404.h @@ -0,0 +1,20 @@ +/* + * ds2404.h - platform data structure for the DS2404 RTC. + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2012 Sven Schnelle + */ + +#ifndef __LINUX_DS2404_H +#define __LINUX_DS2404_H + +struct ds2404_platform_data { + + unsigned int gpio_rst; + unsigned int gpio_clk; + unsigned int gpio_dq; +}; +#endif -- cgit v1.2.3-70-g09d2 From 0e783980b84e7253ba98552d092d1f45ed262d66 Mon Sep 17 00:00:00 2001 From: Venu Byravarasu Date: Thu, 4 Oct 2012 17:13:55 -0700 Subject: rtc: tps65910: add RTC driver for TPS65910 PMIC RTC TPS65910 PMIC is a MFD with RTC as one of the device. Adding RTC driver for supporting RTC device present inside TPS65910 PMIC. Only support for RTC alarm is implemented as part of this patch. Signed-off-by: Venu Byravarasu Cc: Alessandro Zummo Cc: Mark Brown Cc: Samuel Ortiz Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/Kconfig | 10 ++ drivers/rtc/Makefile | 1 + drivers/rtc/rtc-tps65910.c | 352 +++++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/tps65910.h | 10 ++ 4 files changed, 373 insertions(+) create mode 100644 drivers/rtc/rtc-tps65910.c (limited to 'include/linux') diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index f87bfea6718..6309df4e265 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -335,6 +335,16 @@ config RTC_DRV_TWL4030 This driver can also be built as a module. If so, the module will be called rtc-twl. +config RTC_DRV_TPS65910 + tristate "TI TPS65910 RTC driver" + depends on RTC_CLASS && MFD_TPS65910 + help + If you say yes here you get support for the RTC on the + TPS65910 chips. + + This driver can also be built as a module. If so, the module + will be called rtc-tps65910. + config RTC_DRV_S35390A tristate "Seiko Instruments S-35390A" select BITREVERSE diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 6056f71033d..5c8f106237d 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -107,6 +107,7 @@ obj-$(CONFIG_RTC_DRV_TEGRA) += rtc-tegra.o obj-$(CONFIG_RTC_DRV_TEST) += rtc-test.o obj-$(CONFIG_RTC_DRV_TILE) += rtc-tile.o obj-$(CONFIG_RTC_DRV_TWL4030) += rtc-twl.o +obj-$(CONFIG_RTC_DRV_TPS65910) += rtc-tps65910.o obj-$(CONFIG_RTC_DRV_TX4939) += rtc-tx4939.o obj-$(CONFIG_RTC_DRV_V3020) += rtc-v3020.o obj-$(CONFIG_RTC_DRV_VR41XX) += rtc-vr41xx.o diff --git a/drivers/rtc/rtc-tps65910.c b/drivers/rtc/rtc-tps65910.c new file mode 100644 index 00000000000..7c4ca860574 --- /dev/null +++ b/drivers/rtc/rtc-tps65910.c @@ -0,0 +1,352 @@ +/* + * rtc-tps65910.c -- TPS65910 Real Time Clock interface + * + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + * Author: Venu Byravarasu + * + * Based on original TI driver rtc-twl.c + * Copyright (C) 2007 MontaVista Software, Inc + * Author: Alexandre Rusev + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct tps65910_rtc { + struct rtc_device *rtc; + /* To store the list of enabled interrupts */ + u32 irqstat; +}; + +/* Total number of RTC registers needed to set time*/ +#define NUM_TIME_REGS (TPS65910_YEARS - TPS65910_SECONDS + 1) + +static int tps65910_rtc_alarm_irq_enable(struct device *dev, unsigned enabled) +{ + struct tps65910 *tps = dev_get_drvdata(dev->parent); + u8 val = 0; + + if (enabled) + val = TPS65910_RTC_INTERRUPTS_IT_ALARM; + + return regmap_write(tps->regmap, TPS65910_RTC_INTERRUPTS, val); +} + +/* + * Gets current tps65910 RTC time and date parameters. + * + * The RTC's time/alarm representation is not what gmtime(3) requires + * Linux to use: + * + * - Months are 1..12 vs Linux 0-11 + * - Years are 0..99 vs Linux 1900..N (we assume 21st century) + */ +static int tps65910_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + unsigned char rtc_data[NUM_TIME_REGS]; + struct tps65910 *tps = dev_get_drvdata(dev->parent); + int ret; + + /* Copy RTC counting registers to static registers or latches */ + ret = regmap_update_bits(tps->regmap, TPS65910_RTC_CTRL, + TPS65910_RTC_CTRL_GET_TIME, TPS65910_RTC_CTRL_GET_TIME); + if (ret < 0) { + dev_err(dev, "RTC CTRL reg update failed with err:%d\n", ret); + return ret; + } + + ret = regmap_bulk_read(tps->regmap, TPS65910_SECONDS, rtc_data, + NUM_TIME_REGS); + if (ret < 0) { + dev_err(dev, "reading from RTC failed with err:%d\n", ret); + return ret; + } + + tm->tm_sec = bcd2bin(rtc_data[0]); + tm->tm_min = bcd2bin(rtc_data[1]); + tm->tm_hour = bcd2bin(rtc_data[2]); + tm->tm_mday = bcd2bin(rtc_data[3]); + tm->tm_mon = bcd2bin(rtc_data[4]) - 1; + tm->tm_year = bcd2bin(rtc_data[5]) + 100; + + return ret; +} + +static int tps65910_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + unsigned char rtc_data[NUM_TIME_REGS]; + struct tps65910 *tps = dev_get_drvdata(dev->parent); + int ret; + + rtc_data[0] = bin2bcd(tm->tm_sec); + rtc_data[1] = bin2bcd(tm->tm_min); + rtc_data[2] = bin2bcd(tm->tm_hour); + rtc_data[3] = bin2bcd(tm->tm_mday); + rtc_data[4] = bin2bcd(tm->tm_mon + 1); + rtc_data[5] = bin2bcd(tm->tm_year - 100); + + /* Stop RTC while updating the RTC time registers */ + ret = regmap_update_bits(tps->regmap, TPS65910_RTC_CTRL, + TPS65910_RTC_CTRL_STOP_RTC, 0); + if (ret < 0) { + dev_err(dev, "RTC stop failed with err:%d\n", ret); + return ret; + } + + /* update all the time registers in one shot */ + ret = regmap_bulk_write(tps->regmap, TPS65910_SECONDS, rtc_data, + NUM_TIME_REGS); + if (ret < 0) { + dev_err(dev, "rtc_set_time error %d\n", ret); + return ret; + } + + /* Start back RTC */ + ret = regmap_update_bits(tps->regmap, TPS65910_RTC_CTRL, + TPS65910_RTC_CTRL_STOP_RTC, 1); + if (ret < 0) + dev_err(dev, "RTC start failed with err:%d\n", ret); + + return ret; +} + +/* + * Gets current tps65910 RTC alarm time. + */ +static int tps65910_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + unsigned char alarm_data[NUM_TIME_REGS]; + u32 int_val; + struct tps65910 *tps = dev_get_drvdata(dev->parent); + int ret; + + ret = regmap_bulk_read(tps->regmap, TPS65910_SECONDS, alarm_data, + NUM_TIME_REGS); + if (ret < 0) { + dev_err(dev, "rtc_read_alarm error %d\n", ret); + return ret; + } + + alm->time.tm_sec = bcd2bin(alarm_data[0]); + alm->time.tm_min = bcd2bin(alarm_data[1]); + alm->time.tm_hour = bcd2bin(alarm_data[2]); + alm->time.tm_mday = bcd2bin(alarm_data[3]); + alm->time.tm_mon = bcd2bin(alarm_data[4]) - 1; + alm->time.tm_year = bcd2bin(alarm_data[5]) + 100; + + ret = regmap_read(tps->regmap, TPS65910_RTC_INTERRUPTS, &int_val); + if (ret < 0) + return ret; + + if (int_val & TPS65910_RTC_INTERRUPTS_IT_ALARM) + alm->enabled = 1; + + return ret; +} + +static int tps65910_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + unsigned char alarm_data[NUM_TIME_REGS]; + struct tps65910 *tps = dev_get_drvdata(dev->parent); + int ret; + + ret = tps65910_rtc_alarm_irq_enable(dev, 0); + if (ret) + return ret; + + alarm_data[0] = bin2bcd(alm->time.tm_sec); + alarm_data[1] = bin2bcd(alm->time.tm_min); + alarm_data[2] = bin2bcd(alm->time.tm_hour); + alarm_data[3] = bin2bcd(alm->time.tm_mday); + alarm_data[4] = bin2bcd(alm->time.tm_mon + 1); + alarm_data[5] = bin2bcd(alm->time.tm_year - 100); + + /* update all the alarm registers in one shot */ + ret = regmap_bulk_write(tps->regmap, TPS65910_ALARM_SECONDS, + alarm_data, NUM_TIME_REGS); + if (ret) { + dev_err(dev, "rtc_set_alarm error %d\n", ret); + return ret; + } + + if (alm->enabled) + ret = tps65910_rtc_alarm_irq_enable(dev, 1); + + return ret; +} + +static irqreturn_t tps65910_rtc_interrupt(int irq, void *rtc) +{ + struct device *dev = rtc; + unsigned long events = 0; + struct tps65910 *tps = dev_get_drvdata(dev->parent); + struct tps65910_rtc *tps_rtc = dev_get_drvdata(dev); + int ret; + u32 rtc_reg; + + ret = regmap_read(tps->regmap, TPS65910_RTC_STATUS, &rtc_reg); + if (ret) + return IRQ_NONE; + + if (rtc_reg & TPS65910_RTC_STATUS_ALARM) + events = RTC_IRQF | RTC_AF; + + ret = regmap_write(tps->regmap, TPS65910_RTC_STATUS, rtc_reg); + if (ret) + return IRQ_NONE; + + /* Notify RTC core on event */ + rtc_update_irq(tps_rtc->rtc, 1, events); + + return IRQ_HANDLED; +} + +static const struct rtc_class_ops tps65910_rtc_ops = { + .read_time = tps65910_rtc_read_time, + .set_time = tps65910_rtc_set_time, + .read_alarm = tps65910_rtc_read_alarm, + .set_alarm = tps65910_rtc_set_alarm, + .alarm_irq_enable = tps65910_rtc_alarm_irq_enable, +}; + +static int __devinit tps65910_rtc_probe(struct platform_device *pdev) +{ + struct tps65910 *tps65910 = NULL; + struct tps65910_rtc *tps_rtc = NULL; + struct tps65910_board *pmic_plat_data; + int ret; + int irq; + u32 rtc_reg; + + tps65910 = dev_get_drvdata(pdev->dev.parent); + + tps_rtc = devm_kzalloc(&pdev->dev, sizeof(struct tps65910_rtc), + GFP_KERNEL); + if (!tps_rtc) + return -ENOMEM; + + /* Clear pending interrupts */ + ret = regmap_read(tps65910->regmap, TPS65910_RTC_STATUS, &rtc_reg); + if (ret < 0) + return ret; + + ret = regmap_write(tps65910->regmap, TPS65910_RTC_STATUS, rtc_reg); + if (ret < 0) + return ret; + + dev_dbg(&pdev->dev, "Enabling rtc-tps65910.\n"); + rtc_reg = TPS65910_RTC_CTRL_STOP_RTC; + ret = regmap_write(tps65910->regmap, TPS65910_RTC_CTRL, rtc_reg); + if (ret < 0) + return ret; + + pmic_plat_data = dev_get_platdata(tps65910->dev); + irq = pmic_plat_data->irq_base; + if (irq <= 0) { + dev_warn(&pdev->dev, "Wake up is not possible as irq = %d\n", + irq); + return ret; + } + + irq += TPS65910_IRQ_RTC_ALARM; + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, + tps65910_rtc_interrupt, IRQF_TRIGGER_LOW, + "rtc-tps65910", &pdev->dev); + if (ret < 0) { + dev_err(&pdev->dev, "IRQ is not free.\n"); + return ret; + } + device_init_wakeup(&pdev->dev, 1); + + tps_rtc->rtc = rtc_device_register(pdev->name, &pdev->dev, + &tps65910_rtc_ops, THIS_MODULE); + if (IS_ERR(tps_rtc->rtc)) { + ret = PTR_ERR(tps_rtc->rtc); + dev_err(&pdev->dev, "RTC device register: err %d\n", ret); + return ret; + } + + platform_set_drvdata(pdev, tps_rtc); + + return 0; +} + +/* + * Disable tps65910 RTC interrupts. + * Sets status flag to free. + */ +static int __devexit tps65910_rtc_remove(struct platform_device *pdev) +{ + /* leave rtc running, but disable irqs */ + struct rtc_device *rtc = platform_get_drvdata(pdev); + + tps65910_rtc_alarm_irq_enable(&rtc->dev, 0); + + rtc_device_unregister(rtc); + return 0; +} + +#ifdef CONFIG_PM_SLEEP + +static int tps65910_rtc_suspend(struct device *dev) +{ + struct tps65910 *tps = dev_get_drvdata(dev->parent); + u8 alarm = TPS65910_RTC_INTERRUPTS_IT_ALARM; + int ret; + + /* Store current list of enabled interrupts*/ + ret = regmap_read(tps->regmap, TPS65910_RTC_INTERRUPTS, + &tps->rtc->irqstat); + if (ret < 0) + return ret; + + /* Enable RTC ALARM interrupt only */ + return regmap_write(tps->regmap, TPS65910_RTC_INTERRUPTS, alarm); +} + +static int tps65910_rtc_resume(struct device *dev) +{ + struct tps65910 *tps = dev_get_drvdata(dev->parent); + + /* Restore list of enabled interrupts before suspend */ + return regmap_write(tps->regmap, TPS65910_RTC_INTERRUPTS, + tps->rtc->irqstat); +} + +static const struct dev_pm_ops tps65910_rtc_pm_ops = { + .suspend = tps65910_rtc_suspend, + .resume = tps65910_rtc_resume, +}; + +#define DEV_PM_OPS (&tps65910_rtc_pm_ops) +#else +#define DEV_PM_OPS NULL +#endif + +static struct platform_driver tps65910_rtc_driver = { + .probe = tps65910_rtc_probe, + .remove = __devexit_p(tps65910_rtc_remove), + .driver = { + .owner = THIS_MODULE, + .name = "tps65910-rtc", + .pm = DEV_PM_OPS, + }, +}; + +module_platform_driver(tps65910_rtc_driver); +MODULE_ALIAS("platform:rtc-tps65910"); +MODULE_AUTHOR("Venu Byravarasu "); +MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/tps65910.h b/include/linux/mfd/tps65910.h index 9bf8767818b..ba2eb25f109 100644 --- a/include/linux/mfd/tps65910.h +++ b/include/linux/mfd/tps65910.h @@ -132,6 +132,16 @@ * */ +/* RTC_CTRL_REG bitfields */ +#define TPS65910_RTC_CTRL_STOP_RTC 0x01 /*0=stop, 1=run */ +#define TPS65910_RTC_CTRL_GET_TIME 0x40 + +/* RTC_STATUS_REG bitfields */ +#define TPS65910_RTC_STATUS_ALARM 0x40 + +/* RTC_INTERRUPTS_REG bitfields */ +#define TPS65910_RTC_INTERRUPTS_EVERY 0x03 +#define TPS65910_RTC_INTERRUPTS_IT_ALARM 0x08 /*Register BCK1 (0x80) register.RegisterDescription */ #define BCK1_BCKUP_MASK 0xFF -- cgit v1.2.3-70-g09d2 From 90829c089ef622e54ce1153eb6dcda9ce98cb7c2 Mon Sep 17 00:00:00 2001 From: Venu Byravarasu Date: Thu, 4 Oct 2012 17:14:04 -0700 Subject: rtc: rc5t583: add ricoh rc5t583 RTC driver Add an RTC driver for the RTC device on Ricoh MFD Rc5t583. Ricoh RTC has 3 types of alarms. The current patch adds support for the Y-Alarm of RC5t583 RTC. Signed-off-by: Venu Byravarasu Cc: Alessandro Zummo Cc: Mark Brown Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/Kconfig | 10 ++ drivers/rtc/Makefile | 1 + drivers/rtc/rtc-rc5t583.c | 331 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/rc5t583.h | 22 +++ 4 files changed, 364 insertions(+) create mode 100644 drivers/rtc/rtc-rc5t583.c (limited to 'include/linux') diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index f92d17bc956..94764560d11 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -350,6 +350,16 @@ config RTC_DRV_TPS65910 This driver can also be built as a module. If so, the module will be called rtc-tps65910. +config RTC_DRV_RC5T583 + tristate "RICOH 5T583 RTC driver" + depends on MFD_RC5T583 + help + If you say yes here you get support for the RTC on the + RICOH 5T583 chips. + + This driver can also be built as a module. If so, the module + will be called rtc-rc5t583. + config RTC_DRV_S35390A tristate "Seiko Instruments S-35390A" select BITREVERSE diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index d9526b549f8..56297f0fd38 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -87,6 +87,7 @@ obj-$(CONFIG_RTC_DRV_PS3) += rtc-ps3.o obj-$(CONFIG_RTC_DRV_PUV3) += rtc-puv3.o obj-$(CONFIG_RTC_DRV_PXA) += rtc-pxa.o obj-$(CONFIG_RTC_DRV_R9701) += rtc-r9701.o +obj-$(CONFIG_RTC_DRV_RC5T583) += rtc-rc5t583.o obj-$(CONFIG_RTC_DRV_RP5C01) += rtc-rp5c01.o obj-$(CONFIG_RTC_DRV_RS5C313) += rtc-rs5c313.o obj-$(CONFIG_RTC_DRV_RS5C348) += rtc-rs5c348.o diff --git a/drivers/rtc/rtc-rc5t583.c b/drivers/rtc/rtc-rc5t583.c new file mode 100644 index 00000000000..cdb140c29c5 --- /dev/null +++ b/drivers/rtc/rtc-rc5t583.c @@ -0,0 +1,331 @@ +/* + * rtc-rc5t583.c -- RICOH RC5T583 Real Time Clock + * + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + * Author: Venu Byravarasu + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, see . */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct rc5t583_rtc { + struct rtc_device *rtc; + /* To store the list of enabled interrupts, during system suspend */ + u32 irqen; +}; + +/* Total number of RTC registers needed to set time*/ +#define NUM_TIME_REGS (RC5T583_RTC_YEAR - RC5T583_RTC_SEC + 1) + +/* Total number of RTC registers needed to set Y-Alarm*/ +#define NUM_YAL_REGS (RC5T583_RTC_AY_YEAR - RC5T583_RTC_AY_MIN + 1) + +/* Set Y-Alarm interrupt */ +#define SET_YAL BIT(5) + +/* Get Y-Alarm interrupt status*/ +#define GET_YAL_STATUS BIT(3) + +static int rc5t583_rtc_alarm_irq_enable(struct device *dev, unsigned enabled) +{ + struct rc5t583 *rc5t583 = dev_get_drvdata(dev->parent); + u8 val; + + /* Set Y-Alarm, based on 'enabled' */ + val = enabled ? SET_YAL : 0; + + return regmap_update_bits(rc5t583->regmap, RC5T583_RTC_CTL1, SET_YAL, + val); +} + +/* + * Gets current rc5t583 RTC time and date parameters. + * + * The RTC's time/alarm representation is not what gmtime(3) requires + * Linux to use: + * + * - Months are 1..12 vs Linux 0-11 + * - Years are 0..99 vs Linux 1900..N (we assume 21st century) + */ +static int rc5t583_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct rc5t583 *rc5t583 = dev_get_drvdata(dev->parent); + u8 rtc_data[NUM_TIME_REGS]; + int ret; + + ret = regmap_bulk_read(rc5t583->regmap, RC5T583_RTC_SEC, rtc_data, + NUM_TIME_REGS); + if (ret < 0) { + dev_err(dev, "RTC read time failed with err:%d\n", ret); + return ret; + } + + tm->tm_sec = bcd2bin(rtc_data[0]); + tm->tm_min = bcd2bin(rtc_data[1]); + tm->tm_hour = bcd2bin(rtc_data[2]); + tm->tm_wday = bcd2bin(rtc_data[3]); + tm->tm_mday = bcd2bin(rtc_data[4]); + tm->tm_mon = bcd2bin(rtc_data[5]) - 1; + tm->tm_year = bcd2bin(rtc_data[6]) + 100; + + return ret; +} + +static int rc5t583_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct rc5t583 *rc5t583 = dev_get_drvdata(dev->parent); + unsigned char rtc_data[NUM_TIME_REGS]; + int ret; + + rtc_data[0] = bin2bcd(tm->tm_sec); + rtc_data[1] = bin2bcd(tm->tm_min); + rtc_data[2] = bin2bcd(tm->tm_hour); + rtc_data[3] = bin2bcd(tm->tm_wday); + rtc_data[4] = bin2bcd(tm->tm_mday); + rtc_data[5] = bin2bcd(tm->tm_mon + 1); + rtc_data[6] = bin2bcd(tm->tm_year - 100); + + ret = regmap_bulk_write(rc5t583->regmap, RC5T583_RTC_SEC, rtc_data, + NUM_TIME_REGS); + if (ret < 0) { + dev_err(dev, "RTC set time failed with error %d\n", ret); + return ret; + } + + return ret; +} + +static int rc5t583_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + struct rc5t583 *rc5t583 = dev_get_drvdata(dev->parent); + unsigned char alarm_data[NUM_YAL_REGS]; + u32 interrupt_enable; + int ret; + + ret = regmap_bulk_read(rc5t583->regmap, RC5T583_RTC_AY_MIN, alarm_data, + NUM_YAL_REGS); + if (ret < 0) { + dev_err(dev, "rtc_read_alarm error %d\n", ret); + return ret; + } + + alm->time.tm_min = bcd2bin(alarm_data[0]); + alm->time.tm_hour = bcd2bin(alarm_data[1]); + alm->time.tm_mday = bcd2bin(alarm_data[2]); + alm->time.tm_mon = bcd2bin(alarm_data[3]) - 1; + alm->time.tm_year = bcd2bin(alarm_data[4]) + 100; + + ret = regmap_read(rc5t583->regmap, RC5T583_RTC_CTL1, &interrupt_enable); + if (ret < 0) + return ret; + + /* check if YALE is set */ + if (interrupt_enable & SET_YAL) + alm->enabled = 1; + + return ret; +} + +static int rc5t583_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + struct rc5t583 *rc5t583 = dev_get_drvdata(dev->parent); + unsigned char alarm_data[NUM_YAL_REGS]; + int ret; + + ret = rc5t583_rtc_alarm_irq_enable(dev, 0); + if (ret) + return ret; + + alarm_data[0] = bin2bcd(alm->time.tm_min); + alarm_data[1] = bin2bcd(alm->time.tm_hour); + alarm_data[2] = bin2bcd(alm->time.tm_mday); + alarm_data[3] = bin2bcd(alm->time.tm_mon + 1); + alarm_data[4] = bin2bcd(alm->time.tm_year - 100); + + ret = regmap_bulk_write(rc5t583->regmap, RC5T583_RTC_AY_MIN, alarm_data, + NUM_YAL_REGS); + if (ret) { + dev_err(dev, "rtc_set_alarm error %d\n", ret); + return ret; + } + + if (alm->enabled) + ret = rc5t583_rtc_alarm_irq_enable(dev, 1); + + return ret; +} + +static irqreturn_t rc5t583_rtc_interrupt(int irq, void *rtc) +{ + struct device *dev = rtc; + struct rc5t583 *rc5t583 = dev_get_drvdata(dev->parent); + struct rc5t583_rtc *rc5t583_rtc = dev_get_drvdata(dev); + unsigned long events = 0; + int ret; + u32 rtc_reg; + + ret = regmap_read(rc5t583->regmap, RC5T583_RTC_CTL2, &rtc_reg); + if (ret < 0) + return IRQ_NONE; + + if (rtc_reg & GET_YAL_STATUS) { + events = RTC_IRQF | RTC_AF; + /* clear pending Y-alarm interrupt bit */ + rtc_reg &= ~GET_YAL_STATUS; + } + + ret = regmap_write(rc5t583->regmap, RC5T583_RTC_CTL2, rtc_reg); + if (ret) + return IRQ_NONE; + + /* Notify RTC core on event */ + rtc_update_irq(rc5t583_rtc->rtc, 1, events); + + return IRQ_HANDLED; +} + +static const struct rtc_class_ops rc5t583_rtc_ops = { + .read_time = rc5t583_rtc_read_time, + .set_time = rc5t583_rtc_set_time, + .read_alarm = rc5t583_rtc_read_alarm, + .set_alarm = rc5t583_rtc_set_alarm, + .alarm_irq_enable = rc5t583_rtc_alarm_irq_enable, +}; + +static int __devinit rc5t583_rtc_probe(struct platform_device *pdev) +{ + struct rc5t583 *rc5t583 = dev_get_drvdata(pdev->dev.parent); + struct rc5t583_rtc *ricoh_rtc; + struct rc5t583_platform_data *pmic_plat_data; + int ret; + int irq; + + ricoh_rtc = devm_kzalloc(&pdev->dev, sizeof(struct rc5t583_rtc), + GFP_KERNEL); + if (!ricoh_rtc) + return -ENOMEM; + + platform_set_drvdata(pdev, ricoh_rtc); + + /* Clear pending interrupts */ + ret = regmap_write(rc5t583->regmap, RC5T583_RTC_CTL2, 0); + if (ret < 0) + return ret; + + /* clear RTC Adjust register */ + ret = regmap_write(rc5t583->regmap, RC5T583_RTC_ADJ, 0); + if (ret < 0) { + dev_err(&pdev->dev, "unable to program rtc_adjust reg\n"); + return -EBUSY; + } + + pmic_plat_data = dev_get_platdata(rc5t583->dev); + irq = pmic_plat_data->irq_base; + if (irq <= 0) { + dev_warn(&pdev->dev, "Wake up is not possible as irq = %d\n", + irq); + return ret; + } + + irq += RC5T583_IRQ_YALE; + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, + rc5t583_rtc_interrupt, IRQF_TRIGGER_LOW, + "rtc-rc5t583", &pdev->dev); + if (ret < 0) { + dev_err(&pdev->dev, "IRQ is not free.\n"); + return ret; + } + device_init_wakeup(&pdev->dev, 1); + + ricoh_rtc->rtc = rtc_device_register(pdev->name, &pdev->dev, + &rc5t583_rtc_ops, THIS_MODULE); + if (IS_ERR(ricoh_rtc->rtc)) { + ret = PTR_ERR(ricoh_rtc->rtc); + dev_err(&pdev->dev, "RTC device register: err %d\n", ret); + return ret; + } + + return 0; +} + +/* + * Disable rc5t583 RTC interrupts. + * Sets status flag to free. + */ +static int __devexit rc5t583_rtc_remove(struct platform_device *pdev) +{ + struct rc5t583_rtc *rc5t583_rtc = dev_get_drvdata(&pdev->dev); + + rc5t583_rtc_alarm_irq_enable(&rc5t583_rtc->rtc->dev, 0); + + rtc_device_unregister(rc5t583_rtc->rtc); + return 0; +} + +#ifdef CONFIG_PM_SLEEP + +static int rc5t583_rtc_suspend(struct device *dev) +{ + struct rc5t583 *rc5t583 = dev_get_drvdata(dev->parent); + struct rc5t583_rtc *rc5t583_rtc = dev_get_drvdata(dev); + int ret; + + /* Store current list of enabled interrupts*/ + ret = regmap_read(rc5t583->regmap, RC5T583_RTC_CTL1, + &rc5t583_rtc->irqen); + return ret; +} + +static int rc5t583_rtc_resume(struct device *dev) +{ + struct rc5t583 *rc5t583 = dev_get_drvdata(dev->parent); + struct rc5t583_rtc *rc5t583_rtc = dev_get_drvdata(dev); + + /* Restore list of enabled interrupts before suspend */ + return regmap_write(rc5t583->regmap, RC5T583_RTC_CTL1, + rc5t583_rtc->irqen); +} + +static const struct dev_pm_ops rc5t583_rtc_pm_ops = { + .suspend = rc5t583_rtc_suspend, + .resume = rc5t583_rtc_resume, +}; + +#define DEV_PM_OPS (&rc5t583_rtc_pm_ops) +#else +#define DEV_PM_OPS NULL +#endif + +static struct platform_driver rc5t583_rtc_driver = { + .probe = rc5t583_rtc_probe, + .remove = __devexit_p(rc5t583_rtc_remove), + .driver = { + .owner = THIS_MODULE, + .name = "rtc-rc5t583", + .pm = DEV_PM_OPS, + }, +}; + +module_platform_driver(rc5t583_rtc_driver); +MODULE_ALIAS("platform:rtc-rc5t583"); +MODULE_AUTHOR("Venu Byravarasu "); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/mfd/rc5t583.h b/include/linux/mfd/rc5t583.h index 3661c59aa1e..36c242e52ef 100644 --- a/include/linux/mfd/rc5t583.h +++ b/include/linux/mfd/rc5t583.h @@ -146,6 +146,28 @@ #define RC5T583_GPIO_MON_IOIN 0xAB #define RC5T583_GPIO_GPOFUNC 0xAC +/* RTC registers */ +#define RC5T583_RTC_SEC 0xE0 +#define RC5T583_RTC_MIN 0xE1 +#define RC5T583_RTC_HOUR 0xE2 +#define RC5T583_RTC_WDAY 0xE3 +#define RC5T583_RTC_DAY 0xE4 +#define RC5T583_RTC_MONTH 0xE5 +#define RC5T583_RTC_YEAR 0xE6 +#define RC5T583_RTC_ADJ 0xE7 +#define RC5T583_RTC_AW_MIN 0xE8 +#define RC5T583_RTC_AW_HOUR 0xE9 +#define RC5T583_RTC_AW_WEEK 0xEA +#define RC5T583_RTC_AD_MIN 0xEB +#define RC5T583_RTC_AD_HOUR 0xEC +#define RC5T583_RTC_CTL1 0xED +#define RC5T583_RTC_CTL2 0xEE +#define RC5T583_RTC_AY_MIN 0xF0 +#define RC5T583_RTC_AY_HOUR 0xF1 +#define RC5T583_RTC_AY_DAY 0xF2 +#define RC5T583_RTC_AY_MONTH 0xF3 +#define RC5T583_RTC_AY_YEAR 0xF4 + /* RICOH_RC5T583 IRQ definitions */ enum { RC5T583_IRQ_ONKEY, -- cgit v1.2.3-70-g09d2 From 4c24e29e65843ed912c14cdc293ed922e33efdcc Mon Sep 17 00:00:00 2001 From: David Fries Date: Thu, 4 Oct 2012 17:14:12 -0700 Subject: rtc_sysfs_show_hctosys(): display 0 if resume failed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Without this patch /sys/class/rtc/$CONFIG_RTC_HCTOSYS_DEVICE/hctosys contains a 1 (meaning "This rtc was used to initialize the system clock") even if setting the time by do_settimeofday() at bootup failed. The RTC can also be used to set the clock on resume, if it did 1, otherwise 0. Previously there was no indication if the RTC was used to set the clock in resume. This uses only CONFIG_RTC_HCTOSYS_DEVICE for conditional compilation instead of it and CONFIG_RTC_HCTOSYS to be more consistent. rtc_hctosys_ret was moved to class.c so class.c no longer depends on hctosys.c. [sfr@canb.auug.org.au: fix build] Signed-off-by: David Fries Cc: Matthew Garrett Cc: Alessandro Zummo Cc: Uwe Kleine-König Signed-off-by: Stephen Rothwell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/class.c | 8 +++++++- drivers/rtc/hctosys.c | 4 +--- drivers/rtc/rtc-sysfs.c | 6 ++++++ include/linux/rtc.h | 2 +- 4 files changed, 15 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c index 37b1d82fda0..f8a0aab218c 100644 --- a/drivers/rtc/class.c +++ b/drivers/rtc/class.c @@ -31,8 +31,12 @@ static void rtc_device_release(struct device *dev) kfree(rtc); } -#if defined(CONFIG_PM) && defined(CONFIG_RTC_HCTOSYS_DEVICE) +#ifdef CONFIG_RTC_HCTOSYS_DEVICE +/* Result of the last RTC to system clock attempt. */ +int rtc_hctosys_ret = -ENODEV; +#endif +#if defined(CONFIG_PM) && defined(CONFIG_RTC_HCTOSYS_DEVICE) /* * On suspend(), measure the delta between one RTC and the * system's wall clock; restore it on resume(). @@ -84,6 +88,7 @@ static int rtc_resume(struct device *dev) struct timespec new_system, new_rtc; struct timespec sleep_time; + rtc_hctosys_ret = -ENODEV; if (strcmp(dev_name(&rtc->dev), CONFIG_RTC_HCTOSYS_DEVICE) != 0) return 0; @@ -117,6 +122,7 @@ static int rtc_resume(struct device *dev) if (sleep_time.tv_sec >= 0) timekeeping_inject_sleeptime(&sleep_time); + rtc_hctosys_ret = 0; return 0; } diff --git a/drivers/rtc/hctosys.c b/drivers/rtc/hctosys.c index bc90b091f19..4aa60d74004 100644 --- a/drivers/rtc/hctosys.c +++ b/drivers/rtc/hctosys.c @@ -22,8 +22,6 @@ * the best guess is to add 0.5s. */ -int rtc_hctosys_ret = -ENODEV; - static int __init rtc_hctosys(void) { int err = -ENODEV; @@ -56,7 +54,7 @@ static int __init rtc_hctosys(void) rtc_tm_to_time(&tm, &tv.tv_sec); - do_settimeofday(&tv); + err = do_settimeofday(&tv); dev_info(rtc->dev.parent, "setting system clock to " diff --git a/drivers/rtc/rtc-sysfs.c b/drivers/rtc/rtc-sysfs.c index 380083ca572..b70e2bb6364 100644 --- a/drivers/rtc/rtc-sysfs.c +++ b/drivers/rtc/rtc-sysfs.c @@ -102,6 +102,12 @@ rtc_sysfs_set_max_user_freq(struct device *dev, struct device_attribute *attr, return n; } +/** + * rtc_sysfs_show_hctosys - indicate if the given RTC set the system time + * + * Returns 1 if the system clock was set by this RTC at the last + * boot or resume event. + */ static ssize_t rtc_sysfs_show_hctosys(struct device *dev, struct device_attribute *attr, char *buf) diff --git a/include/linux/rtc.h b/include/linux/rtc.h index f071b3922c6..20ec4d3bed7 100644 --- a/include/linux/rtc.h +++ b/include/linux/rtc.h @@ -276,7 +276,7 @@ static inline bool is_leap_year(unsigned int year) return (!(year % 4) && (year % 100)) || !(year % 400); } -#ifdef CONFIG_RTC_HCTOSYS +#ifdef CONFIG_RTC_HCTOSYS_DEVICE extern int rtc_hctosys_ret; #else #define rtc_hctosys_ret -ENODEV -- cgit v1.2.3-70-g09d2 From 046d662f481830e652ac34cd112249adde16452a Mon Sep 17 00:00:00 2001 From: Alex Kelly Date: Thu, 4 Oct 2012 17:15:23 -0700 Subject: coredump: make core dump functionality optional Adds an expert Kconfig option, CONFIG_COREDUMP, which allows disabling of core dump. This saves approximately 2.6k in the compiled kernel, and complements CONFIG_ELF_CORE, which now depends on it. CONFIG_COREDUMP also disables coredump-related sysctls, except for suid_dumpable and related functions, which are necessary for ptrace. [akpm@linux-foundation.org: fix binfmt_aout.c build] Signed-off-by: Alex Kelly Reviewed-by: Josh Triplett Acked-by: Serge Hallyn Acked-by: Kees Cook Cc: Randy Dunlap Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/Kconfig.binfmt | 8 ++++++++ fs/Makefile | 3 ++- fs/binfmt_aout.c | 52 +++++++++++++++++++++++++------------------------ include/linux/binfmts.h | 4 ++++ init/Kconfig | 1 + kernel/sysctl.c | 12 +++++++++++- 6 files changed, 53 insertions(+), 27 deletions(-) (limited to 'include/linux') diff --git a/fs/Kconfig.binfmt b/fs/Kconfig.binfmt index 02257420274..0efd1524b97 100644 --- a/fs/Kconfig.binfmt +++ b/fs/Kconfig.binfmt @@ -164,3 +164,11 @@ config BINFMT_MISC You may say M here for module support and later load the module when you have use for it; the module is called binfmt_misc. If you don't know what to answer at this point, say Y. + +config COREDUMP + bool "Enable core dump support" if EXPERT + default y + help + This option enables support for performing core dumps. You almost + certainly want to say Y here. Not necessary on systems that never + need debugging or only ever run flawless code. diff --git a/fs/Makefile b/fs/Makefile index 8938f825032..1d7af79288a 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -11,7 +11,7 @@ obj-y := open.o read_write.o file_table.o super.o \ attr.o bad_inode.o file.o filesystems.o namespace.o \ seq_file.o xattr.o libfs.o fs-writeback.o \ pnode.o drop_caches.o splice.o sync.o utimes.o \ - stack.o fs_struct.o statfs.o coredump.o + stack.o fs_struct.o statfs.o ifeq ($(CONFIG_BLOCK),y) obj-y += buffer.o bio.o block_dev.o direct-io.o mpage.o ioprio.o @@ -48,6 +48,7 @@ obj-$(CONFIG_FS_MBCACHE) += mbcache.o obj-$(CONFIG_FS_POSIX_ACL) += posix_acl.o xattr_acl.o obj-$(CONFIG_NFS_COMMON) += nfs_common/ obj-$(CONFIG_GENERIC_ACL) += generic_acl.o +obj-$(CONFIG_COREDUMP) += coredump.o obj-$(CONFIG_FHANDLE) += fhandle.o diff --git a/fs/binfmt_aout.c b/fs/binfmt_aout.c index d146e181d10..4b5b5117f00 100644 --- a/fs/binfmt_aout.c +++ b/fs/binfmt_aout.c @@ -32,31 +32,8 @@ static int load_aout_binary(struct linux_binprm *, struct pt_regs * regs); static int load_aout_library(struct file*); -static int aout_core_dump(struct coredump_params *cprm); - -static struct linux_binfmt aout_format = { - .module = THIS_MODULE, - .load_binary = load_aout_binary, - .load_shlib = load_aout_library, - .core_dump = aout_core_dump, - .min_coredump = PAGE_SIZE -}; - -#define BAD_ADDR(x) ((unsigned long)(x) >= TASK_SIZE) - -static int set_brk(unsigned long start, unsigned long end) -{ - start = PAGE_ALIGN(start); - end = PAGE_ALIGN(end); - if (end > start) { - unsigned long addr; - addr = vm_brk(start, end - start); - if (BAD_ADDR(addr)) - return addr; - } - return 0; -} +#ifdef CONFIG_COREDUMP /* * Routine writes a core dump image in the current directory. * Currently only a stub-function. @@ -66,7 +43,6 @@ static int set_brk(unsigned long start, unsigned long end) * field, which also makes sure the core-dumps won't be recursive if the * dumping of the process results in another error.. */ - static int aout_core_dump(struct coredump_params *cprm) { struct file *file = cprm->file; @@ -135,6 +111,32 @@ end_coredump: set_fs(fs); return has_dumped; } +#else +#define aout_core_dump NULL +#endif + +static struct linux_binfmt aout_format = { + .module = THIS_MODULE, + .load_binary = load_aout_binary, + .load_shlib = load_aout_library, + .core_dump = aout_core_dump, + .min_coredump = PAGE_SIZE +}; + +#define BAD_ADDR(x) ((unsigned long)(x) >= TASK_SIZE) + +static int set_brk(unsigned long start, unsigned long end) +{ + start = PAGE_ALIGN(start); + end = PAGE_ALIGN(end); + if (end > start) { + unsigned long addr; + addr = vm_brk(start, end - start); + if (BAD_ADDR(addr)) + return addr; + } + return 0; +} /* * create_aout_tables() parses the env- and arg-strings in new user diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h index 366422bc163..00e2e890895 100644 --- a/include/linux/binfmts.h +++ b/include/linux/binfmts.h @@ -132,7 +132,11 @@ extern int copy_strings_kernel(int argc, const char *const *argv, struct linux_binprm *bprm); extern int prepare_bprm_creds(struct linux_binprm *bprm); extern void install_exec_creds(struct linux_binprm *bprm); +#ifdef CONFIG_COREDUMP extern void do_coredump(long signr, int exit_code, struct pt_regs *regs); +#else +static inline void do_coredump(long signr, int exit_code, struct pt_regs *regs) {} +#endif extern void set_binfmt(struct linux_binfmt *new); extern void free_bprm(struct linux_binprm *); diff --git a/init/Kconfig b/init/Kconfig index e82f289290f..ed6334dd5e7 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1199,6 +1199,7 @@ config BUG Just say Y. config ELF_CORE + depends on COREDUMP default y bool "Enable ELF core dumps" if EXPERT help diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 84c76a34e41..c2a2f8084ba 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -97,10 +97,12 @@ extern int sysctl_overcommit_memory; extern int sysctl_overcommit_ratio; extern int max_threads; -extern int core_uses_pid; extern int suid_dumpable; +#ifdef CONFIG_COREDUMP +extern int core_uses_pid; extern char core_pattern[]; extern unsigned int core_pipe_limit; +#endif extern int pid_max; extern int min_free_kbytes; extern int pid_max_min, pid_max_max; @@ -177,8 +179,10 @@ static int proc_dointvec_minmax_sysadmin(struct ctl_table *table, int write, static int proc_dointvec_minmax_coredump(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos); +#ifdef CONFIG_COREDUMP static int proc_dostring_coredump(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos); +#endif #ifdef CONFIG_MAGIC_SYSRQ /* Note: sysrq code uses it's own private copy */ @@ -404,6 +408,7 @@ static struct ctl_table kern_table[] = { .mode = 0644, .proc_handler = proc_dointvec, }, +#ifdef CONFIG_COREDUMP { .procname = "core_uses_pid", .data = &core_uses_pid, @@ -425,6 +430,7 @@ static struct ctl_table kern_table[] = { .mode = 0644, .proc_handler = proc_dointvec, }, +#endif #ifdef CONFIG_PROC_SYSCTL { .procname = "tainted", @@ -2036,12 +2042,14 @@ int proc_dointvec_minmax(struct ctl_table *table, int write, static void validate_coredump_safety(void) { +#ifdef CONFIG_COREDUMP if (suid_dumpable == SUID_DUMPABLE_SAFE && core_pattern[0] != '/' && core_pattern[0] != '|') { printk(KERN_WARNING "Unsafe core_pattern used with "\ "suid_dumpable=2. Pipe handler or fully qualified "\ "core dump path required.\n"); } +#endif } static int proc_dointvec_minmax_coredump(struct ctl_table *table, int write, @@ -2053,6 +2061,7 @@ static int proc_dointvec_minmax_coredump(struct ctl_table *table, int write, return error; } +#ifdef CONFIG_COREDUMP static int proc_dostring_coredump(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { @@ -2061,6 +2070,7 @@ static int proc_dostring_coredump(struct ctl_table *table, int write, validate_coredump_safety(); return error; } +#endif static int __do_proc_doulongvec_minmax(void *data, struct ctl_table *table, int write, void __user *buffer, -- cgit v1.2.3-70-g09d2 From 179899fd5dc780fe3bcd44d0eb7823e3d855c855 Mon Sep 17 00:00:00 2001 From: Alex Kelly Date: Thu, 4 Oct 2012 17:15:24 -0700 Subject: coredump: update coredump-related headers Create a new header file, fs/coredump.h, which contains functions only used by the new coredump.c. It also moves do_coredump to the include/linux/coredump.h header file, for consistency. Signed-off-by: Alex Kelly Reviewed-by: Josh Triplett Acked-by: Serge Hallyn Acked-by: Kees Cook Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/coredump.c | 2 ++ fs/coredump.h | 6 ++++++ fs/exec.c | 1 + include/linux/binfmts.h | 5 ----- include/linux/coredump.h | 5 +++++ include/linux/sched.h | 1 - kernel/signal.c | 1 + 7 files changed, 15 insertions(+), 6 deletions(-) create mode 100644 fs/coredump.h (limited to 'include/linux') diff --git a/fs/coredump.c b/fs/coredump.c index f045bbad682..c01aa7b9ab5 100644 --- a/fs/coredump.c +++ b/fs/coredump.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -39,6 +40,7 @@ #include #include "internal.h" +#include "coredump.h" #include diff --git a/fs/coredump.h b/fs/coredump.h new file mode 100644 index 00000000000..e39ff072110 --- /dev/null +++ b/fs/coredump.h @@ -0,0 +1,6 @@ +#ifndef _FS_COREDUMP_H +#define _FS_COREDUMP_H + +extern int __get_dumpable(unsigned long mm_flags); + +#endif diff --git a/fs/exec.c b/fs/exec.c index 48fb26ef8a1..bd941a1cdbf 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -63,6 +63,7 @@ #include #include "internal.h" +#include "coredump.h" #include diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h index 00e2e890895..c7b16ee7141 100644 --- a/include/linux/binfmts.h +++ b/include/linux/binfmts.h @@ -132,11 +132,6 @@ extern int copy_strings_kernel(int argc, const char *const *argv, struct linux_binprm *bprm); extern int prepare_bprm_creds(struct linux_binprm *bprm); extern void install_exec_creds(struct linux_binprm *bprm); -#ifdef CONFIG_COREDUMP -extern void do_coredump(long signr, int exit_code, struct pt_regs *regs); -#else -static inline void do_coredump(long signr, int exit_code, struct pt_regs *regs) {} -#endif extern void set_binfmt(struct linux_binfmt *new); extern void free_bprm(struct linux_binprm *); diff --git a/include/linux/coredump.h b/include/linux/coredump.h index ba4b85a6d9b..42f9752a0a4 100644 --- a/include/linux/coredump.h +++ b/include/linux/coredump.h @@ -11,5 +11,10 @@ */ extern int dump_write(struct file *file, const void *addr, int nr); extern int dump_seek(struct file *file, loff_t off); +#ifdef CONFIG_COREDUMP +extern void do_coredump(long signr, int exit_code, struct pt_regs *regs); +#else +static inline void do_coredump(long signr, int exit_code, struct pt_regs *regs) {} +#endif #endif /* _LINUX_COREDUMP_H */ diff --git a/include/linux/sched.h b/include/linux/sched.h index 9d51e260bde..9c5612f0374 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -405,7 +405,6 @@ static inline void arch_pick_mmap_layout(struct mm_struct *mm) {} extern void set_dumpable(struct mm_struct *mm, int value); extern int get_dumpable(struct mm_struct *mm); -extern int __get_dumpable(unsigned long mm_flags); /* get/set_dumpable() values */ #define SUID_DUMPABLE_DISABLED 0 diff --git a/kernel/signal.c b/kernel/signal.c index 2c681f11b7d..2ad3f5904bd 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include -- cgit v1.2.3-70-g09d2 From 5ab1c309b344880d81494e9eab7fb27682bc6d9d Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 4 Oct 2012 17:15:29 -0700 Subject: coredump: pass siginfo_t* to do_coredump() and below, not merely signr This is a preparatory patch for the introduction of NT_SIGINFO elf note. With this patch we pass "siginfo_t *siginfo" instead of "int signr" to do_coredump() and put it into coredump_params. It will be used by the next patch. Most changes are simple s/signr/siginfo->si_signo/. Signed-off-by: Denys Vlasenko Reviewed-by: Oleg Nesterov Cc: Amerigo Wang Cc: "Jonathan M. Foote" Cc: Roland McGrath Cc: Pedro Alves Cc: Fengguang Wu Cc: Stephen Rothwell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/binfmt_aout.c | 2 +- fs/binfmt_elf.c | 14 +++++++------- fs/binfmt_elf_fdpic.c | 6 +++--- fs/binfmt_flat.c | 2 +- fs/coredump.c | 10 +++++----- include/linux/binfmts.h | 2 +- include/linux/coredump.h | 4 ++-- kernel/signal.c | 2 +- 8 files changed, 21 insertions(+), 21 deletions(-) (limited to 'include/linux') diff --git a/fs/binfmt_aout.c b/fs/binfmt_aout.c index 4b5b5117f00..0e7a6f81ae3 100644 --- a/fs/binfmt_aout.c +++ b/fs/binfmt_aout.c @@ -65,7 +65,7 @@ static int aout_core_dump(struct coredump_params *cprm) current->flags |= PF_DUMPCORE; strncpy(dump.u_comm, current->comm, sizeof(dump.u_comm)); dump.u_ar0 = offsetof(struct user, regs); - dump.signal = cprm->signr; + dump.signal = cprm->siginfo->si_signo; aout_dump_thread(cprm->regs, &dump); /* If the size of the dump file exceeds the rlimit, then see what would happen diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index 7ef5f9fe272..4450e82a05a 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -1480,7 +1480,7 @@ static int fill_thread_core_info(struct elf_thread_core_info *t, static int fill_note_info(struct elfhdr *elf, int phdrs, struct elf_note_info *info, - long signr, struct pt_regs *regs) + siginfo_t *siginfo, struct pt_regs *regs) { struct task_struct *dump_task = current; const struct user_regset_view *view = task_user_regset_view(dump_task); @@ -1550,7 +1550,7 @@ static int fill_note_info(struct elfhdr *elf, int phdrs, * Now fill in each thread's information. */ for (t = info->thread; t != NULL; t = t->next) - if (!fill_thread_core_info(t, view, signr, &info->size)) + if (!fill_thread_core_info(t, view, siginfo->si_signo, &info->size)) return 0; /* @@ -1713,14 +1713,14 @@ static int elf_note_info_init(struct elf_note_info *info) static int fill_note_info(struct elfhdr *elf, int phdrs, struct elf_note_info *info, - long signr, struct pt_regs *regs) + siginfo_t *siginfo, struct pt_regs *regs) { struct list_head *t; if (!elf_note_info_init(info)) return 0; - if (signr) { + if (siginfo->si_signo) { struct core_thread *ct; struct elf_thread_status *ets; @@ -1738,13 +1738,13 @@ static int fill_note_info(struct elfhdr *elf, int phdrs, int sz; ets = list_entry(t, struct elf_thread_status, list); - sz = elf_dump_thread_status(signr, ets); + sz = elf_dump_thread_status(siginfo->si_signo, ets); info->thread_status_size += sz; } } /* now collect the dump for the current */ memset(info->prstatus, 0, sizeof(*info->prstatus)); - fill_prstatus(info->prstatus, current, signr); + fill_prstatus(info->prstatus, current, siginfo->si_signo); elf_core_copy_regs(&info->prstatus->pr_reg, regs); /* Set up header */ @@ -1951,7 +1951,7 @@ static int elf_core_dump(struct coredump_params *cprm) * Collect all the non-memory information about the process for the * notes. This also sets up the file header. */ - if (!fill_note_info(elf, e_phnum, &info, cprm->signr, cprm->regs)) + if (!fill_note_info(elf, e_phnum, &info, cprm->siginfo, cprm->regs)) goto cleanup; has_dumped = 1; diff --git a/fs/binfmt_elf_fdpic.c b/fs/binfmt_elf_fdpic.c index 3d77cf81ba3..08d812b3228 100644 --- a/fs/binfmt_elf_fdpic.c +++ b/fs/binfmt_elf_fdpic.c @@ -1642,7 +1642,7 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm) goto cleanup; #endif - if (cprm->signr) { + if (cprm->siginfo->si_signo) { struct core_thread *ct; struct elf_thread_status *tmp; @@ -1661,13 +1661,13 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm) int sz; tmp = list_entry(t, struct elf_thread_status, list); - sz = elf_dump_thread_status(cprm->signr, tmp); + sz = elf_dump_thread_status(cprm->siginfo->si_signo, tmp); thread_status_size += sz; } } /* now collect the dump for the current */ - fill_prstatus(prstatus, current, cprm->signr); + fill_prstatus(prstatus, current, cprm->siginfo->si_signo); elf_core_copy_regs(&prstatus->pr_reg, cprm->regs); segs = current->mm->map_count; diff --git a/fs/binfmt_flat.c b/fs/binfmt_flat.c index 178cb70acc2..e280352b28f 100644 --- a/fs/binfmt_flat.c +++ b/fs/binfmt_flat.c @@ -107,7 +107,7 @@ static struct linux_binfmt flat_format = { static int flat_core_dump(struct coredump_params *cprm) { printk("Process %s:%d received signr %d and should have core dumped\n", - current->comm, current->pid, (int) cprm->signr); + current->comm, current->pid, (int) cprm->siginfo->si_signo); return(1); } diff --git a/fs/coredump.c b/fs/coredump.c index 4fce06fc3b5..fd37facac8d 100644 --- a/fs/coredump.c +++ b/fs/coredump.c @@ -200,7 +200,7 @@ static int format_corename(struct core_name *cn, struct coredump_params *cprm) break; /* signal that caused the coredump */ case 's': - err = cn_printf(cn, "%ld", cprm->signr); + err = cn_printf(cn, "%ld", cprm->siginfo->si_signo); break; /* UNIX time of coredump */ case 't': { @@ -457,7 +457,7 @@ static int umh_pipe_setup(struct subprocess_info *info, struct cred *new) return 0; } -void do_coredump(long signr, int exit_code, struct pt_regs *regs) +void do_coredump(siginfo_t *siginfo, struct pt_regs *regs) { struct core_state core_state; struct core_name cn; @@ -472,7 +472,7 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs) bool need_nonrelative = false; static atomic_t core_dump_count = ATOMIC_INIT(0); struct coredump_params cprm = { - .signr = signr, + .siginfo = siginfo, .regs = regs, .limit = rlimit(RLIMIT_CORE), /* @@ -483,7 +483,7 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs) .mm_flags = mm->flags, }; - audit_core_dumps(signr); + audit_core_dumps(siginfo->si_signo); binfmt = mm->binfmt; if (!binfmt || !binfmt->core_dump) @@ -507,7 +507,7 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs) need_nonrelative = true; } - retval = coredump_wait(exit_code, &core_state); + retval = coredump_wait(siginfo->si_signo, &core_state); if (retval < 0) goto fail_creds; diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h index c7b16ee7141..37935c2d2e8 100644 --- a/include/linux/binfmts.h +++ b/include/linux/binfmts.h @@ -72,7 +72,7 @@ struct linux_binprm { /* Function parameter for binfmt->coredump */ struct coredump_params { - long signr; + siginfo_t *siginfo; struct pt_regs *regs; struct file *file; unsigned long limit; diff --git a/include/linux/coredump.h b/include/linux/coredump.h index 42f9752a0a4..1775eb8acc0 100644 --- a/include/linux/coredump.h +++ b/include/linux/coredump.h @@ -12,9 +12,9 @@ extern int dump_write(struct file *file, const void *addr, int nr); extern int dump_seek(struct file *file, loff_t off); #ifdef CONFIG_COREDUMP -extern void do_coredump(long signr, int exit_code, struct pt_regs *regs); +extern void do_coredump(siginfo_t *siginfo, struct pt_regs *regs); #else -static inline void do_coredump(long signr, int exit_code, struct pt_regs *regs) {} +static inline void do_coredump(siginfo_t *siginfo, struct pt_regs *regs) {} #endif #endif /* _LINUX_COREDUMP_H */ diff --git a/kernel/signal.c b/kernel/signal.c index 2ad3f5904bd..0af8868525d 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -2360,7 +2360,7 @@ relock: * first and our do_group_exit call below will use * that value and ignore the one we pass it. */ - do_coredump(info->si_signo, info->si_signo, regs); + do_coredump(info, regs); } /* -- cgit v1.2.3-70-g09d2 From 751f409db6216ebd134a94f6dcd97779933a5106 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 4 Oct 2012 17:15:31 -0700 Subject: compat: move compat_siginfo_t definition to asm/compat.h This is a preparatory patch for the introduction of NT_SIGINFO elf note. Make the location of compat_siginfo_t uniform across eight architectures which have it. Now it can be pulled in by including asm/compat.h or linux/compat.h. Most of the copies are verbatim. compat_uid[32]_t had to be replaced by __compat_uid[32]_t. compat_uptr_t had to be moved up before compat_siginfo_t in asm/compat.h on a several architectures (tile already had it moved up). compat_sigval_t had to be relocated from linux/compat.h to asm/compat.h. Signed-off-by: Denys Vlasenko Cc: Oleg Nesterov Cc: Amerigo Wang Cc: "Jonathan M. Foote" Cc: Roland McGrath Cc: Pedro Alves Cc: Fengguang Wu Cc: Stephen Rothwell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/arm64/include/asm/compat.h | 60 +++++++++++++++++++++++++++- arch/arm64/kernel/signal32.c | 53 ------------------------- arch/mips/include/asm/compat-signal.h | 62 ----------------------------- arch/mips/include/asm/compat.h | 69 +++++++++++++++++++++++++++++++- arch/parisc/include/asm/compat.h | 59 ++++++++++++++++++++++++++- arch/parisc/kernel/signal32.h | 52 ------------------------ arch/powerpc/include/asm/compat.h | 60 +++++++++++++++++++++++++++- arch/powerpc/include/asm/siginfo.h | 1 - arch/powerpc/kernel/ppc32.h | 51 ------------------------ arch/s390/include/asm/compat.h | 75 ++++++++++++++++++++++++++++++++++- arch/s390/kernel/compat_linux.h | 68 ------------------------------- arch/sparc/include/asm/compat.h | 61 +++++++++++++++++++++++++++- arch/sparc/include/asm/siginfo.h | 1 - arch/sparc/kernel/signal32.c | 52 ------------------------ arch/tile/include/asm/compat.h | 62 +++++++++++++++++++++++++++++ arch/tile/kernel/compat_signal.c | 57 -------------------------- arch/x86/include/asm/compat.h | 74 +++++++++++++++++++++++++++++++++- arch/x86/include/asm/ia32.h | 67 ------------------------------- include/linux/compat.h | 5 --- 19 files changed, 513 insertions(+), 476 deletions(-) (limited to 'include/linux') diff --git a/arch/arm64/include/asm/compat.h b/arch/arm64/include/asm/compat.h index a670a33ad73..37e610dc084 100644 --- a/arch/arm64/include/asm/compat.h +++ b/arch/arm64/include/asm/compat.h @@ -55,6 +55,7 @@ typedef s64 compat_s64; typedef u32 compat_uint_t; typedef u32 compat_ulong_t; typedef u64 compat_u64; +typedef u32 compat_uptr_t; struct compat_timespec { compat_time_t tv_sec; @@ -130,6 +131,64 @@ typedef u32 compat_old_sigset_t; typedef u32 compat_sigset_word; +typedef union compat_sigval { + compat_int_t sival_int; + compat_uptr_t sival_ptr; +} compat_sigval_t; + +typedef struct compat_siginfo { + int si_signo; + int si_errno; + int si_code; + + union { + /* The padding is the same size as AArch64. */ + int _pad[128/sizeof(int) - 3]; + + /* kill() */ + struct { + compat_pid_t _pid; /* sender's pid */ + __compat_uid32_t _uid; /* sender's uid */ + } _kill; + + /* POSIX.1b timers */ + struct { + compat_timer_t _tid; /* timer id */ + int _overrun; /* overrun count */ + compat_sigval_t _sigval; /* same as below */ + int _sys_private; /* not to be passed to user */ + } _timer; + + /* POSIX.1b signals */ + struct { + compat_pid_t _pid; /* sender's pid */ + __compat_uid32_t _uid; /* sender's uid */ + compat_sigval_t _sigval; + } _rt; + + /* SIGCHLD */ + struct { + compat_pid_t _pid; /* which child */ + __compat_uid32_t _uid; /* sender's uid */ + int _status; /* exit code */ + compat_clock_t _utime; + compat_clock_t _stime; + } _sigchld; + + /* SIGILL, SIGFPE, SIGSEGV, SIGBUS */ + struct { + compat_uptr_t _addr; /* faulting insn/memory ref. */ + short _addr_lsb; /* LSB of the reported address */ + } _sigfault; + + /* SIGPOLL */ + struct { + compat_long_t _band; /* POLL_IN, POLL_OUT, POLL_MSG */ + int _fd; + } _sigpoll; + } _sifields; +} compat_siginfo_t; + #define COMPAT_OFF_T_MAX 0x7fffffff #define COMPAT_LOFF_T_MAX 0x7fffffffffffffffL @@ -139,7 +198,6 @@ typedef u32 compat_sigset_word; * as pointers because the syscall entry code will have * appropriately converted them already. */ -typedef u32 compat_uptr_t; static inline void __user *compat_ptr(compat_uptr_t uptr) { diff --git a/arch/arm64/kernel/signal32.c b/arch/arm64/kernel/signal32.c index ac74c2f261e..0790a87a434 100644 --- a/arch/arm64/kernel/signal32.c +++ b/arch/arm64/kernel/signal32.c @@ -30,59 +30,6 @@ #include #include -typedef struct compat_siginfo { - int si_signo; - int si_errno; - int si_code; - - union { - /* The padding is the same size as AArch64. */ - int _pad[SI_PAD_SIZE]; - - /* kill() */ - struct { - compat_pid_t _pid; /* sender's pid */ - __compat_uid32_t _uid; /* sender's uid */ - } _kill; - - /* POSIX.1b timers */ - struct { - compat_timer_t _tid; /* timer id */ - int _overrun; /* overrun count */ - compat_sigval_t _sigval; /* same as below */ - int _sys_private; /* not to be passed to user */ - } _timer; - - /* POSIX.1b signals */ - struct { - compat_pid_t _pid; /* sender's pid */ - __compat_uid32_t _uid; /* sender's uid */ - compat_sigval_t _sigval; - } _rt; - - /* SIGCHLD */ - struct { - compat_pid_t _pid; /* which child */ - __compat_uid32_t _uid; /* sender's uid */ - int _status; /* exit code */ - compat_clock_t _utime; - compat_clock_t _stime; - } _sigchld; - - /* SIGILL, SIGFPE, SIGSEGV, SIGBUS */ - struct { - compat_uptr_t _addr; /* faulting insn/memory ref. */ - short _addr_lsb; /* LSB of the reported address */ - } _sigfault; - - /* SIGPOLL */ - struct { - compat_long_t _band; /* POLL_IN, POLL_OUT, POLL_MSG */ - int _fd; - } _sigpoll; - } _sifields; -} compat_siginfo_t; - struct compat_sigaction { compat_uptr_t sa_handler; compat_ulong_t sa_flags; diff --git a/arch/mips/include/asm/compat-signal.h b/arch/mips/include/asm/compat-signal.h index 368a99e5c3e..6599a901b63 100644 --- a/arch/mips/include/asm/compat-signal.h +++ b/arch/mips/include/asm/compat-signal.h @@ -10,68 +10,6 @@ #include -#define SI_PAD_SIZE32 ((SI_MAX_SIZE/sizeof(int)) - 3) - -typedef struct compat_siginfo { - int si_signo; - int si_code; - int si_errno; - - union { - int _pad[SI_PAD_SIZE32]; - - /* kill() */ - struct { - compat_pid_t _pid; /* sender's pid */ - compat_uid_t _uid; /* sender's uid */ - } _kill; - - /* SIGCHLD */ - struct { - compat_pid_t _pid; /* which child */ - compat_uid_t _uid; /* sender's uid */ - int _status; /* exit code */ - compat_clock_t _utime; - compat_clock_t _stime; - } _sigchld; - - /* IRIX SIGCHLD */ - struct { - compat_pid_t _pid; /* which child */ - compat_clock_t _utime; - int _status; /* exit code */ - compat_clock_t _stime; - } _irix_sigchld; - - /* SIGILL, SIGFPE, SIGSEGV, SIGBUS */ - struct { - s32 _addr; /* faulting insn/memory ref. */ - } _sigfault; - - /* SIGPOLL, SIGXFSZ (To do ...) */ - struct { - int _band; /* POLL_IN, POLL_OUT, POLL_MSG */ - int _fd; - } _sigpoll; - - /* POSIX.1b timers */ - struct { - timer_t _tid; /* timer id */ - int _overrun; /* overrun count */ - compat_sigval_t _sigval;/* same as below */ - int _sys_private; /* not to be passed to user */ - } _timer; - - /* POSIX.1b signals */ - struct { - compat_pid_t _pid; /* sender's pid */ - compat_uid_t _uid; /* sender's uid */ - compat_sigval_t _sigval; - } _rt; - - } _sifields; -} compat_siginfo_t; - static inline int __copy_conv_sigset_to_user(compat_sigset_t __user *d, const sigset_t *s) { diff --git a/arch/mips/include/asm/compat.h b/arch/mips/include/asm/compat.h index b77df0366ee..58277e0e9cd 100644 --- a/arch/mips/include/asm/compat.h +++ b/arch/mips/include/asm/compat.h @@ -43,6 +43,7 @@ typedef s64 compat_s64; typedef u32 compat_uint_t; typedef u32 compat_ulong_t; typedef u64 compat_u64; +typedef u32 compat_uptr_t; struct compat_timespec { compat_time_t tv_sec; @@ -124,6 +125,73 @@ typedef u32 compat_old_sigset_t; /* at least 32 bits */ typedef u32 compat_sigset_word; +typedef union compat_sigval { + compat_int_t sival_int; + compat_uptr_t sival_ptr; +} compat_sigval_t; + +#define SI_PAD_SIZE32 (128/sizeof(int) - 3) + +typedef struct compat_siginfo { + int si_signo; + int si_code; + int si_errno; + + union { + int _pad[SI_PAD_SIZE32]; + + /* kill() */ + struct { + compat_pid_t _pid; /* sender's pid */ + __compat_uid_t _uid; /* sender's uid */ + } _kill; + + /* SIGCHLD */ + struct { + compat_pid_t _pid; /* which child */ + __compat_uid_t _uid; /* sender's uid */ + int _status; /* exit code */ + compat_clock_t _utime; + compat_clock_t _stime; + } _sigchld; + + /* IRIX SIGCHLD */ + struct { + compat_pid_t _pid; /* which child */ + compat_clock_t _utime; + int _status; /* exit code */ + compat_clock_t _stime; + } _irix_sigchld; + + /* SIGILL, SIGFPE, SIGSEGV, SIGBUS */ + struct { + s32 _addr; /* faulting insn/memory ref. */ + } _sigfault; + + /* SIGPOLL, SIGXFSZ (To do ...) */ + struct { + int _band; /* POLL_IN, POLL_OUT, POLL_MSG */ + int _fd; + } _sigpoll; + + /* POSIX.1b timers */ + struct { + timer_t _tid; /* timer id */ + int _overrun; /* overrun count */ + compat_sigval_t _sigval;/* same as below */ + int _sys_private; /* not to be passed to user */ + } _timer; + + /* POSIX.1b signals */ + struct { + compat_pid_t _pid; /* sender's pid */ + __compat_uid_t _uid; /* sender's uid */ + compat_sigval_t _sigval; + } _rt; + + } _sifields; +} compat_siginfo_t; + #define COMPAT_OFF_T_MAX 0x7fffffff #define COMPAT_LOFF_T_MAX 0x7fffffffffffffffL @@ -133,7 +201,6 @@ typedef u32 compat_sigset_word; * as pointers because the syscall entry code will have * appropriately converted them already. */ -typedef u32 compat_uptr_t; static inline void __user *compat_ptr(compat_uptr_t uptr) { diff --git a/arch/parisc/include/asm/compat.h b/arch/parisc/include/asm/compat.h index 760f331d4fa..db7a662691a 100644 --- a/arch/parisc/include/asm/compat.h +++ b/arch/parisc/include/asm/compat.h @@ -36,6 +36,7 @@ typedef s64 compat_s64; typedef u32 compat_uint_t; typedef u32 compat_ulong_t; typedef u64 compat_u64; +typedef u32 compat_uptr_t; struct compat_timespec { compat_time_t tv_sec; @@ -127,6 +128,63 @@ typedef u32 compat_old_sigset_t; /* at least 32 bits */ typedef u32 compat_sigset_word; +typedef union compat_sigval { + compat_int_t sival_int; + compat_uptr_t sival_ptr; +} compat_sigval_t; + +typedef struct compat_siginfo { + int si_signo; + int si_errno; + int si_code; + + union { + int _pad[128/sizeof(int) - 3]; + + /* kill() */ + struct { + unsigned int _pid; /* sender's pid */ + unsigned int _uid; /* sender's uid */ + } _kill; + + /* POSIX.1b timers */ + struct { + compat_timer_t _tid; /* timer id */ + int _overrun; /* overrun count */ + char _pad[sizeof(unsigned int) - sizeof(int)]; + compat_sigval_t _sigval; /* same as below */ + int _sys_private; /* not to be passed to user */ + } _timer; + + /* POSIX.1b signals */ + struct { + unsigned int _pid; /* sender's pid */ + unsigned int _uid; /* sender's uid */ + compat_sigval_t _sigval; + } _rt; + + /* SIGCHLD */ + struct { + unsigned int _pid; /* which child */ + unsigned int _uid; /* sender's uid */ + int _status; /* exit code */ + compat_clock_t _utime; + compat_clock_t _stime; + } _sigchld; + + /* SIGILL, SIGFPE, SIGSEGV, SIGBUS */ + struct { + unsigned int _addr; /* faulting insn/memory ref. */ + } _sigfault; + + /* SIGPOLL */ + struct { + int _band; /* POLL_IN, POLL_OUT, POLL_MSG */ + int _fd; + } _sigpoll; + } _sifields; +} compat_siginfo_t; + #define COMPAT_OFF_T_MAX 0x7fffffff #define COMPAT_LOFF_T_MAX 0x7fffffffffffffffL @@ -136,7 +194,6 @@ typedef u32 compat_sigset_word; * as pointers because the syscall entry code will have * appropriately converted them already. */ -typedef u32 compat_uptr_t; static inline void __user *compat_ptr(compat_uptr_t uptr) { diff --git a/arch/parisc/kernel/signal32.h b/arch/parisc/kernel/signal32.h index c7800846422..08a88b5349a 100644 --- a/arch/parisc/kernel/signal32.h +++ b/arch/parisc/kernel/signal32.h @@ -55,58 +55,6 @@ struct k_sigaction32 { struct compat_sigaction sa; }; -typedef struct compat_siginfo { - int si_signo; - int si_errno; - int si_code; - - union { - int _pad[((128/sizeof(int)) - 3)]; - - /* kill() */ - struct { - unsigned int _pid; /* sender's pid */ - unsigned int _uid; /* sender's uid */ - } _kill; - - /* POSIX.1b timers */ - struct { - compat_timer_t _tid; /* timer id */ - int _overrun; /* overrun count */ - char _pad[sizeof(unsigned int) - sizeof(int)]; - compat_sigval_t _sigval; /* same as below */ - int _sys_private; /* not to be passed to user */ - } _timer; - - /* POSIX.1b signals */ - struct { - unsigned int _pid; /* sender's pid */ - unsigned int _uid; /* sender's uid */ - compat_sigval_t _sigval; - } _rt; - - /* SIGCHLD */ - struct { - unsigned int _pid; /* which child */ - unsigned int _uid; /* sender's uid */ - int _status; /* exit code */ - compat_clock_t _utime; - compat_clock_t _stime; - } _sigchld; - - /* SIGILL, SIGFPE, SIGSEGV, SIGBUS */ - struct { - unsigned int _addr; /* faulting insn/memory ref. */ - } _sigfault; - - /* SIGPOLL */ - struct { - int _band; /* POLL_IN, POLL_OUT, POLL_MSG */ - int _fd; - } _sigpoll; - } _sifields; -} compat_siginfo_t; - int copy_siginfo_to_user32 (compat_siginfo_t __user *to, siginfo_t *from); int copy_siginfo_from_user32 (siginfo_t *to, compat_siginfo_t __user *from); diff --git a/arch/powerpc/include/asm/compat.h b/arch/powerpc/include/asm/compat.h index 88e602f6430..84fdf6857c3 100644 --- a/arch/powerpc/include/asm/compat.h +++ b/arch/powerpc/include/asm/compat.h @@ -38,6 +38,7 @@ typedef s64 compat_s64; typedef u32 compat_uint_t; typedef u32 compat_ulong_t; typedef u64 compat_u64; +typedef u32 compat_uptr_t; struct compat_timespec { compat_time_t tv_sec; @@ -114,6 +115,64 @@ typedef u32 compat_old_sigset_t; typedef u32 compat_sigset_word; +typedef union compat_sigval { + compat_int_t sival_int; + compat_uptr_t sival_ptr; +} compat_sigval_t; + +#define SI_PAD_SIZE32 (128/sizeof(int) - 3) + +typedef struct compat_siginfo { + int si_signo; + int si_errno; + int si_code; + + union { + int _pad[SI_PAD_SIZE32]; + + /* kill() */ + struct { + compat_pid_t _pid; /* sender's pid */ + __compat_uid_t _uid; /* sender's uid */ + } _kill; + + /* POSIX.1b timers */ + struct { + compat_timer_t _tid; /* timer id */ + int _overrun; /* overrun count */ + compat_sigval_t _sigval; /* same as below */ + int _sys_private; /* not to be passed to user */ + } _timer; + + /* POSIX.1b signals */ + struct { + compat_pid_t _pid; /* sender's pid */ + __compat_uid_t _uid; /* sender's uid */ + compat_sigval_t _sigval; + } _rt; + + /* SIGCHLD */ + struct { + compat_pid_t _pid; /* which child */ + __compat_uid_t _uid; /* sender's uid */ + int _status; /* exit code */ + compat_clock_t _utime; + compat_clock_t _stime; + } _sigchld; + + /* SIGILL, SIGFPE, SIGSEGV, SIGBUS, SIGEMT */ + struct { + unsigned int _addr; /* faulting insn/memory ref. */ + } _sigfault; + + /* SIGPOLL */ + struct { + int _band; /* POLL_IN, POLL_OUT, POLL_MSG */ + int _fd; + } _sigpoll; + } _sifields; +} compat_siginfo_t; + #define COMPAT_OFF_T_MAX 0x7fffffff #define COMPAT_LOFF_T_MAX 0x7fffffffffffffffL @@ -123,7 +182,6 @@ typedef u32 compat_sigset_word; * as pointers because the syscall entry code will have * appropriately converted them already. */ -typedef u32 compat_uptr_t; static inline void __user *compat_ptr(compat_uptr_t uptr) { diff --git a/arch/powerpc/include/asm/siginfo.h b/arch/powerpc/include/asm/siginfo.h index 49495b0534e..ccce3ef5cd8 100644 --- a/arch/powerpc/include/asm/siginfo.h +++ b/arch/powerpc/include/asm/siginfo.h @@ -10,7 +10,6 @@ #ifdef __powerpc64__ # define __ARCH_SI_PREAMBLE_SIZE (4 * sizeof(int)) -# define SI_PAD_SIZE32 ((SI_MAX_SIZE/sizeof(int)) - 3) #endif #include diff --git a/arch/powerpc/kernel/ppc32.h b/arch/powerpc/kernel/ppc32.h index dc16aefe1dd..02fb0ee2609 100644 --- a/arch/powerpc/kernel/ppc32.h +++ b/arch/powerpc/kernel/ppc32.h @@ -16,57 +16,6 @@ /* These are here to support 32-bit syscalls on a 64-bit kernel. */ -typedef struct compat_siginfo { - int si_signo; - int si_errno; - int si_code; - - union { - int _pad[SI_PAD_SIZE32]; - - /* kill() */ - struct { - compat_pid_t _pid; /* sender's pid */ - compat_uid_t _uid; /* sender's uid */ - } _kill; - - /* POSIX.1b timers */ - struct { - compat_timer_t _tid; /* timer id */ - int _overrun; /* overrun count */ - compat_sigval_t _sigval; /* same as below */ - int _sys_private; /* not to be passed to user */ - } _timer; - - /* POSIX.1b signals */ - struct { - compat_pid_t _pid; /* sender's pid */ - compat_uid_t _uid; /* sender's uid */ - compat_sigval_t _sigval; - } _rt; - - /* SIGCHLD */ - struct { - compat_pid_t _pid; /* which child */ - compat_uid_t _uid; /* sender's uid */ - int _status; /* exit code */ - compat_clock_t _utime; - compat_clock_t _stime; - } _sigchld; - - /* SIGILL, SIGFPE, SIGSEGV, SIGBUS, SIGEMT */ - struct { - unsigned int _addr; /* faulting insn/memory ref. */ - } _sigfault; - - /* SIGPOLL */ - struct { - int _band; /* POLL_IN, POLL_OUT, POLL_MSG */ - int _fd; - } _sigpoll; - } _sifields; -} compat_siginfo_t; - #define __old_sigaction32 old_sigaction32 struct __old_sigaction32 { diff --git a/arch/s390/include/asm/compat.h b/arch/s390/include/asm/compat.h index 234f1d859ce..a34a9d612fc 100644 --- a/arch/s390/include/asm/compat.h +++ b/arch/s390/include/asm/compat.h @@ -65,6 +65,7 @@ typedef s64 compat_s64; typedef u32 compat_uint_t; typedef u32 compat_ulong_t; typedef u64 compat_u64; +typedef u32 compat_uptr_t; struct compat_timespec { compat_time_t tv_sec; @@ -144,6 +145,79 @@ typedef u32 compat_old_sigset_t; /* at least 32 bits */ typedef u32 compat_sigset_word; +typedef union compat_sigval { + compat_int_t sival_int; + compat_uptr_t sival_ptr; +} compat_sigval_t; + +typedef struct compat_siginfo { + int si_signo; + int si_errno; + int si_code; + + union { + int _pad[128/sizeof(int) - 3]; + + /* kill() */ + struct { + pid_t _pid; /* sender's pid */ + uid_t _uid; /* sender's uid */ + } _kill; + + /* POSIX.1b timers */ + struct { + compat_timer_t _tid; /* timer id */ + int _overrun; /* overrun count */ + compat_sigval_t _sigval; /* same as below */ + int _sys_private; /* not to be passed to user */ + } _timer; + + /* POSIX.1b signals */ + struct { + pid_t _pid; /* sender's pid */ + uid_t _uid; /* sender's uid */ + compat_sigval_t _sigval; + } _rt; + + /* SIGCHLD */ + struct { + pid_t _pid; /* which child */ + uid_t _uid; /* sender's uid */ + int _status;/* exit code */ + compat_clock_t _utime; + compat_clock_t _stime; + } _sigchld; + + /* SIGILL, SIGFPE, SIGSEGV, SIGBUS */ + struct { + __u32 _addr; /* faulting insn/memory ref. - pointer */ + } _sigfault; + + /* SIGPOLL */ + struct { + int _band; /* POLL_IN, POLL_OUT, POLL_MSG */ + int _fd; + } _sigpoll; + } _sifields; +} compat_siginfo_t; + +/* + * How these fields are to be accessed. + */ +#define si_pid _sifields._kill._pid +#define si_uid _sifields._kill._uid +#define si_status _sifields._sigchld._status +#define si_utime _sifields._sigchld._utime +#define si_stime _sifields._sigchld._stime +#define si_value _sifields._rt._sigval +#define si_int _sifields._rt._sigval.sival_int +#define si_ptr _sifields._rt._sigval.sival_ptr +#define si_addr _sifields._sigfault._addr +#define si_band _sifields._sigpoll._band +#define si_fd _sifields._sigpoll._fd +#define si_tid _sifields._timer._tid +#define si_overrun _sifields._timer._overrun + #define COMPAT_OFF_T_MAX 0x7fffffff #define COMPAT_LOFF_T_MAX 0x7fffffffffffffffL @@ -153,7 +227,6 @@ typedef u32 compat_sigset_word; * as pointers because the syscall entry code will have * appropriately converted them already. */ -typedef u32 compat_uptr_t; static inline void __user *compat_ptr(compat_uptr_t uptr) { diff --git a/arch/s390/kernel/compat_linux.h b/arch/s390/kernel/compat_linux.h index 9635d759c2b..90887bd98cf 100644 --- a/arch/s390/kernel/compat_linux.h +++ b/arch/s390/kernel/compat_linux.h @@ -23,74 +23,6 @@ struct old_sigaction32 { __u32 sa_flags; __u32 sa_restorer; /* Another 32 bit pointer */ }; - -typedef struct compat_siginfo { - int si_signo; - int si_errno; - int si_code; - - union { - int _pad[((128/sizeof(int)) - 3)]; - - /* kill() */ - struct { - pid_t _pid; /* sender's pid */ - uid_t _uid; /* sender's uid */ - } _kill; - - /* POSIX.1b timers */ - struct { - compat_timer_t _tid; /* timer id */ - int _overrun; /* overrun count */ - compat_sigval_t _sigval; /* same as below */ - int _sys_private; /* not to be passed to user */ - } _timer; - - /* POSIX.1b signals */ - struct { - pid_t _pid; /* sender's pid */ - uid_t _uid; /* sender's uid */ - compat_sigval_t _sigval; - } _rt; - - /* SIGCHLD */ - struct { - pid_t _pid; /* which child */ - uid_t _uid; /* sender's uid */ - int _status;/* exit code */ - compat_clock_t _utime; - compat_clock_t _stime; - } _sigchld; - - /* SIGILL, SIGFPE, SIGSEGV, SIGBUS */ - struct { - __u32 _addr; /* faulting insn/memory ref. - pointer */ - } _sigfault; - - /* SIGPOLL */ - struct { - int _band; /* POLL_IN, POLL_OUT, POLL_MSG */ - int _fd; - } _sigpoll; - } _sifields; -} compat_siginfo_t; - -/* - * How these fields are to be accessed. - */ -#define si_pid _sifields._kill._pid -#define si_uid _sifields._kill._uid -#define si_status _sifields._sigchld._status -#define si_utime _sifields._sigchld._utime -#define si_stime _sifields._sigchld._stime -#define si_value _sifields._rt._sigval -#define si_int _sifields._rt._sigval.sival_int -#define si_ptr _sifields._rt._sigval.sival_ptr -#define si_addr _sifields._sigfault._addr -#define si_band _sifields._sigpoll._band -#define si_fd _sifields._sigpoll._fd -#define si_tid _sifields._timer._tid -#define si_overrun _sifields._timer._overrun /* asm/sigcontext.h */ typedef union diff --git a/arch/sparc/include/asm/compat.h b/arch/sparc/include/asm/compat.h index b8be20d42a0..cef99fbc0a2 100644 --- a/arch/sparc/include/asm/compat.h +++ b/arch/sparc/include/asm/compat.h @@ -36,6 +36,7 @@ typedef s64 compat_s64; typedef u32 compat_uint_t; typedef u32 compat_ulong_t; typedef u64 compat_u64; +typedef u32 compat_uptr_t; struct compat_timespec { compat_time_t tv_sec; @@ -147,6 +148,65 @@ typedef u32 compat_old_sigset_t; typedef u32 compat_sigset_word; +typedef union compat_sigval { + compat_int_t sival_int; + compat_uptr_t sival_ptr; +} compat_sigval_t; + +#define SI_PAD_SIZE32 (128/sizeof(int) - 3) + +typedef struct compat_siginfo { + int si_signo; + int si_errno; + int si_code; + + union { + int _pad[SI_PAD_SIZE32]; + + /* kill() */ + struct { + compat_pid_t _pid; /* sender's pid */ + unsigned int _uid; /* sender's uid */ + } _kill; + + /* POSIX.1b timers */ + struct { + compat_timer_t _tid; /* timer id */ + int _overrun; /* overrun count */ + compat_sigval_t _sigval; /* same as below */ + int _sys_private; /* not to be passed to user */ + } _timer; + + /* POSIX.1b signals */ + struct { + compat_pid_t _pid; /* sender's pid */ + unsigned int _uid; /* sender's uid */ + compat_sigval_t _sigval; + } _rt; + + /* SIGCHLD */ + struct { + compat_pid_t _pid; /* which child */ + unsigned int _uid; /* sender's uid */ + int _status; /* exit code */ + compat_clock_t _utime; + compat_clock_t _stime; + } _sigchld; + + /* SIGILL, SIGFPE, SIGSEGV, SIGBUS, SIGEMT */ + struct { + u32 _addr; /* faulting insn/memory ref. */ + int _trapno; + } _sigfault; + + /* SIGPOLL */ + struct { + int _band; /* POLL_IN, POLL_OUT, POLL_MSG */ + int _fd; + } _sigpoll; + } _sifields; +} compat_siginfo_t; + #define COMPAT_OFF_T_MAX 0x7fffffff #define COMPAT_LOFF_T_MAX 0x7fffffffffffffffL @@ -156,7 +216,6 @@ typedef u32 compat_sigset_word; * as pointers because the syscall entry code will have * appropriately converted them already. */ -typedef u32 compat_uptr_t; static inline void __user *compat_ptr(compat_uptr_t uptr) { diff --git a/arch/sparc/include/asm/siginfo.h b/arch/sparc/include/asm/siginfo.h index 215900fce21..dbc182c438b 100644 --- a/arch/sparc/include/asm/siginfo.h +++ b/arch/sparc/include/asm/siginfo.h @@ -3,7 +3,6 @@ #if defined(__sparc__) && defined(__arch64__) -#define SI_PAD_SIZE32 ((SI_MAX_SIZE/sizeof(int)) - 3) #define __ARCH_SI_PREAMBLE_SIZE (4 * sizeof(int)) #define __ARCH_SI_BAND_T int diff --git a/arch/sparc/kernel/signal32.c b/arch/sparc/kernel/signal32.c index a53e0a5fd3a..53e48f721ce 100644 --- a/arch/sparc/kernel/signal32.c +++ b/arch/sparc/kernel/signal32.c @@ -54,58 +54,6 @@ struct signal_frame32 { /* __siginfo_rwin_t * */u32 rwin_save; } __attribute__((aligned(8))); -typedef struct compat_siginfo{ - int si_signo; - int si_errno; - int si_code; - - union { - int _pad[SI_PAD_SIZE32]; - - /* kill() */ - struct { - compat_pid_t _pid; /* sender's pid */ - unsigned int _uid; /* sender's uid */ - } _kill; - - /* POSIX.1b timers */ - struct { - compat_timer_t _tid; /* timer id */ - int _overrun; /* overrun count */ - compat_sigval_t _sigval; /* same as below */ - int _sys_private; /* not to be passed to user */ - } _timer; - - /* POSIX.1b signals */ - struct { - compat_pid_t _pid; /* sender's pid */ - unsigned int _uid; /* sender's uid */ - compat_sigval_t _sigval; - } _rt; - - /* SIGCHLD */ - struct { - compat_pid_t _pid; /* which child */ - unsigned int _uid; /* sender's uid */ - int _status; /* exit code */ - compat_clock_t _utime; - compat_clock_t _stime; - } _sigchld; - - /* SIGILL, SIGFPE, SIGSEGV, SIGBUS, SIGEMT */ - struct { - u32 _addr; /* faulting insn/memory ref. */ - int _trapno; - } _sigfault; - - /* SIGPOLL */ - struct { - int _band; /* POLL_IN, POLL_OUT, POLL_MSG */ - int _fd; - } _sigpoll; - } _sifields; -}compat_siginfo_t; - struct rt_signal_frame32 { struct sparc_stackf32 ss; compat_siginfo_t info; diff --git a/arch/tile/include/asm/compat.h b/arch/tile/include/asm/compat.h index 6e74450ff0a..3063e6fc8da 100644 --- a/arch/tile/include/asm/compat.h +++ b/arch/tile/include/asm/compat.h @@ -110,6 +110,68 @@ struct compat_flock64 { typedef u32 compat_sigset_word; +typedef union compat_sigval { + compat_int_t sival_int; + compat_uptr_t sival_ptr; +} compat_sigval_t; + +#define COMPAT_SI_PAD_SIZE (128/sizeof(int) - 3) + +typedef struct compat_siginfo { + int si_signo; + int si_errno; + int si_code; + + union { + int _pad[COMPAT_SI_PAD_SIZE]; + + /* kill() */ + struct { + unsigned int _pid; /* sender's pid */ + unsigned int _uid; /* sender's uid */ + } _kill; + + /* POSIX.1b timers */ + struct { + compat_timer_t _tid; /* timer id */ + int _overrun; /* overrun count */ + compat_sigval_t _sigval; /* same as below */ + int _sys_private; /* not to be passed to user */ + int _overrun_incr; /* amount to add to overrun */ + } _timer; + + /* POSIX.1b signals */ + struct { + unsigned int _pid; /* sender's pid */ + unsigned int _uid; /* sender's uid */ + compat_sigval_t _sigval; + } _rt; + + /* SIGCHLD */ + struct { + unsigned int _pid; /* which child */ + unsigned int _uid; /* sender's uid */ + int _status; /* exit code */ + compat_clock_t _utime; + compat_clock_t _stime; + } _sigchld; + + /* SIGILL, SIGFPE, SIGSEGV, SIGBUS */ + struct { + unsigned int _addr; /* faulting insn/memory ref. */ +#ifdef __ARCH_SI_TRAPNO + int _trapno; /* TRAP # which caused the signal */ +#endif + } _sigfault; + + /* SIGPOLL */ + struct { + int _band; /* POLL_IN, POLL_OUT, POLL_MSG */ + int _fd; + } _sigpoll; + } _sifields; +} compat_siginfo_t; + #define COMPAT_OFF_T_MAX 0x7fffffff #define COMPAT_LOFF_T_MAX 0x7fffffffffffffffL diff --git a/arch/tile/kernel/compat_signal.c b/arch/tile/kernel/compat_signal.c index 474571b8408..7bc0859a9f5 100644 --- a/arch/tile/kernel/compat_signal.c +++ b/arch/tile/kernel/compat_signal.c @@ -55,63 +55,6 @@ struct compat_ucontext { sigset_t uc_sigmask; /* mask last for extensibility */ }; -#define COMPAT_SI_PAD_SIZE ((SI_MAX_SIZE - 3 * sizeof(int)) / sizeof(int)) - -struct compat_siginfo { - int si_signo; - int si_errno; - int si_code; - - union { - int _pad[COMPAT_SI_PAD_SIZE]; - - /* kill() */ - struct { - unsigned int _pid; /* sender's pid */ - unsigned int _uid; /* sender's uid */ - } _kill; - - /* POSIX.1b timers */ - struct { - compat_timer_t _tid; /* timer id */ - int _overrun; /* overrun count */ - compat_sigval_t _sigval; /* same as below */ - int _sys_private; /* not to be passed to user */ - int _overrun_incr; /* amount to add to overrun */ - } _timer; - - /* POSIX.1b signals */ - struct { - unsigned int _pid; /* sender's pid */ - unsigned int _uid; /* sender's uid */ - compat_sigval_t _sigval; - } _rt; - - /* SIGCHLD */ - struct { - unsigned int _pid; /* which child */ - unsigned int _uid; /* sender's uid */ - int _status; /* exit code */ - compat_clock_t _utime; - compat_clock_t _stime; - } _sigchld; - - /* SIGILL, SIGFPE, SIGSEGV, SIGBUS */ - struct { - unsigned int _addr; /* faulting insn/memory ref. */ -#ifdef __ARCH_SI_TRAPNO - int _trapno; /* TRAP # which caused the signal */ -#endif - } _sigfault; - - /* SIGPOLL */ - struct { - int _band; /* POLL_IN, POLL_OUT, POLL_MSG */ - int _fd; - } _sigpoll; - } _sifields; -}; - struct compat_rt_sigframe { unsigned char save_area[C_ABI_SAVE_AREA_SIZE]; /* caller save area */ struct compat_siginfo info; diff --git a/arch/x86/include/asm/compat.h b/arch/x86/include/asm/compat.h index fedf32b73e6..59c6c401f79 100644 --- a/arch/x86/include/asm/compat.h +++ b/arch/x86/include/asm/compat.h @@ -41,6 +41,7 @@ typedef s64 __attribute__((aligned(4))) compat_s64; typedef u32 compat_uint_t; typedef u32 compat_ulong_t; typedef u64 __attribute__((aligned(4))) compat_u64; +typedef u32 compat_uptr_t; struct compat_timespec { compat_time_t tv_sec; @@ -124,6 +125,78 @@ typedef u32 compat_old_sigset_t; /* at least 32 bits */ typedef u32 compat_sigset_word; +typedef union compat_sigval { + compat_int_t sival_int; + compat_uptr_t sival_ptr; +} compat_sigval_t; + +typedef struct compat_siginfo { + int si_signo; + int si_errno; + int si_code; + + union { + int _pad[128/sizeof(int) - 3]; + + /* kill() */ + struct { + unsigned int _pid; /* sender's pid */ + unsigned int _uid; /* sender's uid */ + } _kill; + + /* POSIX.1b timers */ + struct { + compat_timer_t _tid; /* timer id */ + int _overrun; /* overrun count */ + compat_sigval_t _sigval; /* same as below */ + int _sys_private; /* not to be passed to user */ + int _overrun_incr; /* amount to add to overrun */ + } _timer; + + /* POSIX.1b signals */ + struct { + unsigned int _pid; /* sender's pid */ + unsigned int _uid; /* sender's uid */ + compat_sigval_t _sigval; + } _rt; + + /* SIGCHLD */ + struct { + unsigned int _pid; /* which child */ + unsigned int _uid; /* sender's uid */ + int _status; /* exit code */ + compat_clock_t _utime; + compat_clock_t _stime; + } _sigchld; + + /* SIGCHLD (x32 version) */ + struct { + unsigned int _pid; /* which child */ + unsigned int _uid; /* sender's uid */ + int _status; /* exit code */ + compat_s64 _utime; + compat_s64 _stime; + } _sigchld_x32; + + /* SIGILL, SIGFPE, SIGSEGV, SIGBUS */ + struct { + unsigned int _addr; /* faulting insn/memory ref. */ + } _sigfault; + + /* SIGPOLL */ + struct { + int _band; /* POLL_IN, POLL_OUT, POLL_MSG */ + int _fd; + } _sigpoll; + + struct { + unsigned int _call_addr; /* calling insn */ + int _syscall; /* triggering system call number */ + unsigned int _arch; /* AUDIT_ARCH_* of syscall */ + } _sigsys; + } _sifields; +} compat_siginfo_t; + #define COMPAT_OFF_T_MAX 0x7fffffff #define COMPAT_LOFF_T_MAX 0x7fffffffffffffffL @@ -209,7 +282,6 @@ typedef struct user_regs_struct32 compat_elf_gregset_t; * as pointers because the syscall entry code will have * appropriately converted them already. */ -typedef u32 compat_uptr_t; static inline void __user *compat_ptr(compat_uptr_t uptr) { diff --git a/arch/x86/include/asm/ia32.h b/arch/x86/include/asm/ia32.h index b04cbdb138c..e6232773ce4 100644 --- a/arch/x86/include/asm/ia32.h +++ b/arch/x86/include/asm/ia32.h @@ -86,73 +86,6 @@ struct stat64 { unsigned long long st_ino; } __attribute__((packed)); -typedef struct compat_siginfo { - int si_signo; - int si_errno; - int si_code; - - union { - int _pad[((128 / sizeof(int)) - 3)]; - - /* kill() */ - struct { - unsigned int _pid; /* sender's pid */ - unsigned int _uid; /* sender's uid */ - } _kill; - - /* POSIX.1b timers */ - struct { - compat_timer_t _tid; /* timer id */ - int _overrun; /* overrun count */ - compat_sigval_t _sigval; /* same as below */ - int _sys_private; /* not to be passed to user */ - int _overrun_incr; /* amount to add to overrun */ - } _timer; - - /* POSIX.1b signals */ - struct { - unsigned int _pid; /* sender's pid */ - unsigned int _uid; /* sender's uid */ - compat_sigval_t _sigval; - } _rt; - - /* SIGCHLD */ - struct { - unsigned int _pid; /* which child */ - unsigned int _uid; /* sender's uid */ - int _status; /* exit code */ - compat_clock_t _utime; - compat_clock_t _stime; - } _sigchld; - - /* SIGCHLD (x32 version) */ - struct { - unsigned int _pid; /* which child */ - unsigned int _uid; /* sender's uid */ - int _status; /* exit code */ - compat_s64 _utime; - compat_s64 _stime; - } _sigchld_x32; - - /* SIGILL, SIGFPE, SIGSEGV, SIGBUS */ - struct { - unsigned int _addr; /* faulting insn/memory ref. */ - } _sigfault; - - /* SIGPOLL */ - struct { - int _band; /* POLL_IN, POLL_OUT, POLL_MSG */ - int _fd; - } _sigpoll; - - struct { - unsigned int _call_addr; /* calling insn */ - int _syscall; /* triggering system call number */ - unsigned int _arch; /* AUDIT_ARCH_* of syscall */ - } _sigsys; - } _sifields; -} compat_siginfo_t; - #define IA32_STACK_TOP IA32_PAGE_OFFSET #ifdef __KERNEL__ diff --git a/include/linux/compat.h b/include/linux/compat.h index fd4e29956d1..3f53d002c7c 100644 --- a/include/linux/compat.h +++ b/include/linux/compat.h @@ -160,11 +160,6 @@ struct compat_ustat { char f_fpack[6]; }; -typedef union compat_sigval { - compat_int_t sival_int; - compat_uptr_t sival_ptr; -} compat_sigval_t; - #define COMPAT_SIGEV_PAD_SIZE ((SIGEV_MAX_SIZE/sizeof(int)) - 3) typedef struct compat_sigevent { -- cgit v1.2.3-70-g09d2 From 49ae4d4b113be03dc4a2ec5f2a1f573ff0fcddb3 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 4 Oct 2012 17:15:35 -0700 Subject: coredump: add a new elf note with siginfo of the signal Existing PRSTATUS note contains only si_signo, si_code, si_errno fields from the siginfo of the signal which caused core to be dumped. There are tools which try to analyze crashes for possible security implications, and they want to use, among other data, si_addr field from the SIGSEGV. This patch adds a new elf note, NT_SIGINFO, which contains the complete siginfo_t of the signal which killed the process. Signed-off-by: Denys Vlasenko Reviewed-by: Oleg Nesterov Cc: Amerigo Wang Cc: "Jonathan M. Foote" Cc: Roland McGrath Cc: Pedro Alves Cc: Fengguang Wu Cc: Stephen Rothwell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/binfmt_elf.c | 27 +++++++++++++++++++++++++-- fs/compat_binfmt_elf.c | 6 ++++++ include/linux/elf.h | 5 +++++ 3 files changed, 36 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index 4450e82a05a..865f9be6a2d 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -37,6 +37,10 @@ #include #include +#ifndef user_siginfo_t +#define user_siginfo_t siginfo_t +#endif + static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs); static int load_elf_library(struct file *); static unsigned long elf_map(struct file *, unsigned long, struct elf_phdr *, @@ -1372,6 +1376,16 @@ static void fill_auxv_note(struct memelfnote *note, struct mm_struct *mm) fill_note(note, "CORE", NT_AUXV, i * sizeof(elf_addr_t), auxv); } +static void fill_siginfo_note(struct memelfnote *note, user_siginfo_t *csigdata, + siginfo_t *siginfo) +{ + mm_segment_t old_fs = get_fs(); + set_fs(KERNEL_DS); + copy_siginfo_to_user((user_siginfo_t __user *) csigdata, siginfo); + set_fs(old_fs); + fill_note(note, "CORE", NT_SIGINFO, sizeof(*csigdata), csigdata); +} + #ifdef CORE_DUMP_USE_REGSET #include @@ -1385,7 +1399,9 @@ struct elf_thread_core_info { struct elf_note_info { struct elf_thread_core_info *thread; struct memelfnote psinfo; + struct memelfnote signote; struct memelfnote auxv; + user_siginfo_t csigdata; size_t size; int thread_notes; }; @@ -1559,6 +1575,9 @@ static int fill_note_info(struct elfhdr *elf, int phdrs, fill_psinfo(psinfo, dump_task->group_leader, dump_task->mm); info->size += notesize(&info->psinfo); + fill_siginfo_note(&info->signote, &info->csigdata, siginfo); + info->size += notesize(&info->signote); + fill_auxv_note(&info->auxv, current->mm); info->size += notesize(&info->auxv); @@ -1588,6 +1607,8 @@ static int write_note_info(struct elf_note_info *info, if (first && !writenote(&info->psinfo, file, foffset)) return 0; + if (first && !writenote(&info->signote, file, foffset)) + return 0; if (first && !writenote(&info->auxv, file, foffset)) return 0; @@ -1681,6 +1702,7 @@ struct elf_note_info { #ifdef ELF_CORE_COPY_XFPREGS elf_fpxregset_t *xfpu; #endif + user_siginfo_t csigdata; int thread_status_size; int numnote; }; @@ -1690,8 +1712,8 @@ static int elf_note_info_init(struct elf_note_info *info) memset(info, 0, sizeof(*info)); INIT_LIST_HEAD(&info->thread_list); - /* Allocate space for six ELF notes */ - info->notes = kmalloc(6 * sizeof(struct memelfnote), GFP_KERNEL); + /* Allocate space for ELF notes */ + info->notes = kmalloc(7 * sizeof(struct memelfnote), GFP_KERNEL); if (!info->notes) return 0; info->psinfo = kmalloc(sizeof(*info->psinfo), GFP_KERNEL); @@ -1763,6 +1785,7 @@ static int fill_note_info(struct elfhdr *elf, int phdrs, info->numnote = 2; + fill_siginfo_note(&info->notes[info->numnote++], &info->csigdata, siginfo); fill_auxv_note(&info->notes[info->numnote++], current->mm); /* Try to dump the FPU. */ diff --git a/fs/compat_binfmt_elf.c b/fs/compat_binfmt_elf.c index 112e45a17e9..0fbcf634743 100644 --- a/fs/compat_binfmt_elf.c +++ b/fs/compat_binfmt_elf.c @@ -37,6 +37,12 @@ #define elf_note elf32_note #define elf_addr_t Elf32_Addr +/* + * Some data types as stored in coredump. + */ +#define user_siginfo_t compat_siginfo_t +#define copy_siginfo_to_user copy_siginfo_to_user32 + /* * The machine-dependent core note format types are defined in elfcore-compat.h, * which requires asm/elf.h to define compat_elf_gregset_t et al. diff --git a/include/linux/elf.h b/include/linux/elf.h index 0a05051a892..dc62da7447c 100644 --- a/include/linux/elf.h +++ b/include/linux/elf.h @@ -372,6 +372,11 @@ typedef struct elf64_shdr { #define NT_PRPSINFO 3 #define NT_TASKSTRUCT 4 #define NT_AUXV 6 +/* + * Note to userspace developers: size of NT_SIGINFO note may increase + * in the future to accomodate more fields, don't assume it is fixed! + */ +#define NT_SIGINFO 0x53494749 #define NT_PRXFPREG 0x46e62b7f /* copied from gdb5.1/include/elf/common.h */ #define NT_PPC_VMX 0x100 /* PowerPC Altivec/VMX registers */ #define NT_PPC_SPE 0x101 /* PowerPC SPE/EVR registers */ -- cgit v1.2.3-70-g09d2 From 2aa362c49c314a98fb9aebbd7760a461667bac05 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 4 Oct 2012 17:15:36 -0700 Subject: coredump: extend core dump note section to contain file names of mapped files This note has the following format: long count -- how many files are mapped long page_size -- units for file_ofs array of [COUNT] elements of long start long end long file_ofs followed by COUNT filenames in ASCII: "FILE1" NUL "FILE2" NUL... Signed-off-by: Denys Vlasenko Cc: Oleg Nesterov Cc: Amerigo Wang Cc: "Jonathan M. Foote" Cc: Roland McGrath Cc: Pedro Alves Cc: Fengguang Wu Cc: Stephen Rothwell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/binfmt_elf.c | 110 +++++++++++++++++++++++++++++++++++++++++++++++-- fs/compat_binfmt_elf.c | 1 + include/linux/elf.h | 1 + 3 files changed, 108 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index 865f9be6a2d..28a64e76952 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -37,6 +38,9 @@ #include #include +#ifndef user_long_t +#define user_long_t long +#endif #ifndef user_siginfo_t #define user_siginfo_t siginfo_t #endif @@ -1386,6 +1390,93 @@ static void fill_siginfo_note(struct memelfnote *note, user_siginfo_t *csigdata, fill_note(note, "CORE", NT_SIGINFO, sizeof(*csigdata), csigdata); } +#define MAX_FILE_NOTE_SIZE (4*1024*1024) +/* + * Format of NT_FILE note: + * + * long count -- how many files are mapped + * long page_size -- units for file_ofs + * array of [COUNT] elements of + * long start + * long end + * long file_ofs + * followed by COUNT filenames in ASCII: "FILE1" NUL "FILE2" NUL... + */ +static void fill_files_note(struct memelfnote *note) +{ + struct vm_area_struct *vma; + unsigned count, size, names_ofs, remaining, n; + user_long_t *data; + user_long_t *start_end_ofs; + char *name_base, *name_curpos; + + /* *Estimated* file count and total data size needed */ + count = current->mm->map_count; + size = count * 64; + + names_ofs = (2 + 3 * count) * sizeof(data[0]); + alloc: + if (size >= MAX_FILE_NOTE_SIZE) /* paranoia check */ + goto err; + size = round_up(size, PAGE_SIZE); + data = vmalloc(size); + if (!data) + goto err; + + start_end_ofs = data + 2; + name_base = name_curpos = ((char *)data) + names_ofs; + remaining = size - names_ofs; + count = 0; + for (vma = current->mm->mmap; vma != NULL; vma = vma->vm_next) { + struct file *file; + const char *filename; + + file = vma->vm_file; + if (!file) + continue; + filename = d_path(&file->f_path, name_curpos, remaining); + if (IS_ERR(filename)) { + if (PTR_ERR(filename) == -ENAMETOOLONG) { + vfree(data); + size = size * 5 / 4; + goto alloc; + } + continue; + } + + /* d_path() fills at the end, move name down */ + /* n = strlen(filename) + 1: */ + n = (name_curpos + remaining) - filename; + remaining = filename - name_curpos; + memmove(name_curpos, filename, n); + name_curpos += n; + + *start_end_ofs++ = vma->vm_start; + *start_end_ofs++ = vma->vm_end; + *start_end_ofs++ = vma->vm_pgoff; + count++; + } + + /* Now we know exact count of files, can store it */ + data[0] = count; + data[1] = PAGE_SIZE; + /* + * Count usually is less than current->mm->map_count, + * we need to move filenames down. + */ + n = current->mm->map_count - count; + if (n != 0) { + unsigned shift_bytes = n * 3 * sizeof(data[0]); + memmove(name_base - shift_bytes, name_base, + name_curpos - name_base); + name_curpos -= shift_bytes; + } + + size = name_curpos - (char *)data; + fill_note(note, "CORE", NT_FILE, size, data); + err: ; +} + #ifdef CORE_DUMP_USE_REGSET #include @@ -1401,6 +1492,7 @@ struct elf_note_info { struct memelfnote psinfo; struct memelfnote signote; struct memelfnote auxv; + struct memelfnote files; user_siginfo_t csigdata; size_t size; int thread_notes; @@ -1581,6 +1673,9 @@ static int fill_note_info(struct elfhdr *elf, int phdrs, fill_auxv_note(&info->auxv, current->mm); info->size += notesize(&info->auxv); + fill_files_note(&info->files); + info->size += notesize(&info->files); + return 1; } @@ -1611,6 +1706,8 @@ static int write_note_info(struct elf_note_info *info, return 0; if (first && !writenote(&info->auxv, file, foffset)) return 0; + if (first && !writenote(&info->files, file, foffset)) + return 0; for (i = 1; i < info->thread_notes; ++i) if (t->notes[i].data && @@ -1637,6 +1734,7 @@ static void free_note_info(struct elf_note_info *info) kfree(t); } kfree(info->psinfo.data); + vfree(info->files.data); } #else @@ -1713,7 +1811,7 @@ static int elf_note_info_init(struct elf_note_info *info) INIT_LIST_HEAD(&info->thread_list); /* Allocate space for ELF notes */ - info->notes = kmalloc(7 * sizeof(struct memelfnote), GFP_KERNEL); + info->notes = kmalloc(8 * sizeof(struct memelfnote), GFP_KERNEL); if (!info->notes) return 0; info->psinfo = kmalloc(sizeof(*info->psinfo), GFP_KERNEL); @@ -1783,10 +1881,11 @@ static int fill_note_info(struct elfhdr *elf, int phdrs, fill_note(info->notes + 1, "CORE", NT_PRPSINFO, sizeof(*info->psinfo), info->psinfo); - info->numnote = 2; + fill_siginfo_note(info->notes + 2, &info->csigdata, siginfo); + fill_auxv_note(info->notes + 3, current->mm); + fill_files_note(info->notes + 4); - fill_siginfo_note(&info->notes[info->numnote++], &info->csigdata, siginfo); - fill_auxv_note(&info->notes[info->numnote++], current->mm); + info->numnote = 5; /* Try to dump the FPU. */ info->prstatus->pr_fpvalid = elf_core_copy_task_fpregs(current, regs, @@ -1848,6 +1947,9 @@ static void free_note_info(struct elf_note_info *info) kfree(list_entry(tmp, struct elf_thread_status, list)); } + /* Free data allocated by fill_files_note(): */ + vfree(info->notes[4].data); + kfree(info->prstatus); kfree(info->psinfo); kfree(info->notes); diff --git a/fs/compat_binfmt_elf.c b/fs/compat_binfmt_elf.c index 0fbcf634743..a81147e2e4e 100644 --- a/fs/compat_binfmt_elf.c +++ b/fs/compat_binfmt_elf.c @@ -40,6 +40,7 @@ /* * Some data types as stored in coredump. */ +#define user_long_t compat_long_t #define user_siginfo_t compat_siginfo_t #define copy_siginfo_to_user copy_siginfo_to_user32 diff --git a/include/linux/elf.h b/include/linux/elf.h index dc62da7447c..59ef40650e1 100644 --- a/include/linux/elf.h +++ b/include/linux/elf.h @@ -377,6 +377,7 @@ typedef struct elf64_shdr { * in the future to accomodate more fields, don't assume it is fixed! */ #define NT_SIGINFO 0x53494749 +#define NT_FILE 0x46494c45 #define NT_PRXFPREG 0x46e62b7f /* copied from gdb5.1/include/elf/common.h */ #define NT_PPC_VMX 0x100 /* PowerPC Altivec/VMX registers */ #define NT_PPC_SPE 0x101 /* PowerPC SPE/EVR registers */ -- cgit v1.2.3-70-g09d2 From ed43f44f868045baa27bc140db0d4898e9695d07 Mon Sep 17 00:00:00 2001 From: Alexandre Bounine Date: Thu, 4 Oct 2012 17:15:51 -0700 Subject: rapidio/tsi721: modify mport name assignment Modify RapidIO mport device name assignment to include device name of PCIe side of Tsi721 bridge. The new name format is intended to provide definitive reference between RapidIO and PCIe sides of the bridge in systems with multiple Tsi721 bridges. Signed-off-by: Alexandre Bounine Cc: Matt Porter Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rapidio/devices/tsi721.c | 3 ++- include/linux/rio.h | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/rapidio/devices/tsi721.c b/drivers/rapidio/devices/tsi721.c index d5e1625bbac..19743597cd9 100644 --- a/drivers/rapidio/devices/tsi721.c +++ b/drivers/rapidio/devices/tsi721.c @@ -2165,7 +2165,8 @@ static int __devinit tsi721_setup_mport(struct tsi721_device *priv) rio_init_dbell_res(&mport->riores[RIO_DOORBELL_RESOURCE], 0, 0xffff); rio_init_mbox_res(&mport->riores[RIO_INB_MBOX_RESOURCE], 0, 3); rio_init_mbox_res(&mport->riores[RIO_OUTB_MBOX_RESOURCE], 0, 3); - strcpy(mport->name, "Tsi721 mport"); + snprintf(mport->name, RIO_MAX_MPORT_NAME, "%s(%s)", + dev_driver_string(&pdev->dev), dev_name(&pdev->dev)); /* Hook up interrupt handler */ diff --git a/include/linux/rio.h b/include/linux/rio.h index a90ebadd9da..1a7b6c7787a 100644 --- a/include/linux/rio.h +++ b/include/linux/rio.h @@ -30,6 +30,7 @@ #define RIO_MAX_MPORTS 8 #define RIO_MAX_MPORT_RESOURCES 16 #define RIO_MAX_DEV_RESOURCES 16 +#define RIO_MAX_MPORT_NAME 40 #define RIO_GLOBAL_TABLE 0xff /* Indicates access of a switch's global routing table if it @@ -255,7 +256,7 @@ struct rio_mport { */ enum rio_phy_type phy_type; /* RapidIO phy type */ u32 phys_efptr; - unsigned char name[40]; + unsigned char name[RIO_MAX_MPORT_NAME]; void *priv; /* Master port private data */ #ifdef CONFIG_RAPIDIO_DMA_ENGINE struct dma_device dma; -- cgit v1.2.3-70-g09d2 From fe50c927d7ab17983ab05d3e29e73395dcd577b6 Mon Sep 17 00:00:00 2001 From: Alexandre Bounine Date: Thu, 4 Oct 2012 17:15:52 -0700 Subject: rapidio: fix kerneldoc warnings after DMA support was added Signed-off-by: Alexandre Bounine Reported-by: Robert P. J. Day Cc: Robert P. J. Day Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/rio.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/rio.h b/include/linux/rio.h index 1a7b6c7787a..ba382f2b57e 100644 --- a/include/linux/rio.h +++ b/include/linux/rio.h @@ -236,6 +236,7 @@ enum rio_phy_type { * @phys_efptr: RIO port extended features pointer * @name: Port name string * @priv: Master port private data + * @dma: DMA device associated with mport */ struct rio_mport { struct list_head dbells; /* list of doorbell events */ @@ -404,7 +405,7 @@ union rio_pw_msg { #ifdef CONFIG_RAPIDIO_DMA_ENGINE -/** +/* * enum rio_write_type - RIO write transaction types used in DMA transfers * * Note: RapidIO specification defines write (NWRITE) and -- cgit v1.2.3-70-g09d2 From da1589f07393e88e917c317ea0c85ea37918a1cf Mon Sep 17 00:00:00 2001 From: Alexandre Bounine Date: Thu, 4 Oct 2012 17:15:57 -0700 Subject: rapidio: add inbound memory mapping interface Add common inbound memory mapping/unmapping interface. This allows to make local memory space accessible from the RapidIO side using hardware mapping capabilities of RapidIO bridging devices. The new interface is intended to enable data transfers between RapidIO devices in combination with DMA engine support. This patch is based on patch submitted by Li Yang (https://lists.ozlabs.org/pipermail/linuxppc-dev/2009-April/071210.html) Signed-off-by: Alexandre Bounine Cc: Matt Porter Cc: Li Yang Cc: Kumar Gala Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rapidio/rio.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/rio.h | 5 +++++ include/linux/rio_drv.h | 5 +++++ 3 files changed, 54 insertions(+) (limited to 'include/linux') diff --git a/drivers/rapidio/rio.c b/drivers/rapidio/rio.c index c40665a4fa3..d7b68cc2d84 100644 --- a/drivers/rapidio/rio.c +++ b/drivers/rapidio/rio.c @@ -33,6 +33,7 @@ static LIST_HEAD(rio_mports); static unsigned char next_portid; +static DEFINE_SPINLOCK(rio_mmap_lock); /** * rio_local_get_device_id - Get the base/extended device id for a port @@ -397,6 +398,49 @@ int rio_release_inb_pwrite(struct rio_dev *rdev) } EXPORT_SYMBOL_GPL(rio_release_inb_pwrite); +/** + * rio_map_inb_region -- Map inbound memory region. + * @mport: Master port. + * @lstart: physical address of memory region to be mapped + * @rbase: RIO base address assigned to this window + * @size: Size of the memory region + * @rflags: Flags for mapping. + * + * Return: 0 -- Success. + * + * This function will create the mapping from RIO space to local memory. + */ +int rio_map_inb_region(struct rio_mport *mport, dma_addr_t local, + u64 rbase, u32 size, u32 rflags) +{ + int rc = 0; + unsigned long flags; + + if (!mport->ops->map_inb) + return -1; + spin_lock_irqsave(&rio_mmap_lock, flags); + rc = mport->ops->map_inb(mport, local, rbase, size, rflags); + spin_unlock_irqrestore(&rio_mmap_lock, flags); + return rc; +} +EXPORT_SYMBOL_GPL(rio_map_inb_region); + +/** + * rio_unmap_inb_region -- Unmap the inbound memory region + * @mport: Master port + * @lstart: physical address of memory region to be unmapped + */ +void rio_unmap_inb_region(struct rio_mport *mport, dma_addr_t lstart) +{ + unsigned long flags; + if (!mport->ops->unmap_inb) + return; + spin_lock_irqsave(&rio_mmap_lock, flags); + mport->ops->unmap_inb(mport, lstart); + spin_unlock_irqrestore(&rio_mmap_lock, flags); +} +EXPORT_SYMBOL_GPL(rio_unmap_inb_region); + /** * rio_mport_get_physefb - Helper function that returns register offset * for Physical Layer Extended Features Block. diff --git a/include/linux/rio.h b/include/linux/rio.h index ba382f2b57e..4d1a104cd78 100644 --- a/include/linux/rio.h +++ b/include/linux/rio.h @@ -301,6 +301,8 @@ struct rio_net { * @add_outb_message: Callback to add a message to an outbound mailbox queue. * @add_inb_buffer: Callback to add a buffer to an inbound mailbox queue. * @get_inb_message: Callback to get a message from an inbound mailbox queue. + * @map_inb: Callback to map RapidIO address region into local memory space. + * @unmap_inb: Callback to unmap RapidIO address region mapped with map_inb(). */ struct rio_ops { int (*lcread) (struct rio_mport *mport, int index, u32 offset, int len, @@ -323,6 +325,9 @@ struct rio_ops { int mbox, void *buffer, size_t len); int (*add_inb_buffer)(struct rio_mport *mport, int mbox, void *buf); void *(*get_inb_message)(struct rio_mport *mport, int mbox); + int (*map_inb)(struct rio_mport *mport, dma_addr_t lstart, + u64 rstart, u32 size, u32 flags); + void (*unmap_inb)(struct rio_mport *mport, dma_addr_t lstart); }; #define RIO_RESOURCE_MEM 0x00000100 diff --git a/include/linux/rio_drv.h b/include/linux/rio_drv.h index 31ad146be31..b75c05920ab 100644 --- a/include/linux/rio_drv.h +++ b/include/linux/rio_drv.h @@ -365,6 +365,11 @@ void rio_release_regions(struct rio_dev *); int rio_request_region(struct rio_dev *, int, char *); void rio_release_region(struct rio_dev *, int); +/* Memory mapping functions */ +extern int rio_map_inb_region(struct rio_mport *mport, dma_addr_t local, + u64 rbase, u32 size, u32 rflags); +extern void rio_unmap_inb_region(struct rio_mport *mport, dma_addr_t lstart); + /* Port-Write management */ extern int rio_request_inb_pwrite(struct rio_dev *, int (*)(struct rio_dev *, union rio_pw_msg*, int)); -- cgit v1.2.3-70-g09d2 From a7071efc20567f4b6c454ff93ca80daf51bf93e9 Mon Sep 17 00:00:00 2001 From: Alexandre Bounine Date: Thu, 4 Oct 2012 17:16:05 -0700 Subject: rapidio: use device lists handling on per-net basis Modify handling of device lists to resolve issues caused by using single global list of RIO devices during enumeration/discovery. The most common sign of existing issue is incorrect contents of switch routing tables in systems with multiple mport controllers while single-port configuration performs as expected. Signed-off-by: Alexandre Bounine Cc: Matt Porter Cc: Li Yang Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rapidio/rio-scan.c | 60 ++++++++++++++++++++++++---------------------- include/linux/rio.h | 1 + 2 files changed, 32 insertions(+), 29 deletions(-) (limited to 'include/linux') diff --git a/drivers/rapidio/rio-scan.c b/drivers/rapidio/rio-scan.c index 0a27253c921..8b7c4bce7a4 100644 --- a/drivers/rapidio/rio-scan.c +++ b/drivers/rapidio/rio-scan.c @@ -38,7 +38,6 @@ #include "rio.h" LIST_HEAD(rio_devices); -static LIST_HEAD(rio_switches); static void rio_init_em(struct rio_dev *rdev); @@ -104,14 +103,15 @@ static void rio_local_set_device_id(struct rio_mport *port, u16 did) /** * rio_clear_locks- Release all host locks and signal enumeration complete - * @port: Master port to issue transaction + * @net: RIO network to run on * * Marks the component tag CSR on each device with the enumeration * complete flag. When complete, it then release the host locks on * each device. Returns 0 on success or %-EINVAL on failure. */ -static int rio_clear_locks(struct rio_mport *port) +static int rio_clear_locks(struct rio_net *net) { + struct rio_mport *port = net->hport; struct rio_dev *rdev; u32 result; int ret = 0; @@ -126,7 +126,7 @@ static int rio_clear_locks(struct rio_mport *port) result); ret = -EINVAL; } - list_for_each_entry(rdev, &rio_devices, global_list) { + list_for_each_entry(rdev, &net->devices, net_list) { rio_write_config_32(rdev, RIO_HOST_DID_LOCK_CSR, port->host_deviceid); rio_read_config_32(rdev, RIO_HOST_DID_LOCK_CSR, &result); @@ -479,7 +479,7 @@ static struct rio_dev __devinit *rio_setup_device(struct rio_net *net, rswitch->clr_table(port, destid, hopcount, RIO_GLOBAL_TABLE); - list_add_tail(&rswitch->node, &rio_switches); + list_add_tail(&rswitch->node, &net->switches); } else { if (do_enum) @@ -1058,6 +1058,7 @@ static struct rio_net __devinit *rio_alloc_net(struct rio_mport *port) if (net) { INIT_LIST_HEAD(&net->node); INIT_LIST_HEAD(&net->devices); + INIT_LIST_HEAD(&net->switches); INIT_LIST_HEAD(&net->mports); list_add_tail(&port->nnode, &net->mports); net->hport = port; @@ -1068,24 +1069,24 @@ static struct rio_net __devinit *rio_alloc_net(struct rio_mport *port) /** * rio_update_route_tables- Updates route tables in switches - * @port: Master port associated with the RIO network + * @net: RIO network to run update on * * For each enumerated device, ensure that each switch in a system * has correct routing entries. Add routes for devices that where * unknown dirung the first enumeration pass through the switch. */ -static void rio_update_route_tables(struct rio_mport *port) +static void rio_update_route_tables(struct rio_net *net) { struct rio_dev *rdev, *swrdev; struct rio_switch *rswitch; u8 sport; u16 destid; - list_for_each_entry(rdev, &rio_devices, global_list) { + list_for_each_entry(rdev, &net->devices, net_list) { destid = rdev->destid; - list_for_each_entry(rswitch, &rio_switches, node) { + list_for_each_entry(rswitch, &net->switches, node) { if (rio_is_switch(rdev) && (rdev->rswitch == rswitch)) continue; @@ -1181,12 +1182,12 @@ int __devinit rio_enum_mport(struct rio_mport *mport) printk(KERN_INFO "RIO: master port %d device has lost enumeration to a remote host\n", mport->id); - rio_clear_locks(mport); + rio_clear_locks(net); rc = -EBUSY; goto out; } - rio_update_route_tables(mport); - rio_clear_locks(mport); + rio_update_route_tables(net); + rio_clear_locks(net); rio_pw_enable(mport, 1); } else { printk(KERN_INFO "RIO: master port %d link inactive\n", @@ -1200,33 +1201,34 @@ int __devinit rio_enum_mport(struct rio_mport *mport) /** * rio_build_route_tables- Generate route tables from switch route entries + * @net: RIO network to run route tables scan on * * For each switch device, generate a route table by copying existing * route entries from the switch. */ -static void rio_build_route_tables(void) +static void rio_build_route_tables(struct rio_net *net) { + struct rio_switch *rswitch; struct rio_dev *rdev; int i; u8 sport; - list_for_each_entry(rdev, &rio_devices, global_list) - if (rio_is_switch(rdev)) { - rio_lock_device(rdev->net->hport, rdev->destid, - rdev->hopcount, 1000); - for (i = 0; - i < RIO_MAX_ROUTE_ENTRIES(rdev->net->hport->sys_size); - i++) { - if (rio_route_get_entry(rdev, - RIO_GLOBAL_TABLE, i, &sport, 0) < 0) - continue; - rdev->rswitch->route_table[i] = sport; - } + list_for_each_entry(rswitch, &net->switches, node) { + rdev = sw_to_rio_dev(rswitch); - rio_unlock_device(rdev->net->hport, - rdev->destid, - rdev->hopcount); + rio_lock_device(net->hport, rdev->destid, + rdev->hopcount, 1000); + for (i = 0; + i < RIO_MAX_ROUTE_ENTRIES(net->hport->sys_size); + i++) { + if (rio_route_get_entry(rdev, RIO_GLOBAL_TABLE, + i, &sport, 0) < 0) + continue; + rswitch->route_table[i] = sport; } + + rio_unlock_device(net->hport, rdev->destid, rdev->hopcount); + } } /** @@ -1284,7 +1286,7 @@ enum_done: goto bail; } - rio_build_route_tables(); + rio_build_route_tables(net); } return 0; diff --git a/include/linux/rio.h b/include/linux/rio.h index 4d1a104cd78..7ea02c44b32 100644 --- a/include/linux/rio.h +++ b/include/linux/rio.h @@ -275,6 +275,7 @@ struct rio_mport { struct rio_net { struct list_head node; /* node in list of networks */ struct list_head devices; /* list of devices in this net */ + struct list_head switches; /* list of switches in this net */ struct list_head mports; /* list of ports accessing net */ struct rio_mport *hport; /* primary port for accessing net */ unsigned char id; /* RIO network ID */ -- cgit v1.2.3-70-g09d2 From de74e00a965177e8a0d44af0ba31971b80f2bf3f Mon Sep 17 00:00:00 2001 From: Alexandre Bounine Date: Thu, 4 Oct 2012 17:16:13 -0700 Subject: rapidio: add destination ID allocation mechanism Replace the single global destination ID counter with per-net allocation mechanism to allow independent destID management for each available RapidIO network. Using bitmap based mechanism instead of counters allows destination ID release and reuse in systems that support hot-swap. Signed-off-by: Alexandre Bounine Cc: Matt Porter Cc: Li Yang Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rapidio/rio-scan.c | 205 +++++++++++++++++++++++++++++++++++++-------- include/linux/rio.h | 9 ++ 2 files changed, 179 insertions(+), 35 deletions(-) (limited to 'include/linux') diff --git a/drivers/rapidio/rio-scan.c b/drivers/rapidio/rio-scan.c index 745670f535e..48e9041dd1e 100644 --- a/drivers/rapidio/rio-scan.c +++ b/drivers/rapidio/rio-scan.c @@ -54,6 +54,114 @@ static int rio_mport_phys_table[] = { -1, }; + +/* + * rio_destid_alloc - Allocate next available destID for given network + * net: RIO network + * + * Returns next available device destination ID for the specified RIO network. + * Marks allocated ID as one in use. + * Returns RIO_INVALID_DESTID if new destID is not available. + */ +static u16 rio_destid_alloc(struct rio_net *net) +{ + int destid; + struct rio_id_table *idtab = &net->destid_table; + + spin_lock(&idtab->lock); + destid = find_next_zero_bit(idtab->table, idtab->max, idtab->next); + if (destid >= idtab->max) + destid = find_first_zero_bit(idtab->table, idtab->max); + + if (destid < idtab->max) { + idtab->next = destid + 1; + if (idtab->next >= idtab->max) + idtab->next = 0; + set_bit(destid, idtab->table); + destid += idtab->start; + } else + destid = RIO_INVALID_DESTID; + + spin_unlock(&idtab->lock); + return (u16)destid; +} + +/* + * rio_destid_reserve - Reserve the specivied destID + * net: RIO network + * destid: destID to reserve + * + * Tries to reserve the specified destID. + * Returns 0 if successfull. + */ +static int rio_destid_reserve(struct rio_net *net, u16 destid) +{ + int oldbit; + struct rio_id_table *idtab = &net->destid_table; + + destid -= idtab->start; + spin_lock(&idtab->lock); + oldbit = test_and_set_bit(destid, idtab->table); + spin_unlock(&idtab->lock); + return oldbit; +} + +/* + * rio_destid_free - free a previously allocated destID + * net: RIO network + * destid: destID to free + * + * Makes the specified destID available for use. + */ +static void rio_destid_free(struct rio_net *net, u16 destid) +{ + struct rio_id_table *idtab = &net->destid_table; + + destid -= idtab->start; + spin_lock(&idtab->lock); + clear_bit(destid, idtab->table); + spin_unlock(&idtab->lock); +} + +/* + * rio_destid_first - return first destID in use + * net: RIO network + */ +static u16 rio_destid_first(struct rio_net *net) +{ + int destid; + struct rio_id_table *idtab = &net->destid_table; + + spin_lock(&idtab->lock); + destid = find_first_bit(idtab->table, idtab->max); + if (destid >= idtab->max) + destid = RIO_INVALID_DESTID; + else + destid += idtab->start; + spin_unlock(&idtab->lock); + return (u16)destid; +} + +/* + * rio_destid_next - return next destID in use + * net: RIO network + * from: destination ID from which search shall continue + */ +static u16 rio_destid_next(struct rio_net *net, u16 from) +{ + int destid; + struct rio_id_table *idtab = &net->destid_table; + + spin_lock(&idtab->lock); + destid = find_next_bit(idtab->table, idtab->max, from); + if (destid >= idtab->max) + destid = RIO_INVALID_DESTID; + else + destid += idtab->start; + spin_unlock(&idtab->lock); + return (u16)destid; +} + /** * rio_get_device_id - Get the base/extended device id for a device * @port: RIO master port @@ -171,10 +279,6 @@ static int rio_enum_host(struct rio_mport *port) /* Set master port destid and init destid ctr */ rio_local_set_device_id(port, port->host_deviceid); - - if (next_destid == port->host_deviceid) - next_destid++; - return 0; } @@ -441,9 +545,8 @@ static struct rio_dev __devinit *rio_setup_device(struct rio_net *net, if (rio_device_has_destid(port, rdev->src_ops, rdev->dst_ops)) { if (do_enum) { rio_set_device_id(port, destid, hopcount, next_destid); - rdev->destid = next_destid++; - if (next_destid == port->host_deviceid) - next_destid++; + rdev->destid = next_destid; + next_destid = rio_destid_alloc(net); } else rdev->destid = rio_get_device_id(port, destid, hopcount); @@ -742,12 +845,7 @@ static u16 rio_get_host_deviceid_lock(struct rio_mport *port, u8 hopcount) static int __devinit rio_enum_peer(struct rio_net *net, struct rio_mport *port, u8 hopcount, struct rio_dev *prev, int prev_port) { - int port_num; - int cur_destid; - int sw_destid; - int sw_inport; struct rio_dev *rdev; - u16 destid; u32 regval; int tmp; @@ -813,19 +911,26 @@ static int __devinit rio_enum_peer(struct rio_net *net, struct rio_mport *port, return -1; if (rio_is_switch(rdev)) { + int sw_destid; + int cur_destid; + int sw_inport; + u16 destid; + int port_num; + sw_inport = RIO_GET_PORT_NUM(rdev->swpinfo); rio_route_add_entry(rdev, RIO_GLOBAL_TABLE, port->host_deviceid, sw_inport, 0); rdev->rswitch->route_table[port->host_deviceid] = sw_inport; - for (destid = 0; destid < next_destid; destid++) { - if (destid == port->host_deviceid) - continue; - rio_route_add_entry(rdev, RIO_GLOBAL_TABLE, - destid, sw_inport, 0); - rdev->rswitch->route_table[destid] = sw_inport; + destid = rio_destid_first(net); + while (destid != RIO_INVALID_DESTID && destid < next_destid) { + if (destid != port->host_deviceid) { + rio_route_add_entry(rdev, RIO_GLOBAL_TABLE, + destid, sw_inport, 0); + rdev->rswitch->route_table[destid] = sw_inport; + } + destid = rio_destid_next(net, destid + 1); } - pr_debug( "RIO: found %s (vid %4.4x did %4.4x) with %d ports\n", rio_name(rdev), rdev->vid, rdev->did, @@ -863,19 +968,22 @@ static int __devinit rio_enum_peer(struct rio_net *net, struct rio_mport *port, return -1; /* Update routing tables */ - if (next_destid > cur_destid) { + destid = rio_destid_next(net, cur_destid + 1); + if (destid != RIO_INVALID_DESTID) { for (destid = cur_destid; - destid < next_destid; destid++) { - if (destid == port->host_deviceid) - continue; - rio_route_add_entry(rdev, + destid < next_destid;) { + if (destid != port->host_deviceid) { + rio_route_add_entry(rdev, RIO_GLOBAL_TABLE, destid, port_num, 0); - rdev->rswitch-> - route_table[destid] = - port_num; + rdev->rswitch-> + route_table[destid] = + port_num; + } + destid = rio_destid_next(net, + destid + 1); } } } else { @@ -901,11 +1009,8 @@ static int __devinit rio_enum_peer(struct rio_net *net, struct rio_mport *port, rio_init_em(rdev); /* Check for empty switch */ - if (next_destid == sw_destid) { - next_destid++; - if (next_destid == port->host_deviceid) - next_destid++; - } + if (next_destid == sw_destid) + next_destid = rio_destid_alloc(net); rdev->destid = sw_destid; } else @@ -1043,17 +1148,39 @@ static int rio_mport_is_active(struct rio_mport *port) /** * rio_alloc_net- Allocate and configure a new RIO network * @port: Master port associated with the RIO network + * @do_enum: Enumeration/Discovery mode flag + * @start: logical minimal start id for new net * * Allocates a RIO network structure, initializes per-network * list heads, and adds the associated master port to the * network list of associated master ports. Returns a * RIO network pointer on success or %NULL on failure. */ -static struct rio_net __devinit *rio_alloc_net(struct rio_mport *port) +static struct rio_net __devinit *rio_alloc_net(struct rio_mport *port, + int do_enum, u16 start) { struct rio_net *net; net = kzalloc(sizeof(struct rio_net), GFP_KERNEL); + if (net && do_enum) { + net->destid_table.table = kzalloc( + BITS_TO_LONGS(RIO_MAX_ROUTE_ENTRIES(port->sys_size)) * + sizeof(long), + GFP_KERNEL); + + if (net->destid_table.table == NULL) { + pr_err("RIO: failed to allocate destID table\n"); + kfree(net); + net = NULL; + } else { + net->destid_table.start = start; + net->destid_table.next = 0; + net->destid_table.max = + RIO_MAX_ROUTE_ENTRIES(port->sys_size); + spin_lock_init(&net->destid_table.lock); + } + } + if (net) { INIT_LIST_HEAD(&net->node); INIT_LIST_HEAD(&net->devices); @@ -1163,12 +1290,16 @@ int __devinit rio_enum_mport(struct rio_mport *mport) /* If master port has an active link, allocate net and enum peers */ if (rio_mport_is_active(mport)) { - if (!(net = rio_alloc_net(mport))) { + net = rio_alloc_net(mport, 1, 0); + if (!net) { printk(KERN_ERR "RIO: failed to allocate new net\n"); rc = -ENOMEM; goto out; } + /* reserve mport destID in new net */ + rio_destid_reserve(net, mport->host_deviceid); + /* Enable Input Output Port (transmitter reviever) */ rio_enable_rx_tx_port(mport, 1, 0, 0, 0); @@ -1176,6 +1307,8 @@ int __devinit rio_enum_mport(struct rio_mport *mport) rio_local_write_config_32(mport, RIO_COMPONENT_TAG_CSR, next_comptag++); + next_destid = rio_destid_alloc(net); + if (rio_enum_peer(net, mport, 0, NULL, 0) < 0) { /* A higher priority host won enumeration, bail. */ printk(KERN_INFO @@ -1185,6 +1318,8 @@ int __devinit rio_enum_mport(struct rio_mport *mport) rc = -EBUSY; goto out; } + /* free the last allocated destID (unused) */ + rio_destid_free(net, next_destid); rio_update_route_tables(net); rio_clear_locks(net); rio_pw_enable(mport, 1); @@ -1265,7 +1400,7 @@ int __devinit rio_disc_mport(struct rio_mport *mport) enum_done: pr_debug("RIO: ... enumeration done\n"); - net = rio_alloc_net(mport); + net = rio_alloc_net(mport, 0, 0); if (!net) { printk(KERN_ERR "RIO: Failed to allocate new net\n"); goto bail; diff --git a/include/linux/rio.h b/include/linux/rio.h index 7ea02c44b32..d2dff22cf68 100644 --- a/include/linux/rio.h +++ b/include/linux/rio.h @@ -264,6 +264,14 @@ struct rio_mport { #endif }; +struct rio_id_table { + u16 start; /* logical minimal id */ + u16 next; /* hint for find */ + u32 max; /* max number of IDs in table */ + spinlock_t lock; + unsigned long *table; +}; + /** * struct rio_net - RIO network info * @node: Node in global list of RIO networks @@ -279,6 +287,7 @@ struct rio_net { struct list_head mports; /* list of ports accessing net */ struct rio_mport *hport; /* primary port for accessing net */ unsigned char id; /* RIO network ID */ + struct rio_id_table destid_table; /* destID allocation table */ }; /* Definitions used by switch sysfs initialization callback */ -- cgit v1.2.3-70-g09d2 From 2f012508880f8037590372c24ca6e8b6af8fffb6 Mon Sep 17 00:00:00 2001 From: Paul Clements Date: Thu, 4 Oct 2012 17:16:15 -0700 Subject: nbd: add set flags ioctl Add a set-flags ioctl, allowing various option flags to be set on an nbd device. This allows the nbd-client to set the device flags (to enable read-only mode, or enable discard support, etc.). Flags are typically specified by the nbd-server. During the negotiation phase of the nbd connection, the server sends its flags to the client. The client then uses NBD_SET_FLAGS to inform the kernel of the options. Also included is a one-line fix to debug output for the set-timeout ioctl. Signed-off-by: Paul Clements Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/block/nbd.c | 8 +++++++- include/linux/nbd.h | 9 +++++---- 2 files changed, 12 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index 0c03411c59e..68fe7514413 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -78,6 +78,8 @@ static const char *ioctl_cmd_to_ascii(int cmd) case NBD_SET_SOCK: return "set-sock"; case NBD_SET_BLKSIZE: return "set-blksize"; case NBD_SET_SIZE: return "set-size"; + case NBD_SET_TIMEOUT: return "set-timeout"; + case NBD_SET_FLAGS: return "set-flags"; case NBD_DO_IT: return "do-it"; case NBD_CLEAR_SOCK: return "clear-sock"; case NBD_CLEAR_QUE: return "clear-que"; @@ -468,7 +470,7 @@ static void nbd_handle_req(struct nbd_device *nbd, struct request *req) nbd_cmd(req) = NBD_CMD_READ; if (rq_data_dir(req) == WRITE) { nbd_cmd(req) = NBD_CMD_WRITE; - if (nbd->flags & NBD_READ_ONLY) { + if (nbd->flags & NBD_FLAG_READ_ONLY) { dev_err(disk_to_dev(nbd->disk), "Write on read-only\n"); goto error_out; @@ -651,6 +653,10 @@ static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *nbd, nbd->xmit_timeout = arg * HZ; return 0; + case NBD_SET_FLAGS: + nbd->flags = arg; + return 0; + case NBD_SET_SIZE_BLOCKS: nbd->bytesize = ((u64) arg) * nbd->blksize; bdev->bd_inode->i_size = nbd->bytesize; diff --git a/include/linux/nbd.h b/include/linux/nbd.h index d146ca10c0f..4b8ed5a1cfb 100644 --- a/include/linux/nbd.h +++ b/include/linux/nbd.h @@ -27,6 +27,7 @@ #define NBD_SET_SIZE_BLOCKS _IO( 0xab, 7 ) #define NBD_DISCONNECT _IO( 0xab, 8 ) #define NBD_SET_TIMEOUT _IO( 0xab, 9 ) +#define NBD_SET_FLAGS _IO( 0xab, 10) enum { NBD_CMD_READ = 0, @@ -34,6 +35,10 @@ enum { NBD_CMD_DISC = 2 }; +/* values for flags field */ +#define NBD_FLAG_HAS_FLAGS (1 << 0) /* nbd-server supports flags */ +#define NBD_FLAG_READ_ONLY (1 << 1) /* device is read-only */ + #define nbd_cmd(req) ((req)->cmd[0]) /* userspace doesn't need the nbd_device structure */ @@ -42,10 +47,6 @@ enum { #include #include -/* values for flags field */ -#define NBD_READ_ONLY 0x0001 -#define NBD_WRITE_NOCHK 0x0002 - struct request; struct nbd_device { -- cgit v1.2.3-70-g09d2 From a336d29870f8a1f8e5f10d9f1aa95531c4edeabe Mon Sep 17 00:00:00 2001 From: Paul Clements Date: Thu, 4 Oct 2012 17:16:18 -0700 Subject: nbd: handle discard requests Add discard support to nbd. If the nbd-server supports discard, it will send NBD_FLAG_SEND_TRIM to the client. The client will then set the flag in the kernel via NBD_SET_FLAGS, which tells the kernel to enable discards for the device (QUEUE_FLAG_DISCARD). If discard support is enabled, then when the nbd client system receives a discard request, this will be passed along to the nbd-server. When the discard request is received by the nbd-server, it will perform: fallocate(.. FALLOC_FL_PUNCH_HOLE ..) To punch a hole in the backend storage, which is no longer needed. Signed-off-by: Paul Clements Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/block/nbd.c | 15 ++++++++++++++- include/linux/nbd.h | 6 +++++- 2 files changed, 19 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index 68fe7514413..043ddcca4ab 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -98,6 +98,7 @@ static const char *nbdcmd_to_ascii(int cmd) case NBD_CMD_READ: return "read"; case NBD_CMD_WRITE: return "write"; case NBD_CMD_DISC: return "disconnect"; + case NBD_CMD_TRIM: return "trim/discard"; } return "invalid"; } @@ -469,7 +470,11 @@ static void nbd_handle_req(struct nbd_device *nbd, struct request *req) nbd_cmd(req) = NBD_CMD_READ; if (rq_data_dir(req) == WRITE) { - nbd_cmd(req) = NBD_CMD_WRITE; + if ((req->cmd_flags & REQ_DISCARD)) { + WARN_ON(!(nbd->flags & NBD_FLAG_SEND_TRIM)); + nbd_cmd(req) = NBD_CMD_TRIM; + } else + nbd_cmd(req) = NBD_CMD_WRITE; if (nbd->flags & NBD_FLAG_READ_ONLY) { dev_err(disk_to_dev(nbd->disk), "Write on read-only\n"); @@ -676,6 +681,10 @@ static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *nbd, mutex_unlock(&nbd->tx_lock); + if (nbd->flags & NBD_FLAG_SEND_TRIM) + queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, + nbd->disk->queue); + thread = kthread_create(nbd_thread, nbd, nbd->disk->disk_name); if (IS_ERR(thread)) { mutex_lock(&nbd->tx_lock); @@ -693,6 +702,7 @@ static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *nbd, nbd->file = NULL; nbd_clear_que(nbd); dev_warn(disk_to_dev(nbd->disk), "queue cleared\n"); + queue_flag_clear_unlocked(QUEUE_FLAG_DISCARD, nbd->disk->queue); if (file) fput(file); nbd->bytesize = 0; @@ -811,6 +821,9 @@ static int __init nbd_init(void) * Tell the block layer that we are not a rotational device */ queue_flag_set_unlocked(QUEUE_FLAG_NONROT, disk->queue); + disk->queue->limits.discard_granularity = 512; + disk->queue->limits.max_discard_sectors = UINT_MAX; + disk->queue->limits.discard_zeroes_data = 0; } if (register_blkdev(NBD_MAJOR, "nbd")) { diff --git a/include/linux/nbd.h b/include/linux/nbd.h index 4b8ed5a1cfb..5c86e2b33e2 100644 --- a/include/linux/nbd.h +++ b/include/linux/nbd.h @@ -32,12 +32,16 @@ enum { NBD_CMD_READ = 0, NBD_CMD_WRITE = 1, - NBD_CMD_DISC = 2 + NBD_CMD_DISC = 2, + /* there is a gap here to match userspace */ + NBD_CMD_TRIM = 4 }; /* values for flags field */ #define NBD_FLAG_HAS_FLAGS (1 << 0) /* nbd-server supports flags */ #define NBD_FLAG_READ_ONLY (1 << 1) /* device is read-only */ +/* there is a gap here to match userspace */ +#define NBD_FLAG_SEND_TRIM (1 << 5) /* send trim/discard */ #define nbd_cmd(req) ((req)->cmd[0]) -- cgit v1.2.3-70-g09d2