mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-11-30 10:06:43 +07:00
7d12e780e0
Maintain a per-CPU global "struct pt_regs *" variable which can be used instead of passing regs around manually through all ~1800 interrupt handlers in the Linux kernel. The regs pointer is used in few places, but it potentially costs both stack space and code to pass it around. On the FRV arch, removing the regs parameter from all the genirq function results in a 20% speed up of the IRQ exit path (ie: from leaving timer_interrupt() to leaving do_IRQ()). Where appropriate, an arch may override the generic storage facility and do something different with the variable. On FRV, for instance, the address is maintained in GR28 at all times inside the kernel as part of general exception handling. Having looked over the code, it appears that the parameter may be handed down through up to twenty or so layers of functions. Consider a USB character device attached to a USB hub, attached to a USB controller that posts its interrupts through a cascaded auxiliary interrupt controller. A character device driver may want to pass regs to the sysrq handler through the input layer which adds another few layers of parameter passing. I've build this code with allyesconfig for x86_64 and i386. I've runtested the main part of the code on FRV and i386, though I can't test most of the drivers. I've also done partial conversion for powerpc and MIPS - these at least compile with minimal configurations. This will affect all archs. Mostly the changes should be relatively easy. Take do_IRQ(), store the regs pointer at the beginning, saving the old one: struct pt_regs *old_regs = set_irq_regs(regs); And put the old one back at the end: set_irq_regs(old_regs); Don't pass regs through to generic_handle_irq() or __do_IRQ(). In timer_interrupt(), this sort of change will be necessary: - update_process_times(user_mode(regs)); - profile_tick(CPU_PROFILING, regs); + update_process_times(user_mode(get_irq_regs())); + profile_tick(CPU_PROFILING); I'd like to move update_process_times()'s use of get_irq_regs() into itself, except that i386, alone of the archs, uses something other than user_mode(). Some notes on the interrupt handling in the drivers: (*) input_dev() is now gone entirely. The regs pointer is no longer stored in the input_dev struct. (*) finish_unlinks() in drivers/usb/host/ohci-q.c needs checking. It does something different depending on whether it's been supplied with a regs pointer or not. (*) Various IRQ handler function pointers have been moved to type irq_handler_t. Signed-Off-By: David Howells <dhowells@redhat.com> (cherry picked from 1b16e7ac850969f38b375e511e3fa2f474a33867 commit)
216 lines
7.3 KiB
C
216 lines
7.3 KiB
C
/*
|
|
* Generic RTC interface.
|
|
* This version contains the part of the user interface to the Real Time Clock
|
|
* service. It is used with both the legacy mc146818 and also EFI
|
|
* Struct rtc_time and first 12 ioctl by Paul Gortmaker, 1996 - separated out
|
|
* from <linux/mc146818rtc.h> to this file for 2.4 kernels.
|
|
*
|
|
* Copyright (C) 1999 Hewlett-Packard Co.
|
|
* Copyright (C) 1999 Stephane Eranian <eranian@hpl.hp.com>
|
|
*/
|
|
#ifndef _LINUX_RTC_H_
|
|
#define _LINUX_RTC_H_
|
|
|
|
/*
|
|
* The struct used to pass data via the following ioctl. Similar to the
|
|
* struct tm in <time.h>, but it needs to be here so that the kernel
|
|
* source is self contained, allowing cross-compiles, etc. etc.
|
|
*/
|
|
|
|
struct rtc_time {
|
|
int tm_sec;
|
|
int tm_min;
|
|
int tm_hour;
|
|
int tm_mday;
|
|
int tm_mon;
|
|
int tm_year;
|
|
int tm_wday;
|
|
int tm_yday;
|
|
int tm_isdst;
|
|
};
|
|
|
|
/*
|
|
* This data structure is inspired by the EFI (v0.92) wakeup
|
|
* alarm API.
|
|
*/
|
|
struct rtc_wkalrm {
|
|
unsigned char enabled; /* 0 = alarm disabled, 1 = alarm enabled */
|
|
unsigned char pending; /* 0 = alarm not pending, 1 = alarm pending */
|
|
struct rtc_time time; /* time the alarm is set to */
|
|
};
|
|
|
|
/*
|
|
* Data structure to control PLL correction some better RTC feature
|
|
* pll_value is used to get or set current value of correction,
|
|
* the rest of the struct is used to query HW capabilities.
|
|
* This is modeled after the RTC used in Q40/Q60 computers but
|
|
* should be sufficiently flexible for other devices
|
|
*
|
|
* +ve pll_value means clock will run faster by
|
|
* pll_value*pll_posmult/pll_clock
|
|
* -ve pll_value means clock will run slower by
|
|
* pll_value*pll_negmult/pll_clock
|
|
*/
|
|
|
|
struct rtc_pll_info {
|
|
int pll_ctrl; /* placeholder for fancier control */
|
|
int pll_value; /* get/set correction value */
|
|
int pll_max; /* max +ve (faster) adjustment value */
|
|
int pll_min; /* max -ve (slower) adjustment value */
|
|
int pll_posmult; /* factor for +ve correction */
|
|
int pll_negmult; /* factor for -ve correction */
|
|
long pll_clock; /* base PLL frequency */
|
|
};
|
|
|
|
/*
|
|
* ioctl calls that are permitted to the /dev/rtc interface, if
|
|
* any of the RTC drivers are enabled.
|
|
*/
|
|
|
|
#define RTC_AIE_ON _IO('p', 0x01) /* Alarm int. enable on */
|
|
#define RTC_AIE_OFF _IO('p', 0x02) /* ... off */
|
|
#define RTC_UIE_ON _IO('p', 0x03) /* Update int. enable on */
|
|
#define RTC_UIE_OFF _IO('p', 0x04) /* ... off */
|
|
#define RTC_PIE_ON _IO('p', 0x05) /* Periodic int. enable on */
|
|
#define RTC_PIE_OFF _IO('p', 0x06) /* ... off */
|
|
#define RTC_WIE_ON _IO('p', 0x0f) /* Watchdog int. enable on */
|
|
#define RTC_WIE_OFF _IO('p', 0x10) /* ... off */
|
|
|
|
#define RTC_ALM_SET _IOW('p', 0x07, struct rtc_time) /* Set alarm time */
|
|
#define RTC_ALM_READ _IOR('p', 0x08, struct rtc_time) /* Read alarm time */
|
|
#define RTC_RD_TIME _IOR('p', 0x09, struct rtc_time) /* Read RTC time */
|
|
#define RTC_SET_TIME _IOW('p', 0x0a, struct rtc_time) /* Set RTC time */
|
|
#define RTC_IRQP_READ _IOR('p', 0x0b, unsigned long) /* Read IRQ rate */
|
|
#define RTC_IRQP_SET _IOW('p', 0x0c, unsigned long) /* Set IRQ rate */
|
|
#define RTC_EPOCH_READ _IOR('p', 0x0d, unsigned long) /* Read epoch */
|
|
#define RTC_EPOCH_SET _IOW('p', 0x0e, unsigned long) /* Set epoch */
|
|
|
|
#define RTC_WKALM_SET _IOW('p', 0x0f, struct rtc_wkalrm)/* Set wakeup alarm*/
|
|
#define RTC_WKALM_RD _IOR('p', 0x10, struct rtc_wkalrm)/* Get wakeup alarm*/
|
|
|
|
#define RTC_PLL_GET _IOR('p', 0x11, struct rtc_pll_info) /* Get PLL correction */
|
|
#define RTC_PLL_SET _IOW('p', 0x12, struct rtc_pll_info) /* Set PLL correction */
|
|
|
|
/* interrupt flags */
|
|
#define RTC_IRQF 0x80 /* any of the following is active */
|
|
#define RTC_PF 0x40
|
|
#define RTC_AF 0x20
|
|
#define RTC_UF 0x10
|
|
|
|
#ifdef __KERNEL__
|
|
|
|
#include <linux/interrupt.h>
|
|
|
|
extern int rtc_month_days(unsigned int month, unsigned int year);
|
|
extern int rtc_year_days(unsigned int day, unsigned int month, unsigned int year);
|
|
extern int rtc_valid_tm(struct rtc_time *tm);
|
|
extern int rtc_tm_to_time(struct rtc_time *tm, unsigned long *time);
|
|
extern void rtc_time_to_tm(unsigned long time, struct rtc_time *tm);
|
|
|
|
#include <linux/device.h>
|
|
#include <linux/seq_file.h>
|
|
#include <linux/cdev.h>
|
|
#include <linux/poll.h>
|
|
#include <linux/mutex.h>
|
|
|
|
extern struct class *rtc_class;
|
|
|
|
struct rtc_class_ops {
|
|
int (*open)(struct device *);
|
|
void (*release)(struct device *);
|
|
int (*ioctl)(struct device *, unsigned int, unsigned long);
|
|
int (*read_time)(struct device *, struct rtc_time *);
|
|
int (*set_time)(struct device *, struct rtc_time *);
|
|
int (*read_alarm)(struct device *, struct rtc_wkalrm *);
|
|
int (*set_alarm)(struct device *, struct rtc_wkalrm *);
|
|
int (*proc)(struct device *, struct seq_file *);
|
|
int (*set_mmss)(struct device *, unsigned long secs);
|
|
int (*irq_set_state)(struct device *, int enabled);
|
|
int (*irq_set_freq)(struct device *, int freq);
|
|
int (*read_callback)(struct device *, int data);
|
|
};
|
|
|
|
#define RTC_DEVICE_NAME_SIZE 20
|
|
struct rtc_task;
|
|
|
|
struct rtc_device
|
|
{
|
|
struct class_device class_dev;
|
|
struct module *owner;
|
|
|
|
int id;
|
|
char name[RTC_DEVICE_NAME_SIZE];
|
|
|
|
const struct rtc_class_ops *ops;
|
|
struct mutex ops_lock;
|
|
|
|
struct class_device *rtc_dev;
|
|
struct cdev char_dev;
|
|
struct mutex char_lock;
|
|
|
|
unsigned long irq_data;
|
|
spinlock_t irq_lock;
|
|
wait_queue_head_t irq_queue;
|
|
struct fasync_struct *async_queue;
|
|
|
|
struct rtc_task *irq_task;
|
|
spinlock_t irq_task_lock;
|
|
int irq_freq;
|
|
int max_user_freq;
|
|
#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL
|
|
struct work_struct uie_task;
|
|
struct timer_list uie_timer;
|
|
/* Those fields are protected by rtc->irq_lock */
|
|
unsigned int oldsecs;
|
|
unsigned int irq_active:1;
|
|
unsigned int stop_uie_polling:1;
|
|
unsigned int uie_task_active:1;
|
|
unsigned int uie_timer_active:1;
|
|
#endif
|
|
};
|
|
#define to_rtc_device(d) container_of(d, struct rtc_device, class_dev)
|
|
|
|
extern struct rtc_device *rtc_device_register(const char *name,
|
|
struct device *dev,
|
|
const struct rtc_class_ops *ops,
|
|
struct module *owner);
|
|
extern void rtc_device_unregister(struct rtc_device *rdev);
|
|
extern int rtc_interface_register(struct class_interface *intf);
|
|
|
|
extern int rtc_read_time(struct class_device *class_dev, struct rtc_time *tm);
|
|
extern int rtc_set_time(struct class_device *class_dev, struct rtc_time *tm);
|
|
extern int rtc_set_mmss(struct class_device *class_dev, unsigned long secs);
|
|
extern int rtc_read_alarm(struct class_device *class_dev,
|
|
struct rtc_wkalrm *alrm);
|
|
extern int rtc_set_alarm(struct class_device *class_dev,
|
|
struct rtc_wkalrm *alrm);
|
|
extern void rtc_update_irq(struct class_device *class_dev,
|
|
unsigned long num, unsigned long events);
|
|
|
|
extern struct class_device *rtc_class_open(char *name);
|
|
extern void rtc_class_close(struct class_device *class_dev);
|
|
|
|
extern int rtc_irq_register(struct class_device *class_dev,
|
|
struct rtc_task *task);
|
|
extern void rtc_irq_unregister(struct class_device *class_dev,
|
|
struct rtc_task *task);
|
|
extern int rtc_irq_set_state(struct class_device *class_dev,
|
|
struct rtc_task *task, int enabled);
|
|
extern int rtc_irq_set_freq(struct class_device *class_dev,
|
|
struct rtc_task *task, int freq);
|
|
|
|
typedef struct rtc_task {
|
|
void (*func)(void *private_data);
|
|
void *private_data;
|
|
} rtc_task_t;
|
|
|
|
int rtc_register(rtc_task_t *task);
|
|
int rtc_unregister(rtc_task_t *task);
|
|
int rtc_control(rtc_task_t *t, unsigned int cmd, unsigned long arg);
|
|
void rtc_get_rtc_time(struct rtc_time *rtc_tm);
|
|
irqreturn_t rtc_interrupt(int irq, void *dev_id);
|
|
|
|
#endif /* __KERNEL__ */
|
|
|
|
#endif /* _LINUX_RTC_H_ */
|