diff options
Diffstat (limited to 'arch')
364 files changed, 16694 insertions, 15953 deletions
diff --git a/arch/Kconfig b/arch/Kconfig index 364c6dadde0..0267babe5eb 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -13,6 +13,20 @@ config OPROFILE If unsure, say N. +config OPROFILE_IBS + bool "OProfile AMD IBS support (EXPERIMENTAL)" + default n + depends on OPROFILE && SMP && X86 + help + Instruction-Based Sampling (IBS) is a new profiling + technique that provides rich, precise program performance + information. IBS is introduced by AMD Family10h processors + (AMD Opteron Quad-Core processor “Barcelona”) to overcome + the limitations of conventional performance counter + sampling. + + If unsure, say N. + config HAVE_OPROFILE def_bool n diff --git a/arch/alpha/Kconfig b/arch/alpha/Kconfig index 1bec55d63ef..ee35226c44e 100644 --- a/arch/alpha/Kconfig +++ b/arch/alpha/Kconfig @@ -5,6 +5,7 @@ config ALPHA bool default y + select HAVE_AOUT select HAVE_IDE select HAVE_OPROFILE help @@ -68,9 +69,6 @@ config AUTO_IRQ_AFFINITY depends on SMP default y -config ARCH_SUPPORTS_AOUT - def_bool y - source "init/Kconfig" diff --git a/arch/alpha/include/asm/statfs.h b/arch/alpha/include/asm/statfs.h index ad15830baef..de35cd438a1 100644 --- a/arch/alpha/include/asm/statfs.h +++ b/arch/alpha/include/asm/statfs.h @@ -1,6 +1,10 @@ #ifndef _ALPHA_STATFS_H #define _ALPHA_STATFS_H +/* Alpha is the only 64-bit platform with 32-bit statfs. And doesn't + even seem to implement statfs64 */ +#define __statfs_word __u32 + #include <asm-generic/statfs.h> #endif diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index efeed65b4a6..4853f9df37b 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -8,6 +8,7 @@ mainmenu "Linux Kernel Configuration" config ARM bool default y + select HAVE_AOUT select HAVE_IDE select RTC_LIB select SYS_SUPPORTS_APM_EMULATION @@ -140,9 +141,6 @@ config GENERIC_CALIBRATE_DELAY bool default y -config ARCH_SUPPORTS_AOUT - def_bool y - config ARCH_MAY_HAVE_PC_FDC bool diff --git a/arch/arm/include/asm/statfs.h b/arch/arm/include/asm/statfs.h index a02e6a8c3d7..079447c05ba 100644 --- a/arch/arm/include/asm/statfs.h +++ b/arch/arm/include/asm/statfs.h @@ -1,42 +1,12 @@ #ifndef _ASMARM_STATFS_H #define _ASMARM_STATFS_H -#ifndef __KERNEL_STRICT_NAMES -# include <linux/types.h> -typedef __kernel_fsid_t fsid_t; -#endif - -struct statfs { - __u32 f_type; - __u32 f_bsize; - __u32 f_blocks; - __u32 f_bfree; - __u32 f_bavail; - __u32 f_files; - __u32 f_ffree; - __kernel_fsid_t f_fsid; - __u32 f_namelen; - __u32 f_frsize; - __u32 f_spare[5]; -}; - /* * With EABI there is 4 bytes of padding added to this structure. * Let's pack it so the padding goes away to simplify dual ABI support. * Note that user space does NOT have to pack this structure. */ -struct statfs64 { - __u32 f_type; - __u32 f_bsize; - __u64 f_blocks; - __u64 f_bfree; - __u64 f_bavail; - __u64 f_files; - __u64 f_ffree; - __kernel_fsid_t f_fsid; - __u32 f_namelen; - __u32 f_frsize; - __u32 f_spare[5]; -} __attribute__ ((packed,aligned(4))); +#define ARCH_PACK_STATFS64 __attribute__((packed,aligned(4))) +#include <asm-generic/statfs.h> #endif diff --git a/arch/arm/kernel/dma.c b/arch/arm/kernel/dma.c index ba99a203552..d006085ed7e 100644 --- a/arch/arm/kernel/dma.c +++ b/arch/arm/kernel/dma.c @@ -26,23 +26,6 @@ EXPORT_SYMBOL(dma_spin_lock); static dma_t dma_chan[MAX_DMA_CHANNELS]; /* - * Get dma list for /proc/dma - */ -int get_dma_list(char *buf) -{ - dma_t *dma; - char *p = buf; - int i; - - for (i = 0, dma = dma_chan; i < MAX_DMA_CHANNELS; i++, dma++) - if (dma->lock) - p += sprintf(p, "%2d: %14s %s\n", i, - dma->d_ops->type, dma->device_id); - - return p - buf; -} - -/* * Request DMA channel * * On certain platforms, we have to allocate an interrupt as well... diff --git a/arch/arm/mach-orion5x/include/mach/orion5x.h b/arch/arm/mach-orion5x/include/mach/orion5x.h index e67c843baa0..9f5ce1ce584 100644 --- a/arch/arm/mach-orion5x/include/mach/orion5x.h +++ b/arch/arm/mach-orion5x/include/mach/orion5x.h @@ -157,9 +157,11 @@ #define CPU_CONF ORION5X_BRIDGE_REG(0x100) #define CPU_CTRL ORION5X_BRIDGE_REG(0x104) #define CPU_RESET_MASK ORION5X_BRIDGE_REG(0x108) +#define WDT_RESET 0x0002 #define CPU_SOFT_RESET ORION5X_BRIDGE_REG(0x10c) #define POWER_MNG_CTRL_REG ORION5X_BRIDGE_REG(0x11C) #define BRIDGE_CAUSE ORION5X_BRIDGE_REG(0x110) +#define WDT_INT_REQ 0x0008 #define BRIDGE_MASK ORION5X_BRIDGE_REG(0x114) #define BRIDGE_INT_TIMER0 0x0002 #define BRIDGE_INT_TIMER1 0x0004 diff --git a/arch/arm/plat-omap/devices.c b/arch/arm/plat-omap/devices.c index a716ecd1db2..97187fa0ae5 100644 --- a/arch/arm/plat-omap/devices.c +++ b/arch/arm/plat-omap/devices.c @@ -441,16 +441,8 @@ static inline void omap_init_uwire(void) {} #if defined(CONFIG_OMAP_WATCHDOG) || defined(CONFIG_OMAP_WATCHDOG_MODULE) -#ifdef CONFIG_ARCH_OMAP24XX -#define OMAP_WDT_BASE 0x48022000 -#else -#define OMAP_WDT_BASE 0xfffeb000 -#endif - static struct resource wdt_resources[] = { { - .start = OMAP_WDT_BASE, - .end = OMAP_WDT_BASE + 0x4f, .flags = IORESOURCE_MEM, }, }; @@ -464,6 +456,19 @@ static struct platform_device omap_wdt_device = { static void omap_init_wdt(void) { + if (cpu_is_omap16xx()) + wdt_resources[0].start = 0xfffeb000; + else if (cpu_is_omap2420()) + wdt_resources[0].start = 0x48022000; /* WDT2 */ + else if (cpu_is_omap2430()) + wdt_resources[0].start = 0x49016000; /* WDT2 */ + else if (cpu_is_omap343x()) + wdt_resources[0].start = 0x48314000; /* WDT2 */ + else + return; + + wdt_resources[0].end = wdt_resources[0].start + 0x4f; + (void) platform_device_register(&omap_wdt_device); } #else diff --git a/arch/avr32/boards/atngw100/setup.c b/arch/avr32/boards/atngw100/setup.c index b8286f1ce85..6c54580a66d 100644 --- a/arch/avr32/boards/atngw100/setup.c +++ b/arch/avr32/boards/atngw100/setup.c @@ -9,6 +9,7 @@ */ #include <linux/clk.h> #include <linux/etherdevice.h> +#include <linux/gpio.h> #include <linux/irq.h> #include <linux/i2c.h> #include <linux/i2c-gpio.h> @@ -53,8 +54,11 @@ static struct spi_board_info spi0_board_info[] __initdata = { }; static struct mci_platform_data __initdata mci0_data = { - .detect_pin = GPIO_PIN_PC(25), - .wp_pin = GPIO_PIN_PE(0), + .slot[0] = { + .bus_width = 4, + .detect_pin = GPIO_PIN_PC(25), + .wp_pin = GPIO_PIN_PE(0), + }, }; /* @@ -190,7 +194,7 @@ static int __init atngw100_init(void) * PB28/EXTINT3 doesn't; it should be SMBALERT# (for PMBus), * but it's not available off-board. */ - at32_select_periph(GPIO_PIN_PB(28), 0, AT32_GPIOF_PULLUP); + at32_select_periph(GPIO_PIOB_BASE, 1 << 28, 0, AT32_GPIOF_PULLUP); at32_select_gpio(i2c_gpio_data.sda_pin, AT32_GPIOF_MULTIDRV | AT32_GPIOF_OUTPUT | AT32_GPIOF_HIGH); at32_select_gpio(i2c_gpio_data.scl_pin, @@ -204,6 +208,15 @@ postcore_initcall(atngw100_init); static int __init atngw100_arch_init(void) { + /* PB30 is the otherwise unused jumper on the mainboard, with an + * external pullup; the jumper grounds it. Use it however you + * like, including letting U-Boot or Linux tweak boot sequences. + */ + at32_select_gpio(GPIO_PIN_PB(30), 0); + gpio_request(GPIO_PIN_PB(30), "j15"); + gpio_direction_input(GPIO_PIN_PB(30)); + gpio_export(GPIO_PIN_PB(30), false); + /* set_irq_type() after the arch_initcall for EIC has run, and * before the I2C subsystem could try using this IRQ. */ diff --git a/arch/avr32/boards/atstk1000/atstk1002.c b/arch/avr32/boards/atstk1000/atstk1002.c index dfc3443e23a..29e5b51a7fd 100644 --- a/arch/avr32/boards/atstk1000/atstk1002.c +++ b/arch/avr32/boards/atstk1000/atstk1002.c @@ -232,7 +232,7 @@ static void __init atstk1002_setup_extdac(void) goto err_set_clk; } - at32_select_periph(GPIO_PIN_PA(30), GPIO_PERIPH_A, 0); + at32_select_periph(GPIO_PIOA_BASE, (1 << 30), GPIO_PERIPH_A, 0); at73c213_data.dac_clk = gclk; err_set_clk: @@ -264,16 +264,20 @@ void __init setup_board(void) #ifndef CONFIG_BOARD_ATSTK100X_SW2_CUSTOM +static struct mci_platform_data __initdata mci0_data = { + .slot[0] = { + .bus_width = 4, + /* MMC card detect requires MACB0 *NOT* be used */ #ifdef CONFIG_BOARD_ATSTK1002_SW6_CUSTOM -static struct mci_platform_data __initdata mci0_data = { - .detect_pin = GPIO_PIN_PC(14), /* gpio30/sdcd */ - .wp_pin = GPIO_PIN_PC(15), /* gpio31/sdwp */ -}; -#define MCI_PDATA &mci0_data + .detect_pin = GPIO_PIN_PC(14), /* gpio30/sdcd */ + .wp_pin = GPIO_PIN_PC(15), /* gpio31/sdwp */ #else -#define MCI_PDATA NULL + .detect_pin = -ENODEV, + .wp_pin = -ENODEV, #endif /* SW6 for sd{cd,wp} routing */ + }, +}; #endif /* SW2 for MMC signal routing */ @@ -326,13 +330,14 @@ static int __init atstk1002_init(void) at32_add_device_spi(1, spi1_board_info, ARRAY_SIZE(spi1_board_info)); #endif #ifndef CONFIG_BOARD_ATSTK100X_SW2_CUSTOM - at32_add_device_mci(0, MCI_PDATA); + at32_add_device_mci(0, &mci0_data); #endif #ifdef CONFIG_BOARD_ATSTK1002_SW5_CUSTOM set_hw_addr(at32_add_device_eth(1, ð_data[1])); #else at32_add_device_lcdc(0, &atstk1000_lcdc_data, - fbmem_start, fbmem_size, 0); + fbmem_start, fbmem_size, + ATMEL_LCDC_PRI_24BIT | ATMEL_LCDC_PRI_CONTROL); #endif at32_add_device_usba(0, NULL); #ifndef CONFIG_BOARD_ATSTK100X_SW3_CUSTOM diff --git a/arch/avr32/boards/atstk1000/atstk1003.c b/arch/avr32/boards/atstk1000/atstk1003.c index 0cf664174c1..be089d7f37e 100644 --- a/arch/avr32/boards/atstk1000/atstk1003.c +++ b/arch/avr32/boards/atstk1000/atstk1003.c @@ -19,6 +19,7 @@ #include <linux/spi/spi.h> #include <asm/setup.h> +#include <asm/atmel-mci.h> #include <mach/at32ap700x.h> #include <mach/board.h> @@ -66,6 +67,16 @@ static struct spi_board_info spi1_board_info[] __initdata = { { } }; #endif +#ifndef CONFIG_BOARD_ATSTK100X_SW2_CUSTOM +static struct mci_platform_data __initdata mci0_data = { + .slot[0] = { + .bus_width = 4, + .detect_pin = -ENODEV, + .wp_pin = -ENODEV, + }, +}; +#endif + #ifdef CONFIG_BOARD_ATSTK1000_EXTDAC static void __init atstk1003_setup_extdac(void) { @@ -84,7 +95,7 @@ static void __init atstk1003_setup_extdac(void) goto err_set_clk; } - at32_select_periph(GPIO_PIN_PA(30), GPIO_PERIPH_A, 0); + at32_select_periph(GPIO_PIOA_BASE, (1 << 30), GPIO_PERIPH_A, 0); at73c213_data.dac_clk = gclk; err_set_clk: @@ -154,7 +165,7 @@ static int __init atstk1003_init(void) at32_add_device_spi(1, spi1_board_info, ARRAY_SIZE(spi1_board_info)); #endif #ifndef CONFIG_BOARD_ATSTK100X_SW2_CUSTOM - at32_add_device_mci(0, NULL); + at32_add_device_mci(0, &mci0_data); #endif at32_add_device_usba(0, NULL); #ifndef CONFIG_BOARD_ATSTK100X_SW3_CUSTOM diff --git a/arch/avr32/boards/atstk1000/atstk1004.c b/arch/avr32/boards/atstk1000/atstk1004.c index 50a5273e591..248ef237c16 100644 --- a/arch/avr32/boards/atstk1000/atstk1004.c +++ b/arch/avr32/boards/atstk1000/atstk1004.c @@ -21,6 +21,7 @@ #include <video/atmel_lcdc.h> #include <asm/setup.h> +#include <asm/atmel-mci.h> #include <mach/at32ap700x.h> #include <mach/board.h> @@ -71,6 +72,16 @@ static struct spi_board_info spi1_board_info[] __initdata = { { } }; #endif +#ifndef CONFIG_BOARD_ATSTK100X_SW2_CUSTOM +static struct mci_platform_data __initdata mci0_data = { + .slot[0] = { + .bus_width = 4, + .detect_pin = -ENODEV, + .wp_pin = -ENODEV, + }, +}; +#endif + #ifdef CONFIG_BOARD_ATSTK1000_EXTDAC static void __init atstk1004_setup_extdac(void) { @@ -89,7 +100,7 @@ static void __init atstk1004_setup_extdac(void) goto err_set_clk; } - at32_select_periph(GPIO_PIN_PA(30), GPIO_PERIPH_A, 0); + at32_select_periph(GPIO_PIOA_BASE, (1 << 30), GPIO_PERIPH_A, 0); at73c213_data.dac_clk = gclk; err_set_clk: @@ -137,10 +148,11 @@ static int __init atstk1004_init(void) at32_add_device_spi(1, spi1_board_info, ARRAY_SIZE(spi1_board_info)); #endif #ifndef CONFIG_BOARD_ATSTK100X_SW2_CUSTOM - at32_add_device_mci(0, NULL); + at32_add_device_mci(0, &mci0_data); #endif at32_add_device_lcdc(0, &atstk1000_lcdc_data, - fbmem_start, fbmem_size, 0); + fbmem_start, fbmem_size, + ATMEL_LCDC_PRI_24BIT | ATMEL_LCDC_PRI_CONTROL); at32_add_device_usba(0, NULL); #ifndef CONFIG_BOARD_ATSTK100X_SW3_CUSTOM at32_add_device_ssc(0, ATMEL_SSC_TX); diff --git a/arch/avr32/include/asm/a.out.h b/arch/avr32/include/asm/a.out.h deleted file mode 100644 index e46375a34a7..00000000000 --- a/arch/avr32/include/asm/a.out.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef __ASM_AVR32_A_OUT_H -#define __ASM_AVR32_A_OUT_H - -struct exec -{ - unsigned long a_info; /* Use macros N_MAGIC, etc for access */ - unsigned a_text; /* length of text, in bytes */ - unsigned a_data; /* length of data, in bytes */ - unsigned a_bss; /* length of uninitialized data area for file, in bytes */ - unsigned a_syms; /* length of symbol table data in file, in bytes */ - unsigned a_entry; /* start address */ - unsigned a_trsize; /* length of relocation info for text, in bytes */ - unsigned a_drsize; /* length of relocation info for data, in bytes */ -}; - -#define N_TRSIZE(a) ((a).a_trsize) -#define N_DRSIZE(a) ((a).a_drsize) -#define N_SYMSIZE(a) ((a).a_syms) - -#endif /* __ASM_AVR32_A_OUT_H */ diff --git a/arch/avr32/include/asm/atmel-mci.h b/arch/avr32/include/asm/atmel-mci.h index c2ea6e1c9aa..59f3fadd0b6 100644 --- a/arch/avr32/include/asm/atmel-mci.h +++ b/arch/avr32/include/asm/atmel-mci.h @@ -1,9 +1,39 @@ #ifndef __ASM_AVR32_ATMEL_MCI_H #define __ASM_AVR32_ATMEL_MCI_H -struct mci_platform_data { +#define ATMEL_MCI_MAX_NR_SLOTS 2 + +struct dma_slave; + +/** + * struct mci_slot_pdata - board-specific per-slot configuration + * @bus_width: Number of data lines wired up the slot + * @detect_pin: GPIO pin wired to the card detect switch + * @wp_pin: GPIO pin wired to the write protect sensor + * + * If a given slot is not present on the board, @bus_width should be + * set to 0. The other fields are ignored in this case. + * + * Any pins that aren't available should be set to a negative value. + * + * Note that support for multiple slots is experimental -- some cards + * might get upset if we don't get the clock management exactly right. + * But in most cases, it should work just fine. + */ +struct mci_slot_pdata { + unsigned int bus_width; int detect_pin; int wp_pin; }; +/** + * struct mci_platform_data - board-specific MMC/SDcard configuration + * @dma_slave: DMA slave interface to use in data transfers, or NULL. + * @slot: Per-slot configuration data. + */ +struct mci_platform_data { + struct dma_slave *dma_slave; + struct mci_slot_pdata slot[ATMEL_MCI_MAX_NR_SLOTS]; +}; + #endif /* __ASM_AVR32_ATMEL_MCI_H */ diff --git a/arch/avr32/include/asm/byteorder.h b/arch/avr32/include/asm/byteorder.h index d77b48ba733..8e3af02076d 100644 --- a/arch/avr32/include/asm/byteorder.h +++ b/arch/avr32/include/asm/byteorder.h @@ -7,6 +7,9 @@ #include <asm/types.h> #include <linux/compiler.h> +#define __BIG_ENDIAN +#define __SWAB_64_THRU_32__ + #ifdef __CHECKER__ extern unsigned long __builtin_bswap_32(unsigned long x); extern unsigned short __builtin_bswap_16(unsigned short x); @@ -17,15 +20,18 @@ extern unsigned short __builtin_bswap_16(unsigned short x); * the result. */ #if !(__GNUC__ == 4 && __GNUC_MINOR__ < 2) -#define __arch__swab32(x) __builtin_bswap_32(x) -#define __arch__swab16(x) __builtin_bswap_16(x) -#endif +static inline __attribute_const__ __u16 __arch_swab16(__u16 val) +{ + return __builtin_bswap_16(val); +} +#define __arch_swab16 __arch_swab16 -#if !defined(__STRICT_ANSI__) || defined(__KERNEL__) -# define __BYTEORDER_HAS_U64__ -# define __SWAB_64_THRU_32__ +static inline __attribute_const__ __u32 __arch_swab32(__u32 val) +{ + return __builtin_bswap_32(val); +} +#define __arch_swab32 __arch_swab32 #endif -#include <linux/byteorder/big_endian.h> - +#include <linux/byteorder.h> #endif /* __ASM_AVR32_BYTEORDER_H */ diff --git a/arch/avr32/include/asm/io.h b/arch/avr32/include/asm/io.h index a520f77ead9..22c97ef9220 100644 --- a/arch/avr32/include/asm/io.h +++ b/arch/avr32/include/asm/io.h @@ -160,6 +160,14 @@ BUILDIO_IOPORT(l, u32) #define readw_relaxed readw #define readl_relaxed readl +#define readb_be __raw_readb +#define readw_be __raw_readw +#define readl_be __raw_readl + +#define writeb_be __raw_writeb +#define writew_be __raw_writew +#define writel_be __raw_writel + #define __BUILD_MEMORY_STRING(bwl, type) \ static inline void writes##bwl(volatile void __iomem *addr, \ const void *data, unsigned int count) \ diff --git a/arch/avr32/kernel/process.c b/arch/avr32/kernel/process.c index 2c08ac992ac..134d5302b6d 100644 --- a/arch/avr32/kernel/process.c +++ b/arch/avr32/kernel/process.c @@ -9,6 +9,7 @@ #include <linux/module.h> #include <linux/kallsyms.h> #include <linux/fs.h> +#include <linux/pm.h> #include <linux/ptrace.h> #include <linux/reboot.h> #include <linux/tick.h> @@ -20,7 +21,7 @@ #include <mach/pm.h> -void (*pm_power_off)(void) = NULL; +void (*pm_power_off)(void); EXPORT_SYMBOL(pm_power_off); /* diff --git a/arch/avr32/kernel/setup.c b/arch/avr32/kernel/setup.c index d8e623c426c..5c7083916c3 100644 --- a/arch/avr32/kernel/setup.c +++ b/arch/avr32/kernel/setup.c @@ -283,6 +283,25 @@ static int __init early_parse_fbmem(char *p) } early_param("fbmem", early_parse_fbmem); +/* + * Pick out the memory size. We look for mem=size@start, + * where start and size are "size[KkMmGg]" + */ +static int __init early_mem(char *p) +{ + resource_size_t size, start; + + start = system_ram->start; + size = memparse(p, &p); + if (*p == '@') + start = memparse(p + 1, &p); + + system_ram->start = start; + system_ram->end = system_ram->start + size - 1; + return 0; +} +early_param("mem", early_mem); + static int __init parse_tag_core(struct tag *tag) { if (tag->hdr.size > 2) { diff --git a/arch/avr32/mach-at32ap/at32ap700x.c b/arch/avr32/mach-at32ap/at32ap700x.c index e01dbe4ebb4..813b6844cdf 100644 --- a/arch/avr32/mach-at32ap/at32ap700x.c +++ b/arch/avr32/mach-at32ap/at32ap700x.c @@ -82,8 +82,9 @@ static struct platform_device _name##_id##_device = { \ .num_resources = ARRAY_SIZE(_name##_id##_resource), \ } -#define select_peripheral(pin, periph, flags) \ - at32_select_periph(GPIO_PIN_##pin, GPIO_##periph, flags) +#define select_peripheral(port, pin_mask, periph, flags) \ + at32_select_periph(GPIO_##port##_BASE, pin_mask, \ + GPIO_##periph, flags) #define DEV_CLK(_name, devname, bus, _index) \ static struct clk devname##_##_name = { \ @@ -871,6 +872,7 @@ static struct clk atmel_psif1_pclk = { struct platform_device *__init at32_add_device_psif(unsigned int id) { struct platform_device *pdev; + u32 pin_mask; if (!(id == 0 || id == 1)) return NULL; @@ -881,20 +883,22 @@ struct platform_device *__init at32_add_device_psif(unsigned int id) switch (id) { case 0: + pin_mask = (1 << 8) | (1 << 9); /* CLOCK & DATA */ + if (platform_device_add_resources(pdev, atmel_psif0_resource, ARRAY_SIZE(atmel_psif0_resource))) goto err_add_resources; atmel_psif0_pclk.dev = &pdev->dev; - select_peripheral(PA(8), PERIPH_A, 0); /* CLOCK */ - select_peripheral(PA(9), PERIPH_A, 0); /* DATA */ + select_peripheral(PIOA, pin_mask, PERIPH_A, 0); break; case 1: + pin_mask = (1 << 11) | (1 << 12); /* CLOCK & DATA */ + if (platform_device_add_resources(pdev, atmel_psif1_resource, ARRAY_SIZE(atmel_psif1_resource))) goto err_add_resources; atmel_psif1_pclk.dev = &pdev->dev; - select_peripheral(PB(11), PERIPH_A, 0); /* CLOCK */ - select_peripheral(PB(12), PERIPH_A, 0); /* DATA */ + select_peripheral(PIOB, pin_mask, PERIPH_A, 0); break; default: return NULL; @@ -958,26 +962,30 @@ DEV_CLK(usart, atmel_usart3, pba, 6); static inline void configure_usart0_pins(void) { - select_peripheral(PA(8), PERIPH_B, 0); /* RXD */ - select_peripheral(PA(9), PERIPH_B, 0); /* TXD */ + u32 pin_mask = (1 << 8) | (1 << 9); /* RXD & TXD */ + + select_peripheral(PIOA, pin_mask, PERIPH_B, 0); } static inline void configure_usart1_pins(void) { - select_peripheral(PA(17), PERIPH_A, 0); /* RXD */ - select_peripheral(PA(18), PERIPH_A, 0); /* TXD */ + u32 pin_mask = (1 << 17) | (1 << 18); /* RXD & TXD */ + + select_peripheral(PIOA, pin_mask, PERIPH_A, 0); } static inline void configure_usart2_pins(void) { - select_peripheral(PB(26), PERIPH_B, 0); /* RXD */ - select_peripheral(PB(27), PERIPH_B, 0); /* TXD */ + u32 pin_mask = (1 << 26) | (1 << 27); /* RXD & TXD */ + + select_peripheral(PIOB, pin_mask, PERIPH_B, 0); } static inline void configure_usart3_pins(void) { - select_peripheral(PB(18), PERIPH_B, 0); /* RXD */ - select_peripheral(PB(17), PERIPH_B, 0); /* TXD */ + u32 pin_mask = (1 << 18) | (1 << 17); /* RXD & TXD */ + + select_peripheral(PIOB, pin_mask, PERIPH_B, 0); } static struct platform_device *__initdata at32_usarts[4]; @@ -1057,59 +1065,69 @@ struct platform_device *__init at32_add_device_eth(unsigned int id, struct eth_platform_data *data) { struct platform_device *pdev; + u32 pin_mask; switch (id) { case 0: pdev = &macb0_device; - select_peripheral(PC(3), PERIPH_A, 0); /* TXD0 */ - select_peripheral(PC(4), PERIPH_A, 0); /* TXD1 */ - select_peripheral(PC(7), PERIPH_A, 0); /* TXEN */ - select_peripheral(PC(8), PERIPH_A, 0); /* TXCK */ - select_peripheral(PC(9), PERIPH_A, 0); /* RXD0 */ - select_peripheral(PC(10), PERIPH_A, 0); /* RXD1 */ - select_peripheral(PC(13), PERIPH_A, 0); /* RXER */ - select_peripheral(PC(15), PERIPH_A, 0); /* RXDV */ - select_peripheral(PC(16), PERIPH_A, 0); /* MDC */ - select_peripheral(PC(17), PERIPH_A, 0); /* MDIO */ + pin_mask = (1 << 3); /* TXD0 */ + pin_mask |= (1 << 4); /* TXD1 */ + pin_mask |= (1 << 7); /* TXEN */ + pin_mask |= (1 << 8); /* TXCK */ + pin_mask |= (1 << 9); /* RXD0 */ + pin_mask |= (1 << 10); /* RXD1 */ + pin_mask |= (1 << 13); /* RXER */ + pin_mask |= (1 << 15); /* RXDV */ + pin_mask |= (1 << 16); /* MDC */ + pin_mask |= (1 << 17); /* MDIO */ if (!data->is_rmii) { - select_peripheral(PC(0), PERIPH_A, 0); /* COL */ - select_peripheral(PC(1), PERIPH_A, 0); /* CRS */ - select_peripheral(PC(2), PERIPH_A, 0); /* TXER */ - select_peripheral(PC(5), PERIPH_A, 0); /* TXD2 */ - select_peripheral(PC(6), PERIPH_A, 0); /* TXD3 */ - select_peripheral(PC(11), PERIPH_A, 0); /* RXD2 */ - select_peripheral(PC(12), PERIPH_A, 0); /* RXD3 */ - select_peripheral(PC(14), PERIPH_A, 0); /* RXCK */ - select_peripheral(PC(18), PERIPH_A, 0); /* SPD */ + pin_mask |= (1 << 0); /* COL */ + pin_mask |= (1 << 1); /* CRS */ + pin_mask |= (1 << 2); /* TXER */ + pin_mask |= (1 << 5); /* TXD2 */ + pin_mask |= (1 << 6); /* TXD3 */ + pin_mask |= (1 << 11); /* RXD2 */ + pin_mask |= (1 << 12); /* RXD3 */ + pin_mask |= (1 << 14); /* RXCK */ + pin_mask |= (1 << 18); /* SPD */ } + + select_peripheral(PIOC, pin_mask, PERIPH_A, 0); + break; case 1: pdev = &macb1_device; - select_peripheral(PD(13), PERIPH_B, 0); /* TXD0 */ - select_peripheral(PD(14), PERIPH_B, 0); /* TXD1 */ - select_peripheral(PD(11), PERIPH_B, 0); /* TXEN */ - select_peripheral(PD(12), PERIPH_B, 0); /* TXCK */ - select_peripheral(PD(10), PERIPH_B, 0); /* RXD0 */ - select_peripheral(PD(6), PERIPH_B, 0); /* RXD1 */ - select_peripheral(PD(5), PERIPH_B, 0); /* RXER */ - select_peripheral(PD(4), PERIPH_B, 0); /* RXDV */ - select_peripheral(PD(3), PERIPH_B, 0); /* MDC */ - select_peripheral(PD(2), PERIPH_B, 0); /* MDIO */ + pin_mask = (1 << 13); /* TXD0 */ + pin_mask |= (1 << 14); /* TXD1 */ + pin_mask |= (1 << 11); /* TXEN */ + pin_mask |= (1 << 12); /* TXCK */ + pin_mask |= (1 << 10); /* RXD0 */ + pin_mask |= (1 << 6); /* RXD1 */ + pin_mask |= (1 << 5); /* RXER */ + pin_mask |= (1 << 4); /* RXDV */ + pin_mask |= (1 << 3); /* MDC */ + pin_mask |= (1 << 2); /* MDIO */ + + if (!data->is_rmii) + pin_mask |= (1 << 15); /* SPD */ + + select_peripheral(PIOD, pin_mask, PERIPH_B, 0); if (!data->is_rmii) { - select_peripheral(PC(19), PERIPH_B, 0); /* COL */ - select_peripheral(PC(23), PERIPH_B, 0); /* CRS */ - select_peripheral(PC(26), PERIPH_B, 0); /* TXER */ - select_peripheral(PC(27), PERIPH_B, 0); /* TXD2 */ - select_peripheral(PC(28), PERIPH_B, 0); /* TXD3 */ - select_peripheral(PC(29), PERIPH_B, 0); /* RXD2 */ - select_peripheral(PC(30), PERIPH_B, 0); /* RXD3 */ - select_peripheral(PC(24), PERIPH_B, 0); /* RXCK */ - select_peripheral(PD(15), PERIPH_B, 0); /* SPD */ + pin_mask = (1 << 19); /* COL */ + pin_mask |= (1 << 23); /* CRS */ + pin_mask |= (1 << 26); /* TXER */ + pin_mask |= (1 << 27); /* TXD2 */ + pin_mask |= (1 << 28); /* TXD3 */ + pin_mask |= (1 << 29); /* RXD2 */ + pin_mask |= (1 << 30); /* RXD3 */ + pin_mask |= (1 << 24); /* RXCK */ + + select_peripheral(PIOC, pin_mask, PERIPH_B, 0); } break; @@ -1177,23 +1195,28 @@ at32_add_device_spi(unsigned int id, struct spi_board_info *b, unsigned int n) { GPIO_PIN_PB(2), GPIO_PIN_PB(3), GPIO_PIN_PB(4), GPIO_PIN_PA(27), }; struct platform_device *pdev; + u32 pin_mask; switch (id) { case 0: pdev = &atmel_spi0_device; + pin_mask = (1 << 1) | (1 << 2); /* MOSI & SCK */ + /* pullup MISO so a level is always defined */ - select_peripheral(PA(0), PERIPH_A, AT32_GPIOF_PULLUP); - select_peripheral(PA(1), PERIPH_A, 0); /* MOSI */ - select_peripheral(PA(2), PERIPH_A, 0); /* SCK */ + select_peripheral(PIOA, (1 << 0), PERIPH_A, AT32_GPIOF_PULLUP); + select_peripheral(PIOA, pin_mask, PERIPH_A, 0); + at32_spi_setup_slaves(0, b, n, spi0_pins); break; case 1: pdev = &atmel_spi1_device; + pin_mask = (1 << 1) | (1 << 5); /* MOSI */ + /* pullup MISO so a level is always defined */ - select_peripheral(PB(0), PERIPH_B, AT32_GPIOF_PULLUP); - select_peripheral(PB(1), PERIPH_B, 0); /* MOSI */ - select_peripheral(PB(5), PERIPH_B, 0); /* SCK */ + select_peripheral(PIOB, (1 << 0), PERIPH_B, AT32_GPIOF_PULLUP); + select_peripheral(PIOB, pin_mask, PERIPH_B, 0); + at32_spi_setup_slaves(1, b, n, spi1_pins); break; @@ -1226,6 +1249,7 @@ struct platform_device *__init at32_add_device_twi(unsigned int id, unsigned int n) { struct platform_device *pdev; + u32 pin_mask; if (id != 0) return NULL; @@ -1238,8 +1262,9 @@ struct platform_device *__init at32_add_device_twi(unsigned int id, ARRAY_SIZE(atmel_twi0_resource))) goto err_add_resources; - select_peripheral(PA(6), PERIPH_A, 0); /* SDA */ - select_peripheral(PA(7), PERIPH_A, 0); /* SDL */ + pin_mask = (1 << 6) | (1 << 7); /* SDA & SDL */ + + select_peripheral(PIOA, pin_mask, PERIPH_A, 0); atmel_twi0_pclk.dev = &pdev->dev; @@ -1272,10 +1297,16 @@ static struct clk atmel_mci0_pclk = { struct platform_device *__init at32_add_device_mci(unsigned int id, struct mci_platform_data *data) { - struct mci_platform_data _data; struct platform_device *pdev; + struct dw_dma_slave *dws; + u32 pioa_mask; + u32 piob_mask; - if (id != 0) + if (id != 0 || !data) + return NULL; + + /* Must have at least one usable slot */ + if (!data->slot[0].bus_width && !data->slot[1].bus_width) return NULL; pdev = platform_device_alloc("atmel_mci", id); @@ -1286,28 +1317,80 @@ at32_add_device_mci(unsigned int id, struct mci_platform_data *data) ARRAY_SIZE(atmel_mci0_resource))) goto fail; - if (!data) { - data = &_data; - memset(data, -1, sizeof(struct mci_platform_data)); - data->detect_pin = GPIO_PIN_NONE; - data->wp_pin = GPIO_PIN_NONE; - } + if (data->dma_slave) + dws = kmemdup(to_dw_dma_slave(data->dma_slave), + sizeof(struct dw_dma_slave), GFP_KERNEL); + else + dws = kzalloc(sizeof(struct dw_dma_slave), GFP_KERNEL); + + dws->slave.dev = &pdev->dev; + dws->slave.dma_dev = &dw_dmac0_device.dev; + dws->slave.reg_width = DMA_SLAVE_WIDTH_32BIT; + dws->cfg_hi = (DWC_CFGH_SRC_PER(0) + | DWC_CFGH_DST_PER(1)); + dws->cfg_lo &= ~(DWC_CFGL_HS_DST_POL + | DWC_CFGL_HS_SRC_POL); + + data->dma_slave = &dws->slave; if (platform_device_add_data(pdev, data, sizeof(struct mci_platform_data))) goto fail; - select_peripheral(PA(10), PERIPH_A, 0); /* CLK */ - select_peripheral(PA(11), PERIPH_A, 0); /* CMD */ - select_peripheral(PA(12), PERIPH_A, 0); /* DATA0 */ - select_peripheral(PA(13), PERIPH_A, 0); /* DATA1 */ - select_peripheral(PA(14), PERIPH_A, 0); /* DATA2 */ - select_peripheral(PA(15), PERIPH_A, 0); /* DATA3 */ + /* CLK line is common to both slots */ + pioa_mask = 1 << 10; - if (gpio_is_valid(data->detect_pin)) - at32_select_gpio(data->detect_pin, 0); - if (gpio_is_valid(data->wp_pin)) - at32_select_gpio(data->wp_pin, 0); + switch (data->slot[0].bus_width) { + case 4: + pioa_mask |= 1 << 13; /* DATA1 */ + pioa_mask |= 1 << 14; /* DATA2 */ + pioa_mask |= 1 << 15; /* DATA3 */ + /* fall through */ + case 1: + pioa_mask |= 1 << 11; /* CMD */ + pioa_mask |= 1 << 12; /* DATA0 */ + + if (gpio_is_valid(data->slot[0].detect_pin)) + at32_select_gpio(data->slot[0].detect_pin, 0); + if (gpio_is_valid(data->slot[0].wp_pin)) + at32_select_gpio(data->slot[0].wp_pin, 0); + break; + case 0: + /* Slot is unused */ + break; + default: + goto fail; + } + + select_peripheral(PIOA, pioa_mask, PERIPH_A, 0); + piob_mask = 0; + + switch (data->slot[1].bus_width) { + case 4: + piob_mask |= 1 << 8; /* DATA1 */ + piob_mask |= 1 << 9; /* DATA2 */ + piob_mask |= 1 << 10; /* DATA3 */ + /* fall through */ + case 1: + piob_mask |= 1 << 6; /* CMD */ + piob_mask |= 1 << 7; /* DATA0 */ + select_peripheral(PIOB, piob_mask, PERIPH_B, 0); + + if (gpio_is_valid(data->slot[1].detect_pin)) + at32_select_gpio(data->slot[1].detect_pin, 0); + if (gpio_is_valid(data->slot[1].wp_pin)) + at32_select_gpio(data->slot[1].wp_pin, 0); + break; + case 0: + /* Slot is unused */ + break; + default: + if (!data->slot[0].bus_width) + goto fail; + + data->slot[1].bus_width = 0; + break; + } atmel_mci0_pclk.dev = &pdev->dev; @@ -1353,13 +1436,14 @@ static struct clk atmel_lcdfb0_pixclk = { struct platform_device *__init at32_add_device_lcdc(unsigned int id, struct atmel_lcdfb_info *data, unsigned long fbmem_start, unsigned long fbmem_len, - unsigned int pin_config) + u64 pin_mask) { struct platform_device *pdev; struct atmel_lcdfb_info *info; struct fb_monspecs *monspecs; struct fb_videomode *modedb; unsigned int modedb_size; + u32 portc_mask, portd_mask, porte_mask; /* * Do a deep copy of the fb data, monspecs and modedb. Make @@ -1381,76 +1465,21 @@ at32_add_device_lcdc(unsigned int id, struct atmel_lcdfb_info *data, case 0: pdev = &atmel_lcdfb0_device; - switch (pin_config) { - case 0: - select_peripheral(PC(19), PERIPH_A, 0); /* CC */ - select_peripheral(PC(20), PERIPH_A, 0); /* HSYNC */ - select_peripheral(PC(21), PERIPH_A, 0); /* PCLK */ - select_peripheral(PC(22), PERIPH_A, 0); /* VSYNC */ - select_peripheral(PC(23), PERIPH_A, 0); /* DVAL */ - select_peripheral(PC(24), PERIPH_A, 0); /* MODE */ - select_peripheral(PC(25), PERIPH_A, 0); /* PWR */ - select_peripheral(PC(26), PERIPH_A, 0); /* DATA0 */ - select_peripheral(PC(27), PERIPH_A, 0); /* DATA1 */ - select_peripheral(PC(28), PERIPH_A, 0); /* DATA2 */ - select_peripheral(PC(29), PERIPH_A, 0); /* DATA3 */ - select_peripheral(PC(30), PERIPH_A, 0); /* DATA4 */ - select_peripheral(PC(31), PERIPH_A, 0); /* DATA5 */ - select_peripheral(PD(0), PERIPH_A, 0); /* DATA6 */ - select_peripheral(PD(1), PERIPH_A, 0); /* DATA7 */ - select_peripheral(PD(2), PERIPH_A, 0); /* DATA8 */ - select_peripheral(PD(3), PERIPH_A, 0); /* DATA9 */ - select_peripheral(PD(4), PERIPH_A, 0); /* DATA10 */ - select_peripheral(PD(5), PERIPH_A, 0); /* DATA11 */ - select_peripheral(PD(6), PERIPH_A, 0); /* DATA12 */ - select_peripheral(PD(7), PERIPH_A, 0); /* DATA13 */ - select_peripheral(PD(8), PERIPH_A, 0); /* DATA14 */ - select_peripheral(PD(9), PERIPH_A, 0); /* DATA15 */ - select_peripheral(PD(10), PERIPH_A, 0); /* DATA16 */ - select_peripheral(PD(11), PERIPH_A, 0); /* DATA17 */ - select_peripheral(PD(12), PERIPH_A, 0); /* DATA18 */ - select_peripheral(PD(13), PERIPH_A, 0); /* DATA19 */ - select_peripheral(PD(14), PERIPH_A, 0); /* DATA20 */ - select_peripheral(PD(15), PERIPH_A, 0); /* DATA21 */ - select_peripheral(PD(16), PERIPH_A, 0); /* DATA22 */ - select_peripheral(PD(17), PERIPH_A, 0); /* DATA23 */ - break; - case 1: - select_peripheral(PE(0), PERIPH_B, 0); /* CC */ - select_peripheral(PC(20), PERIPH_A, 0); /* HSYNC */ - select_peripheral(PC(21), PERIPH_A, 0); /* PCLK */ - select_peripheral(PC(22), PERIPH_A, 0); /* VSYNC */ - select_peripheral(PE(1), PERIPH_B, 0); /* DVAL */ - select_peripheral(PE(2), PERIPH_B, 0); /* MODE */ - select_peripheral(PC(25), PERIPH_A, 0); /* PWR */ - select_peripheral(PE(3), PERIPH_B, 0); /* DATA0 */ - select_peripheral(PE(4), PERIPH_B, 0); /* DATA1 */ - select_peripheral(PE(5), PERIPH_B, 0); /* DATA2 */ - select_peripheral(PE(6), PERIPH_B, 0); /* DATA3 */ - select_peripheral(PE(7), PERIPH_B, 0); /* DATA4 */ - select_peripheral(PC(31), PERIPH_A, 0); /* DATA5 */ - select_peripheral(PD(0), PERIPH_A, 0); /* DATA6 */ - select_peripheral(PD(1), PERIPH_A, 0); /* DATA7 */ - select_peripheral(PE(8), PERIPH_B, 0); /* DATA8 */ - select_peripheral(PE(9), PERIPH_B, 0); /* DATA9 */ - select_peripheral(PE(10), PERIPH_B, 0); /* DATA10 */ - select_peripheral(PE(11), PERIPH_B, 0); /* DATA11 */ - select_peripheral(PE(12), PERIPH_B, 0); /* DATA12 */ - select_peripheral(PD(7), PERIPH_A, 0); /* DATA13 */ - select_peripheral(PD(8), PERIPH_A, 0); /* DATA14 */ - select_peripheral(PD(9), PERIPH_A, 0); /* DATA15 */ - select_peripheral(PE(13), PERIPH_B, 0); /* DATA16 */ - select_peripheral(PE(14), PERIPH_B, 0); /* DATA17 */ - select_peripheral(PE(15), PERIPH_B, 0); /* DATA18 */ - select_peripheral(PE(16), PERIPH_B, 0); /* DATA19 */ - select_peripheral(PE(17), PERIPH_B, 0); /* DATA20 */ - select_peripheral(PE(18), PERIPH_B, 0); /* DATA21 */ - select_peripheral(PD(16), PERIPH_A, 0); /* DATA22 */ - select_peripheral(PD(17), PERIPH_A, 0); /* DATA23 */ - break; - default: - goto err_invalid_id; - } + if (pin_mask == 0ULL) + /* Default to "full" lcdc control signals and 24bit */ + pin_mask = ATMEL_LCDC_PRI_24BIT | ATMEL_LCDC_PRI_CONTROL; + + /* LCDC on port C */ + portc_mask = (pin_mask & 0xfff80000) >> 19; + select_peripheral(PIOC, portc_mask, PERIPH_A, 0); + + /* LCDC on port D */ + portd_mask = pin_mask & 0x0003ffff; + select_peripheral(PIOD, portd_mask, PERIPH_A, 0); + + /* LCDC on port E */ + porte_mask = (pin_mask >> 32) & 0x0007ffff; + select_peripheral(PIOE, porte_mask, PERIPH_B, 0); clk_set_parent(&atmel_lcdfb0_pixclk, &pll0); clk_set_rate(&atmel_lcdfb0_pixclk, clk_get_rate(&pll0)); @@ -1499,6 +1528,7 @@ static struct clk atmel_pwm0_mck = { struct platform_device *__init at32_add_device_pwm(u32 mask) { struct platform_device *pdev; + u32 pin_mask; if (!mask) return NULL; @@ -1514,14 +1544,21 @@ struct platform_device *__init at32_add_device_pwm(u32 mask) if (platform_device_add_data(pdev, &mask, sizeof(mask))) goto out_free_pdev; + pin_mask = 0; if (mask & (1 << 0)) - select_peripheral(PA(28), PERIPH_A, 0); + pin_mask |= (1 << 28); if (mask & (1 << 1)) - select_peripheral(PA(29), PERIPH_A, 0); + pin_mask |= (1 << 29); + if (pin_mask > 0) + select_peripheral(PIOA, pin_mask, PERIPH_A, 0); + + pin_mask = 0; if (mask & (1 << 2)) - select_peripheral(PA(21), PERIPH_B, 0); + pin_mask |= (1 << 21); if (mask & (1 << 3)) - select_peripheral(PA(22), PERIPH_B, 0); + pin_mask |= (1 << 22); + if (pin_mask > 0) + select_peripheral(PIOA, pin_mask, PERIPH_B, 0); atmel_pwm0_mck.dev = &pdev->dev; @@ -1562,52 +1599,65 @@ struct platform_device *__init at32_add_device_ssc(unsigned int id, unsigned int flags) { struct platform_device *pdev; + u32 pin_mask = 0; switch (id) { case 0: pdev = &ssc0_device; if (flags & ATMEL_SSC_RF) - select_peripheral(PA(21), PERIPH_A, 0); /* RF */ + pin_mask |= (1 << 21); /* RF */ if (flags & ATMEL_SSC_RK) - select_peripheral(PA(22), PERIPH_A, 0); /* RK */ + pin_mask |= (1 << 22); /* RK */ if (flags & ATMEL_SSC_TK) - select_peripheral(PA(23), PERIPH_A, 0); /* TK */ + pin_mask |= (1 << 23); /* TK */ if (flags & ATMEL_SSC_TF) - select_peripheral(PA(24), PERIPH_A, 0); /* TF */ + pin_mask |= (1 << 24); /* TF */ if (flags & ATMEL_SSC_TD) - select_peripheral(PA(25), PERIPH_A, 0); /* TD */ + pin_mask |= (1 << 25); /* TD */ if (flags & ATMEL_SSC_RD) - select_peripheral(PA(26), PERIPH_A, 0); /* RD */ + pin_mask |= (1 << 26); /* RD */ + + if (pin_mask > 0) + select_peripheral(PIOA, pin_mask, PERIPH_A, 0); + break; case 1: pdev = &ssc1_device; if (flags & ATMEL_SSC_RF) - select_peripheral(PA(0), PERIPH_B, 0); /* RF */ + pin_mask |= (1 << 0); /* RF */ if (flags & ATMEL_SSC_RK) - select_peripheral(PA(1), PERIPH_B, 0); /* RK */ + pin_mask |= (1 << 1); /* RK */ if (flags & ATMEL_SSC_TK) - select_peripheral(PA(2), PERIPH_B, 0); /* TK */ + pin_mask |= (1 << 2); /* TK */ if (flags & ATMEL_SSC_TF) - select_peripheral(PA(3), PERIPH_B, 0); /* TF */ + pin_mask |= (1 << 3); /* TF */ if (flags & ATMEL_SSC_TD) - select_peripheral(PA(4), PERIPH_B, 0); /* TD */ + pin_mask |= (1 << 4); /* TD */ if (flags & ATMEL_SSC_RD) - select_peripheral(PA(5), PERIPH_B, 0); /* RD */ + pin_mask |= (1 << 5); /* RD */ + + if (pin_mask > 0) + select_peripheral(PIOA, pin_mask, PERIPH_B, 0); + break; case 2: pdev = &ssc2_device; if (flags & ATMEL_SSC_TD) - select_peripheral(PB(13), PERIPH_A, 0); /* TD */ + pin_mask |= (1 << 13); /* TD */ if (flags & ATMEL_SSC_RD) - select_peripheral(PB(14), PERIPH_A, 0); /* RD */ + pin_mask |= (1 << 14); /* RD */ if (flags & ATMEL_SSC_TK) - select_peripheral(PB(15), PERIPH_A, 0); /* TK */ + pin_mask |= (1 << 15); /* TK */ if (flags & ATMEL_SSC_TF) - select_peripheral(PB(16), PERIPH_A, 0); /* TF */ + pin_mask |= (1 << 16); /* TF */ if (flags & ATMEL_SSC_RF) - select_peripheral(PB(17), PERIPH_A, 0); /* RF */ + pin_mask |= (1 << 17); /* RF */ if (flags & ATMEL_SSC_RK) - select_peripheral(PB(18), PERIPH_A, 0); /* RK */ + pin_mask |= (1 << 18); /* RK */ + + if (pin_mask > 0) + select_peripheral(PIOB, pin_mask, PERIPH_A, 0); + break; default: return NULL; @@ -1745,14 +1795,15 @@ static int __init at32_init_ide_or_cf(struct platform_device *pdev, unsigned int cs, unsigned int extint) { static unsigned int extint_pin_map[4] __initdata = { - GPIO_PIN_PB(25), - GPIO_PIN_PB(26), - GPIO_PIN_PB(27), - GPIO_PIN_PB(28), + (1 << 25), + (1 << 26), + (1 << 27), + (1 << 28), }; static bool common_pins_initialized __initdata = false; unsigned int extint_pin; int ret; + u32 pin_mask; if (extint >= ARRAY_SIZE(extint_pin_map)) return -EINVAL; @@ -1766,7 +1817,8 @@ static int __init at32_init_ide_or_cf(struct platform_device *pdev, if (ret) return ret; - select_peripheral(PE(21), PERIPH_A, 0); /* NCS4 -> OE_N */ + /* NCS4 -> OE_N */ + select_peripheral(PIOE, (1 << 21), PERIPH_A, 0); hmatrix_sfr_set_bits(HMATRIX_SLAVE_EBI, HMATRIX_EBI_CF0_ENABLE); break; case 5: @@ -1776,7 +1828,8 @@ static int __init at32_init_ide_or_cf(struct platform_device *pdev, if (ret) return ret; - select_peripheral(PE(22), PERIPH_A, 0); /* NCS5 -> OE_N */ + /* NCS5 -> OE_N */ + select_peripheral(PIOE, (1 << 22), PERIPH_A, 0); hmatrix_sfr_set_bits(HMATRIX_SLAVE_EBI, HMATRIX_EBI_CF1_ENABLE); break; default: @@ -1784,14 +1837,17 @@ static int __init at32_init_ide_or_cf(struct platform_device *pdev, } if (!common_pins_initialized) { - select_peripheral(PE(19), PERIPH_A, 0); /* CFCE1 -> CS0_N */ - select_peripheral(PE(20), PERIPH_A, 0); /* CFCE2 -> CS1_N */ - select_peripheral(PE(23), PERIPH_A, 0); /* CFRNW -> DIR */ - select_peripheral(PE(24), PERIPH_A, 0); /* NWAIT <- IORDY */ + pin_mask = (1 << 19); /* CFCE1 -> CS0_N */ + pin_mask |= (1 << 20); /* CFCE2 -> CS1_N */ + pin_mask |= (1 << 23); /* CFRNW -> DIR */ + pin_mask |= (1 << 24); /* NWAIT <- IORDY */ + + select_peripheral(PIOE, pin_mask, PERIPH_A, 0); + common_pins_initialized = true; } - at32_select_periph(extint_pin, GPIO_PERIPH_A, AT32_GPIOF_DEGLITCH); + select_peripheral(PIOB, extint_pin, PERIPH_A, AT32_GPIOF_DEGLITCH); pdev->resource[1].start = EIM_IRQ_BASE + extint; pdev->resource[1].end = pdev->resource[1].start; @@ -1930,6 +1986,7 @@ at32_add_device_ac97c(unsigned int id, struct ac97c_platform_data *data) { struct platform_device *pdev; struct ac97c_platform_data _data; + u32 pin_mask; if (id != 0) return NULL; @@ -1956,10 +2013,10 @@ at32_add_device_ac97c(unsigned int id, struct ac97c_platform_data *data) sizeof(struct ac97c_platform_data))) goto fail; - select_peripheral(PB(20), PERIPH_B, 0); /* SDO */ - select_peripheral(PB(21), PERIPH_B, 0); /* SYNC */ - select_peripheral(PB(22), PERIPH_B, 0); /* SCLK */ - select_peripheral(PB(23), PERIPH_B, 0); /* SDI */ + pin_mask = (1 << 20) | (1 << 21); /* SDO & SYNC */ + pin_mask |= (1 << 22) | (1 << 23); /* SCLK & SDI */ + + select_peripheral(PIOB, pin_mask, PERIPH_B, 0); /* TODO: gpio_is_valid(data->reset_pin) with kernel 2.6.26. */ if (data->reset_pin != GPIO_PIN_NONE) @@ -2001,6 +2058,7 @@ static struct clk abdac0_sample_clk = { struct platform_device *__init at32_add_device_abdac(unsigned int id) { struct platform_device *pdev; + u32 pin_mask; if (id != 0) return NULL; @@ -2013,10 +2071,10 @@ struct platform_device *__init at32_add_device_abdac(unsigned int id) ARRAY_SIZE(abdac0_resource))) goto err_add_resources; - select_peripheral(PB(20), PERIPH_A, 0); /* DATA1 */ - select_peripheral(PB(21), PERIPH_A, 0); /* DATA0 */ - select_peripheral(PB(22), PERIPH_A, 0); /* DATAN1 */ - select_peripheral(PB(23), PERIPH_A, 0); /* DATAN0 */ + pin_mask = (1 << 20) | (1 << 22); /* DATA1 & DATAN1 */ + pin_mask |= (1 << 21) | (1 << 23); /* DATA0 & DATAN0 */ + + select_peripheral(PIOB, pin_mask, PERIPH_A, 0); abdac0_pclk.dev = &pdev->dev; abdac0_sample_clk.dev = &pdev->dev; @@ -2073,7 +2131,7 @@ static struct clk gclk4 = { .index = 4, }; -struct clk *at32_clock_list[] = { +static __initdata struct clk *init_clocks[] = { &osc32k, &osc0, &osc1, @@ -2137,7 +2195,6 @@ struct clk *at32_clock_list[] = { &gclk3, &gclk4, }; -unsigned int at32_nr_clocks = ARRAY_SIZE(at32_clock_list); void __init setup_platform(void) { @@ -2168,14 +2225,19 @@ void __init setup_platform(void) genclk_init_parent(&abdac0_sample_clk); /* - * Turn on all clocks that have at least one user already, and - * turn off everything else. We only do this for module - * clocks, and even though it isn't particularly pretty to - * check the address of the mode function, it should do the - * trick... + * Build initial dynamic clock list by registering all clocks + * from the array. + * At the same time, turn on all clocks that have at least one + * user already, and turn off everything else. We only do this + * for module clocks, and even though it isn't particularly + * pretty to check the address of the mode function, it should + * do the trick... */ - for (i = 0; i < ARRAY_SIZE(at32_clock_list); i++) { - struct clk *clk = at32_clock_list[i]; + for (i = 0; i < ARRAY_SIZE(init_clocks); i++) { + struct clk *clk = init_clocks[i]; + + /* first, register clock */ + at32_clk_register(clk); if (clk->users == 0) continue; diff --git a/arch/avr32/mach-at32ap/clock.c b/arch/avr32/mach-at32ap/clock.c index 6c27ddac5ad..138a00a2a2d 100644 --- a/arch/avr32/mach-at32ap/clock.c +++ b/arch/avr32/mach-at32ap/clock.c @@ -15,24 +15,40 @@ #include <linux/err.h> #include <linux/device.h> #include <linux/string.h> +#include <linux/list.h> #include <mach/chip.h> #include "clock.h" +/* at32 clock list */ +static LIST_HEAD(at32_clock_list); + static DEFINE_SPINLOCK(clk_lock); +static DEFINE_SPINLOCK(clk_list_lock); + +void at32_clk_register(struct clk *clk) +{ + spin_lock(&clk_list_lock); + /* add the new item to the end of the list */ + list_add_tail(&clk->list, &at32_clock_list); + spin_unlock(&clk_list_lock); +} struct clk *clk_get(struct device *dev, const char *id) { - int i; + struct clk *clk; - for (i = 0; i < at32_nr_clocks; i++) { - struct clk *clk = at32_clock_list[i]; + spin_lock(&clk_list_lock); - if (clk->dev == dev && strcmp(id, clk->name) == 0) + list_for_each_entry(clk, &at32_clock_list, list) { + if (clk->dev == dev && strcmp(id, clk->name) == 0) { + spin_unlock(&clk_list_lock); return clk; + } } + spin_unlock(&clk_list_lock); return ERR_PTR(-ENOENT); } EXPORT_SYMBOL(clk_get); @@ -203,8 +219,8 @@ dump_clock(struct clk *parent, struct clkinf *r) /* cost of this scan is small, but not linear... */ r->nest = nest + NEST_DELTA; - for (i = 3; i < at32_nr_clocks; i++) { - clk = at32_clock_list[i]; + + list_for_each_entry(clk, &at32_clock_list, list) { if (clk->parent == parent) dump_clock(clk, r); } @@ -215,6 +231,7 @@ static int clk_show(struct seq_file *s, void *unused) { struct clkinf r; int i; + struct clk *clk; /* show all the power manager registers */ seq_printf(s, "MCCTRL = %8x\n", pm_readl(MCCTRL)); @@ -234,14 +251,25 @@ static int clk_show(struct seq_file *s, void *unused) seq_printf(s, "\n"); - /* show clock tree as derived from the three oscillators - * we "know" are at the head of the list - */ r.s = s; r.nest = 0; - dump_clock(at32_clock_list[0], &r); - dump_clock(at32_clock_list[1], &r); - dump_clock(at32_clock_list[2], &r); + /* protected from changes on the list while dumping */ + spin_lock(&clk_list_lock); + + /* show clock tree as derived from the three oscillators */ + clk = clk_get(NULL, "osc32k"); + dump_clock(clk, &r); + clk_put(clk); + + clk = clk_get(NULL, "osc0"); + dump_clock(clk, &r); + clk_put(clk); + + clk = clk_get(NULL, "osc1"); + dump_clock(clk, &r); + clk_put(clk); + + spin_unlock(&clk_list_lock); return 0; } diff --git a/arch/avr32/mach-at32ap/clock.h b/arch/avr32/mach-at32ap/clock.h index bb8e1f29583..623bf0e9a1e 100644 --- a/arch/avr32/mach-at32ap/clock.h +++ b/arch/avr32/mach-at32ap/clock.h @@ -12,8 +12,13 @@ * published by the Free Software Foundation. */ #include <linux/clk.h> +#include <linux/list.h> + + +void at32_clk_register(struct clk *clk); struct clk { + struct list_head list; /* linking element */ const char *name; /* Clock name/function */ struct device *dev; /* Device the clock is used by */ struct clk *parent; /* Parent clock, if any */ @@ -25,6 +30,3 @@ struct clk { u16 users; /* Enabled if non-zero */ u16 index; /* Sibling index */ }; - -extern struct clk *at32_clock_list[]; -extern unsigned int at32_nr_clocks; diff --git a/arch/avr32/mach-at32ap/include/mach/at32ap700x.h b/arch/avr32/mach-at32ap/include/mach/at32ap700x.h index 1e9852d65cc..a77d372f6f3 100644 --- a/arch/avr32/mach-at32ap/include/mach/at32ap700x.h +++ b/arch/avr32/mach-at32ap/include/mach/at32ap700x.h @@ -83,4 +83,132 @@ #define HMATRIX_BASE 0xfff00800 #define SDRAMC_BASE 0xfff03800 +/* LCDC on port C */ +#define ATMEL_LCDC_PC_CC (1ULL << 19) +#define ATMEL_LCDC_PC_HSYNC (1ULL << 20) +#define ATMEL_LCDC_PC_PCLK (1ULL << 21) +#define ATMEL_LCDC_PC_VSYNC (1ULL << 22) +#define ATMEL_LCDC_PC_DVAL (1ULL << 23) +#define ATMEL_LCDC_PC_MODE (1ULL << 24) +#define ATMEL_LCDC_PC_PWR (1ULL << 25) +#define ATMEL_LCDC_PC_DATA0 (1ULL << 26) +#define ATMEL_LCDC_PC_DATA1 (1ULL << 27) +#define ATMEL_LCDC_PC_DATA2 (1ULL << 28) +#define ATMEL_LCDC_PC_DATA3 (1ULL << 29) +#define ATMEL_LCDC_PC_DATA4 (1ULL << 30) +#define ATMEL_LCDC_PC_DATA5 (1ULL << 31) + +/* LCDC on port D */ +#define ATMEL_LCDC_PD_DATA6 (1ULL << 0) +#define ATMEL_LCDC_PD_DATA7 (1ULL << 1) +#define ATMEL_LCDC_PD_DATA8 (1ULL << 2) +#define ATMEL_LCDC_PD_DATA9 (1ULL << 3) +#define ATMEL_LCDC_PD_DATA10 (1ULL << 4) +#define ATMEL_LCDC_PD_DATA11 (1ULL << 5) +#define ATMEL_LCDC_PD_DATA12 (1ULL << 6) +#define ATMEL_LCDC_PD_DATA13 (1ULL << 7) +#define ATMEL_LCDC_PD_DATA14 (1ULL << 8) +#define ATMEL_LCDC_PD_DATA15 (1ULL << 9) +#define ATMEL_LCDC_PD_DATA16 (1ULL << 10) +#define ATMEL_LCDC_PD_DATA17 (1ULL << 11) +#define ATMEL_LCDC_PD_DATA18 (1ULL << 12) +#define ATMEL_LCDC_PD_DATA19 (1ULL << 13) +#define ATMEL_LCDC_PD_DATA20 (1ULL << 14) +#define ATMEL_LCDC_PD_DATA21 (1ULL << 15) +#define ATMEL_LCDC_PD_DATA22 (1ULL << 16) +#define ATMEL_LCDC_PD_DATA23 (1ULL << 17) + +/* LCDC on port E */ +#define ATMEL_LCDC_PE_CC (1ULL << (32 + 0)) +#define ATMEL_LCDC_PE_DVAL (1ULL << (32 + 1)) +#define ATMEL_LCDC_PE_MODE (1ULL << (32 + 2)) +#define ATMEL_LCDC_PE_DATA0 (1ULL << (32 + 3)) +#define ATMEL_LCDC_PE_DATA1 (1ULL << (32 + 4)) +#define ATMEL_LCDC_PE_DATA2 (1ULL << (32 + 5)) +#define ATMEL_LCDC_PE_DATA3 (1ULL << (32 + 6)) +#define ATMEL_LCDC_PE_DATA4 (1ULL << (32 + 7)) +#define ATMEL_LCDC_PE_DATA8 (1ULL << (32 + 8)) +#define ATMEL_LCDC_PE_DATA9 (1ULL << (32 + 9)) +#define ATMEL_LCDC_PE_DATA10 (1ULL << (32 + 10)) +#define ATMEL_LCDC_PE_DATA11 (1ULL << (32 + 11)) +#define ATMEL_LCDC_PE_DATA12 (1ULL << (32 + 12)) +#define ATMEL_LCDC_PE_DATA16 (1ULL << (32 + 13)) +#define ATMEL_LCDC_PE_DATA17 (1ULL << (32 + 14)) +#define ATMEL_LCDC_PE_DATA18 (1ULL << (32 + 15)) +#define ATMEL_LCDC_PE_DATA19 (1ULL << (32 + 16)) +#define ATMEL_LCDC_PE_DATA20 (1ULL << (32 + 17)) +#define ATMEL_LCDC_PE_DATA21 (1ULL << (32 + 18)) + + +#define ATMEL_LCDC(PORT, PIN) (ATMEL_LCDC_##PORT##_##PIN) + + +#define ATMEL_LCDC_PRI_24B_DATA ( \ + ATMEL_LCDC(PC, DATA0) | ATMEL_LCDC(PC, DATA1) | \ + ATMEL_LCDC(PC, DATA2) | ATMEL_LCDC(PC, DATA3) | \ + ATMEL_LCDC(PC, DATA4) | ATMEL_LCDC(PC, DATA5) | \ + ATMEL_LCDC(PD, DATA6) | ATMEL_LCDC(PD, DATA7) | \ + ATMEL_LCDC(PD, DATA8) | ATMEL_LCDC(PD, DATA9) | \ + ATMEL_LCDC(PD, DATA10) | ATMEL_LCDC(PD, DATA11) | \ + ATMEL_LCDC(PD, DATA12) | ATMEL_LCDC(PD, DATA13) | \ + ATMEL_LCDC(PD, DATA14) | ATMEL_LCDC(PD, DATA15) | \ + ATMEL_LCDC(PD, DATA16) | ATMEL_LCDC(PD, DATA17) | \ + ATMEL_LCDC(PD, DATA18) | ATMEL_LCDC(PD, DATA19) | \ + ATMEL_LCDC(PD, DATA20) | ATMEL_LCDC(PD, DATA21) | \ + ATMEL_LCDC(PD, DATA22) | ATMEL_LCDC(PD, DATA23)) + +#define ATMEL_LCDC_ALT_24B_DATA ( \ + ATMEL_LCDC(PE, DATA0) | ATMEL_LCDC(PE, DATA1) | \ + ATMEL_LCDC(PE, DATA2) | ATMEL_LCDC(PE, DATA3) | \ + ATMEL_LCDC(PE, DATA4) | ATMEL_LCDC(PC, DATA5) | \ + ATMEL_LCDC(PD, DATA6) | ATMEL_LCDC(PD, DATA7) | \ + ATMEL_LCDC(PE, DATA8) | ATMEL_LCDC(PE, DATA9) | \ + ATMEL_LCDC(PE, DATA10) | ATMEL_LCDC(PE, DATA11) | \ + ATMEL_LCDC(PE, DATA12) | ATMEL_LCDC(PD, DATA13) | \ + ATMEL_LCDC(PD, DATA14) | ATMEL_LCDC(PD, DATA15) | \ + ATMEL_LCDC(PE, DATA16) | ATMEL_LCDC(PE, DATA17) | \ + ATMEL_LCDC(PE, DATA18) | ATMEL_LCDC(PE, DATA19) | \ + ATMEL_LCDC(PE, DATA20) | ATMEL_LCDC(PE, DATA21) | \ + ATMEL_LCDC(PD, DATA22) | ATMEL_LCDC(PD, DATA23)) + +#define ATMEL_LCDC_PRI_15B_DATA ( \ + ATMEL_LCDC(PC, DATA0) | ATMEL_LCDC(PC, DATA1) | \ + ATMEL_LCDC(PC, DATA2) | ATMEL_LCDC(PC, DATA3) | \ + ATMEL_LCDC(PC, DATA4) | ATMEL_LCDC(PC, DATA5) | \ + ATMEL_LCDC(PD, DATA8) | ATMEL_LCDC(PD, DATA9) | \ + ATMEL_LCDC(PD, DATA10) | ATMEL_LCDC(PD, DATA11) | \ + ATMEL_LCDC(PD, DATA12) | ATMEL_LCDC(PD, DATA16) | \ + ATMEL_LCDC(PD, DATA17) | ATMEL_LCDC(PD, DATA18) | \ + ATMEL_LCDC(PD, DATA19) | ATMEL_LCDC(PD, DATA20)) + +#define ATMEL_LCDC_ALT_15B_DATA ( \ + ATMEL_LCDC(PE, DATA0) | ATMEL_LCDC(PE, DATA1) | \ + ATMEL_LCDC(PE, DATA2) | ATMEL_LCDC(PE, DATA3) | \ + ATMEL_LCDC(PE, DATA4) | ATMEL_LCDC(PC, DATA5) | \ + ATMEL_LCDC(PE, DATA8) | ATMEL_LCDC(PE, DATA9) | \ + ATMEL_LCDC(PE, DATA10) | ATMEL_LCDC(PE, DATA11) | \ + ATMEL_LCDC(PE, DATA12) | ATMEL_LCDC(PE, DATA16) | \ + ATMEL_LCDC(PE, DATA17) | ATMEL_LCDC(PE, DATA18) | \ + ATMEL_LCDC(PE, DATA19) | ATMEL_LCDC(PE, DATA20)) + +#define ATMEL_LCDC_PRI_CONTROL ( \ + ATMEL_LCDC(PC, CC) | ATMEL_LCDC(PC, DVAL) | \ + ATMEL_LCDC(PC, MODE) | ATMEL_LCDC(PC, PWR)) + +#define ATMEL_LCDC_ALT_CONTROL ( \ + ATMEL_LCDC(PE, CC) | ATMEL_LCDC(PE, DVAL) | \ + ATMEL_LCDC(PE, MODE) | ATMEL_LCDC(PC, PWR)) + +#define ATMEL_LCDC_CONTROL ( \ + ATMEL_LCDC(PC, HSYNC) | ATMEL_LCDC(PC, VSYNC) | \ + ATMEL_LCDC(PC, PCLK)) + +#define ATMEL_LCDC_PRI_24BIT (ATMEL_LCDC_CONTROL | ATMEL_LCDC_PRI_24B_DATA) + +#define ATMEL_LCDC_ALT_24BIT (ATMEL_LCDC_CONTROL | ATMEL_LCDC_ALT_24B_DATA) + +#define ATMEL_LCDC_PRI_15BIT (ATMEL_LCDC_CONTROL | ATMEL_LCDC_PRI_15B_DATA) + +#define ATMEL_LCDC_ALT_15BIT (ATMEL_LCDC_CONTROL | ATMEL_LCDC_ALT_15B_DATA) + #endif /* __ASM_ARCH_AT32AP700X_H__ */ diff --git a/arch/avr32/mach-at32ap/include/mach/board.h b/arch/avr32/mach-at32ap/include/mach/board.h index e60e9076544..c48386d66bc 100644 --- a/arch/avr32/mach-at32ap/include/mach/board.h +++ b/arch/avr32/mach-at32ap/include/mach/board.h @@ -43,7 +43,7 @@ struct atmel_lcdfb_info; struct platform_device * at32_add_device_lcdc(unsigned int id, struct atmel_lcdfb_info *data, unsigned long fbmem_start, unsigned long fbmem_len, - unsigned int pin_config); + u64 pin_mask); struct usba_platform_data; struct platform_device * diff --git a/arch/avr32/mach-at32ap/include/mach/io.h b/arch/avr32/mach-at32ap/include/mach/io.h index 4ec6abc68ea..22ea79b7405 100644 --- a/arch/avr32/mach-at32ap/include/mach/io.h +++ b/arch/avr32/mach-at32ap/include/mach/io.h @@ -1,8 +1,7 @@ #ifndef __ASM_AVR32_ARCH_AT32AP_IO_H #define __ASM_AVR32_ARCH_AT32AP_IO_H -/* For "bizarre" halfword swapping */ -#include <linux/byteorder/swabb.h> +#include <linux/swab.h> #if defined(CONFIG_AP700X_32_BIT_SMC) # define __swizzle_addr_b(addr) (addr ^ 3UL) diff --git a/arch/avr32/mach-at32ap/include/mach/portmux.h b/arch/avr32/mach-at32ap/include/mach/portmux.h index b1abe6b4e4e..21c79373b53 100644 --- a/arch/avr32/mach-at32ap/include/mach/portmux.h +++ b/arch/avr32/mach-at32ap/include/mach/portmux.h @@ -21,9 +21,10 @@ #define AT32_GPIOF_DEGLITCH 0x00000008 /* (IN) Filter glitches */ #define AT32_GPIOF_MULTIDRV 0x00000010 /* Enable multidriver option */ -void at32_select_periph(unsigned int pin, unsigned int periph, - unsigned long flags); +void at32_select_periph(unsigned int port, unsigned int pin, + unsigned int periph, unsigned long flags); void at32_select_gpio(unsigned int pin, unsigned long flags); +void at32_deselect_pin(unsigned int pin); void at32_reserve_pin(unsigned int pin); #endif /* __ASM_ARCH_PORTMUX_H__ */ diff --git a/arch/avr32/mach-at32ap/pdc.c b/arch/avr32/mach-at32ap/pdc.c index 1040bda4fda..61ab15aae97 100644 --- a/arch/avr32/mach-at32ap/pdc.c +++ b/arch/avr32/mach-at32ap/pdc.c @@ -35,7 +35,6 @@ static int __init pdc_probe(struct platform_device *pdev) } static struct platform_driver pdc_driver = { - .probe = pdc_probe, .driver = { .name = "pdc", }, @@ -43,6 +42,6 @@ static struct platform_driver pdc_driver = { static int __init pdc_init(void) { - return platform_driver_register(&pdc_driver); + return platform_driver_probe(&pdc_driver, pdc_probe); } arch_initcall(pdc_init); diff --git a/arch/avr32/mach-at32ap/pio.c b/arch/avr32/mach-at32ap/pio.c index 405ee6bad4c..ed81a8bcb22 100644 --- a/arch/avr32/mach-at32ap/pio.c +++ b/arch/avr32/mach-at32ap/pio.c @@ -50,35 +50,48 @@ static struct pio_device *gpio_to_pio(unsigned int gpio) } /* Pin multiplexing API */ +static DEFINE_SPINLOCK(pio_lock); -void __init at32_select_periph(unsigned int pin, unsigned int periph, - unsigned long flags) +void __init at32_select_periph(unsigned int port, u32 pin_mask, + unsigned int periph, unsigned long flags) { struct pio_device *pio; - unsigned int pin_index = pin & 0x1f; - u32 mask = 1 << pin_index; - pio = gpio_to_pio(pin); + /* assign and verify pio */ + pio = gpio_to_pio(port); if (unlikely(!pio)) { - printk("pio: invalid pin %u\n", pin); + printk(KERN_WARNING "pio: invalid port %u\n", port); goto fail; } - if (unlikely(test_and_set_bit(pin_index, &pio->pinmux_mask) - || gpiochip_is_requested(&pio->chip, pin_index))) { - printk("%s: pin %u is busy\n", pio->name, pin_index); + /* Test if any of the requested pins is already muxed */ + spin_lock(&pio_lock); + if (unlikely(pio->pinmux_mask & pin_mask)) { + printk(KERN_WARNING "%s: pin(s) busy (requested 0x%x, busy 0x%x)\n", + pio->name, pin_mask, pio->pinmux_mask & pin_mask); + spin_unlock(&pio_lock); goto fail; } - pio_writel(pio, PUER, mask); + pio->pinmux_mask |= pin_mask; + + /* enable pull ups */ + pio_writel(pio, PUER, pin_mask); + + /* select either peripheral A or B */ if (periph) - pio_writel(pio, BSR, mask); + pio_writel(pio, BSR, pin_mask); else - pio_writel(pio, ASR, mask); + pio_writel(pio, ASR, pin_mask); + + /* enable peripheral control */ + pio_writel(pio, PDR, pin_mask); - pio_writel(pio, PDR, mask); + /* Disable pull ups if not requested. */ if (!(flags & AT32_GPIOF_PULLUP)) - pio_writel(pio, PUDR, mask); + pio_writel(pio, PUDR, pin_mask); + + spin_unlock(&pio_lock); return; @@ -134,6 +147,25 @@ fail: dump_stack(); } +/* + * Undo a previous pin reservation. Will not affect the hardware + * configuration. + */ +void at32_deselect_pin(unsigned int pin) +{ + struct pio_device *pio; + unsigned int pin_index = pin & 0x1f; + + pio = gpio_to_pio(pin); + if (unlikely(!pio)) { + printk("pio: invalid pin %u\n", pin); + dump_stack(); + return; + } + + clear_bit(pin_index, &pio->pinmux_mask); +} + /* Reserve a pin, preventing anyone else from changing its configuration. */ void __init at32_reserve_pin(unsigned int pin) { @@ -382,7 +414,6 @@ static int __init pio_probe(struct platform_device *pdev) } static struct platform_driver pio_driver = { - .probe = pio_probe, .driver = { .name = "pio", }, @@ -390,7 +421,7 @@ static struct platform_driver pio_driver = { static int __init pio_init(void) { - return platform_driver_register(&pio_driver); + return platform_driver_probe(&pio_driver, pio_probe); } postcore_initcall(pio_init); diff --git a/arch/avr32/oprofile/Makefile b/arch/avr32/oprofile/Makefile index 1fe81c3c1e8..e0eb520e028 100644 --- a/arch/avr32/oprofile/Makefile +++ b/arch/avr32/oprofile/Makefile @@ -5,4 +5,4 @@ oprofile-y := $(addprefix ../../../drivers/oprofile/, \ event_buffer.o oprofile_files.o \ oprofilefs.o oprofile_stats.o \ timer_int.o) -oprofile-y += op_model_avr32.o +oprofile-y += op_model_avr32.o backtrace.o diff --git a/arch/avr32/oprofile/backtrace.c b/arch/avr32/oprofile/backtrace.c new file mode 100644 index 00000000000..75d9ad6f99c --- /dev/null +++ b/arch/avr32/oprofile/backtrace.c @@ -0,0 +1,81 @@ +/* + * AVR32 specific backtracing code for oprofile + * + * Copyright 2008 Weinmann GmbH + * + * Author: Nikolaus Voss <n.voss@weinmann.de> + * + * Based on i386 oprofile backtrace code by John Levon and David Smith + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/oprofile.h> +#include <linux/sched.h> +#include <linux/uaccess.h> + +/* The first two words of each frame on the stack look like this if we have + * frame pointers */ +struct frame_head { + unsigned long lr; + struct frame_head *fp; +}; + +/* copied from arch/avr32/kernel/process.c */ +static inline int valid_stack_ptr(struct thread_info *tinfo, unsigned long p) +{ + return (p > (unsigned long)tinfo) + && (p < (unsigned long)tinfo + THREAD_SIZE - 3); +} + +/* copied from arch/x86/oprofile/backtrace.c */ +static struct frame_head *dump_user_backtrace(struct frame_head *head) +{ + struct frame_head bufhead[2]; + + /* Also check accessibility of one struct frame_head beyond */ + if (!access_ok(VERIFY_READ, head, sizeof(bufhead))) + return NULL; + if (__copy_from_user_inatomic(bufhead, head, sizeof(bufhead))) + return NULL; + + oprofile_add_trace(bufhead[0].lr); + + /* frame pointers should strictly progress back up the stack + * (towards higher addresses) */ + if (bufhead[0].fp <= head) + return NULL; + + return bufhead[0].fp; +} + +void avr32_backtrace(struct pt_regs * const regs, unsigned int depth) +{ + /* Get first frame pointer */ + struct frame_head *head = (struct frame_head *)(regs->r7); + + if (!user_mode(regs)) { +#ifdef CONFIG_FRAME_POINTER + /* + * Traverse the kernel stack from frame to frame up to + * "depth" steps. + */ + while (depth-- && valid_stack_ptr(task_thread_info(current), + (unsigned long)head)) { + oprofile_add_trace(head->lr); + if (head->fp <= head) + break; + head = head->fp; + } +#endif + } else { + /* Assume we have frame pointers in user mode process */ + while (depth-- && head) + head = dump_user_backtrace(head); + } +} + + diff --git a/arch/avr32/oprofile/op_model_avr32.c b/arch/avr32/oprofile/op_model_avr32.c index df42325c7f8..a3e9b3c4845 100644 --- a/arch/avr32/oprofile/op_model_avr32.c +++ b/arch/avr32/oprofile/op_model_avr32.c @@ -22,6 +22,8 @@ #define AVR32_PERFCTR_IRQ_GROUP 0 #define AVR32_PERFCTR_IRQ_LINE 1 +void avr32_backtrace(struct pt_regs * const regs, unsigned int depth); + enum { PCCNT, PCNT0, PCNT1, NR_counter }; struct avr32_perf_counter { @@ -223,6 +225,8 @@ int __init oprofile_arch_init(struct oprofile_operations *ops) memcpy(ops, &avr32_perf_counter_ops, sizeof(struct oprofile_operations)); + ops->backtrace = avr32_backtrace; + printk(KERN_INFO "oprofile: using AVR32 performance monitoring.\n"); return 0; diff --git a/arch/blackfin/include/asm/a.out.h b/arch/blackfin/include/asm/a.out.h deleted file mode 100644 index 6c3d652ebd3..00000000000 --- a/arch/blackfin/include/asm/a.out.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef __BFIN_A_OUT_H__ -#define __BFIN_A_OUT_H__ - -struct exec { - unsigned long a_info; /* Use macros N_MAGIC, etc for access */ - unsigned a_text; /* length of text, in bytes */ - unsigned a_data; /* length of data, in bytes */ - unsigned a_bss; /* length of uninitialized data area for file, in bytes */ - unsigned a_syms; /* length of symbol table data in file, in bytes */ - unsigned a_entry; /* start address */ - unsigned a_trsize; /* length of relocation info for text, in bytes */ - unsigned a_drsize; /* length of relocation info for data, in bytes */ -}; - -#define N_TRSIZE(a) ((a).a_trsize) -#define N_DRSIZE(a) ((a).a_drsize) -#define N_SYMSIZE(a) ((a).a_syms) - -#endif /* __BFIN_A_OUT_H__ */ diff --git a/arch/blackfin/kernel/bfin_dma_5xx.c b/arch/blackfin/kernel/bfin_dma_5xx.c index 93229b3d6e3..339293d677c 100644 --- a/arch/blackfin/kernel/bfin_dma_5xx.c +++ b/arch/blackfin/kernel/bfin_dma_5xx.c @@ -117,15 +117,14 @@ int request_dma(unsigned int channel, char *device_id) #ifdef CONFIG_BF54x if (channel >= CH_UART2_RX && channel <= CH_UART3_TX) { - if (strncmp(device_id, "BFIN_UART", 9) == 0) { - dma_ch[channel].regs->peripheral_map &= 0x0FFF; - dma_ch[channel].regs->peripheral_map |= + unsigned int per_map; + per_map = dma_ch[channel].regs->peripheral_map & 0xFFF; + if (strncmp(device_id, "BFIN_UART", 9) == 0) + dma_ch[channel].regs->peripheral_map = per_map | ((channel - CH_UART2_RX + 0xC)<<12); - } else { - dma_ch[channel].regs->peripheral_map &= 0x0FFF; - dma_ch[channel].regs->peripheral_map |= + else + dma_ch[channel].regs->peripheral_map = per_map | ((channel - CH_UART2_RX + 0x6)<<12); - } } #endif diff --git a/arch/blackfin/mach-bf527/include/mach/bfin_serial_5xx.h b/arch/blackfin/mach-bf527/include/mach/bfin_serial_5xx.h index 2526b6ed6fa..75722d6008b 100644 --- a/arch/blackfin/mach-bf527/include/mach/bfin_serial_5xx.h +++ b/arch/blackfin/mach-bf527/include/mach/bfin_serial_5xx.h @@ -78,6 +78,9 @@ # define CONFIG_UART1_RTS_PIN -1 # endif #endif + +#define BFIN_UART_TX_FIFO_SIZE 2 + /* * The pin configuration is different from schematic */ @@ -119,7 +122,6 @@ static inline void UART_CLEAR_LSR(struct bfin_serial_port *uart) bfin_write16(uart->port.membase + OFFSET_LSR, -1); } -struct bfin_serial_port bfin_serial_ports[BFIN_UART_NR_PORTS]; struct bfin_serial_res { unsigned long uart_base_addr; int uart_irq; @@ -164,8 +166,6 @@ struct bfin_serial_res bfin_serial_resource[] = { #endif }; -int nr_ports = ARRAY_SIZE(bfin_serial_resource); - #define DRIVER_NAME "bfin-uart" static void bfin_serial_hw_init(struct bfin_serial_port *uart) diff --git a/arch/blackfin/mach-bf533/include/mach/bfin_serial_5xx.h b/arch/blackfin/mach-bf533/include/mach/bfin_serial_5xx.h index ebf592b59aa..815bfe5dd1a 100644 --- a/arch/blackfin/mach-bf533/include/mach/bfin_serial_5xx.h +++ b/arch/blackfin/mach-bf533/include/mach/bfin_serial_5xx.h @@ -69,6 +69,8 @@ # endif #endif +#define BFIN_UART_TX_FIFO_SIZE 2 + struct bfin_serial_port { struct uart_port port; unsigned int old_status; @@ -111,7 +113,6 @@ static inline void UART_CLEAR_LSR(struct bfin_serial_port *uart) bfin_write16(uart->port.membase + OFFSET_LSR, -1); } -struct bfin_serial_port bfin_serial_ports[BFIN_UART_NR_PORTS]; struct bfin_serial_res { unsigned long uart_base_addr; int uart_irq; @@ -142,7 +143,6 @@ struct bfin_serial_res bfin_serial_resource[] = { #define DRIVER_NAME "bfin-uart" -int nr_ports = BFIN_UART_NR_PORTS; static void bfin_serial_hw_init(struct bfin_serial_port *uart) { diff --git a/arch/blackfin/mach-bf537/include/mach/bfin_serial_5xx.h b/arch/blackfin/mach-bf537/include/mach/bfin_serial_5xx.h index 1bf56ffa22f..b3f87e1d16a 100644 --- a/arch/blackfin/mach-bf537/include/mach/bfin_serial_5xx.h +++ b/arch/blackfin/mach-bf537/include/mach/bfin_serial_5xx.h @@ -78,6 +78,9 @@ # define CONFIG_UART1_RTS_PIN -1 # endif #endif + +#define BFIN_UART_TX_FIFO_SIZE 2 + /* * The pin configuration is different from schematic */ @@ -119,7 +122,6 @@ static inline void UART_CLEAR_LSR(struct bfin_serial_port *uart) bfin_write16(uart->port.membase + OFFSET_LSR, -1); } -struct bfin_serial_port bfin_serial_ports[BFIN_UART_NR_PORTS]; struct bfin_serial_res { unsigned long uart_base_addr; int uart_irq; @@ -164,8 +166,6 @@ struct bfin_serial_res bfin_serial_resource[] = { #endif }; -int nr_ports = ARRAY_SIZE(bfin_serial_resource); - #define DRIVER_NAME "bfin-uart" static void bfin_serial_hw_init(struct bfin_serial_port *uart) diff --git a/arch/blackfin/mach-bf548/include/mach/bfin_serial_5xx.h b/arch/blackfin/mach-bf548/include/mach/bfin_serial_5xx.h index 5e29446a8e0..e4cf35e7ab9 100644 --- a/arch/blackfin/mach-bf548/include/mach/bfin_serial_5xx.h +++ b/arch/blackfin/mach-bf548/include/mach/bfin_serial_5xx.h @@ -82,6 +82,9 @@ # define CONFIG_UART1_RTS_PIN -1 # endif #endif + +#define BFIN_UART_TX_FIFO_SIZE 2 + /* * The pin configuration is different from schematic */ @@ -105,7 +108,6 @@ struct bfin_serial_port { #endif }; -struct bfin_serial_port bfin_serial_ports[BFIN_UART_NR_PORTS]; struct bfin_serial_res { unsigned long uart_base_addr; int uart_irq; @@ -170,8 +172,6 @@ struct bfin_serial_res bfin_serial_resource[] = { #endif }; -int nr_ports = ARRAY_SIZE(bfin_serial_resource); - #define DRIVER_NAME "bfin-uart" static void bfin_serial_hw_init(struct bfin_serial_port *uart) diff --git a/arch/blackfin/mach-bf561/include/mach/bfin_serial_5xx.h b/arch/blackfin/mach-bf561/include/mach/bfin_serial_5xx.h index 8aa02780e64..e0ce0c1843d 100644 --- a/arch/blackfin/mach-bf561/include/mach/bfin_serial_5xx.h +++ b/arch/blackfin/mach-bf561/include/mach/bfin_serial_5xx.h @@ -69,6 +69,8 @@ # endif #endif +#define BFIN_UART_TX_FIFO_SIZE 2 + struct bfin_serial_port { struct uart_port port; unsigned int old_status; @@ -111,7 +113,6 @@ static inline void UART_CLEAR_LSR(struct bfin_serial_port *uart) bfin_write16(uart->port.membase + OFFSET_LSR, -1); } -struct bfin_serial_port bfin_serial_ports[BFIN_UART_NR_PORTS]; struct bfin_serial_res { unsigned long uart_base_addr; int uart_irq; @@ -142,7 +143,6 @@ struct bfin_serial_res bfin_serial_resource[] = { #define DRIVER_NAME "bfin-uart" -int nr_ports = BFIN_UART_NR_PORTS; static void bfin_serial_hw_init(struct bfin_serial_port *uart) { diff --git a/arch/cris/arch-v10/boot/tools/build.c b/arch/cris/arch-v10/boot/tools/build.c index 2f9bbb26d60..c8adef36416 100644 --- a/arch/cris/arch-v10/boot/tools/build.c +++ b/arch/cris/arch-v10/boot/tools/build.c @@ -30,7 +30,6 @@ #include <sys/sysmacros.h> #include <unistd.h> /* contains read/write */ #include <fcntl.h> -#include <linux/a.out.h> #include <errno.h> #define MINIX_HEADER 32 diff --git a/arch/h8300/Kconfig b/arch/h8300/Kconfig index 396ab059efa..107cb5bb9f3 100644 --- a/arch/h8300/Kconfig +++ b/arch/h8300/Kconfig @@ -66,9 +66,6 @@ config TIME_LOW_RES bool default y -config ARCH_SUPPORTS_AOUT - def_bool y - config NO_IOPORT def_bool y diff --git a/arch/h8300/include/asm/a.out.h b/arch/h8300/include/asm/a.out.h deleted file mode 100644 index ded780f0a49..00000000000 --- a/arch/h8300/include/asm/a.out.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef __H8300_A_OUT_H__ -#define __H8300_A_OUT_H__ - -struct exec -{ - unsigned long a_info; /* Use macros N_MAGIC, etc for access */ - unsigned a_text; /* length of text, in bytes */ - unsigned a_data; /* length of data, in bytes */ - unsigned a_bss; /* length of uninitialized data area for file, in bytes */ - unsigned a_syms; /* length of symbol table data in file, in bytes */ - unsigned a_entry; /* start address */ - unsigned a_trsize; /* length of relocation info for text, in bytes */ - unsigned a_drsize; /* length of relocation info for data, in bytes */ -}; - -#define N_TRSIZE(a) ((a).a_trsize) -#define N_DRSIZE(a) ((a).a_drsize) -#define N_SYMSIZE(a) ((a).a_syms) - -#endif /* __H8300_A_OUT_H__ */ diff --git a/arch/h8300/kernel/process.c b/arch/h8300/kernel/process.c index dfbe7ab9ffe..a8ef654a5a0 100644 --- a/arch/h8300/kernel/process.c +++ b/arch/h8300/kernel/process.c @@ -34,7 +34,6 @@ #include <linux/ptrace.h> #include <linux/slab.h> #include <linux/user.h> -#include <linux/a.out.h> #include <linux/interrupt.h> #include <linux/reboot.h> #include <linux/fs.h> diff --git a/arch/ia64/include/asm/a.out.h b/arch/ia64/include/asm/a.out.h deleted file mode 100644 index 193dcfb6759..00000000000 --- a/arch/ia64/include/asm/a.out.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef _ASM_IA64_A_OUT_H -#define _ASM_IA64_A_OUT_H - -/* - * No a.out format has been (or should be) defined so this file is - * just a dummy that allows us to get binfmt_elf compiled. It - * probably would be better to clean up binfmt_elf.c so it does not - * necessarily depend on there being a.out support. - * - * Modified 1998-2002 - * David Mosberger-Tang <davidm@hpl.hp.com>, Hewlett-Packard Co. - */ - -#include <linux/types.h> - -struct exec { - unsigned long a_info; - unsigned long a_text; - unsigned long a_data; - unsigned long a_bss; - unsigned long a_entry; -}; - -#define N_TXTADDR(x) 0 -#define N_DATADDR(x) 0 -#define N_BSSADDR(x) 0 -#define N_DRSIZE(x) 0 -#define N_TRSIZE(x) 0 -#define N_SYMSIZE(x) 0 -#define N_TXTOFF(x) 0 - -#endif /* _ASM_IA64_A_OUT_H */ diff --git a/arch/ia64/include/asm/siginfo.h b/arch/ia64/include/asm/siginfo.h index 9294e4b0c8b..118d4297900 100644 --- a/arch/ia64/include/asm/siginfo.h +++ b/arch/ia64/include/asm/siginfo.h @@ -113,11 +113,6 @@ typedef struct siginfo { #undef NSIGSEGV #define NSIGSEGV 3 -/* - * SIGTRAP si_codes - */ -#define TRAP_BRANCH (__SI_FAULT|3) /* process taken branch trap */ -#define TRAP_HWBKPT (__SI_FAULT|4) /* hardware breakpoint or watchpoint */ #undef NSIGTRAP #define NSIGTRAP 4 diff --git a/arch/ia64/include/asm/statfs.h b/arch/ia64/include/asm/statfs.h index 811097974f3..1e589669de5 100644 --- a/arch/ia64/include/asm/statfs.h +++ b/arch/ia64/include/asm/statfs.h @@ -8,55 +8,13 @@ * David Mosberger-Tang <davidm@hpl.hp.com>, Hewlett-Packard Co */ -#ifndef __KERNEL_STRICT_NAMES -# include <linux/types.h> -typedef __kernel_fsid_t fsid_t; -#endif - /* - * This is ugly --- we're already 64-bit, so just duplicate the definitions + * We need compat_statfs64 to be packed, because the i386 ABI won't + * add padding at the end to bring it to a multiple of 8 bytes, but + * the IA64 ABI will. */ -struct statfs { - long f_type; - long f_bsize; - long f_blocks; - long f_bfree; - long f_bavail; - long f_files; - long f_ffree; - __kernel_fsid_t f_fsid; - long f_namelen; - long f_frsize; - long f_spare[5]; -}; - - -struct statfs64 { - long f_type; - long f_bsize; - long f_blocks; - long f_bfree; - long f_bavail; - long f_files; - long f_ffree; - __kernel_fsid_t f_fsid; - long f_namelen; - long f_frsize; - long f_spare[5]; -}; +#define ARCH_PACK_COMPAT_STATFS64 __attribute__((packed,aligned(4))) -struct compat_statfs64 { - __u32 f_type; - __u32 f_bsize; - __u64 f_blocks; - __u64 f_bfree; - __u64 f_bavail; - __u64 f_files; - __u64 f_ffree; - __kernel_fsid_t f_fsid; - __u32 f_namelen; - __u32 f_frsize; - __u32 f_spare[5]; -} __attribute__((packed)); +#include <asm-generic/statfs.h> #endif /* _ASM_IA64_STATFS_H */ diff --git a/arch/ia64/mm/init.c b/arch/ia64/mm/init.c index 200100ea761..f482a9098e3 100644 --- a/arch/ia64/mm/init.c +++ b/arch/ia64/mm/init.c @@ -21,7 +21,6 @@ #include <linux/bitops.h> #include <linux/kexec.h> -#include <asm/a.out.h> #include <asm/dma.h> #include <asm/ia32.h> #include <asm/io.h> diff --git a/arch/m32r/Kconfig b/arch/m32r/Kconfig index f57113f1f89..00289c178f8 100644 --- a/arch/m32r/Kconfig +++ b/arch/m32r/Kconfig @@ -36,9 +36,6 @@ config NO_IOPORT config NO_DMA def_bool y -config ARCH_SUPPORTS_AOUT - def_bool y - config HZ int default 100 diff --git a/arch/m68k/Kconfig b/arch/m68k/Kconfig index 8c5e1de68fc..41e5bf02e23 100644 --- a/arch/m68k/Kconfig +++ b/arch/m68k/Kconfig @@ -5,6 +5,7 @@ config M68K bool default y + select HAVE_AOUT select HAVE_IDE config MMU @@ -53,9 +54,6 @@ config NO_IOPORT config NO_DMA def_bool SUN3 -config ARCH_SUPPORTS_AOUT - def_bool y - config HZ int default 100 diff --git a/arch/m68knommu/Kconfig b/arch/m68knommu/Kconfig index 2e7515e8db9..0a8998315e5 100644 --- a/arch/m68knommu/Kconfig +++ b/arch/m68knommu/Kconfig @@ -73,9 +73,6 @@ config GENERIC_CLOCKEVENTS config NO_IOPORT def_bool y -config ARCH_SUPPORTS_AOUT - def_bool y - source "init/Kconfig" menu "Processor type and features" diff --git a/arch/m68knommu/include/asm/a.out.h b/arch/m68knommu/include/asm/a.out.h deleted file mode 100644 index ce18ef99de0..00000000000 --- a/arch/m68knommu/include/asm/a.out.h +++ /dev/null @@ -1 +0,0 @@ -#include <asm-m68k/a.out.h> diff --git a/arch/m68knommu/kernel/process.c b/arch/m68knommu/kernel/process.c index 47502d5ec19..3f2d7745f31 100644 --- a/arch/m68knommu/kernel/process.c +++ b/arch/m68knommu/kernel/process.c @@ -25,7 +25,6 @@ #include <linux/ptrace.h> #include <linux/slab.h> #include <linux/user.h> -#include <linux/a.out.h> #include <linux/interrupt.h> #include <linux/reboot.h> #include <linux/fs.h> diff --git a/arch/m68knommu/kernel/traps.c b/arch/m68knommu/kernel/traps.c index 46f8f9d0c40..5d5d56bcd0e 100644 --- a/arch/m68knommu/kernel/traps.c +++ b/arch/m68knommu/kernel/traps.c @@ -22,7 +22,6 @@ #include <linux/mm.h> #include <linux/module.h> #include <linux/types.h> -#include <linux/a.out.h> #include <linux/user.h> #include <linux/string.h> #include <linux/linkage.h> diff --git a/arch/mips/include/asm/a.out.h b/arch/mips/include/asm/a.out.h deleted file mode 100644 index cad8371422a..00000000000 --- a/arch/mips/include/asm/a.out.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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) 1994 - 1999, 2003 by Ralf Baechle - */ -#ifndef _ASM_A_OUT_H -#define _ASM_A_OUT_H - -#ifdef __KERNEL__ - - -#endif - -struct exec -{ - unsigned long a_info; /* Use macros N_MAGIC, etc for access */ - unsigned a_text; /* length of text, in bytes */ - unsigned a_data; /* length of data, in bytes */ - unsigned a_bss; /* length of uninitialized data area for - file, in bytes */ - unsigned a_syms; /* length of symbol table data in file, - in bytes */ - unsigned a_entry; /* start address */ - unsigned a_trsize; /* length of relocation info for text, in - bytes */ - unsigned a_drsize; /* length of relocation info for data, in bytes */ -}; - -#define N_TRSIZE(a) ((a).a_trsize) -#define N_DRSIZE(a) ((a).a_drsize) -#define N_SYMSIZE(a) ((a).a_syms) - -#endif /* _ASM_A_OUT_H */ diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c index 22fc19bbe87..ca2e4026ad2 100644 --- a/arch/mips/kernel/process.c +++ b/arch/mips/kernel/process.c @@ -22,7 +22,6 @@ #include <linux/personality.h> #include <linux/sys.h> #include <linux/user.h> -#include <linux/a.out.h> #include <linux/init.h> #include <linux/completion.h> #include <linux/kallsyms.h> diff --git a/arch/mips/kernel/syscall.c b/arch/mips/kernel/syscall.c index 343015a2f41..37970d9b218 100644 --- a/arch/mips/kernel/syscall.c +++ b/arch/mips/kernel/syscall.c @@ -7,7 +7,6 @@ * Copyright (C) 1999, 2000 Silicon Graphics, Inc. * Copyright (C) 2001 MIPS Technologies, Inc. */ -#include <linux/a.out.h> #include <linux/capability.h> #include <linux/errno.h> #include <linux/linkage.h> diff --git a/arch/mn10300/Kconfig b/arch/mn10300/Kconfig index e856218da90..dd557c9cf00 100644 --- a/arch/mn10300/Kconfig +++ b/arch/mn10300/Kconfig @@ -53,9 +53,6 @@ config QUICKLIST config ARCH_HAS_ILOG2_U32 def_bool y -config ARCH_SUPPORTS_AOUT - def_bool n - # Use the generic interrupt handling code in kernel/irq/ config GENERIC_HARDIRQS def_bool y diff --git a/arch/parisc/Kconfig b/arch/parisc/Kconfig index a7d4fd353c2..8313fccced5 100644 --- a/arch/parisc/Kconfig +++ b/arch/parisc/Kconfig @@ -76,9 +76,6 @@ config IRQ_PER_CPU bool default y -config ARCH_SUPPORTS_AOUT - def_bool y - # unless you want to implement ACPI on PA-RISC ... ;-) config PM bool diff --git a/arch/powerpc/include/asm/a.out.h b/arch/powerpc/include/asm/a.out.h deleted file mode 100644 index 89cead6b176..00000000000 --- a/arch/powerpc/include/asm/a.out.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef _ASM_POWERPC_A_OUT_H -#define _ASM_POWERPC_A_OUT_H - -struct exec -{ - unsigned long a_info; /* Use macros N_MAGIC, etc for access */ - unsigned a_text; /* length of text, in bytes */ - unsigned a_data; /* length of data, in bytes */ - unsigned a_bss; /* length of uninitialized data area for file, in bytes */ - unsigned a_syms; /* length of symbol table data in file, in bytes */ - unsigned a_entry; /* start address */ - unsigned a_trsize; /* length of relocation info for text, in bytes */ - unsigned a_drsize; /* length of relocation info for data, in bytes */ -}; - -#define N_TRSIZE(a) ((a).a_trsize) -#define N_DRSIZE(a) ((a).a_drsize) -#define N_SYMSIZE(a) ((a).a_syms) - -#endif /* _ASM_POWERPC_A_OUT_H */ diff --git a/arch/powerpc/include/asm/siginfo.h b/arch/powerpc/include/asm/siginfo.h index 12f1bce037b..49495b0534e 100644 --- a/arch/powerpc/include/asm/siginfo.h +++ b/arch/powerpc/include/asm/siginfo.h @@ -15,11 +15,6 @@ #include <asm-generic/siginfo.h> -/* - * SIGTRAP si_codes - */ -#define TRAP_BRANCH (__SI_FAULT|3) /* process taken branch trap */ -#define TRAP_HWBKPT (__SI_FAULT|4) /* hardware breakpoint or watchpoint */ #undef NSIGTRAP #define NSIGTRAP 4 diff --git a/arch/powerpc/include/asm/statfs.h b/arch/powerpc/include/asm/statfs.h index 67024026c10..5244834583a 100644 --- a/arch/powerpc/include/asm/statfs.h +++ b/arch/powerpc/include/asm/statfs.h @@ -1,60 +1,6 @@ #ifndef _ASM_POWERPC_STATFS_H #define _ASM_POWERPC_STATFS_H -/* For ppc32 we just use the generic definitions, not so simple on ppc64 */ - -#ifndef __powerpc64__ #include <asm-generic/statfs.h> -#else - -#ifndef __KERNEL_STRICT_NAMES -#include <linux/types.h> -typedef __kernel_fsid_t fsid_t; -#endif - -/* - * We're already 64-bit, so duplicate the definition - */ -struct statfs { - long f_type; - long f_bsize; - long f_blocks; - long f_bfree; - long f_bavail; - long f_files; - long f_ffree; - __kernel_fsid_t f_fsid; - long f_namelen; - long f_frsize; - long f_spare[5]; -}; - -struct statfs64 { - long f_type; - long f_bsize; - long f_blocks; - long f_bfree; - long f_bavail; - long f_files; - long f_ffree; - __kernel_fsid_t f_fsid; - long f_namelen; - long f_frsize; - long f_spare[5]; -}; -struct compat_statfs64 { - __u32 f_type; - __u32 f_bsize; - __u64 f_blocks; - __u64 f_bfree; - __u64 f_bavail; - __u64 f_files; - __u64 f_ffree; - __kernel_fsid_t f_fsid; - __u32 f_namelen; - __u32 f_frsize; - __u32 f_spare[5]; -}; -#endif /* ! __powerpc64__ */ #endif diff --git a/arch/powerpc/kernel/softemu8xx.c b/arch/powerpc/kernel/softemu8xx.c index c906c4bf683..23c8c5e7dc4 100644 --- a/arch/powerpc/kernel/softemu8xx.c +++ b/arch/powerpc/kernel/softemu8xx.c @@ -23,7 +23,6 @@ #include <linux/ptrace.h> #include <linux/slab.h> #include <linux/user.h> -#include <linux/a.out.h> #include <linux/interrupt.h> #include <asm/pgtable.h> diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c index 81ccb8dd1a5..f5def6cf5cd 100644 --- a/arch/powerpc/kernel/traps.c +++ b/arch/powerpc/kernel/traps.c @@ -23,7 +23,6 @@ #include <linux/ptrace.h> #include <linux/slab.h> #include <linux/user.h> -#include <linux/a.out.h> #include <linux/interrupt.h> #include <linux/init.h> #include <linux/module.h> diff --git a/arch/powerpc/platforms/chrp/setup.c b/arch/powerpc/platforms/chrp/setup.c index 1ba7ce5aafa..272d79a8d28 100644 --- a/arch/powerpc/platforms/chrp/setup.c +++ b/arch/powerpc/platforms/chrp/setup.c @@ -17,7 +17,6 @@ #include <linux/ptrace.h> #include <linux/slab.h> #include <linux/user.h> -#include <linux/a.out.h> #include <linux/tty.h> #include <linux/major.h> #include <linux/interrupt.h> diff --git a/arch/powerpc/platforms/maple/setup.c b/arch/powerpc/platforms/maple/setup.c index 364714757cf..d4c61c3c966 100644 --- a/arch/powerpc/platforms/maple/setup.c +++ b/arch/powerpc/platforms/maple/setup.c @@ -23,7 +23,6 @@ #include <linux/ptrace.h> #include <linux/slab.h> #include <linux/user.h> -#include <linux/a.out.h> #include <linux/tty.h> #include <linux/string.h> #include <linux/delay.h> diff --git a/arch/powerpc/platforms/powermac/setup.c b/arch/powerpc/platforms/powermac/setup.c index 88ccf3a08a9..82c14d203d8 100644 --- a/arch/powerpc/platforms/powermac/setup.c +++ b/arch/powerpc/platforms/powermac/setup.c @@ -33,7 +33,6 @@ #include <linux/ptrace.h> #include <linux/slab.h> #include <linux/user.h> -#include <linux/a.out.h> #include <linux/tty.h> #include <linux/string.h> #include <linux/delay.h> diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c index 7b01d67b4e4..ec341707e41 100644 --- a/arch/powerpc/platforms/pseries/setup.c +++ b/arch/powerpc/platforms/pseries/setup.c @@ -25,7 +25,6 @@ #include <linux/unistd.h> #include <linux/slab.h> #include <linux/user.h> -#include <linux/a.out.h> #include <linux/tty.h> #include <linux/major.h> #include <linux/interrupt.h> diff --git a/arch/s390/include/asm/statfs.h b/arch/s390/include/asm/statfs.h index 099a4557919..06cc70307ec 100644 --- a/arch/s390/include/asm/statfs.h +++ b/arch/s390/include/asm/statfs.h @@ -12,19 +12,16 @@ #ifndef __s390x__ #include <asm-generic/statfs.h> #else +/* + * We can't use <asm-generic/statfs.h> because in 64-bit mode + * we mix ints of different sizes in our struct statfs. + */ #ifndef __KERNEL_STRICT_NAMES - #include <linux/types.h> - typedef __kernel_fsid_t fsid_t; - #endif -/* - * This is ugly -- we're already 64-bit clean, so just duplicate the - * definitions. - */ struct statfs { int f_type; int f_bsize; diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig index a214002114e..97671dac12a 100644 --- a/arch/sparc/Kconfig +++ b/arch/sparc/Kconfig @@ -20,6 +20,11 @@ config GENERIC_ISA_DMA bool default y +config GENERIC_GPIO + bool + help + Generic GPIO API support + config ARCH_NO_VIRT_TO_BUS def_bool y @@ -69,6 +74,9 @@ config SPARC select HAVE_OPROFILE select HAVE_ARCH_KGDB if !SMP select HAVE_ARCH_TRACEHOOK + select ARCH_WANT_OPTIONAL_GPIOLIB + select RTC_CLASS + select RTC_DRV_M48T59 # Identify this as a Sparc32 build config SPARC32 @@ -204,17 +212,6 @@ config SUN_PM Enable power management and CPU standby features on supported SPARC platforms. -config SUN4 - bool "Support for SUN4 machines (disables SUN4[CDM] support)" - depends on !SMP - default n - help - Say Y here if, and only if, your machine is a sun4. Note that - a kernel compiled with this option will run only on sun4. - (And the current version will probably work only on sun4/330.) - -if !SUN4 - config PCI bool "Support for PCI and PS/2 keyboard/mouse" help @@ -227,11 +224,6 @@ config PCI_SYSCALL source "drivers/pci/Kconfig" -endif - -config NO_DMA - def_bool !PCI - config SUN_OPENPROMFS tristate "Openprom tree appears in /proc/openprom" help @@ -263,9 +255,7 @@ source "net/Kconfig" source "drivers/Kconfig" -if !SUN4 source "drivers/sbus/char/Kconfig" -endif # This one must be before the filesystem configs. -DaveM diff --git a/arch/sparc/include/asm/Kbuild b/arch/sparc/include/asm/Kbuild index a5f0ce734ff..2d2769d766e 100644 --- a/arch/sparc/include/asm/Kbuild +++ b/arch/sparc/include/asm/Kbuild @@ -15,14 +15,11 @@ header-y += signal_32.h header-y += signal_64.h header-y += stat_32.h header-y += stat_64.h -header-y += statfs_32.h -header-y += statfs_64.h header-y += unistd_32.h header-y += unistd_64.h header-y += apc.h header-y += asi.h -header-y += bpp.h header-y += display7seg.h header-y += envctrl.h header-y += fbio.h @@ -41,5 +38,4 @@ header-y += reg_64.h header-y += traps.h header-y += uctx.h header-y += utrap.h -header-y += vfc_ioctls.h header-y += watchdog.h diff --git a/arch/sparc/include/asm/asmmacro.h b/arch/sparc/include/asm/asmmacro.h index a619a4d97aa..a995bf8aba3 100644 --- a/arch/sparc/include/asm/asmmacro.h +++ b/arch/sparc/include/asm/asmmacro.h @@ -34,12 +34,7 @@ /* sun4 probably wants half word accesses to ASI_SEGMAP, while sun4c+ likes byte accesses. These are to avoid ifdef mania. */ -#ifdef CONFIG_SUN4 -#define lduXa lduha -#define stXa stha -#else #define lduXa lduba #define stXa stba -#endif #endif /* !(_SPARC_ASMMACRO_H) */ diff --git a/arch/sparc/include/asm/bpp.h b/arch/sparc/include/asm/bpp.h deleted file mode 100644 index 31f515e499a..00000000000 --- a/arch/sparc/include/asm/bpp.h +++ /dev/null @@ -1,73 +0,0 @@ -#ifndef _SPARC_BPP_H -#define _SPARC_BPP_H - -/* - * Copyright (c) 1995 Picture Elements - * Stephen Williams - * Gus Baldauf - * - * Linux/SPARC port by Peter Zaitcev. - * Integration into SPARC tree by Tom Dyas. - */ - -#include <linux/ioctl.h> - -/* - * This is a driver that supports IEEE Std 1284-1994 communications - * with compliant or compatible devices. It will use whatever features - * the device supports, prefering those that are typically faster. - * - * When the device is opened, it is left in COMPATIBILITY mode, and - * writes work like any printer device. The driver only attempt to - * negotiate 1284 modes when needed so that plugs can be pulled, - * switch boxes switched, etc., without disrupting things. It will - * also leave the device in compatibility mode when closed. - */ - - - -/* - * This driver also supplies ioctls to manually manipulate the - * pins. This is great for testing devices, or writing code to deal - * with bizzarro-mode of the ACME Special TurboThingy Plus. - * - * NOTE: These ioctl currently do not interact well with - * read/write. Caveat emptor. - * - * PUT_PINS allows us to assign the sense of all the pins, including - * the data pins if being driven by the host. The GET_PINS returns the - * pins that the peripheral drives, including data if appropriate. - */ - -# define BPP_PUT_PINS _IOW('B', 1, int) -# define BPP_GET_PINS _IOR('B', 2, char) /* that's bogus - should've been _IO */ -# define BPP_PUT_DATA _IOW('B', 3, int) -# define BPP_GET_DATA _IOR('B', 4, char) /* ditto */ - -/* - * Set the data bus to input mode. Disengage the data bin driver and - * be prepared to read values from the peripheral. If the arg is 0, - * then revert the bus to output mode. - */ -# define BPP_SET_INPUT _IOW('B', 5, int) - -/* - * These bits apply to the PUT operation... - */ -# define BPP_PP_nStrobe 0x0001 -# define BPP_PP_nAutoFd 0x0002 -# define BPP_PP_nInit 0x0004 -# define BPP_PP_nSelectIn 0x0008 - -/* - * These apply to the GET operation, which also reads the current value - * of the previously put values. A bit mask of these will be returned - * as a bit mask in the return code of the ioctl(). - */ -# define BPP_GP_nAck 0x0100 -# define BPP_GP_Busy 0x0200 -# define BPP_GP_PError 0x0400 -# define BPP_GP_Select 0x0800 -# define BPP_GP_nFault 0x1000 - -#endif diff --git a/arch/sparc/include/asm/bugs.h b/arch/sparc/include/asm/bugs.h index e179bc12f64..61d86bbbe2b 100644 --- a/arch/sparc/include/asm/bugs.h +++ b/arch/sparc/include/asm/bugs.h @@ -7,10 +7,6 @@ #include <asm/cpudata.h> #endif -#ifdef CONFIG_SPARC64 -#include <asm/sstate.h> -#endif - extern unsigned long loops_per_jiffy; static void __init check_bugs(void) @@ -18,7 +14,4 @@ static void __init check_bugs(void) #if defined(CONFIG_SPARC32) && !defined(CONFIG_SMP) cpu_data(0).udelay_val = loops_per_jiffy; #endif -#ifdef CONFIG_SPARC64 - sstate_running(); -#endif } diff --git a/arch/sparc/include/asm/cpudata_64.h b/arch/sparc/include/asm/cpudata_64.h index 532975ecfe1..7da7c13d23c 100644 --- a/arch/sparc/include/asm/cpudata_64.h +++ b/arch/sparc/include/asm/cpudata_64.h @@ -86,7 +86,6 @@ extern struct trap_per_cpu trap_block[NR_CPUS]; extern void init_cur_cpu_trap(struct thread_info *); extern void setup_tba(void); extern int ncpus_probed; -extern void __init cpu_probe(void); extern const struct seq_operations cpuinfo_op; extern unsigned long real_hard_smp_processor_id(void); diff --git a/arch/sparc/include/asm/dma-mapping_32.h b/arch/sparc/include/asm/dma-mapping_32.h index f3a641e6b2c..8a57ea0573e 100644 --- a/arch/sparc/include/asm/dma-mapping_32.h +++ b/arch/sparc/include/asm/dma-mapping_32.h @@ -1,11 +1,60 @@ #ifndef _ASM_SPARC_DMA_MAPPING_H #define _ASM_SPARC_DMA_MAPPING_H +#include <linux/types.h> -#ifdef CONFIG_PCI -#include <asm-generic/dma-mapping.h> -#else -#include <asm-generic/dma-mapping-broken.h> -#endif /* PCI */ +struct device; +struct scatterlist; +struct page; + +#define DMA_ERROR_CODE (~(dma_addr_t)0x0) + +extern int dma_supported(struct device *dev, u64 mask); +extern int dma_set_mask(struct device *dev, u64 dma_mask); +extern void *dma_alloc_coherent(struct device *dev, size_t size, + dma_addr_t *dma_handle, gfp_t flag); +extern void dma_free_coherent(struct device *dev, size_t size, + void *cpu_addr, dma_addr_t dma_handle); +extern dma_addr_t dma_map_single(struct device *dev, void *cpu_addr, + size_t size, + enum dma_data_direction direction); +extern void dma_unmap_single(struct device *dev, dma_addr_t dma_addr, + size_t size, + enum dma_data_direction direction); +extern dma_addr_t dma_map_page(struct device *dev, struct page *page, + unsigned long offset, size_t size, + enum dma_data_direction direction); +extern void dma_unmap_page(struct device *dev, dma_addr_t dma_address, + size_t size, enum dma_data_direction direction); +extern int dma_map_sg(struct device *dev, struct scatterlist *sg, + int nents, enum dma_data_direction direction); +extern void dma_unmap_sg(struct device *dev, struct scatterlist *sg, + int nents, enum dma_data_direction direction); +extern void dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle, + size_t size, + enum dma_data_direction direction); +extern void dma_sync_single_for_device(struct device *dev, + dma_addr_t dma_handle, + size_t size, + enum dma_data_direction direction); +extern void dma_sync_single_range_for_cpu(struct device *dev, + dma_addr_t dma_handle, + unsigned long offset, + size_t size, + enum dma_data_direction direction); +extern void dma_sync_single_range_for_device(struct device *dev, + dma_addr_t dma_handle, + unsigned long offset, size_t size, + enum dma_data_direction direction); +extern void dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, + int nelems, enum dma_data_direction direction); +extern void dma_sync_sg_for_device(struct device *dev, + struct scatterlist *sg, int nelems, + enum dma_data_direction direction); +extern int dma_mapping_error(struct device *dev, dma_addr_t dma_addr); +extern int dma_get_cache_alignment(void); + +#define dma_alloc_noncoherent dma_alloc_coherent +#define dma_free_noncoherent dma_free_coherent #endif /* _ASM_SPARC_DMA_MAPPING_H */ diff --git a/arch/sparc/include/asm/dma.h b/arch/sparc/include/asm/dma.h index aa1d90ac04c..b554927bbaf 100644 --- a/arch/sparc/include/asm/dma.h +++ b/arch/sparc/include/asm/dma.h @@ -1,8 +1,139 @@ -#ifndef ___ASM_SPARC_DMA_H -#define ___ASM_SPARC_DMA_H -#if defined(__sparc__) && defined(__arch64__) -#include <asm/dma_64.h> +#ifndef _ASM_SPARC_DMA_H +#define _ASM_SPARC_DMA_H + +/* These are irrelevant for Sparc DMA, but we leave it in so that + * things can compile. + */ +#define MAX_DMA_CHANNELS 8 +#define DMA_MODE_READ 1 +#define DMA_MODE_WRITE 2 +#define MAX_DMA_ADDRESS (~0UL) + +/* Useful constants */ +#define SIZE_16MB (16*1024*1024) +#define SIZE_64K (64*1024) + +/* SBUS DMA controller reg offsets */ +#define DMA_CSR 0x00UL /* rw DMA control/status register 0x00 */ +#define DMA_ADDR 0x04UL /* rw DMA transfer address register 0x04 */ +#define DMA_COUNT 0x08UL /* rw DMA transfer count register 0x08 */ +#define DMA_TEST 0x0cUL /* rw DMA test/debug register 0x0c */ + +/* Fields in the cond_reg register */ +/* First, the version identification bits */ +#define DMA_DEVICE_ID 0xf0000000 /* Device identification bits */ +#define DMA_VERS0 0x00000000 /* Sunray DMA version */ +#define DMA_ESCV1 0x40000000 /* DMA ESC Version 1 */ +#define DMA_VERS1 0x80000000 /* DMA rev 1 */ +#define DMA_VERS2 0xa0000000 /* DMA rev 2 */ +#define DMA_VERHME 0xb0000000 /* DMA hme gate array */ +#define DMA_VERSPLUS 0x90000000 /* DMA rev 1 PLUS */ + +#define DMA_HNDL_INTR 0x00000001 /* An IRQ needs to be handled */ +#define DMA_HNDL_ERROR 0x00000002 /* We need to take an error */ +#define DMA_FIFO_ISDRAIN 0x0000000c /* The DMA FIFO is draining */ +#define DMA_INT_ENAB 0x00000010 /* Turn on interrupts */ +#define DMA_FIFO_INV 0x00000020 /* Invalidate the FIFO */ +#define DMA_ACC_SZ_ERR 0x00000040 /* The access size was bad */ +#define DMA_FIFO_STDRAIN 0x00000040 /* DMA_VERS1 Drain the FIFO */ +#define DMA_RST_SCSI 0x00000080 /* Reset the SCSI controller */ +#define DMA_RST_ENET DMA_RST_SCSI /* Reset the ENET controller */ +#define DMA_ST_WRITE 0x00000100 /* write from device to memory */ +#define DMA_ENABLE 0x00000200 /* Fire up DMA, handle requests */ +#define DMA_PEND_READ 0x00000400 /* DMA_VERS1/0/PLUS Pending Read */ +#define DMA_ESC_BURST 0x00000800 /* 1=16byte 0=32byte */ +#define DMA_READ_AHEAD 0x00001800 /* DMA read ahead partial longword */ +#define DMA_DSBL_RD_DRN 0x00001000 /* No EC drain on slave reads */ +#define DMA_BCNT_ENAB 0x00002000 /* If on, use the byte counter */ +#define DMA_TERM_CNTR 0x00004000 /* Terminal counter */ +#define DMA_SCSI_SBUS64 0x00008000 /* HME: Enable 64-bit SBUS mode. */ +#define DMA_CSR_DISAB 0x00010000 /* No FIFO drains during csr */ +#define DMA_SCSI_DISAB 0x00020000 /* No FIFO drains during reg */ +#define DMA_DSBL_WR_INV 0x00020000 /* No EC inval. on slave writes */ +#define DMA_ADD_ENABLE 0x00040000 /* Special ESC DVMA optimization */ +#define DMA_E_BURSTS 0x000c0000 /* ENET: SBUS r/w burst mask */ +#define DMA_E_BURST32 0x00040000 /* ENET: SBUS 32 byte r/w burst */ +#define DMA_E_BURST16 0x00000000 /* ENET: SBUS 16 byte r/w burst */ +#define DMA_BRST_SZ 0x000c0000 /* SCSI: SBUS r/w burst size */ +#define DMA_BRST64 0x000c0000 /* SCSI: 64byte bursts (HME on UltraSparc only) */ +#define DMA_BRST32 0x00040000 /* SCSI: 32byte bursts */ +#define DMA_BRST16 0x00000000 /* SCSI: 16byte bursts */ +#define DMA_BRST0 0x00080000 /* SCSI: no bursts (non-HME gate arrays) */ +#define DMA_ADDR_DISAB 0x00100000 /* No FIFO drains during addr */ +#define DMA_2CLKS 0x00200000 /* Each transfer = 2 clock ticks */ +#define DMA_3CLKS 0x00400000 /* Each transfer = 3 clock ticks */ +#define DMA_EN_ENETAUI DMA_3CLKS /* Put lance into AUI-cable mode */ +#define DMA_CNTR_DISAB 0x00800000 /* No IRQ when DMA_TERM_CNTR set */ +#define DMA_AUTO_NADDR 0x01000000 /* Use "auto nxt addr" feature */ +#define DMA_SCSI_ON 0x02000000 /* Enable SCSI dma */ +#define DMA_PARITY_OFF 0x02000000 /* HME: disable parity checking */ +#define DMA_LOADED_ADDR 0x04000000 /* Address has been loaded */ +#define DMA_LOADED_NADDR 0x08000000 /* Next address has been loaded */ +#define DMA_RESET_FAS366 0x08000000 /* HME: Assert RESET to FAS366 */ + +/* Values describing the burst-size property from the PROM */ +#define DMA_BURST1 0x01 +#define DMA_BURST2 0x02 +#define DMA_BURST4 0x04 +#define DMA_BURST8 0x08 +#define DMA_BURST16 0x10 +#define DMA_BURST32 0x20 +#define DMA_BURST64 0x40 +#define DMA_BURSTBITS 0x7f + +/* From PCI */ + +#ifdef CONFIG_PCI +extern int isa_dma_bridge_buggy; #else -#include <asm/dma_32.h> +#define isa_dma_bridge_buggy (0) #endif + +#ifdef CONFIG_SPARC32 + +/* Routines for data transfer buffers. */ +BTFIXUPDEF_CALL(char *, mmu_lockarea, char *, unsigned long) +BTFIXUPDEF_CALL(void, mmu_unlockarea, char *, unsigned long) + +#define mmu_lockarea(vaddr,len) BTFIXUP_CALL(mmu_lockarea)(vaddr,len) +#define mmu_unlockarea(vaddr,len) BTFIXUP_CALL(mmu_unlockarea)(vaddr,len) + +struct page; +struct device; +struct scatterlist; + +/* These are implementations for sbus_map_sg/sbus_unmap_sg... collapse later */ +BTFIXUPDEF_CALL(__u32, mmu_get_scsi_one, struct device *, char *, unsigned long) +BTFIXUPDEF_CALL(void, mmu_get_scsi_sgl, struct device *, struct scatterlist *, int) +BTFIXUPDEF_CALL(void, mmu_release_scsi_one, struct device *, __u32, unsigned long) +BTFIXUPDEF_CALL(void, mmu_release_scsi_sgl, struct device *, struct scatterlist *, int) + +#define mmu_get_scsi_one(dev,vaddr,len) BTFIXUP_CALL(mmu_get_scsi_one)(dev,vaddr,len) +#define mmu_get_scsi_sgl(dev,sg,sz) BTFIXUP_CALL(mmu_get_scsi_sgl)(dev,sg,sz) +#define mmu_release_scsi_one(dev,vaddr,len) BTFIXUP_CALL(mmu_release_scsi_one)(dev,vaddr,len) +#define mmu_release_scsi_sgl(dev,sg,sz) BTFIXUP_CALL(mmu_release_scsi_sgl)(dev,sg,sz) + +/* + * mmu_map/unmap are provided by iommu/iounit; Invalid to call on IIep. + * + * The mmu_map_dma_area establishes two mappings in one go. + * These mappings point to pages normally mapped at 'va' (linear address). + * First mapping is for CPU visible address at 'a', uncached. + * This is an alias, but it works because it is an uncached mapping. + * Second mapping is for device visible address, or "bus" address. + * The bus address is returned at '*pba'. + * + * These functions seem distinct, but are hard to split. On sun4c, + * at least for now, 'a' is equal to bus address, and retured in *pba. + * On sun4m, page attributes depend on the CPU type, so we have to + * know if we are mapping RAM or I/O, so it has to be an additional argument + * to a separate mapping function for CPU visible mappings. + */ +BTFIXUPDEF_CALL(int, mmu_map_dma_area, struct device *, dma_addr_t *, unsigned long, unsigned long, int len) +BTFIXUPDEF_CALL(void, mmu_unmap_dma_area, struct device *, unsigned long busa, int len) + +#define mmu_map_dma_area(dev,pba,va,a,len) BTFIXUP_CALL(mmu_map_dma_area)(dev,pba,va,a,len) +#define mmu_unmap_dma_area(dev,ba,len) BTFIXUP_CALL(mmu_unmap_dma_area)(dev,ba,len) #endif + +#endif /* !(_ASM_SPARC_DMA_H) */ diff --git a/arch/sparc/include/asm/dma_32.h b/arch/sparc/include/asm/dma_32.h deleted file mode 100644 index cf7189c0079..00000000000 --- a/arch/sparc/include/asm/dma_32.h +++ /dev/null @@ -1,288 +0,0 @@ -/* include/asm/dma.h - * - * Copyright 1995 (C) David S. Miller (davem@davemloft.net) - */ - -#ifndef _ASM_SPARC_DMA_H -#define _ASM_SPARC_DMA_H - -#include <linux/kernel.h> -#include <linux/types.h> - -#include <asm/vac-ops.h> /* for invalidate's, etc. */ -#include <asm/sbus.h> -#include <asm/delay.h> -#include <asm/oplib.h> -#include <asm/system.h> -#include <asm/io.h> -#include <linux/spinlock.h> - -struct page; -extern spinlock_t dma_spin_lock; - -static inline unsigned long claim_dma_lock(void) -{ - unsigned long flags; - spin_lock_irqsave(&dma_spin_lock, flags); - return flags; -} - -static inline void release_dma_lock(unsigned long flags) -{ - spin_unlock_irqrestore(&dma_spin_lock, flags); -} - -/* These are irrelevant for Sparc DMA, but we leave it in so that - * things can compile. - */ -#define MAX_DMA_CHANNELS 8 -#define MAX_DMA_ADDRESS (~0UL) -#define DMA_MODE_READ 1 -#define DMA_MODE_WRITE 2 - -/* Useful constants */ -#define SIZE_16MB (16*1024*1024) -#define SIZE_64K (64*1024) - -/* SBUS DMA controller reg offsets */ -#define DMA_CSR 0x00UL /* rw DMA control/status register 0x00 */ -#define DMA_ADDR 0x04UL /* rw DMA transfer address register 0x04 */ -#define DMA_COUNT 0x08UL /* rw DMA transfer count register 0x08 */ -#define DMA_TEST 0x0cUL /* rw DMA test/debug register 0x0c */ - -/* DVMA chip revisions */ -enum dvma_rev { - dvmarev0, - dvmaesc1, - dvmarev1, - dvmarev2, - dvmarev3, - dvmarevplus, - dvmahme -}; - -#define DMA_HASCOUNT(rev) ((rev)==dvmaesc1) - -/* Linux DMA information structure, filled during probe. */ -struct sbus_dma { - struct sbus_dma *next; - struct sbus_dev *sdev; - void __iomem *regs; - - /* Status, misc info */ - int node; /* Prom node for this DMA device */ - int running; /* Are we doing DMA now? */ - int allocated; /* Are we "owned" by anyone yet? */ - - /* Transfer information. */ - unsigned long addr; /* Start address of current transfer */ - int nbytes; /* Size of current transfer */ - int realbytes; /* For splitting up large transfers, etc. */ - - /* DMA revision */ - enum dvma_rev revision; -}; - -extern struct sbus_dma *dma_chain; - -/* Broken hardware... */ -#ifdef CONFIG_SUN4 -/* Have to sort this out. Does rev0 work fine on sun4[cmd] without isbroken? - * Or is rev0 present only on sun4 boxes? -jj */ -#define DMA_ISBROKEN(dma) ((dma)->revision == dvmarev0 || (dma)->revision == dvmarev1) -#else -#define DMA_ISBROKEN(dma) ((dma)->revision == dvmarev1) -#endif -#define DMA_ISESC1(dma) ((dma)->revision == dvmaesc1) - -/* Main routines in dma.c */ -extern void dvma_init(struct sbus_bus *); - -/* Fields in the cond_reg register */ -/* First, the version identification bits */ -#define DMA_DEVICE_ID 0xf0000000 /* Device identification bits */ -#define DMA_VERS0 0x00000000 /* Sunray DMA version */ -#define DMA_ESCV1 0x40000000 /* DMA ESC Version 1 */ -#define DMA_VERS1 0x80000000 /* DMA rev 1 */ -#define DMA_VERS2 0xa0000000 /* DMA rev 2 */ -#define DMA_VERHME 0xb0000000 /* DMA hme gate array */ -#define DMA_VERSPLUS 0x90000000 /* DMA rev 1 PLUS */ - -#define DMA_HNDL_INTR 0x00000001 /* An IRQ needs to be handled */ -#define DMA_HNDL_ERROR 0x00000002 /* We need to take an error */ -#define DMA_FIFO_ISDRAIN 0x0000000c /* The DMA FIFO is draining */ -#define DMA_INT_ENAB 0x00000010 /* Turn on interrupts */ -#define DMA_FIFO_INV 0x00000020 /* Invalidate the FIFO */ -#define DMA_ACC_SZ_ERR 0x00000040 /* The access size was bad */ -#define DMA_FIFO_STDRAIN 0x00000040 /* DMA_VERS1 Drain the FIFO */ -#define DMA_RST_SCSI 0x00000080 /* Reset the SCSI controller */ -#define DMA_RST_ENET DMA_RST_SCSI /* Reset the ENET controller */ -#define DMA_RST_BPP DMA_RST_SCSI /* Reset the BPP controller */ -#define DMA_ST_WRITE 0x00000100 /* write from device to memory */ -#define DMA_ENABLE 0x00000200 /* Fire up DMA, handle requests */ -#define DMA_PEND_READ 0x00000400 /* DMA_VERS1/0/PLUS Pending Read */ -#define DMA_ESC_BURST 0x00000800 /* 1=16byte 0=32byte */ -#define DMA_READ_AHEAD 0x00001800 /* DMA read ahead partial longword */ -#define DMA_DSBL_RD_DRN 0x00001000 /* No EC drain on slave reads */ -#define DMA_BCNT_ENAB 0x00002000 /* If on, use the byte counter */ -#define DMA_TERM_CNTR 0x00004000 /* Terminal counter */ -#define DMA_SCSI_SBUS64 0x00008000 /* HME: Enable 64-bit SBUS mode. */ -#define DMA_CSR_DISAB 0x00010000 /* No FIFO drains during csr */ -#define DMA_SCSI_DISAB 0x00020000 /* No FIFO drains during reg */ -#define DMA_DSBL_WR_INV 0x00020000 /* No EC inval. on slave writes */ -#define DMA_ADD_ENABLE 0x00040000 /* Special ESC DVMA optimization */ -#define DMA_E_BURSTS 0x000c0000 /* ENET: SBUS r/w burst mask */ -#define DMA_E_BURST32 0x00040000 /* ENET: SBUS 32 byte r/w burst */ -#define DMA_E_BURST16 0x00000000 /* ENET: SBUS 16 byte r/w burst */ -#define DMA_BRST_SZ 0x000c0000 /* SCSI: SBUS r/w burst size */ -#define DMA_BRST64 0x00080000 /* SCSI: 64byte bursts (HME on UltraSparc only) */ -#define DMA_BRST32 0x00040000 /* SCSI/BPP: 32byte bursts */ -#define DMA_BRST16 0x00000000 /* SCSI/BPP: 16byte bursts */ -#define DMA_BRST0 0x00080000 /* SCSI: no bursts (non-HME gate arrays) */ -#define DMA_ADDR_DISAB 0x00100000 /* No FIFO drains during addr */ -#define DMA_2CLKS 0x00200000 /* Each transfer = 2 clock ticks */ -#define DMA_3CLKS 0x00400000 /* Each transfer = 3 clock ticks */ -#define DMA_EN_ENETAUI DMA_3CLKS /* Put lance into AUI-cable mode */ -#define DMA_CNTR_DISAB 0x00800000 /* No IRQ when DMA_TERM_CNTR set */ -#define DMA_AUTO_NADDR 0x01000000 /* Use "auto nxt addr" feature */ -#define DMA_SCSI_ON 0x02000000 /* Enable SCSI dma */ -#define DMA_BPP_ON DMA_SCSI_ON /* Enable BPP dma */ -#define DMA_PARITY_OFF 0x02000000 /* HME: disable parity checking */ -#define DMA_LOADED_ADDR 0x04000000 /* Address has been loaded */ -#define DMA_LOADED_NADDR 0x08000000 /* Next address has been loaded */ -#define DMA_RESET_FAS366 0x08000000 /* HME: Assert RESET to FAS366 */ - -/* Values describing the burst-size property from the PROM */ -#define DMA_BURST1 0x01 -#define DMA_BURST2 0x02 -#define DMA_BURST4 0x04 -#define DMA_BURST8 0x08 -#define DMA_BURST16 0x10 -#define DMA_BURST32 0x20 -#define DMA_BURST64 0x40 -#define DMA_BURSTBITS 0x7f - -/* Determine highest possible final transfer address given a base */ -#define DMA_MAXEND(addr) (0x01000000UL-(((unsigned long)(addr))&0x00ffffffUL)) - -/* Yes, I hack a lot of elisp in my spare time... */ -#define DMA_ERROR_P(regs) ((((regs)->cond_reg) & DMA_HNDL_ERROR)) -#define DMA_IRQ_P(regs) ((((regs)->cond_reg) & (DMA_HNDL_INTR | DMA_HNDL_ERROR))) -#define DMA_WRITE_P(regs) ((((regs)->cond_reg) & DMA_ST_WRITE)) -#define DMA_OFF(regs) ((((regs)->cond_reg) &= (~DMA_ENABLE))) -#define DMA_INTSOFF(regs) ((((regs)->cond_reg) &= (~DMA_INT_ENAB))) -#define DMA_INTSON(regs) ((((regs)->cond_reg) |= (DMA_INT_ENAB))) -#define DMA_PUNTFIFO(regs) ((((regs)->cond_reg) |= DMA_FIFO_INV)) -#define DMA_SETSTART(regs, addr) ((((regs)->st_addr) = (char *) addr)) -#define DMA_BEGINDMA_W(regs) \ - ((((regs)->cond_reg |= (DMA_ST_WRITE|DMA_ENABLE|DMA_INT_ENAB)))) -#define DMA_BEGINDMA_R(regs) \ - ((((regs)->cond_reg |= ((DMA_ENABLE|DMA_INT_ENAB)&(~DMA_ST_WRITE))))) - -/* For certain DMA chips, we need to disable ints upon irq entry - * and turn them back on when we are done. So in any ESP interrupt - * handler you *must* call DMA_IRQ_ENTRY upon entry and DMA_IRQ_EXIT - * when leaving the handler. You have been warned... - */ -#define DMA_IRQ_ENTRY(dma, dregs) do { \ - if(DMA_ISBROKEN(dma)) DMA_INTSOFF(dregs); \ - } while (0) - -#define DMA_IRQ_EXIT(dma, dregs) do { \ - if(DMA_ISBROKEN(dma)) DMA_INTSON(dregs); \ - } while(0) - -#if 0 /* P3 this stuff is inline in ledma.c:init_restart_ledma() */ -/* Pause until counter runs out or BIT isn't set in the DMA condition - * register. - */ -static inline void sparc_dma_pause(struct sparc_dma_registers *regs, - unsigned long bit) -{ - int ctr = 50000; /* Let's find some bugs ;) */ - - /* Busy wait until the bit is not set any more */ - while((regs->cond_reg&bit) && (ctr>0)) { - ctr--; - __delay(5); - } - - /* Check for bogus outcome. */ - if(!ctr) - panic("DMA timeout"); -} - -/* Reset the friggin' thing... */ -#define DMA_RESET(dma) do { \ - struct sparc_dma_registers *regs = dma->regs; \ - /* Let the current FIFO drain itself */ \ - sparc_dma_pause(regs, (DMA_FIFO_ISDRAIN)); \ - /* Reset the logic */ \ - regs->cond_reg |= (DMA_RST_SCSI); /* assert */ \ - __delay(400); /* let the bits set ;) */ \ - regs->cond_reg &= ~(DMA_RST_SCSI); /* de-assert */ \ - sparc_dma_enable_interrupts(regs); /* Re-enable interrupts */ \ - /* Enable FAST transfers if available */ \ - if(dma->revision>dvmarev1) regs->cond_reg |= DMA_3CLKS; \ - dma->running = 0; \ -} while(0) -#endif - -#define for_each_dvma(dma) \ - for((dma) = dma_chain; (dma); (dma) = (dma)->next) - -extern int get_dma_list(char *); -extern int request_dma(unsigned int, __const__ char *); -extern void free_dma(unsigned int); - -/* From PCI */ - -#ifdef CONFIG_PCI -extern int isa_dma_bridge_buggy; -#else -#define isa_dma_bridge_buggy (0) -#endif - -/* Routines for data transfer buffers. */ -BTFIXUPDEF_CALL(char *, mmu_lockarea, char *, unsigned long) -BTFIXUPDEF_CALL(void, mmu_unlockarea, char *, unsigned long) - -#define mmu_lockarea(vaddr,len) BTFIXUP_CALL(mmu_lockarea)(vaddr,len) -#define mmu_unlockarea(vaddr,len) BTFIXUP_CALL(mmu_unlockarea)(vaddr,len) - -/* These are implementations for sbus_map_sg/sbus_unmap_sg... collapse later */ -BTFIXUPDEF_CALL(__u32, mmu_get_scsi_one, char *, unsigned long, struct sbus_bus *sbus) -BTFIXUPDEF_CALL(void, mmu_get_scsi_sgl, struct scatterlist *, int, struct sbus_bus *sbus) -BTFIXUPDEF_CALL(void, mmu_release_scsi_one, __u32, unsigned long, struct sbus_bus *sbus) -BTFIXUPDEF_CALL(void, mmu_release_scsi_sgl, struct scatterlist *, int, struct sbus_bus *sbus) - -#define mmu_get_scsi_one(vaddr,len,sbus) BTFIXUP_CALL(mmu_get_scsi_one)(vaddr,len,sbus) -#define mmu_get_scsi_sgl(sg,sz,sbus) BTFIXUP_CALL(mmu_get_scsi_sgl)(sg,sz,sbus) -#define mmu_release_scsi_one(vaddr,len,sbus) BTFIXUP_CALL(mmu_release_scsi_one)(vaddr,len,sbus) -#define mmu_release_scsi_sgl(sg,sz,sbus) BTFIXUP_CALL(mmu_release_scsi_sgl)(sg,sz,sbus) - -/* - * mmu_map/unmap are provided by iommu/iounit; Invalid to call on IIep. - * - * The mmu_map_dma_area establishes two mappings in one go. - * These mappings point to pages normally mapped at 'va' (linear address). - * First mapping is for CPU visible address at 'a', uncached. - * This is an alias, but it works because it is an uncached mapping. - * Second mapping is for device visible address, or "bus" address. - * The bus address is returned at '*pba'. - * - * These functions seem distinct, but are hard to split. On sun4c, - * at least for now, 'a' is equal to bus address, and retured in *pba. - * On sun4m, page attributes depend on the CPU type, so we have to - * know if we are mapping RAM or I/O, so it has to be an additional argument - * to a separate mapping function for CPU visible mappings. - */ -BTFIXUPDEF_CALL(int, mmu_map_dma_area, dma_addr_t *, unsigned long, unsigned long, int len) -BTFIXUPDEF_CALL(struct page *, mmu_translate_dvma, unsigned long busa) -BTFIXUPDEF_CALL(void, mmu_unmap_dma_area, unsigned long busa, int len) - -#define mmu_map_dma_area(pba,va,a,len) BTFIXUP_CALL(mmu_map_dma_area)(pba,va,a,len) -#define mmu_unmap_dma_area(ba,len) BTFIXUP_CALL(mmu_unmap_dma_area)(ba,len) -#define mmu_translate_dvma(ba) BTFIXUP_CALL(mmu_translate_dvma)(ba) - -#endif /* !(_ASM_SPARC_DMA_H) */ diff --git a/arch/sparc/include/asm/dma_64.h b/arch/sparc/include/asm/dma_64.h deleted file mode 100644 index 46a8aecffc0..00000000000 --- a/arch/sparc/include/asm/dma_64.h +++ /dev/null @@ -1,205 +0,0 @@ -/* - * include/asm/dma.h - * - * Copyright 1996 (C) David S. Miller (davem@caip.rutgers.edu) - */ - -#ifndef _ASM_SPARC64_DMA_H -#define _ASM_SPARC64_DMA_H - -#include <linux/kernel.h> -#include <linux/types.h> -#include <linux/spinlock.h> - -#include <asm/sbus.h> -#include <asm/delay.h> -#include <asm/oplib.h> - -/* These are irrelevant for Sparc DMA, but we leave it in so that - * things can compile. - */ -#define MAX_DMA_CHANNELS 8 -#define DMA_MODE_READ 1 -#define DMA_MODE_WRITE 2 -#define MAX_DMA_ADDRESS (~0UL) - -/* Useful constants */ -#define SIZE_16MB (16*1024*1024) -#define SIZE_64K (64*1024) - -/* SBUS DMA controller reg offsets */ -#define DMA_CSR 0x00UL /* rw DMA control/status register 0x00 */ -#define DMA_ADDR 0x04UL /* rw DMA transfer address register 0x04 */ -#define DMA_COUNT 0x08UL /* rw DMA transfer count register 0x08 */ -#define DMA_TEST 0x0cUL /* rw DMA test/debug register 0x0c */ - -/* DVMA chip revisions */ -enum dvma_rev { - dvmarev0, - dvmaesc1, - dvmarev1, - dvmarev2, - dvmarev3, - dvmarevplus, - dvmahme -}; - -#define DMA_HASCOUNT(rev) ((rev)==dvmaesc1) - -/* Linux DMA information structure, filled during probe. */ -struct sbus_dma { - struct sbus_dma *next; - struct sbus_dev *sdev; - void __iomem *regs; - - /* Status, misc info */ - int node; /* Prom node for this DMA device */ - int running; /* Are we doing DMA now? */ - int allocated; /* Are we "owned" by anyone yet? */ - - /* Transfer information. */ - u32 addr; /* Start address of current transfer */ - int nbytes; /* Size of current transfer */ - int realbytes; /* For splitting up large transfers, etc. */ - - /* DMA revision */ - enum dvma_rev revision; -}; - -extern struct sbus_dma *dma_chain; - -/* Broken hardware... */ -#define DMA_ISBROKEN(dma) ((dma)->revision == dvmarev1) -#define DMA_ISESC1(dma) ((dma)->revision == dvmaesc1) - -/* Main routines in dma.c */ -extern void dvma_init(struct sbus_bus *); - -/* Fields in the cond_reg register */ -/* First, the version identification bits */ -#define DMA_DEVICE_ID 0xf0000000 /* Device identification bits */ -#define DMA_VERS0 0x00000000 /* Sunray DMA version */ -#define DMA_ESCV1 0x40000000 /* DMA ESC Version 1 */ -#define DMA_VERS1 0x80000000 /* DMA rev 1 */ -#define DMA_VERS2 0xa0000000 /* DMA rev 2 */ -#define DMA_VERHME 0xb0000000 /* DMA hme gate array */ -#define DMA_VERSPLUS 0x90000000 /* DMA rev 1 PLUS */ - -#define DMA_HNDL_INTR 0x00000001 /* An IRQ needs to be handled */ -#define DMA_HNDL_ERROR 0x00000002 /* We need to take an error */ -#define DMA_FIFO_ISDRAIN 0x0000000c /* The DMA FIFO is draining */ -#define DMA_INT_ENAB 0x00000010 /* Turn on interrupts */ -#define DMA_FIFO_INV 0x00000020 /* Invalidate the FIFO */ -#define DMA_ACC_SZ_ERR 0x00000040 /* The access size was bad */ -#define DMA_FIFO_STDRAIN 0x00000040 /* DMA_VERS1 Drain the FIFO */ -#define DMA_RST_SCSI 0x00000080 /* Reset the SCSI controller */ -#define DMA_RST_ENET DMA_RST_SCSI /* Reset the ENET controller */ -#define DMA_ST_WRITE 0x00000100 /* write from device to memory */ -#define DMA_ENABLE 0x00000200 /* Fire up DMA, handle requests */ -#define DMA_PEND_READ 0x00000400 /* DMA_VERS1/0/PLUS Pending Read */ -#define DMA_ESC_BURST 0x00000800 /* 1=16byte 0=32byte */ -#define DMA_READ_AHEAD 0x00001800 /* DMA read ahead partial longword */ -#define DMA_DSBL_RD_DRN 0x00001000 /* No EC drain on slave reads */ -#define DMA_BCNT_ENAB 0x00002000 /* If on, use the byte counter */ -#define DMA_TERM_CNTR 0x00004000 /* Terminal counter */ -#define DMA_SCSI_SBUS64 0x00008000 /* HME: Enable 64-bit SBUS mode. */ -#define DMA_CSR_DISAB 0x00010000 /* No FIFO drains during csr */ -#define DMA_SCSI_DISAB 0x00020000 /* No FIFO drains during reg */ -#define DMA_DSBL_WR_INV 0x00020000 /* No EC inval. on slave writes */ -#define DMA_ADD_ENABLE 0x00040000 /* Special ESC DVMA optimization */ -#define DMA_E_BURSTS 0x000c0000 /* ENET: SBUS r/w burst mask */ -#define DMA_E_BURST32 0x00040000 /* ENET: SBUS 32 byte r/w burst */ -#define DMA_E_BURST16 0x00000000 /* ENET: SBUS 16 byte r/w burst */ -#define DMA_BRST_SZ 0x000c0000 /* SCSI: SBUS r/w burst size */ -#define DMA_BRST64 0x000c0000 /* SCSI: 64byte bursts (HME on UltraSparc only) */ -#define DMA_BRST32 0x00040000 /* SCSI: 32byte bursts */ -#define DMA_BRST16 0x00000000 /* SCSI: 16byte bursts */ -#define DMA_BRST0 0x00080000 /* SCSI: no bursts (non-HME gate arrays) */ -#define DMA_ADDR_DISAB 0x00100000 /* No FIFO drains during addr */ -#define DMA_2CLKS 0x00200000 /* Each transfer = 2 clock ticks */ -#define DMA_3CLKS 0x00400000 /* Each transfer = 3 clock ticks */ -#define DMA_EN_ENETAUI DMA_3CLKS /* Put lance into AUI-cable mode */ -#define DMA_CNTR_DISAB 0x00800000 /* No IRQ when DMA_TERM_CNTR set */ -#define DMA_AUTO_NADDR 0x01000000 /* Use "auto nxt addr" feature */ -#define DMA_SCSI_ON 0x02000000 /* Enable SCSI dma */ -#define DMA_PARITY_OFF 0x02000000 /* HME: disable parity checking */ -#define DMA_LOADED_ADDR 0x04000000 /* Address has been loaded */ -#define DMA_LOADED_NADDR 0x08000000 /* Next address has been loaded */ -#define DMA_RESET_FAS366 0x08000000 /* HME: Assert RESET to FAS366 */ - -/* Values describing the burst-size property from the PROM */ -#define DMA_BURST1 0x01 -#define DMA_BURST2 0x02 -#define DMA_BURST4 0x04 -#define DMA_BURST8 0x08 -#define DMA_BURST16 0x10 -#define DMA_BURST32 0x20 -#define DMA_BURST64 0x40 -#define DMA_BURSTBITS 0x7f - -/* Determine highest possible final transfer address given a base */ -#define DMA_MAXEND(addr) (0x01000000UL-(((unsigned long)(addr))&0x00ffffffUL)) - -/* Yes, I hack a lot of elisp in my spare time... */ -#define DMA_ERROR_P(regs) ((sbus_readl((regs) + DMA_CSR) & DMA_HNDL_ERROR)) -#define DMA_IRQ_P(regs) ((sbus_readl((regs) + DMA_CSR)) & (DMA_HNDL_INTR | DMA_HNDL_ERROR)) -#define DMA_WRITE_P(regs) ((sbus_readl((regs) + DMA_CSR) & DMA_ST_WRITE)) -#define DMA_OFF(__regs) \ -do { u32 tmp = sbus_readl((__regs) + DMA_CSR); \ - tmp &= ~DMA_ENABLE; \ - sbus_writel(tmp, (__regs) + DMA_CSR); \ -} while(0) -#define DMA_INTSOFF(__regs) \ -do { u32 tmp = sbus_readl((__regs) + DMA_CSR); \ - tmp &= ~DMA_INT_ENAB; \ - sbus_writel(tmp, (__regs) + DMA_CSR); \ -} while(0) -#define DMA_INTSON(__regs) \ -do { u32 tmp = sbus_readl((__regs) + DMA_CSR); \ - tmp |= DMA_INT_ENAB; \ - sbus_writel(tmp, (__regs) + DMA_CSR); \ -} while(0) -#define DMA_PUNTFIFO(__regs) \ -do { u32 tmp = sbus_readl((__regs) + DMA_CSR); \ - tmp |= DMA_FIFO_INV; \ - sbus_writel(tmp, (__regs) + DMA_CSR); \ -} while(0) -#define DMA_SETSTART(__regs, __addr) \ - sbus_writel((u32)(__addr), (__regs) + DMA_ADDR); -#define DMA_BEGINDMA_W(__regs) \ -do { u32 tmp = sbus_readl((__regs) + DMA_CSR); \ - tmp |= (DMA_ST_WRITE|DMA_ENABLE|DMA_INT_ENAB); \ - sbus_writel(tmp, (__regs) + DMA_CSR); \ -} while(0) -#define DMA_BEGINDMA_R(__regs) \ -do { u32 tmp = sbus_readl((__regs) + DMA_CSR); \ - tmp |= (DMA_ENABLE|DMA_INT_ENAB); \ - tmp &= ~DMA_ST_WRITE; \ - sbus_writel(tmp, (__regs) + DMA_CSR); \ -} while(0) - -/* For certain DMA chips, we need to disable ints upon irq entry - * and turn them back on when we are done. So in any ESP interrupt - * handler you *must* call DMA_IRQ_ENTRY upon entry and DMA_IRQ_EXIT - * when leaving the handler. You have been warned... - */ -#define DMA_IRQ_ENTRY(dma, dregs) do { \ - if(DMA_ISBROKEN(dma)) DMA_INTSOFF(dregs); \ - } while (0) - -#define DMA_IRQ_EXIT(dma, dregs) do { \ - if(DMA_ISBROKEN(dma)) DMA_INTSON(dregs); \ - } while(0) - -#define for_each_dvma(dma) \ - for((dma) = dma_chain; (dma); (dma) = (dma)->next) - -/* From PCI */ - -#ifdef CONFIG_PCI -extern int isa_dma_bridge_buggy; -#else -#define isa_dma_bridge_buggy (0) -#endif - -#endif /* !(_ASM_SPARC64_DMA_H) */ diff --git a/arch/sparc/include/asm/ebus.h b/arch/sparc/include/asm/ebus.h deleted file mode 100644 index 83a6d16c22e..00000000000 --- a/arch/sparc/include/asm/ebus.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef ___ASM_SPARC_EBUS_H -#define ___ASM_SPARC_EBUS_H -#if defined(__sparc__) && defined(__arch64__) -#include <asm/ebus_64.h> -#else -#include <asm/ebus_32.h> -#endif -#endif diff --git a/arch/sparc/include/asm/ebus_32.h b/arch/sparc/include/asm/ebus_32.h deleted file mode 100644 index f91f0b267ce..00000000000 --- a/arch/sparc/include/asm/ebus_32.h +++ /dev/null @@ -1,99 +0,0 @@ -/* - * ebus.h: PCI to Ebus pseudo driver software state. - * - * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) - * - * Adopted for sparc by V. Roganov and G. Raiko. - */ - -#ifndef __SPARC_EBUS_H -#define __SPARC_EBUS_H - -#ifndef _LINUX_IOPORT_H -#include <linux/ioport.h> -#endif -#include <linux/of_device.h> -#include <asm/oplib.h> -#include <asm/prom.h> - -struct linux_ebus_child { - struct linux_ebus_child *next; - struct linux_ebus_device *parent; - struct linux_ebus *bus; - struct device_node *prom_node; - struct resource resource[PROMREG_MAX]; - int num_addrs; - unsigned int irqs[PROMINTR_MAX]; - int num_irqs; -}; - -struct linux_ebus_device { - struct of_device ofdev; - struct linux_ebus_device *next; - struct linux_ebus_child *children; - struct linux_ebus *bus; - struct device_node *prom_node; - struct resource resource[PROMREG_MAX]; - int num_addrs; - unsigned int irqs[PROMINTR_MAX]; - int num_irqs; -}; -#define to_ebus_device(d) container_of(d, struct linux_ebus_device, ofdev.dev) - -struct linux_ebus { - struct of_device ofdev; - struct linux_ebus *next; - struct linux_ebus_device *devices; - struct linux_pbm_info *parent; - struct pci_dev *self; - struct device_node *prom_node; -}; -#define to_ebus(d) container_of(d, struct linux_ebus, ofdev.dev) - -struct linux_ebus_dma { - unsigned int dcsr; - unsigned int dacr; - unsigned int dbcr; -}; - -#define EBUS_DCSR_INT_PEND 0x00000001 -#define EBUS_DCSR_ERR_PEND 0x00000002 -#define EBUS_DCSR_DRAIN 0x00000004 -#define EBUS_DCSR_INT_EN 0x00000010 -#define EBUS_DCSR_RESET 0x00000080 -#define EBUS_DCSR_WRITE 0x00000100 -#define EBUS_DCSR_EN_DMA 0x00000200 -#define EBUS_DCSR_CYC_PEND 0x00000400 -#define EBUS_DCSR_DIAG_RD_DONE 0x00000800 -#define EBUS_DCSR_DIAG_WR_DONE 0x00001000 -#define EBUS_DCSR_EN_CNT 0x00002000 -#define EBUS_DCSR_TC 0x00004000 -#define EBUS_DCSR_DIS_CSR_DRN 0x00010000 -#define EBUS_DCSR_BURST_SZ_MASK 0x000c0000 -#define EBUS_DCSR_BURST_SZ_1 0x00080000 -#define EBUS_DCSR_BURST_SZ_4 0x00000000 -#define EBUS_DCSR_BURST_SZ_8 0x00040000 -#define EBUS_DCSR_BURST_SZ_16 0x000c0000 -#define EBUS_DCSR_DIAG_EN 0x00100000 -#define EBUS_DCSR_DIS_ERR_PEND 0x00400000 -#define EBUS_DCSR_TCI_DIS 0x00800000 -#define EBUS_DCSR_EN_NEXT 0x01000000 -#define EBUS_DCSR_DMA_ON 0x02000000 -#define EBUS_DCSR_A_LOADED 0x04000000 -#define EBUS_DCSR_NA_LOADED 0x08000000 -#define EBUS_DCSR_DEV_ID_MASK 0xf0000000 - -extern struct linux_ebus *ebus_chain; - -extern void ebus_init(void); - -#define for_each_ebus(bus) \ - for((bus) = ebus_chain; (bus); (bus) = (bus)->next) - -#define for_each_ebusdev(dev, bus) \ - for((dev) = (bus)->devices; (dev); (dev) = (dev)->next) - -#define for_each_edevchild(dev, child) \ - for((child) = (dev)->children; (child); (child) = (child)->next) - -#endif /* !(__SPARC_EBUS_H) */ diff --git a/arch/sparc/include/asm/ebus_64.h b/arch/sparc/include/asm/ebus_64.h deleted file mode 100644 index 14c6a111f60..00000000000 --- a/arch/sparc/include/asm/ebus_64.h +++ /dev/null @@ -1,95 +0,0 @@ -/* - * ebus.h: PCI to Ebus pseudo driver software state. - * - * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) - * Copyright (C) 1999 David S. Miller (davem@redhat.com) - */ - -#ifndef __SPARC64_EBUS_H -#define __SPARC64_EBUS_H - -#include <linux/of_device.h> - -#include <asm/oplib.h> -#include <asm/prom.h> - -struct linux_ebus_child { - struct linux_ebus_child *next; - struct linux_ebus_device *parent; - struct linux_ebus *bus; - struct device_node *prom_node; - struct resource resource[PROMREG_MAX]; - int num_addrs; - unsigned int irqs[PROMINTR_MAX]; - int num_irqs; -}; - -struct linux_ebus_device { - struct of_device ofdev; - struct linux_ebus_device *next; - struct linux_ebus_child *children; - struct linux_ebus *bus; - struct device_node *prom_node; - struct resource resource[PROMREG_MAX]; - int num_addrs; - unsigned int irqs[PROMINTR_MAX]; - int num_irqs; -}; -#define to_ebus_device(d) container_of(d, struct linux_ebus_device, ofdev.dev) - -struct linux_ebus { - struct of_device ofdev; - struct linux_ebus *next; - struct linux_ebus_device *devices; - struct pci_dev *self; - int index; - int is_rio; - struct device_node *prom_node; -}; -#define to_ebus(d) container_of(d, struct linux_ebus, ofdev.dev) - -struct ebus_dma_info { - spinlock_t lock; - void __iomem *regs; - - unsigned int flags; -#define EBUS_DMA_FLAG_USE_EBDMA_HANDLER 0x00000001 -#define EBUS_DMA_FLAG_TCI_DISABLE 0x00000002 - - /* These are only valid is EBUS_DMA_FLAG_USE_EBDMA_HANDLER is - * set. - */ - void (*callback)(struct ebus_dma_info *p, int event, void *cookie); - void *client_cookie; - unsigned int irq; -#define EBUS_DMA_EVENT_ERROR 1 -#define EBUS_DMA_EVENT_DMA 2 -#define EBUS_DMA_EVENT_DEVICE 4 - - unsigned char name[64]; -}; - -extern int ebus_dma_register(struct ebus_dma_info *p); -extern int ebus_dma_irq_enable(struct ebus_dma_info *p, int on); -extern void ebus_dma_unregister(struct ebus_dma_info *p); -extern int ebus_dma_request(struct ebus_dma_info *p, dma_addr_t bus_addr, - size_t len); -extern void ebus_dma_prepare(struct ebus_dma_info *p, int write); -extern unsigned int ebus_dma_residue(struct ebus_dma_info *p); -extern unsigned int ebus_dma_addr(struct ebus_dma_info *p); -extern void ebus_dma_enable(struct ebus_dma_info *p, int on); - -extern struct linux_ebus *ebus_chain; - -extern void ebus_init(void); - -#define for_each_ebus(bus) \ - for((bus) = ebus_chain; (bus); (bus) = (bus)->next) - -#define for_each_ebusdev(dev, bus) \ - for((dev) = (bus)->devices; (dev); (dev) = (dev)->next) - -#define for_each_edevchild(dev, child) \ - for((child) = (dev)->children; (child); (child) = (child)->next) - -#endif /* !(__SPARC64_EBUS_H) */ diff --git a/arch/sparc/include/asm/ebus_dma.h b/arch/sparc/include/asm/ebus_dma.h new file mode 100644 index 00000000000..f07a5b541c9 --- /dev/null +++ b/arch/sparc/include/asm/ebus_dma.h @@ -0,0 +1,35 @@ +#ifndef __ASM_SPARC_EBUS_DMA_H +#define __ASM_SPARC_EBUS_DMA_H + +struct ebus_dma_info { + spinlock_t lock; + void __iomem *regs; + + unsigned int flags; +#define EBUS_DMA_FLAG_USE_EBDMA_HANDLER 0x00000001 +#define EBUS_DMA_FLAG_TCI_DISABLE 0x00000002 + + /* These are only valid is EBUS_DMA_FLAG_USE_EBDMA_HANDLER is + * set. + */ + void (*callback)(struct ebus_dma_info *p, int event, void *cookie); + void *client_cookie; + unsigned int irq; +#define EBUS_DMA_EVENT_ERROR 1 +#define EBUS_DMA_EVENT_DMA 2 +#define EBUS_DMA_EVENT_DEVICE 4 + + unsigned char name[64]; +}; + +extern int ebus_dma_register(struct ebus_dma_info *p); +extern int ebus_dma_irq_enable(struct ebus_dma_info *p, int on); +extern void ebus_dma_unregister(struct ebus_dma_info *p); +extern int ebus_dma_request(struct ebus_dma_info *p, dma_addr_t bus_addr, + size_t len); +extern void ebus_dma_prepare(struct ebus_dma_info *p, int write); +extern unsigned int ebus_dma_residue(struct ebus_dma_info *p); +extern unsigned int ebus_dma_addr(struct ebus_dma_info *p); +extern void ebus_dma_enable(struct ebus_dma_info *p, int on); + +#endif /* __ASM_SPARC_EBUS_DMA_H */ diff --git a/arch/sparc/include/asm/elf_32.h b/arch/sparc/include/asm/elf_32.h index d043f80bc2f..b7ab6054782 100644 --- a/arch/sparc/include/asm/elf_32.h +++ b/arch/sparc/include/asm/elf_32.h @@ -105,11 +105,8 @@ typedef struct { #define ELF_DATA ELFDATA2MSB #define USE_ELF_CORE_DUMP -#ifndef CONFIG_SUN4 + #define ELF_EXEC_PAGESIZE 4096 -#else -#define ELF_EXEC_PAGESIZE 8192 -#endif /* This is the location that an ET_DYN program is loaded if exec'ed. Typical @@ -126,7 +123,7 @@ typedef struct { /* Sun4c has none of the capabilities, most sun4m's have them all. * XXX This is gross, set some global variable at boot time. -DaveM */ -#define ELF_HWCAP ((ARCH_SUN4C_SUN4) ? 0 : \ +#define ELF_HWCAP ((ARCH_SUN4C) ? 0 : \ (HWCAP_SPARC_FLUSH | HWCAP_SPARC_STBAR | \ HWCAP_SPARC_SWAP | \ ((srmmu_modtype != Cypress && \ diff --git a/arch/sparc/include/asm/fhc.h b/arch/sparc/include/asm/fhc.h index 788cbc46a11..57f1b303ad5 100644 --- a/arch/sparc/include/asm/fhc.h +++ b/arch/sparc/include/asm/fhc.h @@ -1,5 +1,4 @@ -/* - * fhc.h: Structures for central/fhc pseudo driver on Sunfire/Starfire/Wildfire. +/* fhc.h: FHC and Clock board register definitions. * * Copyright (C) 1997, 1999 David S. Miller (davem@redhat.com) */ @@ -7,14 +6,6 @@ #ifndef _SPARC64_FHC_H #define _SPARC64_FHC_H -#include <linux/timer.h> - -#include <asm/oplib.h> -#include <asm/prom.h> -#include <asm/upa.h> - -struct linux_fhc; - /* Clock board register offsets. */ #define CLOCK_CTRL 0x00UL /* Main control */ #define CLOCK_STAT1 0x10UL /* Status one */ @@ -29,21 +20,7 @@ struct linux_fhc; #define CLOCK_CTRL_MLED 0x02 /* Mid LED, 1 == on */ #define CLOCK_CTRL_RLED 0x01 /* RIght LED, 1 == on */ -struct linux_central { - struct linux_fhc *child; - unsigned long cfreg; - unsigned long clkregs; - unsigned long clkver; - int slots; - struct device_node *prom_node; - - struct linux_prom_ranges central_ranges[PROMREG_MAX]; - int num_central_ranges; -}; - /* Firehose controller register offsets */ -struct fhc_regs { - unsigned long pregs; /* FHC internal regs */ #define FHC_PREGS_ID 0x00UL /* FHC ID */ #define FHC_ID_VERS 0xf0000000 /* Version of this FHC */ #define FHC_ID_PARTID 0x0ffff000 /* Part ID code (0x0f9f == FHC) */ @@ -90,32 +67,14 @@ struct fhc_regs { #define FHC_JTAG_CTRL_MENAB 0x80000000 /* Indicates this is JTAG Master */ #define FHC_JTAG_CTRL_MNONE 0x40000000 /* Indicates no JTAG Master present */ #define FHC_PREGS_JCMD 0x100UL /* FHC JTAG Command Register */ - unsigned long ireg; /* FHC IGN reg */ #define FHC_IREG_IGN 0x00UL /* This FHC's IGN */ - unsigned long ffregs; /* FHC fanfail regs */ #define FHC_FFREGS_IMAP 0x00UL /* FHC Fanfail IMAP */ #define FHC_FFREGS_ICLR 0x10UL /* FHC Fanfail ICLR */ - unsigned long sregs; /* FHC system regs */ #define FHC_SREGS_IMAP 0x00UL /* FHC System IMAP */ #define FHC_SREGS_ICLR 0x10UL /* FHC System ICLR */ - unsigned long uregs; /* FHC uart regs */ #define FHC_UREGS_IMAP 0x00UL /* FHC Uart IMAP */ #define FHC_UREGS_ICLR 0x10UL /* FHC Uart ICLR */ - unsigned long tregs; /* FHC TOD regs */ #define FHC_TREGS_IMAP 0x00UL /* FHC TOD IMAP */ #define FHC_TREGS_ICLR 0x10UL /* FHC TOD ICLR */ -}; - -struct linux_fhc { - struct linux_fhc *next; - struct linux_central *parent; /* NULL if not central FHC */ - struct fhc_regs fhc_regs; - int board; - int jtag_master; - struct device_node *prom_node; - - struct linux_prom_ranges fhc_ranges[PROMREG_MAX]; - int num_fhc_ranges; -}; #endif /* !(_SPARC64_FHC_H) */ diff --git a/arch/sparc/include/asm/floppy_32.h b/arch/sparc/include/asm/floppy_32.h index ae3f00bf22f..c792830636d 100644 --- a/arch/sparc/include/asm/floppy_32.h +++ b/arch/sparc/include/asm/floppy_32.h @@ -6,6 +6,9 @@ #ifndef __ASM_SPARC_FLOPPY_H #define __ASM_SPARC_FLOPPY_H +#include <linux/of.h> +#include <linux/of_device.h> + #include <asm/page.h> #include <asm/pgtable.h> #include <asm/system.h> @@ -343,7 +346,7 @@ static int sun_floppy_init(void) r.flags = fd_regs[0].which_io; r.start = fd_regs[0].phys_addr; sun_fdc = (struct sun_flpy_controller *) - sbus_ioremap(&r, 0, fd_regs[0].reg_size, "floppy"); + of_ioremap(&r, 0, fd_regs[0].reg_size, "floppy"); /* Last minute sanity check... */ if(sun_fdc->status_82072 == 0xff) { @@ -385,4 +388,15 @@ static int sparc_eject(void) #define EXTRA_FLOPPY_PARAMS +static DEFINE_SPINLOCK(dma_spin_lock); + +#define claim_dma_lock() \ +({ unsigned long flags; \ + spin_lock_irqsave(&dma_spin_lock, flags); \ + flags; \ +}) + +#define release_dma_lock(__flags) \ + spin_unlock_irqrestore(&dma_spin_lock, __flags); + #endif /* !(__ASM_SPARC_FLOPPY_H) */ diff --git a/arch/sparc/include/asm/floppy_64.h b/arch/sparc/include/asm/floppy_64.h index c39db1060bc..36439d67ad7 100644 --- a/arch/sparc/include/asm/floppy_64.h +++ b/arch/sparc/include/asm/floppy_64.h @@ -1,6 +1,6 @@ /* floppy.h: Sparc specific parts of the Floppy driver. * - * Copyright (C) 1996, 2007 David S. Miller (davem@davemloft.net) + * Copyright (C) 1996, 2007, 2008 David S. Miller (davem@davemloft.net) * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) * * Ultra/PCI support added: Sep 1997 Eddie C. Dost (ecd@skynet.be) @@ -9,18 +9,11 @@ #ifndef __ASM_SPARC64_FLOPPY_H #define __ASM_SPARC64_FLOPPY_H -#include <linux/init.h> -#include <linux/pci.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/dma-mapping.h> -#include <asm/page.h> -#include <asm/pgtable.h> -#include <asm/system.h> -#include <asm/idprom.h> -#include <asm/oplib.h> #include <asm/auxio.h> -#include <asm/sbus.h> -#include <asm/irq.h> - /* * Define this to enable exchanging drive 0 and 1 if only drive 1 is @@ -50,7 +43,7 @@ struct sun_flpy_controller { /* You'll only ever find one controller on an Ultra anyways. */ static struct sun_flpy_controller *sun_fdc = (struct sun_flpy_controller *)-1; unsigned long fdc_status; -static struct sbus_dev *floppy_sdev = NULL; +static struct of_device *floppy_op = NULL; struct sun_floppy_ops { unsigned char (*fd_inb) (unsigned long port); @@ -291,12 +284,11 @@ static int sun_fd_eject(int drive) return 0; } -#ifdef CONFIG_PCI -#include <asm/ebus.h> +#include <asm/ebus_dma.h> #include <asm/ns87303.h> static struct ebus_dma_info sun_pci_fd_ebus_dma; -static struct pci_dev *sun_pci_ebus_dev; +static struct device *sun_floppy_dev; static int sun_pci_broken_drive = -1; struct sun_pci_dma_op { @@ -377,7 +369,7 @@ static void sun_pci_fd_enable_dma(void) sun_pci_dma_pending.addr = -1U; sun_pci_dma_current.addr = - pci_map_single(sun_pci_ebus_dev, + dma_map_single(sun_floppy_dev, sun_pci_dma_current.buf, sun_pci_dma_current.len, sun_pci_dma_current.direction); @@ -394,7 +386,7 @@ static void sun_pci_fd_disable_dma(void) { ebus_dma_enable(&sun_pci_fd_ebus_dma, 0); if (sun_pci_dma_current.addr != -1U) - pci_unmap_single(sun_pci_ebus_dev, + dma_unmap_single(sun_floppy_dev, sun_pci_dma_current.addr, sun_pci_dma_current.len, sun_pci_dma_current.direction); @@ -404,9 +396,9 @@ static void sun_pci_fd_disable_dma(void) static void sun_pci_fd_set_dma_mode(int mode) { if (mode == DMA_MODE_WRITE) - sun_pci_dma_pending.direction = PCI_DMA_TODEVICE; + sun_pci_dma_pending.direction = DMA_TO_DEVICE; else - sun_pci_dma_pending.direction = PCI_DMA_FROMDEVICE; + sun_pci_dma_pending.direction = DMA_FROM_DEVICE; ebus_dma_prepare(&sun_pci_fd_ebus_dma, mode != DMA_MODE_WRITE); } @@ -538,80 +530,84 @@ static int sun_pci_fd_test_drive(unsigned long port, int drive) #undef MSR #undef DOR -#endif /* CONFIG_PCI */ - -#ifdef CONFIG_PCI -static int __init ebus_fdthree_p(struct linux_ebus_device *edev) +static int __init ebus_fdthree_p(struct device_node *dp) { - if (!strcmp(edev->prom_node->name, "fdthree")) + if (!strcmp(dp->name, "fdthree")) return 1; - if (!strcmp(edev->prom_node->name, "floppy")) { + if (!strcmp(dp->name, "floppy")) { const char *compat; - compat = of_get_property(edev->prom_node, - "compatible", NULL); + compat = of_get_property(dp, "compatible", NULL); if (compat && !strcmp(compat, "fdthree")) return 1; } return 0; } -#endif static unsigned long __init sun_floppy_init(void) { - char state[128]; - struct sbus_bus *bus; - struct sbus_dev *sdev = NULL; static int initialized = 0; + struct device_node *dp; + struct of_device *op; + const char *prop; + char state[128]; if (initialized) return sun_floppy_types[0]; initialized = 1; - for_all_sbusdev (sdev, bus) { - if (!strcmp(sdev->prom_name, "SUNW,fdtwo")) + op = NULL; + + for_each_node_by_name(dp, "SUNW,fdtwo") { + if (strcmp(dp->parent->name, "sbus")) + continue; + op = of_find_device_by_node(dp); + if (op) break; } - if(sdev) { - floppy_sdev = sdev; - FLOPPY_IRQ = sdev->irqs[0]; + if (op) { + floppy_op = op; + FLOPPY_IRQ = op->irqs[0]; } else { -#ifdef CONFIG_PCI - struct linux_ebus *ebus; - struct linux_ebus_device *edev = NULL; - unsigned long config = 0; + struct device_node *ebus_dp; void __iomem *auxio_reg; const char *state_prop; + unsigned long config; - for_each_ebus(ebus) { - for_each_ebusdev(edev, ebus) { - if (ebus_fdthree_p(edev)) - goto ebus_done; + dp = NULL; + for_each_node_by_name(ebus_dp, "ebus") { + for (dp = ebus_dp->child; dp; dp = dp->sibling) { + if (ebus_fdthree_p(dp)) + goto found_fdthree; } } - ebus_done: - if (!edev) + found_fdthree: + if (!dp) + return 0; + + op = of_find_device_by_node(dp); + if (!op) return 0; - state_prop = of_get_property(edev->prom_node, "status", NULL); + state_prop = of_get_property(op->node, "status", NULL); if (state_prop && !strncmp(state_prop, "disabled", 8)) return 0; - FLOPPY_IRQ = edev->irqs[0]; + FLOPPY_IRQ = op->irqs[0]; /* Make sure the high density bit is set, some systems * (most notably Ultra5/Ultra10) come up with it clear. */ - auxio_reg = (void __iomem *) edev->resource[2].start; + auxio_reg = (void __iomem *) op->resource[2].start; writel(readl(auxio_reg)|0x2, auxio_reg); - sun_pci_ebus_dev = ebus->self; + sun_floppy_dev = &op->dev; spin_lock_init(&sun_pci_fd_ebus_dma.lock); /* XXX ioremap */ sun_pci_fd_ebus_dma.regs = (void __iomem *) - edev->resource[1].start; + op->resource[1].start; if (!sun_pci_fd_ebus_dma.regs) return 0; @@ -625,7 +621,7 @@ static unsigned long __init sun_floppy_init(void) return 0; /* XXX ioremap */ - sun_fdc = (struct sun_flpy_controller *)edev->resource[0].start; + sun_fdc = (struct sun_flpy_controller *) op->resource[0].start; sun_fdops.fd_inb = sun_pci_fd_inb; sun_fdops.fd_outb = sun_pci_fd_outb; @@ -662,12 +658,15 @@ static unsigned long __init sun_floppy_init(void) /* * Find NS87303 SuperIO config registers (through ecpp). */ - for_each_ebus(ebus) { - for_each_ebusdev(edev, ebus) { - if (!strcmp(edev->prom_node->name, "ecpp")) { - config = edev->resource[1].start; - goto config_done; - } + config = 0; + for (dp = ebus_dp->child; dp; dp = dp->sibling) { + if (!strcmp(dp->name, "ecpp")) { + struct of_device *ecpp_op; + + ecpp_op = of_find_device_by_node(dp); + if (ecpp_op) + config = ecpp_op->resource[1].start; + goto config_done; } } config_done: @@ -716,26 +715,23 @@ static unsigned long __init sun_floppy_init(void) #endif /* PCI_FDC_SWAP_DRIVES */ return sun_floppy_types[0]; -#else - return 0; -#endif } - prom_getproperty(sdev->prom_node, "status", state, sizeof(state)); - if(!strncmp(state, "disabled", 8)) + prop = of_get_property(op->node, "status", NULL); + if (prop && !strncmp(state, "disabled", 8)) return 0; /* - * We cannot do sbus_ioremap here: it does request_region, + * We cannot do of_ioremap here: it does request_region, * which the generic floppy driver tries to do once again. * But we must use the sdev resource values as they have * had parent ranges applied. */ sun_fdc = (struct sun_flpy_controller *) - (sdev->resource[0].start + - ((sdev->resource[0].flags & 0x1ffUL) << 32UL)); + (op->resource[0].start + + ((op->resource[0].flags & 0x1ffUL) << 32UL)); /* Last minute sanity check... */ - if(sbus_readb(&sun_fdc->status1_82077) == 0xff) { + if (sbus_readb(&sun_fdc->status1_82077) == 0xff) { sun_fdc = (struct sun_flpy_controller *)-1; return 0; } diff --git a/arch/sparc/include/asm/gpio.h b/arch/sparc/include/asm/gpio.h new file mode 100644 index 00000000000..a0e3ac0af59 --- /dev/null +++ b/arch/sparc/include/asm/gpio.h @@ -0,0 +1,36 @@ +#ifndef __ASM_SPARC_GPIO_H +#define __ASM_SPARC_GPIO_H + +#include <linux/errno.h> +#include <asm-generic/gpio.h> + +#ifdef CONFIG_GPIOLIB + +static inline int gpio_get_value(unsigned int gpio) +{ + return __gpio_get_value(gpio); +} + +static inline void gpio_set_value(unsigned int gpio, int value) +{ + __gpio_set_value(gpio, value); +} + +static inline int gpio_cansleep(unsigned int gpio) +{ + return __gpio_cansleep(gpio); +} + +static inline int gpio_to_irq(unsigned int gpio) +{ + return -ENOSYS; +} + +static inline int irq_to_gpio(unsigned int irq) +{ + return -EINVAL; +} + +#endif /* CONFIG_GPIOLIB */ + +#endif /* __ASM_SPARC_GPIO_H */ diff --git a/arch/sparc/include/asm/io-unit.h b/arch/sparc/include/asm/io-unit.h index 96823b47fd4..01ab2f613e9 100644 --- a/arch/sparc/include/asm/io-unit.h +++ b/arch/sparc/include/asm/io-unit.h @@ -55,8 +55,4 @@ struct iounit_struct { #define IOUNIT_BMAPM_START IOUNIT_BMAP2_END #define IOUNIT_BMAPM_END ((IOUNIT_DMA_SIZE - IOUNIT_DVMA_SIZE) >> PAGE_SHIFT) -extern __u32 iounit_map_dma_init(struct sbus_bus *, int); -#define iounit_map_dma_finish(sbus, addr, len) mmu_release_scsi_one(addr, len, sbus) -extern __u32 iounit_map_dma_page(__u32, void *, struct sbus_bus *); - #endif /* !(_SPARC_IO_UNIT_H) */ diff --git a/arch/sparc/include/asm/io_32.h b/arch/sparc/include/asm/io_32.h index 10d7da45007..93fe21e02c8 100644 --- a/arch/sparc/include/asm/io_32.h +++ b/arch/sparc/include/asm/io_32.h @@ -293,14 +293,6 @@ extern void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long max); extern void pci_iounmap(struct pci_dev *dev, void __iomem *); /* - * Bus number may be in res->flags... somewhere. - */ -extern void __iomem *sbus_ioremap(struct resource *res, unsigned long offset, - unsigned long size, char *name); -extern void sbus_iounmap(volatile void __iomem *vaddr, unsigned long size); - - -/* * At the moment, we do not use CMOS_READ anywhere outside of rtc.c, * so rtc_port is static in it. This should not change unless a new * hardware pops up. @@ -308,6 +300,17 @@ extern void sbus_iounmap(volatile void __iomem *vaddr, unsigned long size); #define RTC_PORT(x) (rtc_port + (x)) #define RTC_ALWAYS_BCD 0 +static inline int sbus_can_dma_64bit(void) +{ + return 0; /* actually, sparc_cpu_model==sun4d */ +} +static inline int sbus_can_burst64(void) +{ + return 0; /* actually, sparc_cpu_model==sun4d */ +} +struct device; +extern void sbus_set_sbus64(struct device *, int); + #endif #define __ARCH_HAS_NO_PAGE_ZERO_MAPPED 1 diff --git a/arch/sparc/include/asm/io_64.h b/arch/sparc/include/asm/io_64.h index 0bff078ffdd..4aee21dc9c6 100644 --- a/arch/sparc/include/asm/io_64.h +++ b/arch/sparc/include/asm/io_64.h @@ -482,18 +482,16 @@ struct pci_dev; extern void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long max); extern void pci_iounmap(struct pci_dev *dev, void __iomem *); -/* Similarly for SBUS. */ -#define sbus_ioremap(__res, __offset, __size, __name) \ -({ unsigned long __ret; \ - __ret = (__res)->start + (((__res)->flags & 0x1ffUL) << 32UL); \ - __ret += (unsigned long) (__offset); \ - if (! request_region((__ret), (__size), (__name))) \ - __ret = 0UL; \ - (void __iomem *) __ret; \ -}) - -#define sbus_iounmap(__addr, __size) \ - release_region((unsigned long)(__addr), (__size)) +static inline int sbus_can_dma_64bit(void) +{ + return 1; +} +static inline int sbus_can_burst64(void) +{ + return 1; +} +struct device; +extern void sbus_set_sbus64(struct device *, int); /* * Convert a physical pointer to a virtual kernel pointer for /dev/mem diff --git a/arch/sparc/include/asm/iommu_64.h b/arch/sparc/include/asm/iommu_64.h index d7b9afcba08..caf798b5619 100644 --- a/arch/sparc/include/asm/iommu_64.h +++ b/arch/sparc/include/asm/iommu_64.h @@ -48,6 +48,9 @@ struct strbuf { unsigned long strbuf_control; unsigned long strbuf_pflush; unsigned long strbuf_fsync; + unsigned long strbuf_err_stat; + unsigned long strbuf_tag_diag; + unsigned long strbuf_line_diag; unsigned long strbuf_ctxflush; unsigned long strbuf_ctxmatch_base; unsigned long strbuf_flushflag_pa; diff --git a/arch/sparc/include/asm/irq_64.h b/arch/sparc/include/asm/irq_64.h index e3dd9303643..71673eca366 100644 --- a/arch/sparc/include/asm/irq_64.h +++ b/arch/sparc/include/asm/irq_64.h @@ -56,7 +56,6 @@ extern unsigned int sun4u_build_msi(u32 portid, unsigned int *virt_irq_p, unsigned long imap_base, unsigned long iclr_base); extern void sun4u_destroy_msi(unsigned int virt_irq); -extern unsigned int sbus_build_irq(void *sbus, unsigned int ino); extern unsigned char virt_irq_alloc(unsigned int dev_handle, unsigned int dev_ino); diff --git a/arch/sparc/include/asm/mc146818rtc_64.h b/arch/sparc/include/asm/mc146818rtc_64.h index e9c0fcc25c6..7238d174e0e 100644 --- a/arch/sparc/include/asm/mc146818rtc_64.h +++ b/arch/sparc/include/asm/mc146818rtc_64.h @@ -7,12 +7,8 @@ #include <asm/io.h> #ifndef RTC_PORT -#ifdef CONFIG_PCI -extern unsigned long ds1287_regs; -#else -#define ds1287_regs (0UL) -#endif -#define RTC_PORT(x) (ds1287_regs + (x)) +extern unsigned long cmos_regs; +#define RTC_PORT(x) (cmos_regs + (x)) #define RTC_ALWAYS_BCD 0 #endif @@ -29,6 +25,4 @@ outb_p((addr),RTC_PORT(0)); \ outb_p((val),RTC_PORT(1)); \ }) -#define RTC_IRQ 8 - #endif /* __ASM_SPARC64_MC146818RTC_H */ diff --git a/arch/sparc/include/asm/memctrl.h b/arch/sparc/include/asm/memctrl.h new file mode 100644 index 00000000000..4065c56af7b --- /dev/null +++ b/arch/sparc/include/asm/memctrl.h @@ -0,0 +1,9 @@ +#ifndef _SPARC_MEMCTRL_H +#define _SPARC_MEMCTRL_H + +typedef int (*dimm_printer_t)(int synd_code, unsigned long paddr, char *buf, int buflen); + +int register_dimm_printer(dimm_printer_t func); +void unregister_dimm_printer(dimm_printer_t func); + +#endif /* _SPARC_MEMCTRL_H */ diff --git a/arch/sparc/include/asm/mostek.h b/arch/sparc/include/asm/mostek.h deleted file mode 100644 index 433be3e0a69..00000000000 --- a/arch/sparc/include/asm/mostek.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef ___ASM_SPARC_MOSTEK_H -#define ___ASM_SPARC_MOSTEK_H -#if defined(__sparc__) && defined(__arch64__) -#include <asm/mostek_64.h> -#else -#include <asm/mostek_32.h> -#endif -#endif diff --git a/arch/sparc/include/asm/mostek_32.h b/arch/sparc/include/asm/mostek_32.h deleted file mode 100644 index a99590c4c50..00000000000 --- a/arch/sparc/include/asm/mostek_32.h +++ /dev/null @@ -1,171 +0,0 @@ -/* - * mostek.h: Describes the various Mostek time of day clock registers. - * - * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) - * Copyright (C) 1996 Thomas K. Dyas (tdyas@eden.rutgers.edu) - * Added intersil code 05/25/98 Chris Davis (cdavis@cois.on.ca) - */ - -#ifndef _SPARC_MOSTEK_H -#define _SPARC_MOSTEK_H - -#include <asm/idprom.h> -#include <asm/io.h> - -/* M48T02 Register Map (adapted from Sun NVRAM/Hostid FAQ) - * - * Data - * Address Function - * Bit 7 Bit 6 Bit 5 Bit 4Bit 3 Bit 2 Bit 1 Bit 0 - * 7ff - - - - - - - - Year 00-99 - * 7fe 0 0 0 - - - - - Month 01-12 - * 7fd 0 0 - - - - - - Date 01-31 - * 7fc 0 FT 0 0 0 - - - Day 01-07 - * 7fb KS 0 - - - - - - Hours 00-23 - * 7fa 0 - - - - - - - Minutes 00-59 - * 7f9 ST - - - - - - - Seconds 00-59 - * 7f8 W R S - - - - - Control - * - * * ST is STOP BIT - * * W is WRITE BIT - * * R is READ BIT - * * S is SIGN BIT - * * FT is FREQ TEST BIT - * * KS is KICK START BIT - */ - -/* The Mostek 48t02 real time clock and NVRAM chip. The registers - * other than the control register are in binary coded decimal. Some - * control bits also live outside the control register. - */ -#define mostek_read(_addr) readb(_addr) -#define mostek_write(_addr,_val) writeb(_val, _addr) -#define MOSTEK_EEPROM 0x0000UL -#define MOSTEK_IDPROM 0x07d8UL -#define MOSTEK_CREG 0x07f8UL -#define MOSTEK_SEC 0x07f9UL -#define MOSTEK_MIN 0x07faUL -#define MOSTEK_HOUR 0x07fbUL -#define MOSTEK_DOW 0x07fcUL -#define MOSTEK_DOM 0x07fdUL -#define MOSTEK_MONTH 0x07feUL -#define MOSTEK_YEAR 0x07ffUL - -struct mostek48t02 { - volatile char eeprom[2008]; /* This is the eeprom, don't touch! */ - struct idprom idprom; /* The idprom lives here. */ - volatile unsigned char creg; /* Control register */ - volatile unsigned char sec; /* Seconds (0-59) */ - volatile unsigned char min; /* Minutes (0-59) */ - volatile unsigned char hour; /* Hour (0-23) */ - volatile unsigned char dow; /* Day of the week (1-7) */ - volatile unsigned char dom; /* Day of the month (1-31) */ - volatile unsigned char month; /* Month of year (1-12) */ - volatile unsigned char year; /* Year (0-99) */ -}; - -extern spinlock_t mostek_lock; -extern void __iomem *mstk48t02_regs; - -/* Control register values. */ -#define MSTK_CREG_WRITE 0x80 /* Must set this before placing values. */ -#define MSTK_CREG_READ 0x40 /* Stop updates to allow a clean read. */ -#define MSTK_CREG_SIGN 0x20 /* Slow/speed clock in calibration mode. */ - -/* Control bits that live in the other registers. */ -#define MSTK_STOP 0x80 /* Stop the clock oscillator. (sec) */ -#define MSTK_KICK_START 0x80 /* Kick start the clock chip. (hour) */ -#define MSTK_FREQ_TEST 0x40 /* Frequency test mode. (day) */ - -#define MSTK_YEAR_ZERO 1968 /* If year reg has zero, it is 1968. */ -#define MSTK_CVT_YEAR(yr) ((yr) + MSTK_YEAR_ZERO) - -/* Masks that define how much space each value takes up. */ -#define MSTK_SEC_MASK 0x7f -#define MSTK_MIN_MASK 0x7f -#define MSTK_HOUR_MASK 0x3f -#define MSTK_DOW_MASK 0x07 -#define MSTK_DOM_MASK 0x3f -#define MSTK_MONTH_MASK 0x1f -#define MSTK_YEAR_MASK 0xffU - -/* Binary coded decimal conversion macros. */ -#define MSTK_REGVAL_TO_DECIMAL(x) (((x) & 0x0F) + 0x0A * ((x) >> 0x04)) -#define MSTK_DECIMAL_TO_REGVAL(x) ((((x) / 0x0A) << 0x04) + ((x) % 0x0A)) - -/* Generic register set and get macros for internal use. */ -#define MSTK_GET(regs,var,mask) (MSTK_REGVAL_TO_DECIMAL(((struct mostek48t02 *)regs)->var & MSTK_ ## mask ## _MASK)) -#define MSTK_SET(regs,var,value,mask) do { ((struct mostek48t02 *)regs)->var &= ~(MSTK_ ## mask ## _MASK); ((struct mostek48t02 *)regs)->var |= MSTK_DECIMAL_TO_REGVAL(value) & (MSTK_ ## mask ## _MASK); } while (0) - -/* Macros to make register access easier on our fingers. These give you - * the decimal value of the register requested if applicable. You pass - * the a pointer to a 'struct mostek48t02'. - */ -#define MSTK_REG_CREG(regs) (((struct mostek48t02 *)regs)->creg) -#define MSTK_REG_SEC(regs) MSTK_GET(regs,sec,SEC) -#define MSTK_REG_MIN(regs) MSTK_GET(regs,min,MIN) -#define MSTK_REG_HOUR(regs) MSTK_GET(regs,hour,HOUR) -#define MSTK_REG_DOW(regs) MSTK_GET(regs,dow,DOW) -#define MSTK_REG_DOM(regs) MSTK_GET(regs,dom,DOM) -#define MSTK_REG_MONTH(regs) MSTK_GET(regs,month,MONTH) -#define MSTK_REG_YEAR(regs) MSTK_GET(regs,year,YEAR) - -#define MSTK_SET_REG_SEC(regs,value) MSTK_SET(regs,sec,value,SEC) -#define MSTK_SET_REG_MIN(regs,value) MSTK_SET(regs,min,value,MIN) -#define MSTK_SET_REG_HOUR(regs,value) MSTK_SET(regs,hour,value,HOUR) -#define MSTK_SET_REG_DOW(regs,value) MSTK_SET(regs,dow,value,DOW) -#define MSTK_SET_REG_DOM(regs,value) MSTK_SET(regs,dom,value,DOM) -#define MSTK_SET_REG_MONTH(regs,value) MSTK_SET(regs,month,value,MONTH) -#define MSTK_SET_REG_YEAR(regs,value) MSTK_SET(regs,year,value,YEAR) - - -/* The Mostek 48t08 clock chip. Found on Sun4m's I think. It has the - * same (basically) layout of the 48t02 chip except for the extra - * NVRAM on board (8 KB against the 48t02's 2 KB). - */ -struct mostek48t08 { - char offset[6*1024]; /* Magic things may be here, who knows? */ - struct mostek48t02 regs; /* Here is what we are interested in. */ -}; - -#ifdef CONFIG_SUN4 -enum sparc_clock_type { MSTK48T02, MSTK48T08, \ -INTERSIL, MSTK_INVALID }; -#else -enum sparc_clock_type { MSTK48T02, MSTK48T08, \ -MSTK_INVALID }; -#endif - -#ifdef CONFIG_SUN4 -/* intersil on a sun 4/260 code data from harris doc */ -struct intersil_dt { - volatile unsigned char int_csec; - volatile unsigned char int_hour; - volatile unsigned char int_min; - volatile unsigned char int_sec; - volatile unsigned char int_month; - volatile unsigned char int_day; - volatile unsigned char int_year; - volatile unsigned char int_dow; -}; - -struct intersil { - struct intersil_dt clk; - struct intersil_dt cmp; - volatile unsigned char int_intr_reg; - volatile unsigned char int_cmd_reg; -}; - -#define INTERSIL_STOP 0x0 -#define INTERSIL_START 0x8 -#define INTERSIL_INTR_DISABLE 0x0 -#define INTERSIL_INTR_ENABLE 0x10 -#define INTERSIL_32K 0x0 -#define INTERSIL_NORMAL 0x0 -#define INTERSIL_24H 0x4 -#define INTERSIL_INT_100HZ 0x2 - -/* end of intersil info */ -#endif - -#endif /* !(_SPARC_MOSTEK_H) */ diff --git a/arch/sparc/include/asm/mostek_64.h b/arch/sparc/include/asm/mostek_64.h deleted file mode 100644 index c5652de2ace..00000000000 --- a/arch/sparc/include/asm/mostek_64.h +++ /dev/null @@ -1,143 +0,0 @@ -/* mostek.h: Describes the various Mostek time of day clock registers. - * - * Copyright (C) 1995 David S. Miller (davem@davemloft.net) - * Copyright (C) 1996 Thomas K. Dyas (tdyas@eden.rutgers.edu) - */ - -#ifndef _SPARC64_MOSTEK_H -#define _SPARC64_MOSTEK_H - -#include <asm/idprom.h> - -/* M48T02 Register Map (adapted from Sun NVRAM/Hostid FAQ) - * - * Data - * Address Function - * Bit 7 Bit 6 Bit 5 Bit 4Bit 3 Bit 2 Bit 1 Bit 0 - * 7ff - - - - - - - - Year 00-99 - * 7fe 0 0 0 - - - - - Month 01-12 - * 7fd 0 0 - - - - - - Date 01-31 - * 7fc 0 FT 0 0 0 - - - Day 01-07 - * 7fb KS 0 - - - - - - Hours 00-23 - * 7fa 0 - - - - - - - Minutes 00-59 - * 7f9 ST - - - - - - - Seconds 00-59 - * 7f8 W R S - - - - - Control - * - * * ST is STOP BIT - * * W is WRITE BIT - * * R is READ BIT - * * S is SIGN BIT - * * FT is FREQ TEST BIT - * * KS is KICK START BIT - */ - -/* The Mostek 48t02 real time clock and NVRAM chip. The registers - * other than the control register are in binary coded decimal. Some - * control bits also live outside the control register. - * - * We now deal with physical addresses for I/O to the chip. -DaveM - */ -static inline u8 mostek_read(void __iomem *addr) -{ - u8 ret; - - __asm__ __volatile__("lduba [%1] %2, %0" - : "=r" (ret) - : "r" (addr), "i" (ASI_PHYS_BYPASS_EC_E)); - return ret; -} - -static inline void mostek_write(void __iomem *addr, u8 val) -{ - __asm__ __volatile__("stba %0, [%1] %2" - : /* no outputs */ - : "r" (val), "r" (addr), "i" (ASI_PHYS_BYPASS_EC_E)); -} - -#define MOSTEK_EEPROM 0x0000UL -#define MOSTEK_IDPROM 0x07d8UL -#define MOSTEK_CREG 0x07f8UL -#define MOSTEK_SEC 0x07f9UL -#define MOSTEK_MIN 0x07faUL -#define MOSTEK_HOUR 0x07fbUL -#define MOSTEK_DOW 0x07fcUL -#define MOSTEK_DOM 0x07fdUL -#define MOSTEK_MONTH 0x07feUL -#define MOSTEK_YEAR 0x07ffUL - -extern spinlock_t mostek_lock; -extern void __iomem *mstk48t02_regs; - -/* Control register values. */ -#define MSTK_CREG_WRITE 0x80 /* Must set this before placing values. */ -#define MSTK_CREG_READ 0x40 /* Stop updates to allow a clean read. */ -#define MSTK_CREG_SIGN 0x20 /* Slow/speed clock in calibration mode. */ - -/* Control bits that live in the other registers. */ -#define MSTK_STOP 0x80 /* Stop the clock oscillator. (sec) */ -#define MSTK_KICK_START 0x80 /* Kick start the clock chip. (hour) */ -#define MSTK_FREQ_TEST 0x40 /* Frequency test mode. (day) */ - -#define MSTK_YEAR_ZERO 1968 /* If year reg has zero, it is 1968. */ -#define MSTK_CVT_YEAR(yr) ((yr) + MSTK_YEAR_ZERO) - -/* Masks that define how much space each value takes up. */ -#define MSTK_SEC_MASK 0x7f -#define MSTK_MIN_MASK 0x7f -#define MSTK_HOUR_MASK 0x3f -#define MSTK_DOW_MASK 0x07 -#define MSTK_DOM_MASK 0x3f -#define MSTK_MONTH_MASK 0x1f -#define MSTK_YEAR_MASK 0xffU - -/* Binary coded decimal conversion macros. */ -#define MSTK_REGVAL_TO_DECIMAL(x) (((x) & 0x0F) + 0x0A * ((x) >> 0x04)) -#define MSTK_DECIMAL_TO_REGVAL(x) ((((x) / 0x0A) << 0x04) + ((x) % 0x0A)) - -/* Generic register set and get macros for internal use. */ -#define MSTK_GET(regs,name) \ - (MSTK_REGVAL_TO_DECIMAL(mostek_read(regs + MOSTEK_ ## name) & MSTK_ ## name ## _MASK)) -#define MSTK_SET(regs,name,value) \ -do { u8 __val = mostek_read(regs + MOSTEK_ ## name); \ - __val &= ~(MSTK_ ## name ## _MASK); \ - __val |= (MSTK_DECIMAL_TO_REGVAL(value) & \ - (MSTK_ ## name ## _MASK)); \ - mostek_write(regs + MOSTEK_ ## name, __val); \ -} while(0) - -/* Macros to make register access easier on our fingers. These give you - * the decimal value of the register requested if applicable. You pass - * the a pointer to a 'struct mostek48t02'. - */ -#define MSTK_REG_CREG(regs) (mostek_read((regs) + MOSTEK_CREG)) -#define MSTK_REG_SEC(regs) MSTK_GET(regs,SEC) -#define MSTK_REG_MIN(regs) MSTK_GET(regs,MIN) -#define MSTK_REG_HOUR(regs) MSTK_GET(regs,HOUR) -#define MSTK_REG_DOW(regs) MSTK_GET(regs,DOW) -#define MSTK_REG_DOM(regs) MSTK_GET(regs,DOM) -#define MSTK_REG_MONTH(regs) MSTK_GET(regs,MONTH) -#define MSTK_REG_YEAR(regs) MSTK_GET(regs,YEAR) - -#define MSTK_SET_REG_SEC(regs,value) MSTK_SET(regs,SEC,value) -#define MSTK_SET_REG_MIN(regs,value) MSTK_SET(regs,MIN,value) -#define MSTK_SET_REG_HOUR(regs,value) MSTK_SET(regs,HOUR,value) -#define MSTK_SET_REG_DOW(regs,value) MSTK_SET(regs,DOW,value) -#define MSTK_SET_REG_DOM(regs,value) MSTK_SET(regs,DOM,value) -#define MSTK_SET_REG_MONTH(regs,value) MSTK_SET(regs,MONTH,value) -#define MSTK_SET_REG_YEAR(regs,value) MSTK_SET(regs,YEAR,value) - - -/* The Mostek 48t08 clock chip. Found on Sun4m's I think. It has the - * same (basically) layout of the 48t02 chip except for the extra - * NVRAM on board (8 KB against the 48t02's 2 KB). - */ -#define MOSTEK_48T08_OFFSET 0x0000UL /* Lower NVRAM portions */ -#define MOSTEK_48T08_48T02 0x1800UL /* Offset to 48T02 chip */ - -/* SUN5 systems usually have 48t59 model clock chipsets. But we keep the older - * clock chip definitions around just in case. - */ -#define MOSTEK_48T59_OFFSET 0x0000UL /* Lower NVRAM portions */ -#define MOSTEK_48T59_48T02 0x1800UL /* Offset to 48T02 chip */ - -#endif /* !(_SPARC64_MOSTEK_H) */ diff --git a/arch/sparc/include/asm/obio.h b/arch/sparc/include/asm/obio.h index 1a7544ceb57..4ade0c8a2c7 100644 --- a/arch/sparc/include/asm/obio.h +++ b/arch/sparc/include/asm/obio.h @@ -155,17 +155,6 @@ static inline void bw_set_ctrl(int cpu, unsigned ctrl) "i" (ASI_M_CTL)); } -extern unsigned char cpu_leds[32]; - -static inline void show_leds(int cpuid) -{ - cpuid &= 0x1e; - __asm__ __volatile__ ("stba %0, [%1] %2" : : - "r" ((cpu_leds[cpuid] << 4) | cpu_leds[cpuid+1]), - "r" (ECSR_BASE(cpuid) | BB_LEDS), - "i" (ASI_M_CTL)); -} - static inline unsigned cc_get_ipen(void) { unsigned pending; diff --git a/arch/sparc/include/asm/of_device.h b/arch/sparc/include/asm/of_device.h index bba777a416d..a5d9811f969 100644 --- a/arch/sparc/include/asm/of_device.h +++ b/arch/sparc/include/asm/of_device.h @@ -30,6 +30,8 @@ struct of_device extern void __iomem *of_ioremap(struct resource *res, unsigned long offset, unsigned long size, char *name); extern void of_iounmap(struct resource *res, void __iomem *base, unsigned long size); +extern void of_propagate_archdata(struct of_device *bus); + /* This is just here during the transition */ #include <linux/of_platform.h> diff --git a/arch/sparc/include/asm/of_platform.h b/arch/sparc/include/asm/of_platform.h index 2348ab90a57..90da99059f8 100644 --- a/arch/sparc/include/asm/of_platform.h +++ b/arch/sparc/include/asm/of_platform.h @@ -13,9 +13,6 @@ * */ -extern struct bus_type ebus_bus_type; -extern struct bus_type sbus_bus_type; - #define of_bus_type of_platform_bus_type /* for compatibility */ #endif diff --git a/arch/sparc/include/asm/oplib_32.h b/arch/sparc/include/asm/oplib_32.h index b2631da259e..699da05235c 100644 --- a/arch/sparc/include/asm/oplib_32.h +++ b/arch/sparc/include/asm/oplib_32.h @@ -21,7 +21,6 @@ enum prom_major_version { PROM_V2, /* sun4c and early sun4m V2 prom */ PROM_V3, /* sun4m and later, up to sun4d/sun4e machines V3 */ PROM_P1275, /* IEEE compliant ISA based Sun PROM, only sun4u */ - PROM_SUN4, /* Old sun4 proms are totally different, but we'll shoehorn it to make it fit */ }; extern enum prom_major_version prom_vers; diff --git a/arch/sparc/include/asm/page_32.h b/arch/sparc/include/asm/page_32.h index cf5fb70ca1c..d1806edc095 100644 --- a/arch/sparc/include/asm/page_32.h +++ b/arch/sparc/include/asm/page_32.h @@ -8,11 +8,8 @@ #ifndef _SPARC_PAGE_H #define _SPARC_PAGE_H -#ifdef CONFIG_SUN4 -#define PAGE_SHIFT 13 -#else #define PAGE_SHIFT 12 -#endif + #ifndef __ASSEMBLY__ /* I have my suspicions... -DaveM */ #define PAGE_SIZE (1UL << PAGE_SHIFT) diff --git a/arch/sparc/include/asm/page_64.h b/arch/sparc/include/asm/page_64.h index b579b910ef5..4274ed13ddb 100644 --- a/arch/sparc/include/asm/page_64.h +++ b/arch/sparc/include/asm/page_64.h @@ -38,6 +38,8 @@ #ifndef __ASSEMBLY__ +#define WANT_PAGE_VIRTUAL + extern void _clear_page(void *page); #define clear_page(X) _clear_page((void *)(X)) struct page; diff --git a/arch/sparc/include/asm/parport.h b/arch/sparc/include/asm/parport.h index d9830621c90..dff3f0253aa 100644 --- a/arch/sparc/include/asm/parport.h +++ b/arch/sparc/include/asm/parport.h @@ -8,7 +8,7 @@ #include <linux/of_device.h> -#include <asm/ebus.h> +#include <asm/ebus_dma.h> #include <asm/ns87303.h> #include <asm/prom.h> @@ -215,7 +215,7 @@ static int __devexit ecpp_remove(struct of_device *op) return 0; } -static struct of_device_id ecpp_match[] = { +static const struct of_device_id ecpp_match[] = { { .name = "ecpp", }, diff --git a/arch/sparc/include/asm/pci_32.h b/arch/sparc/include/asm/pci_32.h index 0ee949d220c..b41c4c19815 100644 --- a/arch/sparc/include/asm/pci_32.h +++ b/arch/sparc/include/asm/pci_32.h @@ -3,6 +3,8 @@ #ifdef __KERNEL__ +#include <linux/dma-mapping.h> + /* Can be used to override the logic in pci_scan_bus for skipping * already-configured bus numbers - to be used for buggy BIOSes * or architectures with incomplete PCI setup by the loader. diff --git a/arch/sparc/include/asm/pgtable_32.h b/arch/sparc/include/asm/pgtable_32.h index 08237fda887..e0cabe790ec 100644 --- a/arch/sparc/include/asm/pgtable_32.h +++ b/arch/sparc/include/asm/pgtable_32.h @@ -14,11 +14,7 @@ #include <linux/spinlock.h> #include <linux/swap.h> #include <asm/types.h> -#ifdef CONFIG_SUN4 -#include <asm/pgtsun4.h> -#else #include <asm/pgtsun4c.h> -#endif #include <asm/pgtsrmmu.h> #include <asm/vac-ops.h> #include <asm/oplib.h> diff --git a/arch/sparc/include/asm/pgtable_64.h b/arch/sparc/include/asm/pgtable_64.h index bb9ec2cce35..b049abf9902 100644 --- a/arch/sparc/include/asm/pgtable_64.h +++ b/arch/sparc/include/asm/pgtable_64.h @@ -770,6 +770,8 @@ extern void sun4v_patch_tlb_handlers(void); extern unsigned long cmdline_memory_size; +extern asmlinkage void do_sparc64_fault(struct pt_regs *regs); + #endif /* !(__ASSEMBLY__) */ #endif /* !(_SPARC64_PGTABLE_H) */ diff --git a/arch/sparc/include/asm/prom.h b/arch/sparc/include/asm/prom.h index fd55522481c..900d44714f8 100644 --- a/arch/sparc/include/asm/prom.h +++ b/arch/sparc/include/asm/prom.h @@ -18,6 +18,7 @@ */ #include <linux/types.h> #include <linux/proc_fs.h> +#include <linux/mutex.h> #include <asm/atomic.h> #define OF_ROOT_NODE_ADDR_CELLS_DEFAULT 2 @@ -73,6 +74,7 @@ struct of_irq_controller { extern struct device_node *of_find_node_by_cpuid(int cpuid); extern int of_set_property(struct device_node *node, const char *name, void *val, int len); +extern struct mutex of_set_property_mutex; extern int of_getintprop_default(struct device_node *np, const char *name, int def); @@ -94,6 +96,16 @@ static inline void of_node_put(struct device_node *node) { } +/* These routines are here to provide compatibility with how powerpc + * handles IRQ mapping for OF device nodes. We precompute and permanently + * register them in the of_device objects, whereas powerpc computes them + * on request. + */ +extern unsigned int irq_of_parse_and_map(struct device_node *node, int index); +static inline void irq_dispose_mapping(unsigned int virq) +{ +} + /* * NB: This is here while we transition from using asm/prom.h * to linux/of.h diff --git a/arch/sparc/include/asm/ptrace_64.h b/arch/sparc/include/asm/ptrace_64.h index 06e4914c13f..3d3e9c161d8 100644 --- a/arch/sparc/include/asm/ptrace_64.h +++ b/arch/sparc/include/asm/ptrace_64.h @@ -113,6 +113,8 @@ struct sparc_trapf { #ifdef __KERNEL__ +#include <linux/threads.h> + static inline int pt_regs_trap_type(struct pt_regs *regs) { return regs->magic & 0x1ff; @@ -138,6 +140,7 @@ struct global_reg_snapshot { struct thread_info *thread; unsigned long pad1; }; +extern struct global_reg_snapshot global_reg_snapshot[NR_CPUS]; #define __ARCH_WANT_COMPAT_SYS_PTRACE diff --git a/arch/sparc/include/asm/reboot.h b/arch/sparc/include/asm/reboot.h deleted file mode 100644 index 3f3f43f5be5..00000000000 --- a/arch/sparc/include/asm/reboot.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef _SPARC64_REBOOT_H -#define _SPARC64_REBOOT_H - -extern void machine_alt_power_off(void); - -#endif /* _SPARC64_REBOOT_H */ diff --git a/arch/sparc/include/asm/rtc.h b/arch/sparc/include/asm/rtc.h deleted file mode 100644 index f9ecb1fe2ec..00000000000 --- a/arch/sparc/include/asm/rtc.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * rtc.h: Definitions for access to the Mostek real time clock - * - * Copyright (C) 1996 Thomas K. Dyas (tdyas@eden.rutgers.edu) - */ - -#ifndef _RTC_H -#define _RTC_H - -#include <linux/ioctl.h> - -struct rtc_time -{ - int sec; /* Seconds (0-59) */ - int min; /* Minutes (0-59) */ - int hour; /* Hour (0-23) */ - int dow; /* Day of the week (1-7) */ - int dom; /* Day of the month (1-31) */ - int month; /* Month of year (1-12) */ - int year; /* Year (0-99) */ -}; - -#define RTCGET _IOR('p', 20, struct rtc_time) -#define RTCSET _IOW('p', 21, struct rtc_time) - -#endif diff --git a/arch/sparc/include/asm/sbus.h b/arch/sparc/include/asm/sbus.h deleted file mode 100644 index f82481ab44d..00000000000 --- a/arch/sparc/include/asm/sbus.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef ___ASM_SPARC_SBUS_H -#define ___ASM_SPARC_SBUS_H -#if defined(__sparc__) && defined(__arch64__) -#include <asm/sbus_64.h> -#else -#include <asm/sbus_32.h> -#endif -#endif diff --git a/arch/sparc/include/asm/sbus_32.h b/arch/sparc/include/asm/sbus_32.h deleted file mode 100644 index a7b4fa21931..00000000000 --- a/arch/sparc/include/asm/sbus_32.h +++ /dev/null @@ -1,153 +0,0 @@ -/* - * sbus.h: Defines for the Sun SBus. - * - * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) - */ - -#ifndef _SPARC_SBUS_H -#define _SPARC_SBUS_H - -#include <linux/dma-mapping.h> -#include <linux/ioport.h> -#include <linux/of_device.h> - -#include <asm/oplib.h> -#include <asm/prom.h> -#include <asm/scatterlist.h> - -/* We scan which devices are on the SBus using the PROM node device - * tree. SBus devices are described in two different ways. You can - * either get an absolute address at which to access the device, or - * you can get a SBus 'slot' number and an offset within that slot. - */ - -/* The base address at which to calculate device OBIO addresses. */ -#define SUN_SBUS_BVADDR 0xf8000000 -#define SBUS_OFF_MASK 0x01ffffff - -/* These routines are used to calculate device address from slot - * numbers + offsets, and vice versa. - */ - -static inline unsigned long sbus_devaddr(int slotnum, unsigned long offset) -{ - return (unsigned long) (SUN_SBUS_BVADDR+((slotnum)<<25)+(offset)); -} - -static inline int sbus_dev_slot(unsigned long dev_addr) -{ - return (int) (((dev_addr)-SUN_SBUS_BVADDR)>>25); -} - -struct sbus_bus; - -/* Linux SBUS device tables */ -struct sbus_dev { - struct of_device ofdev; - struct sbus_bus *bus; - struct sbus_dev *next; - struct sbus_dev *child; - struct sbus_dev *parent; - int prom_node; - char prom_name[64]; - int slot; - - struct resource resource[PROMREG_MAX]; - - struct linux_prom_registers reg_addrs[PROMREG_MAX]; - int num_registers; - - struct linux_prom_ranges device_ranges[PROMREG_MAX]; - int num_device_ranges; - - unsigned int irqs[4]; - int num_irqs; -}; -#define to_sbus_device(d) container_of(d, struct sbus_dev, ofdev.dev) - -/* This struct describes the SBus(s) found on this machine. */ -struct sbus_bus { - struct of_device ofdev; - struct sbus_dev *devices; /* Link to devices on this SBus */ - struct sbus_bus *next; /* next SBus, if more than one SBus */ - int prom_node; /* PROM device tree node for this SBus */ - char prom_name[64]; /* Usually "sbus" or "sbi" */ - int clock_freq; - - struct linux_prom_ranges sbus_ranges[PROMREG_MAX]; - int num_sbus_ranges; - - int devid; - int board; -}; -#define to_sbus(d) container_of(d, struct sbus_bus, ofdev.dev) - -extern struct sbus_bus *sbus_root; - -static inline int -sbus_is_slave(struct sbus_dev *dev) -{ - /* XXX Have to write this for sun4c's */ - return 0; -} - -/* Device probing routines could find these handy */ -#define for_each_sbus(bus) \ - for((bus) = sbus_root; (bus); (bus)=(bus)->next) - -#define for_each_sbusdev(device, bus) \ - for((device) = (bus)->devices; (device); (device)=(device)->next) - -#define for_all_sbusdev(device, bus) \ - for ((bus) = sbus_root; (bus); (bus) = (bus)->next) \ - for ((device) = (bus)->devices; (device); (device) = (device)->next) - -/* Driver DVMA interfaces. */ -#define sbus_can_dma_64bit(sdev) (0) /* actually, sparc_cpu_model==sun4d */ -#define sbus_can_burst64(sdev) (0) /* actually, sparc_cpu_model==sun4d */ -extern void sbus_set_sbus64(struct sbus_dev *, int); -extern void sbus_fill_device_irq(struct sbus_dev *); - -/* These yield IOMMU mappings in consistent mode. */ -extern void *sbus_alloc_consistent(struct sbus_dev *, long, u32 *dma_addrp); -extern void sbus_free_consistent(struct sbus_dev *, long, void *, u32); -void prom_adjust_ranges(struct linux_prom_ranges *, int, - struct linux_prom_ranges *, int); - -#define SBUS_DMA_BIDIRECTIONAL DMA_BIDIRECTIONAL -#define SBUS_DMA_TODEVICE DMA_TO_DEVICE -#define SBUS_DMA_FROMDEVICE DMA_FROM_DEVICE -#define SBUS_DMA_NONE DMA_NONE - -/* All the rest use streaming mode mappings. */ -extern dma_addr_t sbus_map_single(struct sbus_dev *, void *, size_t, int); -extern void sbus_unmap_single(struct sbus_dev *, dma_addr_t, size_t, int); -extern int sbus_map_sg(struct sbus_dev *, struct scatterlist *, int, int); -extern void sbus_unmap_sg(struct sbus_dev *, struct scatterlist *, int, int); - -/* Finally, allow explicit synchronization of streamable mappings. */ -extern void sbus_dma_sync_single_for_cpu(struct sbus_dev *, dma_addr_t, size_t, int); -#define sbus_dma_sync_single sbus_dma_sync_single_for_cpu -extern void sbus_dma_sync_single_for_device(struct sbus_dev *, dma_addr_t, size_t, int); -extern void sbus_dma_sync_sg_for_cpu(struct sbus_dev *, struct scatterlist *, int, int); -#define sbus_dma_sync_sg sbus_dma_sync_sg_for_cpu -extern void sbus_dma_sync_sg_for_device(struct sbus_dev *, struct scatterlist *, int, int); - -/* Eric Brower (ebrower@usa.net) - * Translate SBus interrupt levels to ino values-- - * this is used when converting sbus "interrupts" OBP - * node values to "intr" node values, and is platform - * dependent. If only we could call OBP with - * "sbus-intr>cpu (sbint -- ino)" from kernel... - * See .../drivers/sbus/sbus.c for details. - */ -BTFIXUPDEF_CALL(unsigned int, sbint_to_irq, struct sbus_dev *sdev, unsigned int) -#define sbint_to_irq(sdev, sbint) BTFIXUP_CALL(sbint_to_irq)(sdev, sbint) - -extern void sbus_arch_bus_ranges_init(struct device_node *, struct sbus_bus *); -extern void sbus_setup_iommu(struct sbus_bus *, struct device_node *); -extern void sbus_setup_arch_props(struct sbus_bus *, struct device_node *); -extern int sbus_arch_preinit(void); -extern void sbus_arch_postinit(void); - -#endif /* !(_SPARC_SBUS_H) */ diff --git a/arch/sparc/include/asm/sbus_64.h b/arch/sparc/include/asm/sbus_64.h deleted file mode 100644 index b606c14343f..00000000000 --- a/arch/sparc/include/asm/sbus_64.h +++ /dev/null @@ -1,190 +0,0 @@ -/* sbus.h: Defines for the Sun SBus. - * - * Copyright (C) 1996, 1999, 2007 David S. Miller (davem@davemloft.net) - */ - -#ifndef _SPARC64_SBUS_H -#define _SPARC64_SBUS_H - -#include <linux/dma-mapping.h> -#include <linux/ioport.h> -#include <linux/of_device.h> - -#include <asm/oplib.h> -#include <asm/prom.h> -#include <asm/iommu.h> -#include <asm/scatterlist.h> - -/* We scan which devices are on the SBus using the PROM node device - * tree. SBus devices are described in two different ways. You can - * either get an absolute address at which to access the device, or - * you can get a SBus 'slot' number and an offset within that slot. - */ - -/* The base address at which to calculate device OBIO addresses. */ -#define SUN_SBUS_BVADDR 0x00000000 -#define SBUS_OFF_MASK 0x0fffffff - -/* These routines are used to calculate device address from slot - * numbers + offsets, and vice versa. - */ - -static inline unsigned long sbus_devaddr(int slotnum, unsigned long offset) -{ - return (unsigned long) (SUN_SBUS_BVADDR+((slotnum)<<28)+(offset)); -} - -static inline int sbus_dev_slot(unsigned long dev_addr) -{ - return (int) (((dev_addr)-SUN_SBUS_BVADDR)>>28); -} - -struct sbus_bus; - -/* Linux SBUS device tables */ -struct sbus_dev { - struct of_device ofdev; - struct sbus_bus *bus; - struct sbus_dev *next; - struct sbus_dev *child; - struct sbus_dev *parent; - int prom_node; - char prom_name[64]; - int slot; - - struct resource resource[PROMREG_MAX]; - - struct linux_prom_registers reg_addrs[PROMREG_MAX]; - int num_registers; - - struct linux_prom_ranges device_ranges[PROMREG_MAX]; - int num_device_ranges; - - unsigned int irqs[4]; - int num_irqs; -}; -#define to_sbus_device(d) container_of(d, struct sbus_dev, ofdev.dev) - -/* This struct describes the SBus(s) found on this machine. */ -struct sbus_bus { - struct of_device ofdev; - struct sbus_dev *devices; /* Tree of SBUS devices */ - struct sbus_bus *next; /* Next SBUS in system */ - int prom_node; /* OBP node of SBUS */ - char prom_name[64]; /* Usually "sbus" or "sbi" */ - int clock_freq; - - struct linux_prom_ranges sbus_ranges[PROMREG_MAX]; - int num_sbus_ranges; - - int portid; -}; -#define to_sbus(d) container_of(d, struct sbus_bus, ofdev.dev) - -extern struct sbus_bus *sbus_root; - -/* Device probing routines could find these handy */ -#define for_each_sbus(bus) \ - for((bus) = sbus_root; (bus); (bus)=(bus)->next) - -#define for_each_sbusdev(device, bus) \ - for((device) = (bus)->devices; (device); (device)=(device)->next) - -#define for_all_sbusdev(device, bus) \ - for ((bus) = sbus_root; (bus); (bus) = (bus)->next) \ - for ((device) = (bus)->devices; (device); (device) = (device)->next) - -/* Driver DVMA interfaces. */ -#define sbus_can_dma_64bit(sdev) (1) -#define sbus_can_burst64(sdev) (1) -extern void sbus_set_sbus64(struct sbus_dev *, int); -extern void sbus_fill_device_irq(struct sbus_dev *); - -static inline void *sbus_alloc_consistent(struct sbus_dev *sdev , size_t size, - dma_addr_t *dma_handle) -{ - return dma_alloc_coherent(&sdev->ofdev.dev, size, - dma_handle, GFP_ATOMIC); -} - -static inline void sbus_free_consistent(struct sbus_dev *sdev, size_t size, - void *vaddr, dma_addr_t dma_handle) -{ - return dma_free_coherent(&sdev->ofdev.dev, size, vaddr, dma_handle); -} - -#define SBUS_DMA_BIDIRECTIONAL DMA_BIDIRECTIONAL -#define SBUS_DMA_TODEVICE DMA_TO_DEVICE -#define SBUS_DMA_FROMDEVICE DMA_FROM_DEVICE -#define SBUS_DMA_NONE DMA_NONE - -/* All the rest use streaming mode mappings. */ -static inline dma_addr_t sbus_map_single(struct sbus_dev *sdev, void *ptr, - size_t size, int direction) -{ - return dma_map_single(&sdev->ofdev.dev, ptr, size, - (enum dma_data_direction) direction); -} - -static inline void sbus_unmap_single(struct sbus_dev *sdev, - dma_addr_t dma_addr, size_t size, - int direction) -{ - dma_unmap_single(&sdev->ofdev.dev, dma_addr, size, - (enum dma_data_direction) direction); -} - -static inline int sbus_map_sg(struct sbus_dev *sdev, struct scatterlist *sg, - int nents, int direction) -{ - return dma_map_sg(&sdev->ofdev.dev, sg, nents, - (enum dma_data_direction) direction); -} - -static inline void sbus_unmap_sg(struct sbus_dev *sdev, struct scatterlist *sg, - int nents, int direction) -{ - dma_unmap_sg(&sdev->ofdev.dev, sg, nents, - (enum dma_data_direction) direction); -} - -/* Finally, allow explicit synchronization of streamable mappings. */ -static inline void sbus_dma_sync_single_for_cpu(struct sbus_dev *sdev, - dma_addr_t dma_handle, - size_t size, int direction) -{ - dma_sync_single_for_cpu(&sdev->ofdev.dev, dma_handle, size, - (enum dma_data_direction) direction); -} -#define sbus_dma_sync_single sbus_dma_sync_single_for_cpu - -static inline void sbus_dma_sync_single_for_device(struct sbus_dev *sdev, - dma_addr_t dma_handle, - size_t size, int direction) -{ - /* No flushing needed to sync cpu writes to the device. */ -} - -static inline void sbus_dma_sync_sg_for_cpu(struct sbus_dev *sdev, - struct scatterlist *sg, - int nents, int direction) -{ - dma_sync_sg_for_cpu(&sdev->ofdev.dev, sg, nents, - (enum dma_data_direction) direction); -} -#define sbus_dma_sync_sg sbus_dma_sync_sg_for_cpu - -static inline void sbus_dma_sync_sg_for_device(struct sbus_dev *sdev, - struct scatterlist *sg, - int nents, int direction) -{ - /* No flushing needed to sync cpu writes to the device. */ -} - -extern void sbus_arch_bus_ranges_init(struct device_node *, struct sbus_bus *); -extern void sbus_setup_iommu(struct sbus_bus *, struct device_node *); -extern void sbus_setup_arch_props(struct sbus_bus *, struct device_node *); -extern int sbus_arch_preinit(void); -extern void sbus_arch_postinit(void); - -#endif /* !(_SPARC64_SBUS_H) */ diff --git a/arch/sparc/include/asm/serial.h b/arch/sparc/include/asm/serial.h new file mode 100644 index 00000000000..f90d61c2805 --- /dev/null +++ b/arch/sparc/include/asm/serial.h @@ -0,0 +1,6 @@ +#ifndef __SPARC_SERIAL_H +#define __SPARC_SERIAL_H + +#define BASE_BAUD ( 1843200 / 16 ) + +#endif /* __SPARC_SERIAL_H */ diff --git a/arch/sparc/include/asm/spinlock_32.h b/arch/sparc/include/asm/spinlock_32.h index de2249b267c..bf2d532593e 100644 --- a/arch/sparc/include/asm/spinlock_32.h +++ b/arch/sparc/include/asm/spinlock_32.h @@ -6,8 +6,6 @@ #ifndef __SPARC_SPINLOCK_H #define __SPARC_SPINLOCK_H -#include <linux/threads.h> /* For NR_CPUS */ - #ifndef __ASSEMBLY__ #include <asm/psr.h> diff --git a/arch/sparc/include/asm/spinlock_64.h b/arch/sparc/include/asm/spinlock_64.h index 0006fe9f8c7..120cfe4577c 100644 --- a/arch/sparc/include/asm/spinlock_64.h +++ b/arch/sparc/include/asm/spinlock_64.h @@ -6,8 +6,6 @@ #ifndef __SPARC64_SPINLOCK_H #define __SPARC64_SPINLOCK_H -#include <linux/threads.h> /* For NR_CPUS */ - #ifndef __ASSEMBLY__ /* To get debugging spinlocks which detect and catch diff --git a/arch/sparc/include/asm/sstate.h b/arch/sparc/include/asm/sstate.h deleted file mode 100644 index a7c35dbcb28..00000000000 --- a/arch/sparc/include/asm/sstate.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef _SPARC64_SSTATE_H -#define _SPARC64_SSTATE_H - -extern void sstate_booting(void); -extern void sstate_running(void); -extern void sstate_halt(void); -extern void sstate_poweroff(void); -extern void sstate_panic(void); -extern void sstate_reboot(void); - -extern void sun4v_sstate_init(void); - -#endif /* _SPARC64_SSTATE_H */ diff --git a/arch/sparc/include/asm/starfire.h b/arch/sparc/include/asm/starfire.h index 07bafd31e33..d56ce60a599 100644 --- a/arch/sparc/include/asm/starfire.h +++ b/arch/sparc/include/asm/starfire.h @@ -12,7 +12,6 @@ extern int this_is_starfire; extern void check_if_starfire(void); -extern void starfire_cpu_setup(void); extern int starfire_hard_smp_processor_id(void); extern void starfire_hookup(int); extern unsigned int starfire_translate(unsigned long imap, unsigned int upaid); diff --git a/arch/sparc/include/asm/statfs.h b/arch/sparc/include/asm/statfs.h index 5e937a73743..55e607ad461 100644 --- a/arch/sparc/include/asm/statfs.h +++ b/arch/sparc/include/asm/statfs.h @@ -1,8 +1,6 @@ #ifndef ___ASM_SPARC_STATFS_H #define ___ASM_SPARC_STATFS_H -#if defined(__sparc__) && defined(__arch64__) -#include <asm/statfs_64.h> -#else -#include <asm/statfs_32.h> -#endif + +#include <asm-generic/statfs.h> + #endif diff --git a/arch/sparc/include/asm/statfs_32.h b/arch/sparc/include/asm/statfs_32.h deleted file mode 100644 index 304520fa886..00000000000 --- a/arch/sparc/include/asm/statfs_32.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef _SPARC_STATFS_H -#define _SPARC_STATFS_H - -#include <asm-generic/statfs.h> - -#endif diff --git a/arch/sparc/include/asm/statfs_64.h b/arch/sparc/include/asm/statfs_64.h deleted file mode 100644 index 79b3c890a5f..00000000000 --- a/arch/sparc/include/asm/statfs_64.h +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef _SPARC64_STATFS_H -#define _SPARC64_STATFS_H - -#ifndef __KERNEL_STRICT_NAMES - -#include <linux/types.h> - -typedef __kernel_fsid_t fsid_t; - -#endif - -struct statfs { - long f_type; - long f_bsize; - long f_blocks; - long f_bfree; - long f_bavail; - long f_files; - long f_ffree; - __kernel_fsid_t f_fsid; - long f_namelen; - long f_frsize; - long f_spare[5]; -}; - -struct statfs64 { - long f_type; - long f_bsize; - long f_blocks; - long f_bfree; - long f_bavail; - long f_files; - long f_ffree; - __kernel_fsid_t f_fsid; - long f_namelen; - long f_frsize; - long f_spare[5]; -}; - -struct compat_statfs64 { - __u32 f_type; - __u32 f_bsize; - __u64 f_blocks; - __u64 f_bfree; - __u64 f_bavail; - __u64 f_files; - __u64 f_ffree; - __kernel_fsid_t f_fsid; - __u32 f_namelen; - __u32 f_frsize; - __u32 f_spare[5]; -}; - -#endif diff --git a/arch/sparc/include/asm/sun4paddr.h b/arch/sparc/include/asm/sun4paddr.h deleted file mode 100644 index d52985f19f4..00000000000 --- a/arch/sparc/include/asm/sun4paddr.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * sun4paddr.h: Various physical addresses on sun4 machines - * - * Copyright (C) 1997 Anton Blanchard (anton@progsoc.uts.edu.au) - * Copyright (C) 1998 Chris Davis (cdavis@cois.on.ca) - * - * Now supports more sun4's - */ - -#ifndef _SPARC_SUN4PADDR_H -#define _SPARC_SUN4PADDR_H - -#define SUN4_IE_PHYSADDR 0xf5000000 -#define SUN4_UNUSED_PHYSADDR 0 - -/* these work for me */ -#define SUN4_200_MEMREG_PHYSADDR 0xf4000000 -#define SUN4_200_CLOCK_PHYSADDR 0xf3000000 -#define SUN4_200_BWTWO_PHYSADDR 0xfd000000 -#define SUN4_200_ETH_PHYSADDR 0xf6000000 -#define SUN4_200_SI_PHYSADDR 0xff200000 - -/* these were here before */ -#define SUN4_300_MEMREG_PHYSADDR 0xf4000000 -#define SUN4_300_CLOCK_PHYSADDR 0xf2000000 -#define SUN4_300_TIMER_PHYSADDR 0xef000000 -#define SUN4_300_ETH_PHYSADDR 0xf9000000 -#define SUN4_300_BWTWO_PHYSADDR 0xfb400000 -#define SUN4_300_DMA_PHYSADDR 0xfa001000 -#define SUN4_300_ESP_PHYSADDR 0xfa000000 - -/* Are these right? */ -#define SUN4_400_MEMREG_PHYSADDR 0xf4000000 -#define SUN4_400_CLOCK_PHYSADDR 0xf2000000 -#define SUN4_400_TIMER_PHYSADDR 0xef000000 -#define SUN4_400_ETH_PHYSADDR 0xf9000000 -#define SUN4_400_BWTWO_PHYSADDR 0xfb400000 -#define SUN4_400_DMA_PHYSADDR 0xfa001000 -#define SUN4_400_ESP_PHYSADDR 0xfa000000 - -/* - these are the actual values set and used in the code. Unused items set - to SUN_UNUSED_PHYSADDR - */ - -extern int sun4_memreg_physaddr; /* memory register (ecc?) */ -extern int sun4_clock_physaddr; /* system clock */ -extern int sun4_timer_physaddr; /* timer, where applicable */ -extern int sun4_eth_physaddr; /* onboard ethernet (ie/le) */ -extern int sun4_si_physaddr; /* sun3 scsi adapter */ -extern int sun4_bwtwo_physaddr; /* onboard bw2 */ -extern int sun4_dma_physaddr; /* scsi dma */ -extern int sun4_esp_physaddr; /* esp scsi */ -extern int sun4_ie_physaddr; /* interrupt enable */ - -#endif /* !(_SPARC_SUN4PADDR_H) */ diff --git a/arch/sparc/include/asm/sun4prom.h b/arch/sparc/include/asm/sun4prom.h deleted file mode 100644 index 9c8b4cbf629..00000000000 --- a/arch/sparc/include/asm/sun4prom.h +++ /dev/null @@ -1,83 +0,0 @@ -/* - * sun4prom.h -- interface to sun4 PROM monitor. We don't use most of this, - * so most of these are just placeholders. - */ - -#ifndef _SUN4PROM_H_ -#define _SUN4PROM_H_ - -/* - * Although this looks similar to an romvec for a OpenProm machine, it is - * actually closer to what was used in the Sun2 and Sun3. - * - * V2 entries exist only in version 2 PROMs and later, V3 in version 3 and later. - * - * Many of the function prototypes are guesses. Some are certainly wrong. - * Use with care. - */ - -typedef struct { - char *initSP; /* Initial system stack ptr */ - void (*startmon)(void); /* Initial PC for hardware */ - int *diagberr; /* Bus err handler for diags */ - struct linux_arguments_v0 **bootParam; /* Info for bootstrapped pgm */ - unsigned int *memorysize; /* Usable memory in bytes */ - unsigned char (*getchar)(void); /* Get char from input device */ - void (*putchar)(char); /* Put char to output device */ - int (*mayget)(void); /* Maybe get char, or -1 */ - int (*mayput)(int); /* Maybe put char, or -1 */ - unsigned char *echo; /* Should getchar echo? */ - unsigned char *insource; /* Input source selector */ - unsigned char *outsink; /* Output sink selector */ - int (*getkey)(void); /* Get next key if one exists */ - void (*initgetkey)(void); /* Initialize get key */ - unsigned int *translation; /* Kbd translation selector */ - unsigned char *keybid; /* Keyboard ID byte */ - int *screen_x; /* V2: Screen x pos (r/o) */ - int *screen_y; /* V2: Screen y pos (r/o) */ - struct keybuf *keybuf; /* Up/down keycode buffer */ - char *monid; /* Monitor version ID */ - void (*fbwritechar)(char); /* Write a character to FB */ - int *fbAddr; /* Address of frame buffer */ - char **font; /* Font table for FB */ - void (*fbwritestr)(char *); /* Write string to FB */ - void (*reboot)(char *); /* e.g. reboot("sd()vmlinux") */ - unsigned char *linebuf; /* The line input buffer */ - unsigned char **lineptr; /* Cur pointer into linebuf */ - int *linesize; /* length of line in linebuf */ - void (*getline)(char *); /* Get line from user */ - unsigned char (*getnextchar)(void); /* Get next char from linebuf */ - unsigned char (*peeknextchar)(void); /* Peek at next char */ - int *fbthere; /* =1 if frame buffer there */ - int (*getnum)(void); /* Grab hex num from line */ - int (*printf)(char *, ...); /* See prom_printf() instead */ - void (*printhex)(int); /* Format N digits in hex */ - unsigned char *leds; /* RAM copy of LED register */ - void (*setLEDs)(unsigned char *); /* Sets LED's and RAM copy */ - void (*NMIaddr)(void *); /* Addr for level 7 vector */ - void (*abortentry)(void); /* Entry for keyboard abort */ - int *nmiclock; /* Counts up in msec */ - int *FBtype; /* Frame buffer type */ - unsigned int romvecversion; /* Version number for this romvec */ - struct globram *globram; /* monitor global variables ??? */ - void * kbdaddr; /* Addr of keyboard in use */ - int *keyrinit; /* ms before kbd repeat */ - unsigned char *keyrtick; /* ms between repetitions */ - unsigned int *memoryavail; /* V1: Main mem usable size */ - long *resetaddr; /* where to jump on a reset */ - long *resetmap; /* pgmap entry for resetaddr */ - void (*exittomon)(void); /* Exit from user program */ - unsigned char **memorybitmap; /* V1: &{0 or &bits} */ - void (*setcxsegmap)(int ctxt, char *va, int pmeg); /* Set seg in any context */ - void (**vector_cmd)(void *); /* V2: Handler for 'v' cmd */ - unsigned long *expectedtrapsig; /* V3: Location of the expected trap signal */ - unsigned long *trapvectorbasetable; /* V3: Address of the trap vector table */ - int unused1; - int unused2; - int unused3; - int unused4; -} linux_sun4_romvec; - -extern linux_sun4_romvec *sun4_romvec; - -#endif /* _SUN4PROM_H_ */ diff --git a/arch/sparc/include/asm/system_32.h b/arch/sparc/include/asm/system_32.h index b4b024445fc..8623fc48fe2 100644 --- a/arch/sparc/include/asm/system_32.h +++ b/arch/sparc/include/asm/system_32.h @@ -34,13 +34,7 @@ enum sparc_cpu { extern enum sparc_cpu sparc_cpu_model; -#ifndef CONFIG_SUN4 -#define ARCH_SUN4C_SUN4 (sparc_cpu_model==sun4c) -#define ARCH_SUN4 0 -#else -#define ARCH_SUN4C_SUN4 1 -#define ARCH_SUN4 1 -#endif +#define ARCH_SUN4C (sparc_cpu_model==sun4c) #define SUN4M_NCPUS 4 /* Architectural limit of sun4m. */ @@ -55,6 +49,7 @@ extern unsigned long empty_zero_page; extern void sun_do_break(void); extern int serial_console; extern int stop_a_enabled; +extern int scons_pwroff; static inline int con_is_present(void) { diff --git a/arch/sparc/include/asm/system_64.h b/arch/sparc/include/asm/system_64.h index db9e742a406..8759f2a1b83 100644 --- a/arch/sparc/include/asm/system_64.h +++ b/arch/sparc/include/asm/system_64.h @@ -26,9 +26,8 @@ enum sparc_cpu { #define sparc_cpu_model sun4u -/* This cannot ever be a sun4c nor sun4 :) That's just history. */ -#define ARCH_SUN4C_SUN4 0 -#define ARCH_SUN4 0 +/* This cannot ever be a sun4c :) That's just history. */ +#define ARCH_SUN4C 0 extern char reboot_command[]; @@ -118,6 +117,7 @@ do { __asm__ __volatile__("ba,pt %%xcc, 1f\n\t" \ extern void sun_do_break(void); extern int stop_a_enabled; +extern int scons_pwroff; extern void fault_in_user_windows(void); extern void synchronize_user_stack(void); diff --git a/arch/sparc/include/asm/thread_info_32.h b/arch/sparc/include/asm/thread_info_32.h index cbb892d0dff..29899fd5b1b 100644 --- a/arch/sparc/include/asm/thread_info_32.h +++ b/arch/sparc/include/asm/thread_info_32.h @@ -80,11 +80,7 @@ register struct thread_info *current_thread_info_reg asm("g6"); /* * thread information allocation */ -#if PAGE_SHIFT == 13 -#define THREAD_INFO_ORDER 0 -#else /* PAGE_SHIFT */ #define THREAD_INFO_ORDER 1 -#endif #define __HAVE_ARCH_THREAD_INFO_ALLOCATOR diff --git a/arch/sparc/include/asm/timer_32.h b/arch/sparc/include/asm/timer_32.h index 361e53898dd..2ec030ef381 100644 --- a/arch/sparc/include/asm/timer_32.h +++ b/arch/sparc/include/asm/timer_32.h @@ -9,96 +9,9 @@ #define _SPARC_TIMER_H #include <asm/system.h> /* For SUN4M_NCPUS */ -#include <asm/sun4paddr.h> #include <asm/btfixup.h> -/* Timer structures. The interrupt timer has two properties which - * are the counter (which is handled in do_timer in sched.c) and the limit. - * This limit is where the timer's counter 'wraps' around. Oddly enough, - * the sun4c timer when it hits the limit wraps back to 1 and not zero - * thus when calculating the value at which it will fire a microsecond you - * must adjust by one. Thanks SUN for designing such great hardware ;( - */ - -/* Note that I am only going to use the timer that interrupts at - * Sparc IRQ 10. There is another one available that can fire at - * IRQ 14. Currently it is left untouched, we keep the PROM's limit - * register value and let the prom take these interrupts. This allows - * L1-A to work. - */ - -struct sun4c_timer_info { - __volatile__ unsigned int cur_count10; - __volatile__ unsigned int timer_limit10; - __volatile__ unsigned int cur_count14; - __volatile__ unsigned int timer_limit14; -}; - -#define SUN4C_TIMER_PHYSADDR 0xf3000000 -#ifdef CONFIG_SUN4 -#define SUN_TIMER_PHYSADDR SUN4_300_TIMER_PHYSADDR -#else -#define SUN_TIMER_PHYSADDR SUN4C_TIMER_PHYSADDR -#endif - -/* A sun4m has two blocks of registers which are probably of the same - * structure. LSI Logic's L64851 is told to _decrement_ from the limit - * value. Aurora behaves similarly but its limit value is compacted in - * other fashion (it's wider). Documented fields are defined here. - */ - -/* As with the interrupt register, we have two classes of timer registers - * which are per-cpu and master. Per-cpu timers only hit that cpu and are - * only level 14 ticks, master timer hits all cpus and is level 10. - */ - -#define SUN4M_PRM_CNT_L 0x80000000 -#define SUN4M_PRM_CNT_LVALUE 0x7FFFFC00 - -struct sun4m_timer_percpu_info { - __volatile__ unsigned int l14_timer_limit; /* Initial value is 0x009c4000 */ - __volatile__ unsigned int l14_cur_count; - - /* This register appears to be write only and/or inaccessible - * on Uni-Processor sun4m machines. - */ - __volatile__ unsigned int l14_limit_noclear; /* Data access error is here */ - - __volatile__ unsigned int cntrl; /* =1 after POST on Aurora */ - __volatile__ unsigned char space[PAGE_SIZE - 16]; -}; - -struct sun4m_timer_regs { - struct sun4m_timer_percpu_info cpu_timers[SUN4M_NCPUS]; - volatile unsigned int l10_timer_limit; - volatile unsigned int l10_cur_count; - - /* Again, this appears to be write only and/or inaccessible - * on uni-processor sun4m machines. - */ - volatile unsigned int l10_limit_noclear; - - /* This register too, it must be magic. */ - volatile unsigned int foobar; - - volatile unsigned int cfg; /* equals zero at boot time... */ -}; - -#define SUN4D_PRM_CNT_L 0x80000000 -#define SUN4D_PRM_CNT_LVALUE 0x7FFFFC00 - -struct sun4d_timer_regs { - volatile unsigned int l10_timer_limit; - volatile unsigned int l10_cur_countx; - volatile unsigned int l10_limit_noclear; - volatile unsigned int ctrl; - volatile unsigned int l10_cur_count; -}; - -extern struct sun4d_timer_regs *sun4d_timers; - extern __volatile__ unsigned int *master_l10_counter; -extern __volatile__ unsigned int *master_l10_limit; /* FIXME: Make do_[gs]ettimeofday btfixup calls */ BTFIXUPDEF_CALL(int, bus_do_settimeofday, struct timespec *tv) diff --git a/arch/sparc/include/asm/vac-ops.h b/arch/sparc/include/asm/vac-ops.h index d10527611f1..a63e88ef042 100644 --- a/arch/sparc/include/asm/vac-ops.h +++ b/arch/sparc/include/asm/vac-ops.h @@ -76,11 +76,7 @@ * cacheable bit in the pte's of all such pages. */ -#ifdef CONFIG_SUN4 -#define S4CVAC_BADBITS 0x0001e000 -#else #define S4CVAC_BADBITS 0x0000f000 -#endif /* The following is true if vaddr1 and vaddr2 would cause * a 'bad alias'. @@ -94,10 +90,7 @@ */ struct sun4c_vac_props { unsigned int num_bytes; /* Size of the cache */ - unsigned int num_lines; /* Number of cache lines */ unsigned int do_hwflushes; /* Hardware flushing available? */ - enum { VAC_NONE, VAC_WRITE_THROUGH, - VAC_WRITE_BACK } type; /* What type of VAC? */ unsigned int linesize; /* Size of each line in bytes */ unsigned int log2lsize; /* log2(linesize) */ unsigned int on; /* VAC is enabled */ diff --git a/arch/sparc/include/asm/vfc_ioctls.h b/arch/sparc/include/asm/vfc_ioctls.h deleted file mode 100644 index af8b69007b2..00000000000 --- a/arch/sparc/include/asm/vfc_ioctls.h +++ /dev/null @@ -1,58 +0,0 @@ -/* Copyright (c) 1996 by Manish Vachharajani */ - -#ifndef _LINUX_VFC_IOCTLS_H_ -#define _LINUX_VFC_IOCTLS_H_ - - /* IOCTLs */ -#define VFC_IOCTL(a) (('j' << 8) | a) -#define VFCGCTRL (VFC_IOCTL (0)) /* get vfc attributes */ -#define VFCSCTRL (VFC_IOCTL (1)) /* set vfc attributes */ -#define VFCGVID (VFC_IOCTL (2)) /* get video decoder attributes */ -#define VFCSVID (VFC_IOCTL (3)) /* set video decoder attributes */ -#define VFCHUE (VFC_IOCTL (4)) /* set hue */ -#define VFCPORTCHG (VFC_IOCTL (5)) /* change port */ -#define VFCRDINFO (VFC_IOCTL (6)) /* read info */ - - /* Options for setting the vfc attributes and status */ -#define MEMPRST 0x1 /* reset FIFO ptr. */ -#define CAPTRCMD 0x2 /* start capture and wait */ -#define DIAGMODE 0x3 /* diag mode */ -#define NORMMODE 0x4 /* normal mode */ -#define CAPTRSTR 0x5 /* start capture */ -#define CAPTRWAIT 0x6 /* wait for capture to finish */ - - - /* Options for the decoder */ -#define STD_NTSC 0x1 /* NTSC mode */ -#define STD_PAL 0x2 /* PAL mode */ -#define COLOR_ON 0x3 /* force color ON */ -#define MONO 0x4 /* force color OFF */ - - /* Values returned by ioctl 2 */ - -#define NO_LOCK 1 -#define NTSC_COLOR 2 -#define NTSC_NOCOLOR 3 -#define PAL_COLOR 4 -#define PAL_NOCOLOR 5 - -/* Not too sure what this does yet */ - /* Options for setting Field number */ -#define ODD_FIELD 0x1 -#define EVEN_FIELD 0x0 -#define ACTIVE_ONLY 0x2 -#define NON_ACTIVE 0x0 - -/* Debug options */ -#define VFC_I2C_SEND 0 -#define VFC_I2C_RECV 1 - -struct vfc_debug_inout -{ - unsigned long addr; - unsigned long ret; - unsigned long len; - unsigned char __user *buffer; -}; - -#endif /* _LINUX_VFC_IOCTLS_H_ */ diff --git a/arch/sparc/include/asm/visasm.h b/arch/sparc/include/asm/visasm.h index de797b9bf55..39ca301920d 100644 --- a/arch/sparc/include/asm/visasm.h +++ b/arch/sparc/include/asm/visasm.h @@ -57,6 +57,7 @@ static inline void save_and_clear_fpu(void) { " " : : "i" (FPRS_FEF|FPRS_DU) : "o5", "g1", "g2", "g3", "g7", "cc"); } +extern int vis_emul(struct pt_regs *, unsigned int); #endif #endif /* _SPARC64_ASI_H */ diff --git a/arch/sparc/kernel/Makefile b/arch/sparc/kernel/Makefile index 6e03a2a7863..2d658209509 100644 --- a/arch/sparc/kernel/Makefile +++ b/arch/sparc/kernel/Makefile @@ -13,15 +13,13 @@ obj-y := entry.o wof.o wuf.o etrap.o rtrap.o traps.o $(IRQ_OBJS) \ time.o windows.o cpu.o devices.o \ tadpole.o tick14.o ptrace.o \ unaligned.o una_asm.o muldiv.o \ - prom.o of_device.o devres.o + prom.o of_device.o devres.o dma.o devres-y = ../../../kernel/irq/devres.o obj-$(CONFIG_PCI) += pcic.o -obj-$(CONFIG_SUN4) += sun4setup.o obj-$(CONFIG_SMP) += trampoline.o smp.o sun4m_smp.o sun4d_smp.o obj-$(CONFIG_SUN_AUXIO) += auxio.o -obj-$(CONFIG_PCI) += ebus.o obj-$(CONFIG_SUN_PM) += apc.o pmc.o obj-$(CONFIG_MODULES) += module.o sparc_ksyms.o obj-$(CONFIG_SPARC_LED) += led.o diff --git a/arch/sparc/kernel/apc.c b/arch/sparc/kernel/apc.c index 5267d48fb2c..4dd1ba752ce 100644 --- a/arch/sparc/kernel/apc.c +++ b/arch/sparc/kernel/apc.c @@ -12,9 +12,10 @@ #include <linux/miscdevice.h> #include <linux/smp_lock.h> #include <linux/pm.h> +#include <linux/of.h> +#include <linux/of_device.h> #include <asm/io.h> -#include <asm/sbus.h> #include <asm/oplib.h> #include <asm/uaccess.h> #include <asm/auxio.h> @@ -29,11 +30,10 @@ #define APC_OBPNAME "power-management" #define APC_DEVNAME "apc" -volatile static u8 __iomem *regs; -static int apc_regsize; +static u8 __iomem *regs; static int apc_no_idle __initdata = 0; -#define apc_readb(offs) (sbus_readb(regs+offs)) +#define apc_readb(offs) (sbus_readb(regs+offs)) #define apc_writeb(val, offs) (sbus_writeb(val, regs+offs)) /* Specify "apc=noidle" on the kernel command line to @@ -69,9 +69,9 @@ static void apc_swift_idle(void) #endif } -static inline void apc_free(void) +static inline void apc_free(struct of_device *op) { - sbus_iounmap(regs, apc_regsize); + of_iounmap(&op->resource[0], regs, resource_size(&op->resource[0])); } static int apc_open(struct inode *inode, struct file *f) @@ -153,52 +153,56 @@ static const struct file_operations apc_fops = { static struct miscdevice apc_miscdev = { APC_MINOR, APC_DEVNAME, &apc_fops }; -static int __init apc_probe(void) +static int __devinit apc_probe(struct of_device *op, + const struct of_device_id *match) { - struct sbus_bus *sbus = NULL; - struct sbus_dev *sdev = NULL; - int iTmp = 0; - - for_each_sbus(sbus) { - for_each_sbusdev(sdev, sbus) { - if (!strcmp(sdev->prom_name, APC_OBPNAME)) { - goto sbus_done; - } - } - } + int err; -sbus_done: - if (!sdev) { - return -ENODEV; - } - - apc_regsize = sdev->reg_addrs[0].reg_size; - regs = sbus_ioremap(&sdev->resource[0], 0, - apc_regsize, APC_OBPNAME); - if(!regs) { + regs = of_ioremap(&op->resource[0], 0, + resource_size(&op->resource[0]), APC_OBPNAME); + if (!regs) { printk(KERN_ERR "%s: unable to map registers\n", APC_DEVNAME); return -ENODEV; } - iTmp = misc_register(&apc_miscdev); - if (iTmp != 0) { + err = misc_register(&apc_miscdev); + if (err) { printk(KERN_ERR "%s: unable to register device\n", APC_DEVNAME); - apc_free(); + apc_free(op); return -ENODEV; } /* Assign power management IDLE handler */ - if(!apc_no_idle) + if (!apc_no_idle) pm_idle = apc_swift_idle; printk(KERN_INFO "%s: power management initialized%s\n", - APC_DEVNAME, apc_no_idle ? " (CPU idle disabled)" : ""); + APC_DEVNAME, apc_no_idle ? " (CPU idle disabled)" : ""); + return 0; } +static struct of_device_id __initdata apc_match[] = { + { + .name = APC_OBPNAME, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, apc_match); + +static struct of_platform_driver apc_driver = { + .name = "apc", + .match_table = apc_match, + .probe = apc_probe, +}; + +static int __init apc_init(void) +{ + return of_register_driver(&apc_driver, &of_bus_type); +} + /* This driver is not critical to the boot process * and is easiest to ioremap when SBus is already * initialized, so we install ourselves thusly: */ -__initcall(apc_probe); - +__initcall(apc_init); diff --git a/arch/sparc/kernel/auxio.c b/arch/sparc/kernel/auxio.c index baf4ed3fb0f..09c857215a5 100644 --- a/arch/sparc/kernel/auxio.c +++ b/arch/sparc/kernel/auxio.c @@ -6,6 +6,8 @@ #include <linux/stddef.h> #include <linux/init.h> #include <linux/spinlock.h> +#include <linux/of.h> +#include <linux/of_device.h> #include <asm/oplib.h> #include <asm/io.h> #include <asm/auxio.h> @@ -59,7 +61,7 @@ void __init auxio_probe(void) r.flags = auxregs[0].which_io & 0xF; r.start = auxregs[0].phys_addr; r.end = auxregs[0].phys_addr + auxregs[0].reg_size - 1; - auxio_register = sbus_ioremap(&r, 0, auxregs[0].reg_size, "auxio"); + auxio_register = of_ioremap(&r, 0, auxregs[0].reg_size, "auxio"); /* Fix the address on sun4m and sun4c. */ if((((unsigned long) auxregs[0].phys_addr) & 3) == 3 || sparc_cpu_model == sun4c) @@ -128,7 +130,7 @@ void __init auxio_power_probe(void) r.flags = regs.which_io & 0xF; r.start = regs.phys_addr; r.end = regs.phys_addr + regs.reg_size - 1; - auxio_power_register = (unsigned char *) sbus_ioremap(&r, 0, + auxio_power_register = (unsigned char *) of_ioremap(&r, 0, regs.reg_size, "auxpower"); /* Display a quick message on the console. */ diff --git a/arch/sparc/kernel/devices.c b/arch/sparc/kernel/devices.c index b240b8863fd..ad656b044b8 100644 --- a/arch/sparc/kernel/devices.c +++ b/arch/sparc/kernel/devices.c @@ -143,7 +143,7 @@ void __init device_scan(void) #endif clock_stop_probe(); - if (ARCH_SUN4C_SUN4) + if (ARCH_SUN4C) sun4c_probe_memerr_reg(); return; diff --git a/arch/sparc/kernel/dma.c b/arch/sparc/kernel/dma.c new file mode 100644 index 00000000000..ebc8403b035 --- /dev/null +++ b/arch/sparc/kernel/dma.c @@ -0,0 +1,227 @@ +/* dma.c: PCI and SBUS DMA accessors for 32-bit sparc. + * + * Copyright (C) 2008 David S. Miller <davem@davemloft.net> + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/dma-mapping.h> +#include <linux/scatterlist.h> +#include <linux/mm.h> + +#ifdef CONFIG_PCI +#include <linux/pci.h> +#endif + +#include "dma.h" + +int dma_supported(struct device *dev, u64 mask) +{ +#ifdef CONFIG_PCI + if (dev->bus == &pci_bus_type) + return pci_dma_supported(to_pci_dev(dev), mask); +#endif + return 0; +} +EXPORT_SYMBOL(dma_supported); + +int dma_set_mask(struct device *dev, u64 dma_mask) +{ +#ifdef CONFIG_PCI + if (dev->bus == &pci_bus_type) + return pci_set_dma_mask(to_pci_dev(dev), dma_mask); +#endif + return -EOPNOTSUPP; +} +EXPORT_SYMBOL(dma_set_mask); + +void *dma_alloc_coherent(struct device *dev, size_t size, + dma_addr_t *dma_handle, gfp_t flag) +{ +#ifdef CONFIG_PCI + if (dev->bus == &pci_bus_type) + return pci_alloc_consistent(to_pci_dev(dev), size, dma_handle); +#endif + return sbus_alloc_consistent(dev, size, dma_handle); +} +EXPORT_SYMBOL(dma_alloc_coherent); + +void dma_free_coherent(struct device *dev, size_t size, + void *cpu_addr, dma_addr_t dma_handle) +{ +#ifdef CONFIG_PCI + if (dev->bus == &pci_bus_type) { + pci_free_consistent(to_pci_dev(dev), size, + cpu_addr, dma_handle); + return; + } +#endif + sbus_free_consistent(dev, size, cpu_addr, dma_handle); +} +EXPORT_SYMBOL(dma_free_coherent); + +dma_addr_t dma_map_single(struct device *dev, void *cpu_addr, + size_t size, enum dma_data_direction direction) +{ +#ifdef CONFIG_PCI + if (dev->bus == &pci_bus_type) + return pci_map_single(to_pci_dev(dev), cpu_addr, + size, (int)direction); +#endif + return sbus_map_single(dev, cpu_addr, size, (int)direction); +} +EXPORT_SYMBOL(dma_map_single); + +void dma_unmap_single(struct device *dev, dma_addr_t dma_addr, + size_t size, + enum dma_data_direction direction) +{ +#ifdef CONFIG_PCI + if (dev->bus == &pci_bus_type) { + pci_unmap_single(to_pci_dev(dev), dma_addr, + size, (int)direction); + return; + } +#endif + sbus_unmap_single(dev, dma_addr, size, (int)direction); +} +EXPORT_SYMBOL(dma_unmap_single); + +dma_addr_t dma_map_page(struct device *dev, struct page *page, + unsigned long offset, size_t size, + enum dma_data_direction direction) +{ +#ifdef CONFIG_PCI + if (dev->bus == &pci_bus_type) + return pci_map_page(to_pci_dev(dev), page, offset, + size, (int)direction); +#endif + return sbus_map_single(dev, page_address(page) + offset, + size, (int)direction); +} +EXPORT_SYMBOL(dma_map_page); + +void dma_unmap_page(struct device *dev, dma_addr_t dma_address, + size_t size, enum dma_data_direction direction) +{ +#ifdef CONFIG_PCI + if (dev->bus == &pci_bus_type) { + pci_unmap_page(to_pci_dev(dev), dma_address, + size, (int)direction); + return; + } +#endif + sbus_unmap_single(dev, dma_address, size, (int)direction); +} +EXPORT_SYMBOL(dma_unmap_page); + +int dma_map_sg(struct device *dev, struct scatterlist *sg, + int nents, enum dma_data_direction direction) +{ +#ifdef CONFIG_PCI + if (dev->bus == &pci_bus_type) + return pci_map_sg(to_pci_dev(dev), sg, nents, (int)direction); +#endif + return sbus_map_sg(dev, sg, nents, direction); +} +EXPORT_SYMBOL(dma_map_sg); + +void dma_unmap_sg(struct device *dev, struct scatterlist *sg, + int nents, enum dma_data_direction direction) +{ +#ifdef CONFIG_PCI + if (dev->bus == &pci_bus_type) { + pci_unmap_sg(to_pci_dev(dev), sg, nents, (int)direction); + return; + } +#endif + sbus_unmap_sg(dev, sg, nents, (int)direction); +} +EXPORT_SYMBOL(dma_unmap_sg); + +void dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle, + size_t size, enum dma_data_direction direction) +{ +#ifdef CONFIG_PCI + if (dev->bus == &pci_bus_type) { + pci_dma_sync_single_for_cpu(to_pci_dev(dev), dma_handle, + size, (int)direction); + return; + } +#endif + sbus_dma_sync_single_for_cpu(dev, dma_handle, size, (int) direction); +} +EXPORT_SYMBOL(dma_sync_single_for_cpu); + +void dma_sync_single_for_device(struct device *dev, dma_addr_t dma_handle, + size_t size, enum dma_data_direction direction) +{ +#ifdef CONFIG_PCI + if (dev->bus == &pci_bus_type) { + pci_dma_sync_single_for_device(to_pci_dev(dev), dma_handle, + size, (int)direction); + return; + } +#endif + sbus_dma_sync_single_for_device(dev, dma_handle, size, (int) direction); +} +EXPORT_SYMBOL(dma_sync_single_for_device); + +void dma_sync_single_range_for_cpu(struct device *dev, + dma_addr_t dma_handle, + unsigned long offset, + size_t size, + enum dma_data_direction direction) +{ + dma_sync_single_for_cpu(dev, dma_handle+offset, size, direction); +} +EXPORT_SYMBOL(dma_sync_single_range_for_cpu); + +void dma_sync_single_range_for_device(struct device *dev, dma_addr_t dma_handle, + unsigned long offset, size_t size, + enum dma_data_direction direction) +{ + dma_sync_single_for_device(dev, dma_handle+offset, size, direction); +} +EXPORT_SYMBOL(dma_sync_single_range_for_device); + +void dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, + int nelems, enum dma_data_direction direction) +{ +#ifdef CONFIG_PCI + if (dev->bus == &pci_bus_type) { + pci_dma_sync_sg_for_cpu(to_pci_dev(dev), sg, + nelems, (int)direction); + return; + } +#endif + BUG(); +} +EXPORT_SYMBOL(dma_sync_sg_for_cpu); + +void dma_sync_sg_for_device(struct device *dev, + struct scatterlist *sg, int nelems, + enum dma_data_direction direction) +{ +#ifdef CONFIG_PCI + if (dev->bus == &pci_bus_type) { + pci_dma_sync_sg_for_device(to_pci_dev(dev), sg, + nelems, (int)direction); + return; + } +#endif + BUG(); +} +EXPORT_SYMBOL(dma_sync_sg_for_device); + +int dma_mapping_error(struct device *dev, dma_addr_t dma_addr) +{ + return (dma_addr == DMA_ERROR_CODE); +} +EXPORT_SYMBOL(dma_mapping_error); + +int dma_get_cache_alignment(void) +{ + return 32; +} +EXPORT_SYMBOL(dma_get_cache_alignment); diff --git a/arch/sparc/kernel/dma.h b/arch/sparc/kernel/dma.h new file mode 100644 index 00000000000..f8d8951adb5 --- /dev/null +++ b/arch/sparc/kernel/dma.h @@ -0,0 +1,14 @@ +void *sbus_alloc_consistent(struct device *dev, long len, u32 *dma_addrp); +void sbus_free_consistent(struct device *dev, long n, void *p, u32 ba); +dma_addr_t sbus_map_single(struct device *dev, void *va, + size_t len, int direction); +void sbus_unmap_single(struct device *dev, dma_addr_t ba, + size_t n, int direction); +int sbus_map_sg(struct device *dev, struct scatterlist *sg, + int n, int direction); +void sbus_unmap_sg(struct device *dev, struct scatterlist *sg, + int n, int direction); +void sbus_dma_sync_single_for_cpu(struct device *dev, dma_addr_t ba, + size_t size, int direction); +void sbus_dma_sync_single_for_device(struct device *dev, dma_addr_t ba, + size_t size, int direction); diff --git a/arch/sparc/kernel/ebus.c b/arch/sparc/kernel/ebus.c deleted file mode 100644 index 97294232259..00000000000 --- a/arch/sparc/kernel/ebus.c +++ /dev/null @@ -1,393 +0,0 @@ -/* - * ebus.c: PCI to EBus bridge device. - * - * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) - * - * Adopted for sparc by V. Roganov and G. Raiko. - * Fixes for different platforms by Pete Zaitcev. - */ - -#include <linux/kernel.h> -#include <linux/types.h> -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/string.h> - -#include <asm/system.h> -#include <asm/page.h> -#include <asm/pbm.h> -#include <asm/ebus.h> -#include <asm/io.h> -#include <asm/oplib.h> -#include <asm/prom.h> -#include <asm/bpp.h> - -struct linux_ebus *ebus_chain = NULL; - -/* We are together with pcic.c under CONFIG_PCI. */ -extern unsigned int pcic_pin_to_irq(unsigned int, const char *name); - -/* - * IRQ Blacklist - * Here we list PROMs and systems that are known to supply crap as IRQ numbers. - */ -struct ebus_device_irq { - char *name; - unsigned int pin; -}; - -struct ebus_system_entry { - char *esname; - struct ebus_device_irq *ipt; -}; - -static struct ebus_device_irq je1_1[] = { - { "8042", 3 }, - { "SUNW,CS4231", 0 }, - { "parallel", 0 }, - { "se", 2 }, - { NULL, 0 } -}; - -/* - * Gleb's JE1 supplied reasonable pin numbers, but mine did not (OBP 2.32). - * Blacklist the sucker... Note that Gleb's system will work. - */ -static struct ebus_system_entry ebus_blacklist[] = { - { "SUNW,JavaEngine1", je1_1 }, - { NULL, NULL } -}; - -static struct ebus_device_irq *ebus_blackp = NULL; - -/* - */ -static inline unsigned long ebus_alloc(size_t size) -{ - return (unsigned long)kmalloc(size, GFP_ATOMIC); -} - -/* - */ -static int __init ebus_blacklist_irq(const char *name) -{ - struct ebus_device_irq *dp; - - if ((dp = ebus_blackp) != NULL) { - for (; dp->name != NULL; dp++) { - if (strcmp(name, dp->name) == 0) { - return pcic_pin_to_irq(dp->pin, name); - } - } - } - return 0; -} - -static void __init fill_ebus_child(struct device_node *dp, - struct linux_ebus_child *dev) -{ - const int *regs; - const int *irqs; - int i, len; - - dev->prom_node = dp; - regs = of_get_property(dp, "reg", &len); - if (!regs) - len = 0; - dev->num_addrs = len / sizeof(regs[0]); - - for (i = 0; i < dev->num_addrs; i++) { - if (regs[i] >= dev->parent->num_addrs) { - prom_printf("UGH: property for %s was %d, need < %d\n", - dev->prom_node->name, len, - dev->parent->num_addrs); - panic(__func__); - } - - /* XXX resource */ - dev->resource[i].start = - dev->parent->resource[regs[i]].start; - } - - for (i = 0; i < PROMINTR_MAX; i++) - dev->irqs[i] = PCI_IRQ_NONE; - - if ((dev->irqs[0] = ebus_blacklist_irq(dev->prom_node->name)) != 0) { - dev->num_irqs = 1; - } else { - irqs = of_get_property(dp, "interrupts", &len); - if (!irqs) { - dev->num_irqs = 0; - dev->irqs[0] = 0; - if (dev->parent->num_irqs != 0) { - dev->num_irqs = 1; - dev->irqs[0] = dev->parent->irqs[0]; - } - } else { - dev->num_irqs = len / sizeof(irqs[0]); - if (irqs[0] == 0 || irqs[0] >= 8) { - /* - * XXX Zero is a valid pin number... - * This works as long as Ebus is not wired - * to INTA#. - */ - printk("EBUS: %s got bad irq %d from PROM\n", - dev->prom_node->name, irqs[0]); - dev->num_irqs = 0; - dev->irqs[0] = 0; - } else { - dev->irqs[0] = - pcic_pin_to_irq(irqs[0], - dev->prom_node->name); - } - } - } -} - -static void __init fill_ebus_device(struct device_node *dp, - struct linux_ebus_device *dev) -{ - const struct linux_prom_registers *regs; - struct linux_ebus_child *child; - struct dev_archdata *sd; - const int *irqs; - int i, n, len; - unsigned long baseaddr; - - dev->prom_node = dp; - - regs = of_get_property(dp, "reg", &len); - if (!regs) - len = 0; - if (len % sizeof(struct linux_prom_registers)) { - prom_printf("UGH: proplen for %s was %d, need multiple of %d\n", - dev->prom_node->name, len, - (int)sizeof(struct linux_prom_registers)); - panic(__func__); - } - dev->num_addrs = len / sizeof(struct linux_prom_registers); - - for (i = 0; i < dev->num_addrs; i++) { - /* - * XXX Collect JE-1 PROM - * - * Example - JS-E with 3.11: - * /ebus - * regs - * 0x00000000, 0x0, 0x00000000, 0x0, 0x00000000, - * 0x82000010, 0x0, 0xf0000000, 0x0, 0x01000000, - * 0x82000014, 0x0, 0x38800000, 0x0, 0x00800000, - * ranges - * 0x00, 0x00000000, 0x02000010, 0x0, 0x0, 0x01000000, - * 0x01, 0x01000000, 0x02000014, 0x0, 0x0, 0x00800000, - * /ebus/8042 - * regs - * 0x00000001, 0x00300060, 0x00000008, - * 0x00000001, 0x00300060, 0x00000008, - */ - n = regs[i].which_io; - if (n >= 4) { - /* XXX This is copied from old JE-1 by Gleb. */ - n = (regs[i].which_io - 0x10) >> 2; - } else { - ; - } - -/* - * XXX Now as we have regions, why don't we make an on-demand allocation... - */ - dev->resource[i].start = 0; - if ((baseaddr = dev->bus->self->resource[n].start + - regs[i].phys_addr) != 0) { - /* dev->resource[i].name = dev->prom_name; */ - if ((baseaddr = (unsigned long) ioremap(baseaddr, - regs[i].reg_size)) == 0) { - panic("ebus: unable to remap dev %s", - dev->prom_node->name); - } - } - dev->resource[i].start = baseaddr; /* XXX Unaligned */ - } - - for (i = 0; i < PROMINTR_MAX; i++) - dev->irqs[i] = PCI_IRQ_NONE; - - if ((dev->irqs[0] = ebus_blacklist_irq(dev->prom_node->name)) != 0) { - dev->num_irqs = 1; - } else { - irqs = of_get_property(dp, "interrupts", &len); - if (!irqs) { - dev->num_irqs = 0; - if ((dev->irqs[0] = dev->bus->self->irq) != 0) { - dev->num_irqs = 1; -/* P3 */ /* printk("EBUS: child %s irq %d from parent\n", dev->prom_name, dev->irqs[0]); */ - } - } else { - dev->num_irqs = 1; /* dev->num_irqs = len / sizeof(irqs[0]); */ - if (irqs[0] == 0 || irqs[0] >= 8) { - /* See above for the parent. XXX */ - printk("EBUS: %s got bad irq %d from PROM\n", - dev->prom_node->name, irqs[0]); - dev->num_irqs = 0; - dev->irqs[0] = 0; - } else { - dev->irqs[0] = - pcic_pin_to_irq(irqs[0], - dev->prom_node->name); - } - } - } - - sd = &dev->ofdev.dev.archdata; - sd->prom_node = dp; - sd->op = &dev->ofdev; - sd->iommu = dev->bus->ofdev.dev.parent->archdata.iommu; - - dev->ofdev.node = dp; - dev->ofdev.dev.parent = &dev->bus->ofdev.dev; - dev->ofdev.dev.bus = &ebus_bus_type; - sprintf(dev->ofdev.dev.bus_id, "ebus[%08x]", dp->node); - - /* Register with core */ - if (of_device_register(&dev->ofdev) != 0) - printk(KERN_DEBUG "ebus: device registration error for %s!\n", - dp->path_component_name); - - if ((dp = dp->child) != NULL) { - dev->children = (struct linux_ebus_child *) - ebus_alloc(sizeof(struct linux_ebus_child)); - - child = dev->children; - child->next = NULL; - child->parent = dev; - child->bus = dev->bus; - fill_ebus_child(dp, child); - - while ((dp = dp->sibling) != NULL) { - child->next = (struct linux_ebus_child *) - ebus_alloc(sizeof(struct linux_ebus_child)); - - child = child->next; - child->next = NULL; - child->parent = dev; - child->bus = dev->bus; - fill_ebus_child(dp, child); - } - } -} - -void __init ebus_init(void) -{ - const struct linux_prom_pci_registers *regs; - struct linux_pbm_info *pbm; - struct linux_ebus_device *dev; - struct linux_ebus *ebus; - struct ebus_system_entry *sp; - struct pci_dev *pdev; - struct pcidev_cookie *cookie; - struct device_node *dp; - struct resource *p; - unsigned short pci_command; - int len, reg, nreg; - int num_ebus = 0; - - dp = of_find_node_by_path("/"); - for (sp = ebus_blacklist; sp->esname != NULL; sp++) { - if (strcmp(dp->name, sp->esname) == 0) { - ebus_blackp = sp->ipt; - break; - } - } - - pdev = pci_get_device(PCI_VENDOR_ID_SUN, PCI_DEVICE_ID_SUN_EBUS, NULL); - if (!pdev) - return; - - cookie = pdev->sysdata; - dp = cookie->prom_node; - - ebus_chain = ebus = (struct linux_ebus *) - ebus_alloc(sizeof(struct linux_ebus)); - ebus->next = NULL; - - while (dp) { - struct device_node *nd; - - ebus->prom_node = dp; - ebus->self = pdev; - ebus->parent = pbm = cookie->pbm; - - /* Enable BUS Master. */ - pci_read_config_word(pdev, PCI_COMMAND, &pci_command); - pci_command |= PCI_COMMAND_MASTER; - pci_write_config_word(pdev, PCI_COMMAND, pci_command); - - regs = of_get_property(dp, "reg", &len); - if (!regs) { - prom_printf("%s: can't find reg property\n", - __func__); - prom_halt(); - } - nreg = len / sizeof(struct linux_prom_pci_registers); - - p = &ebus->self->resource[0]; - for (reg = 0; reg < nreg; reg++) { - if (!(regs[reg].which_io & 0x03000000)) - continue; - - (p++)->start = regs[reg].phys_lo; - } - - ebus->ofdev.node = dp; - ebus->ofdev.dev.parent = &pdev->dev; - ebus->ofdev.dev.bus = &ebus_bus_type; - sprintf(ebus->ofdev.dev.bus_id, "ebus%d", num_ebus); - - /* Register with core */ - if (of_device_register(&ebus->ofdev) != 0) - printk(KERN_DEBUG "ebus: device registration error for %s!\n", - dp->path_component_name); - - - nd = dp->child; - if (!nd) - goto next_ebus; - - ebus->devices = (struct linux_ebus_device *) - ebus_alloc(sizeof(struct linux_ebus_device)); - - dev = ebus->devices; - dev->next = NULL; - dev->children = NULL; - dev->bus = ebus; - fill_ebus_device(nd, dev); - - while ((nd = nd->sibling) != NULL) { - dev->next = (struct linux_ebus_device *) - ebus_alloc(sizeof(struct linux_ebus_device)); - - dev = dev->next; - dev->next = NULL; - dev->children = NULL; - dev->bus = ebus; - fill_ebus_device(nd, dev); - } - - next_ebus: - pdev = pci_get_device(PCI_VENDOR_ID_SUN, - PCI_DEVICE_ID_SUN_EBUS, pdev); - if (!pdev) - break; - - cookie = pdev->sysdata; - dp = cookie->prom_node; - - ebus->next = (struct linux_ebus *) - ebus_alloc(sizeof(struct linux_ebus)); - ebus = ebus->next; - ebus->next = NULL; - ++num_ebus; - } - if (pdev) - pci_dev_put(pdev); -} diff --git a/arch/sparc/kernel/entry.S b/arch/sparc/kernel/entry.S index e8cdf715a54..faf9ccd9ef5 100644 --- a/arch/sparc/kernel/entry.S +++ b/arch/sparc/kernel/entry.S @@ -20,11 +20,7 @@ #include <asm/memreg.h> #include <asm/page.h> #include <asm/pgtable.h> -#ifdef CONFIG_SUN4 -#include <asm/pgtsun4.h> -#else #include <asm/pgtsun4c.h> -#endif #include <asm/winmacro.h> #include <asm/signal.h> #include <asm/obio.h> @@ -276,17 +272,18 @@ smp4m_ticker: */ maybe_smp4m_msg: GET_PROCESSOR4M_ID(o3) - set sun4m_interrupts, %l5 - ld [%l5], %o5 + sethi %hi(sun4m_irq_percpu), %l5 + sll %o3, 2, %o3 + or %l5, %lo(sun4m_irq_percpu), %o5 sethi %hi(0x40000000), %o2 - sll %o3, 12, %o3 ld [%o5 + %o3], %o1 - andcc %o1, %o2, %g0 + ld [%o1 + 0x00], %o3 ! sun4m_irq_percpu[cpu]->pending + andcc %o3, %o2, %g0 be,a smp4m_ticker cmp %l7, 14 - st %o2, [%o5 + 0x4] + st %o2, [%o1 + 0x04] ! sun4m_irq_percpu[cpu]->clear=0x40000000 WRITE_PAUSE - ld [%o5], %g0 + ld [%o1 + 0x00], %g0 ! sun4m_irq_percpu[cpu]->pending WRITE_PAUSE or %l0, PSR_PIL, %l4 wr %l4, 0x0, %psr @@ -304,16 +301,16 @@ linux_trap_ipi15_sun4m: SAVE_ALL sethi %hi(0x80000000), %o2 GET_PROCESSOR4M_ID(o0) - set sun4m_interrupts, %l5 - ld [%l5], %o5 - sll %o0, 12, %o0 - add %o5, %o0, %o5 - ld [%o5], %o3 + sethi %hi(sun4m_irq_percpu), %l5 + or %l5, %lo(sun4m_irq_percpu), %o5 + sll %o0, 2, %o0 + ld [%o5 + %o0], %o5 + ld [%o5 + 0x00], %o3 ! sun4m_irq_percpu[cpu]->pending andcc %o3, %o2, %g0 be 1f ! Must be an NMI async memory error - st %o2, [%o5 + 4] + st %o2, [%o5 + 0x04] ! sun4m_irq_percpu[cpu]->clear=0x80000000 WRITE_PAUSE - ld [%o5], %g0 + ld [%o5 + 0x00], %g0 ! sun4m_irq_percpu[cpu]->pending WRITE_PAUSE or %l0, PSR_PIL, %l4 wr %l4, 0x0, %psr @@ -327,12 +324,11 @@ linux_trap_ipi15_sun4m: 1: /* NMI async memory error handling. */ sethi %hi(0x80000000), %l4 - sethi %hi(0x4000), %o3 - sub %o5, %o0, %o5 - add %o5, %o3, %l5 - st %l4, [%l5 + 0xc] + sethi %hi(sun4m_irq_global), %o5 + ld [%o5 + %lo(sun4m_irq_global)], %l5 + st %l4, [%l5 + 0x0c] ! sun4m_irq_global->mask_set=0x80000000 WRITE_PAUSE - ld [%l5], %g0 + ld [%l5 + 0x00], %g0 ! sun4m_irq_global->pending WRITE_PAUSE or %l0, PSR_PIL, %l4 wr %l4, 0x0, %psr @@ -341,9 +337,9 @@ linux_trap_ipi15_sun4m: WRITE_PAUSE call sun4m_nmi nop - st %l4, [%l5 + 0x8] + st %l4, [%l5 + 0x08] ! sun4m_irq_global->mask_clear=0x80000000 WRITE_PAUSE - ld [%l5], %g0 + ld [%l5 + 0x00], %g0 ! sun4m_irq_global->pending WRITE_PAUSE RESTORE_ALL @@ -775,11 +771,7 @@ vac_linesize_patch_32: subcc %l7, 32, %l7 * Ugly, but we cant use hardware flushing on the sun4 and we'd require * two instructions (Anton) */ -#ifdef CONFIG_SUN4 -vac_hwflush_patch1_on: nop -#else vac_hwflush_patch1_on: addcc %l7, -PAGE_SIZE, %l7 -#endif vac_hwflush_patch2_on: sta %g0, [%l3 + %l7] ASI_HWFLUSHSEG @@ -798,42 +790,10 @@ vac_hwflush_patch2_on: sta %g0, [%l3 + %l7] ASI_HWFLUSHSEG ! %l7 = 1 for textfault ! We want error in %l5, vaddr in %l6 sun4c_fault: -#ifdef CONFIG_SUN4 - sethi %hi(sun4c_memerr_reg), %l4 - ld [%l4+%lo(sun4c_memerr_reg)], %l4 ! memerr ctrl reg addr - ld [%l4], %l6 ! memerr ctrl reg - ld [%l4 + 4], %l5 ! memerr vaddr reg - andcc %l6, 0x80, %g0 ! check for error type - st %g0, [%l4 + 4] ! clear the error - be 0f ! normal error - sethi %hi(AC_BUS_ERROR), %l4 ! bus err reg addr - - call prom_halt ! something weird happened - ! what exactly did happen? - ! what should we do here? - -0: or %l4, %lo(AC_BUS_ERROR), %l4 ! bus err reg addr - lduba [%l4] ASI_CONTROL, %l6 ! bus err reg - - cmp %l7, 1 ! text fault? - be 1f ! yes - nop - - ld [%l1], %l4 ! load instruction that caused fault - srl %l4, 21, %l4 - andcc %l4, 1, %g0 ! store instruction? - - be 1f ! no - sethi %hi(SUN4C_SYNC_BADWRITE), %l4 ! yep - ! %lo(SUN4C_SYNC_BADWRITE) = 0 - or %l4, %l6, %l6 ! set write bit to emulate sun4c -1: -#else sethi %hi(AC_SYNC_ERR), %l4 add %l4, 0x4, %l6 ! AC_SYNC_VA in %l6 lda [%l6] ASI_CONTROL, %l5 ! Address lda [%l4] ASI_CONTROL, %l6 ! Error, retained for a bit -#endif andn %l5, 0xfff, %l5 ! Encode all info into l7 srl %l6, 14, %l4 @@ -880,12 +840,7 @@ sun4c_fault: or %l4, %lo(swapper_pg_dir), %l4 sll %l6, 2, %l6 ld [%l4 + %l6], %l4 -#ifdef CONFIG_SUN4 - sethi %hi(PAGE_MASK), %l6 - andcc %l4, %l6, %g0 -#else andcc %l4, PAGE_MASK, %g0 -#endif be sun4c_fault_fromuser lduXa [%l5] ASI_SEGMAP, %l4 @@ -937,11 +892,7 @@ invalid_segment_patch1: ld [%l6 + 0x08], %l3 ! tmp = entry->vaddr ! Flush segment from the cache. -#ifdef CONFIG_SUN4 - sethi %hi((128 * 1024)), %l7 -#else sethi %hi((64 * 1024)), %l7 -#endif 9: vac_hwflush_patch1: vac_linesize_patch: @@ -1029,12 +980,7 @@ invalid_segment_patch2: or %l4, %lo(swapper_pg_dir), %l4 sll %l3, 2, %l3 ld [%l4 + %l3], %l4 -#ifndef CONFIG_SUN4 and %l4, PAGE_MASK, %l4 -#else - sethi %hi(PAGE_MASK), %l6 - and %l4, %l6, %l4 -#endif srl %l5, (PAGE_SHIFT - 2), %l6 and %l6, ((SUN4C_PTRS_PER_PTE - 1) << 2), %l6 diff --git a/arch/sparc/kernel/head.S b/arch/sparc/kernel/head.S index 50d9a16af79..2d325fd8457 100644 --- a/arch/sparc/kernel/head.S +++ b/arch/sparc/kernel/head.S @@ -63,15 +63,9 @@ cputypvar_sun4m: .align 4 -#ifndef CONFIG_SUN4 sun4_notsup: - .asciz "Sparc-Linux sun4 needs a specially compiled kernel, turn CONFIG_SUN4 on.\n\n" + .asciz "Sparc-Linux sun4 support does no longer exist.\n\n" .align 4 -#else -sun4cdm_notsup: - .asciz "Kernel compiled with CONFIG_SUN4 cannot run on SUN4C/SUN4M/SUN4D\nTurn CONFIG_SUN4 off.\n\n" - .align 4 -#endif sun4e_notsup: .asciz "Sparc-Linux sun4e support does not exist\n\n" @@ -780,15 +774,6 @@ execute_in_high_mem: nop found_version: -#ifdef CONFIG_SUN4 -/* For people who try sun4 kernels, even if Configure.help advises them. */ - ld [%g7 + 0x68], %o1 - set sun4cdm_notsup, %o0 - call %o1 - nop - b halt_me - nop -#endif /* Get the machine type via the mysterious romvec node operations. */ add %g7, 0x1c, %l1 @@ -1150,15 +1135,6 @@ sun4c_continue_boot: nop sun4_init: -#ifdef CONFIG_SUN4 -/* There, happy now Adrian? */ - set cputypval, %o2 ! Let everyone know we - set ' ', %o0 ! are a "sun4 " architecture - stb %o0, [%o2 + 0x4] - - b got_prop - nop -#else sethi %hi(SUN4_PROM_VECTOR+0x84), %o1 ld [%o1 + %lo(SUN4_PROM_VECTOR+0x84)], %o1 set sun4_notsup, %o0 @@ -1170,7 +1146,7 @@ sun4_init: nop 1: ba 1b ! Cannot exit into KMON nop -#endif + no_sun4e_here: ld [%g7 + 0x68], %o1 set sun4e_notsup, %o0 diff --git a/arch/sparc/kernel/idprom.c b/arch/sparc/kernel/idprom.c index fc511f3c4c1..223a6582e1e 100644 --- a/arch/sparc/kernel/idprom.c +++ b/arch/sparc/kernel/idprom.c @@ -12,10 +12,6 @@ #include <asm/oplib.h> #include <asm/idprom.h> #include <asm/machines.h> /* Fun with Sun released architectures. */ -#ifdef CONFIG_SUN4 -#include <asm/sun4paddr.h> -extern void sun4setup(void); -#endif struct idprom *idprom; static struct idprom idprom_buffer; @@ -101,7 +97,4 @@ void __init idprom_init(void) idprom->id_ethaddr[0], idprom->id_ethaddr[1], idprom->id_ethaddr[2], idprom->id_ethaddr[3], idprom->id_ethaddr[4], idprom->id_ethaddr[5]); -#ifdef CONFIG_SUN4 - sun4setup(); -#endif } diff --git a/arch/sparc/kernel/ioport.c b/arch/sparc/kernel/ioport.c index 2a8a847764d..4f025b36934 100644 --- a/arch/sparc/kernel/ioport.c +++ b/arch/sparc/kernel/ioport.c @@ -42,10 +42,13 @@ #include <asm/vaddrs.h> #include <asm/oplib.h> #include <asm/prom.h> -#include <asm/sbus.h> #include <asm/page.h> #include <asm/pgalloc.h> #include <asm/dma.h> +#include <asm/iommu.h> +#include <asm/io-unit.h> + +#include "dma.h" #define mmu_inval_dma_area(p, l) /* Anton pulled it out for 2.4.0-xx */ @@ -139,15 +142,6 @@ void iounmap(volatile void __iomem *virtual) } } -/* - */ -void __iomem *sbus_ioremap(struct resource *phyres, unsigned long offset, - unsigned long size, char *name) -{ - return _sparc_alloc_io(phyres->flags & 0xF, - phyres->start + offset, size, name); -} - void __iomem *of_ioremap(struct resource *res, unsigned long offset, unsigned long size, char *name) { @@ -164,13 +158,6 @@ void of_iounmap(struct resource *res, void __iomem *base, unsigned long size) EXPORT_SYMBOL(of_iounmap); /* - */ -void sbus_iounmap(volatile void __iomem *addr, unsigned long size) -{ - iounmap(addr); -} - -/* * Meat of mapping */ static void __iomem *_sparc_alloc_io(unsigned int busno, unsigned long phys, @@ -246,63 +233,19 @@ static void _sparc_free_io(struct resource *res) #ifdef CONFIG_SBUS -void sbus_set_sbus64(struct sbus_dev *sdev, int x) +void sbus_set_sbus64(struct device *dev, int x) { printk("sbus_set_sbus64: unsupported\n"); } -extern unsigned int sun4d_build_irq(struct sbus_dev *sdev, int irq); -void __init sbus_fill_device_irq(struct sbus_dev *sdev) -{ - struct linux_prom_irqs irqs[PROMINTR_MAX]; - int len; - - len = prom_getproperty(sdev->prom_node, "intr", - (char *)irqs, sizeof(irqs)); - if (len != -1) { - sdev->num_irqs = len / 8; - if (sdev->num_irqs == 0) { - sdev->irqs[0] = 0; - } else if (sparc_cpu_model == sun4d) { - for (len = 0; len < sdev->num_irqs; len++) - sdev->irqs[len] = - sun4d_build_irq(sdev, irqs[len].pri); - } else { - for (len = 0; len < sdev->num_irqs; len++) - sdev->irqs[len] = irqs[len].pri; - } - } else { - int interrupts[PROMINTR_MAX]; - - /* No "intr" node found-- check for "interrupts" node. - * This node contains SBus interrupt levels, not IPLs - * as in "intr", and no vector values. We convert - * SBus interrupt levels to PILs (platform specific). - */ - len = prom_getproperty(sdev->prom_node, "interrupts", - (char *)interrupts, sizeof(interrupts)); - if (len == -1) { - sdev->irqs[0] = 0; - sdev->num_irqs = 0; - } else { - sdev->num_irqs = len / sizeof(int); - for (len = 0; len < sdev->num_irqs; len++) { - sdev->irqs[len] = - sbint_to_irq(sdev, interrupts[len]); - } - } - } -} - /* * Allocate a chunk of memory suitable for DMA. * Typically devices use them for control blocks. * CPU may access them without any explicit flushing. - * - * XXX Some clever people know that sdev is not used and supply NULL. Watch. */ -void *sbus_alloc_consistent(struct sbus_dev *sdev, long len, u32 *dma_addrp) +void *sbus_alloc_consistent(struct device *dev, long len, u32 *dma_addrp) { + struct of_device *op = to_of_device(dev); unsigned long len_total = (len + PAGE_SIZE-1) & PAGE_MASK; unsigned long va; struct resource *res; @@ -336,13 +279,10 @@ void *sbus_alloc_consistent(struct sbus_dev *sdev, long len, u32 *dma_addrp) * XXX That's where sdev would be used. Currently we load * all iommu tables with the same translations. */ - if (mmu_map_dma_area(dma_addrp, va, res->start, len_total) != 0) + if (mmu_map_dma_area(dev, dma_addrp, va, res->start, len_total) != 0) goto err_noiommu; - /* Set the resource name, if known. */ - if (sdev) { - res->name = sdev->prom_name; - } + res->name = op->node->name; return (void *)(unsigned long)res->start; @@ -356,7 +296,7 @@ err_nopages: return NULL; } -void sbus_free_consistent(struct sbus_dev *sdev, long n, void *p, u32 ba) +void sbus_free_consistent(struct device *dev, long n, void *p, u32 ba) { struct resource *res; struct page *pgv; @@ -383,8 +323,8 @@ void sbus_free_consistent(struct sbus_dev *sdev, long n, void *p, u32 ba) kfree(res); /* mmu_inval_dma_area(va, n); */ /* it's consistent, isn't it */ - pgv = mmu_translate_dvma(ba); - mmu_unmap_dma_area(ba, n); + pgv = virt_to_page(p); + mmu_unmap_dma_area(dev, ba, n); __free_pages(pgv, get_order(n)); } @@ -394,7 +334,7 @@ void sbus_free_consistent(struct sbus_dev *sdev, long n, void *p, u32 ba) * CPU view of this memory may be inconsistent with * a device view and explicit flushing is necessary. */ -dma_addr_t sbus_map_single(struct sbus_dev *sdev, void *va, size_t len, int direction) +dma_addr_t sbus_map_single(struct device *dev, void *va, size_t len, int direction) { /* XXX why are some lengths signed, others unsigned? */ if (len <= 0) { @@ -404,17 +344,17 @@ dma_addr_t sbus_map_single(struct sbus_dev *sdev, void *va, size_t len, int dire if (len > 256*1024) { /* __get_free_pages() limit */ return 0; } - return mmu_get_scsi_one(va, len, sdev->bus); + return mmu_get_scsi_one(dev, va, len); } -void sbus_unmap_single(struct sbus_dev *sdev, dma_addr_t ba, size_t n, int direction) +void sbus_unmap_single(struct device *dev, dma_addr_t ba, size_t n, int direction) { - mmu_release_scsi_one(ba, n, sdev->bus); + mmu_release_scsi_one(dev, ba, n); } -int sbus_map_sg(struct sbus_dev *sdev, struct scatterlist *sg, int n, int direction) +int sbus_map_sg(struct device *dev, struct scatterlist *sg, int n, int direction) { - mmu_get_scsi_sgl(sg, n, sdev->bus); + mmu_get_scsi_sgl(dev, sg, n); /* * XXX sparc64 can return a partial length here. sun4c should do this @@ -423,145 +363,28 @@ int sbus_map_sg(struct sbus_dev *sdev, struct scatterlist *sg, int n, int direct return n; } -void sbus_unmap_sg(struct sbus_dev *sdev, struct scatterlist *sg, int n, int direction) -{ - mmu_release_scsi_sgl(sg, n, sdev->bus); -} - -/* - */ -void sbus_dma_sync_single_for_cpu(struct sbus_dev *sdev, dma_addr_t ba, size_t size, int direction) -{ -#if 0 - unsigned long va; - struct resource *res; - - /* We do not need the resource, just print a message if invalid. */ - res = _sparc_find_resource(&_sparc_dvma, ba); - if (res == NULL) - panic("sbus_dma_sync_single: 0x%x\n", ba); - - va = page_address(mmu_translate_dvma(ba)); /* XXX higmem */ - /* - * XXX This bogosity will be fixed with the iommu rewrite coming soon - * to a kernel near you. - Anton - */ - /* mmu_inval_dma_area(va, (size + PAGE_SIZE-1) & PAGE_MASK); */ -#endif -} - -void sbus_dma_sync_single_for_device(struct sbus_dev *sdev, dma_addr_t ba, size_t size, int direction) +void sbus_unmap_sg(struct device *dev, struct scatterlist *sg, int n, int direction) { -#if 0 - unsigned long va; - struct resource *res; - - /* We do not need the resource, just print a message if invalid. */ - res = _sparc_find_resource(&_sparc_dvma, ba); - if (res == NULL) - panic("sbus_dma_sync_single: 0x%x\n", ba); - - va = page_address(mmu_translate_dvma(ba)); /* XXX higmem */ - /* - * XXX This bogosity will be fixed with the iommu rewrite coming soon - * to a kernel near you. - Anton - */ - /* mmu_inval_dma_area(va, (size + PAGE_SIZE-1) & PAGE_MASK); */ -#endif + mmu_release_scsi_sgl(dev, sg, n); } -void sbus_dma_sync_sg_for_cpu(struct sbus_dev *sdev, struct scatterlist *sg, int n, int direction) +void sbus_dma_sync_single_for_cpu(struct device *dev, dma_addr_t ba, size_t size, int direction) { - printk("sbus_dma_sync_sg_for_cpu: not implemented yet\n"); } -void sbus_dma_sync_sg_for_device(struct sbus_dev *sdev, struct scatterlist *sg, int n, int direction) +void sbus_dma_sync_single_for_device(struct device *dev, dma_addr_t ba, size_t size, int direction) { - printk("sbus_dma_sync_sg_for_device: not implemented yet\n"); -} - -/* Support code for sbus_init(). */ -/* - * XXX This functions appears to be a distorted version of - * prom_sbus_ranges_init(), with all sun4d stuff cut away. - * Ask DaveM what is going on here, how is sun4d supposed to work... XXX - */ -/* added back sun4d patch from Thomas Bogendoerfer - should be OK (crn) */ -void __init sbus_arch_bus_ranges_init(struct device_node *pn, struct sbus_bus *sbus) -{ - int parent_node = pn->node; - - if (sparc_cpu_model == sun4d) { - struct linux_prom_ranges iounit_ranges[PROMREG_MAX]; - int num_iounit_ranges, len; - - len = prom_getproperty(parent_node, "ranges", - (char *) iounit_ranges, - sizeof (iounit_ranges)); - if (len != -1) { - num_iounit_ranges = - (len / sizeof(struct linux_prom_ranges)); - prom_adjust_ranges(sbus->sbus_ranges, - sbus->num_sbus_ranges, - iounit_ranges, num_iounit_ranges); - } - } } -void __init sbus_setup_iommu(struct sbus_bus *sbus, struct device_node *dp) -{ -#ifndef CONFIG_SUN4 - struct device_node *parent = dp->parent; - - if (sparc_cpu_model != sun4d && - parent != NULL && - !strcmp(parent->name, "iommu")) { - extern void iommu_init(int iommu_node, struct sbus_bus *sbus); - - iommu_init(parent->node, sbus); - } - - if (sparc_cpu_model == sun4d) { - extern void iounit_init(int sbi_node, int iounit_node, - struct sbus_bus *sbus); - - iounit_init(dp->node, parent->node, sbus); - } -#endif -} - -void __init sbus_setup_arch_props(struct sbus_bus *sbus, struct device_node *dp) -{ - if (sparc_cpu_model == sun4d) { - struct device_node *parent = dp->parent; - - sbus->devid = of_getintprop_default(parent, "device-id", 0); - sbus->board = of_getintprop_default(parent, "board#", 0); - } -} - -int __init sbus_arch_preinit(void) +static int __init sparc_register_ioport(void) { register_proc_sparc_ioport(); -#ifdef CONFIG_SUN4 - { - extern void sun4_dvma_init(void); - sun4_dvma_init(); - } - return 1; -#else return 0; -#endif } -void __init sbus_arch_postinit(void) -{ - if (sparc_cpu_model == sun4d) { - extern void sun4d_init_sbi_irq(void); - sun4d_init_sbi_irq(); - } -} +arch_initcall(sparc_register_ioport); + #endif /* CONFIG_SBUS */ #ifdef CONFIG_PCI diff --git a/arch/sparc/kernel/irq.h b/arch/sparc/kernel/irq.h index 32ef3ebd0a8..db751388153 100644 --- a/arch/sparc/kernel/irq.h +++ b/arch/sparc/kernel/irq.h @@ -13,7 +13,6 @@ BTFIXUPDEF_CALL(void, enable_irq, unsigned int) BTFIXUPDEF_CALL(void, disable_pil_irq, unsigned int) BTFIXUPDEF_CALL(void, enable_pil_irq, unsigned int) BTFIXUPDEF_CALL(void, clear_clock_irq, void) -BTFIXUPDEF_CALL(void, clear_profile_irq, int) BTFIXUPDEF_CALL(void, load_profile_irq, int, unsigned int) static inline void __disable_irq(unsigned int irq) @@ -41,11 +40,6 @@ static inline void clear_clock_irq(void) BTFIXUP_CALL(clear_clock_irq)(); } -static inline void clear_profile_irq(int irq) -{ - BTFIXUP_CALL(clear_profile_irq)(irq); -} - static inline void load_profile_irq(int cpu, int limit) { BTFIXUP_CALL(load_profile_irq)(cpu, limit); diff --git a/arch/sparc/kernel/of_device.c b/arch/sparc/kernel/of_device.c index f58c537446a..0837bd52e28 100644 --- a/arch/sparc/kernel/of_device.c +++ b/arch/sparc/kernel/of_device.c @@ -29,15 +29,38 @@ struct of_device *of_find_device_by_node(struct device_node *dp) } EXPORT_SYMBOL(of_find_device_by_node); -#ifdef CONFIG_PCI -struct bus_type ebus_bus_type; -EXPORT_SYMBOL(ebus_bus_type); -#endif +unsigned int irq_of_parse_and_map(struct device_node *node, int index) +{ + struct of_device *op = of_find_device_by_node(node); + + if (!op || index >= op->num_irqs) + return 0; + + return op->irqs[index]; +} +EXPORT_SYMBOL(irq_of_parse_and_map); + +/* Take the archdata values for IOMMU, STC, and HOSTDATA found in + * BUS and propagate to all child of_device objects. + */ +void of_propagate_archdata(struct of_device *bus) +{ + struct dev_archdata *bus_sd = &bus->dev.archdata; + struct device_node *bus_dp = bus->node; + struct device_node *dp; + + for (dp = bus_dp->child; dp; dp = dp->sibling) { + struct of_device *op = of_find_device_by_node(dp); -#ifdef CONFIG_SBUS -struct bus_type sbus_bus_type; -EXPORT_SYMBOL(sbus_bus_type); -#endif + op->dev.archdata.iommu = bus_sd->iommu; + op->dev.archdata.stc = bus_sd->stc; + op->dev.archdata.host_controller = bus_sd->host_controller; + op->dev.archdata.numa_node = bus_sd->numa_node; + + if (dp->child) + of_propagate_archdata(op); + } +} struct bus_type of_platform_bus_type; EXPORT_SYMBOL(of_platform_bus_type); @@ -327,6 +350,27 @@ static int __init build_one_resource(struct device_node *parent, return 1; } +static int __init use_1to1_mapping(struct device_node *pp) +{ + /* If we have a ranges property in the parent, use it. */ + if (of_find_property(pp, "ranges", NULL) != NULL) + return 0; + + /* Some SBUS devices use intermediate nodes to express + * hierarchy within the device itself. These aren't + * real bus nodes, and don't have a 'ranges' property. + * But, we should still pass the translation work up + * to the SBUS itself. + */ + if (!strcmp(pp->name, "dma") || + !strcmp(pp->name, "espdma") || + !strcmp(pp->name, "ledma") || + !strcmp(pp->name, "lebuffer")) + return 0; + + return 1; +} + static int of_resource_verbose; static void __init build_device_resources(struct of_device *op, @@ -373,10 +417,7 @@ static void __init build_device_resources(struct of_device *op, flags = bus->get_flags(reg, 0); - /* If the immediate parent has no ranges property to apply, - * just use a 1<->1 mapping. - */ - if (of_find_property(pp, "ranges", NULL) == NULL) { + if (use_1to1_mapping(pp)) { result = of_read_addr(addr, na); goto build_res; } @@ -565,15 +606,6 @@ static int __init of_bus_driver_init(void) int err; err = of_bus_type_init(&of_platform_bus_type, "of"); -#ifdef CONFIG_PCI - if (!err) - err = of_bus_type_init(&ebus_bus_type, "ebus"); -#endif -#ifdef CONFIG_SBUS - if (!err) - err = of_bus_type_init(&sbus_bus_type, "sbus"); -#endif - if (!err) scan_of_devices(); diff --git a/arch/sparc/kernel/pcic.c b/arch/sparc/kernel/pcic.c index a6a6f982337..462584e55fb 100644 --- a/arch/sparc/kernel/pcic.c +++ b/arch/sparc/kernel/pcic.c @@ -17,8 +17,6 @@ #include <linux/slab.h> #include <linux/jiffies.h> -#include <asm/ebus.h> -#include <asm/sbus.h> /* for sanity check... */ #include <asm/swift.h> /* for cache flushing. */ #include <asm/io.h> @@ -430,7 +428,6 @@ static int __init pcic_init(void) pcic_pbm_scan_bus(pcic); - ebus_init(); return 0; } @@ -493,10 +490,6 @@ static void pcic_map_pci_device(struct linux_pcic *pcic, * do ioremap() before accessing PC-style I/O, * we supply virtual, ready to access address. * - * Ebus devices do not come here even if - * CheerIO makes a similar conversion. - * See ebus.c for details. - * * Note that request_region() * works for these devices. * @@ -677,7 +670,7 @@ void __devinit pcibios_fixup_bus(struct pci_bus *bus) } /* - * pcic_pin_to_irq() is exported to ebus.c. + * pcic_pin_to_irq() is exported to bus probing code */ unsigned int pcic_pin_to_irq(unsigned int pin, const char *name) @@ -904,11 +897,6 @@ static void pcic_enable_irq(unsigned int irq_nr) local_irq_restore(flags); } -static void pcic_clear_profile_irq(int cpu) -{ - printk("PCIC: unimplemented code: FILE=%s LINE=%d", __FILE__, __LINE__); -} - static void pcic_load_profile_irq(int cpu, unsigned int limit) { printk("PCIC: unimplemented code: FILE=%s LINE=%d", __FILE__, __LINE__); @@ -934,7 +922,6 @@ void __init sun4m_pci_init_IRQ(void) BTFIXUPSET_CALL(enable_pil_irq, pcic_enable_pil_irq, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(disable_pil_irq, pcic_disable_pil_irq, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(clear_clock_irq, pcic_clear_clock_irq, BTFIXUPCALL_NORM); - BTFIXUPSET_CALL(clear_profile_irq, pcic_clear_profile_irq, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(load_profile_irq, pcic_load_profile_irq, BTFIXUPCALL_NORM); } diff --git a/arch/sparc/kernel/pmc.c b/arch/sparc/kernel/pmc.c index 7eca8871ff4..2afcfab4f11 100644 --- a/arch/sparc/kernel/pmc.c +++ b/arch/sparc/kernel/pmc.c @@ -8,11 +8,11 @@ #include <linux/fs.h> #include <linux/errno.h> #include <linux/init.h> -#include <linux/miscdevice.h> #include <linux/pm.h> +#include <linux/of.h> +#include <linux/of_device.h> #include <asm/io.h> -#include <asm/sbus.h> #include <asm/oplib.h> #include <asm/uaccess.h> #include <asm/auxio.h> @@ -23,17 +23,15 @@ * #define PMC_NO_IDLE */ -#define PMC_MINOR MISC_DYNAMIC_MINOR #define PMC_OBPNAME "SUNW,pmc" #define PMC_DEVNAME "pmc" #define PMC_IDLE_REG 0x00 #define PMC_IDLE_ON 0x01 -volatile static u8 __iomem *regs; -static int pmc_regsize; +static u8 __iomem *regs; -#define pmc_readb(offs) (sbus_readb(regs+offs)) +#define pmc_readb(offs) (sbus_readb(regs+offs)) #define pmc_writeb(val, offs) (sbus_writeb(val, regs+offs)) /* @@ -53,31 +51,11 @@ void pmc_swift_idle(void) #endif } -static inline void pmc_free(void) +static int __devinit pmc_probe(struct of_device *op, + const struct of_device_id *match) { - sbus_iounmap(regs, pmc_regsize); -} - -static int __init pmc_probe(void) -{ - struct sbus_bus *sbus = NULL; - struct sbus_dev *sdev = NULL; - for_each_sbus(sbus) { - for_each_sbusdev(sdev, sbus) { - if (!strcmp(sdev->prom_name, PMC_OBPNAME)) { - goto sbus_done; - } - } - } - -sbus_done: - if (!sdev) { - return -ENODEV; - } - - pmc_regsize = sdev->reg_addrs[0].reg_size; - regs = sbus_ioremap(&sdev->resource[0], 0, - pmc_regsize, PMC_OBPNAME); + regs = of_ioremap(&op->resource[0], 0, + resource_size(&op->resource[0]), PMC_OBPNAME); if (!regs) { printk(KERN_ERR "%s: unable to map registers\n", PMC_DEVNAME); return -ENODEV; @@ -92,8 +70,27 @@ sbus_done: return 0; } +static struct of_device_id __initdata pmc_match[] = { + { + .name = PMC_OBPNAME, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, pmc_match); + +static struct of_platform_driver pmc_driver = { + .name = "pmc", + .match_table = pmc_match, + .probe = pmc_probe, +}; + +static int __init pmc_init(void) +{ + return of_register_driver(&pmc_driver, &of_bus_type); +} + /* This driver is not critical to the boot process * and is easiest to ioremap when SBus is already * initialized, so we install ourselves thusly: */ -__initcall(pmc_probe); +__initcall(pmc_init); diff --git a/arch/sparc/kernel/process.c b/arch/sparc/kernel/process.c index 4bb430940a6..e8c43ffe317 100644 --- a/arch/sparc/kernel/process.c +++ b/arch/sparc/kernel/process.c @@ -75,7 +75,7 @@ void cpu_idle(void) { /* endless idle loop with no priority at all */ for (;;) { - if (ARCH_SUN4C_SUN4) { + if (ARCH_SUN4C) { static int count = HZ; static unsigned long last_jiffies; static unsigned long last_faults; diff --git a/arch/sparc/kernel/prom.c b/arch/sparc/kernel/prom.c index cd4fb79aa3a..eee5efcfe50 100644 --- a/arch/sparc/kernel/prom.c +++ b/arch/sparc/kernel/prom.c @@ -54,6 +54,9 @@ int of_getintprop_default(struct device_node *np, const char *name, int def) } EXPORT_SYMBOL(of_getintprop_default); +DEFINE_MUTEX(of_set_property_mutex); +EXPORT_SYMBOL(of_set_property_mutex); + int of_set_property(struct device_node *dp, const char *name, void *val, int len) { struct property **prevp; @@ -77,7 +80,10 @@ int of_set_property(struct device_node *dp, const char *name, void *val, int len void *old_val = prop->value; int ret; + mutex_lock(&of_set_property_mutex); ret = prom_setprop(dp->node, (char *) name, val, len); + mutex_unlock(&of_set_property_mutex); + err = -EINVAL; if (ret >= 0) { prop->value = new_val; @@ -436,7 +442,6 @@ static void __init of_console_init(void) switch (prom_vers) { case PROM_V0: - case PROM_SUN4: skip = 0; switch (*romvec->pv_stdout) { case PROMDEV_SCREEN: diff --git a/arch/sparc/kernel/setup.c b/arch/sparc/kernel/setup.c index 9e451b21202..24fe3078bd4 100644 --- a/arch/sparc/kernel/setup.c +++ b/arch/sparc/kernel/setup.c @@ -213,23 +213,25 @@ void __init setup_arch(char **cmdline_p) /* Initialize PROM console and command line. */ *cmdline_p = prom_getbootargs(); strcpy(boot_command_line, *cmdline_p); + parse_early_param(); /* Set sparc_cpu_model */ sparc_cpu_model = sun_unknown; - if(!strcmp(&cputypval,"sun4 ")) { sparc_cpu_model=sun4; } - if(!strcmp(&cputypval,"sun4c")) { sparc_cpu_model=sun4c; } - if(!strcmp(&cputypval,"sun4m")) { sparc_cpu_model=sun4m; } - if(!strcmp(&cputypval,"sun4s")) { sparc_cpu_model=sun4m; } /* CP-1200 with PROM 2.30 -E */ - if(!strcmp(&cputypval,"sun4d")) { sparc_cpu_model=sun4d; } - if(!strcmp(&cputypval,"sun4e")) { sparc_cpu_model=sun4e; } - if(!strcmp(&cputypval,"sun4u")) { sparc_cpu_model=sun4u; } - -#ifdef CONFIG_SUN4 - if (sparc_cpu_model != sun4) { - prom_printf("This kernel is for Sun4 architecture only.\n"); - prom_halt(); - } -#endif + if (!strcmp(&cputypval,"sun4 ")) + sparc_cpu_model = sun4; + if (!strcmp(&cputypval,"sun4c")) + sparc_cpu_model = sun4c; + if (!strcmp(&cputypval,"sun4m")) + sparc_cpu_model = sun4m; + if (!strcmp(&cputypval,"sun4s")) + sparc_cpu_model = sun4m; /* CP-1200 with PROM 2.30 -E */ + if (!strcmp(&cputypval,"sun4d")) + sparc_cpu_model = sun4d; + if (!strcmp(&cputypval,"sun4e")) + sparc_cpu_model = sun4e; + if (!strcmp(&cputypval,"sun4u")) + sparc_cpu_model = sun4u; + printk("ARCH: "); switch(sparc_cpu_model) { case sun4: @@ -263,7 +265,7 @@ void __init setup_arch(char **cmdline_p) boot_flags_init(*cmdline_p); idprom_init(); - if (ARCH_SUN4C_SUN4) + if (ARCH_SUN4C) sun4c_probe_vac(); load_mmu(); diff --git a/arch/sparc/kernel/sparc_ksyms.c b/arch/sparc/kernel/sparc_ksyms.c index b23cea5ca5d..b0dfff84865 100644 --- a/arch/sparc/kernel/sparc_ksyms.c +++ b/arch/sparc/kernel/sparc_ksyms.c @@ -38,17 +38,12 @@ #include <asm/idprom.h> #include <asm/head.h> #include <asm/smp.h> -#include <asm/mostek.h> #include <asm/ptrace.h> #include <asm/uaccess.h> #include <asm/checksum.h> #ifdef CONFIG_SBUS -#include <asm/sbus.h> #include <asm/dma.h> #endif -#ifdef CONFIG_PCI -#include <asm/ebus.h> -#endif #include <asm/io-unit.h> #include <asm/bug.h> @@ -127,16 +122,11 @@ EXPORT_SYMBOL(phys_cpu_present_map); EXPORT_SYMBOL(__udelay); EXPORT_SYMBOL(__ndelay); EXPORT_SYMBOL(rtc_lock); -EXPORT_SYMBOL(mostek_lock); -EXPORT_SYMBOL(mstk48t02_regs); #ifdef CONFIG_SUN_AUXIO EXPORT_SYMBOL(set_auxio); EXPORT_SYMBOL(get_auxio); #endif EXPORT_SYMBOL(io_remap_pfn_range); - /* P3: iounit_xxx may be needed, sun4d users */ -/* EXPORT_SYMBOL(iounit_map_dma_init); */ -/* EXPORT_SYMBOL(iounit_map_dma_page); */ #ifndef CONFIG_SMP EXPORT_SYMBOL(BTFIXUP_CALL(___xchg32)); @@ -153,24 +143,9 @@ EXPORT_SYMBOL(BTFIXUP_CALL(mmu_release_scsi_one)); EXPORT_SYMBOL(BTFIXUP_CALL(pgprot_noncached)); #ifdef CONFIG_SBUS -EXPORT_SYMBOL(sbus_root); -EXPORT_SYMBOL(dma_chain); EXPORT_SYMBOL(sbus_set_sbus64); -EXPORT_SYMBOL(sbus_alloc_consistent); -EXPORT_SYMBOL(sbus_free_consistent); -EXPORT_SYMBOL(sbus_map_single); -EXPORT_SYMBOL(sbus_unmap_single); -EXPORT_SYMBOL(sbus_map_sg); -EXPORT_SYMBOL(sbus_unmap_sg); -EXPORT_SYMBOL(sbus_dma_sync_single_for_cpu); -EXPORT_SYMBOL(sbus_dma_sync_single_for_device); -EXPORT_SYMBOL(sbus_dma_sync_sg_for_cpu); -EXPORT_SYMBOL(sbus_dma_sync_sg_for_device); -EXPORT_SYMBOL(sbus_iounmap); -EXPORT_SYMBOL(sbus_ioremap); #endif #ifdef CONFIG_PCI -EXPORT_SYMBOL(ebus_chain); EXPORT_SYMBOL(insb); EXPORT_SYMBOL(outsb); EXPORT_SYMBOL(insw); diff --git a/arch/sparc/kernel/sun4c_irq.c b/arch/sparc/kernel/sun4c_irq.c index 340fc395fe2..5dc8a576948 100644 --- a/arch/sparc/kernel/sun4c_irq.c +++ b/arch/sparc/kernel/sun4c_irq.c @@ -18,6 +18,8 @@ #include <linux/interrupt.h> #include <linux/slab.h> #include <linux/init.h> +#include <linux/of.h> +#include <linux/of_device.h> #include "irq.h" #include <asm/ptrace.h> @@ -31,15 +33,8 @@ #include <asm/traps.h> #include <asm/irq.h> #include <asm/io.h> -#include <asm/sun4paddr.h> #include <asm/idprom.h> #include <asm/machines.h> -#include <asm/sbus.h> - -#if 0 -static struct resource sun4c_timer_eb = { "sun4c_timer" }; -static struct resource sun4c_intr_eb = { "sun4c_intr" }; -#endif /* * Bit field defines for the interrupt registers on various @@ -64,19 +59,7 @@ static struct resource sun4c_intr_eb = { "sun4c_intr" }; * * so don't go making it static, like I tried. sigh. */ -unsigned char *interrupt_enable = NULL; - -static int sun4c_pil_map[] = { 0, 1, 2, 3, 5, 7, 8, 9 }; - -static unsigned int sun4c_sbint_to_irq(struct sbus_dev *sdev, - unsigned int sbint) -{ - if (sbint >= sizeof(sun4c_pil_map)) { - printk(KERN_ERR "%s: bogus SBINT %d\n", sdev->prom_name, sbint); - BUG(); - } - return sun4c_pil_map[sbint]; -} +unsigned char __iomem *interrupt_enable = NULL; static void sun4c_disable_irq(unsigned int irq_nr) { @@ -85,7 +68,7 @@ static void sun4c_disable_irq(unsigned int irq_nr) local_irq_save(flags); irq_nr &= (NR_IRQS - 1); - current_mask = *interrupt_enable; + current_mask = sbus_readb(interrupt_enable); switch(irq_nr) { case 1: new_mask = ((current_mask) & (~(SUN4C_INT_E1))); @@ -103,7 +86,7 @@ static void sun4c_disable_irq(unsigned int irq_nr) local_irq_restore(flags); return; } - *interrupt_enable = new_mask; + sbus_writeb(new_mask, interrupt_enable); local_irq_restore(flags); } @@ -114,7 +97,7 @@ static void sun4c_enable_irq(unsigned int irq_nr) local_irq_save(flags); irq_nr &= (NR_IRQS - 1); - current_mask = *interrupt_enable; + current_mask = sbus_readb(interrupt_enable); switch(irq_nr) { case 1: new_mask = ((current_mask) | SUN4C_INT_E1); @@ -132,37 +115,22 @@ static void sun4c_enable_irq(unsigned int irq_nr) local_irq_restore(flags); return; } - *interrupt_enable = new_mask; + sbus_writeb(new_mask, interrupt_enable); local_irq_restore(flags); } -#define TIMER_IRQ 10 /* Also at level 14, but we ignore that one. */ -#define PROFILE_IRQ 14 /* Level14 ticker.. used by OBP for polling */ - -volatile struct sun4c_timer_info *sun4c_timers; +struct sun4c_timer_info { + u32 l10_count; + u32 l10_limit; + u32 l14_count; + u32 l14_limit; +}; -#ifdef CONFIG_SUN4 -/* This is an ugly hack to work around the - current timer code, and make it work with - the sun4/260 intersil - */ -volatile struct sun4c_timer_info sun4_timer; -#endif +static struct sun4c_timer_info __iomem *sun4c_timers; static void sun4c_clear_clock_irq(void) { - volatile unsigned int clear_intr; -#ifdef CONFIG_SUN4 - if (idprom->id_machtype == (SM_SUN4 | SM_4_260)) - clear_intr = sun4_timer.timer_limit10; - else -#endif - clear_intr = sun4c_timers->timer_limit10; -} - -static void sun4c_clear_profile_irq(int cpu) -{ - /* Errm.. not sure how to do this.. */ + sbus_readl(&sun4c_timers->l10_limit); } static void sun4c_load_profile_irq(int cpu, unsigned int limit) @@ -172,41 +140,48 @@ static void sun4c_load_profile_irq(int cpu, unsigned int limit) static void __init sun4c_init_timers(irq_handler_t counter_fn) { - int irq; + const struct linux_prom_irqs *irq; + struct device_node *dp; + const u32 *addr; + int err; + + dp = of_find_node_by_name(NULL, "counter-timer"); + if (!dp) { + prom_printf("sun4c_init_timers: Unable to find counter-timer\n"); + prom_halt(); + } - /* Map the Timer chip, this is implemented in hardware inside - * the cache chip on the sun4c. - */ -#ifdef CONFIG_SUN4 - if (idprom->id_machtype == (SM_SUN4 | SM_4_260)) - sun4c_timers = &sun4_timer; - else -#endif - sun4c_timers = ioremap(SUN_TIMER_PHYSADDR, - sizeof(struct sun4c_timer_info)); + addr = of_get_property(dp, "address", NULL); + if (!addr) { + prom_printf("sun4c_init_timers: No address property\n"); + prom_halt(); + } + + sun4c_timers = (void __iomem *) (unsigned long) addr[0]; + + irq = of_get_property(dp, "intr", NULL); + if (!irq) { + prom_printf("sun4c_init_timers: No intr property\n"); + prom_halt(); + } /* Have the level 10 timer tick at 100HZ. We don't touch the * level 14 timer limit since we are letting the prom handle * them until we have a real console driver so L1-A works. */ - sun4c_timers->timer_limit10 = (((1000000/HZ) + 1) << 10); - master_l10_counter = &sun4c_timers->cur_count10; - master_l10_limit = &sun4c_timers->timer_limit10; + sbus_writel((((1000000/HZ) + 1) << 10), &sun4c_timers->l10_limit); - irq = request_irq(TIMER_IRQ, - counter_fn, + master_l10_counter = &sun4c_timers->l10_count; + + err = request_irq(irq[0].pri, counter_fn, (IRQF_DISABLED | SA_STATIC_ALLOC), "timer", NULL); - if (irq) { - prom_printf("time_init: unable to attach IRQ%d\n",TIMER_IRQ); + if (err) { + prom_printf("sun4c_init_timers: request_irq() fails with %d\n", err); prom_halt(); } -#if 0 - /* This does not work on 4/330 */ - sun4c_enable_irq(10); -#endif - claim_ticker14(NULL, PROFILE_IRQ, 0); + sun4c_disable_irq(irq[1].pri); } #ifdef CONFIG_SMP @@ -215,41 +190,28 @@ static void sun4c_nop(void) {} void __init sun4c_init_IRQ(void) { - struct linux_prom_registers int_regs[2]; - int ie_node; + struct device_node *dp; + const u32 *addr; - if (ARCH_SUN4) { - interrupt_enable = (char *) - ioremap(sun4_ie_physaddr, PAGE_SIZE); - } else { - struct resource phyres; - - ie_node = prom_searchsiblings (prom_getchild(prom_root_node), - "interrupt-enable"); - if(ie_node == 0) - panic("Cannot find /interrupt-enable node"); + dp = of_find_node_by_name(NULL, "interrupt-enable"); + if (!dp) { + prom_printf("sun4c_init_IRQ: Unable to find interrupt-enable\n"); + prom_halt(); + } - /* Depending on the "address" property is bad news... */ - interrupt_enable = NULL; - if (prom_getproperty(ie_node, "reg", (char *) int_regs, - sizeof(int_regs)) != -1) { - memset(&phyres, 0, sizeof(struct resource)); - phyres.flags = int_regs[0].which_io; - phyres.start = int_regs[0].phys_addr; - interrupt_enable = (char *) sbus_ioremap(&phyres, 0, - int_regs[0].reg_size, "sun4c_intr"); - } + addr = of_get_property(dp, "address", NULL); + if (!addr) { + prom_printf("sun4c_init_IRQ: No address property\n"); + prom_halt(); } - if (!interrupt_enable) - panic("Cannot map interrupt_enable"); - BTFIXUPSET_CALL(sbint_to_irq, sun4c_sbint_to_irq, BTFIXUPCALL_NORM); + interrupt_enable = (void __iomem *) (unsigned long) addr[0]; + BTFIXUPSET_CALL(enable_irq, sun4c_enable_irq, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(disable_irq, sun4c_disable_irq, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(enable_pil_irq, sun4c_enable_irq, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(disable_pil_irq, sun4c_disable_irq, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(clear_clock_irq, sun4c_clear_clock_irq, BTFIXUPCALL_NORM); - BTFIXUPSET_CALL(clear_profile_irq, sun4c_clear_profile_irq, BTFIXUPCALL_NOP); BTFIXUPSET_CALL(load_profile_irq, sun4c_load_profile_irq, BTFIXUPCALL_NOP); sparc_init_timers = sun4c_init_timers; #ifdef CONFIG_SMP @@ -257,6 +219,6 @@ void __init sun4c_init_IRQ(void) BTFIXUPSET_CALL(clear_cpu_int, sun4c_nop, BTFIXUPCALL_NOP); BTFIXUPSET_CALL(set_irq_udt, sun4c_nop, BTFIXUPCALL_NOP); #endif - *interrupt_enable = (SUN4C_INT_ENABLE); + sbus_writeb(SUN4C_INT_ENABLE, interrupt_enable); /* Cannot enable interrupts until OBP ticker is disabled. */ } diff --git a/arch/sparc/kernel/sun4d_irq.c b/arch/sparc/kernel/sun4d_irq.c index 1290b5998f8..d3cb76ce418 100644 --- a/arch/sparc/kernel/sun4d_irq.c +++ b/arch/sparc/kernel/sun4d_irq.c @@ -19,6 +19,8 @@ #include <linux/smp.h> #include <linux/spinlock.h> #include <linux/seq_file.h> +#include <linux/of.h> +#include <linux/of_device.h> #include <asm/ptrace.h> #include <asm/processor.h> @@ -34,7 +36,6 @@ #include <asm/io.h> #include <asm/pgalloc.h> #include <asm/pgtable.h> -#include <asm/sbus.h> #include <asm/sbi.h> #include <asm/cacheflush.h> #include <asm/irq_regs.h> @@ -44,16 +45,22 @@ /* If you trust current SCSI layer to handle different SCSI IRQs, enable this. I don't trust it... -jj */ /* #define DISTRIBUTE_IRQS */ -struct sun4d_timer_regs *sun4d_timers; +struct sun4d_timer_regs { + u32 l10_timer_limit; + u32 l10_cur_countx; + u32 l10_limit_noclear; + u32 ctrl; + u32 l10_cur_count; +}; + +static struct sun4d_timer_regs __iomem *sun4d_timers; + #define TIMER_IRQ 10 #define MAX_STATIC_ALLOC 4 extern struct irqaction static_irqaction[MAX_STATIC_ALLOC]; extern int static_irq_count; -unsigned char cpu_leds[32]; -#ifdef CONFIG_SMP static unsigned char sbus_tid[32]; -#endif static struct irqaction *irq_action[NR_IRQS]; extern spinlock_t irq_action_lock; @@ -72,9 +79,9 @@ static int sbus_to_pil[] = { }; static int nsbi; -#ifdef CONFIG_SMP + +/* Exported for sun4d_smp.c */ DEFINE_SPINLOCK(sun4d_imsk_lock); -#endif int show_sun4d_interrupts(struct seq_file *p, void *v) { @@ -257,26 +264,6 @@ void sun4d_handler_irq(int irq, struct pt_regs * regs) set_irq_regs(old_regs); } -unsigned int sun4d_build_irq(struct sbus_dev *sdev, int irq) -{ - int sbusl = pil_to_sbus[irq]; - - if (sbusl) - return ((sdev->bus->board + 1) << 5) + (sbusl << 2) + sdev->slot; - else - return irq; -} - -static unsigned int sun4d_sbint_to_irq(struct sbus_dev *sdev, - unsigned int sbint) -{ - if (sbint >= sizeof(sbus_to_pil)) { - printk(KERN_ERR "%s: bogus SBINT %d\n", sdev->prom_name, sbint); - BUG(); - } - return sun4d_build_irq(sdev, sbus_to_pil[sbint]); -} - int sun4d_request_irq(unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char * devname, void *dev_id) @@ -360,36 +347,28 @@ out: static void sun4d_disable_irq(unsigned int irq) { -#ifdef CONFIG_SMP int tid = sbus_tid[(irq >> 5) - 1]; unsigned long flags; -#endif - if (irq < NR_IRQS) return; -#ifdef CONFIG_SMP + if (irq < NR_IRQS) + return; + spin_lock_irqsave(&sun4d_imsk_lock, flags); cc_set_imsk_other(tid, cc_get_imsk_other(tid) | (1 << sbus_to_pil[(irq >> 2) & 7])); spin_unlock_irqrestore(&sun4d_imsk_lock, flags); -#else - cc_set_imsk(cc_get_imsk() | (1 << sbus_to_pil[(irq >> 2) & 7])); -#endif } static void sun4d_enable_irq(unsigned int irq) { -#ifdef CONFIG_SMP int tid = sbus_tid[(irq >> 5) - 1]; unsigned long flags; -#endif - if (irq < NR_IRQS) return; -#ifdef CONFIG_SMP + if (irq < NR_IRQS) + return; + spin_lock_irqsave(&sun4d_imsk_lock, flags); cc_set_imsk_other(tid, cc_get_imsk_other(tid) & ~(1 << sbus_to_pil[(irq >> 2) & 7])); spin_unlock_irqrestore(&sun4d_imsk_lock, flags); -#else - cc_set_imsk(cc_get_imsk() & ~(1 << sbus_to_pil[(irq >> 2) & 7])); -#endif } #ifdef CONFIG_SMP @@ -409,47 +388,55 @@ static void sun4d_set_udt(int cpu) /* Setup IRQ distribution scheme. */ void __init sun4d_distribute_irqs(void) { + struct device_node *dp; + #ifdef DISTRIBUTE_IRQS - struct sbus_bus *sbus; - unsigned long sbus_serving_map; + cpumask_t sbus_serving_map; sbus_serving_map = cpu_present_map; - for_each_sbus(sbus) { - if ((sbus->board * 2) == boot_cpu_id && (cpu_present_map & (1 << (sbus->board * 2 + 1)))) - sbus_tid[sbus->board] = (sbus->board * 2 + 1); - else if (cpu_present_map & (1 << (sbus->board * 2))) - sbus_tid[sbus->board] = (sbus->board * 2); - else if (cpu_present_map & (1 << (sbus->board * 2 + 1))) - sbus_tid[sbus->board] = (sbus->board * 2 + 1); + for_each_node_by_name(dp, "sbi") { + int board = of_getintprop_default(dp, "board#", 0); + + if ((board * 2) == boot_cpu_id && cpu_isset(board * 2 + 1, cpu_present_map)) + sbus_tid[board] = (board * 2 + 1); + else if (cpu_isset(board * 2, cpu_present_map)) + sbus_tid[board] = (board * 2); + else if (cpu_isset(board * 2 + 1, cpu_present_map)) + sbus_tid[board] = (board * 2 + 1); else - sbus_tid[sbus->board] = 0xff; - if (sbus_tid[sbus->board] != 0xff) - sbus_serving_map &= ~(1 << sbus_tid[sbus->board]); + sbus_tid[board] = 0xff; + if (sbus_tid[board] != 0xff) + cpu_clear(sbus_tid[board], sbus_serving_map); } - for_each_sbus(sbus) - if (sbus_tid[sbus->board] == 0xff) { + for_each_node_by_name(dp, "sbi") { + int board = of_getintprop_default(dp, "board#", 0); + if (sbus_tid[board] == 0xff) { int i = 31; - if (!sbus_serving_map) + if (cpus_empty(sbus_serving_map)) sbus_serving_map = cpu_present_map; - while (!(sbus_serving_map & (1 << i))) + while (cpu_isset(i, sbus_serving_map)) i--; - sbus_tid[sbus->board] = i; - sbus_serving_map &= ~(1 << i); + sbus_tid[board] = i; + cpu_clear(i, sbus_serving_map); } - for_each_sbus(sbus) { - printk("sbus%d IRQs directed to CPU%d\n", sbus->board, sbus_tid[sbus->board]); - set_sbi_tid(sbus->devid, sbus_tid[sbus->board] << 3); + } + for_each_node_by_name(dp, "sbi") { + int devid = of_getintprop_default(dp, "device-id", 0); + int board = of_getintprop_default(dp, "board#", 0); + printk("sbus%d IRQs directed to CPU%d\n", board, sbus_tid[board]); + set_sbi_tid(devid, sbus_tid[board] << 3); } #else - struct sbus_bus *sbus; int cpuid = cpu_logical_map(1); if (cpuid == -1) cpuid = cpu_logical_map(0); - for_each_sbus(sbus) { - sbus_tid[sbus->board] = cpuid; - set_sbi_tid(sbus->devid, cpuid << 3); + for_each_node_by_name(dp, "sbi") { + int devid = of_getintprop_default(dp, "device-id", 0); + int board = of_getintprop_default(dp, "board#", 0); + sbus_tid[board] = cpuid; + set_sbi_tid(devid, cpuid << 3); } printk("All sbus IRQs directed to CPU%d\n", cpuid); #endif @@ -458,13 +445,7 @@ void __init sun4d_distribute_irqs(void) static void sun4d_clear_clock_irq(void) { - volatile unsigned int clear_intr; - clear_intr = sun4d_timers->l10_timer_limit; -} - -static void sun4d_clear_profile_irq(int cpu) -{ - bw_get_prof_limit(cpu); + sbus_readl(&sun4d_timers->l10_timer_limit); } static void sun4d_load_profile_irq(int cpu, unsigned int limit) @@ -472,98 +453,121 @@ static void sun4d_load_profile_irq(int cpu, unsigned int limit) bw_set_prof_limit(cpu, limit); } -static void __init sun4d_init_timers(irq_handler_t counter_fn) +static void __init sun4d_load_profile_irqs(void) { - int irq; - int cpu; - struct resource r; - int mid; + int cpu = 0, mid; - /* Map the User Timer registers. */ - memset(&r, 0, sizeof(r)); + while (!cpu_find_by_instance(cpu, NULL, &mid)) { + sun4d_load_profile_irq(mid >> 3, 0); + cpu++; + } +} + +static void __init sun4d_fixup_trap_table(void) +{ #ifdef CONFIG_SMP - r.start = CSR_BASE(boot_cpu_id)+BW_TIMER_LIMIT; -#else - r.start = CSR_BASE(0)+BW_TIMER_LIMIT; + unsigned long flags; + extern unsigned long lvl14_save[4]; + struct tt_entry *trap_table = &sparc_ttable[SP_TRAP_IRQ1 + (14 - 1)]; + extern unsigned int real_irq_entry[], smp4d_ticker[]; + extern unsigned int patchme_maybe_smp_msg[]; + + /* Adjust so that we jump directly to smp4d_ticker */ + lvl14_save[2] += smp4d_ticker - real_irq_entry; + + /* For SMP we use the level 14 ticker, however the bootup code + * has copied the firmware's level 14 vector into the boot cpu's + * trap table, we must fix this now or we get squashed. + */ + local_irq_save(flags); + patchme_maybe_smp_msg[0] = 0x01000000; /* NOP out the branch */ + trap_table->inst_one = lvl14_save[0]; + trap_table->inst_two = lvl14_save[1]; + trap_table->inst_three = lvl14_save[2]; + trap_table->inst_four = lvl14_save[3]; + local_flush_cache_all(); + local_irq_restore(flags); #endif - r.flags = 0xf; - sun4d_timers = (struct sun4d_timer_regs *) sbus_ioremap(&r, 0, - PAGE_SIZE, "user timer"); +} - sun4d_timers->l10_timer_limit = (((1000000/HZ) + 1) << 10); - master_l10_counter = &sun4d_timers->l10_cur_count; - master_l10_limit = &sun4d_timers->l10_timer_limit; +static void __init sun4d_init_timers(irq_handler_t counter_fn) +{ + struct device_node *dp; + struct resource res; + const u32 *reg; + int err; + + dp = of_find_node_by_name(NULL, "cpu-unit"); + if (!dp) { + prom_printf("sun4d_init_timers: Unable to find cpu-unit\n"); + prom_halt(); + } - irq = request_irq(TIMER_IRQ, - counter_fn, - (IRQF_DISABLED | SA_STATIC_ALLOC), - "timer", NULL); - if (irq) { - prom_printf("time_init: unable to attach IRQ%d\n",TIMER_IRQ); + /* Which cpu-unit we use is arbitrary, we can view the bootbus timer + * registers via any cpu's mapping. The first 'reg' property is the + * bootbus. + */ + reg = of_get_property(dp, "reg", NULL); + if (!reg) { + prom_printf("sun4d_init_timers: No reg property\n"); prom_halt(); } - - /* Enable user timer free run for CPU 0 in BW */ - /* bw_set_ctrl(0, bw_get_ctrl(0) | BW_CTRL_USER_TIMER); */ - cpu = 0; - while (!cpu_find_by_instance(cpu, NULL, &mid)) { - sun4d_load_profile_irq(mid >> 3, 0); - cpu++; + res.start = reg[1]; + res.end = reg[2] - 1; + res.flags = reg[0] & 0xff; + sun4d_timers = of_ioremap(&res, BW_TIMER_LIMIT, + sizeof(struct sun4d_timer_regs), "user timer"); + if (!sun4d_timers) { + prom_printf("sun4d_init_timers: Can't map timer regs\n"); + prom_halt(); } - -#ifdef CONFIG_SMP - { - unsigned long flags; - extern unsigned long lvl14_save[4]; - struct tt_entry *trap_table = &sparc_ttable[SP_TRAP_IRQ1 + (14 - 1)]; - extern unsigned int real_irq_entry[], smp4d_ticker[]; - extern unsigned int patchme_maybe_smp_msg[]; - - /* Adjust so that we jump directly to smp4d_ticker */ - lvl14_save[2] += smp4d_ticker - real_irq_entry; - - /* For SMP we use the level 14 ticker, however the bootup code - * has copied the firmware's level 14 vector into the boot cpu's - * trap table, we must fix this now or we get squashed. - */ - local_irq_save(flags); - patchme_maybe_smp_msg[0] = 0x01000000; /* NOP out the branch */ - trap_table->inst_one = lvl14_save[0]; - trap_table->inst_two = lvl14_save[1]; - trap_table->inst_three = lvl14_save[2]; - trap_table->inst_four = lvl14_save[3]; - local_flush_cache_all(); - local_irq_restore(flags); + + sbus_writel((((1000000/HZ) + 1) << 10), &sun4d_timers->l10_timer_limit); + + master_l10_counter = &sun4d_timers->l10_cur_count; + + err = request_irq(TIMER_IRQ, counter_fn, + (IRQF_DISABLED | SA_STATIC_ALLOC), + "timer", NULL); + if (err) { + prom_printf("sun4d_init_timers: request_irq() failed with %d\n", err); + prom_halt(); } -#endif + sun4d_load_profile_irqs(); + sun4d_fixup_trap_table(); } void __init sun4d_init_sbi_irq(void) { - struct sbus_bus *sbus; - unsigned mask; + struct device_node *dp; + int target_cpu = 0; + +#ifdef CONFIG_SMP + target_cpu = boot_cpu_id; +#endif nsbi = 0; - for_each_sbus(sbus) + for_each_node_by_name(dp, "sbi") nsbi++; sbus_actions = kzalloc (nsbi * 8 * 4 * sizeof(struct sbus_action), GFP_ATOMIC); if (!sbus_actions) { prom_printf("SUN4D: Cannot allocate sbus_actions, halting.\n"); prom_halt(); } - for_each_sbus(sbus) { -#ifdef CONFIG_SMP - extern unsigned char boot_cpu_id; - - set_sbi_tid(sbus->devid, boot_cpu_id << 3); - sbus_tid[sbus->board] = boot_cpu_id; -#endif + for_each_node_by_name(dp, "sbi") { + int devid = of_getintprop_default(dp, "device-id", 0); + int board = of_getintprop_default(dp, "board#", 0); + unsigned int mask; + + set_sbi_tid(devid, target_cpu << 3); + sbus_tid[board] = target_cpu; + /* Get rid of pending irqs from PROM */ - mask = acquire_sbi(sbus->devid, 0xffffffff); + mask = acquire_sbi(devid, 0xffffffff); if (mask) { - printk ("Clearing pending IRQs %08x on SBI %d\n", mask, sbus->board); - release_sbi(sbus->devid, mask); + printk ("Clearing pending IRQs %08x on SBI %d\n", mask, board); + release_sbi(devid, mask); } } } @@ -572,11 +576,9 @@ void __init sun4d_init_IRQ(void) { local_irq_disable(); - BTFIXUPSET_CALL(sbint_to_irq, sun4d_sbint_to_irq, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(enable_irq, sun4d_enable_irq, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(disable_irq, sun4d_disable_irq, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(clear_clock_irq, sun4d_clear_clock_irq, BTFIXUPCALL_NORM); - BTFIXUPSET_CALL(clear_profile_irq, sun4d_clear_profile_irq, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(load_profile_irq, sun4d_load_profile_irq, BTFIXUPCALL_NORM); sparc_init_timers = sun4d_init_timers; #ifdef CONFIG_SMP diff --git a/arch/sparc/kernel/sun4d_smp.c b/arch/sparc/kernel/sun4d_smp.c index 446767e8f56..7a6a5e79592 100644 --- a/arch/sparc/kernel/sun4d_smp.c +++ b/arch/sparc/kernel/sun4d_smp.c @@ -20,6 +20,7 @@ #include <linux/swap.h> #include <linux/profile.h> #include <linux/delay.h> +#include <linux/cpu.h> #include <asm/ptrace.h> #include <asm/atomic.h> @@ -30,7 +31,6 @@ #include <asm/pgalloc.h> #include <asm/pgtable.h> #include <asm/oplib.h> -#include <asm/sbus.h> #include <asm/sbi.h> #include <asm/tlbflush.h> #include <asm/cacheflush.h> @@ -72,6 +72,17 @@ static void smp_setup_percpu_timer(void); extern void cpu_probe(void); extern void sun4d_distribute_irqs(void); +static unsigned char cpu_leds[32]; + +static inline void show_leds(int cpuid) +{ + cpuid &= 0x1e; + __asm__ __volatile__ ("stba %0, [%1] %2" : : + "r" ((cpu_leds[cpuid] << 4) | cpu_leds[cpuid+1]), + "r" (ECSR_BASE(cpuid) | BB_LEDS), + "i" (ASI_M_CTL)); +} + void __init smp4d_callin(void) { int cpuid = hard_smp4d_processor_id(); diff --git a/arch/sparc/kernel/sun4m_irq.c b/arch/sparc/kernel/sun4m_irq.c index 94e02de960e..f10317179ee 100644 --- a/arch/sparc/kernel/sun4m_irq.c +++ b/arch/sparc/kernel/sun4m_irq.c @@ -20,6 +20,8 @@ #include <linux/slab.h> #include <linux/init.h> #include <linux/ioport.h> +#include <linux/of.h> +#include <linux/of_device.h> #include <asm/ptrace.h> #include <asm/processor.h> @@ -35,59 +37,27 @@ #include <asm/smp.h> #include <asm/irq.h> #include <asm/io.h> -#include <asm/sbus.h> #include <asm/cacheflush.h> #include "irq.h" -/* On the sun4m, just like the timers, we have both per-cpu and master - * interrupt registers. - */ - -/* These registers are used for sending/receiving irqs from/to - * different cpu's. - */ -struct sun4m_intreg_percpu { - unsigned int tbt; /* Interrupts still pending for this cpu. */ - - /* These next two registers are WRITE-ONLY and are only - * "on bit" sensitive, "off bits" written have NO affect. - */ - unsigned int clear; /* Clear this cpus irqs here. */ - unsigned int set; /* Set this cpus irqs here. */ - unsigned char space[PAGE_SIZE - 12]; +struct sun4m_irq_percpu { + u32 pending; + u32 clear; + u32 set; }; -/* - * djhr - * Actually the clear and set fields in this struct are misleading.. - * according to the SLAVIO manual (and the same applies for the SEC) - * the clear field clears bits in the mask which will ENABLE that IRQ - * the set field sets bits in the mask to DISABLE the IRQ. - * - * Also the undirected_xx address in the SLAVIO is defined as - * RESERVED and write only.. - * - * DAVEM_NOTE: The SLAVIO only specifies behavior on uniprocessor - * sun4m machines, for MP the layout makes more sense. - */ -struct sun4m_intregs { - struct sun4m_intreg_percpu cpu_intregs[SUN4M_NCPUS]; - unsigned int tbt; /* IRQ's that are still pending. */ - unsigned int irqs; /* Master IRQ bits. */ - - /* Again, like the above, two these registers are WRITE-ONLY. */ - unsigned int clear; /* Clear master IRQ's by setting bits here. */ - unsigned int set; /* Set master IRQ's by setting bits here. */ - - /* This register is both READ and WRITE. */ - unsigned int undirected_target; /* Which cpu gets undirected irqs. */ +struct sun4m_irq_global { + u32 pending; + u32 mask; + u32 mask_clear; + u32 mask_set; + u32 interrupt_target; }; -static unsigned long dummy; - -struct sun4m_intregs *sun4m_interrupts; -unsigned long *irq_rcvreg = &dummy; +/* Code in entry.S needs to get at these register mappings. */ +struct sun4m_irq_percpu __iomem *sun4m_irq_percpu[SUN4M_NCPUS]; +struct sun4m_irq_global __iomem *sun4m_irq_global; /* Dave Redman (djhr@tadpole.co.uk) * The sun4m interrupt registers. @@ -101,8 +71,9 @@ unsigned long *irq_rcvreg = &dummy; #define SUN4M_INT_MASKALL 0x80000000 /* mask all interrupts */ #define SUN4M_INT_MODULE_ERR 0x40000000 /* module error */ -#define SUN4M_INT_M2S_WRITE 0x20000000 /* write buffer error */ -#define SUN4M_INT_ECC 0x10000000 /* ecc memory error */ +#define SUN4M_INT_M2S_WRITE_ERR 0x20000000 /* write buffer error */ +#define SUN4M_INT_ECC_ERR 0x10000000 /* ecc memory error */ +#define SUN4M_INT_VME_ERR 0x08000000 /* vme async error */ #define SUN4M_INT_FLOPPY 0x00400000 /* floppy disk */ #define SUN4M_INT_MODULE 0x00200000 /* module interrupt */ #define SUN4M_INT_VIDEO 0x00100000 /* onboard video */ @@ -113,75 +84,126 @@ unsigned long *irq_rcvreg = &dummy; #define SUN4M_INT_SERIAL 0x00008000 /* serial ports */ #define SUN4M_INT_KBDMS 0x00004000 /* keyboard/mouse */ #define SUN4M_INT_SBUSBITS 0x00003F80 /* sbus int bits */ +#define SUN4M_INT_VMEBITS 0x0000007F /* vme int bits */ + +#define SUN4M_INT_ERROR (SUN4M_INT_MODULE_ERR | \ + SUN4M_INT_M2S_WRITE_ERR | \ + SUN4M_INT_ECC_ERR | \ + SUN4M_INT_VME_ERR) #define SUN4M_INT_SBUS(x) (1 << (x+7)) #define SUN4M_INT_VME(x) (1 << (x)) -/* These tables only apply for interrupts greater than 15.. - * - * any intr value below 0x10 is considered to be a soft-int - * this may be useful or it may not.. but that's how I've done it. - * and it won't clash with what OBP is telling us about devices. +/* Interrupt levels used by OBP */ +#define OBP_INT_LEVEL_SOFT 0x10 +#define OBP_INT_LEVEL_ONBOARD 0x20 +#define OBP_INT_LEVEL_SBUS 0x30 +#define OBP_INT_LEVEL_VME 0x40 + +/* Interrupt level assignment on sun4m: + * + * level source + * ------------------------------------------------------------ + * 1 softint-1 + * 2 softint-2, VME/SBUS level 1 + * 3 softint-3, VME/SBUS level 2 + * 4 softint-4, onboard SCSI + * 5 softint-5, VME/SBUS level 3 + * 6 softint-6, onboard ETHERNET + * 7 softint-7, VME/SBUS level 4 + * 8 softint-8, onboard VIDEO + * 9 softint-9, VME/SBUS level 5, Module Interrupt + * 10 softint-10, system counter/timer + * 11 softint-11, VME/SBUS level 6, Floppy + * 12 softint-12, Keyboard/Mouse, Serial + * 13 softint-13, VME/SBUS level 7, ISDN Audio + * 14 softint-14, per-processor counter/timer + * 15 softint-15, Asynchronous Errors (broadcast) * - * take an encoded intr value and lookup if it's valid - * then get the mask bits that match from irq_mask + * Each interrupt source is masked distinctly in the sun4m interrupt + * registers. The PIL level alone is therefore ambiguous, since multiple + * interrupt sources map to a single PIL. * - * P3: Translation from irq 0x0d to mask 0x2000 is for MrCoffee. + * This ambiguity is resolved in the 'intr' property for device nodes + * in the OF device tree. Each 'intr' property entry is composed of + * two 32-bit words. The first word is the IRQ priority value, which + * is what we're intersted in. The second word is the IRQ vector, which + * is unused. + * + * The low 4 bits of the IRQ priority indicate the PIL, and the upper + * 4 bits indicate onboard vs. SBUS leveled vs. VME leveled. 0x20 + * means onboard, 0x30 means SBUS leveled, and 0x40 means VME leveled. + * + * For example, an 'intr' IRQ priority value of 0x24 is onboard SCSI + * whereas a value of 0x33 is SBUS level 2. Here are some sample + * 'intr' property IRQ priority values from ss4, ss5, ss10, ss20, and + * Tadpole S3 GX systems. + * + * esp: 0x24 onboard ESP SCSI + * le: 0x26 onboard Lance ETHERNET + * p9100: 0x32 SBUS level 1 P9100 video + * bpp: 0x33 SBUS level 2 BPP parallel port device + * DBRI: 0x39 SBUS level 5 DBRI ISDN audio + * SUNW,leo: 0x39 SBUS level 5 LEO video + * pcmcia: 0x3b SBUS level 6 PCMCIA controller + * uctrl: 0x3b SBUS level 6 UCTRL device + * modem: 0x3d SBUS level 7 MODEM + * zs: 0x2c onboard keyboard/mouse/serial + * floppy: 0x2b onboard Floppy + * power: 0x22 onboard power device (XXX unknown mask bit XXX) */ -static unsigned char irq_xlate[32] = { - /* 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f */ - 0, 0, 0, 0, 1, 0, 2, 0, 3, 0, 4, 5, 6, 14, 0, 7, - 0, 0, 8, 9, 0, 10, 0, 11, 0, 12, 0, 13, 0, 14, 0, 0 -}; -static unsigned long irq_mask[] = { - 0, /* illegal index */ - SUN4M_INT_SCSI, /* 1 irq 4 */ - SUN4M_INT_ETHERNET, /* 2 irq 6 */ - SUN4M_INT_VIDEO, /* 3 irq 8 */ - SUN4M_INT_REALTIME, /* 4 irq 10 */ - SUN4M_INT_FLOPPY, /* 5 irq 11 */ - (SUN4M_INT_SERIAL | SUN4M_INT_KBDMS), /* 6 irq 12 */ - SUN4M_INT_MODULE_ERR, /* 7 irq 15 */ - SUN4M_INT_SBUS(0), /* 8 irq 2 */ - SUN4M_INT_SBUS(1), /* 9 irq 3 */ - SUN4M_INT_SBUS(2), /* 10 irq 5 */ - SUN4M_INT_SBUS(3), /* 11 irq 7 */ - SUN4M_INT_SBUS(4), /* 12 irq 9 */ - SUN4M_INT_SBUS(5), /* 13 irq 11 */ - SUN4M_INT_SBUS(6) /* 14 irq 13 */ +static unsigned long irq_mask[0x50] = { + /* SMP */ + 0, SUN4M_SOFT_INT(1), + SUN4M_SOFT_INT(2), SUN4M_SOFT_INT(3), + SUN4M_SOFT_INT(4), SUN4M_SOFT_INT(5), + SUN4M_SOFT_INT(6), SUN4M_SOFT_INT(7), + SUN4M_SOFT_INT(8), SUN4M_SOFT_INT(9), + SUN4M_SOFT_INT(10), SUN4M_SOFT_INT(11), + SUN4M_SOFT_INT(12), SUN4M_SOFT_INT(13), + SUN4M_SOFT_INT(14), SUN4M_SOFT_INT(15), + /* soft */ + 0, SUN4M_SOFT_INT(1), + SUN4M_SOFT_INT(2), SUN4M_SOFT_INT(3), + SUN4M_SOFT_INT(4), SUN4M_SOFT_INT(5), + SUN4M_SOFT_INT(6), SUN4M_SOFT_INT(7), + SUN4M_SOFT_INT(8), SUN4M_SOFT_INT(9), + SUN4M_SOFT_INT(10), SUN4M_SOFT_INT(11), + SUN4M_SOFT_INT(12), SUN4M_SOFT_INT(13), + SUN4M_SOFT_INT(14), SUN4M_SOFT_INT(15), + /* onboard */ + 0, 0, 0, 0, + SUN4M_INT_SCSI, 0, SUN4M_INT_ETHERNET, 0, + SUN4M_INT_VIDEO, SUN4M_INT_MODULE, + SUN4M_INT_REALTIME, SUN4M_INT_FLOPPY, + (SUN4M_INT_SERIAL | SUN4M_INT_KBDMS), + SUN4M_INT_AUDIO, 0, SUN4M_INT_MODULE_ERR, + /* sbus */ + 0, 0, SUN4M_INT_SBUS(0), SUN4M_INT_SBUS(1), + 0, SUN4M_INT_SBUS(2), 0, SUN4M_INT_SBUS(3), + 0, SUN4M_INT_SBUS(4), 0, SUN4M_INT_SBUS(5), + 0, SUN4M_INT_SBUS(6), 0, 0, + /* vme */ + 0, 0, SUN4M_INT_VME(0), SUN4M_INT_VME(1), + 0, SUN4M_INT_VME(2), 0, SUN4M_INT_VME(3), + 0, SUN4M_INT_VME(4), 0, SUN4M_INT_VME(5), + 0, SUN4M_INT_VME(6), 0, 0 }; -static int sun4m_pil_map[] = { 0, 2, 3, 5, 7, 9, 11, 13 }; - -static unsigned int sun4m_sbint_to_irq(struct sbus_dev *sdev, - unsigned int sbint) -{ - if (sbint >= sizeof(sun4m_pil_map)) { - printk(KERN_ERR "%s: bogus SBINT %d\n", sdev->prom_name, sbint); - BUG(); - } - return sun4m_pil_map[sbint] | 0x30; -} - static unsigned long sun4m_get_irqmask(unsigned int irq) { unsigned long mask; - if (irq > 0x20) { - /* OBIO/SBUS interrupts */ - irq &= 0x1f; - mask = irq_mask[irq_xlate[irq]]; - if (!mask) - printk("sun4m_get_irqmask: IRQ%d has no valid mask!\n",irq); - } else { - /* Soft Interrupts will come here. - * Currently there is no way to trigger them but I'm sure - * something could be cooked up. - */ - irq &= 0xf; - mask = SUN4M_SOFT_INT(irq); - } + if (irq < 0x50) + mask = irq_mask[irq]; + else + mask = 0; + + if (!mask) + printk(KERN_ERR "sun4m_get_irqmask: IRQ%d has no valid mask!\n", + irq); + return mask; } @@ -193,9 +215,9 @@ static void sun4m_disable_irq(unsigned int irq_nr) mask = sun4m_get_irqmask(irq_nr); local_irq_save(flags); if (irq_nr > 15) - sun4m_interrupts->set = mask; + sbus_writel(mask, &sun4m_irq_global->mask_set); else - sun4m_interrupts->cpu_intregs[cpu].set = mask; + sbus_writel(mask, &sun4m_irq_percpu[cpu]->set); local_irq_restore(flags); } @@ -212,13 +234,13 @@ static void sun4m_enable_irq(unsigned int irq_nr) mask = sun4m_get_irqmask(irq_nr); local_irq_save(flags); if (irq_nr > 15) - sun4m_interrupts->clear = mask; + sbus_writel(mask, &sun4m_irq_global->mask_clear); else - sun4m_interrupts->cpu_intregs[cpu].clear = mask; + sbus_writel(mask, &sun4m_irq_percpu[cpu]->clear); local_irq_restore(flags); } else { local_irq_save(flags); - sun4m_interrupts->clear = SUN4M_INT_FLOPPY; + sbus_writel(SUN4M_INT_FLOPPY, &sun4m_irq_global->mask_clear); local_irq_restore(flags); } } @@ -236,10 +258,10 @@ static unsigned long cpu_pil_to_imask[16] = { /*9*/ SUN4M_INT_SBUS(4) | SUN4M_INT_VME(4) | SUN4M_INT_MODULE_ERR, /*10*/ SUN4M_INT_REALTIME, /*11*/ SUN4M_INT_SBUS(5) | SUN4M_INT_VME(5) | SUN4M_INT_FLOPPY, -/*12*/ SUN4M_INT_SERIAL | SUN4M_INT_KBDMS, -/*13*/ SUN4M_INT_AUDIO, +/*12*/ SUN4M_INT_SERIAL | SUN4M_INT_KBDMS, +/*13*/ SUN4M_INT_SBUS(6) | SUN4M_INT_VME(6) | SUN4M_INT_AUDIO, /*14*/ SUN4M_INT_E14, -/*15*/ 0x00000000 +/*15*/ SUN4M_INT_ERROR }; /* We assume the caller has disabled local interrupts when these are called, @@ -247,126 +269,141 @@ static unsigned long cpu_pil_to_imask[16] = { */ static void sun4m_disable_pil_irq(unsigned int pil) { - sun4m_interrupts->set = cpu_pil_to_imask[pil]; + sbus_writel(cpu_pil_to_imask[pil], &sun4m_irq_global->mask_set); } static void sun4m_enable_pil_irq(unsigned int pil) { - sun4m_interrupts->clear = cpu_pil_to_imask[pil]; + sbus_writel(cpu_pil_to_imask[pil], &sun4m_irq_global->mask_clear); } #ifdef CONFIG_SMP static void sun4m_send_ipi(int cpu, int level) { - unsigned long mask; - - mask = sun4m_get_irqmask(level); - sun4m_interrupts->cpu_intregs[cpu].set = mask; + unsigned long mask = sun4m_get_irqmask(level); + sbus_writel(mask, &sun4m_irq_percpu[cpu]->set); } static void sun4m_clear_ipi(int cpu, int level) { - unsigned long mask; - - mask = sun4m_get_irqmask(level); - sun4m_interrupts->cpu_intregs[cpu].clear = mask; + unsigned long mask = sun4m_get_irqmask(level); + sbus_writel(mask, &sun4m_irq_percpu[cpu]->clear); } static void sun4m_set_udt(int cpu) { - sun4m_interrupts->undirected_target = cpu; + sbus_writel(cpu, &sun4m_irq_global->interrupt_target); } #endif -#define OBIO_INTR 0x20 -#define TIMER_IRQ (OBIO_INTR | 10) -#define PROFILE_IRQ (OBIO_INTR | 14) +struct sun4m_timer_percpu { + u32 l14_limit; + u32 l14_count; + u32 l14_limit_noclear; + u32 user_timer_start_stop; +}; + +static struct sun4m_timer_percpu __iomem *timers_percpu[SUN4M_NCPUS]; + +struct sun4m_timer_global { + u32 l10_limit; + u32 l10_count; + u32 l10_limit_noclear; + u32 reserved; + u32 timer_config; +}; + +static struct sun4m_timer_global __iomem *timers_global; + +#define TIMER_IRQ (OBP_INT_LEVEL_ONBOARD | 10) -static struct sun4m_timer_regs *sun4m_timers; unsigned int lvl14_resolution = (((1000000/HZ) + 1) << 10); static void sun4m_clear_clock_irq(void) { - volatile unsigned int clear_intr; - clear_intr = sun4m_timers->l10_timer_limit; + sbus_readl(&timers_global->l10_limit); } -static void sun4m_clear_profile_irq(int cpu) +void sun4m_nmi(struct pt_regs *regs) { - volatile unsigned int clear; - - clear = sun4m_timers->cpu_timers[cpu].l14_timer_limit; + unsigned long afsr, afar, si; + + printk(KERN_ERR "Aieee: sun4m NMI received!\n"); + /* XXX HyperSparc hack XXX */ + __asm__ __volatile__("mov 0x500, %%g1\n\t" + "lda [%%g1] 0x4, %0\n\t" + "mov 0x600, %%g1\n\t" + "lda [%%g1] 0x4, %1\n\t" : + "=r" (afsr), "=r" (afar)); + printk(KERN_ERR "afsr=%08lx afar=%08lx\n", afsr, afar); + si = sbus_readl(&sun4m_irq_global->pending); + printk(KERN_ERR "si=%08lx\n", si); + if (si & SUN4M_INT_MODULE_ERR) + printk(KERN_ERR "Module async error\n"); + if (si & SUN4M_INT_M2S_WRITE_ERR) + printk(KERN_ERR "MBus/SBus async error\n"); + if (si & SUN4M_INT_ECC_ERR) + printk(KERN_ERR "ECC memory error\n"); + if (si & SUN4M_INT_VME_ERR) + printk(KERN_ERR "VME async error\n"); + printk(KERN_ERR "you lose buddy boy...\n"); + show_regs(regs); + prom_halt(); +} + +/* Exported for sun4m_smp.c */ +void sun4m_clear_profile_irq(int cpu) +{ + sbus_readl(&timers_percpu[cpu]->l14_limit); } static void sun4m_load_profile_irq(int cpu, unsigned int limit) { - sun4m_timers->cpu_timers[cpu].l14_timer_limit = limit; + sbus_writel(limit, &timers_percpu[cpu]->l14_limit); } static void __init sun4m_init_timers(irq_handler_t counter_fn) { - int reg_count, irq, cpu; - struct linux_prom_registers cnt_regs[PROMREG_MAX]; - int obio_node, cnt_node; - struct resource r; - - cnt_node = 0; - if((obio_node = - prom_searchsiblings (prom_getchild(prom_root_node), "obio")) == 0 || - (obio_node = prom_getchild (obio_node)) == 0 || - (cnt_node = prom_searchsiblings (obio_node, "counter")) == 0) { - prom_printf("Cannot find /obio/counter node\n"); - prom_halt(); + struct device_node *dp = of_find_node_by_name(NULL, "counter"); + int i, err, len, num_cpu_timers; + const u32 *addr; + + if (!dp) { + printk(KERN_ERR "sun4m_init_timers: No 'counter' node.\n"); + return; } - reg_count = prom_getproperty(cnt_node, "reg", - (void *) cnt_regs, sizeof(cnt_regs)); - reg_count = (reg_count/sizeof(struct linux_prom_registers)); - - /* Apply the obio ranges to the timer registers. */ - prom_apply_obio_ranges(cnt_regs, reg_count); - - cnt_regs[4].phys_addr = cnt_regs[reg_count-1].phys_addr; - cnt_regs[4].reg_size = cnt_regs[reg_count-1].reg_size; - cnt_regs[4].which_io = cnt_regs[reg_count-1].which_io; - for(obio_node = 1; obio_node < 4; obio_node++) { - cnt_regs[obio_node].phys_addr = - cnt_regs[obio_node-1].phys_addr + PAGE_SIZE; - cnt_regs[obio_node].reg_size = cnt_regs[obio_node-1].reg_size; - cnt_regs[obio_node].which_io = cnt_regs[obio_node-1].which_io; + + addr = of_get_property(dp, "address", &len); + if (!addr) { + printk(KERN_ERR "sun4m_init_timers: No 'address' prop.\n"); + return; } - memset((char*)&r, 0, sizeof(struct resource)); - /* Map the per-cpu Counter registers. */ - r.flags = cnt_regs[0].which_io; - r.start = cnt_regs[0].phys_addr; - sun4m_timers = (struct sun4m_timer_regs *) sbus_ioremap(&r, 0, - PAGE_SIZE*SUN4M_NCPUS, "sun4m_cpu_cnt"); - /* Map the system Counter register. */ - /* XXX Here we expect consequent calls to yeld adjusent maps. */ - r.flags = cnt_regs[4].which_io; - r.start = cnt_regs[4].phys_addr; - sbus_ioremap(&r, 0, cnt_regs[4].reg_size, "sun4m_sys_cnt"); - - sun4m_timers->l10_timer_limit = (((1000000/HZ) + 1) << 10); - master_l10_counter = &sun4m_timers->l10_cur_count; - master_l10_limit = &sun4m_timers->l10_timer_limit; - - irq = request_irq(TIMER_IRQ, - counter_fn, - (IRQF_DISABLED | SA_STATIC_ALLOC), - "timer", NULL); - if (irq) { - prom_printf("time_init: unable to attach IRQ%d\n",TIMER_IRQ); - prom_halt(); + num_cpu_timers = (len / sizeof(u32)) - 1; + for (i = 0; i < num_cpu_timers; i++) { + timers_percpu[i] = (void __iomem *) + (unsigned long) addr[i]; } - - if (!cpu_find_by_instance(1, NULL, NULL)) { - for(cpu = 0; cpu < 4; cpu++) - sun4m_timers->cpu_timers[cpu].l14_timer_limit = 0; - sun4m_interrupts->set = SUN4M_INT_E14; - } else { - sun4m_timers->cpu_timers[0].l14_timer_limit = 0; + timers_global = (void __iomem *) + (unsigned long) addr[num_cpu_timers]; + + sbus_writel((((1000000/HZ) + 1) << 10), &timers_global->l10_limit); + + master_l10_counter = &timers_global->l10_count; + + err = request_irq(TIMER_IRQ, counter_fn, + (IRQF_DISABLED | SA_STATIC_ALLOC), "timer", NULL); + if (err) { + printk(KERN_ERR "sun4m_init_timers: Register IRQ error %d.\n", + err); + return; } + + for (i = 0; i < num_cpu_timers; i++) + sbus_writel(0, &timers_percpu[i]->l14_limit); + if (num_cpu_timers == 4) + sbus_writel(SUN4M_INT_E14, &sun4m_irq_global->mask_set); + #ifdef CONFIG_SMP { unsigned long flags; @@ -390,70 +427,43 @@ static void __init sun4m_init_timers(irq_handler_t counter_fn) void __init sun4m_init_IRQ(void) { - int ie_node,i; - struct linux_prom_registers int_regs[PROMREG_MAX]; - int num_regs; - struct resource r; - int mid; - - local_irq_disable(); - if((ie_node = prom_searchsiblings(prom_getchild(prom_root_node), "obio")) == 0 || - (ie_node = prom_getchild (ie_node)) == 0 || - (ie_node = prom_searchsiblings (ie_node, "interrupt")) == 0) { - prom_printf("Cannot find /obio/interrupt node\n"); - prom_halt(); + struct device_node *dp = of_find_node_by_name(NULL, "interrupt"); + int len, i, mid, num_cpu_iregs; + const u32 *addr; + + if (!dp) { + printk(KERN_ERR "sun4m_init_IRQ: No 'interrupt' node.\n"); + return; } - num_regs = prom_getproperty(ie_node, "reg", (char *) int_regs, - sizeof(int_regs)); - num_regs = (num_regs/sizeof(struct linux_prom_registers)); - - /* Apply the obio ranges to these registers. */ - prom_apply_obio_ranges(int_regs, num_regs); - - int_regs[4].phys_addr = int_regs[num_regs-1].phys_addr; - int_regs[4].reg_size = int_regs[num_regs-1].reg_size; - int_regs[4].which_io = int_regs[num_regs-1].which_io; - for(ie_node = 1; ie_node < 4; ie_node++) { - int_regs[ie_node].phys_addr = int_regs[ie_node-1].phys_addr + PAGE_SIZE; - int_regs[ie_node].reg_size = int_regs[ie_node-1].reg_size; - int_regs[ie_node].which_io = int_regs[ie_node-1].which_io; + + addr = of_get_property(dp, "address", &len); + if (!addr) { + printk(KERN_ERR "sun4m_init_IRQ: No 'address' prop.\n"); + return; } - memset((char *)&r, 0, sizeof(struct resource)); - /* Map the interrupt registers for all possible cpus. */ - r.flags = int_regs[0].which_io; - r.start = int_regs[0].phys_addr; - sun4m_interrupts = (struct sun4m_intregs *) sbus_ioremap(&r, 0, - PAGE_SIZE*SUN4M_NCPUS, "interrupts_percpu"); + num_cpu_iregs = (len / sizeof(u32)) - 1; + for (i = 0; i < num_cpu_iregs; i++) { + sun4m_irq_percpu[i] = (void __iomem *) + (unsigned long) addr[i]; + } + sun4m_irq_global = (void __iomem *) + (unsigned long) addr[num_cpu_iregs]; - /* Map the system interrupt control registers. */ - r.flags = int_regs[4].which_io; - r.start = int_regs[4].phys_addr; - sbus_ioremap(&r, 0, int_regs[4].reg_size, "interrupts_system"); + local_irq_disable(); - sun4m_interrupts->set = ~SUN4M_INT_MASKALL; + sbus_writel(~SUN4M_INT_MASKALL, &sun4m_irq_global->mask_set); for (i = 0; !cpu_find_by_instance(i, NULL, &mid); i++) - sun4m_interrupts->cpu_intregs[mid].clear = ~0x17fff; - - if (!cpu_find_by_instance(1, NULL, NULL)) { - /* system wide interrupts go to cpu 0, this should always - * be safe because it is guaranteed to be fitted or OBP doesn't - * come up - * - * Not sure, but writing here on SLAVIO systems may puke - * so I don't do it unless there is more than 1 cpu. - */ - irq_rcvreg = (unsigned long *) - &sun4m_interrupts->undirected_target; - sun4m_interrupts->undirected_target = 0; - } - BTFIXUPSET_CALL(sbint_to_irq, sun4m_sbint_to_irq, BTFIXUPCALL_NORM); + sbus_writel(~0x17fff, &sun4m_irq_percpu[mid]->clear); + + if (num_cpu_iregs == 4) + sbus_writel(0, &sun4m_irq_global->interrupt_target); + BTFIXUPSET_CALL(enable_irq, sun4m_enable_irq, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(disable_irq, sun4m_disable_irq, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(enable_pil_irq, sun4m_enable_pil_irq, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(disable_pil_irq, sun4m_disable_pil_irq, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(clear_clock_irq, sun4m_clear_clock_irq, BTFIXUPCALL_NORM); - BTFIXUPSET_CALL(clear_profile_irq, sun4m_clear_profile_irq, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(load_profile_irq, sun4m_load_profile_irq, BTFIXUPCALL_NORM); sparc_init_timers = sun4m_init_timers; #ifdef CONFIG_SMP @@ -461,5 +471,6 @@ void __init sun4m_init_IRQ(void) BTFIXUPSET_CALL(clear_cpu_int, sun4m_clear_ipi, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(set_irq_udt, sun4m_set_udt, BTFIXUPCALL_NORM); #endif + /* Cannot enable interrupts until OBP ticker is disabled. */ } diff --git a/arch/sparc/kernel/sun4m_smp.c b/arch/sparc/kernel/sun4m_smp.c index 9964890dc1d..5fc386d08c4 100644 --- a/arch/sparc/kernel/sun4m_smp.c +++ b/arch/sparc/kernel/sun4m_smp.c @@ -17,6 +17,7 @@ #include <linux/swap.h> #include <linux/profile.h> #include <linux/delay.h> +#include <linux/cpu.h> #include <asm/cacheflush.h> #include <asm/tlbflush.h> @@ -315,6 +316,8 @@ void smp4m_cross_call_irq(void) ccall_info.processors_out[i] = 1; } +extern void sun4m_clear_profile_irq(int cpu); + void smp4m_percpu_timer_interrupt(struct pt_regs *regs) { struct pt_regs *old_regs; @@ -322,7 +325,7 @@ void smp4m_percpu_timer_interrupt(struct pt_regs *regs) old_regs = set_irq_regs(regs); - clear_profile_irq(cpu); + sun4m_clear_profile_irq(cpu); profile_tick(CPU_PROFILING); diff --git a/arch/sparc/kernel/sun4setup.c b/arch/sparc/kernel/sun4setup.c deleted file mode 100644 index 229a52f55f1..00000000000 --- a/arch/sparc/kernel/sun4setup.c +++ /dev/null @@ -1,75 +0,0 @@ -/* sun4setup.c: Setup the hardware address of various items in the sun4 - * architecture. Called from idprom_init - * - * Copyright (C) 1998 Chris G. Davis (cdavis@cois.on.ca) - */ - -#include <asm/page.h> -#include <asm/oplib.h> -#include <asm/idprom.h> -#include <asm/sun4paddr.h> -#include <asm/machines.h> - -int sun4_memreg_physaddr; -int sun4_ie_physaddr; -int sun4_clock_physaddr; -int sun4_timer_physaddr; -int sun4_eth_physaddr; -int sun4_si_physaddr; -int sun4_bwtwo_physaddr; -int sun4_zs0_physaddr; -int sun4_zs1_physaddr; -int sun4_dma_physaddr; -int sun4_esp_physaddr; -int sun4_ie_physaddr; - -void __init sun4setup(void) -{ - printk("Sun4 Hardware Setup v1.0 18/May/98 Chris Davis (cdavis@cois.on.ca). "); - /* - setup standard sun4 info - */ - sun4_ie_physaddr=SUN4_IE_PHYSADDR; - - /* - setup model specific info - */ - switch(idprom->id_machtype) { - case (SM_SUN4 | SM_4_260 ): - printk("Setup for a SUN4/260\n"); - sun4_memreg_physaddr=SUN4_200_MEMREG_PHYSADDR; - sun4_clock_physaddr=SUN4_200_CLOCK_PHYSADDR; - sun4_timer_physaddr=SUN4_UNUSED_PHYSADDR; - sun4_eth_physaddr=SUN4_200_ETH_PHYSADDR; - sun4_si_physaddr=SUN4_200_SI_PHYSADDR; - sun4_bwtwo_physaddr=SUN4_200_BWTWO_PHYSADDR; - sun4_dma_physaddr=SUN4_UNUSED_PHYSADDR; - sun4_esp_physaddr=SUN4_UNUSED_PHYSADDR; - break; - case (SM_SUN4 | SM_4_330 ): - printk("Setup for a SUN4/330\n"); - sun4_memreg_physaddr=SUN4_300_MEMREG_PHYSADDR; - sun4_clock_physaddr=SUN4_300_CLOCK_PHYSADDR; - sun4_timer_physaddr=SUN4_300_TIMER_PHYSADDR; - sun4_eth_physaddr=SUN4_300_ETH_PHYSADDR; - sun4_si_physaddr=SUN4_UNUSED_PHYSADDR; - sun4_bwtwo_physaddr=SUN4_300_BWTWO_PHYSADDR; - sun4_dma_physaddr=SUN4_300_DMA_PHYSADDR; - sun4_esp_physaddr=SUN4_300_ESP_PHYSADDR; - break; - case (SM_SUN4 | SM_4_470 ): - printk("Setup for a SUN4/470\n"); - sun4_memreg_physaddr=SUN4_400_MEMREG_PHYSADDR; - sun4_clock_physaddr=SUN4_400_CLOCK_PHYSADDR; - sun4_timer_physaddr=SUN4_400_TIMER_PHYSADDR; - sun4_eth_physaddr=SUN4_400_ETH_PHYSADDR; - sun4_si_physaddr=SUN4_UNUSED_PHYSADDR; - sun4_bwtwo_physaddr=SUN4_400_BWTWO_PHYSADDR; - sun4_dma_physaddr=SUN4_400_DMA_PHYSADDR; - sun4_esp_physaddr=SUN4_400_ESP_PHYSADDR; - break; - default: - ; - } -} - diff --git a/arch/sparc/kernel/sys_sparc.c b/arch/sparc/kernel/sys_sparc.c index 4d73421559c..03035c852a4 100644 --- a/arch/sparc/kernel/sys_sparc.c +++ b/arch/sparc/kernel/sys_sparc.c @@ -53,7 +53,7 @@ unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr, unsi /* See asm-sparc/uaccess.h */ if (len > TASK_SIZE - PAGE_SIZE) return -ENOMEM; - if (ARCH_SUN4C_SUN4 && len > 0x20000000) + if (ARCH_SUN4C && len > 0x20000000) return -ENOMEM; if (!addr) addr = TASK_UNMAPPED_BASE; @@ -65,7 +65,7 @@ unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr, unsi for (vmm = find_vma(current->mm, addr); ; vmm = vmm->vm_next) { /* At this point: (!vmm || addr < vmm->vm_end). */ - if (ARCH_SUN4C_SUN4 && addr < 0xe0000000 && 0x20000000 - len < addr) { + if (ARCH_SUN4C && addr < 0xe0000000 && 0x20000000 - len < addr) { addr = PAGE_OFFSET; vmm = find_vma(current->mm, PAGE_OFFSET); } @@ -81,7 +81,7 @@ unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr, unsi asmlinkage unsigned long sparc_brk(unsigned long brk) { - if(ARCH_SUN4C_SUN4) { + if(ARCH_SUN4C) { if ((brk & 0xe0000000) != (current->mm->brk & 0xe0000000)) return current->mm->brk; } @@ -221,7 +221,7 @@ out: int sparc_mmap_check(unsigned long addr, unsigned long len) { - if (ARCH_SUN4C_SUN4 && + if (ARCH_SUN4C && (len > 0x20000000 || (addr < 0xe0000000 && addr + len > 0x20000000))) return -EINVAL; diff --git a/arch/sparc/kernel/tick14.c b/arch/sparc/kernel/tick14.c index 707bfda8657..138bbf5f872 100644 --- a/arch/sparc/kernel/tick14.c +++ b/arch/sparc/kernel/tick14.c @@ -1,31 +1,12 @@ /* tick14.c - * linux/arch/sparc/kernel/tick14.c * * Copyright (C) 1996 David Redman (djhr@tadpole.co.uk) * * This file handles the Sparc specific level14 ticker * This is really useful for profiling OBP uses it for keyboard * aborts and other stuff. - * - * */ -#include <linux/errno.h> -#include <linux/sched.h> #include <linux/kernel.h> -#include <linux/param.h> -#include <linux/string.h> -#include <linux/mm.h> -#include <linux/timex.h> -#include <linux/interrupt.h> - -#include <asm/oplib.h> -#include <asm/timer.h> -#include <asm/mostek.h> -#include <asm/system.h> -#include <asm/irq.h> -#include <asm/io.h> - -#include "irq.h" extern unsigned long lvl14_save[5]; static unsigned long *linux_lvl14 = NULL; @@ -56,31 +37,3 @@ void install_obp_ticker(void) linux_lvl14[2] = obp_lvl14[2]; linux_lvl14[3] = obp_lvl14[3]; } - -void claim_ticker14(irq_handler_t handler, - int irq_nr, unsigned int timeout ) -{ - int cpu = smp_processor_id(); - - /* first we copy the obp handler instructions - */ - __disable_irq(irq_nr); - if (!handler) - return; - - linux_lvl14 = (unsigned long *)lvl14_save[4]; - obp_lvl14[0] = linux_lvl14[0]; - obp_lvl14[1] = linux_lvl14[1]; - obp_lvl14[2] = linux_lvl14[2]; - obp_lvl14[3] = linux_lvl14[3]; - - if (!request_irq(irq_nr, - handler, - (IRQF_DISABLED | SA_STATIC_ALLOC), - "counter14", - NULL)) { - install_linux_ticker(); - load_profile_irq(cpu, timeout); - __enable_irq(irq_nr); - } -} diff --git a/arch/sparc/kernel/time.c b/arch/sparc/kernel/time.c index 0762f5db192..62c1d94cb43 100644 --- a/arch/sparc/kernel/time.c +++ b/arch/sparc/kernel/time.c @@ -23,22 +23,24 @@ #include <linux/mm.h> #include <linux/interrupt.h> #include <linux/time.h> +#include <linux/rtc.h> +#include <linux/rtc/m48t59.h> #include <linux/timex.h> #include <linux/init.h> #include <linux/pci.h> #include <linux/ioport.h> #include <linux/profile.h> +#include <linux/of.h> #include <linux/of_device.h> +#include <linux/platform_device.h> #include <asm/oplib.h> #include <asm/timer.h> -#include <asm/mostek.h> #include <asm/system.h> #include <asm/irq.h> #include <asm/io.h> #include <asm/idprom.h> #include <asm/machines.h> -#include <asm/sun4paddr.h> #include <asm/page.h> #include <asm/pcic.h> #include <asm/irq_regs.h> @@ -46,34 +48,9 @@ #include "irq.h" DEFINE_SPINLOCK(rtc_lock); -static enum sparc_clock_type sp_clock_typ; -DEFINE_SPINLOCK(mostek_lock); -void __iomem *mstk48t02_regs = NULL; -static struct mostek48t08 __iomem *mstk48t08_regs = NULL; static int set_rtc_mmss(unsigned long); static int sbus_do_settimeofday(struct timespec *tv); -#ifdef CONFIG_SUN4 -struct intersil *intersil_clock; -#define intersil_cmd(intersil_reg, intsil_cmd) intersil_reg->int_cmd_reg = \ - (intsil_cmd) - -#define intersil_intr(intersil_reg, intsil_cmd) intersil_reg->int_intr_reg = \ - (intsil_cmd) - -#define intersil_start(intersil_reg) intersil_cmd(intersil_reg, \ - ( INTERSIL_START | INTERSIL_32K | INTERSIL_NORMAL | INTERSIL_24H |\ - INTERSIL_INTR_ENABLE)) - -#define intersil_stop(intersil_reg) intersil_cmd(intersil_reg, \ - ( INTERSIL_STOP | INTERSIL_32K | INTERSIL_NORMAL | INTERSIL_24H |\ - INTERSIL_INTR_ENABLE)) - -#define intersil_read_intr(intersil_reg, towhere) towhere = \ - intersil_reg->int_intr_reg - -#endif - unsigned long profile_pc(struct pt_regs *regs) { extern char __copy_user_begin[], __copy_user_end[]; @@ -96,7 +73,6 @@ unsigned long profile_pc(struct pt_regs *regs) EXPORT_SYMBOL(profile_pc); __volatile__ unsigned int *master_l10_counter; -__volatile__ unsigned int *master_l10_limit; /* * timer_interrupt() needs to keep up the real-time clock, @@ -116,15 +92,7 @@ static irqreturn_t timer_interrupt(int dummy, void *dev_id) /* Protect counter clear so that do_gettimeoffset works */ write_seqlock(&xtime_lock); -#ifdef CONFIG_SUN4 - if((idprom->id_machtype == (SM_SUN4 | SM_4_260)) || - (idprom->id_machtype == (SM_SUN4 | SM_4_110))) { - int temp; - intersil_read_intr(intersil_clock, temp); - /* re-enable the irq */ - enable_pil_irq(10); - } -#endif + clear_clock_irq(); do_timer(1); @@ -147,157 +115,56 @@ static irqreturn_t timer_interrupt(int dummy, void *dev_id) return IRQ_HANDLED; } -/* Kick start a stopped clock (procedure from the Sun NVRAM/hostid FAQ). */ -static void __devinit kick_start_clock(void) +static unsigned char mostek_read_byte(struct device *dev, u32 ofs) { - struct mostek48t02 *regs = (struct mostek48t02 *)mstk48t02_regs; - unsigned char sec; - int i, count; - - prom_printf("CLOCK: Clock was stopped. Kick start "); - - spin_lock_irq(&mostek_lock); - - /* Turn on the kick start bit to start the oscillator. */ - regs->creg |= MSTK_CREG_WRITE; - regs->sec &= ~MSTK_STOP; - regs->hour |= MSTK_KICK_START; - regs->creg &= ~MSTK_CREG_WRITE; - - spin_unlock_irq(&mostek_lock); - - /* Delay to allow the clock oscillator to start. */ - sec = MSTK_REG_SEC(regs); - for (i = 0; i < 3; i++) { - while (sec == MSTK_REG_SEC(regs)) - for (count = 0; count < 100000; count++) - /* nothing */ ; - prom_printf("."); - sec = regs->sec; - } - prom_printf("\n"); - - spin_lock_irq(&mostek_lock); - - /* Turn off kick start and set a "valid" time and date. */ - regs->creg |= MSTK_CREG_WRITE; - regs->hour &= ~MSTK_KICK_START; - MSTK_SET_REG_SEC(regs,0); - MSTK_SET_REG_MIN(regs,0); - MSTK_SET_REG_HOUR(regs,0); - MSTK_SET_REG_DOW(regs,5); - MSTK_SET_REG_DOM(regs,1); - MSTK_SET_REG_MONTH(regs,8); - MSTK_SET_REG_YEAR(regs,1996 - MSTK_YEAR_ZERO); - regs->creg &= ~MSTK_CREG_WRITE; - - spin_unlock_irq(&mostek_lock); - - /* Ensure the kick start bit is off. If it isn't, turn it off. */ - while (regs->hour & MSTK_KICK_START) { - prom_printf("CLOCK: Kick start still on!\n"); - - spin_lock_irq(&mostek_lock); - regs->creg |= MSTK_CREG_WRITE; - regs->hour &= ~MSTK_KICK_START; - regs->creg &= ~MSTK_CREG_WRITE; - spin_unlock_irq(&mostek_lock); + struct platform_device *pdev = to_platform_device(dev); + struct m48t59_plat_data *pdata = pdev->dev.platform_data; + void __iomem *regs = pdata->ioaddr; + unsigned char val = readb(regs + ofs); + + /* the year 0 is 1968 */ + if (ofs == pdata->offset + M48T59_YEAR) { + val += 0x68; + if ((val & 0xf) > 9) + val += 6; } - - prom_printf("CLOCK: Kick start procedure successful.\n"); -} - -/* Return nonzero if the clock chip battery is low. */ -static inline int has_low_battery(void) -{ - struct mostek48t02 *regs = (struct mostek48t02 *)mstk48t02_regs; - unsigned char data1, data2; - - spin_lock_irq(&mostek_lock); - data1 = regs->eeprom[0]; /* Read some data. */ - regs->eeprom[0] = ~data1; /* Write back the complement. */ - data2 = regs->eeprom[0]; /* Read back the complement. */ - regs->eeprom[0] = data1; /* Restore the original value. */ - spin_unlock_irq(&mostek_lock); - - return (data1 == data2); /* Was the write blocked? */ + return val; } -static void __devinit mostek_set_system_time(void) +static void mostek_write_byte(struct device *dev, u32 ofs, u8 val) { - unsigned int year, mon, day, hour, min, sec; - struct mostek48t02 *mregs; - - mregs = (struct mostek48t02 *)mstk48t02_regs; - if(!mregs) { - prom_printf("Something wrong, clock regs not mapped yet.\n"); - prom_halt(); - } - spin_lock_irq(&mostek_lock); - mregs->creg |= MSTK_CREG_READ; - sec = MSTK_REG_SEC(mregs); - min = MSTK_REG_MIN(mregs); - hour = MSTK_REG_HOUR(mregs); - day = MSTK_REG_DOM(mregs); - mon = MSTK_REG_MONTH(mregs); - year = MSTK_CVT_YEAR( MSTK_REG_YEAR(mregs) ); - xtime.tv_sec = mktime(year, mon, day, hour, min, sec); - xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ); - set_normalized_timespec(&wall_to_monotonic, - -xtime.tv_sec, -xtime.tv_nsec); - mregs->creg &= ~MSTK_CREG_READ; - spin_unlock_irq(&mostek_lock); + struct platform_device *pdev = to_platform_device(dev); + struct m48t59_plat_data *pdata = pdev->dev.platform_data; + void __iomem *regs = pdata->ioaddr; + + if (ofs == pdata->offset + M48T59_YEAR) { + if (val < 0x68) + val += 0x32; + else + val -= 0x68; + if ((val & 0xf) > 9) + val += 6; + if ((val & 0xf0) > 0x9A) + val += 0x60; + } + writeb(val, regs + ofs); } -/* Probe for the real time clock chip on Sun4 */ -static inline void sun4_clock_probe(void) -{ -#ifdef CONFIG_SUN4 - int temp; - struct resource r; - - memset(&r, 0, sizeof(r)); - if( idprom->id_machtype == (SM_SUN4 | SM_4_330) ) { - sp_clock_typ = MSTK48T02; - r.start = sun4_clock_physaddr; - mstk48t02_regs = sbus_ioremap(&r, 0, - sizeof(struct mostek48t02), NULL); - mstk48t08_regs = NULL; /* To catch weirdness */ - intersil_clock = NULL; /* just in case */ - - /* Kick start the clock if it is completely stopped. */ - if (mostek_read(mstk48t02_regs + MOSTEK_SEC) & MSTK_STOP) - kick_start_clock(); - } else if( idprom->id_machtype == (SM_SUN4 | SM_4_260)) { - /* intersil setup code */ - printk("Clock: INTERSIL at %8x ",sun4_clock_physaddr); - sp_clock_typ = INTERSIL; - r.start = sun4_clock_physaddr; - intersil_clock = (struct intersil *) - sbus_ioremap(&r, 0, sizeof(*intersil_clock), "intersil"); - mstk48t02_regs = 0; /* just be sure */ - mstk48t08_regs = NULL; /* ditto */ - /* initialise the clock */ - - intersil_intr(intersil_clock,INTERSIL_INT_100HZ); - - intersil_start(intersil_clock); - - intersil_read_intr(intersil_clock, temp); - while (!(temp & 0x80)) - intersil_read_intr(intersil_clock, temp); - - intersil_read_intr(intersil_clock, temp); - while (!(temp & 0x80)) - intersil_read_intr(intersil_clock, temp); - - intersil_stop(intersil_clock); +static struct m48t59_plat_data m48t59_data = { + .read_byte = mostek_read_byte, + .write_byte = mostek_write_byte, +}; - } -#endif -} +/* resource is set at runtime */ +static struct platform_device m48t59_rtc = { + .name = "rtc-m48t59", + .id = 0, + .num_resources = 1, + .dev = { + .platform_data = &m48t59_data, + }, +}; -#ifndef CONFIG_SUN4 static int __devinit clock_probe(struct of_device *op, const struct of_device_id *match) { struct device_node *dp = op->node; @@ -306,38 +173,26 @@ static int __devinit clock_probe(struct of_device *op, const struct of_device_id if (!model) return -ENODEV; + m48t59_rtc.resource = &op->resource[0]; if (!strcmp(model, "mk48t02")) { - sp_clock_typ = MSTK48T02; - /* Map the clock register io area read-only */ - mstk48t02_regs = of_ioremap(&op->resource[0], 0, - sizeof(struct mostek48t02), - "mk48t02"); - mstk48t08_regs = NULL; /* To catch weirdness */ + m48t59_data.ioaddr = of_ioremap(&op->resource[0], 0, + 2048, "rtc-m48t59"); + m48t59_data.type = M48T59RTC_TYPE_M48T02; } else if (!strcmp(model, "mk48t08")) { - sp_clock_typ = MSTK48T08; - mstk48t08_regs = of_ioremap(&op->resource[0], 0, - sizeof(struct mostek48t08), - "mk48t08"); - - mstk48t02_regs = &mstk48t08_regs->regs; + m48t59_data.ioaddr = of_ioremap(&op->resource[0], 0, + 8192, "rtc-m48t59"); + m48t59_data.type = M48T59RTC_TYPE_M48T08; } else return -ENODEV; - /* Report a low battery voltage condition. */ - if (has_low_battery()) - printk(KERN_CRIT "NVRAM: Low battery voltage!\n"); - - /* Kick start the clock if it is completely stopped. */ - if (mostek_read(mstk48t02_regs + MOSTEK_SEC) & MSTK_STOP) - kick_start_clock(); - - mostek_set_system_time(); + if (platform_device_register(&m48t59_rtc) < 0) + printk(KERN_ERR "Registering RTC device failed\n"); return 0; } -static struct of_device_id clock_match[] = { +static struct of_device_id __initdata clock_match[] = { { .name = "eeprom", }, @@ -348,7 +203,7 @@ static struct of_platform_driver clock_driver = { .match_table = clock_match, .probe = clock_probe, .driver = { - .name = "clock", + .name = "rtc", }, }; @@ -364,7 +219,6 @@ static int __init clock_init(void) * need to see the clock registers. */ fs_initcall(clock_init); -#endif /* !CONFIG_SUN4 */ static void __init sbus_time_init(void) { @@ -372,51 +226,8 @@ static void __init sbus_time_init(void) BTFIXUPSET_CALL(bus_do_settimeofday, sbus_do_settimeofday, BTFIXUPCALL_NORM); btfixup(); - if (ARCH_SUN4) - sun4_clock_probe(); - sparc_init_timers(timer_interrupt); -#ifdef CONFIG_SUN4 - if(idprom->id_machtype == (SM_SUN4 | SM_4_330)) { - mostek_set_system_time(); - } else if(idprom->id_machtype == (SM_SUN4 | SM_4_260) ) { - /* initialise the intersil on sun4 */ - unsigned int year, mon, day, hour, min, sec; - int temp; - struct intersil *iregs; - - iregs=intersil_clock; - if(!iregs) { - prom_printf("Something wrong, clock regs not mapped yet.\n"); - prom_halt(); - } - - intersil_intr(intersil_clock,INTERSIL_INT_100HZ); - disable_pil_irq(10); - intersil_stop(iregs); - intersil_read_intr(intersil_clock, temp); - - temp = iregs->clk.int_csec; - - sec = iregs->clk.int_sec; - min = iregs->clk.int_min; - hour = iregs->clk.int_hour; - day = iregs->clk.int_day; - mon = iregs->clk.int_month; - year = MSTK_CVT_YEAR(iregs->clk.int_year); - - enable_pil_irq(10); - intersil_start(iregs); - - xtime.tv_sec = mktime(year, mon, day, hour, min, sec); - xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ); - set_normalized_timespec(&wall_to_monotonic, - -xtime.tv_sec, -xtime.tv_nsec); - printk("%u/%u/%u %u:%u:%u\n",day,mon,year,hour,min,sec); - } -#endif - /* Now that OBP ticker has been silenced, it is safe to enable IRQ. */ local_irq_enable(); } @@ -522,80 +333,15 @@ static int sbus_do_settimeofday(struct timespec *tv) return 0; } -/* - * BUG: This routine does not handle hour overflow properly; it just - * sets the minutes. Usually you won't notice until after reboot! - */ -static int set_rtc_mmss(unsigned long nowtime) +static int set_rtc_mmss(unsigned long secs) { - int real_seconds, real_minutes, mostek_minutes; - struct mostek48t02 *regs = (struct mostek48t02 *)mstk48t02_regs; - unsigned long flags; -#ifdef CONFIG_SUN4 - struct intersil *iregs = intersil_clock; - int temp; -#endif + struct rtc_device *rtc = rtc_class_open("rtc0"); + int err = -1; - /* Not having a register set can lead to trouble. */ - if (!regs) { -#ifdef CONFIG_SUN4 - if(!iregs) - return -1; - else { - temp = iregs->clk.int_csec; - - mostek_minutes = iregs->clk.int_min; - - real_seconds = nowtime % 60; - real_minutes = nowtime / 60; - if (((abs(real_minutes - mostek_minutes) + 15)/30) & 1) - real_minutes += 30; /* correct for half hour time zone */ - real_minutes %= 60; - - if (abs(real_minutes - mostek_minutes) < 30) { - intersil_stop(iregs); - iregs->clk.int_sec=real_seconds; - iregs->clk.int_min=real_minutes; - intersil_start(iregs); - } else { - printk(KERN_WARNING - "set_rtc_mmss: can't update from %d to %d\n", - mostek_minutes, real_minutes); - return -1; - } - - return 0; - } -#endif + if (rtc) { + err = rtc_set_mmss(rtc, secs); + rtc_class_close(rtc); } - spin_lock_irqsave(&mostek_lock, flags); - /* Read the current RTC minutes. */ - regs->creg |= MSTK_CREG_READ; - mostek_minutes = MSTK_REG_MIN(regs); - regs->creg &= ~MSTK_CREG_READ; - - /* - * since we're only adjusting minutes and seconds, - * don't interfere with hour overflow. This avoids - * messing with unknown time zones but requires your - * RTC not to be off by more than 15 minutes - */ - real_seconds = nowtime % 60; - real_minutes = nowtime / 60; - if (((abs(real_minutes - mostek_minutes) + 15)/30) & 1) - real_minutes += 30; /* correct for half hour time zone */ - real_minutes %= 60; - - if (abs(real_minutes - mostek_minutes) < 30) { - regs->creg |= MSTK_CREG_WRITE; - MSTK_SET_REG_SEC(regs,real_seconds); - MSTK_SET_REG_MIN(regs,real_minutes); - regs->creg &= ~MSTK_CREG_WRITE; - spin_unlock_irqrestore(&mostek_lock, flags); - return 0; - } else { - spin_unlock_irqrestore(&mostek_lock, flags); - return -1; - } + return err; } diff --git a/arch/sparc/kernel/traps.c b/arch/sparc/kernel/traps.c index 5d45d5fd8c9..2b7d5065903 100644 --- a/arch/sparc/kernel/traps.c +++ b/arch/sparc/kernel/traps.c @@ -43,23 +43,6 @@ void syscall_trace_exit(struct pt_regs *regs) { } -void sun4m_nmi(struct pt_regs *regs) -{ - unsigned long afsr, afar; - - printk("Aieee: sun4m NMI received!\n"); - /* XXX HyperSparc hack XXX */ - __asm__ __volatile__("mov 0x500, %%g1\n\t" - "lda [%%g1] 0x4, %0\n\t" - "mov 0x600, %%g1\n\t" - "lda [%%g1] 0x4, %1\n\t" : - "=r" (afsr), "=r" (afar)); - printk("afsr=%08lx afar=%08lx\n", afsr, afar); - printk("you lose buddy boy...\n"); - show_regs(regs); - prom_halt(); -} - void sun4d_nmi(struct pt_regs *regs) { printk("Aieee: sun4d NMI received!\n"); diff --git a/arch/sparc/mm/Makefile b/arch/sparc/mm/Makefile index 109c8b22cb3..ea88955d97f 100644 --- a/arch/sparc/mm/Makefile +++ b/arch/sparc/mm/Makefile @@ -3,13 +3,8 @@ EXTRA_AFLAGS := -ansi -obj-y := fault.o init.o loadmmu.o generic.o extable.o btfixup.o - -ifeq ($(CONFIG_SUN4),y) -obj-y += nosrmmu.o -else -obj-y += srmmu.o iommu.o io-unit.o hypersparc.o viking.o tsunami.o swift.o -endif +obj-y := fault.o init.o loadmmu.o generic.o extable.o btfixup.o \ + srmmu.o iommu.o io-unit.o hypersparc.o viking.o tsunami.o swift.o ifdef CONFIG_HIGHMEM obj-y += highmem.o diff --git a/arch/sparc/mm/btfixup.c b/arch/sparc/mm/btfixup.c index a312d127d47..5175ac2f482 100644 --- a/arch/sparc/mm/btfixup.c +++ b/arch/sparc/mm/btfixup.c @@ -20,11 +20,7 @@ extern char *srmmu_name; static char version[] __initdata = "Boot time fixup v1.6. 4/Mar/98 Jakub Jelinek (jj@ultra.linux.cz). Patching kernel for "; -#ifdef CONFIG_SUN4 -static char str_sun4c[] __initdata = "sun4\n"; -#else static char str_sun4c[] __initdata = "sun4c\n"; -#endif static char str_srmmu[] __initdata = "srmmu[%s]/"; static char str_iommu[] __initdata = "iommu\n"; static char str_iounit[] __initdata = "io-unit\n"; @@ -86,7 +82,7 @@ void __init btfixup(void) if (!visited) { visited++; printk(version); - if (ARCH_SUN4C_SUN4) + if (ARCH_SUN4C) printk(str_sun4c); else { printk(str_srmmu, srmmu_name); diff --git a/arch/sparc/mm/fault.c b/arch/sparc/mm/fault.c index 3604c2e8670..a507e117466 100644 --- a/arch/sparc/mm/fault.c +++ b/arch/sparc/mm/fault.c @@ -191,7 +191,7 @@ asmlinkage void do_sparc_fault(struct pt_regs *regs, int text_fault, int write, * only copy the information from the master page table, * nothing more. */ - if (!ARCH_SUN4C_SUN4 && address >= TASK_SIZE) + if (!ARCH_SUN4C && address >= TASK_SIZE) goto vmalloc_fault; info.si_code = SEGV_MAPERR; diff --git a/arch/sparc/mm/init.c b/arch/sparc/mm/init.c index e103f1bb377..677c1e187a2 100644 --- a/arch/sparc/mm/init.c +++ b/arch/sparc/mm/init.c @@ -23,6 +23,7 @@ #include <linux/highmem.h> #include <linux/bootmem.h> #include <linux/pagemap.h> +#include <linux/poison.h> #include <asm/system.h> #include <asm/vac-ops.h> @@ -480,6 +481,7 @@ void free_initmem (void) for (; addr < (unsigned long)(&__init_end); addr += PAGE_SIZE) { struct page *p; + memset((void *)addr, POISON_FREE_INITMEM, PAGE_SIZE); p = virt_to_page(addr); ClearPageReserved(p); @@ -488,20 +490,26 @@ void free_initmem (void) totalram_pages++; num_physpages++; } - printk (KERN_INFO "Freeing unused kernel memory: %dk freed\n", (&__init_end - &__init_begin) >> 10); + printk(KERN_INFO "Freeing unused kernel memory: %dk freed\n", + (&__init_end - &__init_begin) >> 10); } #ifdef CONFIG_BLK_DEV_INITRD void free_initrd_mem(unsigned long start, unsigned long end) { if (start < end) - printk (KERN_INFO "Freeing initrd memory: %ldk freed\n", (end - start) >> 10); + printk(KERN_INFO "Freeing initrd memory: %ldk freed\n", + (end - start) >> 10); for (; start < end; start += PAGE_SIZE) { - struct page *p = virt_to_page(start); + struct page *p; + + memset((void *)start, POISON_FREE_INITMEM, PAGE_SIZE); + p = virt_to_page(start); ClearPageReserved(p); init_page_count(p); __free_page(p); + totalram_pages++; num_physpages++; } } diff --git a/arch/sparc/mm/io-unit.c b/arch/sparc/mm/io-unit.c index f167835db3d..daadf5f8805 100644 --- a/arch/sparc/mm/io-unit.c +++ b/arch/sparc/mm/io-unit.c @@ -12,10 +12,11 @@ #include <linux/highmem.h> /* pte_offset_map => kmap_atomic */ #include <linux/bitops.h> #include <linux/scatterlist.h> +#include <linux/of.h> +#include <linux/of_device.h> #include <asm/pgalloc.h> #include <asm/pgtable.h> -#include <asm/sbus.h> #include <asm/io.h> #include <asm/io-unit.h> #include <asm/mxcc.h> @@ -34,13 +35,10 @@ #define IOPERM (IOUPTE_CACHE | IOUPTE_WRITE | IOUPTE_VALID) #define MKIOPTE(phys) __iopte((((phys)>>4) & IOUPTE_PAGE) | IOPERM) -void __init -iounit_init(int sbi_node, int io_node, struct sbus_bus *sbus) +static void __init iounit_iommu_init(struct of_device *op) { - iopte_t *xpt, *xptend; struct iounit_struct *iounit; - struct linux_prom_registers iommu_promregs[PROMREG_MAX]; - struct resource r; + iopte_t *xpt, *xptend; iounit = kzalloc(sizeof(struct iounit_struct), GFP_ATOMIC); if (!iounit) { @@ -55,18 +53,13 @@ iounit_init(int sbi_node, int io_node, struct sbus_bus *sbus) iounit->rotor[1] = IOUNIT_BMAP2_START; iounit->rotor[2] = IOUNIT_BMAPM_START; - xpt = NULL; - if(prom_getproperty(sbi_node, "reg", (void *) iommu_promregs, - sizeof(iommu_promregs)) != -1) { - prom_apply_generic_ranges(io_node, 0, iommu_promregs, 3); - memset(&r, 0, sizeof(r)); - r.flags = iommu_promregs[2].which_io; - r.start = iommu_promregs[2].phys_addr; - xpt = (iopte_t *) sbus_ioremap(&r, 0, PAGE_SIZE * 16, "XPT"); + xpt = of_ioremap(&op->resource[2], 0, PAGE_SIZE * 16, "XPT"); + if (!xpt) { + prom_printf("SUN4D: Cannot map External Page Table."); + prom_halt(); } - if(!xpt) panic("Cannot map External Page Table."); - sbus->ofdev.dev.archdata.iommu = iounit; + op->dev.archdata.iommu = iounit; iounit->page_table = xpt; spin_lock_init(&iounit->lock); @@ -75,6 +68,25 @@ iounit_init(int sbi_node, int io_node, struct sbus_bus *sbus) iopte_val(*xpt++) = 0; } +static int __init iounit_init(void) +{ + extern void sun4d_init_sbi_irq(void); + struct device_node *dp; + + for_each_node_by_name(dp, "sbi") { + struct of_device *op = of_find_device_by_node(dp); + + iounit_iommu_init(op); + of_propagate_archdata(op); + } + + sun4d_init_sbi_irq(); + + return 0; +} + +subsys_initcall(iounit_init); + /* One has to hold iounit->lock to call this */ static unsigned long iounit_get_area(struct iounit_struct *iounit, unsigned long vaddr, int size) { @@ -124,10 +136,10 @@ nexti: scan = find_next_zero_bit(iounit->bmap, limit, scan); return vaddr; } -static __u32 iounit_get_scsi_one(char *vaddr, unsigned long len, struct sbus_bus *sbus) +static __u32 iounit_get_scsi_one(struct device *dev, char *vaddr, unsigned long len) { + struct iounit_struct *iounit = dev->archdata.iommu; unsigned long ret, flags; - struct iounit_struct *iounit = sbus->ofdev.dev.archdata.iommu; spin_lock_irqsave(&iounit->lock, flags); ret = iounit_get_area(iounit, (unsigned long)vaddr, len); @@ -135,10 +147,10 @@ static __u32 iounit_get_scsi_one(char *vaddr, unsigned long len, struct sbus_bus return ret; } -static void iounit_get_scsi_sgl(struct scatterlist *sg, int sz, struct sbus_bus *sbus) +static void iounit_get_scsi_sgl(struct device *dev, struct scatterlist *sg, int sz) { + struct iounit_struct *iounit = dev->archdata.iommu; unsigned long flags; - struct iounit_struct *iounit = sbus->ofdev.dev.archdata.iommu; /* FIXME: Cache some resolved pages - often several sg entries are to the same page */ spin_lock_irqsave(&iounit->lock, flags); @@ -151,10 +163,10 @@ static void iounit_get_scsi_sgl(struct scatterlist *sg, int sz, struct sbus_bus spin_unlock_irqrestore(&iounit->lock, flags); } -static void iounit_release_scsi_one(__u32 vaddr, unsigned long len, struct sbus_bus *sbus) +static void iounit_release_scsi_one(struct device *dev, __u32 vaddr, unsigned long len) { + struct iounit_struct *iounit = dev->archdata.iommu; unsigned long flags; - struct iounit_struct *iounit = sbus->ofdev.dev.archdata.iommu; spin_lock_irqsave(&iounit->lock, flags); len = ((vaddr & ~PAGE_MASK) + len + (PAGE_SIZE-1)) >> PAGE_SHIFT; @@ -165,11 +177,11 @@ static void iounit_release_scsi_one(__u32 vaddr, unsigned long len, struct sbus_ spin_unlock_irqrestore(&iounit->lock, flags); } -static void iounit_release_scsi_sgl(struct scatterlist *sg, int sz, struct sbus_bus *sbus) +static void iounit_release_scsi_sgl(struct device *dev, struct scatterlist *sg, int sz) { + struct iounit_struct *iounit = dev->archdata.iommu; unsigned long flags; unsigned long vaddr, len; - struct iounit_struct *iounit = sbus->ofdev.dev.archdata.iommu; spin_lock_irqsave(&iounit->lock, flags); while (sz != 0) { @@ -185,12 +197,12 @@ static void iounit_release_scsi_sgl(struct scatterlist *sg, int sz, struct sbus_ } #ifdef CONFIG_SBUS -static int iounit_map_dma_area(dma_addr_t *pba, unsigned long va, __u32 addr, int len) +static int iounit_map_dma_area(struct device *dev, dma_addr_t *pba, unsigned long va, __u32 addr, int len) { + struct iounit_struct *iounit = dev->archdata.iommu; unsigned long page, end; pgprot_t dvma_prot; iopte_t *iopte; - struct sbus_bus *sbus; *pba = addr; @@ -212,12 +224,8 @@ static int iounit_map_dma_area(dma_addr_t *pba, unsigned long va, __u32 addr, in i = ((addr - IOUNIT_DMA_BASE) >> PAGE_SHIFT); - for_each_sbus(sbus) { - struct iounit_struct *iounit = sbus->ofdev.dev.archdata.iommu; - - iopte = (iopte_t *)(iounit->page_table + i); - *iopte = MKIOPTE(__pa(page)); - } + iopte = (iopte_t *)(iounit->page_table + i); + *iopte = MKIOPTE(__pa(page)); } addr += PAGE_SIZE; va += PAGE_SIZE; @@ -228,23 +236,10 @@ static int iounit_map_dma_area(dma_addr_t *pba, unsigned long va, __u32 addr, in return 0; } -static void iounit_unmap_dma_area(unsigned long addr, int len) +static void iounit_unmap_dma_area(struct device *dev, unsigned long addr, int len) { /* XXX Somebody please fill this in */ } - -/* XXX We do not pass sbus device here, bad. */ -static struct page *iounit_translate_dvma(unsigned long addr) -{ - struct sbus_bus *sbus = sbus_root; /* They are all the same */ - struct iounit_struct *iounit = sbus->ofdev.dev.archdata.iommu; - int i; - iopte_t *iopte; - - i = ((addr - IOUNIT_DMA_BASE) >> PAGE_SHIFT); - iopte = (iopte_t *)(iounit->page_table + i); - return pfn_to_page(iopte_val(*iopte) >> (PAGE_SHIFT-4)); /* XXX sun4d guru, help */ -} #endif static char *iounit_lockarea(char *vaddr, unsigned long len) @@ -271,54 +266,5 @@ void __init ld_mmu_iounit(void) #ifdef CONFIG_SBUS BTFIXUPSET_CALL(mmu_map_dma_area, iounit_map_dma_area, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(mmu_unmap_dma_area, iounit_unmap_dma_area, BTFIXUPCALL_NORM); - BTFIXUPSET_CALL(mmu_translate_dvma, iounit_translate_dvma, BTFIXUPCALL_NORM); #endif } - -__u32 iounit_map_dma_init(struct sbus_bus *sbus, int size) -{ - int i, j, k, npages; - unsigned long rotor, scan, limit; - unsigned long flags; - __u32 ret; - struct iounit_struct *iounit = sbus->ofdev.dev.archdata.iommu; - - npages = (size + (PAGE_SIZE-1)) >> PAGE_SHIFT; - i = 0x0213; - spin_lock_irqsave(&iounit->lock, flags); -next: j = (i & 15); - rotor = iounit->rotor[j - 1]; - limit = iounit->limit[j]; - scan = rotor; -nexti: scan = find_next_zero_bit(iounit->bmap, limit, scan); - if (scan + npages > limit) { - if (limit != rotor) { - limit = rotor; - scan = iounit->limit[j - 1]; - goto nexti; - } - i >>= 4; - if (!(i & 15)) - panic("iounit_map_dma_init: Couldn't find free iopte slots for %d bytes\n", size); - goto next; - } - for (k = 1, scan++; k < npages; k++) - if (test_bit(scan++, iounit->bmap)) - goto nexti; - iounit->rotor[j - 1] = (scan < limit) ? scan : iounit->limit[j - 1]; - scan -= npages; - ret = IOUNIT_DMA_BASE + (scan << PAGE_SHIFT); - for (k = 0; k < npages; k++, scan++) - set_bit(scan, iounit->bmap); - spin_unlock_irqrestore(&iounit->lock, flags); - return ret; -} - -__u32 iounit_map_dma_page(__u32 vaddr, void *addr, struct sbus_bus *sbus) -{ - int scan = (vaddr - IOUNIT_DMA_BASE) >> PAGE_SHIFT; - struct iounit_struct *iounit = sbus->ofdev.dev.archdata.iommu; - - iounit->page_table[scan] = MKIOPTE(__pa(((unsigned long)addr) & PAGE_MASK)); - return vaddr + (((unsigned long)addr) & ~PAGE_MASK); -} diff --git a/arch/sparc/mm/iommu.c b/arch/sparc/mm/iommu.c index 4b934270f05..e7a499e3aa3 100644 --- a/arch/sparc/mm/iommu.c +++ b/arch/sparc/mm/iommu.c @@ -13,10 +13,11 @@ #include <linux/slab.h> #include <linux/highmem.h> /* pte_offset_map => kmap_atomic */ #include <linux/scatterlist.h> +#include <linux/of.h> +#include <linux/of_device.h> #include <asm/pgalloc.h> #include <asm/pgtable.h> -#include <asm/sbus.h> #include <asm/io.h> #include <asm/mxcc.h> #include <asm/mbus.h> @@ -55,30 +56,21 @@ static pgprot_t dvma_prot; /* Consistent mapping pte flags */ #define IOPERM (IOPTE_CACHE | IOPTE_WRITE | IOPTE_VALID) #define MKIOPTE(pfn, perm) (((((pfn)<<8) & IOPTE_PAGE) | (perm)) & ~IOPTE_WAZ) -void __init -iommu_init(int iommund, struct sbus_bus *sbus) +static void __init sbus_iommu_init(struct of_device *op) { - unsigned int impl, vers; - unsigned long tmp; struct iommu_struct *iommu; - struct linux_prom_registers iommu_promregs[PROMREG_MAX]; - struct resource r; + unsigned int impl, vers; unsigned long *bitmap; + unsigned long tmp; iommu = kmalloc(sizeof(struct iommu_struct), GFP_ATOMIC); if (!iommu) { prom_printf("Unable to allocate iommu structure\n"); prom_halt(); } - iommu->regs = NULL; - if (prom_getproperty(iommund, "reg", (void *) iommu_promregs, - sizeof(iommu_promregs)) != -1) { - memset(&r, 0, sizeof(r)); - r.flags = iommu_promregs[0].which_io; - r.start = iommu_promregs[0].phys_addr; - iommu->regs = (struct iommu_regs *) - sbus_ioremap(&r, 0, PAGE_SIZE * 3, "iommu_regs"); - } + + iommu->regs = of_ioremap(&op->resource[0], 0, PAGE_SIZE * 3, + "iommu_regs"); if (!iommu->regs) { prom_printf("Cannot map IOMMU registers\n"); prom_halt(); @@ -128,13 +120,29 @@ iommu_init(int iommund, struct sbus_bus *sbus) else iommu->usemap.num_colors = 1; - printk("IOMMU: impl %d vers %d table 0x%p[%d B] map [%d b]\n", - impl, vers, iommu->page_table, - (int)(IOMMU_NPTES*sizeof(iopte_t)), (int)IOMMU_NPTES); + printk(KERN_INFO "IOMMU: impl %d vers %d table 0x%p[%d B] map [%d b]\n", + impl, vers, iommu->page_table, + (int)(IOMMU_NPTES*sizeof(iopte_t)), (int)IOMMU_NPTES); + + op->dev.archdata.iommu = iommu; +} + +static int __init iommu_init(void) +{ + struct device_node *dp; + + for_each_node_by_name(dp, "iommu") { + struct of_device *op = of_find_device_by_node(dp); + + sbus_iommu_init(op); + of_propagate_archdata(op); + } - sbus->ofdev.dev.archdata.iommu = iommu; + return 0; } +subsys_initcall(iommu_init); + /* This begs to be btfixup-ed by srmmu. */ /* Flush the iotlb entries to ram. */ /* This could be better if we didn't have to flush whole pages. */ @@ -164,9 +172,9 @@ static void iommu_flush_iotlb(iopte_t *iopte, unsigned int niopte) } } -static u32 iommu_get_one(struct page *page, int npages, struct sbus_bus *sbus) +static u32 iommu_get_one(struct device *dev, struct page *page, int npages) { - struct iommu_struct *iommu = sbus->ofdev.dev.archdata.iommu; + struct iommu_struct *iommu = dev->archdata.iommu; int ioptex; iopte_t *iopte, *iopte0; unsigned int busa, busa0; @@ -194,8 +202,7 @@ static u32 iommu_get_one(struct page *page, int npages, struct sbus_bus *sbus) return busa0; } -static u32 iommu_get_scsi_one(char *vaddr, unsigned int len, - struct sbus_bus *sbus) +static u32 iommu_get_scsi_one(struct device *dev, char *vaddr, unsigned int len) { unsigned long off; int npages; @@ -205,22 +212,22 @@ static u32 iommu_get_scsi_one(char *vaddr, unsigned int len, off = (unsigned long)vaddr & ~PAGE_MASK; npages = (off + len + PAGE_SIZE-1) >> PAGE_SHIFT; page = virt_to_page((unsigned long)vaddr & PAGE_MASK); - busa = iommu_get_one(page, npages, sbus); + busa = iommu_get_one(dev, page, npages); return busa + off; } -static __u32 iommu_get_scsi_one_noflush(char *vaddr, unsigned long len, struct sbus_bus *sbus) +static __u32 iommu_get_scsi_one_noflush(struct device *dev, char *vaddr, unsigned long len) { - return iommu_get_scsi_one(vaddr, len, sbus); + return iommu_get_scsi_one(dev, vaddr, len); } -static __u32 iommu_get_scsi_one_gflush(char *vaddr, unsigned long len, struct sbus_bus *sbus) +static __u32 iommu_get_scsi_one_gflush(struct device *dev, char *vaddr, unsigned long len) { flush_page_for_dma(0); - return iommu_get_scsi_one(vaddr, len, sbus); + return iommu_get_scsi_one(dev, vaddr, len); } -static __u32 iommu_get_scsi_one_pflush(char *vaddr, unsigned long len, struct sbus_bus *sbus) +static __u32 iommu_get_scsi_one_pflush(struct device *dev, char *vaddr, unsigned long len) { unsigned long page = ((unsigned long) vaddr) & PAGE_MASK; @@ -228,23 +235,23 @@ static __u32 iommu_get_scsi_one_pflush(char *vaddr, unsigned long len, struct sb flush_page_for_dma(page); page += PAGE_SIZE; } - return iommu_get_scsi_one(vaddr, len, sbus); + return iommu_get_scsi_one(dev, vaddr, len); } -static void iommu_get_scsi_sgl_noflush(struct scatterlist *sg, int sz, struct sbus_bus *sbus) +static void iommu_get_scsi_sgl_noflush(struct device *dev, struct scatterlist *sg, int sz) { int n; while (sz != 0) { --sz; n = (sg->length + sg->offset + PAGE_SIZE-1) >> PAGE_SHIFT; - sg->dvma_address = iommu_get_one(sg_page(sg), n, sbus) + sg->offset; + sg->dvma_address = iommu_get_one(dev, sg_page(sg), n) + sg->offset; sg->dvma_length = (__u32) sg->length; sg = sg_next(sg); } } -static void iommu_get_scsi_sgl_gflush(struct scatterlist *sg, int sz, struct sbus_bus *sbus) +static void iommu_get_scsi_sgl_gflush(struct device *dev, struct scatterlist *sg, int sz) { int n; @@ -252,13 +259,13 @@ static void iommu_get_scsi_sgl_gflush(struct scatterlist *sg, int sz, struct sbu while (sz != 0) { --sz; n = (sg->length + sg->offset + PAGE_SIZE-1) >> PAGE_SHIFT; - sg->dvma_address = iommu_get_one(sg_page(sg), n, sbus) + sg->offset; + sg->dvma_address = iommu_get_one(dev, sg_page(sg), n) + sg->offset; sg->dvma_length = (__u32) sg->length; sg = sg_next(sg); } } -static void iommu_get_scsi_sgl_pflush(struct scatterlist *sg, int sz, struct sbus_bus *sbus) +static void iommu_get_scsi_sgl_pflush(struct device *dev, struct scatterlist *sg, int sz) { unsigned long page, oldpage = 0; int n, i; @@ -283,15 +290,15 @@ static void iommu_get_scsi_sgl_pflush(struct scatterlist *sg, int sz, struct sbu } } - sg->dvma_address = iommu_get_one(sg_page(sg), n, sbus) + sg->offset; + sg->dvma_address = iommu_get_one(dev, sg_page(sg), n) + sg->offset; sg->dvma_length = (__u32) sg->length; sg = sg_next(sg); } } -static void iommu_release_one(u32 busa, int npages, struct sbus_bus *sbus) +static void iommu_release_one(struct device *dev, u32 busa, int npages) { - struct iommu_struct *iommu = sbus->ofdev.dev.archdata.iommu; + struct iommu_struct *iommu = dev->archdata.iommu; int ioptex; int i; @@ -305,17 +312,17 @@ static void iommu_release_one(u32 busa, int npages, struct sbus_bus *sbus) bit_map_clear(&iommu->usemap, ioptex, npages); } -static void iommu_release_scsi_one(__u32 vaddr, unsigned long len, struct sbus_bus *sbus) +static void iommu_release_scsi_one(struct device *dev, __u32 vaddr, unsigned long len) { unsigned long off; int npages; off = vaddr & ~PAGE_MASK; npages = (off + len + PAGE_SIZE-1) >> PAGE_SHIFT; - iommu_release_one(vaddr & PAGE_MASK, npages, sbus); + iommu_release_one(dev, vaddr & PAGE_MASK, npages); } -static void iommu_release_scsi_sgl(struct scatterlist *sg, int sz, struct sbus_bus *sbus) +static void iommu_release_scsi_sgl(struct device *dev, struct scatterlist *sg, int sz) { int n; @@ -323,18 +330,18 @@ static void iommu_release_scsi_sgl(struct scatterlist *sg, int sz, struct sbus_b --sz; n = (sg->length + sg->offset + PAGE_SIZE-1) >> PAGE_SHIFT; - iommu_release_one(sg->dvma_address & PAGE_MASK, n, sbus); + iommu_release_one(dev, sg->dvma_address & PAGE_MASK, n); sg->dvma_address = 0x21212121; sg = sg_next(sg); } } #ifdef CONFIG_SBUS -static int iommu_map_dma_area(dma_addr_t *pba, unsigned long va, - unsigned long addr, int len) +static int iommu_map_dma_area(struct device *dev, dma_addr_t *pba, unsigned long va, + unsigned long addr, int len) { + struct iommu_struct *iommu = dev->archdata.iommu; unsigned long page, end; - struct iommu_struct *iommu = sbus_root->ofdev.dev.archdata.iommu; iopte_t *iopte = iommu->page_table; iopte_t *first; int ioptex; @@ -397,9 +404,9 @@ static int iommu_map_dma_area(dma_addr_t *pba, unsigned long va, return 0; } -static void iommu_unmap_dma_area(unsigned long busa, int len) +static void iommu_unmap_dma_area(struct device *dev, unsigned long busa, int len) { - struct iommu_struct *iommu = sbus_root->ofdev.dev.archdata.iommu; + struct iommu_struct *iommu = dev->archdata.iommu; iopte_t *iopte = iommu->page_table; unsigned long end; int ioptex = (busa - iommu->start) >> PAGE_SHIFT; @@ -417,15 +424,6 @@ static void iommu_unmap_dma_area(unsigned long busa, int len) iommu_invalidate(iommu->regs); bit_map_clear(&iommu->usemap, ioptex, len >> PAGE_SHIFT); } - -static struct page *iommu_translate_dvma(unsigned long busa) -{ - struct iommu_struct *iommu = sbus_root->ofdev.dev.archdata.iommu; - iopte_t *iopte = iommu->page_table; - - iopte += ((busa - iommu->start) >> PAGE_SHIFT); - return pfn_to_page((iopte_val(*iopte) & IOPTE_PAGE) >> (PAGE_SHIFT-4)); -} #endif static char *iommu_lockarea(char *vaddr, unsigned long len) @@ -461,7 +459,6 @@ void __init ld_mmu_iommu(void) #ifdef CONFIG_SBUS BTFIXUPSET_CALL(mmu_map_dma_area, iommu_map_dma_area, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(mmu_unmap_dma_area, iommu_unmap_dma_area, BTFIXUPCALL_NORM); - BTFIXUPSET_CALL(mmu_translate_dvma, iommu_translate_dvma, BTFIXUPCALL_NORM); #endif if (viking_mxcc_present || srmmu_modtype == HyperSparc) { diff --git a/arch/sparc/mm/nosrmmu.c b/arch/sparc/mm/nosrmmu.c deleted file mode 100644 index 3701f70fc30..00000000000 --- a/arch/sparc/mm/nosrmmu.c +++ /dev/null @@ -1,59 +0,0 @@ -/* - * nosrmmu.c: This file is a bunch of dummies for sun4 compiles, - * so that it does not need srmmu and avoid ifdefs. - * - * Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) - */ - -#include <linux/kernel.h> -#include <linux/mm.h> -#include <linux/init.h> -#include <asm/mbus.h> -#include <asm/sbus.h> - -static char shouldnothappen[] __initdata = "SUN4 kernel can only run on SUN4\n"; - -enum mbus_module srmmu_modtype; -void *srmmu_nocache_pool; - -int vac_cache_size = 0; - -static void __init should_not_happen(void) -{ - prom_printf(shouldnothappen); - prom_halt(); -} - -void __init srmmu_frob_mem_map(unsigned long start_mem) -{ - should_not_happen(); -} - -unsigned long __init srmmu_paging_init(unsigned long start_mem, unsigned long end_mem) -{ - should_not_happen(); - return 0; -} - -void __init ld_mmu_srmmu(void) -{ - should_not_happen(); -} - -void srmmu_mapioaddr(unsigned long physaddr, unsigned long virt_addr, int bus_type, int rdonly) -{ -} - -void srmmu_unmapioaddr(unsigned long virt_addr) -{ -} - -__u32 iounit_map_dma_init(struct sbus_bus *sbus, int size) -{ - return 0; -} - -__u32 iounit_map_dma_page(__u32 vaddr, void *addr, struct sbus_bus *sbus) -{ - return 0; -} diff --git a/arch/sparc/mm/srmmu.c b/arch/sparc/mm/srmmu.c index ee30462598f..6a5d7cabc04 100644 --- a/arch/sparc/mm/srmmu.c +++ b/arch/sparc/mm/srmmu.c @@ -31,7 +31,6 @@ #include <asm/mbus.h> #include <asm/cache.h> #include <asm/oplib.h> -#include <asm/sbus.h> #include <asm/asi.h> #include <asm/msi.h> #include <asm/mmu_context.h> diff --git a/arch/sparc/mm/sun4c.c b/arch/sparc/mm/sun4c.c index d1782f6368b..fe65aeeb394 100644 --- a/arch/sparc/mm/sun4c.c +++ b/arch/sparc/mm/sun4c.c @@ -31,7 +31,6 @@ #include <asm/oplib.h> #include <asm/openprom.h> #include <asm/mmu_context.h> -#include <asm/sun4paddr.h> #include <asm/highmem.h> #include <asm/btfixup.h> #include <asm/cacheflush.h> @@ -52,15 +51,11 @@ extern int num_segmaps, num_contexts; extern unsigned long page_kernel; -#ifdef CONFIG_SUN4 -#define SUN4C_VAC_SIZE sun4c_vacinfo.num_bytes -#else /* That's it, we prom_halt() on sun4c if the cache size is something other than 65536. * So let's save some cycles and just use that everywhere except for that bootup * sanity check. */ #define SUN4C_VAC_SIZE 65536 -#endif #define SUN4C_KERNEL_BUCKETS 32 @@ -285,75 +280,32 @@ void __init sun4c_probe_vac(void) { sun4c_disable_vac(); - if (ARCH_SUN4) { - switch (idprom->id_machtype) { - - case (SM_SUN4|SM_4_110): - sun4c_vacinfo.type = VAC_NONE; - sun4c_vacinfo.num_bytes = 0; - sun4c_vacinfo.linesize = 0; - sun4c_vacinfo.do_hwflushes = 0; - prom_printf("No VAC. Get some bucks and buy a real computer."); - prom_halt(); - break; - - case (SM_SUN4|SM_4_260): - sun4c_vacinfo.type = VAC_WRITE_BACK; - sun4c_vacinfo.num_bytes = 128 * 1024; - sun4c_vacinfo.linesize = 16; - sun4c_vacinfo.do_hwflushes = 0; - break; - - case (SM_SUN4|SM_4_330): - sun4c_vacinfo.type = VAC_WRITE_THROUGH; - sun4c_vacinfo.num_bytes = 128 * 1024; - sun4c_vacinfo.linesize = 16; - sun4c_vacinfo.do_hwflushes = 0; - break; - - case (SM_SUN4|SM_4_470): - sun4c_vacinfo.type = VAC_WRITE_BACK; - sun4c_vacinfo.num_bytes = 128 * 1024; - sun4c_vacinfo.linesize = 32; - sun4c_vacinfo.do_hwflushes = 0; - break; - - default: - prom_printf("Cannot initialize VAC - weird sun4 model idprom->id_machtype = %d", idprom->id_machtype); - prom_halt(); - }; + if ((idprom->id_machtype == (SM_SUN4C | SM_4C_SS1)) || + (idprom->id_machtype == (SM_SUN4C | SM_4C_SS1PLUS))) { + /* PROM on SS1 lacks this info, to be super safe we + * hard code it here since this arch is cast in stone. + */ + sun4c_vacinfo.num_bytes = 65536; + sun4c_vacinfo.linesize = 16; } else { - sun4c_vacinfo.type = VAC_WRITE_THROUGH; + sun4c_vacinfo.num_bytes = + prom_getintdefault(prom_root_node, "vac-size", 65536); + sun4c_vacinfo.linesize = + prom_getintdefault(prom_root_node, "vac-linesize", 16); + } + sun4c_vacinfo.do_hwflushes = + prom_getintdefault(prom_root_node, "vac-hwflush", 0); - if ((idprom->id_machtype == (SM_SUN4C | SM_4C_SS1)) || - (idprom->id_machtype == (SM_SUN4C | SM_4C_SS1PLUS))) { - /* PROM on SS1 lacks this info, to be super safe we - * hard code it here since this arch is cast in stone. - */ - sun4c_vacinfo.num_bytes = 65536; - sun4c_vacinfo.linesize = 16; - } else { - sun4c_vacinfo.num_bytes = - prom_getintdefault(prom_root_node, "vac-size", 65536); - sun4c_vacinfo.linesize = - prom_getintdefault(prom_root_node, "vac-linesize", 16); - } + if (sun4c_vacinfo.do_hwflushes == 0) sun4c_vacinfo.do_hwflushes = - prom_getintdefault(prom_root_node, "vac-hwflush", 0); - - if (sun4c_vacinfo.do_hwflushes == 0) - sun4c_vacinfo.do_hwflushes = - prom_getintdefault(prom_root_node, "vac_hwflush", 0); + prom_getintdefault(prom_root_node, "vac_hwflush", 0); - if (sun4c_vacinfo.num_bytes != 65536) { - prom_printf("WEIRD Sun4C VAC cache size, " - "tell sparclinux@vger.kernel.org"); - prom_halt(); - } + if (sun4c_vacinfo.num_bytes != 65536) { + prom_printf("WEIRD Sun4C VAC cache size, " + "tell sparclinux@vger.kernel.org"); + prom_halt(); } - sun4c_vacinfo.num_lines = - (sun4c_vacinfo.num_bytes / sun4c_vacinfo.linesize); switch (sun4c_vacinfo.linesize) { case 16: sun4c_vacinfo.log2lsize = 4; @@ -447,49 +399,18 @@ static void __init patch_kernel_fault_handler(void) static void __init sun4c_probe_mmu(void) { - if (ARCH_SUN4) { - switch (idprom->id_machtype) { - case (SM_SUN4|SM_4_110): - prom_printf("No support for 4100 yet\n"); - prom_halt(); - num_segmaps = 256; - num_contexts = 8; - break; - - case (SM_SUN4|SM_4_260): - /* should be 512 segmaps. when it get fixed */ - num_segmaps = 256; - num_contexts = 16; - break; - - case (SM_SUN4|SM_4_330): - num_segmaps = 256; - num_contexts = 16; - break; - - case (SM_SUN4|SM_4_470): - /* should be 1024 segmaps. when it get fixed */ - num_segmaps = 256; - num_contexts = 64; - break; - default: - prom_printf("Invalid SUN4 model\n"); - prom_halt(); - }; + if ((idprom->id_machtype == (SM_SUN4C | SM_4C_SS1)) || + (idprom->id_machtype == (SM_SUN4C | SM_4C_SS1PLUS))) { + /* Hardcode these just to be safe, PROM on SS1 does + * not have this info available in the root node. + */ + num_segmaps = 128; + num_contexts = 8; } else { - if ((idprom->id_machtype == (SM_SUN4C | SM_4C_SS1)) || - (idprom->id_machtype == (SM_SUN4C | SM_4C_SS1PLUS))) { - /* Hardcode these just to be safe, PROM on SS1 does - * not have this info available in the root node. - */ - num_segmaps = 128; - num_contexts = 8; - } else { - num_segmaps = - prom_getintdefault(prom_root_node, "mmu-npmg", 128); - num_contexts = - prom_getintdefault(prom_root_node, "mmu-nctx", 0x8); - } + num_segmaps = + prom_getintdefault(prom_root_node, "mmu-npmg", 128); + num_contexts = + prom_getintdefault(prom_root_node, "mmu-nctx", 0x8); } patch_kernel_fault_handler(); } @@ -501,18 +422,14 @@ void __init sun4c_probe_memerr_reg(void) int node; struct linux_prom_registers regs[1]; - if (ARCH_SUN4) { - sun4c_memerr_reg = ioremap(sun4_memreg_physaddr, PAGE_SIZE); - } else { - node = prom_getchild(prom_root_node); - node = prom_searchsiblings(prom_root_node, "memory-error"); - if (!node) - return; - if (prom_getproperty(node, "reg", (char *)regs, sizeof(regs)) <= 0) - return; - /* hmm I think regs[0].which_io is zero here anyways */ - sun4c_memerr_reg = ioremap(regs[0].phys_addr, regs[0].reg_size); - } + node = prom_getchild(prom_root_node); + node = prom_searchsiblings(prom_root_node, "memory-error"); + if (!node) + return; + if (prom_getproperty(node, "reg", (char *)regs, sizeof(regs)) <= 0) + return; + /* hmm I think regs[0].which_io is zero here anyways */ + sun4c_memerr_reg = ioremap(regs[0].phys_addr, regs[0].reg_size); } static inline void sun4c_init_ss2_cache_bug(void) @@ -521,7 +438,6 @@ static inline void sun4c_init_ss2_cache_bug(void) if ((idprom->id_machtype == (SM_SUN4C | SM_4C_SS2)) || (idprom->id_machtype == (SM_SUN4C | SM_4C_IPX)) || - (idprom->id_machtype == (SM_SUN4 | SM_4_330)) || (idprom->id_machtype == (SM_SUN4C | SM_4C_ELC))) { /* Whee.. */ printk("SS2 cache bug detected, uncaching trap table page\n"); @@ -532,8 +448,8 @@ static inline void sun4c_init_ss2_cache_bug(void) } /* Addr is always aligned on a page boundary for us already. */ -static int sun4c_map_dma_area(dma_addr_t *pba, unsigned long va, - unsigned long addr, int len) +static int sun4c_map_dma_area(struct device *dev, dma_addr_t *pba, unsigned long va, + unsigned long addr, int len) { unsigned long page, end; @@ -555,14 +471,7 @@ static int sun4c_map_dma_area(dma_addr_t *pba, unsigned long va, return 0; } -static struct page *sun4c_translate_dvma(unsigned long busa) -{ - /* Fortunately for us, bus_addr == uncached_virt in sun4c. */ - unsigned long pte = sun4c_get_pte(busa); - return pfn_to_page(pte & SUN4C_PFN_MASK); -} - -static void sun4c_unmap_dma_area(unsigned long busa, int len) +static void sun4c_unmap_dma_area(struct device *dev, unsigned long busa, int len) { /* Fortunately for us, bus_addr == uncached_virt in sun4c. */ /* XXX Implement this */ @@ -624,11 +533,7 @@ static inline void sun4c_init_map_kernelprom(unsigned long kernel_end) { unsigned long vaddr; unsigned char pseg, ctx; -#ifdef CONFIG_SUN4 - /* sun4/110 and 260 have no kadb. */ - if ((idprom->id_machtype != (SM_SUN4 | SM_4_260)) && - (idprom->id_machtype != (SM_SUN4 | SM_4_110))) { -#endif + for (vaddr = KADB_DEBUGGER_BEGVM; vaddr < LINUX_OPPROM_ENDVM; vaddr += SUN4C_REAL_PGDIR_SIZE) { @@ -640,9 +545,7 @@ static inline void sun4c_init_map_kernelprom(unsigned long kernel_end) fix_permissions(vaddr, _SUN4C_PAGE_PRIV, 0); } } -#ifdef CONFIG_SUN4 - } -#endif + for (vaddr = KERNBASE; vaddr < kernel_end; vaddr += SUN4C_REAL_PGDIR_SIZE) { pseg = sun4c_get_segmap(vaddr); mmu_entry_pool[pseg].locked = 1; @@ -1048,14 +951,10 @@ static struct thread_info *sun4c_alloc_thread_info(void) * so we must flush the cache to guarantee consistency. */ sun4c_flush_page(pages); -#ifndef CONFIG_SUN4 sun4c_flush_page(pages + PAGE_SIZE); -#endif sun4c_put_pte(addr, BUCKET_PTE(pages)); -#ifndef CONFIG_SUN4 sun4c_put_pte(addr + PAGE_SIZE, BUCKET_PTE(pages + PAGE_SIZE)); -#endif #ifdef CONFIG_DEBUG_STACK_USAGE memset((void *)addr, 0, PAGE_SIZE << THREAD_INFO_ORDER); @@ -1072,13 +971,11 @@ static void sun4c_free_thread_info(struct thread_info *ti) /* We are deleting a mapping, so the flush here is mandatory. */ sun4c_flush_page(tiaddr); -#ifndef CONFIG_SUN4 sun4c_flush_page(tiaddr + PAGE_SIZE); -#endif + sun4c_put_pte(tiaddr, 0); -#ifndef CONFIG_SUN4 sun4c_put_pte(tiaddr + PAGE_SIZE, 0); -#endif + sun4c_bucket[entry] = BUCKET_EMPTY; if (entry < sun4c_lowbucket_avail) sun4c_lowbucket_avail = entry; @@ -1211,7 +1108,7 @@ static void sun4c_unlockarea(char *vaddr, unsigned long size) * by implication and fool the page locking code above * if passed to by mistake. */ -static __u32 sun4c_get_scsi_one(char *bufptr, unsigned long len, struct sbus_bus *sbus) +static __u32 sun4c_get_scsi_one(struct device *dev, char *bufptr, unsigned long len) { unsigned long page; @@ -1223,7 +1120,7 @@ static __u32 sun4c_get_scsi_one(char *bufptr, unsigned long len, struct sbus_bus return (__u32)sun4c_lockarea(bufptr, len); } -static void sun4c_get_scsi_sgl(struct scatterlist *sg, int sz, struct sbus_bus *sbus) +static void sun4c_get_scsi_sgl(struct device *dev, struct scatterlist *sg, int sz) { while (sz != 0) { --sz; @@ -1233,14 +1130,14 @@ static void sun4c_get_scsi_sgl(struct scatterlist *sg, int sz, struct sbus_bus * } } -static void sun4c_release_scsi_one(__u32 bufptr, unsigned long len, struct sbus_bus *sbus) +static void sun4c_release_scsi_one(struct device *dev, __u32 bufptr, unsigned long len) { if (bufptr < sun4c_iobuffer_start) return; /* On kernel stack or similar, see above */ sun4c_unlockarea((char *)bufptr, len); } -static void sun4c_release_scsi_sgl(struct scatterlist *sg, int sz, struct sbus_bus *sbus) +static void sun4c_release_scsi_sgl(struct device *dev, struct scatterlist *sg, int sz) { while (sz != 0) { --sz; @@ -2263,7 +2160,6 @@ void __init ld_mmu_sun4c(void) BTFIXUPSET_CALL(mmu_map_dma_area, sun4c_map_dma_area, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(mmu_unmap_dma_area, sun4c_unmap_dma_area, BTFIXUPCALL_NORM); - BTFIXUPSET_CALL(mmu_translate_dvma, sun4c_translate_dvma, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(sparc_mapiorange, sun4c_mapiorange, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(sparc_unmapiorange, sun4c_unmapiorange, BTFIXUPCALL_NORM); diff --git a/arch/sparc/prom/Makefile b/arch/sparc/prom/Makefile index 7f5eacfcfbc..8f7e18546c9 100644 --- a/arch/sparc/prom/Makefile +++ b/arch/sparc/prom/Makefile @@ -4,5 +4,3 @@ lib-y := bootstr.o devmap.o devops.o init.o memory.o misc.o mp.o \ palloc.o ranges.o segment.o console.o printf.o tree.o - -lib-$(CONFIG_SUN4) += sun4prom.o diff --git a/arch/sparc/prom/bootstr.c b/arch/sparc/prom/bootstr.c index 5a35c768ff7..916831da7e6 100644 --- a/arch/sparc/prom/bootstr.c +++ b/arch/sparc/prom/bootstr.c @@ -6,15 +6,12 @@ #include <linux/string.h> #include <asm/oplib.h> -#include <asm/sun4prom.h> #include <linux/init.h> #define BARG_LEN 256 static char barg_buf[BARG_LEN] = { 0 }; static char fetched __initdata = 0; -extern linux_sun4_romvec *sun4_romvec; - char * __init prom_getbootargs(void) { @@ -28,7 +25,6 @@ prom_getbootargs(void) switch(prom_vers) { case PROM_V0: - case PROM_SUN4: cp = barg_buf; /* Start from 1 and go over fd(0,0,0)kernel */ for(iter = 1; iter < 8; iter++) { diff --git a/arch/sparc/prom/console.c b/arch/sparc/prom/console.c index 790057a3461..b3075d73fc1 100644 --- a/arch/sparc/prom/console.c +++ b/arch/sparc/prom/console.c @@ -10,7 +10,6 @@ #include <linux/kernel.h> #include <linux/sched.h> #include <asm/openprom.h> -#include <asm/sun4prom.h> #include <asm/oplib.h> #include <asm/system.h> #include <linux/string.h> @@ -30,7 +29,6 @@ prom_nbgetchar(void) spin_lock_irqsave(&prom_lock, flags); switch(prom_vers) { case PROM_V0: - case PROM_SUN4: i = (*(romvec->pv_nbgetchar))(); break; case PROM_V2: @@ -63,7 +61,6 @@ prom_nbputchar(char c) spin_lock_irqsave(&prom_lock, flags); switch(prom_vers) { case PROM_V0: - case PROM_SUN4: i = (*(romvec->pv_nbputchar))(c); break; case PROM_V2: diff --git a/arch/sparc/prom/init.c b/arch/sparc/prom/init.c index 729f8706694..873217c6d82 100644 --- a/arch/sparc/prom/init.c +++ b/arch/sparc/prom/init.c @@ -11,12 +11,10 @@ #include <asm/openprom.h> #include <asm/oplib.h> -#include <asm/sun4prom.h> struct linux_romvec *romvec; enum prom_major_version prom_vers; unsigned int prom_rev, prom_prev; -linux_sun4_romvec *sun4_romvec; /* The root node of the prom device tree. */ int prom_root_node; @@ -34,10 +32,6 @@ extern void prom_ranges_init(void); void __init prom_init(struct linux_romvec *rp) { -#ifdef CONFIG_SUN4 - extern struct linux_romvec *sun4_prom_init(void); - rp = sun4_prom_init(); -#endif romvec = rp; switch(romvec->pv_romvers) { @@ -50,9 +44,6 @@ void __init prom_init(struct linux_romvec *rp) case 3: prom_vers = PROM_V3; break; - case 40: - prom_vers = PROM_SUN4; - break; default: prom_printf("PROMLIB: Bad PROM version %d\n", romvec->pv_romvers); @@ -76,11 +67,8 @@ void __init prom_init(struct linux_romvec *rp) prom_ranges_init(); -#ifndef CONFIG_SUN4 - /* SUN4 prints this in sun4_prom_init */ printk("PROMLIB: Sun Boot Prom Version %d Revision %d\n", romvec->pv_romvers, prom_rev); -#endif /* Initialization successful. */ return; diff --git a/arch/sparc/prom/memory.c b/arch/sparc/prom/memory.c index 947f047dc95..fac7899a29c 100644 --- a/arch/sparc/prom/memory.c +++ b/arch/sparc/prom/memory.c @@ -10,7 +10,6 @@ #include <linux/init.h> #include <asm/openprom.h> -#include <asm/sun4prom.h> #include <asm/oplib.h> #include <asm/page.h> @@ -46,15 +45,6 @@ static int __init prom_meminit_v2(void) return num_ents; } -static int __init prom_meminit_sun4(void) -{ -#ifdef CONFIG_SUN4 - sp_banks[0].base_addr = 0; - sp_banks[0].num_bytes = *(sun4_romvec->memoryavail); -#endif - return 1; -} - static int sp_banks_cmp(const void *a, const void *b) { const struct sparc_phys_banks *x = a, *y = b; @@ -81,10 +71,6 @@ void __init prom_meminit(void) num_ents = prom_meminit_v2(); break; - case PROM_SUN4: - num_ents = prom_meminit_sun4(); - break; - default: break; } diff --git a/arch/sparc/prom/ranges.c b/arch/sparc/prom/ranges.c index f9b7def35f6..64579a37641 100644 --- a/arch/sparc/prom/ranges.c +++ b/arch/sparc/prom/ranges.c @@ -9,7 +9,6 @@ #include <asm/openprom.h> #include <asm/oplib.h> #include <asm/types.h> -#include <asm/sbus.h> #include <asm/system.h> struct linux_prom_ranges promlib_obio_ranges[PROMREG_MAX]; diff --git a/arch/sparc/prom/sun4prom.c b/arch/sparc/prom/sun4prom.c deleted file mode 100644 index 00390a2652a..00000000000 --- a/arch/sparc/prom/sun4prom.c +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright (C) 1996 The Australian National University. - * Copyright (C) 1996 Fujitsu Laboratories Limited - * Copyright (C) 1997 Michael A. Griffith (grif@acm.org) - * Copyright (C) 1997 Sun Weenie (ko@ko.reno.nv.us) - * Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) - * - * This software may be distributed under the terms of the Gnu - * Public License version 2 or later - * - * fake a really simple Sun prom for the SUN4 - */ - -#include <linux/kernel.h> -#include <linux/string.h> -#include <asm/oplib.h> -#include <asm/idprom.h> -#include <asm/machines.h> -#include <asm/sun4prom.h> -#include <asm/asi.h> -#include <asm/contregs.h> -#include <linux/init.h> - -static struct linux_romvec sun4romvec; -static struct idprom sun4_idprom; - -struct property { - char *name; - char *value; - int length; -}; - -struct node { - int level; - struct property *properties; -}; - -struct property null_properties = { NULL, NULL, -1 }; - -struct property root_properties[] = { - {"device_type", "cpu", 4}, - {"idprom", (char *)&sun4_idprom, sizeof(struct idprom)}, - {NULL, NULL, -1} -}; - -struct node nodes[] = { - { 0, &null_properties }, - { 0, root_properties }, - { -1,&null_properties } -}; - - -static int no_nextnode(int node) -{ - if (nodes[node].level == nodes[node+1].level) - return node+1; - return -1; -} - -static int no_child(int node) -{ - if (nodes[node].level == nodes[node+1].level-1) - return node+1; - return -1; -} - -static struct property *find_property(int node,char *name) -{ - struct property *prop = &nodes[node].properties[0]; - while (prop && prop->name) { - if (strcmp(prop->name,name) == 0) return prop; - prop++; - } - return NULL; -} - -static int no_proplen(int node,char *name) -{ - struct property *prop = find_property(node,name); - if (prop) return prop->length; - return -1; -} - -static int no_getprop(int node,char *name,char *value) -{ - struct property *prop = find_property(node,name); - if (prop) { - memcpy(value,prop->value,prop->length); - return 1; - } - return -1; -} - -static int no_setprop(int node,char *name,char *value,int len) -{ - return -1; -} - -static char *no_nextprop(int node,char *name) -{ - struct property *prop = find_property(node,name); - if (prop) return prop[1].name; - return NULL; -} - -static struct linux_nodeops sun4_nodeops = { - no_nextnode, - no_child, - no_proplen, - no_getprop, - no_setprop, - no_nextprop -}; - -static int synch_hook; - -struct linux_romvec * __init sun4_prom_init(void) -{ - int i; - unsigned char x; - char *p; - - p = (char *)&sun4_idprom; - for (i = 0; i < sizeof(sun4_idprom); i++) { - __asm__ __volatile__ ("lduba [%1] %2, %0" : "=r" (x) : - "r" (AC_IDPROM + i), "i" (ASI_CONTROL)); - *p++ = x; - } - - memset(&sun4romvec,0,sizeof(sun4romvec)); - - sun4_romvec = (linux_sun4_romvec *) SUN4_PROM_VECTOR; - - sun4romvec.pv_romvers = 40; - sun4romvec.pv_nodeops = &sun4_nodeops; - sun4romvec.pv_reboot = sun4_romvec->reboot; - sun4romvec.pv_abort = sun4_romvec->abortentry; - sun4romvec.pv_halt = sun4_romvec->exittomon; - sun4romvec.pv_synchook = (void (**)(void))&synch_hook; - sun4romvec.pv_setctxt = sun4_romvec->setcxsegmap; - sun4romvec.pv_v0bootargs = sun4_romvec->bootParam; - sun4romvec.pv_nbgetchar = sun4_romvec->mayget; - sun4romvec.pv_nbputchar = sun4_romvec->mayput; - sun4romvec.pv_stdin = sun4_romvec->insource; - sun4romvec.pv_stdout = sun4_romvec->outsink; - - /* - * We turn on the LEDs to let folks without monitors or - * terminals know we booted. Nothing too fancy now. They - * are all on, except for LED 5, which blinks. When we - * have more time, we can teach the penguin to say "By your - * command" or "Activating turbo boost, Michael". :-) - */ - sun4_romvec->setLEDs(NULL); - - printk("PROMLIB: Old Sun4 boot PROM monitor %s, romvec version %d\n", - sun4_romvec->monid, - sun4_romvec->romvecversion); - - return &sun4romvec; -} diff --git a/arch/sparc64/Kconfig b/arch/sparc64/Kconfig index 36b4b7ab9cf..5446e2a499b 100644 --- a/arch/sparc64/Kconfig +++ b/arch/sparc64/Kconfig @@ -18,6 +18,13 @@ config SPARC64 select HAVE_ARCH_KGDB select USE_GENERIC_SMP_HELPERS if SMP select HAVE_ARCH_TRACEHOOK + select ARCH_WANT_OPTIONAL_GPIOLIB + select RTC_CLASS + select RTC_DRV_M48T59 + select RTC_DRV_CMOS + select RTC_DRV_BQ4802 + select RTC_DRV_SUN4V + select RTC_DRV_STARFIRE config GENERIC_TIME bool @@ -31,6 +38,11 @@ config GENERIC_CLOCKEVENTS bool default y +config GENERIC_GPIO + bool + help + Generic GPIO API support + config 64BIT def_bool y @@ -185,6 +197,17 @@ config US2E_FREQ If in doubt, say N. +config US3_MC + tristate "UltraSPARC-III Memory Controller driver" + default y + help + This adds a driver for the UltraSPARC-III memory controller. + Loading this driver allows exact mnemonic strings to be + printed in the event of a memory error, so that the faulty DIMM + on the motherboard can be matched to the error. + + If in doubt, say Y, as this information can be very useful. + # Global things across all Sun machines. config GENERIC_LOCKBREAK bool diff --git a/arch/sparc64/Makefile b/arch/sparc64/Makefile index b785a395b12..c7214abc0d8 100644 --- a/arch/sparc64/Makefile +++ b/arch/sparc64/Makefile @@ -7,7 +7,7 @@ # Copyright (C) 1998 Jakub Jelinek (jj@ultra.linux.cz) # -CHECKFLAGS += -D__sparc__ -D__sparc_v9__ -m64 +CHECKFLAGS += -D__sparc__ -D__sparc_v9__ -D__arch64__ -m64 # Undefine sparc when processing vmlinux.lds - it is used # And teach CPP we are doing 64 bit builds (for this case) diff --git a/arch/sparc64/kernel/Makefile b/arch/sparc64/kernel/Makefile index 418b5782096..c0b8009ab19 100644 --- a/arch/sparc64/kernel/Makefile +++ b/arch/sparc64/kernel/Makefile @@ -7,16 +7,16 @@ EXTRA_CFLAGS := -Werror extra-y := head.o init_task.o vmlinux.lds -obj-y := process.o setup.o cpu.o idprom.o \ +obj-y := process.o setup.o cpu.o idprom.o reboot.o \ traps.o auxio.o una_asm.o sysfs.o iommu.o \ irq.o ptrace.o time.o sys_sparc.o signal.o \ - unaligned.o central.o pci.o starfire.o \ - power.o sbus.o sparc64_ksyms.o chmc.o \ + unaligned.o central.o starfire.o \ + power.o sbus.o sparc64_ksyms.o ebus.o \ visemul.o prom.o of_device.o hvapi.o sstate.o mdesc.o obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o obj-$(CONFIG_STACKTRACE) += stacktrace.o -obj-$(CONFIG_PCI) += ebus.o pci_common.o \ +obj-$(CONFIG_PCI) += pci.o pci_common.o psycho_common.o \ pci_psycho.o pci_sabre.o pci_schizo.o \ pci_sun4v.o pci_sun4v_asm.o pci_fire.o obj-$(CONFIG_PCI_MSI) += pci_msi.o @@ -25,6 +25,7 @@ obj-$(CONFIG_COMPAT) += sys32.o sys_sparc32.o signal32.o obj-$(CONFIG_MODULES) += module.o obj-$(CONFIG_US3_FREQ) += us3_cpufreq.o obj-$(CONFIG_US2E_FREQ) += us2e_cpufreq.o +obj-$(CONFIG_US3_MC) += chmc.o obj-$(CONFIG_KPROBES) += kprobes.o obj-$(CONFIG_SUN_LDOMS) += ldc.o vio.o viohs.o ds.o obj-$(CONFIG_AUDIT) += audit.o diff --git a/arch/sparc64/kernel/auxio.c b/arch/sparc64/kernel/auxio.c index dd5c7bf8761..858beda8652 100644 --- a/arch/sparc64/kernel/auxio.c +++ b/arch/sparc64/kernel/auxio.c @@ -109,7 +109,7 @@ void auxio_set_lte(int on) } } -static struct of_device_id auxio_match[] = { +static struct of_device_id __initdata auxio_match[] = { { .name = "auxio", }, diff --git a/arch/sparc64/kernel/central.c b/arch/sparc64/kernel/central.c index f2e87d0d7e1..05f1c916db0 100644 --- a/arch/sparc64/kernel/central.c +++ b/arch/sparc64/kernel/central.c @@ -1,461 +1,268 @@ /* central.c: Central FHC driver for Sunfire/Starfire/Wildfire. * - * Copyright (C) 1997, 1999 David S. Miller (davem@davemloft.net) + * Copyright (C) 1997, 1999, 2008 David S. Miller (davem@davemloft.net) */ #include <linux/kernel.h> #include <linux/types.h> #include <linux/string.h> -#include <linux/timer.h> -#include <linux/sched.h> -#include <linux/delay.h> #include <linux/init.h> -#include <linux/bootmem.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> -#include <asm/page.h> #include <asm/fhc.h> -#include <asm/starfire.h> - -static struct linux_central *central_bus = NULL; -static struct linux_fhc *fhc_list = NULL; +#include <asm/upa.h> + +struct clock_board { + void __iomem *clock_freq_regs; + void __iomem *clock_regs; + void __iomem *clock_ver_reg; + int num_slots; + struct resource leds_resource; + struct platform_device leds_pdev; +}; + +struct fhc { + void __iomem *pregs; + bool central; + bool jtag_master; + int board_num; + struct resource leds_resource; + struct platform_device leds_pdev; +}; + +static int __devinit clock_board_calc_nslots(struct clock_board *p) +{ + u8 reg = upa_readb(p->clock_regs + CLOCK_STAT1) & 0xc0; -#define IS_CENTRAL_FHC(__fhc) ((__fhc) == central_bus->child) + switch (reg) { + case 0x40: + return 16; -static void central_probe_failure(int line) -{ - prom_printf("CENTRAL: Critical device probe failure at central.c:%d\n", - line); - prom_halt(); -} + case 0xc0: + return 8; -static void central_ranges_init(struct linux_central *central) -{ - struct device_node *dp = central->prom_node; - const void *pval; - int len; - - central->num_central_ranges = 0; - pval = of_get_property(dp, "ranges", &len); - if (pval) { - memcpy(central->central_ranges, pval, len); - central->num_central_ranges = - (len / sizeof(struct linux_prom_ranges)); + case 0x80: + reg = 0; + if (p->clock_ver_reg) + reg = upa_readb(p->clock_ver_reg); + if (reg) { + if (reg & 0x80) + return 4; + else + return 5; + } + /* Fallthrough */ + default: + return 4; } } -static void fhc_ranges_init(struct linux_fhc *fhc) +static int __devinit clock_board_probe(struct of_device *op, + const struct of_device_id *match) { - struct device_node *dp = fhc->prom_node; - const void *pval; - int len; - - fhc->num_fhc_ranges = 0; - pval = of_get_property(dp, "ranges", &len); - if (pval) { - memcpy(fhc->fhc_ranges, pval, len); - fhc->num_fhc_ranges = - (len / sizeof(struct linux_prom_ranges)); - } -} + struct clock_board *p = kzalloc(sizeof(*p), GFP_KERNEL); + int err = -ENOMEM; -/* Range application routines are exported to various drivers, - * so do not __init this. - */ -static void adjust_regs(struct linux_prom_registers *regp, int nregs, - struct linux_prom_ranges *rangep, int nranges) -{ - int regc, rngc; - - for (regc = 0; regc < nregs; regc++) { - for (rngc = 0; rngc < nranges; rngc++) - if (regp[regc].which_io == rangep[rngc].ot_child_space) - break; /* Fount it */ - if (rngc == nranges) /* oops */ - central_probe_failure(__LINE__); - regp[regc].which_io = rangep[rngc].ot_parent_space; - regp[regc].phys_addr -= rangep[rngc].ot_child_base; - regp[regc].phys_addr += rangep[rngc].ot_parent_base; + if (!p) { + printk(KERN_ERR "clock_board: Cannot allocate struct clock_board\n"); + goto out; } -} -/* Apply probed fhc ranges to registers passed, if no ranges return. */ -static void apply_fhc_ranges(struct linux_fhc *fhc, - struct linux_prom_registers *regs, - int nregs) -{ - if (fhc->num_fhc_ranges) - adjust_regs(regs, nregs, fhc->fhc_ranges, - fhc->num_fhc_ranges); -} + p->clock_freq_regs = of_ioremap(&op->resource[0], 0, + resource_size(&op->resource[0]), + "clock_board_freq"); + if (!p->clock_freq_regs) { + printk(KERN_ERR "clock_board: Cannot map clock_freq_regs\n"); + goto out_free; + } -/* Apply probed central ranges to registers passed, if no ranges return. */ -static void apply_central_ranges(struct linux_central *central, - struct linux_prom_registers *regs, int nregs) -{ - if (central->num_central_ranges) - adjust_regs(regs, nregs, central->central_ranges, - central->num_central_ranges); -} + p->clock_regs = of_ioremap(&op->resource[1], 0, + resource_size(&op->resource[1]), + "clock_board_regs"); + if (!p->clock_regs) { + printk(KERN_ERR "clock_board: Cannot map clock_regs\n"); + goto out_unmap_clock_freq_regs; + } -static void * __init central_alloc_bootmem(unsigned long size) -{ - void *ret; + if (op->resource[2].flags) { + p->clock_ver_reg = of_ioremap(&op->resource[2], 0, + resource_size(&op->resource[2]), + "clock_ver_reg"); + if (!p->clock_ver_reg) { + printk(KERN_ERR "clock_board: Cannot map clock_ver_reg\n"); + goto out_unmap_clock_regs; + } + } - ret = __alloc_bootmem(size, SMP_CACHE_BYTES, 0UL); - if (ret != NULL) - memset(ret, 0, size); + p->num_slots = clock_board_calc_nslots(p); - return ret; -} + p->leds_resource.start = (unsigned long) + (p->clock_regs + CLOCK_CTRL); + p->leds_resource.end = p->leds_resource.end; + p->leds_resource.name = "leds"; -static unsigned long prom_reg_to_paddr(struct linux_prom_registers *r) -{ - unsigned long ret = ((unsigned long) r->which_io) << 32; + p->leds_pdev.name = "sunfire-clockboard-leds"; + p->leds_pdev.resource = &p->leds_resource; + p->leds_pdev.num_resources = 1; + p->leds_pdev.dev.parent = &op->dev; - return ret | (unsigned long) r->phys_addr; -} - -static void __init probe_other_fhcs(void) -{ - struct device_node *dp; - const struct linux_prom64_registers *fpregs; - - for_each_node_by_name(dp, "fhc") { - struct linux_fhc *fhc; - int board; - u32 tmp; - - if (dp->parent && - dp->parent->parent != NULL) - continue; - - fhc = (struct linux_fhc *) - central_alloc_bootmem(sizeof(struct linux_fhc)); - if (fhc == NULL) - central_probe_failure(__LINE__); - - /* Link it into the FHC chain. */ - fhc->next = fhc_list; - fhc_list = fhc; - - /* Toplevel FHCs have no parent. */ - fhc->parent = NULL; - - fhc->prom_node = dp; - fhc_ranges_init(fhc); - - /* Non-central FHC's have 64-bit OBP format registers. */ - fpregs = of_get_property(dp, "reg", NULL); - if (!fpregs) - central_probe_failure(__LINE__); - - /* Only central FHC needs special ranges applied. */ - fhc->fhc_regs.pregs = fpregs[0].phys_addr; - fhc->fhc_regs.ireg = fpregs[1].phys_addr; - fhc->fhc_regs.ffregs = fpregs[2].phys_addr; - fhc->fhc_regs.sregs = fpregs[3].phys_addr; - fhc->fhc_regs.uregs = fpregs[4].phys_addr; - fhc->fhc_regs.tregs = fpregs[5].phys_addr; - - board = of_getintprop_default(dp, "board#", -1); - fhc->board = board; - - tmp = upa_readl(fhc->fhc_regs.pregs + FHC_PREGS_JCTRL); - if ((tmp & FHC_JTAG_CTRL_MENAB) != 0) - fhc->jtag_master = 1; - else - fhc->jtag_master = 0; - - tmp = upa_readl(fhc->fhc_regs.pregs + FHC_PREGS_ID); - printk("FHC(board %d): Version[%x] PartID[%x] Manuf[%x] %s\n", - board, - (tmp & FHC_ID_VERS) >> 28, - (tmp & FHC_ID_PARTID) >> 12, - (tmp & FHC_ID_MANUF) >> 1, - (fhc->jtag_master ? "(JTAG Master)" : "")); - - /* This bit must be set in all non-central FHC's in - * the system. When it is clear, this identifies - * the central board. - */ - tmp = upa_readl(fhc->fhc_regs.pregs + FHC_PREGS_CTRL); - tmp |= FHC_CONTROL_IXIST; - upa_writel(tmp, fhc->fhc_regs.pregs + FHC_PREGS_CTRL); + err = platform_device_register(&p->leds_pdev); + if (err) { + printk(KERN_ERR "clock_board: Could not register LEDS " + "platform device\n"); + goto out_unmap_clock_ver_reg; } -} -static void probe_clock_board(struct linux_central *central, - struct linux_fhc *fhc, - struct device_node *fp) -{ - struct device_node *dp; - struct linux_prom_registers cregs[3]; - const struct linux_prom_registers *pr; - int nslots, tmp, nregs; - - dp = fp->child; - while (dp) { - if (!strcmp(dp->name, "clock-board")) - break; - dp = dp->sibling; - } - if (!dp) - central_probe_failure(__LINE__); + printk(KERN_INFO "clock_board: Detected %d slot Enterprise system.\n", + p->num_slots); - pr = of_get_property(dp, "reg", &nregs); - if (!pr) - central_probe_failure(__LINE__); + err = 0; +out: + return err; - memcpy(cregs, pr, nregs); - nregs /= sizeof(struct linux_prom_registers); +out_unmap_clock_ver_reg: + if (p->clock_ver_reg) + of_iounmap(&op->resource[2], p->clock_ver_reg, + resource_size(&op->resource[2])); - apply_fhc_ranges(fhc, &cregs[0], nregs); - apply_central_ranges(central, &cregs[0], nregs); - central->cfreg = prom_reg_to_paddr(&cregs[0]); - central->clkregs = prom_reg_to_paddr(&cregs[1]); +out_unmap_clock_regs: + of_iounmap(&op->resource[1], p->clock_regs, + resource_size(&op->resource[1])); - if (nregs == 2) - central->clkver = 0UL; - else - central->clkver = prom_reg_to_paddr(&cregs[2]); +out_unmap_clock_freq_regs: + of_iounmap(&op->resource[0], p->clock_freq_regs, + resource_size(&op->resource[0])); - tmp = upa_readb(central->clkregs + CLOCK_STAT1); - tmp &= 0xc0; - switch(tmp) { - case 0x40: - nslots = 16; - break; - case 0xc0: - nslots = 8; - break; - case 0x80: - if (central->clkver != 0UL && - upa_readb(central->clkver) != 0) { - if ((upa_readb(central->clkver) & 0x80) != 0) - nslots = 4; - else - nslots = 5; - break; - } - default: - nslots = 4; - break; - }; - central->slots = nslots; - printk("CENTRAL: Detected %d slot Enterprise system. cfreg[%02x] cver[%02x]\n", - central->slots, upa_readb(central->cfreg), - (central->clkver ? upa_readb(central->clkver) : 0x00)); +out_free: + kfree(p); + goto out; } -static void ZAP(unsigned long iclr, unsigned long imap) +static struct of_device_id __initdata clock_board_match[] = { + { + .name = "clock-board", + }, + {}, +}; + +static struct of_platform_driver clock_board_driver = { + .match_table = clock_board_match, + .probe = clock_board_probe, + .driver = { + .name = "clock_board", + }, +}; + +static int __devinit fhc_probe(struct of_device *op, + const struct of_device_id *match) { - u32 imap_tmp; - - upa_writel(0, iclr); - upa_readl(iclr); - imap_tmp = upa_readl(imap); - imap_tmp &= ~(0x80000000); - upa_writel(imap_tmp, imap); - upa_readl(imap); -} + struct fhc *p = kzalloc(sizeof(*p), GFP_KERNEL); + int err = -ENOMEM; + u32 reg; -static void init_all_fhc_hw(void) -{ - struct linux_fhc *fhc; - - for (fhc = fhc_list; fhc != NULL; fhc = fhc->next) { - u32 tmp; - - /* Clear all of the interrupt mapping registers - * just in case OBP left them in a foul state. - */ - ZAP(fhc->fhc_regs.ffregs + FHC_FFREGS_ICLR, - fhc->fhc_regs.ffregs + FHC_FFREGS_IMAP); - ZAP(fhc->fhc_regs.sregs + FHC_SREGS_ICLR, - fhc->fhc_regs.sregs + FHC_SREGS_IMAP); - ZAP(fhc->fhc_regs.uregs + FHC_UREGS_ICLR, - fhc->fhc_regs.uregs + FHC_UREGS_IMAP); - ZAP(fhc->fhc_regs.tregs + FHC_TREGS_ICLR, - fhc->fhc_regs.tregs + FHC_TREGS_IMAP); - - /* Setup FHC control register. */ - tmp = upa_readl(fhc->fhc_regs.pregs + FHC_PREGS_CTRL); - - /* All non-central boards have this bit set. */ - if (! IS_CENTRAL_FHC(fhc)) - tmp |= FHC_CONTROL_IXIST; - - /* For all FHCs, clear the firmware synchronization - * line and both low power mode enables. - */ - tmp &= ~(FHC_CONTROL_AOFF | FHC_CONTROL_BOFF | - FHC_CONTROL_SLINE); - - upa_writel(tmp, fhc->fhc_regs.pregs + FHC_PREGS_CTRL); - upa_readl(fhc->fhc_regs.pregs + FHC_PREGS_CTRL); + if (!p) { + printk(KERN_ERR "fhc: Cannot allocate struct fhc\n"); + goto out; } -} + if (!strcmp(op->node->parent->name, "central")) + p->central = true; -void __init central_probe(void) -{ - struct linux_prom_registers fpregs[6]; - const struct linux_prom_registers *pr; - struct linux_fhc *fhc; - struct device_node *dp, *fp; - int err; - - dp = of_find_node_by_name(NULL, "central"); - if (!dp) { - if (this_is_starfire) - starfire_cpu_setup(); - return; + p->pregs = of_ioremap(&op->resource[0], 0, + resource_size(&op->resource[0]), + "fhc_pregs"); + if (!p->pregs) { + printk(KERN_ERR "fhc: Cannot map pregs\n"); + goto out_free; } - /* Ok we got one, grab some memory for software state. */ - central_bus = (struct linux_central *) - central_alloc_bootmem(sizeof(struct linux_central)); - if (central_bus == NULL) - central_probe_failure(__LINE__); - - fhc = (struct linux_fhc *) - central_alloc_bootmem(sizeof(struct linux_fhc)); - if (fhc == NULL) - central_probe_failure(__LINE__); - - /* First init central. */ - central_bus->child = fhc; - central_bus->prom_node = dp; - central_ranges_init(central_bus); - - /* And then central's FHC. */ - fhc->next = fhc_list; - fhc_list = fhc; - - fhc->parent = central_bus; - fp = dp->child; - while (fp) { - if (!strcmp(fp->name, "fhc")) - break; - fp = fp->sibling; + if (p->central) { + reg = upa_readl(p->pregs + FHC_PREGS_BSR); + p->board_num = ((reg >> 16) & 1) | ((reg >> 12) & 0x0e); + } else { + p->board_num = of_getintprop_default(op->node, "board#", -1); + if (p->board_num == -1) { + printk(KERN_ERR "fhc: No board# property\n"); + goto out_unmap_pregs; + } + if (upa_readl(p->pregs + FHC_PREGS_JCTRL) & FHC_JTAG_CTRL_MENAB) + p->jtag_master = true; } - if (!fp) - central_probe_failure(__LINE__); - - fhc->prom_node = fp; - fhc_ranges_init(fhc); - - /* Now, map in FHC register set. */ - pr = of_get_property(fp, "reg", NULL); - if (!pr) - central_probe_failure(__LINE__); - memcpy(fpregs, pr, sizeof(fpregs)); - - apply_central_ranges(central_bus, &fpregs[0], 6); - - fhc->fhc_regs.pregs = prom_reg_to_paddr(&fpregs[0]); - fhc->fhc_regs.ireg = prom_reg_to_paddr(&fpregs[1]); - fhc->fhc_regs.ffregs = prom_reg_to_paddr(&fpregs[2]); - fhc->fhc_regs.sregs = prom_reg_to_paddr(&fpregs[3]); - fhc->fhc_regs.uregs = prom_reg_to_paddr(&fpregs[4]); - fhc->fhc_regs.tregs = prom_reg_to_paddr(&fpregs[5]); - - /* Obtain board number from board status register, Central's - * FHC lacks "board#" property. - */ - err = upa_readl(fhc->fhc_regs.pregs + FHC_PREGS_BSR); - fhc->board = (((err >> 16) & 0x01) | - ((err >> 12) & 0x0e)); - - fhc->jtag_master = 0; - - /* Attach the clock board registers for CENTRAL. */ - probe_clock_board(central_bus, fhc, fp); - - err = upa_readl(fhc->fhc_regs.pregs + FHC_PREGS_ID); - printk("FHC(board %d): Version[%x] PartID[%x] Manuf[%x] (CENTRAL)\n", - fhc->board, - ((err & FHC_ID_VERS) >> 28), - ((err & FHC_ID_PARTID) >> 12), - ((err & FHC_ID_MANUF) >> 1)); - - probe_other_fhcs(); - - init_all_fhc_hw(); -} -static inline void fhc_ledblink(struct linux_fhc *fhc, int on) -{ - u32 tmp; + if (!p->central) { + p->leds_resource.start = (unsigned long) + (p->pregs + FHC_PREGS_CTRL); + p->leds_resource.end = p->leds_resource.end; + p->leds_resource.name = "leds"; + + p->leds_pdev.name = "sunfire-fhc-leds"; + p->leds_pdev.resource = &p->leds_resource; + p->leds_pdev.num_resources = 1; + p->leds_pdev.dev.parent = &op->dev; + + err = platform_device_register(&p->leds_pdev); + if (err) { + printk(KERN_ERR "fhc: Could not register LEDS " + "platform device\n"); + goto out_unmap_pregs; + } + } + reg = upa_readl(p->pregs + FHC_PREGS_CTRL); - tmp = upa_readl(fhc->fhc_regs.pregs + FHC_PREGS_CTRL); + if (!p->central) + reg |= FHC_CONTROL_IXIST; - /* NOTE: reverse logic on this bit */ - if (on) - tmp &= ~(FHC_CONTROL_RLED); - else - tmp |= FHC_CONTROL_RLED; - tmp &= ~(FHC_CONTROL_AOFF | FHC_CONTROL_BOFF | FHC_CONTROL_SLINE); + reg &= ~(FHC_CONTROL_AOFF | + FHC_CONTROL_BOFF | + FHC_CONTROL_SLINE); - upa_writel(tmp, fhc->fhc_regs.pregs + FHC_PREGS_CTRL); - upa_readl(fhc->fhc_regs.pregs + FHC_PREGS_CTRL); -} + upa_writel(reg, p->pregs + FHC_PREGS_CTRL); + upa_readl(p->pregs + FHC_PREGS_CTRL); -static inline void central_ledblink(struct linux_central *central, int on) -{ - u8 tmp; - - tmp = upa_readb(central->clkregs + CLOCK_CTRL); + reg = upa_readl(p->pregs + FHC_PREGS_ID); + printk(KERN_INFO "fhc: Board #%d, Version[%x] PartID[%x] Manuf[%x] %s\n", + p->board_num, + (reg & FHC_ID_VERS) >> 28, + (reg & FHC_ID_PARTID) >> 12, + (reg & FHC_ID_MANUF) >> 1, + (p->jtag_master ? + "(JTAG Master)" : + (p->central ? "(Central)" : ""))); - /* NOTE: reverse logic on this bit */ - if (on) - tmp &= ~(CLOCK_CTRL_RLED); - else - tmp |= CLOCK_CTRL_RLED; + err = 0; - upa_writeb(tmp, central->clkregs + CLOCK_CTRL); - upa_readb(central->clkregs + CLOCK_CTRL); -} +out: + return err; -static struct timer_list sftimer; -static int led_state; +out_unmap_pregs: + of_iounmap(&op->resource[0], p->pregs, resource_size(&op->resource[0])); -static void sunfire_timer(unsigned long __ignored) -{ - struct linux_fhc *fhc; - - central_ledblink(central_bus, led_state); - for (fhc = fhc_list; fhc != NULL; fhc = fhc->next) - if (! IS_CENTRAL_FHC(fhc)) - fhc_ledblink(fhc, led_state); - led_state = ! led_state; - sftimer.expires = jiffies + (HZ >> 1); - add_timer(&sftimer); +out_free: + kfree(p); + goto out; } -/* After PCI/SBUS busses have been probed, this is called to perform - * final initialization of all FireHose Controllers in the system. - */ -void firetruck_init(void) +static struct of_device_id __initdata fhc_match[] = { + { + .name = "fhc", + }, + {}, +}; + +static struct of_platform_driver fhc_driver = { + .match_table = fhc_match, + .probe = fhc_probe, + .driver = { + .name = "fhc", + }, +}; + +static int __init sunfire_init(void) { - struct linux_central *central = central_bus; - u8 ctrl; - - /* No central bus, nothing to do. */ - if (central == NULL) - return; - - /* OBP leaves it on, turn it off so clock board timer LED - * is in sync with FHC ones. - */ - ctrl = upa_readb(central->clkregs + CLOCK_CTRL); - ctrl &= ~(CLOCK_CTRL_RLED); - upa_writeb(ctrl, central->clkregs + CLOCK_CTRL); - - led_state = 0; - init_timer(&sftimer); - sftimer.data = 0; - sftimer.function = &sunfire_timer; - sftimer.expires = jiffies + (HZ >> 1); - add_timer(&sftimer); + (void) of_register_driver(&fhc_driver, &of_platform_bus_type); + (void) of_register_driver(&clock_board_driver, &of_platform_bus_type); + return 0; } + +subsys_initcall(sunfire_init); diff --git a/arch/sparc64/kernel/chmc.c b/arch/sparc64/kernel/chmc.c index 6d4f02e8a4c..3b9f4d6e14a 100644 --- a/arch/sparc64/kernel/chmc.c +++ b/arch/sparc64/kernel/chmc.c @@ -1,6 +1,6 @@ -/* memctrlr.c: Driver for UltraSPARC-III memory controller. +/* chmc.c: Driver for UltraSPARC-III memory controller. * - * Copyright (C) 2001, 2007 David S. Miller (davem@davemloft.net) + * Copyright (C) 2001, 2007, 2008 David S. Miller (davem@davemloft.net) */ #include <linux/module.h> @@ -13,45 +13,64 @@ #include <linux/smp.h> #include <linux/errno.h> #include <linux/init.h> +#include <linux/of.h> +#include <linux/of_device.h> #include <asm/spitfire.h> #include <asm/chmctrl.h> #include <asm/cpudata.h> #include <asm/oplib.h> #include <asm/prom.h> +#include <asm/head.h> #include <asm/io.h> +#include <asm/memctrl.h> + +#define DRV_MODULE_NAME "chmc" +#define PFX DRV_MODULE_NAME ": " +#define DRV_MODULE_VERSION "0.2" + +MODULE_AUTHOR("David S. Miller (davem@davemloft.net)"); +MODULE_DESCRIPTION("UltraSPARC-III memory controller driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_MODULE_VERSION); + +static int mc_type; +#define MC_TYPE_SAFARI 1 +#define MC_TYPE_JBUS 2 + +static dimm_printer_t us3mc_dimm_printer; #define CHMCTRL_NDGRPS 2 #define CHMCTRL_NDIMMS 4 -#define DIMMS_PER_MC (CHMCTRL_NDGRPS * CHMCTRL_NDIMMS) +#define CHMC_DIMMS_PER_MC (CHMCTRL_NDGRPS * CHMCTRL_NDIMMS) /* OBP memory-layout property format. */ -struct obp_map { +struct chmc_obp_map { unsigned char dimm_map[144]; unsigned char pin_map[576]; }; #define DIMM_LABEL_SZ 8 -struct obp_mem_layout { +struct chmc_obp_mem_layout { /* One max 8-byte string label per DIMM. Usually * this matches the label on the motherboard where * that DIMM resides. */ - char dimm_labels[DIMMS_PER_MC][DIMM_LABEL_SZ]; + char dimm_labels[CHMC_DIMMS_PER_MC][DIMM_LABEL_SZ]; /* If symmetric use map[0], else it is * asymmetric and map[1] should be used. */ - char symmetric; + char symmetric; - struct obp_map map[2]; + struct chmc_obp_map map[2]; }; #define CHMCTRL_NBANKS 4 -struct bank_info { - struct mctrl_info *mp; +struct chmc_bank_info { + struct chmc *p; int bank_id; u64 raw_reg; @@ -65,28 +84,406 @@ struct bank_info { unsigned long size; }; -struct mctrl_info { - struct list_head list; - int portid; +struct chmc { + struct list_head list; + int portid; + + struct chmc_obp_mem_layout layout_prop; + int layout_size; + + void __iomem *regs; - struct obp_mem_layout layout_prop; - int layout_size; + u64 timing_control1; + u64 timing_control2; + u64 timing_control3; + u64 timing_control4; + u64 memaddr_control; - void __iomem *regs; + struct chmc_bank_info logical_banks[CHMCTRL_NBANKS]; +}; + +#define JBUSMC_REGS_SIZE 8 + +#define JB_MC_REG1_DIMM2_BANK3 0x8000000000000000UL +#define JB_MC_REG1_DIMM1_BANK1 0x4000000000000000UL +#define JB_MC_REG1_DIMM2_BANK2 0x2000000000000000UL +#define JB_MC_REG1_DIMM1_BANK0 0x1000000000000000UL +#define JB_MC_REG1_XOR 0x0000010000000000UL +#define JB_MC_REG1_ADDR_GEN_2 0x000000e000000000UL +#define JB_MC_REG1_ADDR_GEN_2_SHIFT 37 +#define JB_MC_REG1_ADDR_GEN_1 0x0000001c00000000UL +#define JB_MC_REG1_ADDR_GEN_1_SHIFT 34 +#define JB_MC_REG1_INTERLEAVE 0x0000000001800000UL +#define JB_MC_REG1_INTERLEAVE_SHIFT 23 +#define JB_MC_REG1_DIMM2_PTYPE 0x0000000000200000UL +#define JB_MC_REG1_DIMM2_PTYPE_SHIFT 21 +#define JB_MC_REG1_DIMM1_PTYPE 0x0000000000100000UL +#define JB_MC_REG1_DIMM1_PTYPE_SHIFT 20 + +#define PART_TYPE_X8 0 +#define PART_TYPE_X4 1 + +#define INTERLEAVE_NONE 0 +#define INTERLEAVE_SAME 1 +#define INTERLEAVE_INTERNAL 2 +#define INTERLEAVE_BOTH 3 + +#define ADDR_GEN_128MB 0 +#define ADDR_GEN_256MB 1 +#define ADDR_GEN_512MB 2 +#define ADDR_GEN_1GB 3 + +#define JB_NUM_DIMM_GROUPS 2 +#define JB_NUM_DIMMS_PER_GROUP 2 +#define JB_NUM_DIMMS (JB_NUM_DIMM_GROUPS * JB_NUM_DIMMS_PER_GROUP) + +struct jbusmc_obp_map { + unsigned char dimm_map[18]; + unsigned char pin_map[144]; +}; + +struct jbusmc_obp_mem_layout { + /* One max 8-byte string label per DIMM. Usually + * this matches the label on the motherboard where + * that DIMM resides. + */ + char dimm_labels[JB_NUM_DIMMS][DIMM_LABEL_SZ]; + + /* If symmetric use map[0], else it is + * asymmetric and map[1] should be used. + */ + char symmetric; + + struct jbusmc_obp_map map; + + char _pad; +}; - u64 timing_control1; - u64 timing_control2; - u64 timing_control3; - u64 timing_control4; - u64 memaddr_control; +struct jbusmc_dimm_group { + struct jbusmc *controller; + int index; + u64 base_addr; + u64 size; +}; - struct bank_info logical_banks[CHMCTRL_NBANKS]; +struct jbusmc { + void __iomem *regs; + u64 mc_reg_1; + u32 portid; + struct jbusmc_obp_mem_layout layout; + int layout_len; + int num_dimm_groups; + struct jbusmc_dimm_group dimm_groups[JB_NUM_DIMM_GROUPS]; + struct list_head list; }; +static DEFINE_SPINLOCK(mctrl_list_lock); static LIST_HEAD(mctrl_list); +static void mc_list_add(struct list_head *list) +{ + spin_lock(&mctrl_list_lock); + list_add(list, &mctrl_list); + spin_unlock(&mctrl_list_lock); +} + +static void mc_list_del(struct list_head *list) +{ + spin_lock(&mctrl_list_lock); + list_del_init(list); + spin_unlock(&mctrl_list_lock); +} + +#define SYNDROME_MIN -1 +#define SYNDROME_MAX 144 + +/* Covert syndrome code into the way the bits are positioned + * on the bus. + */ +static int syndrome_to_qword_code(int syndrome_code) +{ + if (syndrome_code < 128) + syndrome_code += 16; + else if (syndrome_code < 128 + 9) + syndrome_code -= (128 - 7); + else if (syndrome_code < (128 + 9 + 3)) + syndrome_code -= (128 + 9 - 4); + else + syndrome_code -= (128 + 9 + 3); + return syndrome_code; +} + +/* All this magic has to do with how a cache line comes over the wire + * on Safari and JBUS. A 64-bit line comes over in 1 or more quadword + * cycles, each of which transmit ECC/MTAG info as well as the actual + * data. + */ +#define L2_LINE_SIZE 64 +#define L2_LINE_ADDR_MSK (L2_LINE_SIZE - 1) +#define QW_PER_LINE 4 +#define QW_BYTES (L2_LINE_SIZE / QW_PER_LINE) +#define QW_BITS 144 +#define SAFARI_LAST_BIT (576 - 1) +#define JBUS_LAST_BIT (144 - 1) + +static void get_pin_and_dimm_str(int syndrome_code, unsigned long paddr, + int *pin_p, char **dimm_str_p, void *_prop, + int base_dimm_offset) +{ + int qword_code = syndrome_to_qword_code(syndrome_code); + int cache_line_offset; + int offset_inverse; + int dimm_map_index; + int map_val; + + if (mc_type == MC_TYPE_JBUS) { + struct jbusmc_obp_mem_layout *p = _prop; + + /* JBUS */ + cache_line_offset = qword_code; + offset_inverse = (JBUS_LAST_BIT - cache_line_offset); + dimm_map_index = offset_inverse / 8; + map_val = p->map.dimm_map[dimm_map_index]; + map_val = ((map_val >> ((7 - (offset_inverse & 7)))) & 1); + *dimm_str_p = p->dimm_labels[base_dimm_offset + map_val]; + *pin_p = p->map.pin_map[cache_line_offset]; + } else { + struct chmc_obp_mem_layout *p = _prop; + struct chmc_obp_map *mp; + int qword; + + /* Safari */ + if (p->symmetric) + mp = &p->map[0]; + else + mp = &p->map[1]; + + qword = (paddr & L2_LINE_ADDR_MSK) / QW_BYTES; + cache_line_offset = ((3 - qword) * QW_BITS) + qword_code; + offset_inverse = (SAFARI_LAST_BIT - cache_line_offset); + dimm_map_index = offset_inverse >> 2; + map_val = mp->dimm_map[dimm_map_index]; + map_val = ((map_val >> ((3 - (offset_inverse & 3)) << 1)) & 0x3); + *dimm_str_p = p->dimm_labels[base_dimm_offset + map_val]; + *pin_p = mp->pin_map[cache_line_offset]; + } +} + +static struct jbusmc_dimm_group *jbusmc_find_dimm_group(unsigned long phys_addr) +{ + struct jbusmc *p; + + list_for_each_entry(p, &mctrl_list, list) { + int i; + + for (i = 0; i < p->num_dimm_groups; i++) { + struct jbusmc_dimm_group *dp = &p->dimm_groups[i]; + + if (phys_addr < dp->base_addr || + (dp->base_addr + dp->size) <= phys_addr) + continue; + + return dp; + } + } + return NULL; +} + +static int jbusmc_print_dimm(int syndrome_code, + unsigned long phys_addr, + char *buf, int buflen) +{ + struct jbusmc_obp_mem_layout *prop; + struct jbusmc_dimm_group *dp; + struct jbusmc *p; + int first_dimm; + + dp = jbusmc_find_dimm_group(phys_addr); + if (dp == NULL || + syndrome_code < SYNDROME_MIN || + syndrome_code > SYNDROME_MAX) { + buf[0] = '?'; + buf[1] = '?'; + buf[2] = '?'; + buf[3] = '\0'; + } + p = dp->controller; + prop = &p->layout; + + first_dimm = dp->index * JB_NUM_DIMMS_PER_GROUP; + + if (syndrome_code != SYNDROME_MIN) { + char *dimm_str; + int pin; + + get_pin_and_dimm_str(syndrome_code, phys_addr, &pin, + &dimm_str, prop, first_dimm); + sprintf(buf, "%s, pin %3d", dimm_str, pin); + } else { + int dimm; + + /* Multi-bit error, we just dump out all the + * dimm labels associated with this dimm group. + */ + for (dimm = 0; dimm < JB_NUM_DIMMS_PER_GROUP; dimm++) { + sprintf(buf, "%s ", + prop->dimm_labels[first_dimm + dimm]); + buf += strlen(buf); + } + } + + return 0; +} + +static u64 __devinit jbusmc_dimm_group_size(u64 base, + const struct linux_prom64_registers *mem_regs, + int num_mem_regs) +{ + u64 max = base + (8UL * 1024 * 1024 * 1024); + u64 max_seen = base; + int i; + + for (i = 0; i < num_mem_regs; i++) { + const struct linux_prom64_registers *ent; + u64 this_base; + u64 this_end; + + ent = &mem_regs[i]; + this_base = ent->phys_addr; + this_end = this_base + ent->reg_size; + if (base < this_base || base >= this_end) + continue; + if (this_end > max) + this_end = max; + if (this_end > max_seen) + max_seen = this_end; + } + + return max_seen - base; +} + +static void __devinit jbusmc_construct_one_dimm_group(struct jbusmc *p, + unsigned long index, + const struct linux_prom64_registers *mem_regs, + int num_mem_regs) +{ + struct jbusmc_dimm_group *dp = &p->dimm_groups[index]; + + dp->controller = p; + dp->index = index; + + dp->base_addr = (p->portid * (64UL * 1024 * 1024 * 1024)); + dp->base_addr += (index * (8UL * 1024 * 1024 * 1024)); + dp->size = jbusmc_dimm_group_size(dp->base_addr, mem_regs, num_mem_regs); +} + +static void __devinit jbusmc_construct_dimm_groups(struct jbusmc *p, + const struct linux_prom64_registers *mem_regs, + int num_mem_regs) +{ + if (p->mc_reg_1 & JB_MC_REG1_DIMM1_BANK0) { + jbusmc_construct_one_dimm_group(p, 0, mem_regs, num_mem_regs); + p->num_dimm_groups++; + } + if (p->mc_reg_1 & JB_MC_REG1_DIMM2_BANK2) { + jbusmc_construct_one_dimm_group(p, 1, mem_regs, num_mem_regs); + p->num_dimm_groups++; + } +} + +static int __devinit jbusmc_probe(struct of_device *op, + const struct of_device_id *match) +{ + const struct linux_prom64_registers *mem_regs; + struct device_node *mem_node; + int err, len, num_mem_regs; + struct jbusmc *p; + const u32 *prop; + const void *ml; + + err = -ENODEV; + mem_node = of_find_node_by_path("/memory"); + if (!mem_node) { + printk(KERN_ERR PFX "Cannot find /memory node.\n"); + goto out; + } + mem_regs = of_get_property(mem_node, "reg", &len); + if (!mem_regs) { + printk(KERN_ERR PFX "Cannot get reg property of /memory node.\n"); + goto out; + } + num_mem_regs = len / sizeof(*mem_regs); + + err = -ENOMEM; + p = kzalloc(sizeof(*p), GFP_KERNEL); + if (!p) { + printk(KERN_ERR PFX "Cannot allocate struct jbusmc.\n"); + goto out; + } + + INIT_LIST_HEAD(&p->list); + + err = -ENODEV; + prop = of_get_property(op->node, "portid", &len); + if (!prop || len != 4) { + printk(KERN_ERR PFX "Cannot find portid.\n"); + goto out_free; + } + + p->portid = *prop; + + prop = of_get_property(op->node, "memory-control-register-1", &len); + if (!prop || len != 8) { + printk(KERN_ERR PFX "Cannot get memory control register 1.\n"); + goto out_free; + } + + p->mc_reg_1 = ((u64)prop[0] << 32) | (u64) prop[1]; + + err = -ENOMEM; + p->regs = of_ioremap(&op->resource[0], 0, JBUSMC_REGS_SIZE, "jbusmc"); + if (!p->regs) { + printk(KERN_ERR PFX "Cannot map jbusmc regs.\n"); + goto out_free; + } + + err = -ENODEV; + ml = of_get_property(op->node, "memory-layout", &p->layout_len); + if (!ml) { + printk(KERN_ERR PFX "Cannot get memory layout property.\n"); + goto out_iounmap; + } + if (p->layout_len > sizeof(p->layout)) { + printk(KERN_ERR PFX "Unexpected memory-layout size %d\n", + p->layout_len); + goto out_iounmap; + } + memcpy(&p->layout, ml, p->layout_len); + + jbusmc_construct_dimm_groups(p, mem_regs, num_mem_regs); + + mc_list_add(&p->list); + + printk(KERN_INFO PFX "UltraSPARC-IIIi memory controller at %s\n", + op->node->full_name); + + dev_set_drvdata(&op->dev, p); + + err = 0; + +out: + return err; + +out_iounmap: + of_iounmap(&op->resource[0], p->regs, JBUSMC_REGS_SIZE); + +out_free: + kfree(p); + goto out; +} + /* Does BANK decode PHYS_ADDR? */ -static int bank_match(struct bank_info *bp, unsigned long phys_addr) +static int chmc_bank_match(struct chmc_bank_info *bp, unsigned long phys_addr) { unsigned long upper_bits = (phys_addr & PA_UPPER_BITS) >> PA_UPPER_BITS_SHIFT; unsigned long lower_bits = (phys_addr & PA_LOWER_BITS) >> PA_LOWER_BITS_SHIFT; @@ -118,25 +515,18 @@ static int bank_match(struct bank_info *bp, unsigned long phys_addr) } /* Given PHYS_ADDR, search memory controller banks for a match. */ -static struct bank_info *find_bank(unsigned long phys_addr) +static struct chmc_bank_info *chmc_find_bank(unsigned long phys_addr) { - struct list_head *mctrl_head = &mctrl_list; - struct list_head *mctrl_entry = mctrl_head->next; + struct chmc *p; - for (;;) { - struct mctrl_info *mp = - list_entry(mctrl_entry, struct mctrl_info, list); + list_for_each_entry(p, &mctrl_list, list) { int bank_no; - if (mctrl_entry == mctrl_head) - break; - mctrl_entry = mctrl_entry->next; - for (bank_no = 0; bank_no < CHMCTRL_NBANKS; bank_no++) { - struct bank_info *bp; + struct chmc_bank_info *bp; - bp = &mp->logical_banks[bank_no]; - if (bank_match(bp, phys_addr)) + bp = &p->logical_banks[bank_no]; + if (chmc_bank_match(bp, phys_addr)) return bp; } } @@ -145,17 +535,15 @@ static struct bank_info *find_bank(unsigned long phys_addr) } /* This is the main purpose of this driver. */ -#define SYNDROME_MIN -1 -#define SYNDROME_MAX 144 -int chmc_getunumber(int syndrome_code, - unsigned long phys_addr, - char *buf, int buflen) +static int chmc_print_dimm(int syndrome_code, + unsigned long phys_addr, + char *buf, int buflen) { - struct bank_info *bp; - struct obp_mem_layout *prop; + struct chmc_bank_info *bp; + struct chmc_obp_mem_layout *prop; int bank_in_controller, first_dimm; - bp = find_bank(phys_addr); + bp = chmc_find_bank(phys_addr); if (bp == NULL || syndrome_code < SYNDROME_MIN || syndrome_code > SYNDROME_MAX) { @@ -166,60 +554,18 @@ int chmc_getunumber(int syndrome_code, return 0; } - prop = &bp->mp->layout_prop; + prop = &bp->p->layout_prop; bank_in_controller = bp->bank_id & (CHMCTRL_NBANKS - 1); first_dimm = (bank_in_controller & (CHMCTRL_NDGRPS - 1)); first_dimm *= CHMCTRL_NDIMMS; if (syndrome_code != SYNDROME_MIN) { - struct obp_map *map; - int qword, where_in_line, where, map_index, map_offset; - unsigned int map_val; + char *dimm_str; + int pin; - /* Yaay, single bit error so we can figure out - * the exact dimm. - */ - if (prop->symmetric) - map = &prop->map[0]; - else - map = &prop->map[1]; - - /* Covert syndrome code into the way the bits are - * positioned on the bus. - */ - if (syndrome_code < 144 - 16) - syndrome_code += 16; - else if (syndrome_code < 144) - syndrome_code -= (144 - 7); - else if (syndrome_code < (144 + 3)) - syndrome_code -= (144 + 3 - 4); - else - syndrome_code -= 144 + 3; - - /* All this magic has to do with how a cache line - * comes over the wire on Safari. A 64-bit line - * comes over in 4 quadword cycles, each of which - * transmit ECC/MTAG info as well as the actual - * data. 144 bits per quadword, 576 total. - */ -#define LINE_SIZE 64 -#define LINE_ADDR_MSK (LINE_SIZE - 1) -#define QW_PER_LINE 4 -#define QW_BYTES (LINE_SIZE / QW_PER_LINE) -#define QW_BITS 144 -#define LAST_BIT (576 - 1) - - qword = (phys_addr & LINE_ADDR_MSK) / QW_BYTES; - where_in_line = ((3 - qword) * QW_BITS) + syndrome_code; - where = (LAST_BIT - where_in_line); - map_index = where >> 2; - map_offset = where & 0x3; - map_val = map->dimm_map[map_index]; - map_val = ((map_val >> ((3 - map_offset) << 1)) & (2 - 1)); - - sprintf(buf, "%s, pin %3d", - prop->dimm_labels[first_dimm + map_val], - map->pin_map[where_in_line]); + get_pin_and_dimm_str(syndrome_code, phys_addr, &pin, + &dimm_str, prop, first_dimm); + sprintf(buf, "%s, pin %3d", dimm_str, pin); } else { int dimm; @@ -240,7 +586,7 @@ int chmc_getunumber(int syndrome_code, * the code is executing, you must use special ASI load/store else * you go through the global mapping. */ -static u64 read_mcreg(struct mctrl_info *mp, unsigned long offset) +static u64 chmc_read_mcreg(struct chmc *p, unsigned long offset) { unsigned long ret, this_cpu; @@ -248,14 +594,14 @@ static u64 read_mcreg(struct mctrl_info *mp, unsigned long offset) this_cpu = real_hard_smp_processor_id(); - if (mp->portid == this_cpu) { + if (p->portid == this_cpu) { __asm__ __volatile__("ldxa [%1] %2, %0" : "=r" (ret) : "r" (offset), "i" (ASI_MCU_CTRL_REG)); } else { __asm__ __volatile__("ldxa [%1] %2, %0" : "=r" (ret) - : "r" (mp->regs + offset), + : "r" (p->regs + offset), "i" (ASI_PHYS_BYPASS_EC_E)); } @@ -265,178 +611,253 @@ static u64 read_mcreg(struct mctrl_info *mp, unsigned long offset) } #if 0 /* currently unused */ -static void write_mcreg(struct mctrl_info *mp, unsigned long offset, u64 val) +static void chmc_write_mcreg(struct chmc *p, unsigned long offset, u64 val) { - if (mp->portid == smp_processor_id()) { + if (p->portid == smp_processor_id()) { __asm__ __volatile__("stxa %0, [%1] %2" : : "r" (val), "r" (offset), "i" (ASI_MCU_CTRL_REG)); } else { __asm__ __volatile__("ldxa %0, [%1] %2" : : "r" (val), - "r" (mp->regs + offset), + "r" (p->regs + offset), "i" (ASI_PHYS_BYPASS_EC_E)); } } #endif -static void interpret_one_decode_reg(struct mctrl_info *mp, int which_bank, u64 val) +static void chmc_interpret_one_decode_reg(struct chmc *p, int which_bank, u64 val) { - struct bank_info *p = &mp->logical_banks[which_bank]; - - p->mp = mp; - p->bank_id = (CHMCTRL_NBANKS * mp->portid) + which_bank; - p->raw_reg = val; - p->valid = (val & MEM_DECODE_VALID) >> MEM_DECODE_VALID_SHIFT; - p->uk = (val & MEM_DECODE_UK) >> MEM_DECODE_UK_SHIFT; - p->um = (val & MEM_DECODE_UM) >> MEM_DECODE_UM_SHIFT; - p->lk = (val & MEM_DECODE_LK) >> MEM_DECODE_LK_SHIFT; - p->lm = (val & MEM_DECODE_LM) >> MEM_DECODE_LM_SHIFT; - - p->base = (p->um); - p->base &= ~(p->uk); - p->base <<= PA_UPPER_BITS_SHIFT; - - switch(p->lk) { + struct chmc_bank_info *bp = &p->logical_banks[which_bank]; + + bp->p = p; + bp->bank_id = (CHMCTRL_NBANKS * p->portid) + which_bank; + bp->raw_reg = val; + bp->valid = (val & MEM_DECODE_VALID) >> MEM_DECODE_VALID_SHIFT; + bp->uk = (val & MEM_DECODE_UK) >> MEM_DECODE_UK_SHIFT; + bp->um = (val & MEM_DECODE_UM) >> MEM_DECODE_UM_SHIFT; + bp->lk = (val & MEM_DECODE_LK) >> MEM_DECODE_LK_SHIFT; + bp->lm = (val & MEM_DECODE_LM) >> MEM_DECODE_LM_SHIFT; + + bp->base = (bp->um); + bp->base &= ~(bp->uk); + bp->base <<= PA_UPPER_BITS_SHIFT; + + switch(bp->lk) { case 0xf: default: - p->interleave = 1; + bp->interleave = 1; break; case 0xe: - p->interleave = 2; + bp->interleave = 2; break; case 0xc: - p->interleave = 4; + bp->interleave = 4; break; case 0x8: - p->interleave = 8; + bp->interleave = 8; break; case 0x0: - p->interleave = 16; + bp->interleave = 16; break; }; /* UK[10] is reserved, and UK[11] is not set for the SDRAM * bank size definition. */ - p->size = (((unsigned long)p->uk & - ((1UL << 10UL) - 1UL)) + 1UL) << PA_UPPER_BITS_SHIFT; - p->size /= p->interleave; + bp->size = (((unsigned long)bp->uk & + ((1UL << 10UL) - 1UL)) + 1UL) << PA_UPPER_BITS_SHIFT; + bp->size /= bp->interleave; } -static void fetch_decode_regs(struct mctrl_info *mp) +static void chmc_fetch_decode_regs(struct chmc *p) { - if (mp->layout_size == 0) + if (p->layout_size == 0) return; - interpret_one_decode_reg(mp, 0, - read_mcreg(mp, CHMCTRL_DECODE1)); - interpret_one_decode_reg(mp, 1, - read_mcreg(mp, CHMCTRL_DECODE2)); - interpret_one_decode_reg(mp, 2, - read_mcreg(mp, CHMCTRL_DECODE3)); - interpret_one_decode_reg(mp, 3, - read_mcreg(mp, CHMCTRL_DECODE4)); + chmc_interpret_one_decode_reg(p, 0, + chmc_read_mcreg(p, CHMCTRL_DECODE1)); + chmc_interpret_one_decode_reg(p, 1, + chmc_read_mcreg(p, CHMCTRL_DECODE2)); + chmc_interpret_one_decode_reg(p, 2, + chmc_read_mcreg(p, CHMCTRL_DECODE3)); + chmc_interpret_one_decode_reg(p, 3, + chmc_read_mcreg(p, CHMCTRL_DECODE4)); } -static int init_one_mctrl(struct device_node *dp) +static int __devinit chmc_probe(struct of_device *op, + const struct of_device_id *match) { - struct mctrl_info *mp = kzalloc(sizeof(*mp), GFP_KERNEL); - int portid = of_getintprop_default(dp, "portid", -1); - const struct linux_prom64_registers *regs; + struct device_node *dp = op->node; + unsigned long ver; const void *pval; - int len; + int len, portid; + struct chmc *p; + int err; + + err = -ENODEV; + __asm__ ("rdpr %%ver, %0" : "=r" (ver)); + if ((ver >> 32UL) == __JALAPENO_ID || + (ver >> 32UL) == __SERRANO_ID) + goto out; - if (!mp) - return -1; + portid = of_getintprop_default(dp, "portid", -1); if (portid == -1) - goto fail; + goto out; - mp->portid = portid; pval = of_get_property(dp, "memory-layout", &len); - mp->layout_size = len; - if (!pval) - mp->layout_size = 0; - else { - if (mp->layout_size > sizeof(mp->layout_prop)) - goto fail; - memcpy(&mp->layout_prop, pval, len); + if (pval && len > sizeof(p->layout_prop)) { + printk(KERN_ERR PFX "Unexpected memory-layout property " + "size %d.\n", len); + goto out; } - regs = of_get_property(dp, "reg", NULL); - if (!regs || regs->reg_size != 0x48) - goto fail; + err = -ENOMEM; + p = kzalloc(sizeof(*p), GFP_KERNEL); + if (!p) { + printk(KERN_ERR PFX "Could not allocate struct chmc.\n"); + goto out; + } - mp->regs = ioremap(regs->phys_addr, regs->reg_size); - if (mp->regs == NULL) - goto fail; + p->portid = portid; + p->layout_size = len; + if (!pval) + p->layout_size = 0; + else + memcpy(&p->layout_prop, pval, len); + + p->regs = of_ioremap(&op->resource[0], 0, 0x48, "chmc"); + if (!p->regs) { + printk(KERN_ERR PFX "Could not map registers.\n"); + goto out_free; + } - if (mp->layout_size != 0UL) { - mp->timing_control1 = read_mcreg(mp, CHMCTRL_TCTRL1); - mp->timing_control2 = read_mcreg(mp, CHMCTRL_TCTRL2); - mp->timing_control3 = read_mcreg(mp, CHMCTRL_TCTRL3); - mp->timing_control4 = read_mcreg(mp, CHMCTRL_TCTRL4); - mp->memaddr_control = read_mcreg(mp, CHMCTRL_MACTRL); + if (p->layout_size != 0UL) { + p->timing_control1 = chmc_read_mcreg(p, CHMCTRL_TCTRL1); + p->timing_control2 = chmc_read_mcreg(p, CHMCTRL_TCTRL2); + p->timing_control3 = chmc_read_mcreg(p, CHMCTRL_TCTRL3); + p->timing_control4 = chmc_read_mcreg(p, CHMCTRL_TCTRL4); + p->memaddr_control = chmc_read_mcreg(p, CHMCTRL_MACTRL); } - fetch_decode_regs(mp); + chmc_fetch_decode_regs(p); - list_add(&mp->list, &mctrl_list); + mc_list_add(&p->list); - /* Report the device. */ - printk(KERN_INFO "%s: US3 memory controller at %p [%s]\n", + printk(KERN_INFO PFX "UltraSPARC-III memory controller at %s [%s]\n", dp->full_name, - mp->regs, (mp->layout_size ? "ACTIVE" : "INACTIVE")); + (p->layout_size ? "ACTIVE" : "INACTIVE")); - return 0; + dev_set_drvdata(&op->dev, p); + + err = 0; + +out: + return err; + +out_free: + kfree(p); + goto out; +} -fail: - if (mp) { - if (mp->regs != NULL) - iounmap(mp->regs); - kfree(mp); +static int __devinit us3mc_probe(struct of_device *op, + const struct of_device_id *match) +{ + if (mc_type == MC_TYPE_SAFARI) + return chmc_probe(op, match); + else if (mc_type == MC_TYPE_JBUS) + return jbusmc_probe(op, match); + return -ENODEV; +} + +static void __devexit chmc_destroy(struct of_device *op, struct chmc *p) +{ + list_del(&p->list); + of_iounmap(&op->resource[0], p->regs, 0x48); + kfree(p); +} + +static void __devexit jbusmc_destroy(struct of_device *op, struct jbusmc *p) +{ + mc_list_del(&p->list); + of_iounmap(&op->resource[0], p->regs, JBUSMC_REGS_SIZE); + kfree(p); +} + +static int __devexit us3mc_remove(struct of_device *op) +{ + void *p = dev_get_drvdata(&op->dev); + + if (p) { + if (mc_type == MC_TYPE_SAFARI) + chmc_destroy(op, p); + else if (mc_type == MC_TYPE_JBUS) + jbusmc_destroy(op, p); } - return -1; + return 0; +} + +static const struct of_device_id us3mc_match[] = { + { + .name = "memory-controller", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, us3mc_match); + +static struct of_platform_driver us3mc_driver = { + .name = "us3mc", + .match_table = us3mc_match, + .probe = us3mc_probe, + .remove = __devexit_p(us3mc_remove), +}; + +static inline bool us3mc_platform(void) +{ + if (tlb_type == cheetah || tlb_type == cheetah_plus) + return true; + return false; } -static int __init chmc_init(void) +static int __init us3mc_init(void) { - struct device_node *dp; + unsigned long ver; + int ret; - /* This driver is only for cheetah platforms. */ - if (tlb_type != cheetah && tlb_type != cheetah_plus) + if (!us3mc_platform()) return -ENODEV; - for_each_node_by_name(dp, "memory-controller") - init_one_mctrl(dp); + __asm__ __volatile__("rdpr %%ver, %0" : "=r" (ver)); + if ((ver >> 32UL) == __JALAPENO_ID || + (ver >> 32UL) == __SERRANO_ID) { + mc_type = MC_TYPE_JBUS; + us3mc_dimm_printer = jbusmc_print_dimm; + } else { + mc_type = MC_TYPE_SAFARI; + us3mc_dimm_printer = chmc_print_dimm; + } - for_each_node_by_name(dp, "mc-us3") - init_one_mctrl(dp); + ret = register_dimm_printer(us3mc_dimm_printer); - return 0; + if (!ret) { + ret = of_register_driver(&us3mc_driver, &of_bus_type); + if (ret) + unregister_dimm_printer(us3mc_dimm_printer); + } + return ret; } -static void __exit chmc_cleanup(void) +static void __exit us3mc_cleanup(void) { - struct list_head *head = &mctrl_list; - struct list_head *tmp = head->next; - - for (;;) { - struct mctrl_info *p = - list_entry(tmp, struct mctrl_info, list); - if (tmp == head) - break; - tmp = tmp->next; - - list_del(&p->list); - iounmap(p->regs); - kfree(p); + if (us3mc_platform()) { + unregister_dimm_printer(us3mc_dimm_printer); + of_unregister_driver(&us3mc_driver); } } -module_init(chmc_init); -module_exit(chmc_cleanup); +module_init(us3mc_init); +module_exit(us3mc_cleanup); diff --git a/arch/sparc64/kernel/cpu.c b/arch/sparc64/kernel/cpu.c index 0097c08dc60..0c9ac83ed0a 100644 --- a/arch/sparc64/kernel/cpu.c +++ b/arch/sparc64/kernel/cpu.c @@ -1,7 +1,7 @@ /* cpu.c: Dinky routines to look for the kind of Sparc cpu * we are on. * - * Copyright (C) 1996, 2007 David S. Miller (davem@davemloft.net) + * Copyright (C) 1996, 2007, 2008 David S. Miller (davem@davemloft.net) */ #include <linux/kernel.h> @@ -19,53 +19,86 @@ DEFINE_PER_CPU(cpuinfo_sparc, __cpu_data) = { 0 }; -struct cpu_iu_info { - short manuf; - short impl; - char* cpu_name; /* should be enough I hope... */ +struct cpu_chip_info { + unsigned short manuf; + unsigned short impl; + const char *cpu_name; + const char *fp_name; }; -struct cpu_fp_info { - short manuf; - short impl; - char fpu_vers; - char* fp_name; +static const struct cpu_chip_info cpu_chips[] = { + { + .manuf = 0x17, + .impl = 0x10, + .cpu_name = "TI UltraSparc I (SpitFire)", + .fp_name = "UltraSparc I integrated FPU", + }, + { + .manuf = 0x22, + .impl = 0x10, + .cpu_name = "TI UltraSparc I (SpitFire)", + .fp_name = "UltraSparc I integrated FPU", + }, + { + .manuf = 0x17, + .impl = 0x11, + .cpu_name = "TI UltraSparc II (BlackBird)", + .fp_name = "UltraSparc II integrated FPU", + }, + { + .manuf = 0x17, + .impl = 0x12, + .cpu_name = "TI UltraSparc IIi (Sabre)", + .fp_name = "UltraSparc IIi integrated FPU", + }, + { + .manuf = 0x17, + .impl = 0x13, + .cpu_name = "TI UltraSparc IIe (Hummingbird)", + .fp_name = "UltraSparc IIe integrated FPU", + }, + { + .manuf = 0x3e, + .impl = 0x14, + .cpu_name = "TI UltraSparc III (Cheetah)", + .fp_name = "UltraSparc III integrated FPU", + }, + { + .manuf = 0x3e, + .impl = 0x15, + .cpu_name = "TI UltraSparc III+ (Cheetah+)", + .fp_name = "UltraSparc III+ integrated FPU", + }, + { + .manuf = 0x3e, + .impl = 0x16, + .cpu_name = "TI UltraSparc IIIi (Jalapeno)", + .fp_name = "UltraSparc IIIi integrated FPU", + }, + { + .manuf = 0x3e, + .impl = 0x18, + .cpu_name = "TI UltraSparc IV (Jaguar)", + .fp_name = "UltraSparc IV integrated FPU", + }, + { + .manuf = 0x3e, + .impl = 0x19, + .cpu_name = "TI UltraSparc IV+ (Panther)", + .fp_name = "UltraSparc IV+ integrated FPU", + }, + { + .manuf = 0x3e, + .impl = 0x22, + .cpu_name = "TI UltraSparc IIIi+ (Serrano)", + .fp_name = "UltraSparc IIIi+ integrated FPU", + }, }; -static struct cpu_fp_info linux_sparc_fpu[] = { - { 0x17, 0x10, 0, "UltraSparc I integrated FPU"}, - { 0x22, 0x10, 0, "UltraSparc I integrated FPU"}, - { 0x17, 0x11, 0, "UltraSparc II integrated FPU"}, - { 0x17, 0x12, 0, "UltraSparc IIi integrated FPU"}, - { 0x17, 0x13, 0, "UltraSparc IIe integrated FPU"}, - { 0x3e, 0x14, 0, "UltraSparc III integrated FPU"}, - { 0x3e, 0x15, 0, "UltraSparc III+ integrated FPU"}, - { 0x3e, 0x16, 0, "UltraSparc IIIi integrated FPU"}, - { 0x3e, 0x18, 0, "UltraSparc IV integrated FPU"}, - { 0x3e, 0x19, 0, "UltraSparc IV+ integrated FPU"}, - { 0x3e, 0x22, 0, "UltraSparc IIIi+ integrated FPU"}, -}; - -#define NSPARCFPU ARRAY_SIZE(linux_sparc_fpu) - -static struct cpu_iu_info linux_sparc_chips[] = { - { 0x17, 0x10, "TI UltraSparc I (SpitFire)"}, - { 0x22, 0x10, "TI UltraSparc I (SpitFire)"}, - { 0x17, 0x11, "TI UltraSparc II (BlackBird)"}, - { 0x17, 0x12, "TI UltraSparc IIi (Sabre)"}, - { 0x17, 0x13, "TI UltraSparc IIe (Hummingbird)"}, - { 0x3e, 0x14, "TI UltraSparc III (Cheetah)"}, - { 0x3e, 0x15, "TI UltraSparc III+ (Cheetah+)"}, - { 0x3e, 0x16, "TI UltraSparc IIIi (Jalapeno)"}, - { 0x3e, 0x18, "TI UltraSparc IV (Jaguar)"}, - { 0x3e, 0x19, "TI UltraSparc IV+ (Panther)"}, - { 0x3e, 0x22, "TI UltraSparc IIIi+ (Serrano)"}, -}; +#define NSPARCCHIPS ARRAY_SIZE(linux_sparc_chips) -#define NSPARCCHIPS ARRAY_SIZE(linux_sparc_chips) - -char *sparc_cpu_type; -char *sparc_fpu_type; +const char *sparc_cpu_type; +const char *sparc_fpu_type; static void __init sun4v_cpu_probe(void) { @@ -89,68 +122,45 @@ static void __init sun4v_cpu_probe(void) } } -void __init cpu_probe(void) +static const struct cpu_chip_info * __init find_cpu_chip(unsigned short manuf, + unsigned short impl) { - unsigned long ver, fpu_vers, manuf, impl, fprs; int i; - - if (tlb_type == hypervisor) { - sun4v_cpu_probe(); - return; - } - fprs = fprs_read(); - fprs_write(FPRS_FEF); - __asm__ __volatile__ ("rdpr %%ver, %0; stx %%fsr, [%1]" - : "=&r" (ver) - : "r" (&fpu_vers)); - fprs_write(fprs); - - manuf = ((ver >> 48) & 0xffff); - impl = ((ver >> 32) & 0xffff); - - fpu_vers = ((fpu_vers >> 17) & 0x7); - -retry: - for (i = 0; i < NSPARCCHIPS; i++) { - if (linux_sparc_chips[i].manuf == manuf) { - if (linux_sparc_chips[i].impl == impl) { - sparc_cpu_type = - linux_sparc_chips[i].cpu_name; - break; - } - } - } + for (i = 0; i < ARRAY_SIZE(cpu_chips); i++) { + const struct cpu_chip_info *p = &cpu_chips[i]; - if (i == NSPARCCHIPS) { - /* Maybe it is a cheetah+ derivative, report it as cheetah+ - * in that case until we learn the real names. - */ - if (manuf == 0x3e && - impl > 0x15) { - impl = 0x15; - goto retry; - } else { - printk("DEBUG: manuf[%lx] impl[%lx]\n", - manuf, impl); - } - sparc_cpu_type = "Unknown CPU"; + if (p->manuf == manuf && p->impl == impl) + return p; } + return NULL; +} - for (i = 0; i < NSPARCFPU; i++) { - if (linux_sparc_fpu[i].manuf == manuf && - linux_sparc_fpu[i].impl == impl) { - if (linux_sparc_fpu[i].fpu_vers == fpu_vers) { - sparc_fpu_type = - linux_sparc_fpu[i].fp_name; - break; - } +static int __init cpu_type_probe(void) +{ + if (tlb_type == hypervisor) { + sun4v_cpu_probe(); + } else { + unsigned long ver, manuf, impl; + const struct cpu_chip_info *p; + + __asm__ __volatile__("rdpr %%ver, %0" : "=r" (ver)); + + manuf = ((ver >> 48) & 0xffff); + impl = ((ver >> 32) & 0xffff); + + p = find_cpu_chip(manuf, impl); + if (p) { + sparc_cpu_type = p->cpu_name; + sparc_fpu_type = p->fp_name; + } else { + printk(KERN_ERR "CPU: Unknown chip, manuf[%lx] impl[%lx]\n", + manuf, impl); + sparc_cpu_type = "Unknown CPU"; + sparc_fpu_type = "Unknown FPU"; } } - - if (i == NSPARCFPU) { - printk("DEBUG: manuf[%lx] impl[%lx] fsr.vers[%lx]\n", - manuf, impl, fpu_vers); - sparc_fpu_type = "Unknown FPU"; - } + return 0; } + +arch_initcall(cpu_type_probe); diff --git a/arch/sparc64/kernel/ds.c b/arch/sparc64/kernel/ds.c index d0fa5aa3893..f52e0534d91 100644 --- a/arch/sparc64/kernel/ds.c +++ b/arch/sparc64/kernel/ds.c @@ -1,6 +1,6 @@ /* ds.c: Domain Services driver for Logical Domains * - * Copyright (C) 2007 David S. Miller <davem@davemloft.net> + * Copyright (C) 2007, 2008 David S. Miller <davem@davemloft.net> */ #include <linux/kernel.h> @@ -1217,7 +1217,7 @@ static int ds_remove(struct vio_dev *vdev) return 0; } -static struct vio_device_id ds_match[] = { +static struct vio_device_id __initdata ds_match[] = { { .type = "domain-services-port", }, diff --git a/arch/sparc64/kernel/ebus.c b/arch/sparc64/kernel/ebus.c index 60d36d14255..77dbf6d45fa 100644 --- a/arch/sparc64/kernel/ebus.c +++ b/arch/sparc64/kernel/ebus.c @@ -1,5 +1,4 @@ -/* - * ebus.c: PCI to EBus bridge device. +/* ebus.c: EBUS DMA library code. * * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) * Copyright (C) 1999 David S. Miller (davem@redhat.com) @@ -9,23 +8,11 @@ #include <linux/kernel.h> #include <linux/types.h> #include <linux/init.h> -#include <linux/slab.h> -#include <linux/string.h> #include <linux/interrupt.h> #include <linux/delay.h> -#include <linux/pci.h> -#include <linux/of_device.h> - -#include <asm/system.h> -#include <asm/page.h> -#include <asm/ebus.h> -#include <asm/oplib.h> -#include <asm/prom.h> -#include <asm/bpp.h> -#include <asm/irq.h> -#include <asm/io.h> -/* EBUS dma library. */ +#include <asm/ebus_dma.h> +#include <asm/io.h> #define EBDMA_CSR 0x00UL /* Control/Status */ #define EBDMA_ADDR 0x04UL /* DMA Address */ @@ -268,283 +255,3 @@ void ebus_dma_enable(struct ebus_dma_info *p, int on) spin_unlock_irqrestore(&p->lock, flags); } EXPORT_SYMBOL(ebus_dma_enable); - -struct linux_ebus *ebus_chain = NULL; - -static inline void *ebus_alloc(size_t size) -{ - void *mem; - - mem = kzalloc(size, GFP_ATOMIC); - if (!mem) - panic("ebus_alloc: out of memory"); - return mem; -} - -static void __init fill_ebus_child(struct device_node *dp, - struct linux_ebus_child *dev, - int non_standard_regs) -{ - struct of_device *op; - const int *regs; - int i, len; - - dev->prom_node = dp; - printk(" (%s)", dp->name); - - regs = of_get_property(dp, "reg", &len); - if (!regs) - dev->num_addrs = 0; - else - dev->num_addrs = len / sizeof(regs[0]); - - if (non_standard_regs) { - /* This is to handle reg properties which are not - * in the parent relative format. One example are - * children of the i2c device on CompactPCI systems. - * - * So, for such devices we just record the property - * raw in the child resources. - */ - for (i = 0; i < dev->num_addrs; i++) - dev->resource[i].start = regs[i]; - } else { - for (i = 0; i < dev->num_addrs; i++) { - int rnum = regs[i]; - if (rnum >= dev->parent->num_addrs) { - prom_printf("UGH: property for %s was %d, need < %d\n", - dp->name, len, dev->parent->num_addrs); - prom_halt(); - } - dev->resource[i].start = dev->parent->resource[i].start; - dev->resource[i].end = dev->parent->resource[i].end; - dev->resource[i].flags = IORESOURCE_MEM; - dev->resource[i].name = dp->name; - } - } - - op = of_find_device_by_node(dp); - if (!op) { - dev->num_irqs = 0; - } else { - dev->num_irqs = op->num_irqs; - for (i = 0; i < dev->num_irqs; i++) - dev->irqs[i] = op->irqs[i]; - } - - if (!dev->num_irqs) { - /* - * Oh, well, some PROMs don't export interrupts - * property to children of EBus devices... - * - * Be smart about PS/2 keyboard and mouse. - */ - if (!strcmp(dev->parent->prom_node->name, "8042")) { - if (!strcmp(dev->prom_node->name, "kb_ps2")) { - dev->num_irqs = 1; - dev->irqs[0] = dev->parent->irqs[0]; - } else { - dev->num_irqs = 1; - dev->irqs[0] = dev->parent->irqs[1]; - } - } - } -} - -static int __init child_regs_nonstandard(struct linux_ebus_device *dev) -{ - if (!strcmp(dev->prom_node->name, "i2c") || - !strcmp(dev->prom_node->name, "SUNW,lombus")) - return 1; - return 0; -} - -static void __init fill_ebus_device(struct device_node *dp, struct linux_ebus_device *dev) -{ - struct linux_ebus_child *child; - struct dev_archdata *sd; - struct of_device *op; - int i, len; - - dev->prom_node = dp; - - printk(" [%s", dp->name); - - op = of_find_device_by_node(dp); - if (!op) { - dev->num_addrs = 0; - dev->num_irqs = 0; - } else { - const int *regs = of_get_property(dp, "reg", &len); - - if (!regs) - len = 0; - dev->num_addrs = len / sizeof(struct linux_prom_registers); - - for (i = 0; i < dev->num_addrs; i++) - memcpy(&dev->resource[i], - &op->resource[i], - sizeof(struct resource)); - - dev->num_irqs = op->num_irqs; - for (i = 0; i < dev->num_irqs; i++) - dev->irqs[i] = op->irqs[i]; - } - - sd = &dev->ofdev.dev.archdata; - sd->prom_node = dp; - sd->op = &dev->ofdev; - sd->iommu = dev->bus->ofdev.dev.parent->archdata.iommu; - sd->stc = dev->bus->ofdev.dev.parent->archdata.stc; - sd->numa_node = dev->bus->ofdev.dev.parent->archdata.numa_node; - - dev->ofdev.node = dp; - dev->ofdev.dev.parent = &dev->bus->ofdev.dev; - dev->ofdev.dev.bus = &ebus_bus_type; - dev_set_name(&dev->ofdev.dev, "ebus[%08x]", dp->node); - - /* Register with core */ - if (of_device_register(&dev->ofdev) != 0) - printk(KERN_DEBUG "ebus: device registration error for %s!\n", - dp->path_component_name); - - dp = dp->child; - if (dp) { - printk(" ->"); - dev->children = ebus_alloc(sizeof(struct linux_ebus_child)); - - child = dev->children; - child->next = NULL; - child->parent = dev; - child->bus = dev->bus; - fill_ebus_child(dp, child, - child_regs_nonstandard(dev)); - - while ((dp = dp->sibling) != NULL) { - child->next = ebus_alloc(sizeof(struct linux_ebus_child)); - - child = child->next; - child->next = NULL; - child->parent = dev; - child->bus = dev->bus; - fill_ebus_child(dp, child, - child_regs_nonstandard(dev)); - } - } - printk("]"); -} - -static struct pci_dev *find_next_ebus(struct pci_dev *start, int *is_rio_p) -{ - struct pci_dev *pdev = start; - - while ((pdev = pci_get_device(PCI_VENDOR_ID_SUN, PCI_ANY_ID, pdev))) - if (pdev->device == PCI_DEVICE_ID_SUN_EBUS || - pdev->device == PCI_DEVICE_ID_SUN_RIO_EBUS) - break; - - *is_rio_p = !!(pdev && (pdev->device == PCI_DEVICE_ID_SUN_RIO_EBUS)); - - return pdev; -} - -void __init ebus_init(void) -{ - struct linux_ebus_device *dev; - struct linux_ebus *ebus; - struct pci_dev *pdev; - struct device_node *dp; - int is_rio; - int num_ebus = 0; - - pdev = find_next_ebus(NULL, &is_rio); - if (!pdev) { - printk("ebus: No EBus's found.\n"); - return; - } - - dp = pci_device_to_OF_node(pdev); - - ebus_chain = ebus = ebus_alloc(sizeof(struct linux_ebus)); - ebus->next = NULL; - ebus->is_rio = is_rio; - - while (dp) { - struct device_node *child; - - /* SUNW,pci-qfe uses four empty ebuses on it. - I think we should not consider them here, - as they have half of the properties this - code expects and once we do PCI hot-plug, - we'd have to tweak with the ebus_chain - in the runtime after initialization. -jj */ - if (!dp->child) { - pdev = find_next_ebus(pdev, &is_rio); - if (!pdev) { - if (ebus == ebus_chain) { - ebus_chain = NULL; - printk("ebus: No EBus's found.\n"); - return; - } - break; - } - ebus->is_rio = is_rio; - dp = pci_device_to_OF_node(pdev); - continue; - } - printk("ebus%d:", num_ebus); - - ebus->index = num_ebus; - ebus->prom_node = dp; - ebus->self = pdev; - - ebus->ofdev.node = dp; - ebus->ofdev.dev.parent = &pdev->dev; - ebus->ofdev.dev.bus = &ebus_bus_type; - dev_set_name(&ebus->ofdev.dev, "ebus%d", num_ebus); - - /* Register with core */ - if (of_device_register(&ebus->ofdev) != 0) - printk(KERN_DEBUG "ebus: device registration error for %s!\n", - dp->path_component_name); - - - child = dp->child; - if (!child) - goto next_ebus; - - ebus->devices = ebus_alloc(sizeof(struct linux_ebus_device)); - - dev = ebus->devices; - dev->next = NULL; - dev->children = NULL; - dev->bus = ebus; - fill_ebus_device(child, dev); - - while ((child = child->sibling) != NULL) { - dev->next = ebus_alloc(sizeof(struct linux_ebus_device)); - - dev = dev->next; - dev->next = NULL; - dev->children = NULL; - dev->bus = ebus; - fill_ebus_device(child, dev); - } - - next_ebus: - printk("\n"); - - pdev = find_next_ebus(pdev, &is_rio); - if (!pdev) - break; - - dp = pci_device_to_OF_node(pdev); - - ebus->next = ebus_alloc(sizeof(struct linux_ebus)); - ebus = ebus->next; - ebus->next = NULL; - ebus->is_rio = is_rio; - ++num_ebus; - } - pci_dev_put(pdev); /* XXX for the case, when ebusnd is 0, is it OK? */ -} diff --git a/arch/sparc64/kernel/entry.h b/arch/sparc64/kernel/entry.h index fc294a29289..34d7ab5e10d 100644 --- a/arch/sparc64/kernel/entry.h +++ b/arch/sparc64/kernel/entry.h @@ -5,8 +5,8 @@ #include <linux/types.h> #include <linux/init.h> -extern char *sparc_cpu_type; -extern char *sparc_fpu_type; +extern const char *sparc_cpu_type; +extern const char *sparc_fpu_type; extern void __init per_cpu_patch(void); extern void __init sun4v_patch(void); @@ -22,7 +22,8 @@ extern void do_notify_resume(struct pt_regs *regs, unsigned long orig_i0, unsigned long thread_info_flags); -extern asmlinkage int syscall_trace(struct pt_regs *regs, int syscall_exit_p); +extern asmlinkage int syscall_trace_enter(struct pt_regs *regs); +extern asmlinkage void syscall_trace_leave(struct pt_regs *regs); extern void bad_trap_tl1(struct pt_regs *regs, long lvl); diff --git a/arch/sparc64/kernel/head.S b/arch/sparc64/kernel/head.S index c9afef093d5..353226fa023 100644 --- a/arch/sparc64/kernel/head.S +++ b/arch/sparc64/kernel/head.S @@ -10,6 +10,7 @@ #include <linux/errno.h> #include <linux/threads.h> #include <linux/init.h> +#include <linux/linkage.h> #include <asm/thread_info.h> #include <asm/asi.h> #include <asm/pstate.h> diff --git a/arch/sparc64/kernel/hvapi.c b/arch/sparc64/kernel/hvapi.c index 691760b5b01..1d272c3b574 100644 --- a/arch/sparc64/kernel/hvapi.c +++ b/arch/sparc64/kernel/hvapi.c @@ -9,7 +9,6 @@ #include <asm/hypervisor.h> #include <asm/oplib.h> -#include <asm/sstate.h> /* If the hypervisor indicates that the API setting * calls are unsupported, by returning HV_EBADTRAP or @@ -184,8 +183,6 @@ void __init sun4v_hvapi_init(void) if (sun4v_hvapi_register(group, major, &minor)) goto bad; - sun4v_sstate_init(); - return; bad: diff --git a/arch/sparc64/kernel/hvcalls.S b/arch/sparc64/kernel/hvcalls.S index a2810f3ac70..e066269d159 100644 --- a/arch/sparc64/kernel/hvcalls.S +++ b/arch/sparc64/kernel/hvcalls.S @@ -3,89 +3,75 @@ * * returns %o0: sysino */ - .globl sun4v_devino_to_sysino - .type sun4v_devino_to_sysino,#function -sun4v_devino_to_sysino: +ENTRY(sun4v_devino_to_sysino) mov HV_FAST_INTR_DEVINO2SYSINO, %o5 ta HV_FAST_TRAP retl mov %o1, %o0 - .size sun4v_devino_to_sysino, .-sun4v_devino_to_sysino +ENDPROC(sun4v_devino_to_sysino) /* %o0: sysino * * returns %o0: intr_enabled (HV_INTR_{DISABLED,ENABLED}) */ - .globl sun4v_intr_getenabled - .type sun4v_intr_getenabled,#function -sun4v_intr_getenabled: +ENTRY(sun4v_intr_getenabled) mov HV_FAST_INTR_GETENABLED, %o5 ta HV_FAST_TRAP retl mov %o1, %o0 - .size sun4v_intr_getenabled, .-sun4v_intr_getenabled +ENDPROC(sun4v_intr_getenabled) /* %o0: sysino * %o1: intr_enabled (HV_INTR_{DISABLED,ENABLED}) */ - .globl sun4v_intr_setenabled - .type sun4v_intr_setenabled,#function -sun4v_intr_setenabled: +ENTRY(sun4v_intr_setenabled) mov HV_FAST_INTR_SETENABLED, %o5 ta HV_FAST_TRAP retl nop - .size sun4v_intr_setenabled, .-sun4v_intr_setenabled +ENDPROC(sun4v_intr_setenabled) /* %o0: sysino * * returns %o0: intr_state (HV_INTR_STATE_*) */ - .globl sun4v_intr_getstate - .type sun4v_intr_getstate,#function -sun4v_intr_getstate: +ENTRY(sun4v_intr_getstate) mov HV_FAST_INTR_GETSTATE, %o5 ta HV_FAST_TRAP retl mov %o1, %o0 - .size sun4v_intr_getstate, .-sun4v_intr_getstate +ENDPROC(sun4v_intr_getstate) /* %o0: sysino * %o1: intr_state (HV_INTR_STATE_*) */ - .globl sun4v_intr_setstate - .type sun4v_intr_setstate,#function -sun4v_intr_setstate: +ENTRY(sun4v_intr_setstate) mov HV_FAST_INTR_SETSTATE, %o5 ta HV_FAST_TRAP retl nop - .size sun4v_intr_setstate, .-sun4v_intr_setstate +ENDPROC(sun4v_intr_setstate) /* %o0: sysino * * returns %o0: cpuid */ - .globl sun4v_intr_gettarget - .type sun4v_intr_gettarget,#function -sun4v_intr_gettarget: +ENTRY(sun4v_intr_gettarget) mov HV_FAST_INTR_GETTARGET, %o5 ta HV_FAST_TRAP retl mov %o1, %o0 - .size sun4v_intr_gettarget, .-sun4v_intr_gettarget +ENDPROC(sun4v_intr_gettarget) /* %o0: sysino * %o1: cpuid */ - .globl sun4v_intr_settarget - .type sun4v_intr_settarget,#function -sun4v_intr_settarget: +ENTRY(sun4v_intr_settarget) mov HV_FAST_INTR_SETTARGET, %o5 ta HV_FAST_TRAP retl nop - .size sun4v_intr_settarget, .-sun4v_intr_settarget +ENDPROC(sun4v_intr_settarget) /* %o0: cpuid * %o1: pc @@ -94,37 +80,31 @@ sun4v_intr_settarget: * * returns %o0: status */ - .globl sun4v_cpu_start - .type sun4v_cpu_start,#function -sun4v_cpu_start: +ENTRY(sun4v_cpu_start) mov HV_FAST_CPU_START, %o5 ta HV_FAST_TRAP retl nop - .size sun4v_cpu_start, .-sun4v_cpu_start +ENDPROC(sun4v_cpu_start) /* %o0: cpuid * * returns %o0: status */ - .globl sun4v_cpu_stop - .type sun4v_cpu_stop,#function -sun4v_cpu_stop: +ENTRY(sun4v_cpu_stop) mov HV_FAST_CPU_STOP, %o5 ta HV_FAST_TRAP retl nop - .size sun4v_cpu_stop, .-sun4v_cpu_stop +ENDPROC(sun4v_cpu_stop) /* returns %o0: status */ - .globl sun4v_cpu_yield - .type sun4v_cpu_yield, #function -sun4v_cpu_yield: +ENTRY(sun4v_cpu_yield) mov HV_FAST_CPU_YIELD, %o5 ta HV_FAST_TRAP retl nop - .size sun4v_cpu_yield, .-sun4v_cpu_yield +ENDPROC(sun4v_cpu_yield) /* %o0: type * %o1: queue paddr @@ -132,14 +112,12 @@ sun4v_cpu_yield: * * returns %o0: status */ - .globl sun4v_cpu_qconf - .type sun4v_cpu_qconf,#function -sun4v_cpu_qconf: +ENTRY(sun4v_cpu_qconf) mov HV_FAST_CPU_QCONF, %o5 ta HV_FAST_TRAP retl nop - .size sun4v_cpu_qconf, .-sun4v_cpu_qconf +ENDPROC(sun4v_cpu_qconf) /* %o0: num cpus in cpu list * %o1: cpu list paddr @@ -147,23 +125,19 @@ sun4v_cpu_qconf: * * returns %o0: status */ - .globl sun4v_cpu_mondo_send - .type sun4v_cpu_mondo_send,#function -sun4v_cpu_mondo_send: +ENTRY(sun4v_cpu_mondo_send) mov HV_FAST_CPU_MONDO_SEND, %o5 ta HV_FAST_TRAP retl nop - .size sun4v_cpu_mondo_send, .-sun4v_cpu_mondo_send +ENDPROC(sun4v_cpu_mondo_send) /* %o0: CPU ID * * returns %o0: -status if status non-zero, else * %o0: cpu state as HV_CPU_STATE_* */ - .globl sun4v_cpu_state - .type sun4v_cpu_state,#function -sun4v_cpu_state: +ENTRY(sun4v_cpu_state) mov HV_FAST_CPU_STATE, %o5 ta HV_FAST_TRAP brnz,pn %o0, 1f @@ -171,7 +145,7 @@ sun4v_cpu_state: mov %o1, %o0 1: retl nop - .size sun4v_cpu_state, .-sun4v_cpu_state +ENDPROC(sun4v_cpu_state) /* %o0: virtual address * %o1: must be zero @@ -180,28 +154,24 @@ sun4v_cpu_state: * * returns %o0: status */ - .globl sun4v_mmu_map_perm_addr - .type sun4v_mmu_map_perm_addr,#function -sun4v_mmu_map_perm_addr: +ENTRY(sun4v_mmu_map_perm_addr) mov HV_FAST_MMU_MAP_PERM_ADDR, %o5 ta HV_FAST_TRAP retl nop - .size sun4v_mmu_map_perm_addr, .-sun4v_mmu_map_perm_addr +ENDPROC(sun4v_mmu_map_perm_addr) /* %o0: number of TSB descriptions * %o1: TSB descriptions real address * * returns %o0: status */ - .globl sun4v_mmu_tsb_ctx0 - .type sun4v_mmu_tsb_ctx0,#function -sun4v_mmu_tsb_ctx0: +ENTRY(sun4v_mmu_tsb_ctx0) mov HV_FAST_MMU_TSB_CTX0, %o5 ta HV_FAST_TRAP retl nop - .size sun4v_mmu_tsb_ctx0, .-sun4v_mmu_tsb_ctx0 +ENDPROC(sun4v_mmu_tsb_ctx0) /* %o0: API group number * %o1: pointer to unsigned long major number storage @@ -209,9 +179,7 @@ sun4v_mmu_tsb_ctx0: * * returns %o0: status */ - .globl sun4v_get_version - .type sun4v_get_version,#function -sun4v_get_version: +ENTRY(sun4v_get_version) mov HV_CORE_GET_VER, %o5 mov %o1, %o3 mov %o2, %o4 @@ -219,7 +187,7 @@ sun4v_get_version: stx %o1, [%o3] retl stx %o2, [%o4] - .size sun4v_get_version, .-sun4v_get_version +ENDPROC(sun4v_get_version) /* %o0: API group number * %o1: desired major number @@ -228,51 +196,43 @@ sun4v_get_version: * * returns %o0: status */ - .globl sun4v_set_version - .type sun4v_set_version,#function -sun4v_set_version: +ENTRY(sun4v_set_version) mov HV_CORE_SET_VER, %o5 mov %o3, %o4 ta HV_CORE_TRAP retl stx %o1, [%o4] - .size sun4v_set_version, .-sun4v_set_version +ENDPROC(sun4v_set_version) /* %o0: pointer to unsigned long time * * returns %o0: status */ - .globl sun4v_tod_get - .type sun4v_tod_get,#function -sun4v_tod_get: +ENTRY(sun4v_tod_get) mov %o0, %o4 mov HV_FAST_TOD_GET, %o5 ta HV_FAST_TRAP stx %o1, [%o4] retl nop - .size sun4v_tod_get, .-sun4v_tod_get +ENDPROC(sun4v_tod_get) /* %o0: time * * returns %o0: status */ - .globl sun4v_tod_set - .type sun4v_tod_set,#function -sun4v_tod_set: +ENTRY(sun4v_tod_set) mov HV_FAST_TOD_SET, %o5 ta HV_FAST_TRAP retl nop - .size sun4v_tod_set, .-sun4v_tod_set +ENDPROC(sun4v_tod_set) /* %o0: pointer to unsigned long status * * returns %o0: signed character */ - .globl sun4v_con_getchar - .type sun4v_con_getchar,#function -sun4v_con_getchar: +ENTRY(sun4v_con_getchar) mov %o0, %o4 mov HV_FAST_CONS_GETCHAR, %o5 clr %o0 @@ -281,20 +241,18 @@ sun4v_con_getchar: stx %o0, [%o4] retl sra %o1, 0, %o0 - .size sun4v_con_getchar, .-sun4v_con_getchar +ENDPROC(sun4v_con_getchar) /* %o0: signed long character * * returns %o0: status */ - .globl sun4v_con_putchar - .type sun4v_con_putchar,#function -sun4v_con_putchar: +ENTRY(sun4v_con_putchar) mov HV_FAST_CONS_PUTCHAR, %o5 ta HV_FAST_TRAP retl sra %o0, 0, %o0 - .size sun4v_con_putchar, .-sun4v_con_putchar +ENDPROC(sun4v_con_putchar) /* %o0: buffer real address * %o1: buffer size @@ -302,9 +260,7 @@ sun4v_con_putchar: * * returns %o0: status */ - .globl sun4v_con_read - .type sun4v_con_read,#function -sun4v_con_read: +ENTRY(sun4v_con_read) mov %o2, %o4 mov HV_FAST_CONS_READ, %o5 ta HV_FAST_TRAP @@ -318,7 +274,7 @@ sun4v_con_read: stx %o1, [%o4] 1: retl nop - .size sun4v_con_read, .-sun4v_con_read +ENDPROC(sun4v_con_read) /* %o0: buffer real address * %o1: buffer size @@ -326,43 +282,37 @@ sun4v_con_read: * * returns %o0: status */ - .globl sun4v_con_write - .type sun4v_con_write,#function -sun4v_con_write: +ENTRY(sun4v_con_write) mov %o2, %o4 mov HV_FAST_CONS_WRITE, %o5 ta HV_FAST_TRAP stx %o1, [%o4] retl nop - .size sun4v_con_write, .-sun4v_con_write +ENDPROC(sun4v_con_write) /* %o0: soft state * %o1: address of description string * * returns %o0: status */ - .globl sun4v_mach_set_soft_state - .type sun4v_mach_set_soft_state,#function -sun4v_mach_set_soft_state: +ENTRY(sun4v_mach_set_soft_state) mov HV_FAST_MACH_SET_SOFT_STATE, %o5 ta HV_FAST_TRAP retl nop - .size sun4v_mach_set_soft_state, .-sun4v_mach_set_soft_state +ENDPROC(sun4v_mach_set_soft_state) /* %o0: exit code * * Does not return. */ - .globl sun4v_mach_exit - .type sun4v_mach_exit,#function -sun4v_mach_exit: +ENTRY(sun4v_mach_exit) mov HV_FAST_MACH_EXIT, %o5 ta HV_FAST_TRAP retl nop - .size sun4v_mach_exit, .-sun4v_mach_exit +ENDPROC(sun4v_mach_exit) /* %o0: buffer real address * %o1: buffer length @@ -370,44 +320,38 @@ sun4v_mach_exit: * * returns %o0: status */ - .globl sun4v_mach_desc - .type sun4v_mach_desc,#function -sun4v_mach_desc: +ENTRY(sun4v_mach_desc) mov %o2, %o4 mov HV_FAST_MACH_DESC, %o5 ta HV_FAST_TRAP stx %o1, [%o4] retl nop - .size sun4v_mach_desc, .-sun4v_mach_desc +ENDPROC(sun4v_mach_desc) /* %o0: new timeout in milliseconds * %o1: pointer to unsigned long orig_timeout * * returns %o0: status */ - .globl sun4v_mach_set_watchdog - .type sun4v_mach_set_watchdog,#function -sun4v_mach_set_watchdog: +ENTRY(sun4v_mach_set_watchdog) mov %o1, %o4 mov HV_FAST_MACH_SET_WATCHDOG, %o5 ta HV_FAST_TRAP stx %o1, [%o4] retl nop - .size sun4v_mach_set_watchdog, .-sun4v_mach_set_watchdog +ENDPROC(sun4v_mach_set_watchdog) /* No inputs and does not return. */ - .globl sun4v_mach_sir - .type sun4v_mach_sir,#function -sun4v_mach_sir: +ENTRY(sun4v_mach_sir) mov %o1, %o4 mov HV_FAST_MACH_SIR, %o5 ta HV_FAST_TRAP stx %o1, [%o4] retl nop - .size sun4v_mach_sir, .-sun4v_mach_sir +ENDPROC(sun4v_mach_sir) /* %o0: channel * %o1: ra @@ -415,14 +359,12 @@ sun4v_mach_sir: * * returns %o0: status */ - .globl sun4v_ldc_tx_qconf - .type sun4v_ldc_tx_qconf,#function -sun4v_ldc_tx_qconf: +ENTRY(sun4v_ldc_tx_qconf) mov HV_FAST_LDC_TX_QCONF, %o5 ta HV_FAST_TRAP retl nop - .size sun4v_ldc_tx_qconf, .-sun4v_ldc_tx_qconf +ENDPROC(sun4v_ldc_tx_qconf) /* %o0: channel * %o1: pointer to unsigned long ra @@ -430,9 +372,7 @@ sun4v_ldc_tx_qconf: * * returns %o0: status */ - .globl sun4v_ldc_tx_qinfo - .type sun4v_ldc_tx_qinfo,#function -sun4v_ldc_tx_qinfo: +ENTRY(sun4v_ldc_tx_qinfo) mov %o1, %g1 mov %o2, %g2 mov HV_FAST_LDC_TX_QINFO, %o5 @@ -441,7 +381,7 @@ sun4v_ldc_tx_qinfo: stx %o2, [%g2] retl nop - .size sun4v_ldc_tx_qinfo, .-sun4v_ldc_tx_qinfo +ENDPROC(sun4v_ldc_tx_qinfo) /* %o0: channel * %o1: pointer to unsigned long head_off @@ -450,9 +390,7 @@ sun4v_ldc_tx_qinfo: * * returns %o0: status */ - .globl sun4v_ldc_tx_get_state - .type sun4v_ldc_tx_get_state,#function -sun4v_ldc_tx_get_state: +ENTRY(sun4v_ldc_tx_get_state) mov %o1, %g1 mov %o2, %g2 mov %o3, %g3 @@ -463,21 +401,19 @@ sun4v_ldc_tx_get_state: stx %o3, [%g3] retl nop - .size sun4v_ldc_tx_get_state, .-sun4v_ldc_tx_get_state +ENDPROC(sun4v_ldc_tx_get_state) /* %o0: channel * %o1: tail_off * * returns %o0: status */ - .globl sun4v_ldc_tx_set_qtail - .type sun4v_ldc_tx_set_qtail,#function -sun4v_ldc_tx_set_qtail: +ENTRY(sun4v_ldc_tx_set_qtail) mov HV_FAST_LDC_TX_SET_QTAIL, %o5 ta HV_FAST_TRAP retl nop - .size sun4v_ldc_tx_set_qtail, .-sun4v_ldc_tx_set_qtail +ENDPROC(sun4v_ldc_tx_set_qtail) /* %o0: channel * %o1: ra @@ -485,14 +421,12 @@ sun4v_ldc_tx_set_qtail: * * returns %o0: status */ - .globl sun4v_ldc_rx_qconf - .type sun4v_ldc_rx_qconf,#function -sun4v_ldc_rx_qconf: +ENTRY(sun4v_ldc_rx_qconf) mov HV_FAST_LDC_RX_QCONF, %o5 ta HV_FAST_TRAP retl nop - .size sun4v_ldc_rx_qconf, .-sun4v_ldc_rx_qconf +ENDPROC(sun4v_ldc_rx_qconf) /* %o0: channel * %o1: pointer to unsigned long ra @@ -500,9 +434,7 @@ sun4v_ldc_rx_qconf: * * returns %o0: status */ - .globl sun4v_ldc_rx_qinfo - .type sun4v_ldc_rx_qinfo,#function -sun4v_ldc_rx_qinfo: +ENTRY(sun4v_ldc_rx_qinfo) mov %o1, %g1 mov %o2, %g2 mov HV_FAST_LDC_RX_QINFO, %o5 @@ -511,7 +443,7 @@ sun4v_ldc_rx_qinfo: stx %o2, [%g2] retl nop - .size sun4v_ldc_rx_qinfo, .-sun4v_ldc_rx_qinfo +ENDPROC(sun4v_ldc_rx_qinfo) /* %o0: channel * %o1: pointer to unsigned long head_off @@ -520,9 +452,7 @@ sun4v_ldc_rx_qinfo: * * returns %o0: status */ - .globl sun4v_ldc_rx_get_state - .type sun4v_ldc_rx_get_state,#function -sun4v_ldc_rx_get_state: +ENTRY(sun4v_ldc_rx_get_state) mov %o1, %g1 mov %o2, %g2 mov %o3, %g3 @@ -533,21 +463,19 @@ sun4v_ldc_rx_get_state: stx %o3, [%g3] retl nop - .size sun4v_ldc_rx_get_state, .-sun4v_ldc_rx_get_state +ENDPROC(sun4v_ldc_rx_get_state) /* %o0: channel * %o1: head_off * * returns %o0: status */ - .globl sun4v_ldc_rx_set_qhead - .type sun4v_ldc_rx_set_qhead,#function -sun4v_ldc_rx_set_qhead: +ENTRY(sun4v_ldc_rx_set_qhead) mov HV_FAST_LDC_RX_SET_QHEAD, %o5 ta HV_FAST_TRAP retl nop - .size sun4v_ldc_rx_set_qhead, .-sun4v_ldc_rx_set_qhead +ENDPROC(sun4v_ldc_rx_set_qhead) /* %o0: channel * %o1: ra @@ -555,14 +483,12 @@ sun4v_ldc_rx_set_qhead: * * returns %o0: status */ - .globl sun4v_ldc_set_map_table - .type sun4v_ldc_set_map_table,#function -sun4v_ldc_set_map_table: +ENTRY(sun4v_ldc_set_map_table) mov HV_FAST_LDC_SET_MAP_TABLE, %o5 ta HV_FAST_TRAP retl nop - .size sun4v_ldc_set_map_table, .-sun4v_ldc_set_map_table +ENDPROC(sun4v_ldc_set_map_table) /* %o0: channel * %o1: pointer to unsigned long ra @@ -570,9 +496,7 @@ sun4v_ldc_set_map_table: * * returns %o0: status */ - .globl sun4v_ldc_get_map_table - .type sun4v_ldc_get_map_table,#function -sun4v_ldc_get_map_table: +ENTRY(sun4v_ldc_get_map_table) mov %o1, %g1 mov %o2, %g2 mov HV_FAST_LDC_GET_MAP_TABLE, %o5 @@ -581,7 +505,7 @@ sun4v_ldc_get_map_table: stx %o2, [%g2] retl nop - .size sun4v_ldc_get_map_table, .-sun4v_ldc_get_map_table +ENDPROC(sun4v_ldc_get_map_table) /* %o0: channel * %o1: dir_code @@ -592,16 +516,14 @@ sun4v_ldc_get_map_table: * * returns %o0: status */ - .globl sun4v_ldc_copy - .type sun4v_ldc_copy,#function -sun4v_ldc_copy: +ENTRY(sun4v_ldc_copy) mov %o5, %g1 mov HV_FAST_LDC_COPY, %o5 ta HV_FAST_TRAP stx %o1, [%g1] retl nop - .size sun4v_ldc_copy, .-sun4v_ldc_copy +ENDPROC(sun4v_ldc_copy) /* %o0: channel * %o1: cookie @@ -610,9 +532,7 @@ sun4v_ldc_copy: * * returns %o0: status */ - .globl sun4v_ldc_mapin - .type sun4v_ldc_mapin,#function -sun4v_ldc_mapin: +ENTRY(sun4v_ldc_mapin) mov %o2, %g1 mov %o3, %g2 mov HV_FAST_LDC_MAPIN, %o5 @@ -621,20 +541,18 @@ sun4v_ldc_mapin: stx %o2, [%g2] retl nop - .size sun4v_ldc_mapin, .-sun4v_ldc_mapin +ENDPROC(sun4v_ldc_mapin) /* %o0: ra * * returns %o0: status */ - .globl sun4v_ldc_unmap - .type sun4v_ldc_unmap,#function -sun4v_ldc_unmap: +ENTRY(sun4v_ldc_unmap) mov HV_FAST_LDC_UNMAP, %o5 ta HV_FAST_TRAP retl nop - .size sun4v_ldc_unmap, .-sun4v_ldc_unmap +ENDPROC(sun4v_ldc_unmap) /* %o0: channel * %o1: cookie @@ -642,14 +560,12 @@ sun4v_ldc_unmap: * * returns %o0: status */ - .globl sun4v_ldc_revoke - .type sun4v_ldc_revoke,#function -sun4v_ldc_revoke: +ENTRY(sun4v_ldc_revoke) mov HV_FAST_LDC_REVOKE, %o5 ta HV_FAST_TRAP retl nop - .size sun4v_ldc_revoke, .-sun4v_ldc_revoke +ENDPROC(sun4v_ldc_revoke) /* %o0: device handle * %o1: device INO @@ -657,16 +573,14 @@ sun4v_ldc_revoke: * * returns %o0: status */ - .globl sun4v_vintr_get_cookie - .type sun4v_vintr_get_cookie,#function -sun4v_vintr_get_cookie: +ENTRY(sun4v_vintr_get_cookie) mov %o2, %g1 mov HV_FAST_VINTR_GET_COOKIE, %o5 ta HV_FAST_TRAP stx %o1, [%g1] retl nop - .size sun4v_vintr_get_cookie, .-sun4v_vintr_get_cookie +ENDPROC(sun4v_vintr_get_cookie) /* %o0: device handle * %o1: device INO @@ -674,14 +588,12 @@ sun4v_vintr_get_cookie: * * returns %o0: status */ - .globl sun4v_vintr_set_cookie - .type sun4v_vintr_set_cookie,#function -sun4v_vintr_set_cookie: +ENTRY(sun4v_vintr_set_cookie) mov HV_FAST_VINTR_SET_COOKIE, %o5 ta HV_FAST_TRAP retl nop - .size sun4v_vintr_set_cookie, .-sun4v_vintr_set_cookie +ENDPROC(sun4v_vintr_set_cookie) /* %o0: device handle * %o1: device INO @@ -689,16 +601,14 @@ sun4v_vintr_set_cookie: * * returns %o0: status */ - .globl sun4v_vintr_get_valid - .type sun4v_vintr_get_valid,#function -sun4v_vintr_get_valid: +ENTRY(sun4v_vintr_get_valid) mov %o2, %g1 mov HV_FAST_VINTR_GET_VALID, %o5 ta HV_FAST_TRAP stx %o1, [%g1] retl nop - .size sun4v_vintr_get_valid, .-sun4v_vintr_get_valid +ENDPROC(sun4v_vintr_get_valid) /* %o0: device handle * %o1: device INO @@ -706,14 +616,12 @@ sun4v_vintr_get_valid: * * returns %o0: status */ - .globl sun4v_vintr_set_valid - .type sun4v_vintr_set_valid,#function -sun4v_vintr_set_valid: +ENTRY(sun4v_vintr_set_valid) mov HV_FAST_VINTR_SET_VALID, %o5 ta HV_FAST_TRAP retl nop - .size sun4v_vintr_set_valid, .-sun4v_vintr_set_valid +ENDPROC(sun4v_vintr_set_valid) /* %o0: device handle * %o1: device INO @@ -721,16 +629,14 @@ sun4v_vintr_set_valid: * * returns %o0: status */ - .globl sun4v_vintr_get_state - .type sun4v_vintr_get_state,#function -sun4v_vintr_get_state: +ENTRY(sun4v_vintr_get_state) mov %o2, %g1 mov HV_FAST_VINTR_GET_STATE, %o5 ta HV_FAST_TRAP stx %o1, [%g1] retl nop - .size sun4v_vintr_get_state, .-sun4v_vintr_get_state +ENDPROC(sun4v_vintr_get_state) /* %o0: device handle * %o1: device INO @@ -738,14 +644,12 @@ sun4v_vintr_get_state: * * returns %o0: status */ - .globl sun4v_vintr_set_state - .type sun4v_vintr_set_state,#function -sun4v_vintr_set_state: +ENTRY(sun4v_vintr_set_state) mov HV_FAST_VINTR_SET_STATE, %o5 ta HV_FAST_TRAP retl nop - .size sun4v_vintr_set_state, .-sun4v_vintr_set_state +ENDPROC(sun4v_vintr_set_state) /* %o0: device handle * %o1: device INO @@ -753,16 +657,14 @@ sun4v_vintr_set_state: * * returns %o0: status */ - .globl sun4v_vintr_get_target - .type sun4v_vintr_get_target,#function -sun4v_vintr_get_target: +ENTRY(sun4v_vintr_get_target) mov %o2, %g1 mov HV_FAST_VINTR_GET_TARGET, %o5 ta HV_FAST_TRAP stx %o1, [%g1] retl nop - .size sun4v_vintr_get_target, .-sun4v_vintr_get_target +ENDPROC(sun4v_vintr_get_target) /* %o0: device handle * %o1: device INO @@ -770,14 +672,12 @@ sun4v_vintr_get_target: * * returns %o0: status */ - .globl sun4v_vintr_set_target - .type sun4v_vintr_set_target,#function -sun4v_vintr_set_target: +ENTRY(sun4v_vintr_set_target) mov HV_FAST_VINTR_SET_TARGET, %o5 ta HV_FAST_TRAP retl nop - .size sun4v_vintr_set_target, .-sun4v_vintr_set_target +ENDPROC(sun4v_vintr_set_target) /* %o0: NCS sub-function * %o1: sub-function arg real-address @@ -785,18 +685,14 @@ sun4v_vintr_set_target: * * returns %o0: status */ - .globl sun4v_ncs_request - .type sun4v_ncs_request,#function -sun4v_ncs_request: +ENTRY(sun4v_ncs_request) mov HV_FAST_NCS_REQUEST, %o5 ta HV_FAST_TRAP retl nop - .size sun4v_ncs_request, .-sun4v_ncs_request +ENDPROC(sun4v_ncs_request) - .globl sun4v_svc_send - .type sun4v_svc_send,#function -sun4v_svc_send: +ENTRY(sun4v_svc_send) save %sp, -192, %sp mov %i0, %o0 mov %i1, %o1 @@ -806,11 +702,9 @@ sun4v_svc_send: stx %o1, [%i3] ret restore - .size sun4v_svc_send, .-sun4v_svc_send +ENDPROC(sun4v_svc_send) - .globl sun4v_svc_recv - .type sun4v_svc_recv,#function -sun4v_svc_recv: +ENTRY(sun4v_svc_recv) save %sp, -192, %sp mov %i0, %o0 mov %i1, %o1 @@ -820,62 +714,50 @@ sun4v_svc_recv: stx %o1, [%i3] ret restore - .size sun4v_svc_recv, .-sun4v_svc_recv +ENDPROC(sun4v_svc_recv) - .globl sun4v_svc_getstatus - .type sun4v_svc_getstatus,#function -sun4v_svc_getstatus: +ENTRY(sun4v_svc_getstatus) mov HV_FAST_SVC_GETSTATUS, %o5 mov %o1, %o4 ta HV_FAST_TRAP stx %o1, [%o4] retl nop - .size sun4v_svc_getstatus, .-sun4v_svc_getstatus +ENDPROC(sun4v_svc_getstatus) - .globl sun4v_svc_setstatus - .type sun4v_svc_setstatus,#function -sun4v_svc_setstatus: +ENTRY(sun4v_svc_setstatus) mov HV_FAST_SVC_SETSTATUS, %o5 ta HV_FAST_TRAP retl nop - .size sun4v_svc_setstatus, .-sun4v_svc_setstatus +ENDPROC(sun4v_svc_setstatus) - .globl sun4v_svc_clrstatus - .type sun4v_svc_clrstatus,#function -sun4v_svc_clrstatus: +ENTRY(sun4v_svc_clrstatus) mov HV_FAST_SVC_CLRSTATUS, %o5 ta HV_FAST_TRAP retl nop - .size sun4v_svc_clrstatus, .-sun4v_svc_clrstatus +ENDPROC(sun4v_svc_clrstatus) - .globl sun4v_mmustat_conf - .type sun4v_mmustat_conf,#function -sun4v_mmustat_conf: +ENTRY(sun4v_mmustat_conf) mov %o1, %o4 mov HV_FAST_MMUSTAT_CONF, %o5 ta HV_FAST_TRAP stx %o1, [%o4] retl nop - .size sun4v_mmustat_conf, .-sun4v_mmustat_conf +ENDPROC(sun4v_mmustat_conf) - .globl sun4v_mmustat_info - .type sun4v_mmustat_info,#function -sun4v_mmustat_info: +ENTRY(sun4v_mmustat_info) mov %o0, %o4 mov HV_FAST_MMUSTAT_INFO, %o5 ta HV_FAST_TRAP stx %o1, [%o4] retl nop - .size sun4v_mmustat_info, .-sun4v_mmustat_info +ENDPROC(sun4v_mmustat_info) - .globl sun4v_mmu_demap_all - .type sun4v_mmu_demap_all,#function -sun4v_mmu_demap_all: +ENTRY(sun4v_mmu_demap_all) clr %o0 clr %o1 mov HV_MMU_ALL, %o2 @@ -883,4 +765,4 @@ sun4v_mmu_demap_all: ta HV_FAST_TRAP retl nop - .size sun4v_mmu_demap_all, .-sun4v_mmu_demap_all +ENDPROC(sun4v_mmu_demap_all) diff --git a/arch/sparc64/kernel/irq.c b/arch/sparc64/kernel/irq.c index 7495bc77468..52fc836f464 100644 --- a/arch/sparc64/kernel/irq.c +++ b/arch/sparc64/kernel/irq.c @@ -29,7 +29,6 @@ #include <asm/system.h> #include <asm/irq.h> #include <asm/io.h> -#include <asm/sbus.h> #include <asm/iommu.h> #include <asm/upa.h> #include <asm/oplib.h> diff --git a/arch/sparc64/kernel/of_device.c b/arch/sparc64/kernel/of_device.c index 100ebd52749..0f616ae3246 100644 --- a/arch/sparc64/kernel/of_device.c +++ b/arch/sparc64/kernel/of_device.c @@ -55,15 +55,38 @@ struct of_device *of_find_device_by_node(struct device_node *dp) } EXPORT_SYMBOL(of_find_device_by_node); -#ifdef CONFIG_PCI -struct bus_type ebus_bus_type; -EXPORT_SYMBOL(ebus_bus_type); -#endif +unsigned int irq_of_parse_and_map(struct device_node *node, int index) +{ + struct of_device *op = of_find_device_by_node(node); + + if (!op || index >= op->num_irqs) + return 0; + + return op->irqs[index]; +} +EXPORT_SYMBOL(irq_of_parse_and_map); + +/* Take the archdata values for IOMMU, STC, and HOSTDATA found in + * BUS and propagate to all child of_device objects. + */ +void of_propagate_archdata(struct of_device *bus) +{ + struct dev_archdata *bus_sd = &bus->dev.archdata; + struct device_node *bus_dp = bus->node; + struct device_node *dp; -#ifdef CONFIG_SBUS -struct bus_type sbus_bus_type; -EXPORT_SYMBOL(sbus_bus_type); -#endif + for (dp = bus_dp->child; dp; dp = dp->sibling) { + struct of_device *op = of_find_device_by_node(dp); + + op->dev.archdata.iommu = bus_sd->iommu; + op->dev.archdata.stc = bus_sd->stc; + op->dev.archdata.host_controller = bus_sd->host_controller; + op->dev.archdata.numa_node = bus_sd->numa_node; + + if (dp->child) + of_propagate_archdata(op); + } +} struct bus_type of_platform_bus_type; EXPORT_SYMBOL(of_platform_bus_type); @@ -378,8 +401,7 @@ static int __init build_one_resource(struct device_node *parent, int na, int ns, int pna) { const u32 *ranges; - unsigned int rlen; - int rone; + int rone, rlen; ranges = of_get_property(parent, "ranges", &rlen); if (ranges == NULL || rlen == 0) { @@ -421,8 +443,17 @@ static int __init use_1to1_mapping(struct device_node *pp) /* If the parent is the dma node of an ISA bus, pass * the translation up to the root. + * + * Some SBUS devices use intermediate nodes to express + * hierarchy within the device itself. These aren't + * real bus nodes, and don't have a 'ranges' property. + * But, we should still pass the translation work up + * to the SBUS itself. */ - if (!strcmp(pp->name, "dma")) + if (!strcmp(pp->name, "dma") || + !strcmp(pp->name, "espdma") || + !strcmp(pp->name, "ledma") || + !strcmp(pp->name, "lebuffer")) return 0; /* Similarly for all PCI bridges, if we get this far @@ -844,15 +875,6 @@ static int __init of_bus_driver_init(void) int err; err = of_bus_type_init(&of_platform_bus_type, "of"); -#ifdef CONFIG_PCI - if (!err) - err = of_bus_type_init(&ebus_bus_type, "ebus"); -#endif -#ifdef CONFIG_SBUS - if (!err) - err = of_bus_type_init(&sbus_bus_type, "sbus"); -#endif - if (!err) scan_of_devices(); diff --git a/arch/sparc64/kernel/pci.c b/arch/sparc64/kernel/pci.c index 80dad76f8b8..242ac1ccae7 100644 --- a/arch/sparc64/kernel/pci.c +++ b/arch/sparc64/kernel/pci.c @@ -18,32 +18,17 @@ #include <linux/msi.h> #include <linux/irq.h> #include <linux/init.h> +#include <linux/of.h> +#include <linux/of_device.h> #include <asm/uaccess.h> #include <asm/pgtable.h> #include <asm/irq.h> -#include <asm/ebus.h> #include <asm/prom.h> #include <asm/apb.h> #include "pci_impl.h" -#ifndef CONFIG_PCI -/* A "nop" PCI implementation. */ -asmlinkage int sys_pciconfig_read(unsigned long bus, unsigned long dfn, - unsigned long off, unsigned long len, - unsigned char *buf) -{ - return 0; -} -asmlinkage int sys_pciconfig_write(unsigned long bus, unsigned long dfn, - unsigned long off, unsigned long len, - unsigned char *buf) -{ - return 0; -} -#else - /* List of all PCI controllers found in the system. */ struct pci_pbm_info *pci_pbm_root = NULL; @@ -179,97 +164,6 @@ void pci_config_write32(u32 *addr, u32 val) spin_unlock_irqrestore(&pci_poke_lock, flags); } -/* Probe for all PCI controllers in the system. */ -extern void sabre_init(struct device_node *, const char *); -extern void psycho_init(struct device_node *, const char *); -extern void schizo_init(struct device_node *, const char *); -extern void schizo_plus_init(struct device_node *, const char *); -extern void tomatillo_init(struct device_node *, const char *); -extern void sun4v_pci_init(struct device_node *, const char *); -extern void fire_pci_init(struct device_node *, const char *); - -static struct { - char *model_name; - void (*init)(struct device_node *, const char *); -} pci_controller_table[] __initdata = { - { "SUNW,sabre", sabre_init }, - { "pci108e,a000", sabre_init }, - { "pci108e,a001", sabre_init }, - { "SUNW,psycho", psycho_init }, - { "pci108e,8000", psycho_init }, - { "SUNW,schizo", schizo_init }, - { "pci108e,8001", schizo_init }, - { "SUNW,schizo+", schizo_plus_init }, - { "pci108e,8002", schizo_plus_init }, - { "SUNW,tomatillo", tomatillo_init }, - { "pci108e,a801", tomatillo_init }, - { "SUNW,sun4v-pci", sun4v_pci_init }, - { "pciex108e,80f0", fire_pci_init }, -}; -#define PCI_NUM_CONTROLLER_TYPES ARRAY_SIZE(pci_controller_table) - -static int __init pci_controller_init(const char *model_name, int namelen, struct device_node *dp) -{ - int i; - - for (i = 0; i < PCI_NUM_CONTROLLER_TYPES; i++) { - if (!strncmp(model_name, - pci_controller_table[i].model_name, - namelen)) { - pci_controller_table[i].init(dp, model_name); - return 1; - } - } - - return 0; -} - -static int __init pci_controller_scan(int (*handler)(const char *, int, struct device_node *)) -{ - struct device_node *dp; - int count = 0; - - for_each_node_by_name(dp, "pci") { - struct property *prop; - int len; - - prop = of_find_property(dp, "model", &len); - if (!prop) - prop = of_find_property(dp, "compatible", &len); - - if (prop) { - const char *model = prop->value; - int item_len = 0; - - /* Our value may be a multi-valued string in the - * case of some compatible properties. For sanity, - * only try the first one. - */ - while (model[item_len] && len) { - len--; - item_len++; - } - - if (handler(model, item_len, dp)) - count++; - } - } - - return count; -} - -/* Find each controller in the system, attach and initialize - * software state structure for each and link into the - * pci_pbm_root. Setup the controller enough such - * that bus scanning can be done. - */ -static void __init pci_controller_probe(void) -{ - printk("PCI: Probing for controllers.\n"); - - pci_controller_scan(pci_controller_init); -} - static int ofpci_verbose; static int __init ofpci_debug(char *str) @@ -348,11 +242,12 @@ static void pci_parse_of_addrs(struct of_device *op, } } -struct pci_dev *of_create_pci_dev(struct pci_pbm_info *pbm, - struct device_node *node, - struct pci_bus *bus, int devfn) +static struct pci_dev *of_create_pci_dev(struct pci_pbm_info *pbm, + struct device_node *node, + struct pci_bus *bus, int devfn) { struct dev_archdata *sd; + struct of_device *op; struct pci_dev *dev; const char *type; u32 class; @@ -366,14 +261,17 @@ struct pci_dev *of_create_pci_dev(struct pci_pbm_info *pbm, sd->stc = &pbm->stc; sd->host_controller = pbm; sd->prom_node = node; - sd->op = of_find_device_by_node(node); + sd->op = op = of_find_device_by_node(node); sd->numa_node = pbm->numa_node; - sd = &sd->op->dev.archdata; + sd = &op->dev.archdata; sd->iommu = pbm->iommu; sd->stc = &pbm->stc; sd->numa_node = pbm->numa_node; + if (!strcmp(node->name, "ebus")) + of_propagate_archdata(op); + type = of_get_property(node, "device_type", NULL); if (type == NULL) type = ""; @@ -775,15 +673,15 @@ static void __devinit pci_bus_register_of_sysfs(struct pci_bus *bus) pci_bus_register_of_sysfs(child_bus); } -struct pci_bus * __devinit pci_scan_one_pbm(struct pci_pbm_info *pbm) +struct pci_bus * __devinit pci_scan_one_pbm(struct pci_pbm_info *pbm, + struct device *parent) { - struct device_node *node = pbm->prom_node; + struct device_node *node = pbm->op->node; struct pci_bus *bus; printk("PCI: Scanning PBM %s\n", node->full_name); - /* XXX parent device? XXX */ - bus = pci_create_bus(NULL, pbm->pci_first_busno, pbm->pci_ops, pbm); + bus = pci_create_bus(parent, pbm->pci_first_busno, pbm->pci_ops, pbm); if (!bus) { printk(KERN_ERR "Failed to create bus for %s\n", node->full_name); @@ -802,32 +700,6 @@ struct pci_bus * __devinit pci_scan_one_pbm(struct pci_pbm_info *pbm) return bus; } -static void __init pci_scan_each_controller_bus(void) -{ - struct pci_pbm_info *pbm; - - for (pbm = pci_pbm_root; pbm; pbm = pbm->next) - pbm->scan_bus(pbm); -} - -extern void power_init(void); - -static int __init pcibios_init(void) -{ - pci_controller_probe(); - if (pci_pbm_root == NULL) - return 0; - - pci_scan_each_controller_bus(); - - ebus_init(); - power_init(); - - return 0; -} - -subsys_initcall(pcibios_init); - void __devinit pcibios_fixup_bus(struct pci_bus *pbus) { struct pci_pbm_info *pbm = pbus->sysdata; @@ -1105,14 +977,14 @@ int pcibus_to_node(struct pci_bus *pbus) EXPORT_SYMBOL(pcibus_to_node); #endif -/* Return the domain nuber for this pci bus */ +/* Return the domain number for this pci bus */ int pci_domain_nr(struct pci_bus *pbus) { struct pci_pbm_info *pbm = pbus->sysdata; int ret; - if (pbm == NULL || pbm->parent == NULL) { + if (!pbm) { ret = -ENXIO; } else { ret = pbm->index; @@ -1126,7 +998,7 @@ EXPORT_SYMBOL(pci_domain_nr); int arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc) { struct pci_pbm_info *pbm = pdev->dev.archdata.host_controller; - int virt_irq; + unsigned int virt_irq; if (!pbm->setup_msi_irq) return -EINVAL; @@ -1140,10 +1012,8 @@ void arch_teardown_msi_irq(unsigned int virt_irq) struct pci_dev *pdev = entry->dev; struct pci_pbm_info *pbm = pdev->dev.archdata.host_controller; - if (!pbm->teardown_msi_irq) - return; - - return pbm->teardown_msi_irq(virt_irq, pdev); + if (pbm->teardown_msi_irq) + pbm->teardown_msi_irq(virt_irq, pdev); } #endif /* !(CONFIG_PCI_MSI) */ @@ -1215,5 +1085,3 @@ void pci_resource_to_user(const struct pci_dev *pdev, int bar, *start = rp->start - offset; *end = rp->end - offset; } - -#endif /* !(CONFIG_PCI) */ diff --git a/arch/sparc64/kernel/pci_common.c b/arch/sparc64/kernel/pci_common.c index 09a5ec200c6..23b88082d0b 100644 --- a/arch/sparc64/kernel/pci_common.c +++ b/arch/sparc64/kernel/pci_common.c @@ -314,12 +314,12 @@ struct pci_ops sun4v_pci_ops = { void pci_get_pbm_props(struct pci_pbm_info *pbm) { - const u32 *val = of_get_property(pbm->prom_node, "bus-range", NULL); + const u32 *val = of_get_property(pbm->op->node, "bus-range", NULL); pbm->pci_first_busno = val[0]; pbm->pci_last_busno = val[1]; - val = of_get_property(pbm->prom_node, "ino-bitmap", NULL); + val = of_get_property(pbm->op->node, "ino-bitmap", NULL); if (val) { pbm->ino_bitmap = (((u64)val[1] << 32UL) | ((u64)val[0] << 0UL)); @@ -365,7 +365,7 @@ static void pci_register_legacy_regions(struct resource *io_res, static void pci_register_iommu_region(struct pci_pbm_info *pbm) { - const u32 *vdma = of_get_property(pbm->prom_node, "virtual-dma", NULL); + const u32 *vdma = of_get_property(pbm->op->node, "virtual-dma", NULL); if (vdma) { struct resource *rp = kmalloc(sizeof(*rp), GFP_KERNEL); @@ -389,7 +389,7 @@ void pci_determine_mem_io_space(struct pci_pbm_info *pbm) int num_pbm_ranges; saw_mem = saw_io = 0; - pbm_ranges = of_get_property(pbm->prom_node, "ranges", &i); + pbm_ranges = of_get_property(pbm->op->node, "ranges", &i); if (!pbm_ranges) { prom_printf("PCI: Fatal error, missing PBM ranges property " " for %s\n", diff --git a/arch/sparc64/kernel/pci_fire.c b/arch/sparc64/kernel/pci_fire.c index d23bb6f53cd..9462b68f489 100644 --- a/arch/sparc64/kernel/pci_fire.c +++ b/arch/sparc64/kernel/pci_fire.c @@ -8,34 +8,16 @@ #include <linux/init.h> #include <linux/msi.h> #include <linux/irq.h> +#include <linux/of_device.h> -#include <asm/oplib.h> #include <asm/prom.h> #include <asm/irq.h> +#include <asm/upa.h> #include "pci_impl.h" -#define fire_read(__reg) \ -({ u64 __ret; \ - __asm__ __volatile__("ldxa [%1] %2, %0" \ - : "=r" (__ret) \ - : "r" (__reg), "i" (ASI_PHYS_BYPASS_EC_E) \ - : "memory"); \ - __ret; \ -}) -#define fire_write(__reg, __val) \ - __asm__ __volatile__("stxa %0, [%1] %2" \ - : /* no outputs */ \ - : "r" (__val), "r" (__reg), \ - "i" (ASI_PHYS_BYPASS_EC_E) \ - : "memory") - -static void __init pci_fire_scan_bus(struct pci_pbm_info *pbm) -{ - pbm->pci_bus = pci_scan_one_pbm(pbm); - - /* XXX register error interrupt handlers XXX */ -} +#define DRIVER_NAME "fire" +#define PFX DRIVER_NAME ": " #define FIRE_IOMMU_CONTROL 0x40000UL #define FIRE_IOMMU_TSBBASE 0x40008UL @@ -69,21 +51,21 @@ static int pci_fire_pbm_iommu_init(struct pci_pbm_info *pbm) /* * Invalidate TLB Entries. */ - fire_write(iommu->iommu_flushinv, ~(u64)0); + upa_writeq(~(u64)0, iommu->iommu_flushinv); err = iommu_table_init(iommu, tsbsize * 8 * 1024, vdma[0], dma_mask, pbm->numa_node); if (err) return err; - fire_write(iommu->iommu_tsbbase, __pa(iommu->page_table) | 0x7UL); + upa_writeq(__pa(iommu->page_table) | 0x7UL, iommu->iommu_tsbbase); - control = fire_read(iommu->iommu_control); + control = upa_readq(iommu->iommu_control); control |= (0x00000400 /* TSB cache snoop enable */ | 0x00000300 /* Cache mode */ | 0x00000002 /* Bypass enable */ | 0x00000001 /* Translation enable */); - fire_write(iommu->iommu_control, control); + upa_writeq(control, iommu->iommu_control); return 0; } @@ -165,7 +147,7 @@ struct pci_msiq_entry { static int pci_fire_get_head(struct pci_pbm_info *pbm, unsigned long msiqid, unsigned long *head) { - *head = fire_read(pbm->pbm_regs + EVENT_QUEUE_HEAD(msiqid)); + *head = upa_readq(pbm->pbm_regs + EVENT_QUEUE_HEAD(msiqid)); return 0; } @@ -191,8 +173,7 @@ static int pci_fire_dequeue_msi(struct pci_pbm_info *pbm, unsigned long msiqid, *msi = msi_num = ((ep->word0 & MSIQ_WORD0_DATA0) >> MSIQ_WORD0_DATA0_SHIFT); - fire_write(pbm->pbm_regs + MSI_CLEAR(msi_num), - MSI_CLEAR_EQWR_N); + upa_writeq(MSI_CLEAR_EQWR_N, pbm->pbm_regs + MSI_CLEAR(msi_num)); /* Clear the entry. */ ep->word0 &= ~MSIQ_WORD0_FMT_TYPE; @@ -208,7 +189,7 @@ static int pci_fire_dequeue_msi(struct pci_pbm_info *pbm, unsigned long msiqid, static int pci_fire_set_head(struct pci_pbm_info *pbm, unsigned long msiqid, unsigned long head) { - fire_write(pbm->pbm_regs + EVENT_QUEUE_HEAD(msiqid), head); + upa_writeq(head, pbm->pbm_regs + EVENT_QUEUE_HEAD(msiqid)); return 0; } @@ -217,17 +198,16 @@ static int pci_fire_msi_setup(struct pci_pbm_info *pbm, unsigned long msiqid, { u64 val; - val = fire_read(pbm->pbm_regs + MSI_MAP(msi)); + val = upa_readq(pbm->pbm_regs + MSI_MAP(msi)); val &= ~(MSI_MAP_EQNUM); val |= msiqid; - fire_write(pbm->pbm_regs + MSI_MAP(msi), val); + upa_writeq(val, pbm->pbm_regs + MSI_MAP(msi)); - fire_write(pbm->pbm_regs + MSI_CLEAR(msi), - MSI_CLEAR_EQWR_N); + upa_writeq(MSI_CLEAR_EQWR_N, pbm->pbm_regs + MSI_CLEAR(msi)); - val = fire_read(pbm->pbm_regs + MSI_MAP(msi)); + val = upa_readq(pbm->pbm_regs + MSI_MAP(msi)); val |= MSI_MAP_VALID; - fire_write(pbm->pbm_regs + MSI_MAP(msi), val); + upa_writeq(val, pbm->pbm_regs + MSI_MAP(msi)); return 0; } @@ -237,12 +217,12 @@ static int pci_fire_msi_teardown(struct pci_pbm_info *pbm, unsigned long msi) unsigned long msiqid; u64 val; - val = fire_read(pbm->pbm_regs + MSI_MAP(msi)); + val = upa_readq(pbm->pbm_regs + MSI_MAP(msi)); msiqid = (val & MSI_MAP_EQNUM); val &= ~MSI_MAP_VALID; - fire_write(pbm->pbm_regs + MSI_MAP(msi), val); + upa_writeq(val, pbm->pbm_regs + MSI_MAP(msi)); return 0; } @@ -261,22 +241,19 @@ static int pci_fire_msiq_alloc(struct pci_pbm_info *pbm) memset((char *)pages, 0, PAGE_SIZE << order); pbm->msi_queues = (void *) pages; - fire_write(pbm->pbm_regs + EVENT_QUEUE_BASE_ADDR_REG, - (EVENT_QUEUE_BASE_ADDR_ALL_ONES | - __pa(pbm->msi_queues))); + upa_writeq((EVENT_QUEUE_BASE_ADDR_ALL_ONES | + __pa(pbm->msi_queues)), + pbm->pbm_regs + EVENT_QUEUE_BASE_ADDR_REG); - fire_write(pbm->pbm_regs + IMONDO_DATA0, - pbm->portid << 6); - fire_write(pbm->pbm_regs + IMONDO_DATA1, 0); + upa_writeq(pbm->portid << 6, pbm->pbm_regs + IMONDO_DATA0); + upa_writeq(0, pbm->pbm_regs + IMONDO_DATA1); - fire_write(pbm->pbm_regs + MSI_32BIT_ADDR, - pbm->msi32_start); - fire_write(pbm->pbm_regs + MSI_64BIT_ADDR, - pbm->msi64_start); + upa_writeq(pbm->msi32_start, pbm->pbm_regs + MSI_32BIT_ADDR); + upa_writeq(pbm->msi64_start, pbm->pbm_regs + MSI_64BIT_ADDR); for (i = 0; i < pbm->msiq_num; i++) { - fire_write(pbm->pbm_regs + EVENT_QUEUE_HEAD(i), 0); - fire_write(pbm->pbm_regs + EVENT_QUEUE_TAIL(i), 0); + upa_writeq(0, pbm->pbm_regs + EVENT_QUEUE_HEAD(i)); + upa_writeq(0, pbm->pbm_regs + EVENT_QUEUE_TAIL(i)); } return 0; @@ -310,9 +287,9 @@ static int pci_fire_msiq_build_irq(struct pci_pbm_info *pbm, /* XXX iterate amongst the 4 IRQ controllers XXX */ int_ctrlr = (1UL << 6); - val = fire_read(imap_reg); + val = upa_readq(imap_reg); val |= (1UL << 63) | int_ctrlr; - fire_write(imap_reg, val); + upa_writeq(val, imap_reg); fixup = ((pbm->portid << 6) | devino) - int_ctrlr; @@ -320,9 +297,8 @@ static int pci_fire_msiq_build_irq(struct pci_pbm_info *pbm, if (!virt_irq) return -ENOMEM; - fire_write(pbm->pbm_regs + - EVENT_QUEUE_CONTROL_SET(msiqid), - EVENT_QUEUE_CONTROL_SET_EN); + upa_writeq(EVENT_QUEUE_CONTROL_SET_EN, + pbm->pbm_regs + EVENT_QUEUE_CONTROL_SET(msiqid)); return virt_irq; } @@ -390,77 +366,65 @@ static void pci_fire_hw_init(struct pci_pbm_info *pbm) { u64 val; - fire_write(pbm->controller_regs + FIRE_PARITY_CONTROL, - FIRE_PARITY_ENAB); + upa_writeq(FIRE_PARITY_ENAB, + pbm->controller_regs + FIRE_PARITY_CONTROL); - fire_write(pbm->controller_regs + FIRE_FATAL_RESET_CTL, - (FIRE_FATAL_RESET_SPARE | + upa_writeq((FIRE_FATAL_RESET_SPARE | FIRE_FATAL_RESET_MB | FIRE_FATAL_RESET_CPE | FIRE_FATAL_RESET_APE | FIRE_FATAL_RESET_PIO | FIRE_FATAL_RESET_JW | FIRE_FATAL_RESET_JI | - FIRE_FATAL_RESET_JR)); + FIRE_FATAL_RESET_JR), + pbm->controller_regs + FIRE_FATAL_RESET_CTL); - fire_write(pbm->controller_regs + FIRE_CORE_INTR_ENABLE, ~(u64)0); + upa_writeq(~(u64)0, pbm->controller_regs + FIRE_CORE_INTR_ENABLE); - val = fire_read(pbm->pbm_regs + FIRE_TLU_CTRL); + val = upa_readq(pbm->pbm_regs + FIRE_TLU_CTRL); val |= (FIRE_TLU_CTRL_TIM | FIRE_TLU_CTRL_QDET | FIRE_TLU_CTRL_CFG); - fire_write(pbm->pbm_regs + FIRE_TLU_CTRL, val); - fire_write(pbm->pbm_regs + FIRE_TLU_DEV_CTRL, 0); - fire_write(pbm->pbm_regs + FIRE_TLU_LINK_CTRL, - FIRE_TLU_LINK_CTRL_CLK); - - fire_write(pbm->pbm_regs + FIRE_LPU_RESET, 0); - fire_write(pbm->pbm_regs + FIRE_LPU_LLCFG, - FIRE_LPU_LLCFG_VC0); - fire_write(pbm->pbm_regs + FIRE_LPU_FCTRL_UCTRL, - (FIRE_LPU_FCTRL_UCTRL_N | - FIRE_LPU_FCTRL_UCTRL_P)); - fire_write(pbm->pbm_regs + FIRE_LPU_TXL_FIFOP, - ((0xffff << 16) | (0x0000 << 0))); - fire_write(pbm->pbm_regs + FIRE_LPU_LTSSM_CFG2, 3000000); - fire_write(pbm->pbm_regs + FIRE_LPU_LTSSM_CFG3, 500000); - fire_write(pbm->pbm_regs + FIRE_LPU_LTSSM_CFG4, - (2 << 16) | (140 << 8)); - fire_write(pbm->pbm_regs + FIRE_LPU_LTSSM_CFG5, 0); - - fire_write(pbm->pbm_regs + FIRE_DMC_IENAB, ~(u64)0); - fire_write(pbm->pbm_regs + FIRE_DMC_DBG_SEL_A, 0); - fire_write(pbm->pbm_regs + FIRE_DMC_DBG_SEL_B, 0); - - fire_write(pbm->pbm_regs + FIRE_PEC_IENAB, ~(u64)0); + upa_writeq(val, pbm->pbm_regs + FIRE_TLU_CTRL); + upa_writeq(0, pbm->pbm_regs + FIRE_TLU_DEV_CTRL); + upa_writeq(FIRE_TLU_LINK_CTRL_CLK, + pbm->pbm_regs + FIRE_TLU_LINK_CTRL); + + upa_writeq(0, pbm->pbm_regs + FIRE_LPU_RESET); + upa_writeq(FIRE_LPU_LLCFG_VC0, pbm->pbm_regs + FIRE_LPU_LLCFG); + upa_writeq((FIRE_LPU_FCTRL_UCTRL_N | FIRE_LPU_FCTRL_UCTRL_P), + pbm->pbm_regs + FIRE_LPU_FCTRL_UCTRL); + upa_writeq(((0xffff << 16) | (0x0000 << 0)), + pbm->pbm_regs + FIRE_LPU_TXL_FIFOP); + upa_writeq(3000000, pbm->pbm_regs + FIRE_LPU_LTSSM_CFG2); + upa_writeq(500000, pbm->pbm_regs + FIRE_LPU_LTSSM_CFG3); + upa_writeq((2 << 16) | (140 << 8), + pbm->pbm_regs + FIRE_LPU_LTSSM_CFG4); + upa_writeq(0, pbm->pbm_regs + FIRE_LPU_LTSSM_CFG5); + + upa_writeq(~(u64)0, pbm->pbm_regs + FIRE_DMC_IENAB); + upa_writeq(0, pbm->pbm_regs + FIRE_DMC_DBG_SEL_A); + upa_writeq(0, pbm->pbm_regs + FIRE_DMC_DBG_SEL_B); + + upa_writeq(~(u64)0, pbm->pbm_regs + FIRE_PEC_IENAB); } -static int __init pci_fire_pbm_init(struct pci_controller_info *p, - struct device_node *dp, u32 portid) +static int __init pci_fire_pbm_init(struct pci_pbm_info *pbm, + struct of_device *op, u32 portid) { const struct linux_prom64_registers *regs; - struct pci_pbm_info *pbm; + struct device_node *dp = op->node; int err; - if ((portid & 1) == 0) - pbm = &p->pbm_A; - else - pbm = &p->pbm_B; - - pbm->next = pci_pbm_root; - pci_pbm_root = pbm; - pbm->numa_node = -1; - pbm->scan_bus = pci_fire_scan_bus; pbm->pci_ops = &sun4u_pci_ops; pbm->config_space_reg_bits = 12; pbm->index = pci_num_pbms++; pbm->portid = portid; - pbm->parent = p; - pbm->prom_node = dp; + pbm->op = op; pbm->name = dp->full_name; regs = of_get_property(dp, "reg", NULL); @@ -481,53 +445,77 @@ static int __init pci_fire_pbm_init(struct pci_controller_info *p, pci_fire_msi_init(pbm); - return 0; -} + pbm->pci_bus = pci_scan_one_pbm(pbm, &op->dev); + + /* XXX register error interrupt handlers XXX */ + + pbm->next = pci_pbm_root; + pci_pbm_root = pbm; -static inline int portid_compare(u32 x, u32 y) -{ - if (x == (y ^ 1)) - return 1; return 0; } -void __init fire_pci_init(struct device_node *dp, const char *model_name) +static int __devinit fire_probe(struct of_device *op, + const struct of_device_id *match) { - struct pci_controller_info *p; - u32 portid = of_getintprop_default(dp, "portid", 0xff); - struct iommu *iommu; + struct device_node *dp = op->node; struct pci_pbm_info *pbm; + struct iommu *iommu; + u32 portid; + int err; - for (pbm = pci_pbm_root; pbm; pbm = pbm->next) { - if (portid_compare(pbm->portid, portid)) { - if (pci_fire_pbm_init(pbm->parent, dp, portid)) - goto fatal_memory_error; - return; - } + portid = of_getintprop_default(dp, "portid", 0xff); + + err = -ENOMEM; + pbm = kzalloc(sizeof(*pbm), GFP_KERNEL); + if (!pbm) { + printk(KERN_ERR PFX "Cannot allocate pci_pbminfo.\n"); + goto out_err; } - p = kzalloc(sizeof(struct pci_controller_info), GFP_ATOMIC); - if (!p) - goto fatal_memory_error; + iommu = kzalloc(sizeof(struct iommu), GFP_KERNEL); + if (!iommu) { + printk(KERN_ERR PFX "Cannot allocate PBM iommu.\n"); + goto out_free_controller; + } - iommu = kzalloc(sizeof(struct iommu), GFP_ATOMIC); - if (!iommu) - goto fatal_memory_error; + pbm->iommu = iommu; - p->pbm_A.iommu = iommu; + err = pci_fire_pbm_init(pbm, op, portid); + if (err) + goto out_free_iommu; - iommu = kzalloc(sizeof(struct iommu), GFP_ATOMIC); - if (!iommu) - goto fatal_memory_error; + dev_set_drvdata(&op->dev, pbm); - p->pbm_B.iommu = iommu; + return 0; - if (pci_fire_pbm_init(p, dp, portid)) - goto fatal_memory_error; +out_free_iommu: + kfree(pbm->iommu); + +out_free_controller: + kfree(pbm); - return; +out_err: + return err; +} + +static struct of_device_id __initdata fire_match[] = { + { + .name = "pci", + .compatible = "pciex108e,80f0", + }, + {}, +}; -fatal_memory_error: - prom_printf("PCI_FIRE: Fatal memory allocation error.\n"); - prom_halt(); +static struct of_platform_driver fire_driver = { + .name = DRIVER_NAME, + .match_table = fire_match, + .probe = fire_probe, +}; + +static int __init fire_init(void) +{ + return of_register_driver(&fire_driver, &of_bus_type); } + +subsys_initcall(fire_init); diff --git a/arch/sparc64/kernel/pci_impl.h b/arch/sparc64/kernel/pci_impl.h index c385d126be1..03186824327 100644 --- a/arch/sparc64/kernel/pci_impl.h +++ b/arch/sparc64/kernel/pci_impl.h @@ -10,6 +10,7 @@ #include <linux/spinlock.h> #include <linux/pci.h> #include <linux/msi.h> +#include <linux/of_device.h> #include <asm/io.h> #include <asm/prom.h> #include <asm/iommu.h> @@ -56,15 +57,11 @@ struct sparc64_msiq_cookie { }; #endif -struct pci_controller_info; - struct pci_pbm_info { struct pci_pbm_info *next; + struct pci_pbm_info *sibling; int index; - /* PCI controller we sit under. */ - struct pci_controller_info *parent; - /* Physical address base of controller registers. */ unsigned long controller_regs; @@ -94,7 +91,7 @@ struct pci_pbm_info { char *name; /* OBP specific information. */ - struct device_node *prom_node; + struct of_device *op; u64 ino_bitmap; /* PBM I/O and Memory space resources. */ @@ -107,6 +104,10 @@ struct pci_pbm_info { /* This will be 12 on PCI-E controllers, 8 elsewhere. */ unsigned long config_space_reg_bits; + unsigned long pci_afsr; + unsigned long pci_afar; + unsigned long pci_csr; + /* State of 66MHz capabilities on this PBM. */ int is_66mhz_capable; int all_devs_66mhz; @@ -146,25 +147,19 @@ struct pci_pbm_info { unsigned int pci_first_busno; unsigned int pci_last_busno; struct pci_bus *pci_bus; - void (*scan_bus)(struct pci_pbm_info *); struct pci_ops *pci_ops; int numa_node; }; -struct pci_controller_info { - /* The PCI bus modules controlled by us. */ - struct pci_pbm_info pbm_A; - struct pci_pbm_info pbm_B; -}; - extern struct pci_pbm_info *pci_pbm_root; extern int pci_num_pbms; /* PCI bus scanning and fixup support. */ extern void pci_get_pbm_props(struct pci_pbm_info *pbm); -extern struct pci_bus *pci_scan_one_pbm(struct pci_pbm_info *pbm); +extern struct pci_bus *pci_scan_one_pbm(struct pci_pbm_info *pbm, + struct device *parent); extern void pci_determine_mem_io_space(struct pci_pbm_info *pbm); /* Error reporting support. */ @@ -183,4 +178,8 @@ extern void pci_config_write32(u32 *addr, u32 val); extern struct pci_ops sun4u_pci_ops; extern struct pci_ops sun4v_pci_ops; +extern volatile int pci_poke_in_progress; +extern volatile int pci_poke_cpu; +extern volatile int pci_poke_faulted; + #endif /* !(PCI_IMPL_H) */ diff --git a/arch/sparc64/kernel/pci_msi.c b/arch/sparc64/kernel/pci_msi.c index 60c71e35021..2e680f34f72 100644 --- a/arch/sparc64/kernel/pci_msi.c +++ b/arch/sparc64/kernel/pci_msi.c @@ -323,7 +323,7 @@ void sparc64_pbm_msi_init(struct pci_pbm_info *pbm, const u32 *val; int len; - val = of_get_property(pbm->prom_node, "#msi-eqs", &len); + val = of_get_property(pbm->op->node, "#msi-eqs", &len); if (!val || len != 4) goto no_msi; pbm->msiq_num = *val; @@ -346,16 +346,16 @@ void sparc64_pbm_msi_init(struct pci_pbm_info *pbm, u32 msi64_len; } *arng; - val = of_get_property(pbm->prom_node, "msi-eq-size", &len); + val = of_get_property(pbm->op->node, "msi-eq-size", &len); if (!val || len != 4) goto no_msi; pbm->msiq_ent_count = *val; - mqp = of_get_property(pbm->prom_node, + mqp = of_get_property(pbm->op->node, "msi-eq-to-devino", &len); if (!mqp) - mqp = of_get_property(pbm->prom_node, + mqp = of_get_property(pbm->op->node, "msi-eq-devino", &len); if (!mqp || len != sizeof(struct msiq_prop)) goto no_msi; @@ -363,27 +363,27 @@ void sparc64_pbm_msi_init(struct pci_pbm_info *pbm, pbm->msiq_first = mqp->first_msiq; pbm->msiq_first_devino = mqp->first_devino; - val = of_get_property(pbm->prom_node, "#msi", &len); + val = of_get_property(pbm->op->node, "#msi", &len); if (!val || len != 4) goto no_msi; pbm->msi_num = *val; - mrng = of_get_property(pbm->prom_node, "msi-ranges", &len); + mrng = of_get_property(pbm->op->node, "msi-ranges", &len); if (!mrng || len != sizeof(struct msi_range_prop)) goto no_msi; pbm->msi_first = mrng->first_msi; - val = of_get_property(pbm->prom_node, "msi-data-mask", &len); + val = of_get_property(pbm->op->node, "msi-data-mask", &len); if (!val || len != 4) goto no_msi; pbm->msi_data_mask = *val; - val = of_get_property(pbm->prom_node, "msix-data-width", &len); + val = of_get_property(pbm->op->node, "msix-data-width", &len); if (!val || len != 4) goto no_msi; pbm->msix_data_width = *val; - arng = of_get_property(pbm->prom_node, "msi-address-ranges", + arng = of_get_property(pbm->op->node, "msi-address-ranges", &len); if (!arng || len != sizeof(struct addr_range_prop)) goto no_msi; diff --git a/arch/sparc64/kernel/pci_psycho.c b/arch/sparc64/kernel/pci_psycho.c index f85b6bebb0b..dfb3ec89298 100644 --- a/arch/sparc64/kernel/pci_psycho.c +++ b/arch/sparc64/kernel/pci_psycho.c @@ -17,29 +17,14 @@ #include <asm/irq.h> #include <asm/starfire.h> #include <asm/prom.h> -#include <asm/oplib.h> +#include <asm/upa.h> #include "pci_impl.h" #include "iommu_common.h" +#include "psycho_common.h" -/* All PSYCHO registers are 64-bits. The following accessor - * routines are how they are accessed. The REG parameter - * is a physical address. - */ -#define psycho_read(__reg) \ -({ u64 __ret; \ - __asm__ __volatile__("ldxa [%1] %2, %0" \ - : "=r" (__ret) \ - : "r" (__reg), "i" (ASI_PHYS_BYPASS_EC_E) \ - : "memory"); \ - __ret; \ -}) -#define psycho_write(__reg, __val) \ - __asm__ __volatile__("stxa %0, [%1] %2" \ - : /* no outputs */ \ - : "r" (__val), "r" (__reg), \ - "i" (ASI_PHYS_BYPASS_EC_E) \ - : "memory") +#define DRIVER_NAME "psycho" +#define PFX DRIVER_NAME ": " /* Misc. PSYCHO PCI controller register offsets and definitions. */ #define PSYCHO_CONTROL 0x0010UL @@ -67,37 +52,7 @@ #define PSYCHO_PCICTRL_RESV4 0x00000000000000c0UL /* Reserved */ #define PSYCHO_PCICTRL_AEN 0x000000000000003fUL /* PCI DVMA Arbitration Enable */ -/* U2P Programmer's Manual, page 13-55, configuration space - * address format: - * - * 32 24 23 16 15 11 10 8 7 2 1 0 - * --------------------------------------------------------- - * |0 0 0 0 0 0 0 0 1| bus | device | function | reg | 0 0 | - * --------------------------------------------------------- - */ -#define PSYCHO_CONFIG_BASE(PBM) \ - ((PBM)->config_space | (1UL << 24)) -#define PSYCHO_CONFIG_ENCODE(BUS, DEVFN, REG) \ - (((unsigned long)(BUS) << 16) | \ - ((unsigned long)(DEVFN) << 8) | \ - ((unsigned long)(REG))) - -static void *psycho_pci_config_mkaddr(struct pci_pbm_info *pbm, - unsigned char bus, - unsigned int devfn, - int where) -{ - if (!pbm) - return NULL; - return (void *) - (PSYCHO_CONFIG_BASE(pbm) | - PSYCHO_CONFIG_ENCODE(bus, devfn, where)); -} - /* PSYCHO error handling support. */ -enum psycho_error_type { - UE_ERR, CE_ERR, PCI_ERR -}; /* Helper function of IOMMU error checking, which checks out * the state of the streaming buffers. The IOMMU lock is @@ -122,129 +77,10 @@ enum psycho_error_type { #define PSYCHO_STC_DATA_B 0xc000UL #define PSYCHO_STC_ERR_A 0xb400UL #define PSYCHO_STC_ERR_B 0xc400UL -#define PSYCHO_STCERR_WRITE 0x0000000000000002UL /* Write Error */ -#define PSYCHO_STCERR_READ 0x0000000000000001UL /* Read Error */ #define PSYCHO_STC_TAG_A 0xb800UL #define PSYCHO_STC_TAG_B 0xc800UL -#define PSYCHO_STCTAG_PPN 0x0fffffff00000000UL /* Physical Page Number */ -#define PSYCHO_STCTAG_VPN 0x00000000ffffe000UL /* Virtual Page Number */ -#define PSYCHO_STCTAG_VALID 0x0000000000000002UL /* Valid */ -#define PSYCHO_STCTAG_WRITE 0x0000000000000001UL /* Writable */ #define PSYCHO_STC_LINE_A 0xb900UL #define PSYCHO_STC_LINE_B 0xc900UL -#define PSYCHO_STCLINE_LINDX 0x0000000001e00000UL /* LRU Index */ -#define PSYCHO_STCLINE_SPTR 0x00000000001f8000UL /* Dirty Data Start Pointer */ -#define PSYCHO_STCLINE_LADDR 0x0000000000007f00UL /* Line Address */ -#define PSYCHO_STCLINE_EPTR 0x00000000000000fcUL /* Dirty Data End Pointer */ -#define PSYCHO_STCLINE_VALID 0x0000000000000002UL /* Valid */ -#define PSYCHO_STCLINE_FOFN 0x0000000000000001UL /* Fetch Outstanding / Flush Necessary */ - -static DEFINE_SPINLOCK(stc_buf_lock); -static unsigned long stc_error_buf[128]; -static unsigned long stc_tag_buf[16]; -static unsigned long stc_line_buf[16]; - -static void __psycho_check_one_stc(struct pci_pbm_info *pbm, - int is_pbm_a) -{ - struct strbuf *strbuf = &pbm->stc; - unsigned long regbase = pbm->controller_regs; - unsigned long err_base, tag_base, line_base; - u64 control; - int i; - - if (is_pbm_a) { - err_base = regbase + PSYCHO_STC_ERR_A; - tag_base = regbase + PSYCHO_STC_TAG_A; - line_base = regbase + PSYCHO_STC_LINE_A; - } else { - err_base = regbase + PSYCHO_STC_ERR_B; - tag_base = regbase + PSYCHO_STC_TAG_B; - line_base = regbase + PSYCHO_STC_LINE_B; - } - - spin_lock(&stc_buf_lock); - - /* This is __REALLY__ dangerous. When we put the - * streaming buffer into diagnostic mode to probe - * it's tags and error status, we _must_ clear all - * of the line tag valid bits before re-enabling - * the streaming buffer. If any dirty data lives - * in the STC when we do this, we will end up - * invalidating it before it has a chance to reach - * main memory. - */ - control = psycho_read(strbuf->strbuf_control); - psycho_write(strbuf->strbuf_control, - (control | PSYCHO_STRBUF_CTRL_DENAB)); - for (i = 0; i < 128; i++) { - unsigned long val; - - val = psycho_read(err_base + (i * 8UL)); - psycho_write(err_base + (i * 8UL), 0UL); - stc_error_buf[i] = val; - } - for (i = 0; i < 16; i++) { - stc_tag_buf[i] = psycho_read(tag_base + (i * 8UL)); - stc_line_buf[i] = psycho_read(line_base + (i * 8UL)); - psycho_write(tag_base + (i * 8UL), 0UL); - psycho_write(line_base + (i * 8UL), 0UL); - } - - /* OK, state is logged, exit diagnostic mode. */ - psycho_write(strbuf->strbuf_control, control); - - for (i = 0; i < 16; i++) { - int j, saw_error, first, last; - - saw_error = 0; - first = i * 8; - last = first + 8; - for (j = first; j < last; j++) { - unsigned long errval = stc_error_buf[j]; - if (errval != 0) { - saw_error++; - printk("%s: STC_ERR(%d)[wr(%d)rd(%d)]\n", - pbm->name, - j, - (errval & PSYCHO_STCERR_WRITE) ? 1 : 0, - (errval & PSYCHO_STCERR_READ) ? 1 : 0); - } - } - if (saw_error != 0) { - unsigned long tagval = stc_tag_buf[i]; - unsigned long lineval = stc_line_buf[i]; - printk("%s: STC_TAG(%d)[PA(%016lx)VA(%08lx)V(%d)W(%d)]\n", - pbm->name, - i, - ((tagval & PSYCHO_STCTAG_PPN) >> 19UL), - (tagval & PSYCHO_STCTAG_VPN), - ((tagval & PSYCHO_STCTAG_VALID) ? 1 : 0), - ((tagval & PSYCHO_STCTAG_WRITE) ? 1 : 0)); - printk("%s: STC_LINE(%d)[LIDX(%lx)SP(%lx)LADDR(%lx)EP(%lx)" - "V(%d)FOFN(%d)]\n", - pbm->name, - i, - ((lineval & PSYCHO_STCLINE_LINDX) >> 21UL), - ((lineval & PSYCHO_STCLINE_SPTR) >> 15UL), - ((lineval & PSYCHO_STCLINE_LADDR) >> 8UL), - ((lineval & PSYCHO_STCLINE_EPTR) >> 2UL), - ((lineval & PSYCHO_STCLINE_VALID) ? 1 : 0), - ((lineval & PSYCHO_STCLINE_FOFN) ? 1 : 0)); - } - } - - spin_unlock(&stc_buf_lock); -} - -static void __psycho_check_stc_error(struct pci_pbm_info *pbm, - unsigned long afsr, - unsigned long afar, - enum psycho_error_type type) -{ - __psycho_check_one_stc(pbm, - (pbm == &pbm->parent->pbm_A)); -} /* When an Uncorrectable Error or a PCI Error happens, we * interrogate the IOMMU state to see if it is the cause. @@ -271,122 +107,7 @@ static void __psycho_check_stc_error(struct pci_pbm_info *pbm, #define PSYCHO_IOMMU_TSBBASE 0x0208UL #define PSYCHO_IOMMU_FLUSH 0x0210UL #define PSYCHO_IOMMU_TAG 0xa580UL -#define PSYCHO_IOMMU_TAG_ERRSTS (0x3UL << 23UL) -#define PSYCHO_IOMMU_TAG_ERR (0x1UL << 22UL) -#define PSYCHO_IOMMU_TAG_WRITE (0x1UL << 21UL) -#define PSYCHO_IOMMU_TAG_STREAM (0x1UL << 20UL) -#define PSYCHO_IOMMU_TAG_SIZE (0x1UL << 19UL) -#define PSYCHO_IOMMU_TAG_VPAGE 0x7ffffUL #define PSYCHO_IOMMU_DATA 0xa600UL -#define PSYCHO_IOMMU_DATA_VALID (1UL << 30UL) -#define PSYCHO_IOMMU_DATA_CACHE (1UL << 28UL) -#define PSYCHO_IOMMU_DATA_PPAGE 0xfffffffUL -static void psycho_check_iommu_error(struct pci_pbm_info *pbm, - unsigned long afsr, - unsigned long afar, - enum psycho_error_type type) -{ - struct iommu *iommu = pbm->iommu; - unsigned long iommu_tag[16]; - unsigned long iommu_data[16]; - unsigned long flags; - u64 control; - int i; - - spin_lock_irqsave(&iommu->lock, flags); - control = psycho_read(iommu->iommu_control); - if (control & PSYCHO_IOMMU_CTRL_XLTEERR) { - char *type_string; - - /* Clear the error encountered bit. */ - control &= ~PSYCHO_IOMMU_CTRL_XLTEERR; - psycho_write(iommu->iommu_control, control); - - switch((control & PSYCHO_IOMMU_CTRL_XLTESTAT) >> 25UL) { - case 0: - type_string = "Protection Error"; - break; - case 1: - type_string = "Invalid Error"; - break; - case 2: - type_string = "TimeOut Error"; - break; - case 3: - default: - type_string = "ECC Error"; - break; - }; - printk("%s: IOMMU Error, type[%s]\n", - pbm->name, type_string); - - /* Put the IOMMU into diagnostic mode and probe - * it's TLB for entries with error status. - * - * It is very possible for another DVMA to occur - * while we do this probe, and corrupt the system - * further. But we are so screwed at this point - * that we are likely to crash hard anyways, so - * get as much diagnostic information to the - * console as we can. - */ - psycho_write(iommu->iommu_control, - control | PSYCHO_IOMMU_CTRL_DENAB); - for (i = 0; i < 16; i++) { - unsigned long base = pbm->controller_regs; - - iommu_tag[i] = - psycho_read(base + PSYCHO_IOMMU_TAG + (i * 8UL)); - iommu_data[i] = - psycho_read(base + PSYCHO_IOMMU_DATA + (i * 8UL)); - - /* Now clear out the entry. */ - psycho_write(base + PSYCHO_IOMMU_TAG + (i * 8UL), 0); - psycho_write(base + PSYCHO_IOMMU_DATA + (i * 8UL), 0); - } - - /* Leave diagnostic mode. */ - psycho_write(iommu->iommu_control, control); - - for (i = 0; i < 16; i++) { - unsigned long tag, data; - - tag = iommu_tag[i]; - if (!(tag & PSYCHO_IOMMU_TAG_ERR)) - continue; - - data = iommu_data[i]; - switch((tag & PSYCHO_IOMMU_TAG_ERRSTS) >> 23UL) { - case 0: - type_string = "Protection Error"; - break; - case 1: - type_string = "Invalid Error"; - break; - case 2: - type_string = "TimeOut Error"; - break; - case 3: - default: - type_string = "ECC Error"; - break; - }; - printk("%s: IOMMU TAG(%d)[error(%s) wr(%d) str(%d) sz(%dK) vpg(%08lx)]\n", - pbm->name, i, type_string, - ((tag & PSYCHO_IOMMU_TAG_WRITE) ? 1 : 0), - ((tag & PSYCHO_IOMMU_TAG_STREAM) ? 1 : 0), - ((tag & PSYCHO_IOMMU_TAG_SIZE) ? 64 : 8), - (tag & PSYCHO_IOMMU_TAG_VPAGE) << IOMMU_PAGE_SHIFT); - printk("%s: IOMMU DATA(%d)[valid(%d) cache(%d) ppg(%016lx)]\n", - pbm->name, i, - ((data & PSYCHO_IOMMU_DATA_VALID) ? 1 : 0), - ((data & PSYCHO_IOMMU_DATA_CACHE) ? 1 : 0), - (data & PSYCHO_IOMMU_DATA_PPAGE) << IOMMU_PAGE_SHIFT); - } - } - __psycho_check_stc_error(pbm, afsr, afar, type); - spin_unlock_irqrestore(&iommu->lock, flags); -} /* Uncorrectable Errors. Cause of the error and the address are * recorded in the UE_AFSR and UE_AFAR of PSYCHO. They are errors @@ -410,15 +131,14 @@ static void psycho_check_iommu_error(struct pci_pbm_info *pbm, static irqreturn_t psycho_ue_intr(int irq, void *dev_id) { struct pci_pbm_info *pbm = dev_id; - struct pci_controller_info *p = pbm->parent; unsigned long afsr_reg = pbm->controller_regs + PSYCHO_UE_AFSR; unsigned long afar_reg = pbm->controller_regs + PSYCHO_UE_AFAR; unsigned long afsr, afar, error_bits; int reported; /* Latch uncorrectable error status. */ - afar = psycho_read(afar_reg); - afsr = psycho_read(afsr_reg); + afar = upa_readq(afar_reg); + afsr = upa_readq(afsr_reg); /* Clear the primary/secondary error status bits. */ error_bits = afsr & @@ -426,7 +146,7 @@ static irqreturn_t psycho_ue_intr(int irq, void *dev_id) PSYCHO_UEAFSR_SPIO | PSYCHO_UEAFSR_SDRD | PSYCHO_UEAFSR_SDWR); if (!error_bits) return IRQ_NONE; - psycho_write(afsr_reg, error_bits); + upa_writeq(error_bits, afsr_reg); /* Log the error. */ printk("%s: Uncorrectable Error, primary error type[%s]\n", @@ -463,8 +183,9 @@ static irqreturn_t psycho_ue_intr(int irq, void *dev_id) printk("]\n"); /* Interrogate both IOMMUs for error status. */ - psycho_check_iommu_error(&p->pbm_A, afsr, afar, UE_ERR); - psycho_check_iommu_error(&p->pbm_B, afsr, afar, UE_ERR); + psycho_check_iommu_error(pbm, afsr, afar, UE_ERR); + if (pbm->sibling) + psycho_check_iommu_error(pbm->sibling, afsr, afar, UE_ERR); return IRQ_HANDLED; } @@ -495,8 +216,8 @@ static irqreturn_t psycho_ce_intr(int irq, void *dev_id) int reported; /* Latch error status. */ - afar = psycho_read(afar_reg); - afsr = psycho_read(afsr_reg); + afar = upa_readq(afar_reg); + afsr = upa_readq(afsr_reg); /* Clear primary/secondary error status bits. */ error_bits = afsr & @@ -504,7 +225,7 @@ static irqreturn_t psycho_ce_intr(int irq, void *dev_id) PSYCHO_CEAFSR_SPIO | PSYCHO_CEAFSR_SDRD | PSYCHO_CEAFSR_SDWR); if (!error_bits) return IRQ_NONE; - psycho_write(afsr_reg, error_bits); + upa_writeq(error_bits, afsr_reg); /* Log the error. */ printk("%s: Correctable Error, primary error type[%s]\n", @@ -554,164 +275,9 @@ static irqreturn_t psycho_ce_intr(int irq, void *dev_id) */ #define PSYCHO_PCI_AFSR_A 0x2010UL #define PSYCHO_PCI_AFSR_B 0x4010UL -#define PSYCHO_PCIAFSR_PMA 0x8000000000000000UL /* Primary Master Abort Error */ -#define PSYCHO_PCIAFSR_PTA 0x4000000000000000UL /* Primary Target Abort Error */ -#define PSYCHO_PCIAFSR_PRTRY 0x2000000000000000UL /* Primary Excessive Retries */ -#define PSYCHO_PCIAFSR_PPERR 0x1000000000000000UL /* Primary Parity Error */ -#define PSYCHO_PCIAFSR_SMA 0x0800000000000000UL /* Secondary Master Abort Error */ -#define PSYCHO_PCIAFSR_STA 0x0400000000000000UL /* Secondary Target Abort Error */ -#define PSYCHO_PCIAFSR_SRTRY 0x0200000000000000UL /* Secondary Excessive Retries */ -#define PSYCHO_PCIAFSR_SPERR 0x0100000000000000UL /* Secondary Parity Error */ -#define PSYCHO_PCIAFSR_RESV1 0x00ff000000000000UL /* Reserved */ -#define PSYCHO_PCIAFSR_BMSK 0x0000ffff00000000UL /* Bytemask of failed transfer */ -#define PSYCHO_PCIAFSR_BLK 0x0000000080000000UL /* Trans was block operation */ -#define PSYCHO_PCIAFSR_RESV2 0x0000000040000000UL /* Reserved */ -#define PSYCHO_PCIAFSR_MID 0x000000003e000000UL /* MID causing the error */ -#define PSYCHO_PCIAFSR_RESV3 0x0000000001ffffffUL /* Reserved */ #define PSYCHO_PCI_AFAR_A 0x2018UL #define PSYCHO_PCI_AFAR_B 0x4018UL -static irqreturn_t psycho_pcierr_intr_other(struct pci_pbm_info *pbm, int is_pbm_a) -{ - unsigned long csr_reg, csr, csr_error_bits; - irqreturn_t ret = IRQ_NONE; - u16 stat, *addr; - - if (is_pbm_a) { - csr_reg = pbm->controller_regs + PSYCHO_PCIA_CTRL; - } else { - csr_reg = pbm->controller_regs + PSYCHO_PCIB_CTRL; - } - csr = psycho_read(csr_reg); - csr_error_bits = - csr & (PSYCHO_PCICTRL_SBH_ERR | PSYCHO_PCICTRL_SERR); - if (csr_error_bits) { - /* Clear the errors. */ - psycho_write(csr_reg, csr); - - /* Log 'em. */ - if (csr_error_bits & PSYCHO_PCICTRL_SBH_ERR) - printk("%s: PCI streaming byte hole error asserted.\n", - pbm->name); - if (csr_error_bits & PSYCHO_PCICTRL_SERR) - printk("%s: PCI SERR signal asserted.\n", pbm->name); - ret = IRQ_HANDLED; - } - addr = psycho_pci_config_mkaddr(pbm, pbm->pci_first_busno, - 0, PCI_STATUS); - pci_config_read16(addr, &stat); - if (stat & (PCI_STATUS_PARITY | - PCI_STATUS_SIG_TARGET_ABORT | - PCI_STATUS_REC_TARGET_ABORT | - PCI_STATUS_REC_MASTER_ABORT | - PCI_STATUS_SIG_SYSTEM_ERROR)) { - printk("%s: PCI bus error, PCI_STATUS[%04x]\n", - pbm->name, stat); - pci_config_write16(addr, 0xffff); - ret = IRQ_HANDLED; - } - return ret; -} - -static irqreturn_t psycho_pcierr_intr(int irq, void *dev_id) -{ - struct pci_pbm_info *pbm = dev_id; - struct pci_controller_info *p = pbm->parent; - unsigned long afsr_reg, afar_reg; - unsigned long afsr, afar, error_bits; - int is_pbm_a, reported; - - is_pbm_a = (pbm == &pbm->parent->pbm_A); - if (is_pbm_a) { - afsr_reg = p->pbm_A.controller_regs + PSYCHO_PCI_AFSR_A; - afar_reg = p->pbm_A.controller_regs + PSYCHO_PCI_AFAR_A; - } else { - afsr_reg = p->pbm_A.controller_regs + PSYCHO_PCI_AFSR_B; - afar_reg = p->pbm_A.controller_regs + PSYCHO_PCI_AFAR_B; - } - - /* Latch error status. */ - afar = psycho_read(afar_reg); - afsr = psycho_read(afsr_reg); - - /* Clear primary/secondary error status bits. */ - error_bits = afsr & - (PSYCHO_PCIAFSR_PMA | PSYCHO_PCIAFSR_PTA | - PSYCHO_PCIAFSR_PRTRY | PSYCHO_PCIAFSR_PPERR | - PSYCHO_PCIAFSR_SMA | PSYCHO_PCIAFSR_STA | - PSYCHO_PCIAFSR_SRTRY | PSYCHO_PCIAFSR_SPERR); - if (!error_bits) - return psycho_pcierr_intr_other(pbm, is_pbm_a); - psycho_write(afsr_reg, error_bits); - - /* Log the error. */ - printk("%s: PCI Error, primary error type[%s]\n", - pbm->name, - (((error_bits & PSYCHO_PCIAFSR_PMA) ? - "Master Abort" : - ((error_bits & PSYCHO_PCIAFSR_PTA) ? - "Target Abort" : - ((error_bits & PSYCHO_PCIAFSR_PRTRY) ? - "Excessive Retries" : - ((error_bits & PSYCHO_PCIAFSR_PPERR) ? - "Parity Error" : "???")))))); - printk("%s: bytemask[%04lx] UPA_MID[%02lx] was_block(%d)\n", - pbm->name, - (afsr & PSYCHO_PCIAFSR_BMSK) >> 32UL, - (afsr & PSYCHO_PCIAFSR_MID) >> 25UL, - (afsr & PSYCHO_PCIAFSR_BLK) ? 1 : 0); - printk("%s: PCI AFAR [%016lx]\n", pbm->name, afar); - printk("%s: PCI Secondary errors [", pbm->name); - reported = 0; - if (afsr & PSYCHO_PCIAFSR_SMA) { - reported++; - printk("(Master Abort)"); - } - if (afsr & PSYCHO_PCIAFSR_STA) { - reported++; - printk("(Target Abort)"); - } - if (afsr & PSYCHO_PCIAFSR_SRTRY) { - reported++; - printk("(Excessive Retries)"); - } - if (afsr & PSYCHO_PCIAFSR_SPERR) { - reported++; - printk("(Parity Error)"); - } - if (!reported) - printk("(none)"); - printk("]\n"); - - /* For the error types shown, scan PBM's PCI bus for devices - * which have logged that error type. - */ - - /* If we see a Target Abort, this could be the result of an - * IOMMU translation error of some sort. It is extremely - * useful to log this information as usually it indicates - * a bug in the IOMMU support code or a PCI device driver. - */ - if (error_bits & (PSYCHO_PCIAFSR_PTA | PSYCHO_PCIAFSR_STA)) { - psycho_check_iommu_error(pbm, afsr, afar, PCI_ERR); - pci_scan_for_target_abort(pbm, pbm->pci_bus); - } - if (error_bits & (PSYCHO_PCIAFSR_PMA | PSYCHO_PCIAFSR_SMA)) - pci_scan_for_master_abort(pbm, pbm->pci_bus); - - /* For excessive retries, PSYCHO/PBM will abort the device - * and there is no way to specifically check for excessive - * retries in the config space status registers. So what - * we hope is that we'll catch it via the master/target - * abort events. - */ - - if (error_bits & (PSYCHO_PCIAFSR_PPERR | PSYCHO_PCIAFSR_SPERR)) - pci_scan_for_parity_error(pbm, pbm->pci_bus); - - return IRQ_HANDLED; -} - /* XXX What about PowerFail/PowerManagement??? -DaveM */ #define PSYCHO_ECC_CTRL 0x0020 #define PSYCHO_ECCCTRL_EE 0x8000000000000000UL /* Enable ECC Checking */ @@ -719,7 +285,7 @@ static irqreturn_t psycho_pcierr_intr(int irq, void *dev_id) #define PSYCHO_ECCCTRL_CE 0x2000000000000000UL /* Enable CE INterrupts */ static void psycho_register_error_handlers(struct pci_pbm_info *pbm) { - struct of_device *op = of_find_device_by_node(pbm->prom_node); + struct of_device *op = of_find_device_by_node(pbm->op->node); unsigned long base = pbm->controller_regs; u64 tmp; int err; @@ -762,27 +328,26 @@ static void psycho_register_error_handlers(struct pci_pbm_info *pbm) "err=%d\n", pbm->name, err); /* Enable UE and CE interrupts for controller. */ - psycho_write(base + PSYCHO_ECC_CTRL, - (PSYCHO_ECCCTRL_EE | - PSYCHO_ECCCTRL_UE | - PSYCHO_ECCCTRL_CE)); + upa_writeq((PSYCHO_ECCCTRL_EE | + PSYCHO_ECCCTRL_UE | + PSYCHO_ECCCTRL_CE), base + PSYCHO_ECC_CTRL); /* Enable PCI Error interrupts and clear error * bits for each PBM. */ - tmp = psycho_read(base + PSYCHO_PCIA_CTRL); + tmp = upa_readq(base + PSYCHO_PCIA_CTRL); tmp |= (PSYCHO_PCICTRL_SERR | PSYCHO_PCICTRL_SBH_ERR | PSYCHO_PCICTRL_EEN); tmp &= ~(PSYCHO_PCICTRL_SBH_INT); - psycho_write(base + PSYCHO_PCIA_CTRL, tmp); + upa_writeq(tmp, base + PSYCHO_PCIA_CTRL); - tmp = psycho_read(base + PSYCHO_PCIB_CTRL); + tmp = upa_readq(base + PSYCHO_PCIB_CTRL); tmp |= (PSYCHO_PCICTRL_SERR | PSYCHO_PCICTRL_SBH_ERR | PSYCHO_PCICTRL_EEN); tmp &= ~(PSYCHO_PCICTRL_SBH_INT); - psycho_write(base + PSYCHO_PCIB_CTRL, tmp); + upa_writeq(tmp, base + PSYCHO_PCIB_CTRL); } /* PSYCHO boot time probing and initialization. */ @@ -803,11 +368,12 @@ static void pbm_config_busmastering(struct pci_pbm_info *pbm) pci_config_write8(addr, 64); } -static void __init psycho_scan_bus(struct pci_pbm_info *pbm) +static void __init psycho_scan_bus(struct pci_pbm_info *pbm, + struct device *parent) { pbm_config_busmastering(pbm); pbm->is_66mhz_capable = 0; - pbm->pci_bus = pci_scan_one_pbm(pbm); + pbm->pci_bus = pci_scan_one_pbm(pbm, parent); /* After the PCI bus scan is complete, we can register * the error interrupt handlers. @@ -815,61 +381,6 @@ static void __init psycho_scan_bus(struct pci_pbm_info *pbm) psycho_register_error_handlers(pbm); } -static int psycho_iommu_init(struct pci_pbm_info *pbm) -{ - struct iommu *iommu = pbm->iommu; - unsigned long i; - u64 control; - int err; - - /* Register addresses. */ - iommu->iommu_control = pbm->controller_regs + PSYCHO_IOMMU_CONTROL; - iommu->iommu_tsbbase = pbm->controller_regs + PSYCHO_IOMMU_TSBBASE; - iommu->iommu_flush = pbm->controller_regs + PSYCHO_IOMMU_FLUSH; - iommu->iommu_tags = iommu->iommu_flush + (0xa580UL - 0x0210UL); - - /* PSYCHO's IOMMU lacks ctx flushing. */ - iommu->iommu_ctxflush = 0; - - /* We use the main control register of PSYCHO as the write - * completion register. - */ - iommu->write_complete_reg = pbm->controller_regs + PSYCHO_CONTROL; - - /* - * Invalidate TLB Entries. - */ - control = psycho_read(pbm->controller_regs + PSYCHO_IOMMU_CONTROL); - control |= PSYCHO_IOMMU_CTRL_DENAB; - psycho_write(pbm->controller_regs + PSYCHO_IOMMU_CONTROL, control); - for(i = 0; i < 16; i++) { - psycho_write(pbm->controller_regs + PSYCHO_IOMMU_TAG + (i * 8UL), 0); - psycho_write(pbm->controller_regs + PSYCHO_IOMMU_DATA + (i * 8UL), 0); - } - - /* Leave diag mode enabled for full-flushing done - * in pci_iommu.c - */ - err = iommu_table_init(iommu, IO_TSB_SIZE, 0xc0000000, 0xffffffff, - pbm->numa_node); - if (err) - return err; - - psycho_write(pbm->controller_regs + PSYCHO_IOMMU_TSBBASE, - __pa(iommu->page_table)); - - control = psycho_read(pbm->controller_regs + PSYCHO_IOMMU_CONTROL); - control &= ~(PSYCHO_IOMMU_CTRL_TSBSZ | PSYCHO_IOMMU_CTRL_TBWSZ); - control |= (PSYCHO_IOMMU_TSBSZ_128K | PSYCHO_IOMMU_CTRL_ENAB); - psycho_write(pbm->controller_regs + PSYCHO_IOMMU_CONTROL, control); - - /* If necessary, hook us up for starfire IRQ translations. */ - if (this_is_starfire) - starfire_hookup(pbm->portid); - - return 0; -} - #define PSYCHO_IRQ_RETRY 0x1a00UL #define PSYCHO_PCIA_DIAG 0x2020UL #define PSYCHO_PCIB_DIAG 0x4020UL @@ -886,28 +397,28 @@ static void psycho_controller_hwinit(struct pci_pbm_info *pbm) { u64 tmp; - psycho_write(pbm->controller_regs + PSYCHO_IRQ_RETRY, 5); + upa_writeq(5, pbm->controller_regs + PSYCHO_IRQ_RETRY); /* Enable arbiter for all PCI slots. */ - tmp = psycho_read(pbm->controller_regs + PSYCHO_PCIA_CTRL); + tmp = upa_readq(pbm->controller_regs + PSYCHO_PCIA_CTRL); tmp |= PSYCHO_PCICTRL_AEN; - psycho_write(pbm->controller_regs + PSYCHO_PCIA_CTRL, tmp); + upa_writeq(tmp, pbm->controller_regs + PSYCHO_PCIA_CTRL); - tmp = psycho_read(pbm->controller_regs + PSYCHO_PCIB_CTRL); + tmp = upa_readq(pbm->controller_regs + PSYCHO_PCIB_CTRL); tmp |= PSYCHO_PCICTRL_AEN; - psycho_write(pbm->controller_regs + PSYCHO_PCIB_CTRL, tmp); + upa_writeq(tmp, pbm->controller_regs + PSYCHO_PCIB_CTRL); /* Disable DMA write / PIO read synchronization on * both PCI bus segments. * [ U2P Erratum 1243770, STP2223BGA data sheet ] */ - tmp = psycho_read(pbm->controller_regs + PSYCHO_PCIA_DIAG); + tmp = upa_readq(pbm->controller_regs + PSYCHO_PCIA_DIAG); tmp |= PSYCHO_PCIDIAG_DDWSYNC; - psycho_write(pbm->controller_regs + PSYCHO_PCIA_DIAG, tmp); + upa_writeq(tmp, pbm->controller_regs + PSYCHO_PCIA_DIAG); - tmp = psycho_read(pbm->controller_regs + PSYCHO_PCIB_DIAG); + tmp = upa_readq(pbm->controller_regs + PSYCHO_PCIB_DIAG); tmp |= PSYCHO_PCIDIAG_DDWSYNC; - psycho_write(pbm->controller_regs + PSYCHO_PCIB_DIAG, tmp); + upa_writeq(tmp, pbm->controller_regs + PSYCHO_PCIB_DIAG); } static void psycho_pbm_strbuf_init(struct pci_pbm_info *pbm, @@ -920,10 +431,16 @@ static void psycho_pbm_strbuf_init(struct pci_pbm_info *pbm, pbm->stc.strbuf_control = base + PSYCHO_STRBUF_CONTROL_A; pbm->stc.strbuf_pflush = base + PSYCHO_STRBUF_FLUSH_A; pbm->stc.strbuf_fsync = base + PSYCHO_STRBUF_FSYNC_A; + pbm->stc.strbuf_err_stat = base + PSYCHO_STC_ERR_A; + pbm->stc.strbuf_tag_diag = base + PSYCHO_STC_TAG_A; + pbm->stc.strbuf_line_diag= base + PSYCHO_STC_LINE_A; } else { pbm->stc.strbuf_control = base + PSYCHO_STRBUF_CONTROL_B; pbm->stc.strbuf_pflush = base + PSYCHO_STRBUF_FLUSH_B; pbm->stc.strbuf_fsync = base + PSYCHO_STRBUF_FSYNC_B; + pbm->stc.strbuf_err_stat = base + PSYCHO_STC_ERR_B; + pbm->stc.strbuf_tag_diag = base + PSYCHO_STC_TAG_B; + pbm->stc.strbuf_line_diag= base + PSYCHO_STC_LINE_B; } /* PSYCHO's streaming buffer lacks ctx flushing. */ pbm->stc.strbuf_ctxflush = 0; @@ -946,7 +463,7 @@ static void psycho_pbm_strbuf_init(struct pci_pbm_info *pbm, */ #undef PSYCHO_STRBUF_RERUN_ENABLE #undef PSYCHO_STRBUF_RERUN_DISABLE - control = psycho_read(pbm->stc.strbuf_control); + control = upa_readq(pbm->stc.strbuf_control); control |= PSYCHO_STRBUF_CTRL_ENAB; control &= ~(PSYCHO_STRBUF_CTRL_LENAB | PSYCHO_STRBUF_CTRL_LPTR); #ifdef PSYCHO_STRBUF_RERUN_ENABLE @@ -956,7 +473,7 @@ static void psycho_pbm_strbuf_init(struct pci_pbm_info *pbm, control |= PSYCHO_STRBUF_CTRL_RRDIS; #endif #endif - psycho_write(pbm->stc.strbuf_control, control); + upa_writeq(control, pbm->stc.strbuf_control); pbm->stc.strbuf_enabled = 1; } @@ -968,111 +485,134 @@ static void psycho_pbm_strbuf_init(struct pci_pbm_info *pbm, #define PSYCHO_MEMSPACE_B 0x180000000UL #define PSYCHO_MEMSPACE_SIZE 0x07fffffffUL -static void __init psycho_pbm_init(struct pci_controller_info *p, - struct device_node *dp, int is_pbm_a) +static void __init psycho_pbm_init(struct pci_pbm_info *pbm, + struct of_device *op, int is_pbm_a) { - struct property *prop; - struct pci_pbm_info *pbm; - - if (is_pbm_a) - pbm = &p->pbm_A; - else - pbm = &p->pbm_B; - - pbm->next = pci_pbm_root; - pci_pbm_root = pbm; - - pbm->numa_node = -1; - - pbm->scan_bus = psycho_scan_bus; - pbm->pci_ops = &sun4u_pci_ops; - pbm->config_space_reg_bits = 8; - - pbm->index = pci_num_pbms++; - - pbm->chip_type = PBM_CHIP_TYPE_PSYCHO; - pbm->chip_version = 0; - prop = of_find_property(dp, "version#", NULL); - if (prop) - pbm->chip_version = *(int *) prop->value; - pbm->chip_revision = 0; - prop = of_find_property(dp, "module-revision#", NULL); - if (prop) - pbm->chip_revision = *(int *) prop->value; - - pbm->parent = p; - pbm->prom_node = dp; - pbm->name = dp->full_name; - - printk("%s: PSYCHO PCI Bus Module ver[%x:%x]\n", - pbm->name, - pbm->chip_version, pbm->chip_revision); - - pci_determine_mem_io_space(pbm); + psycho_pbm_init_common(pbm, op, "PSYCHO", PBM_CHIP_TYPE_PSYCHO); + psycho_pbm_strbuf_init(pbm, is_pbm_a); + psycho_scan_bus(pbm, &op->dev); +} - pci_get_pbm_props(pbm); +static struct pci_pbm_info * __devinit psycho_find_sibling(u32 upa_portid) +{ + struct pci_pbm_info *pbm; - psycho_pbm_strbuf_init(pbm, is_pbm_a); + for (pbm = pci_pbm_root; pbm; pbm = pbm->next) { + if (pbm->portid == upa_portid) + return pbm; + } + return NULL; } #define PSYCHO_CONFIGSPACE 0x001000000UL -void __init psycho_init(struct device_node *dp, char *model_name) +static int __devinit psycho_probe(struct of_device *op, + const struct of_device_id *match) { - struct linux_prom64_registers *pr_regs; - struct pci_controller_info *p; + const struct linux_prom64_registers *pr_regs; + struct device_node *dp = op->node; struct pci_pbm_info *pbm; struct iommu *iommu; - struct property *prop; + int is_pbm_a, err; u32 upa_portid; - int is_pbm_a; - upa_portid = 0xff; - prop = of_find_property(dp, "upa-portid", NULL); - if (prop) - upa_portid = *(u32 *) prop->value; + upa_portid = of_getintprop_default(dp, "upa-portid", 0xff); - for (pbm = pci_pbm_root; pbm; pbm = pbm->next) { - struct pci_controller_info *p = pbm->parent; + err = -ENOMEM; + pbm = kzalloc(sizeof(*pbm), GFP_KERNEL); + if (!pbm) { + printk(KERN_ERR PFX "Cannot allocate pci_pbm_info.\n"); + goto out_err; + } - if (p->pbm_A.portid == upa_portid) { - is_pbm_a = (p->pbm_A.prom_node == NULL); - psycho_pbm_init(p, dp, is_pbm_a); - return; + pbm->sibling = psycho_find_sibling(upa_portid); + if (pbm->sibling) { + iommu = pbm->sibling->iommu; + } else { + iommu = kzalloc(sizeof(struct iommu), GFP_KERNEL); + if (!iommu) { + printk(KERN_ERR PFX "Cannot allocate PBM iommu.\n"); + goto out_free_controller; } } - p = kzalloc(sizeof(struct pci_controller_info), GFP_ATOMIC); - if (!p) - goto fatal_memory_error; - iommu = kzalloc(sizeof(struct iommu), GFP_ATOMIC); - if (!iommu) - goto fatal_memory_error; + pbm->iommu = iommu; + pbm->portid = upa_portid; - p->pbm_A.iommu = p->pbm_B.iommu = iommu; + pr_regs = of_get_property(dp, "reg", NULL); + err = -ENODEV; + if (!pr_regs) { + printk(KERN_ERR PFX "No reg property.\n"); + goto out_free_iommu; + } - p->pbm_A.portid = upa_portid; - p->pbm_B.portid = upa_portid; + is_pbm_a = ((pr_regs[0].phys_addr & 0x6000) == 0x2000); - prop = of_find_property(dp, "reg", NULL); - pr_regs = prop->value; + pbm->controller_regs = pr_regs[2].phys_addr; + pbm->config_space = (pr_regs[2].phys_addr + PSYCHO_CONFIGSPACE); - p->pbm_A.controller_regs = pr_regs[2].phys_addr; - p->pbm_B.controller_regs = pr_regs[2].phys_addr; + if (is_pbm_a) { + pbm->pci_afsr = pbm->controller_regs + PSYCHO_PCI_AFSR_A; + pbm->pci_afar = pbm->controller_regs + PSYCHO_PCI_AFAR_A; + pbm->pci_csr = pbm->controller_regs + PSYCHO_PCIA_CTRL; + } else { + pbm->pci_afsr = pbm->controller_regs + PSYCHO_PCI_AFSR_B; + pbm->pci_afar = pbm->controller_regs + PSYCHO_PCI_AFAR_B; + pbm->pci_csr = pbm->controller_regs + PSYCHO_PCIB_CTRL; + } - p->pbm_A.config_space = p->pbm_B.config_space = - (pr_regs[2].phys_addr + PSYCHO_CONFIGSPACE); + psycho_controller_hwinit(pbm); + if (!pbm->sibling) { + err = psycho_iommu_init(pbm, 128, 0xc0000000, + 0xffffffff, PSYCHO_CONTROL); + if (err) + goto out_free_iommu; - psycho_controller_hwinit(&p->pbm_A); + /* If necessary, hook us up for starfire IRQ translations. */ + if (this_is_starfire) + starfire_hookup(pbm->portid); + } - if (psycho_iommu_init(&p->pbm_A)) - goto fatal_memory_error; + psycho_pbm_init(pbm, op, is_pbm_a); - is_pbm_a = ((pr_regs[0].phys_addr & 0x6000) == 0x2000); - psycho_pbm_init(p, dp, is_pbm_a); - return; + pbm->next = pci_pbm_root; + pci_pbm_root = pbm; + + if (pbm->sibling) + pbm->sibling->sibling = pbm; + + dev_set_drvdata(&op->dev, pbm); + + return 0; + +out_free_iommu: + if (!pbm->sibling) + kfree(pbm->iommu); -fatal_memory_error: - prom_printf("PSYCHO: Fatal memory allocation error.\n"); - prom_halt(); +out_free_controller: + kfree(pbm); + +out_err: + return err; } + +static struct of_device_id __initdata psycho_match[] = { + { + .name = "pci", + .compatible = "pci108e,8000", + }, + {}, +}; + +static struct of_platform_driver psycho_driver = { + .name = DRIVER_NAME, + .match_table = psycho_match, + .probe = psycho_probe, +}; + +static int __init psycho_init(void) +{ + return of_register_driver(&psycho_driver, &of_bus_type); +} + +subsys_initcall(psycho_init); diff --git a/arch/sparc64/kernel/pci_sabre.c b/arch/sparc64/kernel/pci_sabre.c index ade5184e75d..713257b6963 100644 --- a/arch/sparc64/kernel/pci_sabre.c +++ b/arch/sparc64/kernel/pci_sabre.c @@ -16,31 +16,15 @@ #include <asm/apb.h> #include <asm/iommu.h> #include <asm/irq.h> -#include <asm/smp.h> -#include <asm/oplib.h> #include <asm/prom.h> +#include <asm/upa.h> #include "pci_impl.h" #include "iommu_common.h" +#include "psycho_common.h" -/* All SABRE registers are 64-bits. The following accessor - * routines are how they are accessed. The REG parameter - * is a physical address. - */ -#define sabre_read(__reg) \ -({ u64 __ret; \ - __asm__ __volatile__("ldxa [%1] %2, %0" \ - : "=r" (__ret) \ - : "r" (__reg), "i" (ASI_PHYS_BYPASS_EC_E) \ - : "memory"); \ - __ret; \ -}) -#define sabre_write(__reg, __val) \ - __asm__ __volatile__("stxa %0, [%1] %2" \ - : /* no outputs */ \ - : "r" (__val), "r" (__reg), \ - "i" (ASI_PHYS_BYPASS_EC_E) \ - : "memory") +#define DRIVER_NAME "sabre" +#define PFX DRIVER_NAME ": " /* SABRE PCI controller register offsets and definitions. */ #define SABRE_UE_AFSR 0x0030UL @@ -208,95 +192,6 @@ static int hummingbird_p; static struct pci_bus *sabre_root_bus; -/* SABRE error handling support. */ -static void sabre_check_iommu_error(struct pci_pbm_info *pbm, - unsigned long afsr, - unsigned long afar) -{ - struct iommu *iommu = pbm->iommu; - unsigned long iommu_tag[16]; - unsigned long iommu_data[16]; - unsigned long flags; - u64 control; - int i; - - spin_lock_irqsave(&iommu->lock, flags); - control = sabre_read(iommu->iommu_control); - if (control & SABRE_IOMMUCTRL_ERR) { - char *type_string; - - /* Clear the error encountered bit. - * NOTE: On Sabre this is write 1 to clear, - * which is different from Psycho. - */ - sabre_write(iommu->iommu_control, control); - switch((control & SABRE_IOMMUCTRL_ERRSTS) >> 25UL) { - case 1: - type_string = "Invalid Error"; - break; - case 3: - type_string = "ECC Error"; - break; - default: - type_string = "Unknown"; - break; - }; - printk("%s: IOMMU Error, type[%s]\n", - pbm->name, type_string); - - /* Enter diagnostic mode and probe for error'd - * entries in the IOTLB. - */ - control &= ~(SABRE_IOMMUCTRL_ERRSTS | SABRE_IOMMUCTRL_ERR); - sabre_write(iommu->iommu_control, - (control | SABRE_IOMMUCTRL_DENAB)); - for (i = 0; i < 16; i++) { - unsigned long base = pbm->controller_regs; - - iommu_tag[i] = - sabre_read(base + SABRE_IOMMU_TAG + (i * 8UL)); - iommu_data[i] = - sabre_read(base + SABRE_IOMMU_DATA + (i * 8UL)); - sabre_write(base + SABRE_IOMMU_TAG + (i * 8UL), 0); - sabre_write(base + SABRE_IOMMU_DATA + (i * 8UL), 0); - } - sabre_write(iommu->iommu_control, control); - - for (i = 0; i < 16; i++) { - unsigned long tag, data; - - tag = iommu_tag[i]; - if (!(tag & SABRE_IOMMUTAG_ERR)) - continue; - - data = iommu_data[i]; - switch((tag & SABRE_IOMMUTAG_ERRSTS) >> 23UL) { - case 1: - type_string = "Invalid Error"; - break; - case 3: - type_string = "ECC Error"; - break; - default: - type_string = "Unknown"; - break; - }; - printk("%s: IOMMU TAG(%d)[RAW(%016lx)error(%s)wr(%d)sz(%dK)vpg(%08lx)]\n", - pbm->name, i, tag, type_string, - ((tag & SABRE_IOMMUTAG_WRITE) ? 1 : 0), - ((tag & SABRE_IOMMUTAG_SIZE) ? 64 : 8), - ((tag & SABRE_IOMMUTAG_VPN) << IOMMU_PAGE_SHIFT)); - printk("%s: IOMMU DATA(%d)[RAW(%016lx)valid(%d)used(%d)cache(%d)ppg(%016lx)\n", - pbm->name, i, data, - ((data & SABRE_IOMMUDATA_VALID) ? 1 : 0), - ((data & SABRE_IOMMUDATA_USED) ? 1 : 0), - ((data & SABRE_IOMMUDATA_CACHE) ? 1 : 0), - ((data & SABRE_IOMMUDATA_PPN) << IOMMU_PAGE_SHIFT)); - } - } - spin_unlock_irqrestore(&iommu->lock, flags); -} - static irqreturn_t sabre_ue_intr(int irq, void *dev_id) { struct pci_pbm_info *pbm = dev_id; @@ -306,8 +201,8 @@ static irqreturn_t sabre_ue_intr(int irq, void *dev_id) int reported; /* Latch uncorrectable error status. */ - afar = sabre_read(afar_reg); - afsr = sabre_read(afsr_reg); + afar = upa_readq(afar_reg); + afsr = upa_readq(afsr_reg); /* Clear the primary/secondary error status bits. */ error_bits = afsr & @@ -316,7 +211,7 @@ static irqreturn_t sabre_ue_intr(int irq, void *dev_id) SABRE_UEAFSR_SDTE | SABRE_UEAFSR_PDTE); if (!error_bits) return IRQ_NONE; - sabre_write(afsr_reg, error_bits); + upa_writeq(error_bits, afsr_reg); /* Log the error. */ printk("%s: Uncorrectable Error, primary error type[%s%s]\n", @@ -352,7 +247,7 @@ static irqreturn_t sabre_ue_intr(int irq, void *dev_id) printk("]\n"); /* Interrogate IOMMU for error status. */ - sabre_check_iommu_error(pbm, afsr, afar); + psycho_check_iommu_error(pbm, afsr, afar, UE_ERR); return IRQ_HANDLED; } @@ -366,8 +261,8 @@ static irqreturn_t sabre_ce_intr(int irq, void *dev_id) int reported; /* Latch error status. */ - afar = sabre_read(afar_reg); - afsr = sabre_read(afsr_reg); + afar = upa_readq(afar_reg); + afsr = upa_readq(afsr_reg); /* Clear primary/secondary error status bits. */ error_bits = afsr & @@ -375,7 +270,7 @@ static irqreturn_t sabre_ce_intr(int irq, void *dev_id) SABRE_CEAFSR_SDRD | SABRE_CEAFSR_SDWR); if (!error_bits) return IRQ_NONE; - sabre_write(afsr_reg, error_bits); + upa_writeq(error_bits, afsr_reg); /* Log the error. */ printk("%s: Correctable Error, primary error type[%s]\n", @@ -413,136 +308,9 @@ static irqreturn_t sabre_ce_intr(int irq, void *dev_id) return IRQ_HANDLED; } -static irqreturn_t sabre_pcierr_intr_other(struct pci_pbm_info *pbm) -{ - unsigned long csr_reg, csr, csr_error_bits; - irqreturn_t ret = IRQ_NONE; - u16 stat; - - csr_reg = pbm->controller_regs + SABRE_PCICTRL; - csr = sabre_read(csr_reg); - csr_error_bits = - csr & SABRE_PCICTRL_SERR; - if (csr_error_bits) { - /* Clear the errors. */ - sabre_write(csr_reg, csr); - - /* Log 'em. */ - if (csr_error_bits & SABRE_PCICTRL_SERR) - printk("%s: PCI SERR signal asserted.\n", - pbm->name); - ret = IRQ_HANDLED; - } - pci_bus_read_config_word(sabre_root_bus, 0, - PCI_STATUS, &stat); - if (stat & (PCI_STATUS_PARITY | - PCI_STATUS_SIG_TARGET_ABORT | - PCI_STATUS_REC_TARGET_ABORT | - PCI_STATUS_REC_MASTER_ABORT | - PCI_STATUS_SIG_SYSTEM_ERROR)) { - printk("%s: PCI bus error, PCI_STATUS[%04x]\n", - pbm->name, stat); - pci_bus_write_config_word(sabre_root_bus, 0, - PCI_STATUS, 0xffff); - ret = IRQ_HANDLED; - } - return ret; -} - -static irqreturn_t sabre_pcierr_intr(int irq, void *dev_id) -{ - struct pci_pbm_info *pbm = dev_id; - unsigned long afsr_reg, afar_reg; - unsigned long afsr, afar, error_bits; - int reported; - - afsr_reg = pbm->controller_regs + SABRE_PIOAFSR; - afar_reg = pbm->controller_regs + SABRE_PIOAFAR; - - /* Latch error status. */ - afar = sabre_read(afar_reg); - afsr = sabre_read(afsr_reg); - - /* Clear primary/secondary error status bits. */ - error_bits = afsr & - (SABRE_PIOAFSR_PMA | SABRE_PIOAFSR_PTA | - SABRE_PIOAFSR_PRTRY | SABRE_PIOAFSR_PPERR | - SABRE_PIOAFSR_SMA | SABRE_PIOAFSR_STA | - SABRE_PIOAFSR_SRTRY | SABRE_PIOAFSR_SPERR); - if (!error_bits) - return sabre_pcierr_intr_other(pbm); - sabre_write(afsr_reg, error_bits); - - /* Log the error. */ - printk("%s: PCI Error, primary error type[%s]\n", - pbm->name, - (((error_bits & SABRE_PIOAFSR_PMA) ? - "Master Abort" : - ((error_bits & SABRE_PIOAFSR_PTA) ? - "Target Abort" : - ((error_bits & SABRE_PIOAFSR_PRTRY) ? - "Excessive Retries" : - ((error_bits & SABRE_PIOAFSR_PPERR) ? - "Parity Error" : "???")))))); - printk("%s: bytemask[%04lx] was_block(%d)\n", - pbm->name, - (afsr & SABRE_PIOAFSR_BMSK) >> 32UL, - (afsr & SABRE_PIOAFSR_BLK) ? 1 : 0); - printk("%s: PCI AFAR [%016lx]\n", pbm->name, afar); - printk("%s: PCI Secondary errors [", pbm->name); - reported = 0; - if (afsr & SABRE_PIOAFSR_SMA) { - reported++; - printk("(Master Abort)"); - } - if (afsr & SABRE_PIOAFSR_STA) { - reported++; - printk("(Target Abort)"); - } - if (afsr & SABRE_PIOAFSR_SRTRY) { - reported++; - printk("(Excessive Retries)"); - } - if (afsr & SABRE_PIOAFSR_SPERR) { - reported++; - printk("(Parity Error)"); - } - if (!reported) - printk("(none)"); - printk("]\n"); - - /* For the error types shown, scan both PCI buses for devices - * which have logged that error type. - */ - - /* If we see a Target Abort, this could be the result of an - * IOMMU translation error of some sort. It is extremely - * useful to log this information as usually it indicates - * a bug in the IOMMU support code or a PCI device driver. - */ - if (error_bits & (SABRE_PIOAFSR_PTA | SABRE_PIOAFSR_STA)) { - sabre_check_iommu_error(pbm, afsr, afar); - pci_scan_for_target_abort(pbm, pbm->pci_bus); - } - if (error_bits & (SABRE_PIOAFSR_PMA | SABRE_PIOAFSR_SMA)) - pci_scan_for_master_abort(pbm, pbm->pci_bus); - - /* For excessive retries, SABRE/PBM will abort the device - * and there is no way to specifically check for excessive - * retries in the config space status registers. So what - * we hope is that we'll catch it via the master/target - * abort events. - */ - - if (error_bits & (SABRE_PIOAFSR_PPERR | SABRE_PIOAFSR_SPERR)) - pci_scan_for_parity_error(pbm, pbm->pci_bus); - - return IRQ_HANDLED; -} - static void sabre_register_error_handlers(struct pci_pbm_info *pbm) { - struct device_node *dp = pbm->prom_node; + struct device_node *dp = pbm->op->node; struct of_device *op; unsigned long base = pbm->controller_regs; u64 tmp; @@ -568,33 +336,34 @@ static void sabre_register_error_handlers(struct pci_pbm_info *pbm) * registering the handler so that we don't get spurious * interrupts. */ - sabre_write(base + SABRE_UE_AFSR, - (SABRE_UEAFSR_PDRD | SABRE_UEAFSR_PDWR | - SABRE_UEAFSR_SDRD | SABRE_UEAFSR_SDWR | - SABRE_UEAFSR_SDTE | SABRE_UEAFSR_PDTE)); + upa_writeq((SABRE_UEAFSR_PDRD | SABRE_UEAFSR_PDWR | + SABRE_UEAFSR_SDRD | SABRE_UEAFSR_SDWR | + SABRE_UEAFSR_SDTE | SABRE_UEAFSR_PDTE), + base + SABRE_UE_AFSR); err = request_irq(op->irqs[1], sabre_ue_intr, 0, "SABRE_UE", pbm); if (err) printk(KERN_WARNING "%s: Couldn't register UE, err=%d.\n", pbm->name, err); - sabre_write(base + SABRE_CE_AFSR, - (SABRE_CEAFSR_PDRD | SABRE_CEAFSR_PDWR | - SABRE_CEAFSR_SDRD | SABRE_CEAFSR_SDWR)); + upa_writeq((SABRE_CEAFSR_PDRD | SABRE_CEAFSR_PDWR | + SABRE_CEAFSR_SDRD | SABRE_CEAFSR_SDWR), + base + SABRE_CE_AFSR); + err = request_irq(op->irqs[2], sabre_ce_intr, 0, "SABRE_CE", pbm); if (err) printk(KERN_WARNING "%s: Couldn't register CE, err=%d.\n", pbm->name, err); - err = request_irq(op->irqs[0], sabre_pcierr_intr, 0, + err = request_irq(op->irqs[0], psycho_pcierr_intr, 0, "SABRE_PCIERR", pbm); if (err) printk(KERN_WARNING "%s: Couldn't register PCIERR, err=%d.\n", pbm->name, err); - tmp = sabre_read(base + SABRE_PCICTRL); + tmp = upa_readq(base + SABRE_PCICTRL); tmp |= SABRE_PCICTRL_ERREN; - sabre_write(base + SABRE_PCICTRL, tmp); + upa_writeq(tmp, base + SABRE_PCICTRL); } static void apb_init(struct pci_bus *sabre_bus) @@ -633,7 +402,8 @@ static void apb_init(struct pci_bus *sabre_bus) } } -static void __init sabre_scan_bus(struct pci_pbm_info *pbm) +static void __init sabre_scan_bus(struct pci_pbm_info *pbm, + struct device *parent) { static int once; @@ -656,12 +426,12 @@ static void __init sabre_scan_bus(struct pci_pbm_info *pbm) * to live at bus 0. */ if (once != 0) { - prom_printf("SABRE: Multiple controllers unsupported.\n"); - prom_halt(); + printk(KERN_ERR PFX "Multiple controllers unsupported.\n"); + return; } once++; - pbm->pci_bus = pci_scan_one_pbm(pbm); + pbm->pci_bus = pci_scan_one_pbm(pbm, parent); if (!pbm->pci_bus) return; @@ -672,133 +442,58 @@ static void __init sabre_scan_bus(struct pci_pbm_info *pbm) sabre_register_error_handlers(pbm); } -static int sabre_iommu_init(struct pci_pbm_info *pbm, - int tsbsize, unsigned long dvma_offset, - u32 dma_mask) -{ - struct iommu *iommu = pbm->iommu; - unsigned long i; - u64 control; - int err; - - /* Register addresses. */ - iommu->iommu_control = pbm->controller_regs + SABRE_IOMMU_CONTROL; - iommu->iommu_tsbbase = pbm->controller_regs + SABRE_IOMMU_TSBBASE; - iommu->iommu_flush = pbm->controller_regs + SABRE_IOMMU_FLUSH; - iommu->iommu_tags = iommu->iommu_flush + (0xa580UL - 0x0210UL); - iommu->write_complete_reg = pbm->controller_regs + SABRE_WRSYNC; - /* Sabre's IOMMU lacks ctx flushing. */ - iommu->iommu_ctxflush = 0; - - /* Invalidate TLB Entries. */ - control = sabre_read(pbm->controller_regs + SABRE_IOMMU_CONTROL); - control |= SABRE_IOMMUCTRL_DENAB; - sabre_write(pbm->controller_regs + SABRE_IOMMU_CONTROL, control); - - for(i = 0; i < 16; i++) { - sabre_write(pbm->controller_regs + SABRE_IOMMU_TAG + (i * 8UL), 0); - sabre_write(pbm->controller_regs + SABRE_IOMMU_DATA + (i * 8UL), 0); - } - - /* Leave diag mode enabled for full-flushing done - * in pci_iommu.c - */ - err = iommu_table_init(iommu, tsbsize * 1024 * 8, - dvma_offset, dma_mask, pbm->numa_node); - if (err) - return err; - - sabre_write(pbm->controller_regs + SABRE_IOMMU_TSBBASE, - __pa(iommu->page_table)); - - control = sabre_read(pbm->controller_regs + SABRE_IOMMU_CONTROL); - control &= ~(SABRE_IOMMUCTRL_TSBSZ | SABRE_IOMMUCTRL_TBWSZ); - control |= SABRE_IOMMUCTRL_ENAB; - switch(tsbsize) { - case 64: - control |= SABRE_IOMMU_TSBSZ_64K; - break; - case 128: - control |= SABRE_IOMMU_TSBSZ_128K; - break; - default: - prom_printf("iommu_init: Illegal TSB size %d\n", tsbsize); - prom_halt(); - break; - } - sabre_write(pbm->controller_regs + SABRE_IOMMU_CONTROL, control); - - return 0; -} - -static void __init sabre_pbm_init(struct pci_controller_info *p, - struct pci_pbm_info *pbm, struct device_node *dp) +static void __init sabre_pbm_init(struct pci_pbm_info *pbm, + struct of_device *op) { - pbm->name = dp->full_name; - printk("%s: SABRE PCI Bus Module\n", pbm->name); - - pbm->numa_node = -1; - - pbm->scan_bus = sabre_scan_bus; - pbm->pci_ops = &sun4u_pci_ops; - pbm->config_space_reg_bits = 8; - - pbm->index = pci_num_pbms++; - - pbm->chip_type = PBM_CHIP_TYPE_SABRE; - pbm->parent = p; - pbm->prom_node = dp; - pci_get_pbm_props(pbm); - - pci_determine_mem_io_space(pbm); + psycho_pbm_init_common(pbm, op, "SABRE", PBM_CHIP_TYPE_SABRE); + pbm->pci_afsr = pbm->controller_regs + SABRE_PIOAFSR; + pbm->pci_afar = pbm->controller_regs + SABRE_PIOAFAR; + pbm->pci_csr = pbm->controller_regs + SABRE_PCICTRL; + sabre_scan_bus(pbm, &op->dev); } -void __init sabre_init(struct device_node *dp, char *model_name) +static int __devinit sabre_probe(struct of_device *op, + const struct of_device_id *match) { const struct linux_prom64_registers *pr_regs; - struct pci_controller_info *p; + struct device_node *dp = op->node; struct pci_pbm_info *pbm; + u32 upa_portid, dma_mask; struct iommu *iommu; - int tsbsize; + int tsbsize, err; const u32 *vdma; - u32 upa_portid, dma_mask; u64 clear_irq; - hummingbird_p = 0; - if (!strcmp(model_name, "pci108e,a001")) - hummingbird_p = 1; - else if (!strcmp(model_name, "SUNW,sabre")) { - const char *compat = of_get_property(dp, "compatible", NULL); - if (compat && !strcmp(compat, "pci108e,a001")) - hummingbird_p = 1; - if (!hummingbird_p) { - struct device_node *dp; - - /* Of course, Sun has to encode things a thousand - * different ways, inconsistently. - */ - for_each_node_by_type(dp, "cpu") { - if (!strcmp(dp->name, "SUNW,UltraSPARC-IIe")) - hummingbird_p = 1; - } + hummingbird_p = (match->data != NULL); + if (!hummingbird_p) { + struct device_node *cpu_dp; + + /* Of course, Sun has to encode things a thousand + * different ways, inconsistently. + */ + for_each_node_by_type(cpu_dp, "cpu") { + if (!strcmp(cpu_dp->name, "SUNW,UltraSPARC-IIe")) + hummingbird_p = 1; } } - p = kzalloc(sizeof(*p), GFP_ATOMIC); - if (!p) - goto fatal_memory_error; + err = -ENOMEM; + pbm = kzalloc(sizeof(*pbm), GFP_KERNEL); + if (!pbm) { + printk(KERN_ERR PFX "Cannot allocate pci_pbm_info.\n"); + goto out_err; + } + + iommu = kzalloc(sizeof(*iommu), GFP_KERNEL); + if (!iommu) { + printk(KERN_ERR PFX "Cannot allocate PBM iommu.\n"); + goto out_free_controller; + } - iommu = kzalloc(sizeof(*iommu), GFP_ATOMIC); - if (!iommu) - goto fatal_memory_error; - pbm = &p->pbm_A; pbm->iommu = iommu; upa_portid = of_getintprop_default(dp, "upa-portid", 0xff); - pbm->next = pci_pbm_root; - pci_pbm_root = pbm; - pbm->portid = upa_portid; /* @@ -806,6 +501,11 @@ void __init sabre_init(struct device_node *dp, char *model_name) */ pr_regs = of_get_property(dp, "reg", NULL); + err = -ENODEV; + if (!pr_regs) { + printk(KERN_ERR PFX "No reg property\n"); + goto out_free_iommu; + } /* * First REG in property is base of entire SABRE register space. @@ -816,22 +516,25 @@ void __init sabre_init(struct device_node *dp, char *model_name) /* PCI first */ for (clear_irq = SABRE_ICLR_A_SLOT0; clear_irq < SABRE_ICLR_B_SLOT0 + 0x80; clear_irq += 8) - sabre_write(pbm->controller_regs + clear_irq, 0x0UL); + upa_writeq(0x0UL, pbm->controller_regs + clear_irq); /* Then OBIO */ for (clear_irq = SABRE_ICLR_SCSI; clear_irq < SABRE_ICLR_SCSI + 0x80; clear_irq += 8) - sabre_write(pbm->controller_regs + clear_irq, 0x0UL); + upa_writeq(0x0UL, pbm->controller_regs + clear_irq); /* Error interrupts are enabled later after the bus scan. */ - sabre_write(pbm->controller_regs + SABRE_PCICTRL, - (SABRE_PCICTRL_MRLEN | SABRE_PCICTRL_SERR | - SABRE_PCICTRL_ARBPARK | SABRE_PCICTRL_AEN)); + upa_writeq((SABRE_PCICTRL_MRLEN | SABRE_PCICTRL_SERR | + SABRE_PCICTRL_ARBPARK | SABRE_PCICTRL_AEN), + pbm->controller_regs + SABRE_PCICTRL); /* Now map in PCI config space for entire SABRE. */ - pbm->config_space = - (pbm->controller_regs + SABRE_CONFIGSPACE); + pbm->config_space = pbm->controller_regs + SABRE_CONFIGSPACE; vdma = of_get_property(dp, "virtual-dma", NULL); + if (!vdma) { + printk(KERN_ERR PFX "No virtual-dma property\n"); + goto out_free_iommu; + } dma_mask = vdma[0]; switch(vdma[1]) { @@ -849,20 +552,58 @@ void __init sabre_init(struct device_node *dp, char *model_name) tsbsize = 128; break; default: - prom_printf("SABRE: strange virtual-dma size.\n"); - prom_halt(); + printk(KERN_ERR PFX "Strange virtual-dma size.\n"); + goto out_free_iommu; } - if (sabre_iommu_init(pbm, tsbsize, vdma[0], dma_mask)) - goto fatal_memory_error; + err = psycho_iommu_init(pbm, tsbsize, vdma[0], dma_mask, SABRE_WRSYNC); + if (err) + goto out_free_iommu; /* * Look for APB underneath. */ - sabre_pbm_init(p, pbm, dp); - return; + sabre_pbm_init(pbm, op); -fatal_memory_error: - prom_printf("SABRE: Fatal memory allocation error.\n"); - prom_halt(); + pbm->next = pci_pbm_root; + pci_pbm_root = pbm; + + dev_set_drvdata(&op->dev, pbm); + + return 0; + +out_free_iommu: + kfree(pbm->iommu); + +out_free_controller: + kfree(pbm); + +out_err: + return err; +} + +static struct of_device_id __initdata sabre_match[] = { + { + .name = "pci", + .compatible = "pci108e,a001", + .data = (void *) 1, + }, + { + .name = "pci", + .compatible = "pci108e,a000", + }, + {}, +}; + +static struct of_platform_driver sabre_driver = { + .name = DRIVER_NAME, + .match_table = sabre_match, + .probe = sabre_probe, +}; + +static int __init sabre_init(void) +{ + return of_register_driver(&sabre_driver, &of_bus_type); } + +subsys_initcall(sabre_init); diff --git a/arch/sparc64/kernel/pci_schizo.c b/arch/sparc64/kernel/pci_schizo.c index 9248c6737f0..45d9dba1ba1 100644 --- a/arch/sparc64/kernel/pci_schizo.c +++ b/arch/sparc64/kernel/pci_schizo.c @@ -1,6 +1,6 @@ /* pci_schizo.c: SCHIZO/TOMATILLO specific PCI controller support. * - * Copyright (C) 2001, 2002, 2003, 2007 David S. Miller (davem@davemloft.net) + * Copyright (C) 2001, 2002, 2003, 2007, 2008 David S. Miller (davem@davemloft.net) */ #include <linux/kernel.h> @@ -13,32 +13,15 @@ #include <asm/iommu.h> #include <asm/irq.h> -#include <asm/upa.h> #include <asm/pstate.h> #include <asm/prom.h> -#include <asm/oplib.h> +#include <asm/upa.h> #include "pci_impl.h" #include "iommu_common.h" -/* All SCHIZO registers are 64-bits. The following accessor - * routines are how they are accessed. The REG parameter - * is a physical address. - */ -#define schizo_read(__reg) \ -({ u64 __ret; \ - __asm__ __volatile__("ldxa [%1] %2, %0" \ - : "=r" (__ret) \ - : "r" (__reg), "i" (ASI_PHYS_BYPASS_EC_E) \ - : "memory"); \ - __ret; \ -}) -#define schizo_write(__reg, __val) \ - __asm__ __volatile__("stxa %0, [%1] %2" \ - : /* no outputs */ \ - : "r" (__val), "r" (__reg), \ - "i" (ASI_PHYS_BYPASS_EC_E) \ - : "memory") +#define DRIVER_NAME "schizo" +#define PFX DRIVER_NAME ": " /* This is a convention that at least Excalibur and Merlin * follow. I suppose the SCHIZO used in Starcat and friends @@ -163,25 +146,25 @@ static void __schizo_check_stc_error_pbm(struct pci_pbm_info *pbm, * invalidating it before it has a chance to reach * main memory. */ - control = schizo_read(strbuf->strbuf_control); - schizo_write(strbuf->strbuf_control, - (control | SCHIZO_STRBUF_CTRL_DENAB)); + control = upa_readq(strbuf->strbuf_control); + upa_writeq((control | SCHIZO_STRBUF_CTRL_DENAB), + strbuf->strbuf_control); for (i = 0; i < 128; i++) { unsigned long val; - val = schizo_read(err_base + (i * 8UL)); - schizo_write(err_base + (i * 8UL), 0UL); + val = upa_readq(err_base + (i * 8UL)); + upa_writeq(0UL, err_base + (i * 8UL)); stc_error_buf[i] = val; } for (i = 0; i < 16; i++) { - stc_tag_buf[i] = schizo_read(tag_base + (i * 8UL)); - stc_line_buf[i] = schizo_read(line_base + (i * 8UL)); - schizo_write(tag_base + (i * 8UL), 0UL); - schizo_write(line_base + (i * 8UL), 0UL); + stc_tag_buf[i] = upa_readq(tag_base + (i * 8UL)); + stc_line_buf[i] = upa_readq(line_base + (i * 8UL)); + upa_writeq(0UL, tag_base + (i * 8UL)); + upa_writeq(0UL, line_base + (i * 8UL)); } /* OK, state is logged, exit diagnostic mode. */ - schizo_write(strbuf->strbuf_control, control); + upa_writeq(control, strbuf->strbuf_control); for (i = 0; i < 16; i++) { int j, saw_error, first, last; @@ -258,14 +241,14 @@ static void schizo_check_iommu_error_pbm(struct pci_pbm_info *pbm, int i; spin_lock_irqsave(&iommu->lock, flags); - control = schizo_read(iommu->iommu_control); + control = upa_readq(iommu->iommu_control); if (control & SCHIZO_IOMMU_CTRL_XLTEERR) { unsigned long base; char *type_string; /* Clear the error encountered bit. */ control &= ~SCHIZO_IOMMU_CTRL_XLTEERR; - schizo_write(iommu->iommu_control, control); + upa_writeq(control, iommu->iommu_control); switch((control & SCHIZO_IOMMU_CTRL_XLTESTAT) >> 25UL) { case 0: @@ -295,24 +278,24 @@ static void schizo_check_iommu_error_pbm(struct pci_pbm_info *pbm, * get as much diagnostic information to the * console as we can. */ - schizo_write(iommu->iommu_control, - control | SCHIZO_IOMMU_CTRL_DENAB); + upa_writeq(control | SCHIZO_IOMMU_CTRL_DENAB, + iommu->iommu_control); base = pbm->pbm_regs; for (i = 0; i < 16; i++) { iommu_tag[i] = - schizo_read(base + SCHIZO_IOMMU_TAG + (i * 8UL)); + upa_readq(base + SCHIZO_IOMMU_TAG + (i * 8UL)); iommu_data[i] = - schizo_read(base + SCHIZO_IOMMU_DATA + (i * 8UL)); + upa_readq(base + SCHIZO_IOMMU_DATA + (i * 8UL)); /* Now clear out the entry. */ - schizo_write(base + SCHIZO_IOMMU_TAG + (i * 8UL), 0); - schizo_write(base + SCHIZO_IOMMU_DATA + (i * 8UL), 0); + upa_writeq(0, base + SCHIZO_IOMMU_TAG + (i * 8UL)); + upa_writeq(0, base + SCHIZO_IOMMU_DATA + (i * 8UL)); } /* Leave diagnostic mode. */ - schizo_write(iommu->iommu_control, control); + upa_writeq(control, iommu->iommu_control); for (i = 0; i < 16; i++) { unsigned long tag, data; @@ -357,11 +340,12 @@ static void schizo_check_iommu_error_pbm(struct pci_pbm_info *pbm, spin_unlock_irqrestore(&iommu->lock, flags); } -static void schizo_check_iommu_error(struct pci_controller_info *p, +static void schizo_check_iommu_error(struct pci_pbm_info *pbm, enum schizo_error_type type) { - schizo_check_iommu_error_pbm(&p->pbm_A, type); - schizo_check_iommu_error_pbm(&p->pbm_B, type); + schizo_check_iommu_error_pbm(pbm, type); + if (pbm->sibling) + schizo_check_iommu_error_pbm(pbm->sibling, type); } /* Uncorrectable ECC error status gathering. */ @@ -386,14 +370,13 @@ static void schizo_check_iommu_error(struct pci_controller_info *p, static irqreturn_t schizo_ue_intr(int irq, void *dev_id) { struct pci_pbm_info *pbm = dev_id; - struct pci_controller_info *p = pbm->parent; unsigned long afsr_reg = pbm->controller_regs + SCHIZO_UE_AFSR; unsigned long afar_reg = pbm->controller_regs + SCHIZO_UE_AFAR; unsigned long afsr, afar, error_bits; int reported, limit; /* Latch uncorrectable error status. */ - afar = schizo_read(afar_reg); + afar = upa_readq(afar_reg); /* If either of the error pending bits are set in the * AFSR, the error status is being actively updated by @@ -401,7 +384,7 @@ static irqreturn_t schizo_ue_intr(int irq, void *dev_id) */ limit = 1000; do { - afsr = schizo_read(afsr_reg); + afsr = upa_readq(afsr_reg); } while ((afsr & SCHIZO_UEAFSR_ERRPNDG) != 0 && --limit); /* Clear the primary/secondary error status bits. */ @@ -410,7 +393,7 @@ static irqreturn_t schizo_ue_intr(int irq, void *dev_id) SCHIZO_UEAFSR_SPIO | SCHIZO_UEAFSR_SDMA); if (!error_bits) return IRQ_NONE; - schizo_write(afsr_reg, error_bits); + upa_writeq(error_bits, afsr_reg); /* Log the error. */ printk("%s: Uncorrectable Error, primary error type[%s]\n", @@ -449,7 +432,7 @@ static irqreturn_t schizo_ue_intr(int irq, void *dev_id) printk("]\n"); /* Interrogate IOMMU for error status. */ - schizo_check_iommu_error(p, UE_ERR); + schizo_check_iommu_error(pbm, UE_ERR); return IRQ_HANDLED; } @@ -481,7 +464,7 @@ static irqreturn_t schizo_ce_intr(int irq, void *dev_id) int reported, limit; /* Latch error status. */ - afar = schizo_read(afar_reg); + afar = upa_readq(afar_reg); /* If either of the error pending bits are set in the * AFSR, the error status is being actively updated by @@ -489,7 +472,7 @@ static irqreturn_t schizo_ce_intr(int irq, void *dev_id) */ limit = 1000; do { - afsr = schizo_read(afsr_reg); + afsr = upa_readq(afsr_reg); } while ((afsr & SCHIZO_UEAFSR_ERRPNDG) != 0 && --limit); /* Clear primary/secondary error status bits. */ @@ -498,7 +481,7 @@ static irqreturn_t schizo_ce_intr(int irq, void *dev_id) SCHIZO_CEAFSR_SPIO | SCHIZO_CEAFSR_SDMA); if (!error_bits) return IRQ_NONE; - schizo_write(afsr_reg, error_bits); + upa_writeq(error_bits, afsr_reg); /* Log the error. */ printk("%s: Correctable Error, primary error type[%s]\n", @@ -600,7 +583,7 @@ static irqreturn_t schizo_pcierr_intr_other(struct pci_pbm_info *pbm) u16 stat; csr_reg = pbm->pbm_regs + SCHIZO_PCI_CTRL; - csr = schizo_read(csr_reg); + csr = upa_readq(csr_reg); csr_error_bits = csr & (SCHIZO_PCICTRL_BUS_UNUS | SCHIZO_PCICTRL_TTO_ERR | @@ -610,7 +593,7 @@ static irqreturn_t schizo_pcierr_intr_other(struct pci_pbm_info *pbm) SCHIZO_PCICTRL_SERR); if (csr_error_bits) { /* Clear the errors. */ - schizo_write(csr_reg, csr); + upa_writeq(csr, csr_reg); /* Log 'em. */ if (csr_error_bits & SCHIZO_PCICTRL_BUS_UNUS) @@ -650,7 +633,6 @@ static irqreturn_t schizo_pcierr_intr_other(struct pci_pbm_info *pbm) static irqreturn_t schizo_pcierr_intr(int irq, void *dev_id) { struct pci_pbm_info *pbm = dev_id; - struct pci_controller_info *p = pbm->parent; unsigned long afsr_reg, afar_reg, base; unsigned long afsr, afar, error_bits; int reported; @@ -661,8 +643,8 @@ static irqreturn_t schizo_pcierr_intr(int irq, void *dev_id) afar_reg = base + SCHIZO_PCI_AFAR; /* Latch error status. */ - afar = schizo_read(afar_reg); - afsr = schizo_read(afsr_reg); + afar = upa_readq(afar_reg); + afsr = upa_readq(afsr_reg); /* Clear primary/secondary error status bits. */ error_bits = afsr & @@ -674,7 +656,7 @@ static irqreturn_t schizo_pcierr_intr(int irq, void *dev_id) SCHIZO_PCIAFSR_STTO | SCHIZO_PCIAFSR_SUNUS); if (!error_bits) return schizo_pcierr_intr_other(pbm); - schizo_write(afsr_reg, error_bits); + upa_writeq(error_bits, afsr_reg); /* Log the error. */ printk("%s: PCI Error, primary error type[%s]\n", @@ -744,7 +726,7 @@ static irqreturn_t schizo_pcierr_intr(int irq, void *dev_id) * a bug in the IOMMU support code or a PCI device driver. */ if (error_bits & (SCHIZO_PCIAFSR_PTA | SCHIZO_PCIAFSR_STA)) { - schizo_check_iommu_error(p, PCI_ERR); + schizo_check_iommu_error(pbm, PCI_ERR); pci_scan_for_target_abort(pbm, pbm->pci_bus); } if (error_bits & (SCHIZO_PCIAFSR_PMA | SCHIZO_PCIAFSR_SMA)) @@ -805,12 +787,11 @@ static irqreturn_t schizo_pcierr_intr(int irq, void *dev_id) static irqreturn_t schizo_safarierr_intr(int irq, void *dev_id) { struct pci_pbm_info *pbm = dev_id; - struct pci_controller_info *p = pbm->parent; u64 errlog; - errlog = schizo_read(pbm->controller_regs + SCHIZO_SAFARI_ERRLOG); - schizo_write(pbm->controller_regs + SCHIZO_SAFARI_ERRLOG, - errlog & ~(SAFARI_ERRLOG_ERROUT)); + errlog = upa_readq(pbm->controller_regs + SCHIZO_SAFARI_ERRLOG); + upa_writeq(errlog & ~(SAFARI_ERRLOG_ERROUT), + pbm->controller_regs + SCHIZO_SAFARI_ERRLOG); if (!(errlog & BUS_ERROR_UNMAP)) { printk("%s: Unexpected Safari/JBUS error interrupt, errlog[%016lx]\n", @@ -821,7 +802,7 @@ static irqreturn_t schizo_safarierr_intr(int irq, void *dev_id) printk("%s: Safari/JBUS interrupt, UNMAPPED error, interrogating IOMMUs.\n", pbm->name); - schizo_check_iommu_error(p, SAFARI_ERR); + schizo_check_iommu_error(pbm, SAFARI_ERR); return IRQ_HANDLED; } @@ -863,7 +844,7 @@ static int pbm_routes_this_ino(struct pci_pbm_info *pbm, u32 ino) */ static void tomatillo_register_error_handlers(struct pci_pbm_info *pbm) { - struct of_device *op = of_find_device_by_node(pbm->prom_node); + struct of_device *op = of_find_device_by_node(pbm->op->node); u64 tmp, err_mask, err_no_mask; int err; @@ -910,10 +891,9 @@ static void tomatillo_register_error_handlers(struct pci_pbm_info *pbm) } /* Enable UE and CE interrupts for controller. */ - schizo_write(pbm->controller_regs + SCHIZO_ECC_CTRL, - (SCHIZO_ECCCTRL_EE | - SCHIZO_ECCCTRL_UE | - SCHIZO_ECCCTRL_CE)); + upa_writeq((SCHIZO_ECCCTRL_EE | + SCHIZO_ECCCTRL_UE | + SCHIZO_ECCCTRL_CE), pbm->controller_regs + SCHIZO_ECC_CTRL); /* Enable PCI Error interrupts and clear error * bits. @@ -926,10 +906,10 @@ static void tomatillo_register_error_handlers(struct pci_pbm_info *pbm) err_no_mask = SCHIZO_PCICTRL_DTO_ERR; - tmp = schizo_read(pbm->pbm_regs + SCHIZO_PCI_CTRL); + tmp = upa_readq(pbm->pbm_regs + SCHIZO_PCI_CTRL); tmp |= err_mask; tmp &= ~err_no_mask; - schizo_write(pbm->pbm_regs + SCHIZO_PCI_CTRL, tmp); + upa_writeq(tmp, pbm->pbm_regs + SCHIZO_PCI_CTRL); err_mask = (SCHIZO_PCIAFSR_PMA | SCHIZO_PCIAFSR_PTA | SCHIZO_PCIAFSR_PRTRY | SCHIZO_PCIAFSR_PPERR | @@ -938,7 +918,7 @@ static void tomatillo_register_error_handlers(struct pci_pbm_info *pbm) SCHIZO_PCIAFSR_SRTRY | SCHIZO_PCIAFSR_SPERR | SCHIZO_PCIAFSR_STTO); - schizo_write(pbm->pbm_regs + SCHIZO_PCI_AFSR, err_mask); + upa_writeq(err_mask, pbm->pbm_regs + SCHIZO_PCI_AFSR); err_mask = (BUS_ERROR_BADCMD | BUS_ERROR_SNOOP_GR | BUS_ERROR_SNOOP_PCI | BUS_ERROR_SNOOP_RD | @@ -950,16 +930,16 @@ static void tomatillo_register_error_handlers(struct pci_pbm_info *pbm) BUS_ERROR_APERR | BUS_ERROR_UNMAP | BUS_ERROR_BUSERR | BUS_ERROR_TIMEOUT); - schizo_write(pbm->controller_regs + SCHIZO_SAFARI_ERRCTRL, - (SCHIZO_SAFERRCTRL_EN | err_mask)); + upa_writeq((SCHIZO_SAFERRCTRL_EN | err_mask), + pbm->controller_regs + SCHIZO_SAFARI_ERRCTRL); - schizo_write(pbm->controller_regs + SCHIZO_SAFARI_IRQCTRL, - (SCHIZO_SAFIRQCTRL_EN | (BUS_ERROR_UNMAP))); + upa_writeq((SCHIZO_SAFIRQCTRL_EN | (BUS_ERROR_UNMAP)), + pbm->controller_regs + SCHIZO_SAFARI_IRQCTRL); } static void schizo_register_error_handlers(struct pci_pbm_info *pbm) { - struct of_device *op = of_find_device_by_node(pbm->prom_node); + struct of_device *op = of_find_device_by_node(pbm->op->node); u64 tmp, err_mask, err_no_mask; int err; @@ -1006,10 +986,9 @@ static void schizo_register_error_handlers(struct pci_pbm_info *pbm) } /* Enable UE and CE interrupts for controller. */ - schizo_write(pbm->controller_regs + SCHIZO_ECC_CTRL, - (SCHIZO_ECCCTRL_EE | - SCHIZO_ECCCTRL_UE | - SCHIZO_ECCCTRL_CE)); + upa_writeq((SCHIZO_ECCCTRL_EE | + SCHIZO_ECCCTRL_UE | + SCHIZO_ECCCTRL_CE), pbm->controller_regs + SCHIZO_ECC_CTRL); err_mask = (SCHIZO_PCICTRL_BUS_UNUS | SCHIZO_PCICTRL_ESLCK | @@ -1025,18 +1004,18 @@ static void schizo_register_error_handlers(struct pci_pbm_info *pbm) /* Enable PCI Error interrupts and clear error * bits for each PBM. */ - tmp = schizo_read(pbm->pbm_regs + SCHIZO_PCI_CTRL); + tmp = upa_readq(pbm->pbm_regs + SCHIZO_PCI_CTRL); tmp |= err_mask; tmp &= ~err_no_mask; - schizo_write(pbm->pbm_regs + SCHIZO_PCI_CTRL, tmp); + upa_writeq(tmp, pbm->pbm_regs + SCHIZO_PCI_CTRL); - schizo_write(pbm->pbm_regs + SCHIZO_PCI_AFSR, - (SCHIZO_PCIAFSR_PMA | SCHIZO_PCIAFSR_PTA | - SCHIZO_PCIAFSR_PRTRY | SCHIZO_PCIAFSR_PPERR | - SCHIZO_PCIAFSR_PTTO | SCHIZO_PCIAFSR_PUNUS | - SCHIZO_PCIAFSR_SMA | SCHIZO_PCIAFSR_STA | - SCHIZO_PCIAFSR_SRTRY | SCHIZO_PCIAFSR_SPERR | - SCHIZO_PCIAFSR_STTO | SCHIZO_PCIAFSR_SUNUS)); + upa_writeq((SCHIZO_PCIAFSR_PMA | SCHIZO_PCIAFSR_PTA | + SCHIZO_PCIAFSR_PRTRY | SCHIZO_PCIAFSR_PPERR | + SCHIZO_PCIAFSR_PTTO | SCHIZO_PCIAFSR_PUNUS | + SCHIZO_PCIAFSR_SMA | SCHIZO_PCIAFSR_STA | + SCHIZO_PCIAFSR_SRTRY | SCHIZO_PCIAFSR_SPERR | + SCHIZO_PCIAFSR_STTO | SCHIZO_PCIAFSR_SUNUS), + pbm->pbm_regs + SCHIZO_PCI_AFSR); /* Make all Safari error conditions fatal except unmapped * errors which we make generate interrupts. @@ -1063,8 +1042,8 @@ static void schizo_register_error_handlers(struct pci_pbm_info *pbm) BUS_ERROR_CPU0PS | BUS_ERROR_CPU0PB); #endif - schizo_write(pbm->controller_regs + SCHIZO_SAFARI_ERRCTRL, - (SCHIZO_SAFERRCTRL_EN | err_mask)); + upa_writeq((SCHIZO_SAFERRCTRL_EN | err_mask), + pbm->controller_regs + SCHIZO_SAFARI_ERRCTRL); } static void pbm_config_busmastering(struct pci_pbm_info *pbm) @@ -1084,14 +1063,15 @@ static void pbm_config_busmastering(struct pci_pbm_info *pbm) pci_config_write8(addr, 64); } -static void __init schizo_scan_bus(struct pci_pbm_info *pbm) +static void __devinit schizo_scan_bus(struct pci_pbm_info *pbm, + struct device *parent) { pbm_config_busmastering(pbm); pbm->is_66mhz_capable = - (of_find_property(pbm->prom_node, "66mhz-capable", NULL) + (of_find_property(pbm->op->node, "66mhz-capable", NULL) != NULL); - pbm->pci_bus = pci_scan_one_pbm(pbm); + pbm->pci_bus = pci_scan_one_pbm(pbm, parent); if (pbm->chip_type == PBM_CHIP_TYPE_TOMATILLO) tomatillo_register_error_handlers(pbm); @@ -1133,12 +1113,12 @@ static void schizo_pbm_strbuf_init(struct pci_pbm_info *pbm) * streaming buffer and leave the rerun-disable * setting however OBP set it. */ - control = schizo_read(pbm->stc.strbuf_control); + control = upa_readq(pbm->stc.strbuf_control); control &= ~(SCHIZO_STRBUF_CTRL_LPTR | SCHIZO_STRBUF_CTRL_LENAB | SCHIZO_STRBUF_CTRL_DENAB); control |= SCHIZO_STRBUF_CTRL_ENAB; - schizo_write(pbm->stc.strbuf_control, control); + upa_writeq(control, pbm->stc.strbuf_control); pbm->stc.strbuf_enabled = 1; } @@ -1150,24 +1130,17 @@ static void schizo_pbm_strbuf_init(struct pci_pbm_info *pbm) static int schizo_pbm_iommu_init(struct pci_pbm_info *pbm) { - struct iommu *iommu = pbm->iommu; + static const u32 vdma_default[] = { 0xc0000000, 0x40000000 }; unsigned long i, tagbase, database; - struct property *prop; - u32 vdma[2], dma_mask; + struct iommu *iommu = pbm->iommu; int tsbsize, err; + const u32 *vdma; + u32 dma_mask; u64 control; - prop = of_find_property(pbm->prom_node, "virtual-dma", NULL); - if (prop) { - u32 *val = prop->value; - - vdma[0] = val[0]; - vdma[1] = val[1]; - } else { - /* No property, use default values. */ - vdma[0] = 0xc0000000; - vdma[1] = 0x40000000; - } + vdma = of_get_property(pbm->op->node, "virtual-dma", NULL); + if (!vdma) + vdma = vdma_default; dma_mask = vdma[0]; switch (vdma[1]) { @@ -1187,9 +1160,9 @@ static int schizo_pbm_iommu_init(struct pci_pbm_info *pbm) break; default: - prom_printf("SCHIZO: strange virtual-dma size.\n"); - prom_halt(); - }; + printk(KERN_ERR PFX "Strange virtual-dma size.\n"); + return -EINVAL; + } /* Register addresses, SCHIZO has iommu ctx flushing. */ iommu->iommu_control = pbm->pbm_regs + SCHIZO_IOMMU_CONTROL; @@ -1206,15 +1179,15 @@ static int schizo_pbm_iommu_init(struct pci_pbm_info *pbm) /* * Invalidate TLB Entries. */ - control = schizo_read(iommu->iommu_control); + control = upa_readq(iommu->iommu_control); control |= SCHIZO_IOMMU_CTRL_DENAB; - schizo_write(iommu->iommu_control, control); + upa_writeq(control, iommu->iommu_control); tagbase = SCHIZO_IOMMU_TAG, database = SCHIZO_IOMMU_DATA; - for(i = 0; i < 16; i++) { - schizo_write(pbm->pbm_regs + tagbase + (i * 8UL), 0); - schizo_write(pbm->pbm_regs + database + (i * 8UL), 0); + for (i = 0; i < 16; i++) { + upa_writeq(0, pbm->pbm_regs + tagbase + (i * 8UL)); + upa_writeq(0, pbm->pbm_regs + database + (i * 8UL)); } /* Leave diag mode enabled for full-flushing done @@ -1222,12 +1195,14 @@ static int schizo_pbm_iommu_init(struct pci_pbm_info *pbm) */ err = iommu_table_init(iommu, tsbsize * 8 * 1024, vdma[0], dma_mask, pbm->numa_node); - if (err) + if (err) { + printk(KERN_ERR PFX "iommu_table_init() fails with %d\n", err); return err; + } - schizo_write(iommu->iommu_tsbbase, __pa(iommu->page_table)); + upa_writeq(__pa(iommu->page_table), iommu->iommu_tsbbase); - control = schizo_read(iommu->iommu_control); + control = upa_readq(iommu->iommu_control); control &= ~(SCHIZO_IOMMU_CTRL_TSBSZ | SCHIZO_IOMMU_CTRL_TBWSZ); switch (tsbsize) { case 64: @@ -1236,10 +1211,10 @@ static int schizo_pbm_iommu_init(struct pci_pbm_info *pbm) case 128: control |= SCHIZO_IOMMU_TSBSZ_128K; break; - }; + } control |= SCHIZO_IOMMU_CTRL_ENAB; - schizo_write(iommu->iommu_control, control); + upa_writeq(control, iommu->iommu_control); return 0; } @@ -1280,12 +1255,11 @@ static int schizo_pbm_iommu_init(struct pci_pbm_info *pbm) static void schizo_pbm_hw_init(struct pci_pbm_info *pbm) { - struct property *prop; u64 tmp; - schizo_write(pbm->pbm_regs + SCHIZO_PCI_IRQ_RETRY, 5); + upa_writeq(5, pbm->pbm_regs + SCHIZO_PCI_IRQ_RETRY); - tmp = schizo_read(pbm->pbm_regs + SCHIZO_PCI_CTRL); + tmp = upa_readq(pbm->pbm_regs + SCHIZO_PCI_CTRL); /* Enable arbiter for all PCI slots. */ tmp |= 0xff; @@ -1294,8 +1268,7 @@ static void schizo_pbm_hw_init(struct pci_pbm_info *pbm) pbm->chip_version >= 0x2) tmp |= 0x3UL << SCHIZO_PCICTRL_PTO_SHIFT; - prop = of_find_property(pbm->prom_node, "no-bus-parking", NULL); - if (!prop) + if (!of_find_property(pbm->op->node, "no-bus-parking", NULL)) tmp |= SCHIZO_PCICTRL_PARK; else tmp &= ~SCHIZO_PCICTRL_PARK; @@ -1311,13 +1284,13 @@ static void schizo_pbm_hw_init(struct pci_pbm_info *pbm) SCHIZO_PCICTRL_RDO_PREF | SCHIZO_PCICTRL_RDL_PREF); - schizo_write(pbm->pbm_regs + SCHIZO_PCI_CTRL, tmp); + upa_writeq(tmp, pbm->pbm_regs + SCHIZO_PCI_CTRL); - tmp = schizo_read(pbm->pbm_regs + SCHIZO_PCI_DIAG); + tmp = upa_readq(pbm->pbm_regs + SCHIZO_PCI_DIAG); tmp &= ~(SCHIZO_PCIDIAG_D_RTRYARB | SCHIZO_PCIDIAG_D_RETRY | SCHIZO_PCIDIAG_D_INTSYNC); - schizo_write(pbm->pbm_regs + SCHIZO_PCI_DIAG, tmp); + upa_writeq(tmp, pbm->pbm_regs + SCHIZO_PCI_DIAG); if (pbm->chip_type == PBM_CHIP_TYPE_TOMATILLO) { /* Clear prefetch lengths to workaround a bug in @@ -1329,17 +1302,16 @@ static void schizo_pbm_hw_init(struct pci_pbm_info *pbm) TOMATILLO_IOC_RDONE_CPENAB | TOMATILLO_IOC_RDLINE_CPENAB); - schizo_write(pbm->pbm_regs + TOMATILLO_PCI_IOC_CSR, - tmp); + upa_writeq(tmp, pbm->pbm_regs + TOMATILLO_PCI_IOC_CSR); } } -static int __init schizo_pbm_init(struct pci_controller_info *p, - struct device_node *dp, u32 portid, - int chip_type) +static int __devinit schizo_pbm_init(struct pci_pbm_info *pbm, + struct of_device *op, u32 portid, + int chip_type) { const struct linux_prom64_registers *regs; - struct pci_pbm_info *pbm; + struct device_node *dp = op->node; const char *chipset_name; int is_pbm_a, err; @@ -1372,25 +1344,19 @@ static int __init schizo_pbm_init(struct pci_controller_info *p, regs = of_get_property(dp, "reg", NULL); is_pbm_a = ((regs[0].phys_addr & 0x00700000) == 0x00600000); - if (is_pbm_a) - pbm = &p->pbm_A; - else - pbm = &p->pbm_B; pbm->next = pci_pbm_root; pci_pbm_root = pbm; pbm->numa_node = -1; - pbm->scan_bus = schizo_scan_bus; pbm->pci_ops = &sun4u_pci_ops; pbm->config_space_reg_bits = 8; pbm->index = pci_num_pbms++; pbm->portid = portid; - pbm->parent = p; - pbm->prom_node = dp; + pbm->op = op; pbm->chip_type = chip_type; pbm->chip_version = of_getintprop_default(dp, "version#", 0); @@ -1420,6 +1386,8 @@ static int __init schizo_pbm_init(struct pci_controller_info *p, schizo_pbm_strbuf_init(pbm); + schizo_scan_bus(pbm, &op->dev); + return 0; } @@ -1433,62 +1401,104 @@ static inline int portid_compare(u32 x, u32 y, int chip_type) return (x == y); } -static void __init __schizo_init(struct device_node *dp, char *model_name, - int chip_type) +static struct pci_pbm_info * __devinit schizo_find_sibling(u32 portid, + int chip_type) { - struct pci_controller_info *p; + struct pci_pbm_info *pbm; + + for (pbm = pci_pbm_root; pbm; pbm = pbm->next) { + if (portid_compare(pbm->portid, portid, chip_type)) + return pbm; + } + return NULL; +} + +static int __devinit __schizo_init(struct of_device *op, unsigned long chip_type) +{ + struct device_node *dp = op->node; struct pci_pbm_info *pbm; struct iommu *iommu; u32 portid; + int err; portid = of_getintprop_default(dp, "portid", 0xff); - for (pbm = pci_pbm_root; pbm; pbm = pbm->next) { - if (portid_compare(pbm->portid, portid, chip_type)) { - if (schizo_pbm_init(pbm->parent, dp, - portid, chip_type)) - goto fatal_memory_error; - return; - } + err = -ENOMEM; + pbm = kzalloc(sizeof(*pbm), GFP_KERNEL); + if (!pbm) { + printk(KERN_ERR PFX "Cannot allocate pci_pbm_info.\n"); + goto out_err; + } + + pbm->sibling = schizo_find_sibling(portid, chip_type); + + iommu = kzalloc(sizeof(struct iommu), GFP_KERNEL); + if (!iommu) { + printk(KERN_ERR PFX "Cannot allocate PBM A iommu.\n"); + goto out_free_pbm; } - p = kzalloc(sizeof(struct pci_controller_info), GFP_ATOMIC); - if (!p) - goto fatal_memory_error; + pbm->iommu = iommu; - iommu = kzalloc(sizeof(struct iommu), GFP_ATOMIC); - if (!iommu) - goto fatal_memory_error; + if (schizo_pbm_init(pbm, op, portid, chip_type)) + goto out_free_iommu; - p->pbm_A.iommu = iommu; + if (pbm->sibling) + pbm->sibling->sibling = pbm; - iommu = kzalloc(sizeof(struct iommu), GFP_ATOMIC); - if (!iommu) - goto fatal_memory_error; + dev_set_drvdata(&op->dev, pbm); - p->pbm_B.iommu = iommu; + return 0; - if (schizo_pbm_init(p, dp, portid, chip_type)) - goto fatal_memory_error; +out_free_iommu: + kfree(pbm->iommu); - return; +out_free_pbm: + kfree(pbm); -fatal_memory_error: - prom_printf("SCHIZO: Fatal memory allocation error.\n"); - prom_halt(); +out_err: + return err; } -void __init schizo_init(struct device_node *dp, char *model_name) +static int __devinit schizo_probe(struct of_device *op, + const struct of_device_id *match) { - __schizo_init(dp, model_name, PBM_CHIP_TYPE_SCHIZO); + return __schizo_init(op, (unsigned long) match->data); } -void __init schizo_plus_init(struct device_node *dp, char *model_name) -{ - __schizo_init(dp, model_name, PBM_CHIP_TYPE_SCHIZO_PLUS); -} +/* The ordering of this table is very important. Some Tomatillo + * nodes announce that they are compatible with both pci108e,a801 + * and pci108e,8001. So list the chips in reverse chronological + * order. + */ +static struct of_device_id __initdata schizo_match[] = { + { + .name = "pci", + .compatible = "pci108e,a801", + .data = (void *) PBM_CHIP_TYPE_TOMATILLO, + }, + { + .name = "pci", + .compatible = "pci108e,8002", + .data = (void *) PBM_CHIP_TYPE_SCHIZO_PLUS, + }, + { + .name = "pci", + .compatible = "pci108e,8001", + .data = (void *) PBM_CHIP_TYPE_SCHIZO, + }, + {}, +}; -void __init tomatillo_init(struct device_node *dp, char *model_name) +static struct of_platform_driver schizo_driver = { + .name = DRIVER_NAME, + .match_table = schizo_match, + .probe = schizo_probe, +}; + +static int __init schizo_init(void) { - __schizo_init(dp, model_name, PBM_CHIP_TYPE_TOMATILLO); + return of_register_driver(&schizo_driver, &of_bus_type); } + +subsys_initcall(schizo_init); diff --git a/arch/sparc64/kernel/pci_sun4v.c b/arch/sparc64/kernel/pci_sun4v.c index a104c80d319..e86c73ec167 100644 --- a/arch/sparc64/kernel/pci_sun4v.c +++ b/arch/sparc64/kernel/pci_sun4v.c @@ -13,12 +13,10 @@ #include <linux/irq.h> #include <linux/msi.h> #include <linux/log2.h> +#include <linux/of_device.h> #include <asm/iommu.h> #include <asm/irq.h> -#include <asm/upa.h> -#include <asm/pstate.h> -#include <asm/oplib.h> #include <asm/hypervisor.h> #include <asm/prom.h> @@ -27,6 +25,9 @@ #include "pci_sun4v.h" +#define DRIVER_NAME "pci_sun4v" +#define PFX DRIVER_NAME ": " + static unsigned long vpci_major = 1; static unsigned long vpci_minor = 1; @@ -41,6 +42,7 @@ struct iommu_batch { }; static DEFINE_PER_CPU(struct iommu_batch, iommu_batch); +static int iommu_batch_initialized; /* Interrupts must be disabled. */ static inline void iommu_batch_start(struct device *dev, unsigned long prot, unsigned long entry) @@ -542,15 +544,16 @@ static const struct dma_ops sun4v_dma_ops = { .sync_sg_for_cpu = dma_4v_sync_sg_for_cpu, }; -static void __init pci_sun4v_scan_bus(struct pci_pbm_info *pbm) +static void __init pci_sun4v_scan_bus(struct pci_pbm_info *pbm, + struct device *parent) { struct property *prop; struct device_node *dp; - dp = pbm->prom_node; + dp = pbm->op->node; prop = of_find_property(dp, "66mhz-capable", NULL); pbm->is_66mhz_capable = (prop != NULL); - pbm->pci_bus = pci_scan_one_pbm(pbm); + pbm->pci_bus = pci_scan_one_pbm(pbm, parent); /* XXX register error interrupt handlers XXX */ } @@ -583,29 +586,22 @@ static unsigned long __init probe_existing_entries(struct pci_pbm_info *pbm, return cnt; } -static void __init pci_sun4v_iommu_init(struct pci_pbm_info *pbm) +static int __init pci_sun4v_iommu_init(struct pci_pbm_info *pbm) { + static const u32 vdma_default[] = { 0x80000000, 0x80000000 }; struct iommu *iommu = pbm->iommu; - struct property *prop; unsigned long num_tsb_entries, sz, tsbsize; - u32 vdma[2], dma_mask, dma_offset; - - prop = of_find_property(pbm->prom_node, "virtual-dma", NULL); - if (prop) { - u32 *val = prop->value; - - vdma[0] = val[0]; - vdma[1] = val[1]; - } else { - /* No property, use default values. */ - vdma[0] = 0x80000000; - vdma[1] = 0x80000000; - } + u32 dma_mask, dma_offset; + const u32 *vdma; + + vdma = of_get_property(pbm->op->node, "virtual-dma", NULL); + if (!vdma) + vdma = vdma_default; if ((vdma[0] | vdma[1]) & ~IO_PAGE_MASK) { - prom_printf("PCI-SUN4V: strange virtual-dma[%08x:%08x].\n", - vdma[0], vdma[1]); - prom_halt(); + printk(KERN_ERR PFX "Strange virtual-dma[%08x:%08x].\n", + vdma[0], vdma[1]); + return -EINVAL; }; dma_mask = (roundup_pow_of_two(vdma[1]) - 1UL); @@ -625,8 +621,8 @@ static void __init pci_sun4v_iommu_init(struct pci_pbm_info *pbm) sz = (sz + 7UL) & ~7UL; iommu->arena.map = kzalloc(sz, GFP_KERNEL); if (!iommu->arena.map) { - prom_printf("PCI_IOMMU: Error, kmalloc(arena.map) failed.\n"); - prom_halt(); + printk(KERN_ERR PFX "Error, kmalloc(arena.map) failed.\n"); + return -ENOMEM; } iommu->arena.limit = num_tsb_entries; @@ -634,6 +630,8 @@ static void __init pci_sun4v_iommu_init(struct pci_pbm_info *pbm) if (sz) printk("%s: Imported %lu TSB entries from OBP\n", pbm->name, sz); + + return 0; } #ifdef CONFIG_PCI_MSI @@ -890,29 +888,20 @@ static void pci_sun4v_msi_init(struct pci_pbm_info *pbm) } #endif /* !(CONFIG_PCI_MSI) */ -static void __init pci_sun4v_pbm_init(struct pci_controller_info *p, - struct device_node *dp, u32 devhandle) +static int __init pci_sun4v_pbm_init(struct pci_pbm_info *pbm, + struct of_device *op, u32 devhandle) { - struct pci_pbm_info *pbm; - - if (devhandle & 0x40) - pbm = &p->pbm_B; - else - pbm = &p->pbm_A; - - pbm->next = pci_pbm_root; - pci_pbm_root = pbm; + struct device_node *dp = op->node; + int err; pbm->numa_node = of_node_to_nid(dp); - pbm->scan_bus = pci_sun4v_scan_bus; pbm->pci_ops = &sun4v_pci_ops; pbm->config_space_reg_bits = 12; pbm->index = pci_num_pbms++; - pbm->parent = p; - pbm->prom_node = dp; + pbm->op = op; pbm->devhandle = devhandle; @@ -924,82 +913,120 @@ static void __init pci_sun4v_pbm_init(struct pci_controller_info *p, pci_determine_mem_io_space(pbm); pci_get_pbm_props(pbm); - pci_sun4v_iommu_init(pbm); + + err = pci_sun4v_iommu_init(pbm); + if (err) + return err; + pci_sun4v_msi_init(pbm); + + pci_sun4v_scan_bus(pbm, &op->dev); + + pbm->next = pci_pbm_root; + pci_pbm_root = pbm; + + return 0; } -void __init sun4v_pci_init(struct device_node *dp, char *model_name) +static int __devinit pci_sun4v_probe(struct of_device *op, + const struct of_device_id *match) { + const struct linux_prom64_registers *regs; static int hvapi_negotiated = 0; - struct pci_controller_info *p; struct pci_pbm_info *pbm; + struct device_node *dp; struct iommu *iommu; - struct property *prop; - struct linux_prom64_registers *regs; u32 devhandle; - int i; + int i, err; + + dp = op->node; if (!hvapi_negotiated++) { - int err = sun4v_hvapi_register(HV_GRP_PCI, - vpci_major, - &vpci_minor); + err = sun4v_hvapi_register(HV_GRP_PCI, + vpci_major, + &vpci_minor); if (err) { - prom_printf("SUN4V_PCI: Could not register hvapi, " - "err=%d\n", err); - prom_halt(); + printk(KERN_ERR PFX "Could not register hvapi, " + "err=%d\n", err); + return err; } - printk("SUN4V_PCI: Registered hvapi major[%lu] minor[%lu]\n", + printk(KERN_INFO PFX "Registered hvapi major[%lu] minor[%lu]\n", vpci_major, vpci_minor); dma_ops = &sun4v_dma_ops; } - prop = of_find_property(dp, "reg", NULL); - if (!prop) { - prom_printf("SUN4V_PCI: Could not find config registers\n"); - prom_halt(); + regs = of_get_property(dp, "reg", NULL); + err = -ENODEV; + if (!regs) { + printk(KERN_ERR PFX "Could not find config registers\n"); + goto out_err; } - regs = prop->value; - devhandle = (regs->phys_addr >> 32UL) & 0x0fffffff; - for (pbm = pci_pbm_root; pbm; pbm = pbm->next) { - if (pbm->devhandle == (devhandle ^ 0x40)) { - pci_sun4v_pbm_init(pbm->parent, dp, devhandle); - return; + err = -ENOMEM; + if (!iommu_batch_initialized) { + for_each_possible_cpu(i) { + unsigned long page = get_zeroed_page(GFP_KERNEL); + + if (!page) + goto out_err; + + per_cpu(iommu_batch, i).pglist = (u64 *) page; } + iommu_batch_initialized = 1; } - for_each_possible_cpu(i) { - unsigned long page = get_zeroed_page(GFP_ATOMIC); - - if (!page) - goto fatal_memory_error; + pbm = kzalloc(sizeof(*pbm), GFP_KERNEL); + if (!pbm) { + printk(KERN_ERR PFX "Could not allocate pci_pbm_info\n"); + goto out_err; + } - per_cpu(iommu_batch, i).pglist = (u64 *) page; + iommu = kzalloc(sizeof(struct iommu), GFP_KERNEL); + if (!iommu) { + printk(KERN_ERR PFX "Could not allocate pbm iommu\n"); + goto out_free_controller; } - p = kzalloc(sizeof(struct pci_controller_info), GFP_ATOMIC); - if (!p) - goto fatal_memory_error; + pbm->iommu = iommu; - iommu = kzalloc(sizeof(struct iommu), GFP_ATOMIC); - if (!iommu) - goto fatal_memory_error; + err = pci_sun4v_pbm_init(pbm, op, devhandle); + if (err) + goto out_free_iommu; + + dev_set_drvdata(&op->dev, pbm); + + return 0; + +out_free_iommu: + kfree(pbm->iommu); - p->pbm_A.iommu = iommu; +out_free_controller: + kfree(pbm); - iommu = kzalloc(sizeof(struct iommu), GFP_ATOMIC); - if (!iommu) - goto fatal_memory_error; +out_err: + return err; +} - p->pbm_B.iommu = iommu; +static struct of_device_id __initdata pci_sun4v_match[] = { + { + .name = "pci", + .compatible = "SUNW,sun4v-pci", + }, + {}, +}; - pci_sun4v_pbm_init(p, dp, devhandle); - return; +static struct of_platform_driver pci_sun4v_driver = { + .name = DRIVER_NAME, + .match_table = pci_sun4v_match, + .probe = pci_sun4v_probe, +}; -fatal_memory_error: - prom_printf("SUN4V_PCI: Fatal memory allocation error.\n"); - prom_halt(); +static int __init pci_sun4v_init(void) +{ + return of_register_driver(&pci_sun4v_driver, &of_bus_type); } + +subsys_initcall(pci_sun4v_init); diff --git a/arch/sparc64/kernel/pci_sun4v_asm.S b/arch/sparc64/kernel/pci_sun4v_asm.S index ecb81f389b0..e606d46c681 100644 --- a/arch/sparc64/kernel/pci_sun4v_asm.S +++ b/arch/sparc64/kernel/pci_sun4v_asm.S @@ -1,8 +1,9 @@ /* pci_sun4v_asm: Hypervisor calls for PCI support. * - * Copyright (C) 2006 David S. Miller <davem@davemloft.net> + * Copyright (C) 2006, 2008 David S. Miller <davem@davemloft.net> */ +#include <linux/linkage.h> #include <asm/hypervisor.h> /* %o0: devhandle @@ -14,8 +15,7 @@ * returns %o0: -status if status was non-zero, else * %o0: num pages mapped */ - .globl pci_sun4v_iommu_map -pci_sun4v_iommu_map: +ENTRY(pci_sun4v_iommu_map) mov %o5, %g1 mov HV_FAST_PCI_IOMMU_MAP, %o5 ta HV_FAST_TRAP @@ -24,6 +24,7 @@ pci_sun4v_iommu_map: mov %o1, %o0 1: retl nop +ENDPROC(pci_sun4v_iommu_map) /* %o0: devhandle * %o1: tsbid @@ -31,12 +32,12 @@ pci_sun4v_iommu_map: * * returns %o0: num ttes demapped */ - .globl pci_sun4v_iommu_demap -pci_sun4v_iommu_demap: +ENTRY(pci_sun4v_iommu_demap) mov HV_FAST_PCI_IOMMU_DEMAP, %o5 ta HV_FAST_TRAP retl mov %o1, %o0 +ENDPROC(pci_sun4v_iommu_demap) /* %o0: devhandle * %o1: tsbid @@ -45,8 +46,7 @@ pci_sun4v_iommu_demap: * * returns %o0: status */ - .globl pci_sun4v_iommu_getmap -pci_sun4v_iommu_getmap: +ENTRY(pci_sun4v_iommu_getmap) mov %o2, %o4 mov HV_FAST_PCI_IOMMU_GETMAP, %o5 ta HV_FAST_TRAP @@ -54,6 +54,7 @@ pci_sun4v_iommu_getmap: stx %o2, [%o3] retl mov %o0, %o0 +ENDPROC(pci_sun4v_iommu_getmap) /* %o0: devhandle * %o1: pci_device @@ -65,14 +66,14 @@ pci_sun4v_iommu_getmap: * If there is an error, the data will be returned * as all 1's. */ - .globl pci_sun4v_config_get -pci_sun4v_config_get: +ENTRY(pci_sun4v_config_get) mov HV_FAST_PCI_CONFIG_GET, %o5 ta HV_FAST_TRAP brnz,a,pn %o1, 1f mov -1, %o2 1: retl mov %o2, %o0 +ENDPROC(pci_sun4v_config_get) /* %o0: devhandle * %o1: pci_device @@ -85,14 +86,14 @@ pci_sun4v_config_get: * status will be zero if the operation completed * successfully, else -1 if not */ - .globl pci_sun4v_config_put -pci_sun4v_config_put: +ENTRY(pci_sun4v_config_put) mov HV_FAST_PCI_CONFIG_PUT, %o5 ta HV_FAST_TRAP brnz,a,pn %o1, 1f mov -1, %o1 1: retl mov %o1, %o0 +ENDPROC(pci_sun4v_config_put) /* %o0: devhandle * %o1: msiqid @@ -104,12 +105,12 @@ pci_sun4v_config_put: * status will be zero if the operation completed * successfully, else -1 if not */ - .globl pci_sun4v_msiq_conf -pci_sun4v_msiq_conf: +ENTRY(pci_sun4v_msiq_conf) mov HV_FAST_PCI_MSIQ_CONF, %o5 ta HV_FAST_TRAP retl mov %o0, %o0 +ENDPROC(pci_sun4v_msiq_conf) /* %o0: devhandle * %o1: msiqid @@ -118,8 +119,7 @@ pci_sun4v_msiq_conf: * * returns %o0: status */ - .globl pci_sun4v_msiq_info -pci_sun4v_msiq_info: +ENTRY(pci_sun4v_msiq_info) mov %o2, %o4 mov HV_FAST_PCI_MSIQ_INFO, %o5 ta HV_FAST_TRAP @@ -127,6 +127,7 @@ pci_sun4v_msiq_info: stx %o2, [%o3] retl mov %o0, %o0 +ENDPROC(pci_sun4v_msiq_info) /* %o0: devhandle * %o1: msiqid @@ -134,13 +135,13 @@ pci_sun4v_msiq_info: * * returns %o0: status */ - .globl pci_sun4v_msiq_getvalid -pci_sun4v_msiq_getvalid: +ENTRY(pci_sun4v_msiq_getvalid) mov HV_FAST_PCI_MSIQ_GETVALID, %o5 ta HV_FAST_TRAP stx %o1, [%o2] retl mov %o0, %o0 +ENDPROC(pci_sun4v_msiq_getvalid) /* %o0: devhandle * %o1: msiqid @@ -148,12 +149,12 @@ pci_sun4v_msiq_getvalid: * * returns %o0: status */ - .globl pci_sun4v_msiq_setvalid -pci_sun4v_msiq_setvalid: +ENTRY(pci_sun4v_msiq_setvalid) mov HV_FAST_PCI_MSIQ_SETVALID, %o5 ta HV_FAST_TRAP retl mov %o0, %o0 +ENDPROC(pci_sun4v_msiq_setvalid) /* %o0: devhandle * %o1: msiqid @@ -161,13 +162,13 @@ pci_sun4v_msiq_setvalid: * * returns %o0: status */ - .globl pci_sun4v_msiq_getstate -pci_sun4v_msiq_getstate: +ENTRY(pci_sun4v_msiq_getstate) mov HV_FAST_PCI_MSIQ_GETSTATE, %o5 ta HV_FAST_TRAP stx %o1, [%o2] retl mov %o0, %o0 +ENDPROC(pci_sun4v_msiq_getstate) /* %o0: devhandle * %o1: msiqid @@ -175,12 +176,12 @@ pci_sun4v_msiq_getstate: * * returns %o0: status */ - .globl pci_sun4v_msiq_setstate -pci_sun4v_msiq_setstate: +ENTRY(pci_sun4v_msiq_setstate) mov HV_FAST_PCI_MSIQ_SETSTATE, %o5 ta HV_FAST_TRAP retl mov %o0, %o0 +ENDPROC(pci_sun4v_msiq_setstate) /* %o0: devhandle * %o1: msiqid @@ -188,13 +189,13 @@ pci_sun4v_msiq_setstate: * * returns %o0: status */ - .globl pci_sun4v_msiq_gethead -pci_sun4v_msiq_gethead: +ENTRY(pci_sun4v_msiq_gethead) mov HV_FAST_PCI_MSIQ_GETHEAD, %o5 ta HV_FAST_TRAP stx %o1, [%o2] retl mov %o0, %o0 +ENDPROC(pci_sun4v_msiq_gethead) /* %o0: devhandle * %o1: msiqid @@ -202,12 +203,12 @@ pci_sun4v_msiq_gethead: * * returns %o0: status */ - .globl pci_sun4v_msiq_sethead -pci_sun4v_msiq_sethead: +ENTRY(pci_sun4v_msiq_sethead) mov HV_FAST_PCI_MSIQ_SETHEAD, %o5 ta HV_FAST_TRAP retl mov %o0, %o0 +ENDPROC(pci_sun4v_msiq_sethead) /* %o0: devhandle * %o1: msiqid @@ -215,13 +216,13 @@ pci_sun4v_msiq_sethead: * * returns %o0: status */ - .globl pci_sun4v_msiq_gettail -pci_sun4v_msiq_gettail: +ENTRY(pci_sun4v_msiq_gettail) mov HV_FAST_PCI_MSIQ_GETTAIL, %o5 ta HV_FAST_TRAP stx %o1, [%o2] retl mov %o0, %o0 +ENDPROC(pci_sun4v_msiq_gettail) /* %o0: devhandle * %o1: msinum @@ -229,13 +230,13 @@ pci_sun4v_msiq_gettail: * * returns %o0: status */ - .globl pci_sun4v_msi_getvalid -pci_sun4v_msi_getvalid: +ENTRY(pci_sun4v_msi_getvalid) mov HV_FAST_PCI_MSI_GETVALID, %o5 ta HV_FAST_TRAP stx %o1, [%o2] retl mov %o0, %o0 +ENDPROC(pci_sun4v_msi_getvalid) /* %o0: devhandle * %o1: msinum @@ -243,12 +244,12 @@ pci_sun4v_msi_getvalid: * * returns %o0: status */ - .globl pci_sun4v_msi_setvalid -pci_sun4v_msi_setvalid: +ENTRY(pci_sun4v_msi_setvalid) mov HV_FAST_PCI_MSI_SETVALID, %o5 ta HV_FAST_TRAP retl mov %o0, %o0 +ENDPROC(pci_sun4v_msi_setvalid) /* %o0: devhandle * %o1: msinum @@ -256,13 +257,13 @@ pci_sun4v_msi_setvalid: * * returns %o0: status */ - .globl pci_sun4v_msi_getmsiq -pci_sun4v_msi_getmsiq: +ENTRY(pci_sun4v_msi_getmsiq) mov HV_FAST_PCI_MSI_GETMSIQ, %o5 ta HV_FAST_TRAP stx %o1, [%o2] retl mov %o0, %o0 +ENDPROC(pci_sun4v_msi_getmsiq) /* %o0: devhandle * %o1: msinum @@ -271,12 +272,12 @@ pci_sun4v_msi_getmsiq: * * returns %o0: status */ - .globl pci_sun4v_msi_setmsiq -pci_sun4v_msi_setmsiq: +ENTRY(pci_sun4v_msi_setmsiq) mov HV_FAST_PCI_MSI_SETMSIQ, %o5 ta HV_FAST_TRAP retl mov %o0, %o0 +ENDPROC(pci_sun4v_msi_setmsiq) /* %o0: devhandle * %o1: msinum @@ -284,13 +285,13 @@ pci_sun4v_msi_setmsiq: * * returns %o0: status */ - .globl pci_sun4v_msi_getstate -pci_sun4v_msi_getstate: +ENTRY(pci_sun4v_msi_getstate) mov HV_FAST_PCI_MSI_GETSTATE, %o5 ta HV_FAST_TRAP stx %o1, [%o2] retl mov %o0, %o0 +ENDPROC(pci_sun4v_msi_getstate) /* %o0: devhandle * %o1: msinum @@ -298,12 +299,12 @@ pci_sun4v_msi_getstate: * * returns %o0: status */ - .globl pci_sun4v_msi_setstate -pci_sun4v_msi_setstate: +ENTRY(pci_sun4v_msi_setstate) mov HV_FAST_PCI_MSI_SETSTATE, %o5 ta HV_FAST_TRAP retl mov %o0, %o0 +ENDPROC(pci_sun4v_msi_setstate) /* %o0: devhandle * %o1: msinum @@ -311,13 +312,13 @@ pci_sun4v_msi_setstate: * * returns %o0: status */ - .globl pci_sun4v_msg_getmsiq -pci_sun4v_msg_getmsiq: +ENTRY(pci_sun4v_msg_getmsiq) mov HV_FAST_PCI_MSG_GETMSIQ, %o5 ta HV_FAST_TRAP stx %o1, [%o2] retl mov %o0, %o0 +ENDPROC(pci_sun4v_msg_getmsiq) /* %o0: devhandle * %o1: msinum @@ -325,12 +326,12 @@ pci_sun4v_msg_getmsiq: * * returns %o0: status */ - .globl pci_sun4v_msg_setmsiq -pci_sun4v_msg_setmsiq: +ENTRY(pci_sun4v_msg_setmsiq) mov HV_FAST_PCI_MSG_SETMSIQ, %o5 ta HV_FAST_TRAP retl mov %o0, %o0 +ENDPROC(pci_sun4v_msg_setmsiq) /* %o0: devhandle * %o1: msinum @@ -338,13 +339,13 @@ pci_sun4v_msg_setmsiq: * * returns %o0: status */ - .globl pci_sun4v_msg_getvalid -pci_sun4v_msg_getvalid: +ENTRY(pci_sun4v_msg_getvalid) mov HV_FAST_PCI_MSG_GETVALID, %o5 ta HV_FAST_TRAP stx %o1, [%o2] retl mov %o0, %o0 +ENDPROC(pci_sun4v_msg_getvalid) /* %o0: devhandle * %o1: msinum @@ -352,10 +353,10 @@ pci_sun4v_msg_getvalid: * * returns %o0: status */ - .globl pci_sun4v_msg_setvalid -pci_sun4v_msg_setvalid: +ENTRY(pci_sun4v_msg_setvalid) mov HV_FAST_PCI_MSG_SETVALID, %o5 ta HV_FAST_TRAP retl mov %o0, %o0 +ENDPROC(pci_sun4v_msg_setvalid) diff --git a/arch/sparc64/kernel/power.c b/arch/sparc64/kernel/power.c index 3bb987a6d03..076cad7f975 100644 --- a/arch/sparc64/kernel/power.c +++ b/arch/sparc64/kernel/power.c @@ -1,34 +1,17 @@ /* power.c: Power management driver. * - * Copyright (C) 1999, 2007 David S. Miller (davem@davemloft.net) + * Copyright (C) 1999, 2007, 2008 David S. Miller (davem@davemloft.net) */ #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> -#include <linux/sched.h> -#include <linux/signal.h> -#include <linux/delay.h> #include <linux/interrupt.h> -#include <linux/pm.h> -#include <linux/syscalls.h> #include <linux/reboot.h> #include <linux/of_device.h> -#include <asm/system.h> -#include <asm/auxio.h> #include <asm/prom.h> #include <asm/io.h> -#include <asm/sstate.h> -#include <asm/reboot.h> - -#include <linux/unistd.h> - -/* - * sysctl - toggle power-off restriction for serial console - * systems in machine_power_off() - */ -int scons_pwroff = 1; static void __iomem *power_reg; @@ -40,31 +23,6 @@ static irqreturn_t power_handler(int irq, void *dev_id) return IRQ_HANDLED; } -static void (*poweroff_method)(void) = machine_alt_power_off; - -void machine_power_off(void) -{ - sstate_poweroff(); - if (strcmp(of_console_device->type, "serial") || scons_pwroff) { - if (power_reg) { - /* Both register bits seem to have the - * same effect, so until I figure out - * what the difference is... - */ - writel(AUXIO_PCIO_CPWR_OFF | AUXIO_PCIO_SPWR_OFF, power_reg); - } else { - if (poweroff_method != NULL) { - poweroff_method(); - /* not reached */ - } - } - } - machine_halt(); -} - -void (*pm_power_off)(void) = machine_power_off; -EXPORT_SYMBOL(pm_power_off); - static int __init has_button_interrupt(unsigned int irq, struct device_node *dp) { if (irq == 0xffffffff) @@ -85,8 +43,6 @@ static int __devinit power_probe(struct of_device *op, const struct of_device_id printk(KERN_INFO "%s: Control reg at %lx\n", op->node->name, res->start); - poweroff_method = machine_halt; /* able to use the standard halt */ - if (has_button_interrupt(irq, op->node)) { if (request_irq(irq, power_handler, 0, "power", NULL) < 0) @@ -96,7 +52,7 @@ static int __devinit power_probe(struct of_device *op, const struct of_device_id return 0; } -static struct of_device_id power_match[] = { +static struct of_device_id __initdata power_match[] = { { .name = "power", }, @@ -111,8 +67,9 @@ static struct of_platform_driver power_driver = { }, }; -void __init power_init(void) +static int __init power_init(void) { - of_register_driver(&power_driver, &of_platform_bus_type); - return; + return of_register_driver(&power_driver, &of_platform_bus_type); } + +device_initcall(power_init); diff --git a/arch/sparc64/kernel/process.c b/arch/sparc64/kernel/process.c index 15f4178592e..d5e2acef987 100644 --- a/arch/sparc64/kernel/process.c +++ b/arch/sparc64/kernel/process.c @@ -22,7 +22,6 @@ #include <linux/ptrace.h> #include <linux/slab.h> #include <linux/user.h> -#include <linux/reboot.h> #include <linux/delay.h> #include <linux/compat.h> #include <linux/tick.h> @@ -31,7 +30,6 @@ #include <linux/elfcore.h> #include <linux/sysrq.h> -#include <asm/oplib.h> #include <asm/uaccess.h> #include <asm/system.h> #include <asm/page.h> @@ -46,8 +44,6 @@ #include <asm/mmu_context.h> #include <asm/unistd.h> #include <asm/hypervisor.h> -#include <asm/sstate.h> -#include <asm/reboot.h> #include <asm/syscalls.h> #include <asm/irq_regs.h> #include <asm/smp.h> @@ -115,35 +111,6 @@ void cpu_idle(void) } } -void machine_halt(void) -{ - sstate_halt(); - prom_halt(); - panic("Halt failed!"); -} - -void machine_alt_power_off(void) -{ - sstate_poweroff(); - prom_halt_power_off(); - panic("Power-off failed!"); -} - -void machine_restart(char * cmd) -{ - char *p; - - sstate_reboot(); - p = strchr (reboot_command, '\n'); - if (p) *p = 0; - if (cmd) - prom_reboot(cmd); - if (*reboot_command) - prom_reboot(reboot_command); - prom_reboot(""); - panic("Reboot failed!"); -} - #ifdef CONFIG_COMPAT static void show_regwindow32(struct pt_regs *regs) { @@ -248,7 +215,6 @@ static void __global_reg_self(struct thread_info *tp, struct pt_regs *regs, global_reg_snapshot[this_cpu].o7 = regs->u_regs[UREG_I7]; if (regs->tstate & TSTATE_PRIV) { - struct thread_info *tp = current_thread_info(); struct reg_window *rw; rw = (struct reg_window *) @@ -304,7 +270,6 @@ void __trigger_all_cpu_backtrace(void) for_each_online_cpu(cpu) { struct global_reg_snapshot *gp = &global_reg_snapshot[cpu]; - struct thread_info *tp; __global_reg_poll(gp); diff --git a/arch/sparc64/kernel/prom.c b/arch/sparc64/kernel/prom.c index 7151513f156..dbba82f9b14 100644 --- a/arch/sparc64/kernel/prom.c +++ b/arch/sparc64/kernel/prom.c @@ -38,7 +38,7 @@ struct device_node *of_find_node_by_phandle(phandle handle) { struct device_node *np; - for (np = allnodes; np != 0; np = np->allnext) + for (np = allnodes; np; np = np->allnext) if (np->node == handle) break; @@ -59,6 +59,9 @@ int of_getintprop_default(struct device_node *np, const char *name, int def) } EXPORT_SYMBOL(of_getintprop_default); +DEFINE_MUTEX(of_set_property_mutex); +EXPORT_SYMBOL(of_set_property_mutex); + int of_set_property(struct device_node *dp, const char *name, void *val, int len) { struct property **prevp; @@ -82,7 +85,10 @@ int of_set_property(struct device_node *dp, const char *name, void *val, int len void *old_val = prop->value; int ret; + mutex_lock(&of_set_property_mutex); ret = prom_setprop(dp->node, name, val, len); + mutex_unlock(&of_set_property_mutex); + err = -EINVAL; if (ret >= 0) { prop->value = new_val; @@ -945,22 +951,30 @@ static void __init irq_trans_init(struct device_node *dp) for (i = 0; i < ARRAY_SIZE(pci_irq_trans_table); i++) { struct irq_trans *t = &pci_irq_trans_table[i]; - if (!strcmp(model, t->name)) - return t->init(dp); + if (!strcmp(model, t->name)) { + t->init(dp); + return; + } } } #endif #ifdef CONFIG_SBUS if (!strcmp(dp->name, "sbus") || - !strcmp(dp->name, "sbi")) - return sbus_irq_trans_init(dp); + !strcmp(dp->name, "sbi")) { + sbus_irq_trans_init(dp); + return; + } #endif if (!strcmp(dp->name, "fhc") && - !strcmp(dp->parent->name, "central")) - return central_irq_trans_init(dp); + !strcmp(dp->parent->name, "central")) { + central_irq_trans_init(dp); + return; + } if (!strcmp(dp->name, "virtual-devices") || - !strcmp(dp->name, "niu")) - return sun4v_vdev_irq_trans_init(dp); + !strcmp(dp->name, "niu")) { + sun4v_vdev_irq_trans_init(dp); + return; + } } static int is_root_node(const struct device_node *dp) @@ -1231,32 +1245,49 @@ static void __init __build_path_component(struct device_node *dp, char *tmp_buf) if (parent != NULL) { if (!strcmp(parent->type, "pci") || - !strcmp(parent->type, "pciex")) - return pci_path_component(dp, tmp_buf); - if (!strcmp(parent->type, "sbus")) - return sbus_path_component(dp, tmp_buf); - if (!strcmp(parent->type, "upa")) - return upa_path_component(dp, tmp_buf); - if (!strcmp(parent->type, "ebus")) - return ebus_path_component(dp, tmp_buf); + !strcmp(parent->type, "pciex")) { + pci_path_component(dp, tmp_buf); + return; + } + if (!strcmp(parent->type, "sbus")) { + sbus_path_component(dp, tmp_buf); + return; + } + if (!strcmp(parent->type, "upa")) { + upa_path_component(dp, tmp_buf); + return; + } + if (!strcmp(parent->type, "ebus")) { + ebus_path_component(dp, tmp_buf); + return; + } if (!strcmp(parent->name, "usb") || - !strcmp(parent->name, "hub")) - return usb_path_component(dp, tmp_buf); - if (!strcmp(parent->type, "i2c")) - return i2c_path_component(dp, tmp_buf); - if (!strcmp(parent->type, "firewire")) - return ieee1394_path_component(dp, tmp_buf); - if (!strcmp(parent->type, "virtual-devices")) - return vdev_path_component(dp, tmp_buf); - + !strcmp(parent->name, "hub")) { + usb_path_component(dp, tmp_buf); + return; + } + if (!strcmp(parent->type, "i2c")) { + i2c_path_component(dp, tmp_buf); + return; + } + if (!strcmp(parent->type, "firewire")) { + ieee1394_path_component(dp, tmp_buf); + return; + } + if (!strcmp(parent->type, "virtual-devices")) { + vdev_path_component(dp, tmp_buf); + return; + } /* "isa" is handled with platform naming */ } /* Use platform naming convention. */ - if (tlb_type == hypervisor) - return sun4v_path_component(dp, tmp_buf); - else - return sun4u_path_component(dp, tmp_buf); + if (tlb_type == hypervisor) { + sun4v_path_component(dp, tmp_buf); + return; + } else { + sun4u_path_component(dp, tmp_buf); + } } static char * __init build_path_component(struct device_node *dp) diff --git a/arch/sparc64/kernel/psycho_common.c b/arch/sparc64/kernel/psycho_common.c new file mode 100644 index 00000000000..790996428c1 --- /dev/null +++ b/arch/sparc64/kernel/psycho_common.c @@ -0,0 +1,470 @@ +/* psycho_common.c: Code common to PSYCHO and derivative PCI controllers. + * + * Copyright (C) 2008 David S. Miller <davem@davemloft.net> + */ +#include <linux/kernel.h> +#include <linux/interrupt.h> + +#include <asm/upa.h> + +#include "pci_impl.h" +#include "iommu_common.h" +#include "psycho_common.h" + +#define PSYCHO_STRBUF_CTRL_DENAB 0x0000000000000002UL +#define PSYCHO_STCERR_WRITE 0x0000000000000002UL +#define PSYCHO_STCERR_READ 0x0000000000000001UL +#define PSYCHO_STCTAG_PPN 0x0fffffff00000000UL +#define PSYCHO_STCTAG_VPN 0x00000000ffffe000UL +#define PSYCHO_STCTAG_VALID 0x0000000000000002UL +#define PSYCHO_STCTAG_WRITE 0x0000000000000001UL +#define PSYCHO_STCLINE_LINDX 0x0000000001e00000UL +#define PSYCHO_STCLINE_SPTR 0x00000000001f8000UL +#define PSYCHO_STCLINE_LADDR 0x0000000000007f00UL +#define PSYCHO_STCLINE_EPTR 0x00000000000000fcUL +#define PSYCHO_STCLINE_VALID 0x0000000000000002UL +#define PSYCHO_STCLINE_FOFN 0x0000000000000001UL + +static DEFINE_SPINLOCK(stc_buf_lock); +static unsigned long stc_error_buf[128]; +static unsigned long stc_tag_buf[16]; +static unsigned long stc_line_buf[16]; + +static void psycho_check_stc_error(struct pci_pbm_info *pbm) +{ + unsigned long err_base, tag_base, line_base; + struct strbuf *strbuf = &pbm->stc; + u64 control; + int i; + + if (!strbuf->strbuf_control) + return; + + err_base = strbuf->strbuf_err_stat; + tag_base = strbuf->strbuf_tag_diag; + line_base = strbuf->strbuf_line_diag; + + spin_lock(&stc_buf_lock); + + /* This is __REALLY__ dangerous. When we put the streaming + * buffer into diagnostic mode to probe it's tags and error + * status, we _must_ clear all of the line tag valid bits + * before re-enabling the streaming buffer. If any dirty data + * lives in the STC when we do this, we will end up + * invalidating it before it has a chance to reach main + * memory. + */ + control = upa_readq(strbuf->strbuf_control); + upa_writeq(control | PSYCHO_STRBUF_CTRL_DENAB, strbuf->strbuf_control); + for (i = 0; i < 128; i++) { + u64 val; + + val = upa_readq(err_base + (i * 8UL)); + upa_writeq(0UL, err_base + (i * 8UL)); + stc_error_buf[i] = val; + } + for (i = 0; i < 16; i++) { + stc_tag_buf[i] = upa_readq(tag_base + (i * 8UL)); + stc_line_buf[i] = upa_readq(line_base + (i * 8UL)); + upa_writeq(0UL, tag_base + (i * 8UL)); + upa_writeq(0UL, line_base + (i * 8UL)); + } + + /* OK, state is logged, exit diagnostic mode. */ + upa_writeq(control, strbuf->strbuf_control); + + for (i = 0; i < 16; i++) { + int j, saw_error, first, last; + + saw_error = 0; + first = i * 8; + last = first + 8; + for (j = first; j < last; j++) { + u64 errval = stc_error_buf[j]; + if (errval != 0) { + saw_error++; + printk(KERN_ERR "%s: STC_ERR(%d)[wr(%d)" + "rd(%d)]\n", + pbm->name, + j, + (errval & PSYCHO_STCERR_WRITE) ? 1 : 0, + (errval & PSYCHO_STCERR_READ) ? 1 : 0); + } + } + if (saw_error != 0) { + u64 tagval = stc_tag_buf[i]; + u64 lineval = stc_line_buf[i]; + printk(KERN_ERR "%s: STC_TAG(%d)[PA(%016lx)VA(%08lx)" + "V(%d)W(%d)]\n", + pbm->name, + i, + ((tagval & PSYCHO_STCTAG_PPN) >> 19UL), + (tagval & PSYCHO_STCTAG_VPN), + ((tagval & PSYCHO_STCTAG_VALID) ? 1 : 0), + ((tagval & PSYCHO_STCTAG_WRITE) ? 1 : 0)); + printk(KERN_ERR "%s: STC_LINE(%d)[LIDX(%lx)SP(%lx)" + "LADDR(%lx)EP(%lx)V(%d)FOFN(%d)]\n", + pbm->name, + i, + ((lineval & PSYCHO_STCLINE_LINDX) >> 21UL), + ((lineval & PSYCHO_STCLINE_SPTR) >> 15UL), + ((lineval & PSYCHO_STCLINE_LADDR) >> 8UL), + ((lineval & PSYCHO_STCLINE_EPTR) >> 2UL), + ((lineval & PSYCHO_STCLINE_VALID) ? 1 : 0), + ((lineval & PSYCHO_STCLINE_FOFN) ? 1 : 0)); + } + } + + spin_unlock(&stc_buf_lock); +} + +#define PSYCHO_IOMMU_TAG 0xa580UL +#define PSYCHO_IOMMU_DATA 0xa600UL + +static void psycho_record_iommu_tags_and_data(struct pci_pbm_info *pbm, + u64 *tag, u64 *data) +{ + int i; + + for (i = 0; i < 16; i++) { + unsigned long base = pbm->controller_regs; + unsigned long off = i * 8UL; + + tag[i] = upa_readq(base + PSYCHO_IOMMU_TAG+off); + data[i] = upa_readq(base + PSYCHO_IOMMU_DATA+off); + + /* Now clear out the entry. */ + upa_writeq(0, base + PSYCHO_IOMMU_TAG + off); + upa_writeq(0, base + PSYCHO_IOMMU_DATA + off); + } +} + +#define PSYCHO_IOMMU_TAG_ERRSTS (0x3UL << 23UL) +#define PSYCHO_IOMMU_TAG_ERR (0x1UL << 22UL) +#define PSYCHO_IOMMU_TAG_WRITE (0x1UL << 21UL) +#define PSYCHO_IOMMU_TAG_STREAM (0x1UL << 20UL) +#define PSYCHO_IOMMU_TAG_SIZE (0x1UL << 19UL) +#define PSYCHO_IOMMU_TAG_VPAGE 0x7ffffUL +#define PSYCHO_IOMMU_DATA_VALID (1UL << 30UL) +#define PSYCHO_IOMMU_DATA_CACHE (1UL << 28UL) +#define PSYCHO_IOMMU_DATA_PPAGE 0xfffffffUL + +static void psycho_dump_iommu_tags_and_data(struct pci_pbm_info *pbm, + u64 *tag, u64 *data) +{ + int i; + + for (i = 0; i < 16; i++) { + u64 tag_val, data_val; + const char *type_str; + tag_val = tag[i]; + if (!(tag_val & PSYCHO_IOMMU_TAG_ERR)) + continue; + + data_val = data[i]; + switch((tag_val & PSYCHO_IOMMU_TAG_ERRSTS) >> 23UL) { + case 0: + type_str = "Protection Error"; + break; + case 1: + type_str = "Invalid Error"; + break; + case 2: + type_str = "TimeOut Error"; + break; + case 3: + default: + type_str = "ECC Error"; + break; + } + + printk(KERN_ERR "%s: IOMMU TAG(%d)[error(%s) wr(%d) " + "str(%d) sz(%dK) vpg(%08lx)]\n", + pbm->name, i, type_str, + ((tag_val & PSYCHO_IOMMU_TAG_WRITE) ? 1 : 0), + ((tag_val & PSYCHO_IOMMU_TAG_STREAM) ? 1 : 0), + ((tag_val & PSYCHO_IOMMU_TAG_SIZE) ? 64 : 8), + (tag_val & PSYCHO_IOMMU_TAG_VPAGE) << IOMMU_PAGE_SHIFT); + printk(KERN_ERR "%s: IOMMU DATA(%d)[valid(%d) cache(%d) " + "ppg(%016lx)]\n", + pbm->name, i, + ((data_val & PSYCHO_IOMMU_DATA_VALID) ? 1 : 0), + ((data_val & PSYCHO_IOMMU_DATA_CACHE) ? 1 : 0), + (data_val & PSYCHO_IOMMU_DATA_PPAGE)<<IOMMU_PAGE_SHIFT); + } +} + +#define PSYCHO_IOMMU_CTRL_XLTESTAT 0x0000000006000000UL +#define PSYCHO_IOMMU_CTRL_XLTEERR 0x0000000001000000UL + +void psycho_check_iommu_error(struct pci_pbm_info *pbm, + unsigned long afsr, + unsigned long afar, + enum psycho_error_type type) +{ + u64 control, iommu_tag[16], iommu_data[16]; + struct iommu *iommu = pbm->iommu; + unsigned long flags; + + spin_lock_irqsave(&iommu->lock, flags); + control = upa_readq(iommu->iommu_control); + if (control & PSYCHO_IOMMU_CTRL_XLTEERR) { + const char *type_str; + + control &= ~PSYCHO_IOMMU_CTRL_XLTEERR; + upa_writeq(control, iommu->iommu_control); + + switch ((control & PSYCHO_IOMMU_CTRL_XLTESTAT) >> 25UL) { + case 0: + type_str = "Protection Error"; + break; + case 1: + type_str = "Invalid Error"; + break; + case 2: + type_str = "TimeOut Error"; + break; + case 3: + default: + type_str = "ECC Error"; + break; + }; + printk(KERN_ERR "%s: IOMMU Error, type[%s]\n", + pbm->name, type_str); + + /* It is very possible for another DVMA to occur while + * we do this probe, and corrupt the system further. + * But we are so screwed at this point that we are + * likely to crash hard anyways, so get as much + * diagnostic information to the console as we can. + */ + psycho_record_iommu_tags_and_data(pbm, iommu_tag, iommu_data); + psycho_dump_iommu_tags_and_data(pbm, iommu_tag, iommu_data); + } + psycho_check_stc_error(pbm); + spin_unlock_irqrestore(&iommu->lock, flags); +} + +#define PSYCHO_PCICTRL_SBH_ERR 0x0000000800000000UL +#define PSYCHO_PCICTRL_SERR 0x0000000400000000UL + +static irqreturn_t psycho_pcierr_intr_other(struct pci_pbm_info *pbm) +{ + irqreturn_t ret = IRQ_NONE; + u64 csr, csr_error_bits; + u16 stat, *addr; + + csr = upa_readq(pbm->pci_csr); + csr_error_bits = csr & (PSYCHO_PCICTRL_SBH_ERR | PSYCHO_PCICTRL_SERR); + if (csr_error_bits) { + /* Clear the errors. */ + upa_writeq(csr, pbm->pci_csr); + + /* Log 'em. */ + if (csr_error_bits & PSYCHO_PCICTRL_SBH_ERR) + printk(KERN_ERR "%s: PCI streaming byte hole " + "error asserted.\n", pbm->name); + if (csr_error_bits & PSYCHO_PCICTRL_SERR) + printk(KERN_ERR "%s: PCI SERR signal asserted.\n", + pbm->name); + ret = IRQ_HANDLED; + } + addr = psycho_pci_config_mkaddr(pbm, pbm->pci_first_busno, + 0, PCI_STATUS); + pci_config_read16(addr, &stat); + if (stat & (PCI_STATUS_PARITY | + PCI_STATUS_SIG_TARGET_ABORT | + PCI_STATUS_REC_TARGET_ABORT | + PCI_STATUS_REC_MASTER_ABORT | + PCI_STATUS_SIG_SYSTEM_ERROR)) { + printk(KERN_ERR "%s: PCI bus error, PCI_STATUS[%04x]\n", + pbm->name, stat); + pci_config_write16(addr, 0xffff); + ret = IRQ_HANDLED; + } + return ret; +} + +#define PSYCHO_PCIAFSR_PMA 0x8000000000000000UL +#define PSYCHO_PCIAFSR_PTA 0x4000000000000000UL +#define PSYCHO_PCIAFSR_PRTRY 0x2000000000000000UL +#define PSYCHO_PCIAFSR_PPERR 0x1000000000000000UL +#define PSYCHO_PCIAFSR_SMA 0x0800000000000000UL +#define PSYCHO_PCIAFSR_STA 0x0400000000000000UL +#define PSYCHO_PCIAFSR_SRTRY 0x0200000000000000UL +#define PSYCHO_PCIAFSR_SPERR 0x0100000000000000UL +#define PSYCHO_PCIAFSR_RESV1 0x00ff000000000000UL +#define PSYCHO_PCIAFSR_BMSK 0x0000ffff00000000UL +#define PSYCHO_PCIAFSR_BLK 0x0000000080000000UL +#define PSYCHO_PCIAFSR_RESV2 0x0000000040000000UL +#define PSYCHO_PCIAFSR_MID 0x000000003e000000UL +#define PSYCHO_PCIAFSR_RESV3 0x0000000001ffffffUL + +irqreturn_t psycho_pcierr_intr(int irq, void *dev_id) +{ + struct pci_pbm_info *pbm = dev_id; + u64 afsr, afar, error_bits; + int reported; + + afsr = upa_readq(pbm->pci_afsr); + afar = upa_readq(pbm->pci_afar); + error_bits = afsr & + (PSYCHO_PCIAFSR_PMA | PSYCHO_PCIAFSR_PTA | + PSYCHO_PCIAFSR_PRTRY | PSYCHO_PCIAFSR_PPERR | + PSYCHO_PCIAFSR_SMA | PSYCHO_PCIAFSR_STA | + PSYCHO_PCIAFSR_SRTRY | PSYCHO_PCIAFSR_SPERR); + if (!error_bits) + return psycho_pcierr_intr_other(pbm); + upa_writeq(error_bits, pbm->pci_afsr); + printk(KERN_ERR "%s: PCI Error, primary error type[%s]\n", + pbm->name, + (((error_bits & PSYCHO_PCIAFSR_PMA) ? + "Master Abort" : + ((error_bits & PSYCHO_PCIAFSR_PTA) ? + "Target Abort" : + ((error_bits & PSYCHO_PCIAFSR_PRTRY) ? + "Excessive Retries" : + ((error_bits & PSYCHO_PCIAFSR_PPERR) ? + "Parity Error" : "???")))))); + printk(KERN_ERR "%s: bytemask[%04lx] UPA_MID[%02lx] was_block(%d)\n", + pbm->name, + (afsr & PSYCHO_PCIAFSR_BMSK) >> 32UL, + (afsr & PSYCHO_PCIAFSR_MID) >> 25UL, + (afsr & PSYCHO_PCIAFSR_BLK) ? 1 : 0); + printk(KERN_ERR "%s: PCI AFAR [%016lx]\n", pbm->name, afar); + printk(KERN_ERR "%s: PCI Secondary errors [", pbm->name); + reported = 0; + if (afsr & PSYCHO_PCIAFSR_SMA) { + reported++; + printk("(Master Abort)"); + } + if (afsr & PSYCHO_PCIAFSR_STA) { + reported++; + printk("(Target Abort)"); + } + if (afsr & PSYCHO_PCIAFSR_SRTRY) { + reported++; + printk("(Excessive Retries)"); + } + if (afsr & PSYCHO_PCIAFSR_SPERR) { + reported++; + printk("(Parity Error)"); + } + if (!reported) + printk("(none)"); + printk("]\n"); + + if (error_bits & (PSYCHO_PCIAFSR_PTA | PSYCHO_PCIAFSR_STA)) { + psycho_check_iommu_error(pbm, afsr, afar, PCI_ERR); + pci_scan_for_target_abort(pbm, pbm->pci_bus); + } + if (error_bits & (PSYCHO_PCIAFSR_PMA | PSYCHO_PCIAFSR_SMA)) + pci_scan_for_master_abort(pbm, pbm->pci_bus); + + if (error_bits & (PSYCHO_PCIAFSR_PPERR | PSYCHO_PCIAFSR_SPERR)) + pci_scan_for_parity_error(pbm, pbm->pci_bus); + + return IRQ_HANDLED; +} + +static void psycho_iommu_flush(struct pci_pbm_info *pbm) +{ + int i; + + for (i = 0; i < 16; i++) { + unsigned long off = i * 8; + + upa_writeq(0, pbm->controller_regs + PSYCHO_IOMMU_TAG + off); + upa_writeq(0, pbm->controller_regs + PSYCHO_IOMMU_DATA + off); + } +} + +#define PSYCHO_IOMMU_CONTROL 0x0200UL +#define PSYCHO_IOMMU_CTRL_TSBSZ 0x0000000000070000UL +#define PSYCHO_IOMMU_TSBSZ_1K 0x0000000000000000UL +#define PSYCHO_IOMMU_TSBSZ_2K 0x0000000000010000UL +#define PSYCHO_IOMMU_TSBSZ_4K 0x0000000000020000UL +#define PSYCHO_IOMMU_TSBSZ_8K 0x0000000000030000UL +#define PSYCHO_IOMMU_TSBSZ_16K 0x0000000000040000UL +#define PSYCHO_IOMMU_TSBSZ_32K 0x0000000000050000UL +#define PSYCHO_IOMMU_TSBSZ_64K 0x0000000000060000UL +#define PSYCHO_IOMMU_TSBSZ_128K 0x0000000000070000UL +#define PSYCHO_IOMMU_CTRL_TBWSZ 0x0000000000000004UL +#define PSYCHO_IOMMU_CTRL_DENAB 0x0000000000000002UL +#define PSYCHO_IOMMU_CTRL_ENAB 0x0000000000000001UL +#define PSYCHO_IOMMU_FLUSH 0x0210UL +#define PSYCHO_IOMMU_TSBBASE 0x0208UL + +int psycho_iommu_init(struct pci_pbm_info *pbm, int tsbsize, + u32 dvma_offset, u32 dma_mask, + unsigned long write_complete_offset) +{ + struct iommu *iommu = pbm->iommu; + u64 control; + int err; + + iommu->iommu_control = pbm->controller_regs + PSYCHO_IOMMU_CONTROL; + iommu->iommu_tsbbase = pbm->controller_regs + PSYCHO_IOMMU_TSBBASE; + iommu->iommu_flush = pbm->controller_regs + PSYCHO_IOMMU_FLUSH; + iommu->iommu_tags = pbm->controller_regs + PSYCHO_IOMMU_TAG; + iommu->write_complete_reg = (pbm->controller_regs + + write_complete_offset); + + iommu->iommu_ctxflush = 0; + + control = upa_readq(iommu->iommu_control); + control |= PSYCHO_IOMMU_CTRL_DENAB; + upa_writeq(control, iommu->iommu_control); + + psycho_iommu_flush(pbm); + + /* Leave diag mode enabled for full-flushing done in pci_iommu.c */ + err = iommu_table_init(iommu, tsbsize * 1024 * 8, + dvma_offset, dma_mask, pbm->numa_node); + if (err) + return err; + + upa_writeq(__pa(iommu->page_table), iommu->iommu_tsbbase); + + control = upa_readq(iommu->iommu_control); + control &= ~(PSYCHO_IOMMU_CTRL_TSBSZ | PSYCHO_IOMMU_CTRL_TBWSZ); + control |= PSYCHO_IOMMU_CTRL_ENAB; + + switch (tsbsize) { + case 64: + control |= PSYCHO_IOMMU_TSBSZ_64K; + break; + case 128: + control |= PSYCHO_IOMMU_TSBSZ_128K; + break; + default: + return -EINVAL; + } + + upa_writeq(control, iommu->iommu_control); + + return 0; + +} + +void psycho_pbm_init_common(struct pci_pbm_info *pbm, struct of_device *op, + const char *chip_name, int chip_type) +{ + struct device_node *dp = op->node; + + pbm->name = dp->full_name; + pbm->numa_node = -1; + pbm->chip_type = chip_type; + pbm->chip_version = of_getintprop_default(dp, "version#", 0); + pbm->chip_revision = of_getintprop_default(dp, "module-revision#", 0); + pbm->op = op; + pbm->pci_ops = &sun4u_pci_ops; + pbm->config_space_reg_bits = 8; + pbm->index = pci_num_pbms++; + pci_get_pbm_props(pbm); + pci_determine_mem_io_space(pbm); + + printk(KERN_INFO "%s: %s PCI Bus Module ver[%x:%x]\n", + pbm->name, chip_name, + pbm->chip_version, pbm->chip_revision); +} diff --git a/arch/sparc64/kernel/psycho_common.h b/arch/sparc64/kernel/psycho_common.h new file mode 100644 index 00000000000..092c278ef28 --- /dev/null +++ b/arch/sparc64/kernel/psycho_common.h @@ -0,0 +1,48 @@ +#ifndef _PSYCHO_COMMON_H +#define _PSYCHO_COMMON_H + +/* U2P Programmer's Manual, page 13-55, configuration space + * address format: + * + * 32 24 23 16 15 11 10 8 7 2 1 0 + * --------------------------------------------------------- + * |0 0 0 0 0 0 0 0 1| bus | device | function | reg | 0 0 | + * --------------------------------------------------------- + */ +#define PSYCHO_CONFIG_BASE(PBM) \ + ((PBM)->config_space | (1UL << 24)) +#define PSYCHO_CONFIG_ENCODE(BUS, DEVFN, REG) \ + (((unsigned long)(BUS) << 16) | \ + ((unsigned long)(DEVFN) << 8) | \ + ((unsigned long)(REG))) + +static inline void *psycho_pci_config_mkaddr(struct pci_pbm_info *pbm, + unsigned char bus, + unsigned int devfn, + int where) +{ + return (void *) + (PSYCHO_CONFIG_BASE(pbm) | + PSYCHO_CONFIG_ENCODE(bus, devfn, where)); +} + +enum psycho_error_type { + UE_ERR, CE_ERR, PCI_ERR +}; + +extern void psycho_check_iommu_error(struct pci_pbm_info *pbm, + unsigned long afsr, + unsigned long afar, + enum psycho_error_type type); + +extern irqreturn_t psycho_pcierr_intr(int irq, void *dev_id); + +extern int psycho_iommu_init(struct pci_pbm_info *pbm, int tsbsize, + u32 dvma_offset, u32 dma_mask, + unsigned long write_complete_offset); + +extern void psycho_pbm_init_common(struct pci_pbm_info *pbm, + struct of_device *op, + const char *chip_name, int chip_type); + +#endif /* _PSYCHO_COMMON_H */ diff --git a/arch/sparc64/kernel/ptrace.c b/arch/sparc64/kernel/ptrace.c index 10306e476e3..f43adbc773c 100644 --- a/arch/sparc64/kernel/ptrace.c +++ b/arch/sparc64/kernel/ptrace.c @@ -1050,31 +1050,17 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) return ret; } -asmlinkage int syscall_trace(struct pt_regs *regs, int syscall_exit_p) +asmlinkage int syscall_trace_enter(struct pt_regs *regs) { int ret = 0; /* do the secure computing check first */ secure_computing(regs->u_regs[UREG_G1]); - if (unlikely(current->audit_context) && syscall_exit_p) { - unsigned long tstate = regs->tstate; - int result = AUDITSC_SUCCESS; - - if (unlikely(tstate & (TSTATE_XCARRY | TSTATE_ICARRY))) - result = AUDITSC_FAILURE; - - audit_syscall_exit(result, regs->u_regs[UREG_I0]); - } - - if (test_thread_flag(TIF_SYSCALL_TRACE)) { - if (syscall_exit_p) - tracehook_report_syscall_exit(regs, 0); - else - ret = tracehook_report_syscall_entry(regs); - } + if (test_thread_flag(TIF_SYSCALL_TRACE)) + ret = tracehook_report_syscall_entry(regs); - if (unlikely(current->audit_context) && !syscall_exit_p && !ret) + if (unlikely(current->audit_context) && !ret) audit_syscall_entry((test_thread_flag(TIF_32BIT) ? AUDIT_ARCH_SPARC : AUDIT_ARCH_SPARC64), @@ -1086,3 +1072,19 @@ asmlinkage int syscall_trace(struct pt_regs *regs, int syscall_exit_p) return ret; } + +asmlinkage void syscall_trace_leave(struct pt_regs *regs) +{ + if (unlikely(current->audit_context)) { + unsigned long tstate = regs->tstate; + int result = AUDITSC_SUCCESS; + + if (unlikely(tstate & (TSTATE_XCARRY | TSTATE_ICARRY))) + result = AUDITSC_FAILURE; + + audit_syscall_exit(result, regs->u_regs[UREG_I0]); + } + + if (test_thread_flag(TIF_SYSCALL_TRACE)) + tracehook_report_syscall_exit(regs, 0); +} diff --git a/arch/sparc64/kernel/reboot.c b/arch/sparc64/kernel/reboot.c new file mode 100644 index 00000000000..ef89d3d6974 --- /dev/null +++ b/arch/sparc64/kernel/reboot.c @@ -0,0 +1,53 @@ +/* reboot.c: reboot/shutdown/halt/poweroff handling + * + * Copyright (C) 2008 David S. Miller <davem@davemloft.net> + */ +#include <linux/kernel.h> +#include <linux/reboot.h> +#include <linux/module.h> +#include <linux/pm.h> + +#include <asm/system.h> +#include <asm/oplib.h> +#include <asm/prom.h> + +/* sysctl - toggle power-off restriction for serial console + * systems in machine_power_off() + */ +int scons_pwroff = 1; + +/* This isn't actually used, it exists merely to satisfy the + * reference in kernel/sys.c + */ +void (*pm_power_off)(void) = machine_power_off; +EXPORT_SYMBOL(pm_power_off); + +void machine_power_off(void) +{ + if (strcmp(of_console_device->type, "serial") || scons_pwroff) + prom_halt_power_off(); + + prom_halt(); +} + +void machine_halt(void) +{ + prom_halt(); + panic("Halt failed!"); +} + +void machine_restart(char *cmd) +{ + char *p; + + p = strchr(reboot_command, '\n'); + if (p) + *p = 0; + if (cmd) + prom_reboot(cmd); + if (*reboot_command) + prom_reboot(reboot_command); + prom_reboot(""); + panic("Reboot failed!"); +} + diff --git a/arch/sparc64/kernel/sbus.c b/arch/sparc64/kernel/sbus.c index e33a8a660e9..2ead310066d 100644 --- a/arch/sparc64/kernel/sbus.c +++ b/arch/sparc64/kernel/sbus.c @@ -11,15 +11,17 @@ #include <linux/slab.h> #include <linux/init.h> #include <linux/interrupt.h> +#include <linux/of.h> +#include <linux/of_device.h> #include <asm/page.h> -#include <asm/sbus.h> #include <asm/io.h> #include <asm/upa.h> #include <asm/cache.h> #include <asm/dma.h> #include <asm/irq.h> #include <asm/prom.h> +#include <asm/oplib.h> #include <asm/starfire.h> #include "iommu_common.h" @@ -52,13 +54,23 @@ #define STRBUF_TAG_VALID 0x02UL /* Enable 64-bit DVMA mode for the given device. */ -void sbus_set_sbus64(struct sbus_dev *sdev, int bursts) +void sbus_set_sbus64(struct device *dev, int bursts) { - struct iommu *iommu = sdev->ofdev.dev.archdata.iommu; - int slot = sdev->slot; + struct iommu *iommu = dev->archdata.iommu; + struct of_device *op = to_of_device(dev); + const struct linux_prom_registers *regs; unsigned long cfg_reg; + int slot; u64 val; + regs = of_get_property(op->node, "reg", NULL); + if (!regs) { + printk(KERN_ERR "sbus_set_sbus64: Cannot find regs for %s\n", + op->node->full_name); + return; + } + slot = regs->which_io; + cfg_reg = iommu->write_complete_reg; switch (slot) { case 0: @@ -191,10 +203,9 @@ static unsigned long sysio_imap_to_iclr(unsigned long imap) return imap + diff; } -unsigned int sbus_build_irq(void *buscookie, unsigned int ino) +static unsigned int sbus_build_irq(struct of_device *op, unsigned int ino) { - struct sbus_bus *sbus = (struct sbus_bus *)buscookie; - struct iommu *iommu = sbus->ofdev.dev.archdata.iommu; + struct iommu *iommu = op->dev.archdata.iommu; unsigned long reg_base = iommu->write_complete_reg - 0x2000UL; unsigned long imap, iclr; int sbus_level = 0; @@ -255,12 +266,12 @@ unsigned int sbus_build_irq(void *buscookie, unsigned int ino) #define SYSIO_UEAFSR_RESV2 0x0000001fffffffffUL /* Reserved */ static irqreturn_t sysio_ue_handler(int irq, void *dev_id) { - struct sbus_bus *sbus = dev_id; - struct iommu *iommu = sbus->ofdev.dev.archdata.iommu; + struct of_device *op = dev_id; + struct iommu *iommu = op->dev.archdata.iommu; unsigned long reg_base = iommu->write_complete_reg - 0x2000UL; unsigned long afsr_reg, afar_reg; unsigned long afsr, afar, error_bits; - int reported; + int reported, portid; afsr_reg = reg_base + SYSIO_UE_AFSR; afar_reg = reg_base + SYSIO_UE_AFAR; @@ -275,9 +286,11 @@ static irqreturn_t sysio_ue_handler(int irq, void *dev_id) SYSIO_UEAFSR_SPIO | SYSIO_UEAFSR_SDRD | SYSIO_UEAFSR_SDWR); upa_writeq(error_bits, afsr_reg); + portid = of_getintprop_default(op->node, "portid", -1); + /* Log the error. */ printk("SYSIO[%x]: Uncorrectable ECC Error, primary error type[%s]\n", - sbus->portid, + portid, (((error_bits & SYSIO_UEAFSR_PPIO) ? "PIO" : ((error_bits & SYSIO_UEAFSR_PDRD) ? @@ -285,12 +298,12 @@ static irqreturn_t sysio_ue_handler(int irq, void *dev_id) ((error_bits & SYSIO_UEAFSR_PDWR) ? "DVMA Write" : "???"))))); printk("SYSIO[%x]: DOFF[%lx] SIZE[%lx] MID[%lx]\n", - sbus->portid, + portid, (afsr & SYSIO_UEAFSR_DOFF) >> 45UL, (afsr & SYSIO_UEAFSR_SIZE) >> 42UL, (afsr & SYSIO_UEAFSR_MID) >> 37UL); - printk("SYSIO[%x]: AFAR[%016lx]\n", sbus->portid, afar); - printk("SYSIO[%x]: Secondary UE errors [", sbus->portid); + printk("SYSIO[%x]: AFAR[%016lx]\n", portid, afar); + printk("SYSIO[%x]: Secondary UE errors [", portid); reported = 0; if (afsr & SYSIO_UEAFSR_SPIO) { reported++; @@ -327,12 +340,12 @@ static irqreturn_t sysio_ue_handler(int irq, void *dev_id) #define SYSIO_CEAFSR_RESV2 0x0000001fffffffffUL /* Reserved */ static irqreturn_t sysio_ce_handler(int irq, void *dev_id) { - struct sbus_bus *sbus = dev_id; - struct iommu *iommu = sbus->ofdev.dev.archdata.iommu; + struct of_device *op = dev_id; + struct iommu *iommu = op->dev.archdata.iommu; unsigned long reg_base = iommu->write_complete_reg - 0x2000UL; unsigned long afsr_reg, afar_reg; unsigned long afsr, afar, error_bits; - int reported; + int reported, portid; afsr_reg = reg_base + SYSIO_CE_AFSR; afar_reg = reg_base + SYSIO_CE_AFAR; @@ -347,8 +360,10 @@ static irqreturn_t sysio_ce_handler(int irq, void *dev_id) SYSIO_CEAFSR_SPIO | SYSIO_CEAFSR_SDRD | SYSIO_CEAFSR_SDWR); upa_writeq(error_bits, afsr_reg); + portid = of_getintprop_default(op->node, "portid", -1); + printk("SYSIO[%x]: Correctable ECC Error, primary error type[%s]\n", - sbus->portid, + portid, (((error_bits & SYSIO_CEAFSR_PPIO) ? "PIO" : ((error_bits & SYSIO_CEAFSR_PDRD) ? @@ -360,14 +375,14 @@ static irqreturn_t sysio_ce_handler(int irq, void *dev_id) * XXX UDB CE trap handler does... -DaveM */ printk("SYSIO[%x]: DOFF[%lx] ECC Syndrome[%lx] Size[%lx] MID[%lx]\n", - sbus->portid, + portid, (afsr & SYSIO_CEAFSR_DOFF) >> 45UL, (afsr & SYSIO_CEAFSR_ESYND) >> 48UL, (afsr & SYSIO_CEAFSR_SIZE) >> 42UL, (afsr & SYSIO_CEAFSR_MID) >> 37UL); - printk("SYSIO[%x]: AFAR[%016lx]\n", sbus->portid, afar); + printk("SYSIO[%x]: AFAR[%016lx]\n", portid, afar); - printk("SYSIO[%x]: Secondary CE errors [", sbus->portid); + printk("SYSIO[%x]: Secondary CE errors [", portid); reported = 0; if (afsr & SYSIO_CEAFSR_SPIO) { reported++; @@ -404,11 +419,11 @@ static irqreturn_t sysio_ce_handler(int irq, void *dev_id) #define SYSIO_SBAFSR_RESV3 0x0000001fffffffffUL /* Reserved */ static irqreturn_t sysio_sbus_error_handler(int irq, void *dev_id) { - struct sbus_bus *sbus = dev_id; - struct iommu *iommu = sbus->ofdev.dev.archdata.iommu; + struct of_device *op = dev_id; + struct iommu *iommu = op->dev.archdata.iommu; unsigned long afsr_reg, afar_reg, reg_base; unsigned long afsr, afar, error_bits; - int reported; + int reported, portid; reg_base = iommu->write_complete_reg - 0x2000UL; afsr_reg = reg_base + SYSIO_SBUS_AFSR; @@ -423,9 +438,11 @@ static irqreturn_t sysio_sbus_error_handler(int irq, void *dev_id) SYSIO_SBAFSR_SLE | SYSIO_SBAFSR_STO | SYSIO_SBAFSR_SBERR); upa_writeq(error_bits, afsr_reg); + portid = of_getintprop_default(op->node, "portid", -1); + /* Log the error. */ printk("SYSIO[%x]: SBUS Error, primary error type[%s] read(%d)\n", - sbus->portid, + portid, (((error_bits & SYSIO_SBAFSR_PLE) ? "Late PIO Error" : ((error_bits & SYSIO_SBAFSR_PTO) ? @@ -434,11 +451,11 @@ static irqreturn_t sysio_sbus_error_handler(int irq, void *dev_id) "Error Ack" : "???")))), (afsr & SYSIO_SBAFSR_RD) ? 1 : 0); printk("SYSIO[%x]: size[%lx] MID[%lx]\n", - sbus->portid, + portid, (afsr & SYSIO_SBAFSR_SIZE) >> 42UL, (afsr & SYSIO_SBAFSR_MID) >> 37UL); - printk("SYSIO[%x]: AFAR[%016lx]\n", sbus->portid, afar); - printk("SYSIO[%x]: Secondary SBUS errors [", sbus->portid); + printk("SYSIO[%x]: AFAR[%016lx]\n", portid, afar); + printk("SYSIO[%x]: Secondary SBUS errors [", portid); reported = 0; if (afsr & SYSIO_SBAFSR_SLE) { reported++; @@ -470,34 +487,37 @@ static irqreturn_t sysio_sbus_error_handler(int irq, void *dev_id) #define SYSIO_CE_INO 0x35 #define SYSIO_SBUSERR_INO 0x36 -static void __init sysio_register_error_handlers(struct sbus_bus *sbus) +static void __init sysio_register_error_handlers(struct of_device *op) { - struct iommu *iommu = sbus->ofdev.dev.archdata.iommu; + struct iommu *iommu = op->dev.archdata.iommu; unsigned long reg_base = iommu->write_complete_reg - 0x2000UL; unsigned int irq; u64 control; + int portid; + + portid = of_getintprop_default(op->node, "portid", -1); - irq = sbus_build_irq(sbus, SYSIO_UE_INO); + irq = sbus_build_irq(op, SYSIO_UE_INO); if (request_irq(irq, sysio_ue_handler, 0, - "SYSIO_UE", sbus) < 0) { + "SYSIO_UE", op) < 0) { prom_printf("SYSIO[%x]: Cannot register UE interrupt.\n", - sbus->portid); + portid); prom_halt(); } - irq = sbus_build_irq(sbus, SYSIO_CE_INO); + irq = sbus_build_irq(op, SYSIO_CE_INO); if (request_irq(irq, sysio_ce_handler, 0, - "SYSIO_CE", sbus) < 0) { + "SYSIO_CE", op) < 0) { prom_printf("SYSIO[%x]: Cannot register CE interrupt.\n", - sbus->portid); + portid); prom_halt(); } - irq = sbus_build_irq(sbus, SYSIO_SBUSERR_INO); + irq = sbus_build_irq(op, SYSIO_SBUSERR_INO); if (request_irq(irq, sysio_sbus_error_handler, 0, - "SYSIO_SBERR", sbus) < 0) { + "SYSIO_SBERR", op) < 0) { prom_printf("SYSIO[%x]: Cannot register SBUS Error interrupt.\n", - sbus->portid); + portid); prom_halt(); } @@ -513,19 +533,15 @@ static void __init sysio_register_error_handlers(struct sbus_bus *sbus) } /* Boot time initialization. */ -static void __init sbus_iommu_init(int __node, struct sbus_bus *sbus) +static void __init sbus_iommu_init(struct of_device *op) { const struct linux_prom64_registers *pr; - struct device_node *dp; + struct device_node *dp = op->node; struct iommu *iommu; struct strbuf *strbuf; unsigned long regs, reg_base; + int i, portid; u64 control; - int i; - - dp = of_find_node_by_phandle(__node); - - sbus->portid = of_getintprop_default(dp, "upa-portid", -1); pr = of_get_property(dp, "reg", NULL); if (!pr) { @@ -542,9 +558,9 @@ static void __init sbus_iommu_init(int __node, struct sbus_bus *sbus) if (!strbuf) goto fatal_memory_error; - sbus->ofdev.dev.archdata.iommu = iommu; - sbus->ofdev.dev.archdata.stc = strbuf; - sbus->ofdev.dev.archdata.numa_node = -1; + op->dev.archdata.iommu = iommu; + op->dev.archdata.stc = strbuf; + op->dev.archdata.numa_node = -1; reg_base = regs + SYSIO_IOMMUREG_BASE; iommu->iommu_control = reg_base + IOMMU_CONTROL; @@ -572,8 +588,9 @@ static void __init sbus_iommu_init(int __node, struct sbus_bus *sbus) */ iommu->write_complete_reg = regs + 0x2000UL; - printk("SYSIO: UPA portID %x, at %016lx\n", - sbus->portid, regs); + portid = of_getintprop_default(op->node, "portid", -1); + printk(KERN_INFO "SYSIO: UPA portID %x, at %016lx\n", + portid, regs); /* Setup for TSB_SIZE=7, TBW_SIZE=0, MMU_DE=1, MMU_EN=1 */ if (iommu_table_init(iommu, IO_TSB_SIZE, MAP_BASE, 0xffffffff, -1)) @@ -631,56 +648,27 @@ static void __init sbus_iommu_init(int __node, struct sbus_bus *sbus) /* Now some Xfire specific grot... */ if (this_is_starfire) - starfire_hookup(sbus->portid); + starfire_hookup(portid); - sysio_register_error_handlers(sbus); + sysio_register_error_handlers(op); return; fatal_memory_error: prom_printf("sbus_iommu_init: Fatal memory allocation error.\n"); } -void sbus_fill_device_irq(struct sbus_dev *sdev) +static int __init sbus_init(void) { - struct device_node *dp = of_find_node_by_phandle(sdev->prom_node); - const struct linux_prom_irqs *irqs; - - irqs = of_get_property(dp, "interrupts", NULL); - if (!irqs) { - sdev->irqs[0] = 0; - sdev->num_irqs = 0; - } else { - unsigned int pri = irqs[0].pri; + struct device_node *dp; - sdev->num_irqs = 1; - if (pri < 0x20) - pri += sdev->slot * 8; + for_each_node_by_name(dp, "sbus") { + struct of_device *op = of_find_device_by_node(dp); - sdev->irqs[0] = sbus_build_irq(sdev->bus, pri); + sbus_iommu_init(op); + of_propagate_archdata(op); } -} -void __init sbus_arch_bus_ranges_init(struct device_node *pn, struct sbus_bus *sbus) -{ -} - -void __init sbus_setup_iommu(struct sbus_bus *sbus, struct device_node *dp) -{ - sbus_iommu_init(dp->node, sbus); -} - -void __init sbus_setup_arch_props(struct sbus_bus *sbus, struct device_node *dp) -{ -} - -int __init sbus_arch_preinit(void) -{ return 0; } -void __init sbus_arch_postinit(void) -{ - extern void firetruck_init(void); - - firetruck_init(); -} +subsys_initcall(sbus_init); diff --git a/arch/sparc64/kernel/smp.c b/arch/sparc64/kernel/smp.c index 2be166c544c..e5627118e61 100644 --- a/arch/sparc64/kernel/smp.c +++ b/arch/sparc64/kernel/smp.c @@ -21,6 +21,7 @@ #include <linux/jiffies.h> #include <linux/profile.h> #include <linux/lmb.h> +#include <linux/cpu.h> #include <asm/head.h> #include <asm/ptrace.h> @@ -115,6 +116,9 @@ void __cpuinit smp_callin(void) atomic_inc(&init_mm.mm_count); current->active_mm = &init_mm; + /* inform the notifiers about the new cpu */ + notify_cpu_starting(cpuid); + while (!cpu_isset(cpuid, smp_commenced_mask)) rmb(); diff --git a/arch/sparc64/kernel/sparc64_ksyms.c b/arch/sparc64/kernel/sparc64_ksyms.c index 0804f71df6c..30bba8b0a3b 100644 --- a/arch/sparc64/kernel/sparc64_ksyms.c +++ b/arch/sparc64/kernel/sparc64_ksyms.c @@ -36,7 +36,6 @@ #include <asm/elf.h> #include <asm/head.h> #include <asm/smp.h> -#include <asm/mostek.h> #include <asm/ptrace.h> #include <asm/uaccess.h> #include <asm/checksum.h> @@ -44,12 +43,8 @@ #include <asm/pgalloc.h> #include <asm/cacheflush.h> #ifdef CONFIG_SBUS -#include <asm/sbus.h> #include <asm/dma.h> #endif -#ifdef CONFIG_PCI -#include <asm/ebus.h> -#endif #include <asm/ns87303.h> #include <asm/timer.h> #include <asm/cpudata.h> @@ -68,7 +63,6 @@ extern void *__memscan_zero(void *, size_t); extern void *__memscan_generic(void *, int, size_t); extern int __memcmp(const void *, const void *, __kernel_size_t); extern __kernel_size_t strlen(const char *); -extern void syscall_trace(struct pt_regs *, int); extern void sys_sigsuspend(void); extern int compat_sys_ioctl(unsigned int fd, unsigned int cmd, u32 arg); extern int (*handle_mathemu)(struct pt_regs *, struct fpustate *); @@ -154,26 +148,12 @@ EXPORT_SYMBOL(flush_dcache_page); EXPORT_SYMBOL(__flush_dcache_range); #endif -EXPORT_SYMBOL(mostek_lock); -EXPORT_SYMBOL(mstk48t02_regs); #ifdef CONFIG_SUN_AUXIO EXPORT_SYMBOL(auxio_set_led); EXPORT_SYMBOL(auxio_set_lte); #endif #ifdef CONFIG_SBUS -EXPORT_SYMBOL(sbus_root); -EXPORT_SYMBOL(dma_chain); EXPORT_SYMBOL(sbus_set_sbus64); -EXPORT_SYMBOL(sbus_alloc_consistent); -EXPORT_SYMBOL(sbus_free_consistent); -EXPORT_SYMBOL(sbus_map_single); -EXPORT_SYMBOL(sbus_unmap_single); -EXPORT_SYMBOL(sbus_map_sg); -EXPORT_SYMBOL(sbus_unmap_sg); -EXPORT_SYMBOL(sbus_dma_sync_single_for_cpu); -EXPORT_SYMBOL(sbus_dma_sync_single_for_device); -EXPORT_SYMBOL(sbus_dma_sync_sg_for_cpu); -EXPORT_SYMBOL(sbus_dma_sync_sg_for_device); #endif EXPORT_SYMBOL(outsb); EXPORT_SYMBOL(outsw); @@ -182,7 +162,6 @@ EXPORT_SYMBOL(insb); EXPORT_SYMBOL(insw); EXPORT_SYMBOL(insl); #ifdef CONFIG_PCI -EXPORT_SYMBOL(ebus_chain); EXPORT_SYMBOL(pci_alloc_consistent); EXPORT_SYMBOL(pci_free_consistent); EXPORT_SYMBOL(pci_map_single); @@ -300,3 +279,5 @@ EXPORT_SYMBOL(xor_niagara_2); EXPORT_SYMBOL(xor_niagara_3); EXPORT_SYMBOL(xor_niagara_4); EXPORT_SYMBOL(xor_niagara_5); + +EXPORT_SYMBOL_GPL(real_hard_smp_processor_id); diff --git a/arch/sparc64/kernel/sstate.c b/arch/sparc64/kernel/sstate.c index 5b6e75b7f05..8cdbe5946b4 100644 --- a/arch/sparc64/kernel/sstate.c +++ b/arch/sparc64/kernel/sstate.c @@ -1,14 +1,15 @@ /* sstate.c: System soft state support. * - * Copyright (C) 2007 David S. Miller <davem@davemloft.net> + * Copyright (C) 2007, 2008 David S. Miller <davem@davemloft.net> */ #include <linux/kernel.h> #include <linux/notifier.h> +#include <linux/reboot.h> #include <linux/init.h> #include <asm/hypervisor.h> -#include <asm/sstate.h> +#include <asm/spitfire.h> #include <asm/oplib.h> #include <asm/head.h> #include <asm/io.h> @@ -50,31 +51,34 @@ static const char rebooting_msg[32] __attribute__((aligned(32))) = static const char panicing_msg[32] __attribute__((aligned(32))) = "Linux panicing"; -void sstate_booting(void) +static int sstate_reboot_call(struct notifier_block *np, unsigned long type, void *_unused) { - do_set_sstate(HV_SOFT_STATE_TRANSITION, booting_msg); -} + const char *msg; -void sstate_running(void) -{ - do_set_sstate(HV_SOFT_STATE_NORMAL, running_msg); -} + switch (type) { + case SYS_DOWN: + default: + msg = rebooting_msg; + break; -void sstate_halt(void) -{ - do_set_sstate(HV_SOFT_STATE_TRANSITION, halting_msg); -} + case SYS_HALT: + msg = halting_msg; + break; -void sstate_poweroff(void) -{ - do_set_sstate(HV_SOFT_STATE_TRANSITION, poweroff_msg); -} + case SYS_POWER_OFF: + msg = poweroff_msg; + break; + } -void sstate_reboot(void) -{ - do_set_sstate(HV_SOFT_STATE_TRANSITION, rebooting_msg); + do_set_sstate(HV_SOFT_STATE_TRANSITION, msg); + + return NOTIFY_OK; } +static struct notifier_block sstate_reboot_notifier = { + .notifier_call = sstate_reboot_call, +}; + static int sstate_panic_event(struct notifier_block *n, unsigned long event, void *ptr) { do_set_sstate(HV_SOFT_STATE_TRANSITION, panicing_msg); @@ -87,18 +91,37 @@ static struct notifier_block sstate_panic_block = { .priority = INT_MAX, }; -void __init sun4v_sstate_init(void) +static int __init sstate_init(void) { unsigned long major, minor; + if (tlb_type != hypervisor) + return 0; + major = 1; minor = 0; if (sun4v_hvapi_register(HV_GRP_SOFT_STATE, major, &minor)) - return; + return 0; hv_supports_soft_state = 1; prom_sun4v_guest_soft_state(); + + do_set_sstate(HV_SOFT_STATE_TRANSITION, booting_msg); + atomic_notifier_chain_register(&panic_notifier_list, &sstate_panic_block); + register_reboot_notifier(&sstate_reboot_notifier); + + return 0; } + +core_initcall(sstate_init); + +static int __init sstate_running(void) +{ + do_set_sstate(HV_SOFT_STATE_NORMAL, running_msg); + return 0; +} + +late_initcall(sstate_running); diff --git a/arch/sparc64/kernel/starfire.c b/arch/sparc64/kernel/starfire.c index 7461581b3bb..060d0f3a615 100644 --- a/arch/sparc64/kernel/starfire.c +++ b/arch/sparc64/kernel/starfire.c @@ -28,11 +28,6 @@ void check_if_starfire(void) this_is_starfire = 1; } -void starfire_cpu_setup(void) -{ - /* Currently, nothing to do. */ -} - int starfire_hard_smp_processor_id(void) { return upa_readl(0x1fff40000d0UL); diff --git a/arch/sparc64/kernel/sys_sparc32.c b/arch/sparc64/kernel/sys_sparc32.c index 3d118531baf..3320c9d0075 100644 --- a/arch/sparc64/kernel/sys_sparc32.c +++ b/arch/sparc64/kernel/sys_sparc32.c @@ -575,14 +575,6 @@ asmlinkage long sys32_settimeofday(struct compat_timeval __user *tv, return do_sys_settimeofday(tv ? &kts : NULL, tz ? &ktz : NULL); } -/* These are here just in case some old sparc32 binary calls it. */ -asmlinkage long sys32_pause(void) -{ - current->state = TASK_INTERRUPTIBLE; - schedule(); - return -ERESTARTNOHAND; -} - asmlinkage compat_ssize_t sys32_pread64(unsigned int fd, char __user *ubuf, compat_size_t count, diff --git a/arch/sparc64/kernel/syscalls.S b/arch/sparc64/kernel/syscalls.S index a2f24270ed8..7a6786a7136 100644 --- a/arch/sparc64/kernel/syscalls.S +++ b/arch/sparc64/kernel/syscalls.S @@ -65,9 +65,8 @@ sys32_rt_sigreturn: andcc %l5, (_TIF_SYSCALL_TRACE|_TIF_SECCOMP|_TIF_SYSCALL_AUDIT), %g0 be,pt %icc, rtrap nop - add %sp, PTREGS_OFF, %o0 - call syscall_trace - mov 1, %o1 + call syscall_trace_leave + add %sp, PTREGS_OFF, %o0 ba,pt %xcc, rtrap nop @@ -159,9 +158,8 @@ linux_sparc_ni_syscall: or %l7, %lo(sys_ni_syscall), %l7 linux_syscall_trace32: - add %sp, PTREGS_OFF, %o0 - call syscall_trace - clr %o1 + call syscall_trace_enter + add %sp, PTREGS_OFF, %o0 brnz,pn %o0, 3f mov -ENOSYS, %o0 srl %i0, 0, %o0 @@ -172,9 +170,8 @@ linux_syscall_trace32: srl %i3, 0, %o3 linux_syscall_trace: - add %sp, PTREGS_OFF, %o0 - call syscall_trace - clr %o1 + call syscall_trace_enter + add %sp, PTREGS_OFF, %o0 brnz,pn %o0, 3f mov -ENOSYS, %o0 mov %i0, %o0 @@ -275,9 +272,8 @@ ret_sys_call: b,pt %xcc, rtrap stx %l2, [%sp + PTREGS_OFF + PT_V9_TNPC] linux_syscall_trace2: - add %sp, PTREGS_OFF, %o0 - call syscall_trace - mov 1, %o1 + call syscall_trace_leave + add %sp, PTREGS_OFF, %o0 stx %l1, [%sp + PTREGS_OFF + PT_V9_TPC] ba,pt %xcc, rtrap stx %l2, [%sp + PTREGS_OFF + PT_V9_TNPC] diff --git a/arch/sparc64/kernel/systbls.S b/arch/sparc64/kernel/systbls.S index 0fdbf3ba956..5daee4b04dd 100644 --- a/arch/sparc64/kernel/systbls.S +++ b/arch/sparc64/kernel/systbls.S @@ -23,7 +23,7 @@ sys_call_table32: /*10*/ .word sys_unlink, sunos_execv, sys_chdir, sys_chown16, sys32_mknod /*15*/ .word sys_chmod, sys_lchown16, sparc_brk, sys32_perfctr, sys32_lseek /*20*/ .word sys_getpid, sys_capget, sys_capset, sys_setuid16, sys_getuid16 -/*25*/ .word sys32_vmsplice, compat_sys_ptrace, sys_alarm, sys32_sigaltstack, sys32_pause +/*25*/ .word sys32_vmsplice, compat_sys_ptrace, sys_alarm, sys32_sigaltstack, sys_pause /*30*/ .word compat_sys_utime, sys_lchown, sys_fchown, sys32_access, sys32_nice .word sys_chown, sys_sync, sys32_kill, compat_sys_newstat, sys32_sendfile /*40*/ .word compat_sys_newlstat, sys_dup, sys_pipe, compat_sys_times, sys_getuid diff --git a/arch/sparc64/kernel/time.c b/arch/sparc64/kernel/time.c index cc16fdcf98a..80d71a5ce1e 100644 --- a/arch/sparc64/kernel/time.c +++ b/arch/sparc64/kernel/time.c @@ -30,13 +30,14 @@ #include <linux/percpu.h> #include <linux/miscdevice.h> #include <linux/rtc.h> +#include <linux/rtc/m48t59.h> #include <linux/kernel_stat.h> #include <linux/clockchips.h> #include <linux/clocksource.h> #include <linux/of_device.h> +#include <linux/platform_device.h> #include <asm/oplib.h> -#include <asm/mostek.h> #include <asm/timer.h> #include <asm/irq.h> #include <asm/io.h> @@ -50,18 +51,7 @@ #include "entry.h" -DEFINE_SPINLOCK(mostek_lock); DEFINE_SPINLOCK(rtc_lock); -void __iomem *mstk48t02_regs = NULL; -#ifdef CONFIG_PCI -unsigned long ds1287_regs = 0UL; -static void __iomem *bq4802_regs; -#endif - -static void __iomem *mstk48t08_regs; -static void __iomem *mstk48t59_regs; - -static int set_rtc_mmss(unsigned long); #define TICK_PRIV_BIT (1UL << 63) #define TICKCMP_IRQ_BIT (1UL << 63) @@ -405,313 +395,167 @@ static unsigned long timer_ticks_per_nsec_quotient __read_mostly; int update_persistent_clock(struct timespec now) { - return set_rtc_mmss(now.tv_sec); -} + struct rtc_device *rtc = rtc_class_open("rtc0"); + int err = -1; -/* Kick start a stopped clock (procedure from the Sun NVRAM/hostid FAQ). */ -static void __init kick_start_clock(void) -{ - void __iomem *regs = mstk48t02_regs; - u8 sec, tmp; - int i, count; - - prom_printf("CLOCK: Clock was stopped. Kick start "); - - spin_lock_irq(&mostek_lock); - - /* Turn on the kick start bit to start the oscillator. */ - tmp = mostek_read(regs + MOSTEK_CREG); - tmp |= MSTK_CREG_WRITE; - mostek_write(regs + MOSTEK_CREG, tmp); - tmp = mostek_read(regs + MOSTEK_SEC); - tmp &= ~MSTK_STOP; - mostek_write(regs + MOSTEK_SEC, tmp); - tmp = mostek_read(regs + MOSTEK_HOUR); - tmp |= MSTK_KICK_START; - mostek_write(regs + MOSTEK_HOUR, tmp); - tmp = mostek_read(regs + MOSTEK_CREG); - tmp &= ~MSTK_CREG_WRITE; - mostek_write(regs + MOSTEK_CREG, tmp); - - spin_unlock_irq(&mostek_lock); - - /* Delay to allow the clock oscillator to start. */ - sec = MSTK_REG_SEC(regs); - for (i = 0; i < 3; i++) { - while (sec == MSTK_REG_SEC(regs)) - for (count = 0; count < 100000; count++) - /* nothing */ ; - prom_printf("."); - sec = MSTK_REG_SEC(regs); - } - prom_printf("\n"); - - spin_lock_irq(&mostek_lock); - - /* Turn off kick start and set a "valid" time and date. */ - tmp = mostek_read(regs + MOSTEK_CREG); - tmp |= MSTK_CREG_WRITE; - mostek_write(regs + MOSTEK_CREG, tmp); - tmp = mostek_read(regs + MOSTEK_HOUR); - tmp &= ~MSTK_KICK_START; - mostek_write(regs + MOSTEK_HOUR, tmp); - MSTK_SET_REG_SEC(regs,0); - MSTK_SET_REG_MIN(regs,0); - MSTK_SET_REG_HOUR(regs,0); - MSTK_SET_REG_DOW(regs,5); - MSTK_SET_REG_DOM(regs,1); - MSTK_SET_REG_MONTH(regs,8); - MSTK_SET_REG_YEAR(regs,1996 - MSTK_YEAR_ZERO); - tmp = mostek_read(regs + MOSTEK_CREG); - tmp &= ~MSTK_CREG_WRITE; - mostek_write(regs + MOSTEK_CREG, tmp); - - spin_unlock_irq(&mostek_lock); - - /* Ensure the kick start bit is off. If it isn't, turn it off. */ - while (mostek_read(regs + MOSTEK_HOUR) & MSTK_KICK_START) { - prom_printf("CLOCK: Kick start still on!\n"); - - spin_lock_irq(&mostek_lock); - - tmp = mostek_read(regs + MOSTEK_CREG); - tmp |= MSTK_CREG_WRITE; - mostek_write(regs + MOSTEK_CREG, tmp); - - tmp = mostek_read(regs + MOSTEK_HOUR); - tmp &= ~MSTK_KICK_START; - mostek_write(regs + MOSTEK_HOUR, tmp); - - tmp = mostek_read(regs + MOSTEK_CREG); - tmp &= ~MSTK_CREG_WRITE; - mostek_write(regs + MOSTEK_CREG, tmp); - - spin_unlock_irq(&mostek_lock); + if (rtc) { + err = rtc_set_mmss(rtc, now.tv_sec); + rtc_class_close(rtc); } - prom_printf("CLOCK: Kick start procedure successful.\n"); + return err; } -/* Return nonzero if the clock chip battery is low. */ -static int __init has_low_battery(void) -{ - void __iomem *regs = mstk48t02_regs; - u8 data1, data2; - - spin_lock_irq(&mostek_lock); +unsigned long cmos_regs; +EXPORT_SYMBOL(cmos_regs); - data1 = mostek_read(regs + MOSTEK_EEPROM); /* Read some data. */ - mostek_write(regs + MOSTEK_EEPROM, ~data1); /* Write back the complement. */ - data2 = mostek_read(regs + MOSTEK_EEPROM); /* Read back the complement. */ - mostek_write(regs + MOSTEK_EEPROM, data1); /* Restore original value. */ +static struct resource rtc_cmos_resource; - spin_unlock_irq(&mostek_lock); - - return (data1 == data2); /* Was the write blocked? */ -} +static struct platform_device rtc_cmos_device = { + .name = "rtc_cmos", + .id = -1, + .resource = &rtc_cmos_resource, + .num_resources = 1, +}; -static void __init mostek_set_system_time(void __iomem *mregs) +static int __devinit rtc_probe(struct of_device *op, const struct of_device_id *match) { - unsigned int year, mon, day, hour, min, sec; - u8 tmp; - - spin_lock_irq(&mostek_lock); + struct resource *r; - /* Traditional Mostek chip. */ - tmp = mostek_read(mregs + MOSTEK_CREG); - tmp |= MSTK_CREG_READ; - mostek_write(mregs + MOSTEK_CREG, tmp); + printk(KERN_INFO "%s: RTC regs at 0x%lx\n", + op->node->full_name, op->resource[0].start); - sec = MSTK_REG_SEC(mregs); - min = MSTK_REG_MIN(mregs); - hour = MSTK_REG_HOUR(mregs); - day = MSTK_REG_DOM(mregs); - mon = MSTK_REG_MONTH(mregs); - year = MSTK_CVT_YEAR( MSTK_REG_YEAR(mregs) ); - - xtime.tv_sec = mktime(year, mon, day, hour, min, sec); - xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ); - set_normalized_timespec(&wall_to_monotonic, - -xtime.tv_sec, -xtime.tv_nsec); + /* The CMOS RTC driver only accepts IORESOURCE_IO, so cons + * up a fake resource so that the probe works for all cases. + * When the RTC is behind an ISA bus it will have IORESOURCE_IO + * already, whereas when it's behind EBUS is will be IORESOURCE_MEM. + */ - tmp = mostek_read(mregs + MOSTEK_CREG); - tmp &= ~MSTK_CREG_READ; - mostek_write(mregs + MOSTEK_CREG, tmp); + r = &rtc_cmos_resource; + r->flags = IORESOURCE_IO; + r->name = op->resource[0].name; + r->start = op->resource[0].start; + r->end = op->resource[0].end; - spin_unlock_irq(&mostek_lock); + cmos_regs = op->resource[0].start; + return platform_device_register(&rtc_cmos_device); } -/* Probe for the real time clock chip. */ -static void __init set_system_time(void) -{ - unsigned int year, mon, day, hour, min, sec; - void __iomem *mregs = mstk48t02_regs; -#ifdef CONFIG_PCI - unsigned long dregs = ds1287_regs; - void __iomem *bregs = bq4802_regs; -#else - unsigned long dregs = 0UL; - void __iomem *bregs = 0UL; -#endif - - if (!mregs && !dregs && !bregs) { - prom_printf("Something wrong, clock regs not mapped yet.\n"); - prom_halt(); - } - - if (mregs) { - mostek_set_system_time(mregs); - return; - } - - if (bregs) { - unsigned char val = readb(bregs + 0x0e); - unsigned int century; +static struct of_device_id __initdata rtc_match[] = { + { + .name = "rtc", + .compatible = "m5819", + }, + { + .name = "rtc", + .compatible = "isa-m5819p", + }, + { + .name = "rtc", + .compatible = "isa-m5823p", + }, + { + .name = "rtc", + .compatible = "ds1287", + }, + {}, +}; - /* BQ4802 RTC chip. */ +static struct of_platform_driver rtc_driver = { + .match_table = rtc_match, + .probe = rtc_probe, + .driver = { + .name = "rtc", + }, +}; - writeb(val | 0x08, bregs + 0x0e); +static struct platform_device rtc_bq4802_device = { + .name = "rtc-bq4802", + .id = -1, + .num_resources = 1, +}; - sec = readb(bregs + 0x00); - min = readb(bregs + 0x02); - hour = readb(bregs + 0x04); - day = readb(bregs + 0x06); - mon = readb(bregs + 0x09); - year = readb(bregs + 0x0a); - century = readb(bregs + 0x0f); +static int __devinit bq4802_probe(struct of_device *op, const struct of_device_id *match) +{ - writeb(val, bregs + 0x0e); + printk(KERN_INFO "%s: BQ4802 regs at 0x%lx\n", + op->node->full_name, op->resource[0].start); - BCD_TO_BIN(sec); - BCD_TO_BIN(min); - BCD_TO_BIN(hour); - BCD_TO_BIN(day); - BCD_TO_BIN(mon); - BCD_TO_BIN(year); - BCD_TO_BIN(century); + rtc_bq4802_device.resource = &op->resource[0]; + return platform_device_register(&rtc_bq4802_device); +} - year += (century * 100); - } else { - /* Dallas 12887 RTC chip. */ - - do { - sec = CMOS_READ(RTC_SECONDS); - min = CMOS_READ(RTC_MINUTES); - hour = CMOS_READ(RTC_HOURS); - day = CMOS_READ(RTC_DAY_OF_MONTH); - mon = CMOS_READ(RTC_MONTH); - year = CMOS_READ(RTC_YEAR); - } while (sec != CMOS_READ(RTC_SECONDS)); - - if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { - BCD_TO_BIN(sec); - BCD_TO_BIN(min); - BCD_TO_BIN(hour); - BCD_TO_BIN(day); - BCD_TO_BIN(mon); - BCD_TO_BIN(year); - } - if ((year += 1900) < 1970) - year += 100; - } +static struct of_device_id __initdata bq4802_match[] = { + { + .name = "rtc", + .compatible = "bq4802", + }, +}; - xtime.tv_sec = mktime(year, mon, day, hour, min, sec); - xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ); - set_normalized_timespec(&wall_to_monotonic, - -xtime.tv_sec, -xtime.tv_nsec); -} +static struct of_platform_driver bq4802_driver = { + .match_table = bq4802_match, + .probe = bq4802_probe, + .driver = { + .name = "bq4802", + }, +}; -/* davem suggests we keep this within the 4M locked kernel image */ -static u32 starfire_get_time(void) +static unsigned char mostek_read_byte(struct device *dev, u32 ofs) { - static char obp_gettod[32]; - static u32 unix_tod; + struct platform_device *pdev = to_platform_device(dev); + struct m48t59_plat_data *pdata = pdev->dev.platform_data; + void __iomem *regs; + unsigned char val; - sprintf(obp_gettod, "h# %08x unix-gettod", - (unsigned int) (long) &unix_tod); - prom_feval(obp_gettod); + regs = (void __iomem *) pdev->resource[0].start; + val = readb(regs + ofs); - return unix_tod; + /* the year 0 is 1968 */ + if (ofs == pdata->offset + M48T59_YEAR) { + val += 0x68; + if ((val & 0xf) > 9) + val += 6; + } + return val; } -static int starfire_set_time(u32 val) +static void mostek_write_byte(struct device *dev, u32 ofs, u8 val) { - /* Do nothing, time is set using the service processor - * console on this platform. - */ - return 0; -} + struct platform_device *pdev = to_platform_device(dev); + struct m48t59_plat_data *pdata = pdev->dev.platform_data; + void __iomem *regs; -static u32 hypervisor_get_time(void) -{ - unsigned long ret, time; - int retries = 10000; - -retry: - ret = sun4v_tod_get(&time); - if (ret == HV_EOK) - return time; - if (ret == HV_EWOULDBLOCK) { - if (--retries > 0) { - udelay(100); - goto retry; - } - printk(KERN_WARNING "SUN4V: tod_get() timed out.\n"); - return 0; + regs = (void __iomem *) pdev->resource[0].start; + if (ofs == pdata->offset + M48T59_YEAR) { + if (val < 0x68) + val += 0x32; + else + val -= 0x68; + if ((val & 0xf) > 9) + val += 6; + if ((val & 0xf0) > 0x9A) + val += 0x60; } - printk(KERN_WARNING "SUN4V: tod_get() not supported.\n"); - return 0; + writeb(val, regs + ofs); } -static int hypervisor_set_time(u32 secs) -{ - unsigned long ret; - int retries = 10000; - -retry: - ret = sun4v_tod_set(secs); - if (ret == HV_EOK) - return 0; - if (ret == HV_EWOULDBLOCK) { - if (--retries > 0) { - udelay(100); - goto retry; - } - printk(KERN_WARNING "SUN4V: tod_set() timed out.\n"); - return -EAGAIN; - } - printk(KERN_WARNING "SUN4V: tod_set() not supported.\n"); - return -EOPNOTSUPP; -} +static struct m48t59_plat_data m48t59_data = { + .read_byte = mostek_read_byte, + .write_byte = mostek_write_byte, +}; -static int __init clock_model_matches(const char *model) -{ - if (strcmp(model, "mk48t02") && - strcmp(model, "mk48t08") && - strcmp(model, "mk48t59") && - strcmp(model, "m5819") && - strcmp(model, "m5819p") && - strcmp(model, "m5823") && - strcmp(model, "ds1287") && - strcmp(model, "bq4802")) - return 0; - - return 1; -} +static struct platform_device m48t59_rtc = { + .name = "rtc-m48t59", + .id = 0, + .num_resources = 1, + .dev = { + .platform_data = &m48t59_data, + }, +}; -static int __devinit clock_probe(struct of_device *op, const struct of_device_id *match) +static int __devinit mostek_probe(struct of_device *op, const struct of_device_id *match) { struct device_node *dp = op->node; - const char *model = of_get_property(dp, "model", NULL); - const char *compat = of_get_property(dp, "compatible", NULL); - unsigned long size, flags; - void __iomem *regs; - - if (!model) - model = compat; - - if (!model || !clock_model_matches(model)) - return -ENODEV; /* On an Enterprise system there can be multiple mostek clocks. * We should only match the one that is on the central FHC bus. @@ -720,88 +564,51 @@ static int __devinit clock_probe(struct of_device *op, const struct of_device_id strcmp(dp->parent->parent->name, "central") != 0) return -ENODEV; - size = (op->resource[0].end - op->resource[0].start) + 1; - regs = of_ioremap(&op->resource[0], 0, size, "clock"); - if (!regs) - return -ENOMEM; - -#ifdef CONFIG_PCI - if (!strcmp(model, "ds1287") || - !strcmp(model, "m5819") || - !strcmp(model, "m5819p") || - !strcmp(model, "m5823")) { - ds1287_regs = (unsigned long) regs; - } else if (!strcmp(model, "bq4802")) { - bq4802_regs = regs; - } else -#endif - if (model[5] == '0' && model[6] == '2') { - mstk48t02_regs = regs; - } else if(model[5] == '0' && model[6] == '8') { - mstk48t08_regs = regs; - mstk48t02_regs = mstk48t08_regs + MOSTEK_48T08_48T02; - } else { - mstk48t59_regs = regs; - mstk48t02_regs = mstk48t59_regs + MOSTEK_48T59_48T02; - } - - printk(KERN_INFO "%s: Clock regs at %p\n", dp->full_name, regs); - - local_irq_save(flags); - - if (mstk48t02_regs != NULL) { - /* Report a low battery voltage condition. */ - if (has_low_battery()) - prom_printf("NVRAM: Low battery voltage!\n"); - - /* Kick start the clock if it is completely stopped. */ - if (mostek_read(mstk48t02_regs + MOSTEK_SEC) & MSTK_STOP) - kick_start_clock(); - } - - set_system_time(); - - local_irq_restore(flags); + printk(KERN_INFO "%s: Mostek regs at 0x%lx\n", + dp->full_name, op->resource[0].start); - return 0; + m48t59_rtc.resource = &op->resource[0]; + return platform_device_register(&m48t59_rtc); } -static struct of_device_id clock_match[] = { +static struct of_device_id __initdata mostek_match[] = { { .name = "eeprom", }, - { - .name = "rtc", - }, {}, }; -static struct of_platform_driver clock_driver = { - .match_table = clock_match, - .probe = clock_probe, +static struct of_platform_driver mostek_driver = { + .match_table = mostek_match, + .probe = mostek_probe, .driver = { - .name = "clock", + .name = "mostek", }, }; +static struct platform_device rtc_sun4v_device = { + .name = "rtc-sun4v", + .id = -1, +}; + +static struct platform_device rtc_starfire_device = { + .name = "rtc-starfire", + .id = -1, +}; + static int __init clock_init(void) { - if (this_is_starfire) { - xtime.tv_sec = starfire_get_time(); - xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ); - set_normalized_timespec(&wall_to_monotonic, - -xtime.tv_sec, -xtime.tv_nsec); - return 0; - } - if (tlb_type == hypervisor) { - xtime.tv_sec = hypervisor_get_time(); - xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ); - set_normalized_timespec(&wall_to_monotonic, - -xtime.tv_sec, -xtime.tv_nsec); - return 0; - } + if (this_is_starfire) + return platform_device_register(&rtc_starfire_device); + + if (tlb_type == hypervisor) + return platform_device_register(&rtc_sun4v_device); + + (void) of_register_driver(&rtc_driver, &of_platform_bus_type); + (void) of_register_driver(&mostek_driver, &of_platform_bus_type); + (void) of_register_driver(&bq4802_driver, &of_platform_bus_type); - return of_register_driver(&clock_driver, &of_platform_bus_type); + return 0; } /* Must be after subsys_initcall() so that busses are probed. Must @@ -814,7 +621,7 @@ fs_initcall(clock_init); static unsigned long sparc64_init_timers(void) { struct device_node *dp; - unsigned long clock; + unsigned long freq; dp = of_find_node_by_path("/"); if (tlb_type == spitfire) { @@ -827,17 +634,17 @@ static unsigned long sparc64_init_timers(void) if (manuf == 0x17 && impl == 0x13) { /* Hummingbird, aka Ultra-IIe */ tick_ops = &hbtick_operations; - clock = of_getintprop_default(dp, "stick-frequency", 0); + freq = of_getintprop_default(dp, "stick-frequency", 0); } else { tick_ops = &tick_operations; - clock = local_cpu_data().clock_tick; + freq = local_cpu_data().clock_tick; } } else { tick_ops = &stick_operations; - clock = of_getintprop_default(dp, "stick-frequency", 0); + freq = of_getintprop_default(dp, "stick-frequency", 0); } - return clock; + return freq; } struct freq_table { @@ -1029,16 +836,16 @@ EXPORT_SYMBOL(udelay); void __init time_init(void) { - unsigned long clock = sparc64_init_timers(); + unsigned long freq = sparc64_init_timers(); - tb_ticks_per_usec = clock / USEC_PER_SEC; + tb_ticks_per_usec = freq / USEC_PER_SEC; timer_ticks_per_nsec_quotient = - clocksource_hz2mult(clock, SPARC64_NSEC_PER_CYC_SHIFT); + clocksource_hz2mult(freq, SPARC64_NSEC_PER_CYC_SHIFT); clocksource_tick.name = tick_ops->name; clocksource_tick.mult = - clocksource_hz2mult(clock, + clocksource_hz2mult(freq, clocksource_tick.shift); clocksource_tick.read = tick_ops->get_tick; @@ -1049,7 +856,7 @@ void __init time_init(void) sparc64_clockevent.name = tick_ops->name; - setup_clockevent_multiplier(clock); + setup_clockevent_multiplier(freq); sparc64_clockevent.max_delta_ns = clockevent_delta2ns(0x7fffffffffffffffUL, &sparc64_clockevent); @@ -1070,672 +877,8 @@ unsigned long long sched_clock(void) >> SPARC64_NSEC_PER_CYC_SHIFT; } -static int set_rtc_mmss(unsigned long nowtime) -{ - int real_seconds, real_minutes, chip_minutes; - void __iomem *mregs = mstk48t02_regs; -#ifdef CONFIG_PCI - unsigned long dregs = ds1287_regs; - void __iomem *bregs = bq4802_regs; -#else - unsigned long dregs = 0UL; - void __iomem *bregs = 0UL; -#endif - unsigned long flags; - u8 tmp; - - /* - * Not having a register set can lead to trouble. - * Also starfire doesn't have a tod clock. - */ - if (!mregs && !dregs && !bregs) - return -1; - - if (mregs) { - spin_lock_irqsave(&mostek_lock, flags); - - /* Read the current RTC minutes. */ - tmp = mostek_read(mregs + MOSTEK_CREG); - tmp |= MSTK_CREG_READ; - mostek_write(mregs + MOSTEK_CREG, tmp); - - chip_minutes = MSTK_REG_MIN(mregs); - - tmp = mostek_read(mregs + MOSTEK_CREG); - tmp &= ~MSTK_CREG_READ; - mostek_write(mregs + MOSTEK_CREG, tmp); - - /* - * since we're only adjusting minutes and seconds, - * don't interfere with hour overflow. This avoids - * messing with unknown time zones but requires your - * RTC not to be off by more than 15 minutes - */ - real_seconds = nowtime % 60; - real_minutes = nowtime / 60; - if (((abs(real_minutes - chip_minutes) + 15)/30) & 1) - real_minutes += 30; /* correct for half hour time zone */ - real_minutes %= 60; - - if (abs(real_minutes - chip_minutes) < 30) { - tmp = mostek_read(mregs + MOSTEK_CREG); - tmp |= MSTK_CREG_WRITE; - mostek_write(mregs + MOSTEK_CREG, tmp); - - MSTK_SET_REG_SEC(mregs,real_seconds); - MSTK_SET_REG_MIN(mregs,real_minutes); - - tmp = mostek_read(mregs + MOSTEK_CREG); - tmp &= ~MSTK_CREG_WRITE; - mostek_write(mregs + MOSTEK_CREG, tmp); - - spin_unlock_irqrestore(&mostek_lock, flags); - - return 0; - } else { - spin_unlock_irqrestore(&mostek_lock, flags); - - return -1; - } - } else if (bregs) { - int retval = 0; - unsigned char val = readb(bregs + 0x0e); - - /* BQ4802 RTC chip. */ - - writeb(val | 0x08, bregs + 0x0e); - - chip_minutes = readb(bregs + 0x02); - BCD_TO_BIN(chip_minutes); - real_seconds = nowtime % 60; - real_minutes = nowtime / 60; - if (((abs(real_minutes - chip_minutes) + 15)/30) & 1) - real_minutes += 30; - real_minutes %= 60; - - if (abs(real_minutes - chip_minutes) < 30) { - BIN_TO_BCD(real_seconds); - BIN_TO_BCD(real_minutes); - writeb(real_seconds, bregs + 0x00); - writeb(real_minutes, bregs + 0x02); - } else { - printk(KERN_WARNING - "set_rtc_mmss: can't update from %d to %d\n", - chip_minutes, real_minutes); - retval = -1; - } - - writeb(val, bregs + 0x0e); - - return retval; - } else { - int retval = 0; - unsigned char save_control, save_freq_select; - - /* Stolen from arch/i386/kernel/time.c, see there for - * credits and descriptive comments. - */ - spin_lock_irqsave(&rtc_lock, flags); - save_control = CMOS_READ(RTC_CONTROL); /* tell the clock it's being set */ - CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL); - - save_freq_select = CMOS_READ(RTC_FREQ_SELECT); /* stop and reset prescaler */ - CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT); - - chip_minutes = CMOS_READ(RTC_MINUTES); - if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) - BCD_TO_BIN(chip_minutes); - real_seconds = nowtime % 60; - real_minutes = nowtime / 60; - if (((abs(real_minutes - chip_minutes) + 15)/30) & 1) - real_minutes += 30; - real_minutes %= 60; - - if (abs(real_minutes - chip_minutes) < 30) { - if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { - BIN_TO_BCD(real_seconds); - BIN_TO_BCD(real_minutes); - } - CMOS_WRITE(real_seconds,RTC_SECONDS); - CMOS_WRITE(real_minutes,RTC_MINUTES); - } else { - printk(KERN_WARNING - "set_rtc_mmss: can't update from %d to %d\n", - chip_minutes, real_minutes); - retval = -1; - } - - CMOS_WRITE(save_control, RTC_CONTROL); - CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT); - spin_unlock_irqrestore(&rtc_lock, flags); - - return retval; - } -} - -#define RTC_IS_OPEN 0x01 /* means /dev/rtc is in use */ -static unsigned char mini_rtc_status; /* bitmapped status byte. */ - -#define FEBRUARY 2 -#define STARTOFTIME 1970 -#define SECDAY 86400L -#define SECYR (SECDAY * 365) -#define leapyear(year) ((year) % 4 == 0 && \ - ((year) % 100 != 0 || (year) % 400 == 0)) -#define days_in_year(a) (leapyear(a) ? 366 : 365) -#define days_in_month(a) (month_days[(a) - 1]) - -static int month_days[12] = { - 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 -}; - -/* - * This only works for the Gregorian calendar - i.e. after 1752 (in the UK) - */ -static void GregorianDay(struct rtc_time * tm) -{ - int leapsToDate; - int lastYear; - int day; - int MonthOffset[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; - - lastYear = tm->tm_year - 1; - - /* - * Number of leap corrections to apply up to end of last year - */ - leapsToDate = lastYear / 4 - lastYear / 100 + lastYear / 400; - - /* - * This year is a leap year if it is divisible by 4 except when it is - * divisible by 100 unless it is divisible by 400 - * - * e.g. 1904 was a leap year, 1900 was not, 1996 is, and 2000 was - */ - day = tm->tm_mon > 2 && leapyear(tm->tm_year); - - day += lastYear*365 + leapsToDate + MonthOffset[tm->tm_mon-1] + - tm->tm_mday; - - tm->tm_wday = day % 7; -} - -static void to_tm(int tim, struct rtc_time *tm) -{ - register int i; - register long hms, day; - - day = tim / SECDAY; - hms = tim % SECDAY; - - /* Hours, minutes, seconds are easy */ - tm->tm_hour = hms / 3600; - tm->tm_min = (hms % 3600) / 60; - tm->tm_sec = (hms % 3600) % 60; - - /* Number of years in days */ - for (i = STARTOFTIME; day >= days_in_year(i); i++) - day -= days_in_year(i); - tm->tm_year = i; - - /* Number of months in days left */ - if (leapyear(tm->tm_year)) - days_in_month(FEBRUARY) = 29; - for (i = 1; day >= days_in_month(i); i++) - day -= days_in_month(i); - days_in_month(FEBRUARY) = 28; - tm->tm_mon = i; - - /* Days are what is left over (+1) from all that. */ - tm->tm_mday = day + 1; - - /* - * Determine the day of week - */ - GregorianDay(tm); -} - -/* Both Starfire and SUN4V give us seconds since Jan 1st, 1970, - * aka Unix time. So we have to convert to/from rtc_time. - */ -static void starfire_get_rtc_time(struct rtc_time *time) -{ - u32 seconds = starfire_get_time(); - - to_tm(seconds, time); - time->tm_year -= 1900; - time->tm_mon -= 1; -} - -static int starfire_set_rtc_time(struct rtc_time *time) -{ - u32 seconds = mktime(time->tm_year + 1900, time->tm_mon + 1, - time->tm_mday, time->tm_hour, - time->tm_min, time->tm_sec); - - return starfire_set_time(seconds); -} - -static void hypervisor_get_rtc_time(struct rtc_time *time) -{ - u32 seconds = hypervisor_get_time(); - - to_tm(seconds, time); - time->tm_year -= 1900; - time->tm_mon -= 1; -} - -static int hypervisor_set_rtc_time(struct rtc_time *time) -{ - u32 seconds = mktime(time->tm_year + 1900, time->tm_mon + 1, - time->tm_mday, time->tm_hour, - time->tm_min, time->tm_sec); - - return hypervisor_set_time(seconds); -} - -#ifdef CONFIG_PCI -static void bq4802_get_rtc_time(struct rtc_time *time) -{ - unsigned char val = readb(bq4802_regs + 0x0e); - unsigned int century; - - writeb(val | 0x08, bq4802_regs + 0x0e); - - time->tm_sec = readb(bq4802_regs + 0x00); - time->tm_min = readb(bq4802_regs + 0x02); - time->tm_hour = readb(bq4802_regs + 0x04); - time->tm_mday = readb(bq4802_regs + 0x06); - time->tm_mon = readb(bq4802_regs + 0x09); - time->tm_year = readb(bq4802_regs + 0x0a); - time->tm_wday = readb(bq4802_regs + 0x08); - century = readb(bq4802_regs + 0x0f); - - writeb(val, bq4802_regs + 0x0e); - - BCD_TO_BIN(time->tm_sec); - BCD_TO_BIN(time->tm_min); - BCD_TO_BIN(time->tm_hour); - BCD_TO_BIN(time->tm_mday); - BCD_TO_BIN(time->tm_mon); - BCD_TO_BIN(time->tm_year); - BCD_TO_BIN(time->tm_wday); - BCD_TO_BIN(century); - - time->tm_year += (century * 100); - time->tm_year -= 1900; - - time->tm_mon--; -} - -static int bq4802_set_rtc_time(struct rtc_time *time) -{ - unsigned char val = readb(bq4802_regs + 0x0e); - unsigned char sec, min, hrs, day, mon, yrs, century; - unsigned int year; - - year = time->tm_year + 1900; - century = year / 100; - yrs = year % 100; - - mon = time->tm_mon + 1; /* tm_mon starts at zero */ - day = time->tm_mday; - hrs = time->tm_hour; - min = time->tm_min; - sec = time->tm_sec; - - BIN_TO_BCD(sec); - BIN_TO_BCD(min); - BIN_TO_BCD(hrs); - BIN_TO_BCD(day); - BIN_TO_BCD(mon); - BIN_TO_BCD(yrs); - BIN_TO_BCD(century); - - writeb(val | 0x08, bq4802_regs + 0x0e); - - writeb(sec, bq4802_regs + 0x00); - writeb(min, bq4802_regs + 0x02); - writeb(hrs, bq4802_regs + 0x04); - writeb(day, bq4802_regs + 0x06); - writeb(mon, bq4802_regs + 0x09); - writeb(yrs, bq4802_regs + 0x0a); - writeb(century, bq4802_regs + 0x0f); - - writeb(val, bq4802_regs + 0x0e); - - return 0; -} - -static void cmos_get_rtc_time(struct rtc_time *rtc_tm) -{ - unsigned char ctrl; - - rtc_tm->tm_sec = CMOS_READ(RTC_SECONDS); - rtc_tm->tm_min = CMOS_READ(RTC_MINUTES); - rtc_tm->tm_hour = CMOS_READ(RTC_HOURS); - rtc_tm->tm_mday = CMOS_READ(RTC_DAY_OF_MONTH); - rtc_tm->tm_mon = CMOS_READ(RTC_MONTH); - rtc_tm->tm_year = CMOS_READ(RTC_YEAR); - rtc_tm->tm_wday = CMOS_READ(RTC_DAY_OF_WEEK); - - ctrl = CMOS_READ(RTC_CONTROL); - if (!(ctrl & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { - BCD_TO_BIN(rtc_tm->tm_sec); - BCD_TO_BIN(rtc_tm->tm_min); - BCD_TO_BIN(rtc_tm->tm_hour); - BCD_TO_BIN(rtc_tm->tm_mday); - BCD_TO_BIN(rtc_tm->tm_mon); - BCD_TO_BIN(rtc_tm->tm_year); - BCD_TO_BIN(rtc_tm->tm_wday); - } - - if (rtc_tm->tm_year <= 69) - rtc_tm->tm_year += 100; - - rtc_tm->tm_mon--; -} - -static int cmos_set_rtc_time(struct rtc_time *rtc_tm) -{ - unsigned char mon, day, hrs, min, sec; - unsigned char save_control, save_freq_select; - unsigned int yrs; - - yrs = rtc_tm->tm_year; - mon = rtc_tm->tm_mon + 1; - day = rtc_tm->tm_mday; - hrs = rtc_tm->tm_hour; - min = rtc_tm->tm_min; - sec = rtc_tm->tm_sec; - - if (yrs >= 100) - yrs -= 100; - - if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { - BIN_TO_BCD(sec); - BIN_TO_BCD(min); - BIN_TO_BCD(hrs); - BIN_TO_BCD(day); - BIN_TO_BCD(mon); - BIN_TO_BCD(yrs); - } - - save_control = CMOS_READ(RTC_CONTROL); - CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL); - save_freq_select = CMOS_READ(RTC_FREQ_SELECT); - CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT); - - CMOS_WRITE(yrs, RTC_YEAR); - CMOS_WRITE(mon, RTC_MONTH); - CMOS_WRITE(day, RTC_DAY_OF_MONTH); - CMOS_WRITE(hrs, RTC_HOURS); - CMOS_WRITE(min, RTC_MINUTES); - CMOS_WRITE(sec, RTC_SECONDS); - - CMOS_WRITE(save_control, RTC_CONTROL); - CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT); - - return 0; -} -#endif /* CONFIG_PCI */ - -static void mostek_get_rtc_time(struct rtc_time *rtc_tm) -{ - void __iomem *regs = mstk48t02_regs; - u8 tmp; - - spin_lock_irq(&mostek_lock); - - tmp = mostek_read(regs + MOSTEK_CREG); - tmp |= MSTK_CREG_READ; - mostek_write(regs + MOSTEK_CREG, tmp); - - rtc_tm->tm_sec = MSTK_REG_SEC(regs); - rtc_tm->tm_min = MSTK_REG_MIN(regs); - rtc_tm->tm_hour = MSTK_REG_HOUR(regs); - rtc_tm->tm_mday = MSTK_REG_DOM(regs); - rtc_tm->tm_mon = MSTK_REG_MONTH(regs); - rtc_tm->tm_year = MSTK_CVT_YEAR( MSTK_REG_YEAR(regs) ); - rtc_tm->tm_wday = MSTK_REG_DOW(regs); - - tmp = mostek_read(regs + MOSTEK_CREG); - tmp &= ~MSTK_CREG_READ; - mostek_write(regs + MOSTEK_CREG, tmp); - - spin_unlock_irq(&mostek_lock); - - rtc_tm->tm_mon--; - rtc_tm->tm_wday--; - rtc_tm->tm_year -= 1900; -} - -static int mostek_set_rtc_time(struct rtc_time *rtc_tm) -{ - unsigned char mon, day, hrs, min, sec, wday; - void __iomem *regs = mstk48t02_regs; - unsigned int yrs; - u8 tmp; - - yrs = rtc_tm->tm_year + 1900; - mon = rtc_tm->tm_mon + 1; - day = rtc_tm->tm_mday; - wday = rtc_tm->tm_wday + 1; - hrs = rtc_tm->tm_hour; - min = rtc_tm->tm_min; - sec = rtc_tm->tm_sec; - - spin_lock_irq(&mostek_lock); - - tmp = mostek_read(regs + MOSTEK_CREG); - tmp |= MSTK_CREG_WRITE; - mostek_write(regs + MOSTEK_CREG, tmp); - - MSTK_SET_REG_SEC(regs, sec); - MSTK_SET_REG_MIN(regs, min); - MSTK_SET_REG_HOUR(regs, hrs); - MSTK_SET_REG_DOW(regs, wday); - MSTK_SET_REG_DOM(regs, day); - MSTK_SET_REG_MONTH(regs, mon); - MSTK_SET_REG_YEAR(regs, yrs - MSTK_YEAR_ZERO); - - tmp = mostek_read(regs + MOSTEK_CREG); - tmp &= ~MSTK_CREG_WRITE; - mostek_write(regs + MOSTEK_CREG, tmp); - - spin_unlock_irq(&mostek_lock); - - return 0; -} - -struct mini_rtc_ops { - void (*get_rtc_time)(struct rtc_time *); - int (*set_rtc_time)(struct rtc_time *); -}; - -static struct mini_rtc_ops starfire_rtc_ops = { - .get_rtc_time = starfire_get_rtc_time, - .set_rtc_time = starfire_set_rtc_time, -}; - -static struct mini_rtc_ops hypervisor_rtc_ops = { - .get_rtc_time = hypervisor_get_rtc_time, - .set_rtc_time = hypervisor_set_rtc_time, -}; - -#ifdef CONFIG_PCI -static struct mini_rtc_ops bq4802_rtc_ops = { - .get_rtc_time = bq4802_get_rtc_time, - .set_rtc_time = bq4802_set_rtc_time, -}; - -static struct mini_rtc_ops cmos_rtc_ops = { - .get_rtc_time = cmos_get_rtc_time, - .set_rtc_time = cmos_set_rtc_time, -}; -#endif /* CONFIG_PCI */ - -static struct mini_rtc_ops mostek_rtc_ops = { - .get_rtc_time = mostek_get_rtc_time, - .set_rtc_time = mostek_set_rtc_time, -}; - -static struct mini_rtc_ops *mini_rtc_ops; - -static inline void mini_get_rtc_time(struct rtc_time *time) -{ - unsigned long flags; - - spin_lock_irqsave(&rtc_lock, flags); - mini_rtc_ops->get_rtc_time(time); - spin_unlock_irqrestore(&rtc_lock, flags); -} - -static inline int mini_set_rtc_time(struct rtc_time *time) -{ - unsigned long flags; - int err; - - spin_lock_irqsave(&rtc_lock, flags); - err = mini_rtc_ops->set_rtc_time(time); - spin_unlock_irqrestore(&rtc_lock, flags); - - return err; -} - -static int mini_rtc_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - struct rtc_time wtime; - void __user *argp = (void __user *)arg; - - switch (cmd) { - - case RTC_PLL_GET: - return -EINVAL; - - case RTC_PLL_SET: - return -EINVAL; - - case RTC_UIE_OFF: /* disable ints from RTC updates. */ - return 0; - - case RTC_UIE_ON: /* enable ints for RTC updates. */ - return -EINVAL; - - case RTC_RD_TIME: /* Read the time/date from RTC */ - /* this doesn't get week-day, who cares */ - memset(&wtime, 0, sizeof(wtime)); - mini_get_rtc_time(&wtime); - - return copy_to_user(argp, &wtime, sizeof(wtime)) ? -EFAULT : 0; - - case RTC_SET_TIME: /* Set the RTC */ - { - int year, days; - - if (!capable(CAP_SYS_TIME)) - return -EACCES; - - if (copy_from_user(&wtime, argp, sizeof(wtime))) - return -EFAULT; - - year = wtime.tm_year + 1900; - days = month_days[wtime.tm_mon] + - ((wtime.tm_mon == 1) && leapyear(year)); - - if ((wtime.tm_mon < 0 || wtime.tm_mon > 11) || - (wtime.tm_mday < 1)) - return -EINVAL; - - if (wtime.tm_mday < 0 || wtime.tm_mday > days) - return -EINVAL; - - if (wtime.tm_hour < 0 || wtime.tm_hour >= 24 || - wtime.tm_min < 0 || wtime.tm_min >= 60 || - wtime.tm_sec < 0 || wtime.tm_sec >= 60) - return -EINVAL; - - return mini_set_rtc_time(&wtime); - } - } - - return -EINVAL; -} - -static int mini_rtc_open(struct inode *inode, struct file *file) -{ - lock_kernel(); - if (mini_rtc_status & RTC_IS_OPEN) { - unlock_kernel(); - return -EBUSY; - } - - mini_rtc_status |= RTC_IS_OPEN; - unlock_kernel(); - - return 0; -} - -static int mini_rtc_release(struct inode *inode, struct file *file) -{ - mini_rtc_status &= ~RTC_IS_OPEN; - return 0; -} - - -static const struct file_operations mini_rtc_fops = { - .owner = THIS_MODULE, - .ioctl = mini_rtc_ioctl, - .open = mini_rtc_open, - .release = mini_rtc_release, -}; - -static struct miscdevice rtc_mini_dev = -{ - .minor = RTC_MINOR, - .name = "rtc", - .fops = &mini_rtc_fops, -}; - -static int __init rtc_mini_init(void) -{ - int retval; - - if (tlb_type == hypervisor) - mini_rtc_ops = &hypervisor_rtc_ops; - else if (this_is_starfire) - mini_rtc_ops = &starfire_rtc_ops; -#ifdef CONFIG_PCI - else if (bq4802_regs) - mini_rtc_ops = &bq4802_rtc_ops; - else if (ds1287_regs) - mini_rtc_ops = &cmos_rtc_ops; -#endif /* CONFIG_PCI */ - else if (mstk48t02_regs) - mini_rtc_ops = &mostek_rtc_ops; - else - return -ENODEV; - - printk(KERN_INFO "Mini RTC Driver\n"); - - retval = misc_register(&rtc_mini_dev); - if (retval < 0) - return retval; - - return 0; -} - -static void __exit rtc_mini_exit(void) -{ - misc_deregister(&rtc_mini_dev); -} - int __devinit read_current_timer(unsigned long *timer_val) { *timer_val = tick_ops->get_tick(); return 0; } - -module_init(rtc_mini_init); -module_exit(rtc_mini_exit); diff --git a/arch/sparc64/kernel/traps.c b/arch/sparc64/kernel/traps.c index c824df13f58..81ccd22e78d 100644 --- a/arch/sparc64/kernel/traps.c +++ b/arch/sparc64/kernel/traps.c @@ -38,6 +38,7 @@ #include <asm/timer.h> #include <asm/head.h> #include <asm/prom.h> +#include <asm/memctrl.h> #include "entry.h" #include "kstack.h" @@ -129,6 +130,56 @@ void do_BUG(const char *file, int line) } #endif +static DEFINE_SPINLOCK(dimm_handler_lock); +static dimm_printer_t dimm_handler; + +static int sprintf_dimm(int synd_code, unsigned long paddr, char *buf, int buflen) +{ + unsigned long flags; + int ret = -ENODEV; + + spin_lock_irqsave(&dimm_handler_lock, flags); + if (dimm_handler) { + ret = dimm_handler(synd_code, paddr, buf, buflen); + } else if (tlb_type == spitfire) { + if (prom_getunumber(synd_code, paddr, buf, buflen) == -1) + ret = -EINVAL; + else + ret = 0; + } else + ret = -ENODEV; + spin_unlock_irqrestore(&dimm_handler_lock, flags); + + return ret; +} + +int register_dimm_printer(dimm_printer_t func) +{ + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&dimm_handler_lock, flags); + if (!dimm_handler) + dimm_handler = func; + else + ret = -EEXIST; + spin_unlock_irqrestore(&dimm_handler_lock, flags); + + return ret; +} +EXPORT_SYMBOL_GPL(register_dimm_printer); + +void unregister_dimm_printer(dimm_printer_t func) +{ + unsigned long flags; + + spin_lock_irqsave(&dimm_handler_lock, flags); + if (dimm_handler == func) + dimm_handler = NULL; + spin_unlock_irqrestore(&dimm_handler_lock, flags); +} +EXPORT_SYMBOL_GPL(unregister_dimm_printer); + void spitfire_insn_access_exception(struct pt_regs *regs, unsigned long sfsr, unsigned long sfar) { siginfo_t info; @@ -291,10 +342,7 @@ void sun4v_data_access_exception_tl1(struct pt_regs *regs, unsigned long addr, u } #ifdef CONFIG_PCI -/* This is really pathetic... */ -extern volatile int pci_poke_in_progress; -extern volatile int pci_poke_cpu; -extern volatile int pci_poke_faulted; +#include "pci_impl.h" #endif /* When access exceptions happen, we must do this. */ @@ -376,8 +424,7 @@ static void spitfire_log_udb_syndrome(unsigned long afar, unsigned long udbh, un if (udbl & bit) { scode = ecc_syndrome_table[udbl & 0xff]; - if (prom_getunumber(scode, afar, - memmod_str, sizeof(memmod_str)) == -1) + if (sprintf_dimm(scode, afar, memmod_str, sizeof(memmod_str)) < 0) p = syndrome_unknown; else p = memmod_str; @@ -388,8 +435,7 @@ static void spitfire_log_udb_syndrome(unsigned long afar, unsigned long udbh, un if (udbh & bit) { scode = ecc_syndrome_table[udbh & 0xff]; - if (prom_getunumber(scode, afar, - memmod_str, sizeof(memmod_str)) == -1) + if (sprintf_dimm(scode, afar, memmod_str, sizeof(memmod_str)) < 0) p = syndrome_unknown; else p = memmod_str; @@ -1062,8 +1108,6 @@ static const char *cheetah_get_string(unsigned long bit) return "???"; } -extern int chmc_getunumber(int, unsigned long, char *, int); - static void cheetah_log_errors(struct pt_regs *regs, struct cheetah_err_info *info, unsigned long afsr, unsigned long afar, int recoverable) { @@ -1105,7 +1149,7 @@ static void cheetah_log_errors(struct pt_regs *regs, struct cheetah_err_info *in syndrome = (afsr & CHAFSR_E_SYNDROME) >> CHAFSR_E_SYNDROME_SHIFT; syndrome = cheetah_ecc_syntab[syndrome]; - ret = chmc_getunumber(syndrome, afar, unum, sizeof(unum)); + ret = sprintf_dimm(syndrome, afar, unum, sizeof(unum)); if (ret != -1) printk("%s" "ERROR(%d): AFAR E-syndrome [%s]\n", (recoverable ? KERN_WARNING : KERN_CRIT), @@ -1116,7 +1160,7 @@ static void cheetah_log_errors(struct pt_regs *regs, struct cheetah_err_info *in syndrome = (afsr & CHAFSR_M_SYNDROME) >> CHAFSR_M_SYNDROME_SHIFT; syndrome = cheetah_mtag_syntab[syndrome]; - ret = chmc_getunumber(syndrome, afar, unum, sizeof(unum)); + ret = sprintf_dimm(syndrome, afar, unum, sizeof(unum)); if (ret != -1) printk("%s" "ERROR(%d): AFAR M-syndrome [%s]\n", (recoverable ? KERN_WARNING : KERN_CRIT), @@ -2224,7 +2268,6 @@ void die_if_kernel(char *str, struct pt_regs *regs) extern int handle_popc(u32 insn, struct pt_regs *regs); extern int handle_ldf_stq(u32 insn, struct pt_regs *regs); -extern int vis_emul(struct pt_regs *, unsigned int); void do_illegal_instruction(struct pt_regs *regs) { diff --git a/arch/sparc64/kernel/vio.c b/arch/sparc64/kernel/vio.c index a490077891a..92b1f8ec01d 100644 --- a/arch/sparc64/kernel/vio.c +++ b/arch/sparc64/kernel/vio.c @@ -152,7 +152,7 @@ show_pciobppath_attr(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR(obppath, S_IRUSR | S_IRGRP | S_IROTH, show_pciobppath_attr, NULL); -struct device_node *cdev_node; +static struct device_node *cdev_node; static struct vio_dev *root_vdev; static u64 cdev_cfg_handle; @@ -371,9 +371,9 @@ static struct mdesc_notifier_client vio_ds_notifier = { .node_name = "domain-services-port", }; -const char *channel_devices_node = "channel-devices"; -const char *channel_devices_compat = "SUNW,sun4v-channel-devices"; -const char *cfg_handle_prop = "cfg-handle"; +static const char *channel_devices_node = "channel-devices"; +static const char *channel_devices_compat = "SUNW,sun4v-channel-devices"; +static const char *cfg_handle_prop = "cfg-handle"; static int __init vio_init(void) { diff --git a/arch/sparc64/kernel/visemul.c b/arch/sparc64/kernel/visemul.c index c3fd64706b5..9e05cb5cb85 100644 --- a/arch/sparc64/kernel/visemul.c +++ b/arch/sparc64/kernel/visemul.c @@ -243,7 +243,7 @@ static inline unsigned int *fps_regaddr(struct fpustate *f, struct edge_tab { u16 left, right; }; -struct edge_tab edge8_tab[8] = { +static struct edge_tab edge8_tab[8] = { { 0xff, 0x80 }, { 0x7f, 0xc0 }, { 0x3f, 0xe0 }, @@ -253,7 +253,7 @@ struct edge_tab edge8_tab[8] = { { 0x03, 0xfe }, { 0x01, 0xff }, }; -struct edge_tab edge8_tab_l[8] = { +static struct edge_tab edge8_tab_l[8] = { { 0xff, 0x01 }, { 0xfe, 0x03 }, { 0xfc, 0x07 }, @@ -263,23 +263,23 @@ struct edge_tab edge8_tab_l[8] = { { 0xc0, 0x7f }, { 0x80, 0xff }, }; -struct edge_tab edge16_tab[4] = { +static struct edge_tab edge16_tab[4] = { { 0xf, 0x8 }, { 0x7, 0xc }, { 0x3, 0xe }, { 0x1, 0xf }, }; -struct edge_tab edge16_tab_l[4] = { +static struct edge_tab edge16_tab_l[4] = { { 0xf, 0x1 }, { 0xe, 0x3 }, { 0xc, 0x7 }, { 0x8, 0xf }, }; -struct edge_tab edge32_tab[2] = { +static struct edge_tab edge32_tab[2] = { { 0x3, 0x2 }, { 0x1, 0x3 }, }; -struct edge_tab edge32_tab_l[2] = { +static struct edge_tab edge32_tab_l[2] = { { 0x3, 0x1 }, { 0x2, 0x3 }, }; diff --git a/arch/sparc64/mm/fault.c b/arch/sparc64/mm/fault.c index ea7d7ae76bc..a9e474bf638 100644 --- a/arch/sparc64/mm/fault.c +++ b/arch/sparc64/mm/fault.c @@ -51,43 +51,6 @@ static inline int notify_page_fault(struct pt_regs *regs) } #endif -/* - * To debug kernel to catch accesses to certain virtual/physical addresses. - * Mode = 0 selects physical watchpoints, mode = 1 selects virtual watchpoints. - * flags = VM_READ watches memread accesses, flags = VM_WRITE watches memwrite accesses. - * Caller passes in a 64bit aligned addr, with mask set to the bytes that need to be - * watched. This is only useful on a single cpu machine for now. After the watchpoint - * is detected, the process causing it will be killed, thus preventing an infinite loop. - */ -void set_brkpt(unsigned long addr, unsigned char mask, int flags, int mode) -{ - unsigned long lsubits; - - __asm__ __volatile__("ldxa [%%g0] %1, %0" - : "=r" (lsubits) - : "i" (ASI_LSU_CONTROL)); - lsubits &= ~(LSU_CONTROL_PM | LSU_CONTROL_VM | - LSU_CONTROL_PR | LSU_CONTROL_VR | - LSU_CONTROL_PW | LSU_CONTROL_VW); - - __asm__ __volatile__("stxa %0, [%1] %2\n\t" - "membar #Sync" - : /* no outputs */ - : "r" (addr), "r" (mode ? VIRT_WATCHPOINT : PHYS_WATCHPOINT), - "i" (ASI_DMMU)); - - lsubits |= ((unsigned long)mask << (mode ? 25 : 33)); - if (flags & VM_READ) - lsubits |= (mode ? LSU_CONTROL_VR : LSU_CONTROL_PR); - if (flags & VM_WRITE) - lsubits |= (mode ? LSU_CONTROL_VW : LSU_CONTROL_PW); - __asm__ __volatile__("stxa %0, [%%g0] %1\n\t" - "membar #Sync" - : /* no outputs */ - : "r" (lsubits), "i" (ASI_LSU_CONTROL) - : "memory"); -} - static void __kprobes unhandled_fault(unsigned long address, struct task_struct *tsk, struct pt_regs *regs) diff --git a/arch/sparc64/mm/init.c b/arch/sparc64/mm/init.c index a41df7bef03..3c10daf8fc0 100644 --- a/arch/sparc64/mm/init.c +++ b/arch/sparc64/mm/init.c @@ -46,15 +46,11 @@ #include <asm/tsb.h> #include <asm/hypervisor.h> #include <asm/prom.h> -#include <asm/sstate.h> #include <asm/mdesc.h> #include <asm/cpudata.h> #include <asm/irq.h> -#define MAX_PHYS_ADDRESS (1UL << 42UL) -#define KPTE_BITMAP_CHUNK_SZ (256UL * 1024UL * 1024UL) -#define KPTE_BITMAP_BYTES \ - ((MAX_PHYS_ADDRESS / KPTE_BITMAP_CHUNK_SZ) / 8) +#include "init.h" unsigned long kern_linear_pte_xor[2] __read_mostly; @@ -416,17 +412,9 @@ void mmu_info(struct seq_file *m) #endif /* CONFIG_DEBUG_DCFLUSH */ } -struct linux_prom_translation { - unsigned long virt; - unsigned long size; - unsigned long data; -}; - -/* Exported for kernel TLB miss handling in ktlb.S */ struct linux_prom_translation prom_trans[512] __read_mostly; unsigned int prom_trans_ents __read_mostly; -/* Exported for SMP bootup purposes. */ unsigned long kern_locked_tte_data; /* The obp translations are saved based on 8k pagesize, since obp can @@ -938,6 +926,10 @@ int of_node_to_nid(struct device_node *dp) int count, nid; u64 grp; + /* This is the right thing to do on currently supported + * SUN4U NUMA platforms as well, as the PCI controller does + * not sit behind any particular memory controller. + */ if (!mlgroups) return -1; @@ -1206,8 +1198,44 @@ out: return err; } +static int __init numa_parse_jbus(void) +{ + unsigned long cpu, index; + + /* NUMA node id is encoded in bits 36 and higher, and there is + * a 1-to-1 mapping from CPU ID to NUMA node ID. + */ + index = 0; + for_each_present_cpu(cpu) { + numa_cpu_lookup_table[cpu] = index; + numa_cpumask_lookup_table[index] = cpumask_of_cpu(cpu); + node_masks[index].mask = ~((1UL << 36UL) - 1UL); + node_masks[index].val = cpu << 36UL; + + index++; + } + num_node_masks = index; + + add_node_ranges(); + + for (index = 0; index < num_node_masks; index++) { + allocate_node_data(index); + node_set_online(index); + } + + return 0; +} + static int __init numa_parse_sun4u(void) { + if (tlb_type == cheetah || tlb_type == cheetah_plus) { + unsigned long ver; + + __asm__ ("rdpr %%ver, %0" : "=r" (ver)); + if ((ver >> 32UL) == __JALAPENO_ID || + (ver >> 32UL) == __SERRANO_ID) + return numa_parse_jbus(); + } return -1; } @@ -1633,8 +1661,6 @@ void __cpuinit sun4v_ktsb_register(void) /* paging_init() sets up the page tables */ -extern void central_probe(void); - static unsigned long last_valid_pfn; pgd_t swapper_pg_dir[2048]; @@ -1679,8 +1705,6 @@ void __init paging_init(void) kern_base = (prom_boot_mapping_phys_low >> 22UL) << 22UL; kern_size = (unsigned long)&_end - (unsigned long)KERNBASE; - sstate_booting(); - /* Invalidate both kernel TSBs. */ memset(swapper_tsb, 0x40, sizeof(swapper_tsb)); #ifndef CONFIG_DEBUG_PAGEALLOC @@ -1803,9 +1827,6 @@ void __init paging_init(void) } printk("Booting Linux...\n"); - - central_probe(); - cpu_probe(); } int __init page_in_phys_avail(unsigned long paddr) @@ -2032,7 +2053,6 @@ pgprot_t PAGE_COPY __read_mostly; pgprot_t PAGE_SHARED __read_mostly; EXPORT_SYMBOL(PAGE_SHARED); -pgprot_t PAGE_EXEC __read_mostly; unsigned long pg_iobits __read_mostly; unsigned long _PAGE_IE __read_mostly; @@ -2045,14 +2065,6 @@ unsigned long _PAGE_CACHE __read_mostly; EXPORT_SYMBOL(_PAGE_CACHE); #ifdef CONFIG_SPARSEMEM_VMEMMAP - -#define VMEMMAP_CHUNK_SHIFT 22 -#define VMEMMAP_CHUNK (1UL << VMEMMAP_CHUNK_SHIFT) -#define VMEMMAP_CHUNK_MASK ~(VMEMMAP_CHUNK - 1UL) -#define VMEMMAP_ALIGN(x) (((x)+VMEMMAP_CHUNK-1UL)&VMEMMAP_CHUNK_MASK) - -#define VMEMMAP_SIZE ((((1UL << MAX_PHYSADDR_BITS) >> PAGE_SHIFT) * \ - sizeof(struct page *)) >> VMEMMAP_CHUNK_SHIFT) unsigned long vmemmap_table[VMEMMAP_SIZE]; int __meminit vmemmap_populate(struct page *start, unsigned long nr, int node) @@ -2136,7 +2148,6 @@ static void __init sun4u_pgprot_init(void) _PAGE_CACHE_4U | _PAGE_P_4U | __ACCESS_BITS_4U | __DIRTY_BITS_4U | _PAGE_EXEC_4U | _PAGE_L_4U); - PAGE_EXEC = __pgprot(_PAGE_EXEC_4U); _PAGE_IE = _PAGE_IE_4U; _PAGE_E = _PAGE_E_4U; @@ -2147,10 +2158,10 @@ static void __init sun4u_pgprot_init(void) #ifdef CONFIG_DEBUG_PAGEALLOC kern_linear_pte_xor[0] = (_PAGE_VALID | _PAGE_SZBITS_4U) ^ - 0xfffff80000000000; + 0xfffff80000000000UL; #else kern_linear_pte_xor[0] = (_PAGE_VALID | _PAGE_SZ4MB_4U) ^ - 0xfffff80000000000; + 0xfffff80000000000UL; #endif kern_linear_pte_xor[0] |= (_PAGE_CP_4U | _PAGE_CV_4U | _PAGE_P_4U | _PAGE_W_4U); @@ -2188,7 +2199,6 @@ static void __init sun4v_pgprot_init(void) __ACCESS_BITS_4V | __DIRTY_BITS_4V | _PAGE_EXEC_4V); PAGE_KERNEL_LOCKED = PAGE_KERNEL; - PAGE_EXEC = __pgprot(_PAGE_EXEC_4V); _PAGE_IE = _PAGE_IE_4V; _PAGE_E = _PAGE_E_4V; @@ -2196,20 +2206,20 @@ static void __init sun4v_pgprot_init(void) #ifdef CONFIG_DEBUG_PAGEALLOC kern_linear_pte_xor[0] = (_PAGE_VALID | _PAGE_SZBITS_4V) ^ - 0xfffff80000000000; + 0xfffff80000000000UL; #else kern_linear_pte_xor[0] = (_PAGE_VALID | _PAGE_SZ4MB_4V) ^ - 0xfffff80000000000; + 0xfffff80000000000UL; #endif kern_linear_pte_xor[0] |= (_PAGE_CP_4V | _PAGE_CV_4V | _PAGE_P_4V | _PAGE_W_4V); #ifdef CONFIG_DEBUG_PAGEALLOC kern_linear_pte_xor[1] = (_PAGE_VALID | _PAGE_SZBITS_4V) ^ - 0xfffff80000000000; + 0xfffff80000000000UL; #else kern_linear_pte_xor[1] = (_PAGE_VALID | _PAGE_SZ256MB_4V) ^ - 0xfffff80000000000; + 0xfffff80000000000UL; #endif kern_linear_pte_xor[1] |= (_PAGE_CP_4V | _PAGE_CV_4V | _PAGE_P_4V | _PAGE_W_4V); diff --git a/arch/sparc64/mm/init.h b/arch/sparc64/mm/init.h new file mode 100644 index 00000000000..16063870a48 --- /dev/null +++ b/arch/sparc64/mm/init.h @@ -0,0 +1,49 @@ +#ifndef _SPARC64_MM_INIT_H +#define _SPARC64_MM_INIT_H + +/* Most of the symbols in this file are defined in init.c and + * marked non-static so that assembler code can get at them. + */ + +#define MAX_PHYS_ADDRESS (1UL << 42UL) +#define KPTE_BITMAP_CHUNK_SZ (256UL * 1024UL * 1024UL) +#define KPTE_BITMAP_BYTES \ + ((MAX_PHYS_ADDRESS / KPTE_BITMAP_CHUNK_SZ) / 8) + +extern unsigned long kern_linear_pte_xor[2]; +extern unsigned long kpte_linear_bitmap[KPTE_BITMAP_BYTES / sizeof(unsigned long)]; +extern unsigned int sparc64_highest_unlocked_tlb_ent; +extern unsigned long sparc64_kern_pri_context; +extern unsigned long sparc64_kern_pri_nuc_bits; +extern unsigned long sparc64_kern_sec_context; +extern void mmu_info(struct seq_file *m); + +struct linux_prom_translation { + unsigned long virt; + unsigned long size; + unsigned long data; +}; + +/* Exported for kernel TLB miss handling in ktlb.S */ +extern struct linux_prom_translation prom_trans[512]; +extern unsigned int prom_trans_ents; + +/* Exported for SMP bootup purposes. */ +extern unsigned long kern_locked_tte_data; + +extern void prom_world(int enter); + +extern void free_initmem(void); + +#ifdef CONFIG_SPARSEMEM_VMEMMAP +#define VMEMMAP_CHUNK_SHIFT 22 +#define VMEMMAP_CHUNK (1UL << VMEMMAP_CHUNK_SHIFT) +#define VMEMMAP_CHUNK_MASK ~(VMEMMAP_CHUNK - 1UL) +#define VMEMMAP_ALIGN(x) (((x)+VMEMMAP_CHUNK-1UL)&VMEMMAP_CHUNK_MASK) + +#define VMEMMAP_SIZE ((((1UL << MAX_PHYSADDR_BITS) >> PAGE_SHIFT) * \ + sizeof(struct page *)) >> VMEMMAP_CHUNK_SHIFT) +extern unsigned long vmemmap_table[VMEMMAP_SIZE]; +#endif + +#endif /* _SPARC64_MM_INIT_H */ diff --git a/arch/sparc64/mm/tlb.c b/arch/sparc64/mm/tlb.c index ae24919cba7..d8f21e24a82 100644 --- a/arch/sparc64/mm/tlb.c +++ b/arch/sparc64/mm/tlb.c @@ -19,7 +19,7 @@ /* Heavily inspired by the ppc64 code. */ -DEFINE_PER_CPU(struct mmu_gather, mmu_gathers) = { 0, }; +DEFINE_PER_CPU(struct mmu_gather, mmu_gathers); void flush_tlb_pending(void) { diff --git a/arch/um/Kconfig.i386 b/arch/um/Kconfig.i386 index e09edfa560d..1f57c113df6 100644 --- a/arch/um/Kconfig.i386 +++ b/arch/um/Kconfig.i386 @@ -9,8 +9,9 @@ config UML_X86 default y config X86_32 - bool - default y + bool + default y + select HAVE_AOUT config RWSEM_XCHGADD_ALGORITHM def_bool y @@ -42,6 +43,3 @@ config ARCH_REUSE_HOST_VSYSCALL_AREA config GENERIC_HWEIGHT bool default y - -config ARCH_SUPPORTS_AOUT - def_bool y diff --git a/arch/um/Kconfig.x86_64 b/arch/um/Kconfig.x86_64 index 5696e7b374b..40b3407cfe1 100644 --- a/arch/um/Kconfig.x86_64 +++ b/arch/um/Kconfig.x86_64 @@ -37,6 +37,3 @@ config SMP_BROKEN config GENERIC_HWEIGHT bool default y - -config ARCH_SUPPORTS_AOUT - def_bool y diff --git a/arch/um/drivers/line.c b/arch/um/drivers/line.c index d741f35d7b3..14a102e877d 100644 --- a/arch/um/drivers/line.c +++ b/arch/um/drivers/line.c @@ -275,6 +275,8 @@ int line_ioctl(struct tty_struct *tty, struct file * file, case TIOCGLTC: case TIOCSLTC: #endif + /* Note: these are out of date as we now have TCGETS2 etc but this + whole lot should probably go away */ case TCGETS: case TCSETSF: case TCSETSW: diff --git a/arch/um/sys-x86_64/syscall_table.c b/arch/um/sys-x86_64/syscall_table.c index c128eb89700..32f5fbe2d0d 100644 --- a/arch/um/sys-x86_64/syscall_table.c +++ b/arch/um/sys-x86_64/syscall_table.c @@ -41,12 +41,12 @@ #define stub_rt_sigreturn sys_rt_sigreturn #define __SYSCALL(nr, sym) extern asmlinkage void sym(void) ; -#undef _ASM_X86_64_UNISTD_H_ +#undef ASM_X86__UNISTD_64_H #include <asm-x86/unistd_64.h> #undef __SYSCALL #define __SYSCALL(nr, sym) [ nr ] = sym, -#undef _ASM_X86_64_UNISTD_H_ +#undef ASM_X86__UNISTD_64_H typedef void (*sys_call_ptr_t)(void); diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 0d7cdbbfc1e..f65c2744d57 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -18,6 +18,7 @@ config X86_64 ### Arch settings config X86 def_bool y + select HAVE_AOUT if X86_32 select HAVE_UNSTABLE_SCHED_CLOCK select HAVE_IDE select HAVE_OPROFILE @@ -152,9 +153,6 @@ config AUDIT_ARCH bool default X86_64 -config ARCH_SUPPORTS_AOUT - def_bool y - config ARCH_SUPPORTS_OPTIMIZED_INLINING def_bool y @@ -778,23 +776,45 @@ config X86_REBOOTFIXUPS Say N otherwise. config MICROCODE - tristate "/dev/cpu/microcode - Intel IA32 CPU microcode support" + tristate "/dev/cpu/microcode - microcode support" select FW_LOADER ---help--- If you say Y here, you will be able to update the microcode on - Intel processors in the IA32 family, e.g. Pentium Pro, Pentium II, - Pentium III, Pentium 4, Xeon etc. You will obviously need the - actual microcode binary data itself which is not shipped with the - Linux kernel. + certain Intel and AMD processors. The Intel support is for the + IA32 family, e.g. Pentium Pro, Pentium II, Pentium III, + Pentium 4, Xeon etc. The AMD support is for family 0x10 and + 0x11 processors, e.g. Opteron, Phenom and Turion 64 Ultra. + You will obviously need the actual microcode binary data itself + which is not shipped with the Linux kernel. - For latest news and information on obtaining all the required - ingredients for this driver, check: - <http://www.urbanmyth.org/microcode/>. + This option selects the general module only, you need to select + at least one vendor specific module as well. To compile this driver as a module, choose M here: the module will be called microcode. -config MICROCODE_OLD_INTERFACE +config MICROCODE_INTEL + bool "Intel microcode patch loading support" + depends on MICROCODE + default MICROCODE + select FW_LOADER + --help--- + This options enables microcode patch loading support for Intel + processors. + + For latest news and information on obtaining all the required + Intel ingredients for this driver, check: + <http://www.urbanmyth.org/microcode/>. + +config MICROCODE_AMD + bool "AMD microcode patch loading support" + depends on MICROCODE + select FW_LOADER + --help--- + If you select this option, microcode patch loading support for AMD + processors will be enabled. + + config MICROCODE_OLD_INTERFACE def_bool y depends on MICROCODE @@ -1061,6 +1081,56 @@ config HIGHPTE low memory. Setting this option will put user-space page table entries in high memory. +config X86_CHECK_BIOS_CORRUPTION + bool "Check for low memory corruption" + help + Periodically check for memory corruption in low memory, which + is suspected to be caused by BIOS. Even when enabled in the + configuration, it is disabled at runtime. Enable it by + setting "memory_corruption_check=1" on the kernel command + line. By default it scans the low 64k of memory every 60 + seconds; see the memory_corruption_check_size and + memory_corruption_check_period parameters in + Documentation/kernel-parameters.txt to adjust this. + + When enabled with the default parameters, this option has + almost no overhead, as it reserves a relatively small amount + of memory and scans it infrequently. It both detects corruption + and prevents it from affecting the running system. + + It is, however, intended as a diagnostic tool; if repeatable + BIOS-originated corruption always affects the same memory, + you can use memmap= to prevent the kernel from using that + memory. + +config X86_BOOTPARAM_MEMORY_CORRUPTION_CHECK + bool "Set the default setting of memory_corruption_check" + depends on X86_CHECK_BIOS_CORRUPTION + default y + help + Set whether the default state of memory_corruption_check is + on or off. + +config X86_RESERVE_LOW_64K + bool "Reserve low 64K of RAM on AMI/Phoenix BIOSen" + default y + help + Reserve the first 64K of physical RAM on BIOSes that are known + to potentially corrupt that memory range. A numbers of BIOSes are + known to utilize this area during suspend/resume, so it must not + be used by the kernel. + + Set this to N if you are absolutely sure that you trust the BIOS + to get all its memory reservations and usages right. + + If you have doubts about the BIOS (e.g. suspend/resume does not + work or there's kernel crashes after certain hardware hotplug + events) and it's not AMI or Phoenix, then you might want to enable + X86_CHECK_BIOS_CORRUPTION=y to allow the kernel to check typical + corruption patterns. + + Say Y if unsure. + config MATH_EMULATION bool prompt "Math emulation" if X86_32 @@ -1689,6 +1759,14 @@ config DMAR_FLOPPY_WA workaround will setup a 1:1 mapping for the first 16M to make floppy (an ISA device) work. +config INTR_REMAP + bool "Support for Interrupt Remapping (EXPERIMENTAL)" + depends on X86_64 && X86_IO_APIC && PCI_MSI && ACPI && EXPERIMENTAL + help + Supports Interrupt remapping for IO-APIC and MSI devices. + To use x2apic mode in the CPU's which support x2APIC enhancements or + to support platforms with CPU's having > 8 bit APIC ID, say Y. + source "drivers/pci/pcie/Kconfig" source "drivers/pci/Kconfig" @@ -1805,7 +1883,7 @@ config IA32_EMULATION config IA32_AOUT tristate "IA32 a.out support" - depends on IA32_EMULATION && ARCH_SUPPORTS_AOUT + depends on IA32_EMULATION help Support old a.out binaries in the 32bit emulation. diff --git a/arch/x86/Kconfig.cpu b/arch/x86/Kconfig.cpu index 60a85768cfc..0b7c4a3f065 100644 --- a/arch/x86/Kconfig.cpu +++ b/arch/x86/Kconfig.cpu @@ -38,8 +38,7 @@ config M386 - "Crusoe" for the Transmeta Crusoe series. - "Efficeon" for the Transmeta Efficeon series. - "Winchip-C6" for original IDT Winchip. - - "Winchip-2" for IDT Winchip 2. - - "Winchip-2A" for IDT Winchips with 3dNow! capabilities. + - "Winchip-2" for IDT Winchips with 3dNow! capabilities. - "GeodeGX1" for Geode GX1 (Cyrix MediaGX). - "Geode GX/LX" For AMD Geode GX and LX processors. - "CyrixIII/VIA C3" for VIA Cyrix III or VIA C3. @@ -194,19 +193,11 @@ config MWINCHIPC6 treat this chip as a 586TSC with some extended instructions and alignment requirements. -config MWINCHIP2 - bool "Winchip-2" - depends on X86_32 - help - Select this for an IDT Winchip-2. Linux and GCC - treat this chip as a 586TSC with some extended instructions - and alignment requirements. - config MWINCHIP3D - bool "Winchip-2A/Winchip-3" + bool "Winchip-2/Winchip-2A/Winchip-3" depends on X86_32 help - Select this for an IDT Winchip-2A or 3. Linux and GCC + Select this for an IDT Winchip-2, 2A or 3. Linux and GCC treat this chip as a 586TSC with some extended instructions and alignment requirements. Also enable out of order memory stores for this CPU, which can increase performance of some @@ -318,7 +309,7 @@ config X86_L1_CACHE_SHIFT int default "7" if MPENTIUM4 || X86_GENERIC || GENERIC_CPU || MPSC default "4" if X86_ELAN || M486 || M386 || MGEODEGX1 - default "5" if MWINCHIP3D || MWINCHIP2 || MWINCHIPC6 || MCRUSOE || MEFFICEON || MCYRIXIII || MK6 || MPENTIUMIII || MPENTIUMII || M686 || M586MMX || M586TSC || M586 || MVIAC3_2 || MGEODE_LX + default "5" if MWINCHIP3D || MWINCHIPC6 || MCRUSOE || MEFFICEON || MCYRIXIII || MK6 || MPENTIUMIII || MPENTIUMII || M686 || M586MMX || M586TSC || M586 || MVIAC3_2 || MGEODE_LX default "6" if MK7 || MK8 || MPENTIUMM || MCORE2 || MVIAC7 config X86_XADD @@ -360,7 +351,7 @@ config X86_POPAD_OK config X86_ALIGNMENT_16 def_bool y - depends on MWINCHIP3D || MWINCHIP2 || MWINCHIPC6 || MCYRIXIII || X86_ELAN || MK6 || M586MMX || M586TSC || M586 || M486 || MVIAC3_2 || MGEODEGX1 + depends on MWINCHIP3D || MWINCHIPC6 || MCYRIXIII || X86_ELAN || MK6 || M586MMX || M586TSC || M586 || M486 || MVIAC3_2 || MGEODEGX1 config X86_INTEL_USERCOPY def_bool y @@ -368,7 +359,7 @@ config X86_INTEL_USERCOPY config X86_USE_PPRO_CHECKSUM def_bool y - depends on MWINCHIP3D || MWINCHIP2 || MWINCHIPC6 || MCYRIXIII || MK7 || MK6 || MPENTIUM4 || MPENTIUMM || MPENTIUMIII || MPENTIUMII || M686 || MK8 || MVIAC3_2 || MEFFICEON || MGEODE_LX || MCORE2 + depends on MWINCHIP3D || MWINCHIPC6 || MCYRIXIII || MK7 || MK6 || MPENTIUM4 || MPENTIUMM || MPENTIUMIII || MPENTIUMII || M686 || MK8 || MVIAC3_2 || MEFFICEON || MGEODE_LX || MCORE2 config X86_USE_3DNOW def_bool y @@ -376,7 +367,7 @@ config X86_USE_3DNOW config X86_OOSTORE def_bool y - depends on (MWINCHIP3D || MWINCHIP2 || MWINCHIPC6) && MTRR + depends on (MWINCHIP3D || MWINCHIPC6) && MTRR # # P6_NOPs are a relatively minor optimization that require a family >= @@ -396,7 +387,7 @@ config X86_P6_NOP config X86_TSC def_bool y - depends on ((MWINCHIP3D || MWINCHIP2 || MCRUSOE || MEFFICEON || MCYRIXIII || MK7 || MK6 || MPENTIUM4 || MPENTIUMM || MPENTIUMIII || MPENTIUMII || M686 || M586MMX || M586TSC || MK8 || MVIAC3_2 || MVIAC7 || MGEODEGX1 || MGEODE_LX || MCORE2) && !X86_NUMAQ) || X86_64 + depends on ((MWINCHIP3D || MCRUSOE || MEFFICEON || MCYRIXIII || MK7 || MK6 || MPENTIUM4 || MPENTIUMM || MPENTIUMIII || MPENTIUMII || M686 || M586MMX || M586TSC || MK8 || MVIAC3_2 || MVIAC7 || MGEODEGX1 || MGEODE_LX || MCORE2) && !X86_NUMAQ) || X86_64 config X86_CMPXCHG64 def_bool y @@ -406,7 +397,7 @@ config X86_CMPXCHG64 # generates cmov. config X86_CMOV def_bool y - depends on (MK7 || MPENTIUM4 || MPENTIUMM || MPENTIUMIII || MPENTIUMII || M686 || MVIAC3_2 || MVIAC7 || X86_64) + depends on (MK8 || MK7 || MCORE2 || MPENTIUM4 || MPENTIUMM || MPENTIUMIII || MPENTIUMII || M686 || MVIAC3_2 || MVIAC7 || MCRUSOE || MEFFICEON || X86_64) config X86_MINIMUM_CPU_FAMILY int @@ -417,7 +408,109 @@ config X86_MINIMUM_CPU_FAMILY config X86_DEBUGCTLMSR def_bool y - depends on !(MK6 || MWINCHIPC6 || MWINCHIP2 || MWINCHIP3D || MCYRIXIII || M586MMX || M586TSC || M586 || M486 || M386) + depends on !(MK6 || MWINCHIPC6 || MWINCHIP3D || MCYRIXIII || M586MMX || M586TSC || M586 || M486 || M386) + +menuconfig PROCESSOR_SELECT + bool "Supported processor vendors" if EMBEDDED + help + This lets you choose what x86 vendor support code your kernel + will include. + +config CPU_SUP_INTEL + default y + bool "Support Intel processors" if PROCESSOR_SELECT + help + This enables detection, tunings and quirks for Intel processors + + You need this enabled if you want your kernel to run on an + Intel CPU. Disabling this option on other types of CPUs + makes the kernel a tiny bit smaller. Disabling it on an Intel + CPU might render the kernel unbootable. + + If unsure, say N. + +config CPU_SUP_CYRIX_32 + default y + bool "Support Cyrix processors" if PROCESSOR_SELECT + depends on !64BIT + help + This enables detection, tunings and quirks for Cyrix processors + + You need this enabled if you want your kernel to run on a + Cyrix CPU. Disabling this option on other types of CPUs + makes the kernel a tiny bit smaller. Disabling it on a Cyrix + CPU might render the kernel unbootable. + + If unsure, say N. + +config CPU_SUP_AMD + default y + bool "Support AMD processors" if PROCESSOR_SELECT + help + This enables detection, tunings and quirks for AMD processors + + You need this enabled if you want your kernel to run on an + AMD CPU. Disabling this option on other types of CPUs + makes the kernel a tiny bit smaller. Disabling it on an AMD + CPU might render the kernel unbootable. + + If unsure, say N. + +config CPU_SUP_CENTAUR_32 + default y + bool "Support Centaur processors" if PROCESSOR_SELECT + depends on !64BIT + help + This enables detection, tunings and quirks for Centaur processors + + You need this enabled if you want your kernel to run on a + Centaur CPU. Disabling this option on other types of CPUs + makes the kernel a tiny bit smaller. Disabling it on a Centaur + CPU might render the kernel unbootable. + + If unsure, say N. + +config CPU_SUP_CENTAUR_64 + default y + bool "Support Centaur processors" if PROCESSOR_SELECT + depends on 64BIT + help + This enables detection, tunings and quirks for Centaur processors + + You need this enabled if you want your kernel to run on a + Centaur CPU. Disabling this option on other types of CPUs + makes the kernel a tiny bit smaller. Disabling it on a Centaur + CPU might render the kernel unbootable. + + If unsure, say N. + +config CPU_SUP_TRANSMETA_32 + default y + bool "Support Transmeta processors" if PROCESSOR_SELECT + depends on !64BIT + help + This enables detection, tunings and quirks for Transmeta processors + + You need this enabled if you want your kernel to run on a + Transmeta CPU. Disabling this option on other types of CPUs + makes the kernel a tiny bit smaller. Disabling it on a Transmeta + CPU might render the kernel unbootable. + + If unsure, say N. + +config CPU_SUP_UMC_32 + default y + bool "Support UMC processors" if PROCESSOR_SELECT + depends on !64BIT + help + This enables detection, tunings and quirks for UMC processors + + You need this enabled if you want your kernel to run on a + UMC CPU. Disabling this option on other types of CPUs + makes the kernel a tiny bit smaller. Disabling it on a UMC + CPU might render the kernel unbootable. + + If unsure, say N. config X86_DS bool "Debug Store support" diff --git a/arch/x86/Kconfig.debug b/arch/x86/Kconfig.debug index 092f019e033..2a3dfbd5e67 100644 --- a/arch/x86/Kconfig.debug +++ b/arch/x86/Kconfig.debug @@ -43,6 +43,19 @@ config EARLY_PRINTK with klogd/syslogd or the X server. You should normally N here, unless you want to debug such a crash. +config EARLY_PRINTK_DBGP + bool "Early printk via EHCI debug port" + default n + depends on EARLY_PRINTK && PCI + help + Write kernel log output directly into the EHCI debug port. + + This is useful for kernel debugging when your machine crashes very + early before the console code is initialized. For normal operation + it is not recommended because it looks ugly and doesn't cooperate + with klogd/syslogd or the X server. You should normally N here, + unless you want to debug such a crash. You need usb debug device. + config DEBUG_STACKOVERFLOW bool "Check for stack overflows" depends on DEBUG_KERNEL diff --git a/arch/x86/Makefile_32.cpu b/arch/x86/Makefile_32.cpu index e372b584e91..80177ec052f 100644 --- a/arch/x86/Makefile_32.cpu +++ b/arch/x86/Makefile_32.cpu @@ -28,7 +28,6 @@ cflags-$(CONFIG_MK8) += $(call cc-option,-march=k8,-march=athlon) cflags-$(CONFIG_MCRUSOE) += -march=i686 $(align)-functions=0 $(align)-jumps=0 $(align)-loops=0 cflags-$(CONFIG_MEFFICEON) += -march=i686 $(call tune,pentium3) $(align)-functions=0 $(align)-jumps=0 $(align)-loops=0 cflags-$(CONFIG_MWINCHIPC6) += $(call cc-option,-march=winchip-c6,-march=i586) -cflags-$(CONFIG_MWINCHIP2) += $(call cc-option,-march=winchip2,-march=i586) cflags-$(CONFIG_MWINCHIP3D) += $(call cc-option,-march=winchip2,-march=i586) cflags-$(CONFIG_MCYRIXIII) += $(call cc-option,-march=c3,-march=i486) $(align)-functions=0 $(align)-jumps=0 $(align)-loops=0 cflags-$(CONFIG_MVIAC3_2) += $(call cc-option,-march=c3-2,-march=i686) @@ -45,3 +44,8 @@ cflags-$(CONFIG_MGEODEGX1) += -march=pentium-mmx # cpu entries cflags-$(CONFIG_X86_GENERIC) += $(call tune,generic,$(call tune,i686)) +# Bug fix for binutils: this option is required in order to keep +# binutils from generating NOPL instructions against our will. +ifneq ($(CONFIG_X86_P6_NOP),y) +cflags-y += $(call cc-option,-Wa$(comma)-mtune=generic32,) +endif diff --git a/arch/x86/boot/Makefile b/arch/x86/boot/Makefile index 7ee102f9c4f..cd48c721001 100644 --- a/arch/x86/boot/Makefile +++ b/arch/x86/boot/Makefile @@ -72,9 +72,7 @@ KBUILD_CFLAGS := $(LINUXINCLUDE) -g -Os -D_SETUP -D__KERNEL__ \ KBUILD_CFLAGS += $(call cc-option,-m32) KBUILD_AFLAGS := $(KBUILD_CFLAGS) -D__ASSEMBLY__ -$(obj)/zImage: IMAGE_OFFSET := 0x1000 $(obj)/zImage: asflags-y := $(SVGA_MODE) $(RAMDISK) -$(obj)/bzImage: IMAGE_OFFSET := 0x100000 $(obj)/bzImage: ccflags-y := -D__BIG_KERNEL__ $(obj)/bzImage: asflags-y := $(SVGA_MODE) $(RAMDISK) -D__BIG_KERNEL__ $(obj)/bzImage: BUILDFLAGS := -b @@ -117,7 +115,7 @@ $(obj)/setup.bin: $(obj)/setup.elf FORCE $(call if_changed,objcopy) $(obj)/compressed/vmlinux: FORCE - $(Q)$(MAKE) $(build)=$(obj)/compressed IMAGE_OFFSET=$(IMAGE_OFFSET) $@ + $(Q)$(MAKE) $(build)=$(obj)/compressed $@ # Set this if you want to pass append arguments to the zdisk/fdimage/isoimage kernel FDARGS = @@ -181,6 +179,7 @@ isoimage: $(BOOTIMAGE) mkisofs -J -r -o $(obj)/image.iso -b isolinux.bin -c boot.cat \ -no-emul-boot -boot-load-size 4 -boot-info-table \ $(obj)/isoimage + isohybrid $(obj)/image.iso 2>/dev/null || true rm -rf $(obj)/isoimage zlilo: $(BOOTIMAGE) diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile index 92fdd35bd93..1771c804e02 100644 --- a/arch/x86/boot/compressed/Makefile +++ b/arch/x86/boot/compressed/Makefile @@ -27,9 +27,8 @@ $(obj)/vmlinux.bin: vmlinux FORCE $(call if_changed,objcopy) -ifeq ($(CONFIG_X86_32),y) -targets += vmlinux.bin.all vmlinux.relocs -hostprogs-y := relocs +targets += vmlinux.bin.all vmlinux.relocs relocs +hostprogs-$(CONFIG_X86_32) += relocs quiet_cmd_relocs = RELOCS $@ cmd_relocs = $(obj)/relocs $< > $@;$(obj)/relocs --abs-relocs $< @@ -43,6 +42,8 @@ quiet_cmd_relocbin = BUILD $@ $(obj)/vmlinux.bin.all: $(vmlinux.bin.all-y) FORCE $(call if_changed,relocbin) +ifeq ($(CONFIG_X86_32),y) + ifdef CONFIG_RELOCATABLE $(obj)/vmlinux.bin.gz: $(obj)/vmlinux.bin.all FORCE $(call if_changed,gzip) @@ -59,6 +60,5 @@ $(obj)/vmlinux.bin.gz: $(obj)/vmlinux.bin FORCE LDFLAGS_piggy.o := -r --format binary --oformat elf64-x86-64 -T endif - $(obj)/piggy.o: $(obj)/vmlinux.scr $(obj)/vmlinux.bin.gz FORCE $(call if_changed,ld) diff --git a/arch/x86/boot/cpu.c b/arch/x86/boot/cpu.c index 75298fe2edc..6ec6bb6e995 100644 --- a/arch/x86/boot/cpu.c +++ b/arch/x86/boot/cpu.c @@ -59,17 +59,18 @@ int validate_cpu(void) u32 e = err_flags[i]; for (j = 0; j < 32; j++) { - int n = (i << 5)+j; - if (*msg_strs < n) { + if (msg_strs[0] < i || + (msg_strs[0] == i && msg_strs[1] < j)) { /* Skip to the next string */ - do { - msg_strs++; - } while (*msg_strs); - msg_strs++; + msg_strs += 2; + while (*msg_strs++) + ; } if (e & 1) { - if (*msg_strs == n && msg_strs[1]) - printf("%s ", msg_strs+1); + if (msg_strs[0] == i && + msg_strs[1] == j && + msg_strs[2]) + printf("%s ", msg_strs+2); else printf("%d:%d ", i, j); } diff --git a/arch/x86/boot/edd.c b/arch/x86/boot/edd.c index d93cbc6464d..1aae8f3e5ca 100644 --- a/arch/x86/boot/edd.c +++ b/arch/x86/boot/edd.c @@ -41,6 +41,7 @@ static u32 read_mbr_sig(u8 devno, struct edd_info *ei, u32 *mbrsig) char *mbrbuf_ptr, *mbrbuf_end; u32 buf_base, mbr_base; extern char _end[]; + u16 mbr_magic; sector_size = ei->params.bytes_per_sector; if (!sector_size) @@ -58,11 +59,15 @@ static u32 read_mbr_sig(u8 devno, struct edd_info *ei, u32 *mbrsig) if (mbrbuf_end > (char *)(size_t)boot_params.hdr.heap_end_ptr) return -1; + memset(mbrbuf_ptr, 0, sector_size); if (read_mbr(devno, mbrbuf_ptr)) return -1; *mbrsig = *(u32 *)&mbrbuf_ptr[EDD_MBR_SIG_OFFSET]; - return 0; + mbr_magic = *(u16 *)&mbrbuf_ptr[510]; + + /* check for valid MBR magic */ + return mbr_magic == 0xAA55 ? 0 : -1; } static int get_edd_info(u8 devno, struct edd_info *ei) diff --git a/arch/x86/boot/mkcpustr.c b/arch/x86/boot/mkcpustr.c index bbe76953bae..8ef60f20b37 100644 --- a/arch/x86/boot/mkcpustr.c +++ b/arch/x86/boot/mkcpustr.c @@ -15,33 +15,33 @@ #include <stdio.h> -#include "../kernel/cpu/feature_names.c" - -#if NCAPFLAGS > 8 -# error "Need to adjust the boot code handling of CPUID strings" -#endif +#include "../kernel/cpu/capflags.c" int main(void) { - int i; + int i, j; const char *str; printf("static const char x86_cap_strs[] = \n"); - for (i = 0; i < NCAPINTS*32; i++) { - str = x86_cap_flags[i]; - - if (i == NCAPINTS*32-1) { - /* The last entry must be unconditional; this - also consumes the compiler-added null character */ - if (!str) - str = ""; - printf("\t\"\\x%02x\"\"%s\"\n", i, str); - } else if (str) { - printf("#if REQUIRED_MASK%d & (1 << %d)\n" - "\t\"\\x%02x\"\"%s\\0\"\n" - "#endif\n", - i >> 5, i & 31, i, str); + for (i = 0; i < NCAPINTS; i++) { + for (j = 0; j < 32; j++) { + str = x86_cap_flags[i*32+j]; + + if (i == NCAPINTS-1 && j == 31) { + /* The last entry must be unconditional; this + also consumes the compiler-added null + character */ + if (!str) + str = ""; + printf("\t\"\\x%02x\\x%02x\"\"%s\"\n", + i, j, str); + } else if (str) { + printf("#if REQUIRED_MASK%d & (1 << %d)\n" + "\t\"\\x%02x\\x%02x\"\"%s\\0\"\n" + "#endif\n", + i, j, i, j, str); + } } } printf("\t;\n"); diff --git a/arch/x86/boot/video-vesa.c b/arch/x86/boot/video-vesa.c index 401ad998ad0..1e6fe0214c8 100644 --- a/arch/x86/boot/video-vesa.c +++ b/arch/x86/boot/video-vesa.c @@ -224,7 +224,7 @@ static void vesa_store_pm_info(void) static void vesa_store_mode_params_graphics(void) { /* Tell the kernel we're in VESA graphics mode */ - boot_params.screen_info.orig_video_isVGA = 0x23; + boot_params.screen_info.orig_video_isVGA = VIDEO_TYPE_VLFB; /* Mode parameters */ boot_params.screen_info.vesa_attributes = vminfo.mode_attr; diff --git a/arch/x86/configs/i386_defconfig b/arch/x86/configs/i386_defconfig index ef9a52005ec..52d0359719d 100644 --- a/arch/x86/configs/i386_defconfig +++ b/arch/x86/configs/i386_defconfig @@ -213,7 +213,6 @@ CONFIG_M686=y # CONFIG_MCRUSOE is not set # CONFIG_MEFFICEON is not set # CONFIG_MWINCHIPC6 is not set -# CONFIG_MWINCHIP2 is not set # CONFIG_MWINCHIP3D is not set # CONFIG_MGEODEGX1 is not set # CONFIG_MGEODE_LX is not set @@ -1535,7 +1534,6 @@ CONFIG_BACKLIGHT_CLASS_DEVICE=y CONFIG_VGA_CONSOLE=y CONFIG_VGACON_SOFT_SCROLLBACK=y CONFIG_VGACON_SOFT_SCROLLBACK_SIZE=64 -CONFIG_VIDEO_SELECT=y CONFIG_DUMMY_CONSOLE=y # CONFIG_FRAMEBUFFER_CONSOLE is not set CONFIG_LOGO=y diff --git a/arch/x86/configs/x86_64_defconfig b/arch/x86/configs/x86_64_defconfig index e620ea6e2a7..f0a03d7a7d6 100644 --- a/arch/x86/configs/x86_64_defconfig +++ b/arch/x86/configs/x86_64_defconfig @@ -210,7 +210,6 @@ CONFIG_X86_PC=y # CONFIG_MCRUSOE is not set # CONFIG_MEFFICEON is not set # CONFIG_MWINCHIPC6 is not set -# CONFIG_MWINCHIP2 is not set # CONFIG_MWINCHIP3D is not set # CONFIG_MGEODEGX1 is not set # CONFIG_MGEODE_LX is not set @@ -1505,7 +1504,6 @@ CONFIG_BACKLIGHT_CLASS_DEVICE=y CONFIG_VGA_CONSOLE=y CONFIG_VGACON_SOFT_SCROLLBACK=y CONFIG_VGACON_SOFT_SCROLLBACK_SIZE=64 -CONFIG_VIDEO_SELECT=y CONFIG_DUMMY_CONSOLE=y # CONFIG_FRAMEBUFFER_CONSOLE is not set CONFIG_LOGO=y diff --git a/arch/x86/ia32/ia32_signal.c b/arch/x86/ia32/ia32_signal.c index f1a2ac777fa..4bc02b23674 100644 --- a/arch/x86/ia32/ia32_signal.c +++ b/arch/x86/ia32/ia32_signal.c @@ -179,9 +179,10 @@ struct sigframe u32 pretcode; int sig; struct sigcontext_ia32 sc; - struct _fpstate_ia32 fpstate; + struct _fpstate_ia32 fpstate_unused; /* look at kernel/sigframe.h */ unsigned int extramask[_COMPAT_NSIG_WORDS-1]; char retcode[8]; + /* fp state follows here */ }; struct rt_sigframe @@ -192,8 +193,8 @@ struct rt_sigframe u32 puc; compat_siginfo_t info; struct ucontext_ia32 uc; - struct _fpstate_ia32 fpstate; char retcode[8]; + /* fp state follows here */ }; #define COPY(x) { \ @@ -215,7 +216,7 @@ static int ia32_restore_sigcontext(struct pt_regs *regs, unsigned int *peax) { unsigned int tmpflags, gs, oldgs, err = 0; - struct _fpstate_ia32 __user *buf; + void __user *buf; u32 tmp; /* Always make any pending restarted system calls return -EINTR */ @@ -259,26 +260,12 @@ static int ia32_restore_sigcontext(struct pt_regs *regs, err |= __get_user(tmp, &sc->fpstate); buf = compat_ptr(tmp); - if (buf) { - if (!access_ok(VERIFY_READ, buf, sizeof(*buf))) - goto badframe; - err |= restore_i387_ia32(buf); - } else { - struct task_struct *me = current; - - if (used_math()) { - clear_fpu(me); - clear_used_math(); - } - } + err |= restore_i387_xstate_ia32(buf); err |= __get_user(tmp, &sc->ax); *peax = tmp; return err; - -badframe: - return 1; } asmlinkage long sys32_sigreturn(struct pt_regs *regs) @@ -350,7 +337,7 @@ badframe: */ static int ia32_setup_sigcontext(struct sigcontext_ia32 __user *sc, - struct _fpstate_ia32 __user *fpstate, + void __user *fpstate, struct pt_regs *regs, unsigned int mask) { int tmp, err = 0; @@ -364,31 +351,28 @@ static int ia32_setup_sigcontext(struct sigcontext_ia32 __user *sc, savesegment(es, tmp); err |= __put_user(tmp, (unsigned int __user *)&sc->es); - err |= __put_user((u32)regs->di, &sc->di); - err |= __put_user((u32)regs->si, &sc->si); - err |= __put_user((u32)regs->bp, &sc->bp); - err |= __put_user((u32)regs->sp, &sc->sp); - err |= __put_user((u32)regs->bx, &sc->bx); - err |= __put_user((u32)regs->dx, &sc->dx); - err |= __put_user((u32)regs->cx, &sc->cx); - err |= __put_user((u32)regs->ax, &sc->ax); - err |= __put_user((u32)regs->cs, &sc->cs); - err |= __put_user((u32)regs->ss, &sc->ss); + err |= __put_user(regs->di, &sc->di); + err |= __put_user(regs->si, &sc->si); + err |= __put_user(regs->bp, &sc->bp); + err |= __put_user(regs->sp, &sc->sp); + err |= __put_user(regs->bx, &sc->bx); + err |= __put_user(regs->dx, &sc->dx); + err |= __put_user(regs->cx, &sc->cx); + err |= __put_user(regs->ax, &sc->ax); + err |= __put_user(regs->cs, &sc->cs); + err |= __put_user(regs->ss, &sc->ss); err |= __put_user(current->thread.trap_no, &sc->trapno); err |= __put_user(current->thread.error_code, &sc->err); - err |= __put_user((u32)regs->ip, &sc->ip); - err |= __put_user((u32)regs->flags, &sc->flags); - err |= __put_user((u32)regs->sp, &sc->sp_at_signal); + err |= __put_user(regs->ip, &sc->ip); + err |= __put_user(regs->flags, &sc->flags); + err |= __put_user(regs->sp, &sc->sp_at_signal); - tmp = save_i387_ia32(fpstate); + tmp = save_i387_xstate_ia32(fpstate); if (tmp < 0) err = -EFAULT; - else { - clear_used_math(); - stts(); + else err |= __put_user(ptr_to_compat(tmp ? fpstate : NULL), &sc->fpstate); - } /* non-iBCS2 extensions.. */ err |= __put_user(mask, &sc->oldmask); @@ -401,7 +385,8 @@ static int ia32_setup_sigcontext(struct sigcontext_ia32 __user *sc, * Determine which stack to use.. */ static void __user *get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, - size_t frame_size) + size_t frame_size, + void **fpstate) { unsigned long sp; @@ -420,6 +405,11 @@ static void __user *get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, ka->sa.sa_restorer) sp = (unsigned long) ka->sa.sa_restorer; + if (used_math()) { + sp = sp - sig_xstate_ia32_size; + *fpstate = (struct _fpstate_ia32 *) sp; + } + sp -= frame_size; /* Align the stack pointer according to the i386 ABI, * i.e. so that on function entry ((sp + 4) & 15) == 0. */ @@ -433,6 +423,7 @@ int ia32_setup_frame(int sig, struct k_sigaction *ka, struct sigframe __user *frame; void __user *restorer; int err = 0; + void __user *fpstate = NULL; /* copy_to_user optimizes that into a single 8 byte store */ static const struct { @@ -447,25 +438,21 @@ int ia32_setup_frame(int sig, struct k_sigaction *ka, 0, }; - frame = get_sigframe(ka, regs, sizeof(*frame)); + frame = get_sigframe(ka, regs, sizeof(*frame), &fpstate); if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) - goto give_sigsegv; + return -EFAULT; - err |= __put_user(sig, &frame->sig); - if (err) - goto give_sigsegv; + if (__put_user(sig, &frame->sig)) + return -EFAULT; - err |= ia32_setup_sigcontext(&frame->sc, &frame->fpstate, regs, - set->sig[0]); - if (err) - goto give_sigsegv; + if (ia32_setup_sigcontext(&frame->sc, fpstate, regs, set->sig[0])) + return -EFAULT; if (_COMPAT_NSIG_WORDS > 1) { - err |= __copy_to_user(frame->extramask, &set->sig[1], - sizeof(frame->extramask)); - if (err) - goto give_sigsegv; + if (__copy_to_user(frame->extramask, &set->sig[1], + sizeof(frame->extramask))) + return -EFAULT; } if (ka->sa.sa_flags & SA_RESTORER) { @@ -486,7 +473,7 @@ int ia32_setup_frame(int sig, struct k_sigaction *ka, */ err |= __copy_to_user(frame->retcode, &code, 8); if (err) - goto give_sigsegv; + return -EFAULT; /* Set up registers for signal handler */ regs->sp = (unsigned long) frame; @@ -509,10 +496,6 @@ int ia32_setup_frame(int sig, struct k_sigaction *ka, #endif return 0; - -give_sigsegv: - force_sigsegv(sig, current); - return -EFAULT; } int ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, @@ -521,6 +504,7 @@ int ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, struct rt_sigframe __user *frame; void __user *restorer; int err = 0; + void __user *fpstate = NULL; /* __copy_to_user optimizes that into a single 8 byte store */ static const struct { @@ -536,30 +520,33 @@ int ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, 0, }; - frame = get_sigframe(ka, regs, sizeof(*frame)); + frame = get_sigframe(ka, regs, sizeof(*frame), &fpstate); if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) - goto give_sigsegv; + return -EFAULT; err |= __put_user(sig, &frame->sig); err |= __put_user(ptr_to_compat(&frame->info), &frame->pinfo); err |= __put_user(ptr_to_compat(&frame->uc), &frame->puc); err |= copy_siginfo_to_user32(&frame->info, info); if (err) - goto give_sigsegv; + return -EFAULT; /* Create the ucontext. */ - err |= __put_user(0, &frame->uc.uc_flags); + if (cpu_has_xsave) + err |= __put_user(UC_FP_XSTATE, &frame->uc.uc_flags); + else + err |= __put_user(0, &frame->uc.uc_flags); err |= __put_user(0, &frame->uc.uc_link); err |= __put_user(current->sas_ss_sp, &frame->uc.uc_stack.ss_sp); err |= __put_user(sas_ss_flags(regs->sp), &frame->uc.uc_stack.ss_flags); err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size); - err |= ia32_setup_sigcontext(&frame->uc.uc_mcontext, &frame->fpstate, + err |= ia32_setup_sigcontext(&frame->uc.uc_mcontext, fpstate, regs, set->sig[0]); err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); if (err) - goto give_sigsegv; + return -EFAULT; if (ka->sa.sa_flags & SA_RESTORER) restorer = ka->sa.sa_restorer; @@ -574,7 +561,7 @@ int ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, */ err |= __copy_to_user(frame->retcode, &code, 8); if (err) - goto give_sigsegv; + return -EFAULT; /* Set up registers for signal handler */ regs->sp = (unsigned long) frame; @@ -602,8 +589,4 @@ int ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, #endif return 0; - -give_sigsegv: - force_sigsegv(sig, current); - return -EFAULT; } diff --git a/arch/x86/ia32/ia32entry.S b/arch/x86/ia32/ia32entry.S index ffc1bb4fed7..eb4314768bf 100644 --- a/arch/x86/ia32/ia32entry.S +++ b/arch/x86/ia32/ia32entry.S @@ -39,11 +39,11 @@ .endm /* clobbers %eax */ - .macro CLEAR_RREGS + .macro CLEAR_RREGS _r9=rax xorl %eax,%eax movq %rax,R11(%rsp) movq %rax,R10(%rsp) - movq %rax,R9(%rsp) + movq %\_r9,R9(%rsp) movq %rax,R8(%rsp) .endm @@ -52,11 +52,10 @@ * We don't reload %eax because syscall_trace_enter() returned * the value it wants us to use in the table lookup. */ - .macro LOAD_ARGS32 offset - movl \offset(%rsp),%r11d - movl \offset+8(%rsp),%r10d + .macro LOAD_ARGS32 offset, _r9=0 + .if \_r9 movl \offset+16(%rsp),%r9d - movl \offset+24(%rsp),%r8d + .endif movl \offset+40(%rsp),%ecx movl \offset+48(%rsp),%edx movl \offset+56(%rsp),%esi @@ -145,7 +144,7 @@ ENTRY(ia32_sysenter_target) SAVE_ARGS 0,0,1 /* no need to do an access_ok check here because rbp has been 32bit zero extended */ -1: movl (%rbp),%r9d +1: movl (%rbp),%ebp .section __ex_table,"a" .quad 1b,ia32_badarg .previous @@ -157,7 +156,7 @@ ENTRY(ia32_sysenter_target) cmpl $(IA32_NR_syscalls-1),%eax ja ia32_badsys sysenter_do_call: - IA32_ARG_FIXUP 1 + IA32_ARG_FIXUP sysenter_dispatch: call *ia32_sys_call_table(,%rax,8) movq %rax,RAX-ARGOFFSET(%rsp) @@ -234,20 +233,17 @@ sysexit_audit: #endif sysenter_tracesys: - xchgl %r9d,%ebp #ifdef CONFIG_AUDITSYSCALL testl $(_TIF_WORK_SYSCALL_ENTRY & ~_TIF_SYSCALL_AUDIT),TI_flags(%r10) jz sysenter_auditsys #endif SAVE_REST CLEAR_RREGS - movq %r9,R9(%rsp) movq $-ENOSYS,RAX(%rsp)/* ptrace can change this for a bad syscall */ movq %rsp,%rdi /* &pt_regs -> arg1 */ call syscall_trace_enter LOAD_ARGS32 ARGOFFSET /* reload args from stack in case ptrace changed it */ RESTORE_REST - xchgl %ebp,%r9d cmpl $(IA32_NR_syscalls-1),%eax ja int_ret_from_sys_call /* sysenter_tracesys has set RAX(%rsp) */ jmp sysenter_do_call @@ -314,9 +310,9 @@ ENTRY(ia32_cstar_target) testl $_TIF_WORK_SYSCALL_ENTRY,TI_flags(%r10) CFI_REMEMBER_STATE jnz cstar_tracesys -cstar_do_call: cmpl $IA32_NR_syscalls-1,%eax ja ia32_badsys +cstar_do_call: IA32_ARG_FIXUP 1 cstar_dispatch: call *ia32_sys_call_table(,%rax,8) @@ -357,15 +353,13 @@ cstar_tracesys: #endif xchgl %r9d,%ebp SAVE_REST - CLEAR_RREGS - movq %r9,R9(%rsp) + CLEAR_RREGS r9 movq $-ENOSYS,RAX(%rsp) /* ptrace can change this for a bad syscall */ movq %rsp,%rdi /* &pt_regs -> arg1 */ call syscall_trace_enter - LOAD_ARGS32 ARGOFFSET /* reload args from stack in case ptrace changed it */ + LOAD_ARGS32 ARGOFFSET, 1 /* reload args from stack in case ptrace changed it */ RESTORE_REST xchgl %ebp,%r9d - movl RSP-ARGOFFSET(%rsp), %r8d cmpl $(IA32_NR_syscalls-1),%eax ja int_ret_from_sys_call /* cstar_tracesys has set RAX(%rsp) */ jmp cstar_do_call diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile index 3db651fc8ec..0d41f0343dc 100644 --- a/arch/x86/kernel/Makefile +++ b/arch/x86/kernel/Makefile @@ -10,7 +10,7 @@ ifdef CONFIG_FTRACE # Do not profile debug and lowlevel utilities CFLAGS_REMOVE_tsc.o = -pg CFLAGS_REMOVE_rtc.o = -pg -CFLAGS_REMOVE_paravirt.o = -pg +CFLAGS_REMOVE_paravirt-spinlocks.o = -pg endif # @@ -23,7 +23,7 @@ CFLAGS_hpet.o := $(nostackp) CFLAGS_tsc.o := $(nostackp) obj-y := process_$(BITS).o signal_$(BITS).o entry_$(BITS).o -obj-y += traps_$(BITS).o irq_$(BITS).o +obj-y += traps.o irq_$(BITS).o dumpstack_$(BITS).o obj-y += time_$(BITS).o ioport.o ldt.o obj-y += setup.o i8259.o irqinit_$(BITS).o setup_percpu.o obj-$(CONFIG_X86_VISWS) += visws_quirks.o @@ -38,7 +38,7 @@ obj-y += tsc.o io_delay.o rtc.o obj-$(CONFIG_X86_TRAMPOLINE) += trampoline.o obj-y += process.o -obj-y += i387.o +obj-y += i387.o xsave.o obj-y += ptrace.o obj-y += ds.o obj-$(CONFIG_X86_32) += tls.o @@ -51,7 +51,6 @@ obj-$(CONFIG_X86_BIOS_REBOOT) += reboot.o obj-$(CONFIG_MCA) += mca_32.o obj-$(CONFIG_X86_MSR) += msr.o obj-$(CONFIG_X86_CPUID) += cpuid.o -obj-$(CONFIG_MICROCODE) += microcode.o obj-$(CONFIG_PCI) += early-quirks.o apm-y := apm_32.o obj-$(CONFIG_APM) += apm.o @@ -69,6 +68,7 @@ obj-$(CONFIG_KEXEC) += machine_kexec_$(BITS).o obj-$(CONFIG_KEXEC) += relocate_kernel_$(BITS).o crash.o obj-$(CONFIG_CRASH_DUMP) += crash_dump_$(BITS).o obj-$(CONFIG_X86_NUMAQ) += numaq_32.o +obj-$(CONFIG_X86_ES7000) += es7000_32.o obj-$(CONFIG_X86_SUMMIT_NUMA) += summit_32.o obj-y += vsmp_64.o obj-$(CONFIG_KPROBES) += kprobes.o @@ -89,7 +89,7 @@ obj-$(CONFIG_DEBUG_NX_TEST) += test_nx.o obj-$(CONFIG_VMI) += vmi_32.o vmiclock_32.o obj-$(CONFIG_KVM_GUEST) += kvm.o obj-$(CONFIG_KVM_CLOCK) += kvmclock.o -obj-$(CONFIG_PARAVIRT) += paravirt.o paravirt_patch_$(BITS).o +obj-$(CONFIG_PARAVIRT) += paravirt.o paravirt_patch_$(BITS).o paravirt-spinlocks.o obj-$(CONFIG_PARAVIRT_CLOCK) += pvclock.o obj-$(CONFIG_PCSPKR_PLATFORM) += pcspeaker.o @@ -99,11 +99,18 @@ scx200-y += scx200_32.o obj-$(CONFIG_OLPC) += olpc.o +microcode-y := microcode_core.o +microcode-$(CONFIG_MICROCODE_INTEL) += microcode_intel.o +microcode-$(CONFIG_MICROCODE_AMD) += microcode_amd.o +obj-$(CONFIG_MICROCODE) += microcode.o + ### # 64 bit specific files ifeq ($(CONFIG_X86_64),y) obj-y += genapic_64.o genapic_flat_64.o genx2apic_uv_x.o tlb_uv.o obj-y += bios_uv.o + obj-y += genx2apic_cluster.o + obj-y += genx2apic_phys.o obj-$(CONFIG_X86_PM_TIMER) += pmtimer_64.o obj-$(CONFIG_AUDIT) += audit_64.o diff --git a/arch/x86/kernel/acpi/boot.c b/arch/x86/kernel/acpi/boot.c index 7d40ef7b36e..eb875cdc736 100644 --- a/arch/x86/kernel/acpi/boot.c +++ b/arch/x86/kernel/acpi/boot.c @@ -252,10 +252,8 @@ static void __cpuinit acpi_register_lapic(int id, u8 enabled) return; } -#ifdef CONFIG_X86_32 if (boot_cpu_physical_apicid != -1U) ver = apic_version[boot_cpu_physical_apicid]; -#endif generic_processor_info(id, ver); } @@ -774,11 +772,9 @@ static void __init acpi_register_lapic_address(unsigned long address) set_fixmap_nocache(FIX_APIC_BASE, address); if (boot_cpu_physical_apicid == -1U) { - boot_cpu_physical_apicid = GET_APIC_ID(read_apic_id()); -#ifdef CONFIG_X86_32 + boot_cpu_physical_apicid = read_apic_id(); apic_version[boot_cpu_physical_apicid] = GET_APIC_VERSION(apic_read(APIC_LVR)); -#endif } } @@ -1350,7 +1346,9 @@ static void __init acpi_process_madt(void) acpi_ioapic = 1; smp_found_config = 1; +#ifdef CONFIG_X86_32 setup_apic_routing(); +#endif } } if (error == -EINVAL) { @@ -1420,8 +1418,16 @@ static int __init force_acpi_ht(const struct dmi_system_id *d) */ static int __init dmi_ignore_irq0_timer_override(const struct dmi_system_id *d) { - pr_notice("%s detected: Ignoring BIOS IRQ0 pin2 override\n", d->ident); - acpi_skip_timer_override = 1; + /* + * The ati_ixp4x0_rev() early PCI quirk should have set + * the acpi_skip_timer_override flag already: + */ + if (!acpi_skip_timer_override) { + WARN(1, KERN_ERR "ati_ixp4x0 quirk not complete.\n"); + pr_notice("%s detected: Ignoring BIOS IRQ0 pin2 override\n", + d->ident); + acpi_skip_timer_override = 1; + } return 0; } diff --git a/arch/x86/kernel/alternative.c b/arch/x86/kernel/alternative.c index fb04e49776b..a84ac7b570e 100644 --- a/arch/x86/kernel/alternative.c +++ b/arch/x86/kernel/alternative.c @@ -444,7 +444,7 @@ void __init alternative_instructions(void) _text, _etext); /* Only switch to UP mode if we don't immediately boot others */ - if (num_possible_cpus() == 1 || setup_max_cpus <= 1) + if (num_present_cpus() == 1 || setup_max_cpus <= 1) alternatives_smp_switch(0); } #endif diff --git a/arch/x86/kernel/amd_iommu_init.c b/arch/x86/kernel/amd_iommu_init.c index 148fcfe22f1..4cd8083c58b 100644 --- a/arch/x86/kernel/amd_iommu_init.c +++ b/arch/x86/kernel/amd_iommu_init.c @@ -723,9 +723,7 @@ static int __init init_iommu_one(struct amd_iommu *iommu, struct ivhd_header *h) init_iommu_from_acpi(iommu, h); init_iommu_devices(iommu); - pci_enable_device(iommu->dev); - - return 0; + return pci_enable_device(iommu->dev); } /* diff --git a/arch/x86/kernel/apic_32.c b/arch/x86/kernel/apic_32.c index f88bd0d982b..21c831d96af 100644 --- a/arch/x86/kernel/apic_32.c +++ b/arch/x86/kernel/apic_32.c @@ -60,10 +60,8 @@ unsigned long mp_lapic_addr; static int force_enable_local_apic; int disable_apic; -/* Local APIC timer verification ok */ -static int local_apic_timer_verify_ok; /* Disable local APIC timer from the kernel commandline or via dmi quirk */ -static int local_apic_timer_disabled; +static int disable_apic_timer __cpuinitdata; /* Local APIC timer works in C2 */ int local_apic_timer_c2_ok; EXPORT_SYMBOL_GPL(local_apic_timer_c2_ok); @@ -130,7 +128,11 @@ static inline int lapic_get_version(void) */ static inline int lapic_is_integrated(void) { +#ifdef CONFIG_X86_64 + return 1; +#else return APIC_INTEGRATED(lapic_get_version()); +#endif } /* @@ -145,13 +147,18 @@ static int modern_apic(void) return lapic_get_version() >= 0x14; } -void apic_wait_icr_idle(void) +/* + * Paravirt kernels also might be using these below ops. So we still + * use generic apic_read()/apic_write(), which might be pointing to different + * ops in PARAVIRT case. + */ +void xapic_wait_icr_idle(void) { while (apic_read(APIC_ICR) & APIC_ICR_BUSY) cpu_relax(); } -u32 safe_apic_wait_icr_idle(void) +u32 safe_xapic_wait_icr_idle(void) { u32 send_status; int timeout; @@ -167,16 +174,48 @@ u32 safe_apic_wait_icr_idle(void) return send_status; } +void xapic_icr_write(u32 low, u32 id) +{ + apic_write(APIC_ICR2, SET_APIC_DEST_FIELD(id)); + apic_write(APIC_ICR, low); +} + +u64 xapic_icr_read(void) +{ + u32 icr1, icr2; + + icr2 = apic_read(APIC_ICR2); + icr1 = apic_read(APIC_ICR); + + return icr1 | ((u64)icr2 << 32); +} + +static struct apic_ops xapic_ops = { + .read = native_apic_mem_read, + .write = native_apic_mem_write, + .icr_read = xapic_icr_read, + .icr_write = xapic_icr_write, + .wait_icr_idle = xapic_wait_icr_idle, + .safe_wait_icr_idle = safe_xapic_wait_icr_idle, +}; + +struct apic_ops __read_mostly *apic_ops = &xapic_ops; +EXPORT_SYMBOL_GPL(apic_ops); + /** * enable_NMI_through_LVT0 - enable NMI through local vector table 0 */ void __cpuinit enable_NMI_through_LVT0(void) { - unsigned int v = APIC_DM_NMI; + unsigned int v; - /* Level triggered for 82489DX */ + /* unmask and set to NMI */ + v = APIC_DM_NMI; + + /* Level triggered for 82489DX (32bit mode) */ if (!lapic_is_integrated()) v |= APIC_LVT_LEVEL_TRIGGER; + apic_write(APIC_LVT0, v); } @@ -193,9 +232,13 @@ int get_physical_broadcast(void) */ int lapic_get_maxlvt(void) { - unsigned int v = apic_read(APIC_LVR); + unsigned int v; - /* 82489DXs do not report # of LVT entries. */ + v = apic_read(APIC_LVR); + /* + * - we always have APIC integrated on 64bit mode + * - 82489DXs do not report # of LVT entries + */ return APIC_INTEGRATED(GET_APIC_VERSION(v)) ? GET_APIC_MAXLVT(v) : 2; } @@ -203,8 +246,12 @@ int lapic_get_maxlvt(void) * Local APIC timer */ -/* Clock divisor is set to 16 */ +/* Clock divisor */ +#ifdef CONFG_X86_64 +#define APIC_DIVISOR 1 +#else #define APIC_DIVISOR 16 +#endif /* * This function sets up the local APIC timer, with a timeout of @@ -212,6 +259,9 @@ int lapic_get_maxlvt(void) * this function twice on the boot CPU, once with a bogus timeout * value, second time for real. The other (noncalibrating) CPUs * call this function only once, with the real, calibrated value. + * + * We do reads before writes even if unnecessary, to get around the + * P5 APIC double write bug. */ static void __setup_APIC_LVTT(unsigned int clocks, int oneshot, int irqen) { @@ -233,14 +283,48 @@ static void __setup_APIC_LVTT(unsigned int clocks, int oneshot, int irqen) */ tmp_value = apic_read(APIC_TDCR); apic_write(APIC_TDCR, - (tmp_value & ~(APIC_TDR_DIV_1 | APIC_TDR_DIV_TMBASE)) | - APIC_TDR_DIV_16); + (tmp_value & ~(APIC_TDR_DIV_1 | APIC_TDR_DIV_TMBASE)) | + APIC_TDR_DIV_16); if (!oneshot) apic_write(APIC_TMICT, clocks / APIC_DIVISOR); } /* + * Setup extended LVT, AMD specific (K8, family 10h) + * + * Vector mappings are hard coded. On K8 only offset 0 (APIC500) and + * MCE interrupts are supported. Thus MCE offset must be set to 0. + * + * If mask=1, the LVT entry does not generate interrupts while mask=0 + * enables the vector. See also the BKDGs. + */ + +#define APIC_EILVT_LVTOFF_MCE 0 +#define APIC_EILVT_LVTOFF_IBS 1 + +static void setup_APIC_eilvt(u8 lvt_off, u8 vector, u8 msg_type, u8 mask) +{ + unsigned long reg = (lvt_off << 4) + APIC_EILVT0; + unsigned int v = (mask << 16) | (msg_type << 8) | vector; + + apic_write(reg, v); +} + +u8 setup_APIC_eilvt_mce(u8 vector, u8 msg_type, u8 mask) +{ + setup_APIC_eilvt(APIC_EILVT_LVTOFF_MCE, vector, msg_type, mask); + return APIC_EILVT_LVTOFF_MCE; +} + +u8 setup_APIC_eilvt_ibs(u8 vector, u8 msg_type, u8 mask) +{ + setup_APIC_eilvt(APIC_EILVT_LVTOFF_IBS, vector, msg_type, mask); + return APIC_EILVT_LVTOFF_IBS; +} +EXPORT_SYMBOL_GPL(setup_APIC_eilvt_ibs); + +/* * Program the next event, relative to now */ static int lapic_next_event(unsigned long delta, @@ -259,8 +343,8 @@ static void lapic_timer_setup(enum clock_event_mode mode, unsigned long flags; unsigned int v; - /* Lapic used for broadcast ? */ - if (!local_apic_timer_verify_ok) + /* Lapic used as dummy for broadcast ? */ + if (evt->features & CLOCK_EVT_FEAT_DUMMY) return; local_irq_save(flags); @@ -473,7 +557,7 @@ static int __init calibrate_APIC_clock(void) return -1; } - local_apic_timer_verify_ok = 1; + levt->features &= ~CLOCK_EVT_FEAT_DUMMY; /* We trust the pm timer based calibration */ if (!pm_referenced) { @@ -507,11 +591,11 @@ static int __init calibrate_APIC_clock(void) if (deltaj >= LAPIC_CAL_LOOPS-2 && deltaj <= LAPIC_CAL_LOOPS+2) apic_printk(APIC_VERBOSE, "... jiffies result ok\n"); else - local_apic_timer_verify_ok = 0; + levt->features |= CLOCK_EVT_FEAT_DUMMY; } else local_irq_enable(); - if (!local_apic_timer_verify_ok) { + if (levt->features & CLOCK_EVT_FEAT_DUMMY) { printk(KERN_WARNING "APIC timer disabled due to verification failure.\n"); return -1; @@ -533,7 +617,8 @@ void __init setup_boot_APIC_clock(void) * timer as a dummy clock event source on SMP systems, so the * broadcast mechanism is used. On UP systems simply ignore it. */ - if (local_apic_timer_disabled) { + if (disable_apic_timer) { + printk(KERN_INFO "Disabling APIC timer\n"); /* No broadcast on UP ! */ if (num_possible_cpus() > 1) { lapic_clockevent.mult = 1; @@ -602,7 +687,11 @@ static void local_apic_timer_interrupt(void) /* * the NMI deadlock-detector uses this. */ +#ifdef CONFIG_X86_64 + add_pda(apic_timer_irqs, 1); +#else per_cpu(irq_stat, cpu).apic_timer_irqs++; +#endif evt->event_handler(evt); } @@ -642,35 +731,6 @@ int setup_profiling_timer(unsigned int multiplier) } /* - * Setup extended LVT, AMD specific (K8, family 10h) - * - * Vector mappings are hard coded. On K8 only offset 0 (APIC500) and - * MCE interrupts are supported. Thus MCE offset must be set to 0. - */ - -#define APIC_EILVT_LVTOFF_MCE 0 -#define APIC_EILVT_LVTOFF_IBS 1 - -static void setup_APIC_eilvt(u8 lvt_off, u8 vector, u8 msg_type, u8 mask) -{ - unsigned long reg = (lvt_off << 4) + APIC_EILVT0; - unsigned int v = (mask << 16) | (msg_type << 8) | vector; - apic_write(reg, v); -} - -u8 setup_APIC_eilvt_mce(u8 vector, u8 msg_type, u8 mask) -{ - setup_APIC_eilvt(APIC_EILVT_LVTOFF_MCE, vector, msg_type, mask); - return APIC_EILVT_LVTOFF_MCE; -} - -u8 setup_APIC_eilvt_ibs(u8 vector, u8 msg_type, u8 mask) -{ - setup_APIC_eilvt(APIC_EILVT_LVTOFF_IBS, vector, msg_type, mask); - return APIC_EILVT_LVTOFF_IBS; -} - -/* * Local APIC start and shutdown */ @@ -715,7 +775,7 @@ void clear_local_APIC(void) } /* lets not touch this if we didn't frob it */ -#ifdef CONFIG_X86_MCE_P4THERMAL +#if defined(CONFIG_X86_MCE_P4THERMAL) || defined(X86_MCE_INTEL) if (maxlvt >= 5) { v = apic_read(APIC_LVTTHMR); apic_write(APIC_LVTTHMR, v | APIC_LVT_MASKED); @@ -732,10 +792,6 @@ void clear_local_APIC(void) if (maxlvt >= 4) apic_write(APIC_LVTPC, APIC_LVT_MASKED); -#ifdef CONFIG_X86_MCE_P4THERMAL - if (maxlvt >= 5) - apic_write(APIC_LVTTHMR, APIC_LVT_MASKED); -#endif /* Integrated APIC (!82489DX) ? */ if (lapic_is_integrated()) { if (maxlvt > 3) @@ -750,7 +806,7 @@ void clear_local_APIC(void) */ void disable_local_APIC(void) { - unsigned long value; + unsigned int value; clear_local_APIC(); @@ -762,6 +818,7 @@ void disable_local_APIC(void) value &= ~APIC_SPIV_APIC_ENABLED; apic_write(APIC_SPIV, value); +#ifdef CONFIG_X86_32 /* * When LAPIC was disabled by the BIOS and enabled by the kernel, * restore the disabled state. @@ -773,6 +830,7 @@ void disable_local_APIC(void) l &= ~MSR_IA32_APICBASE_ENABLE; wrmsr(MSR_IA32_APICBASE, l, h); } +#endif } /* @@ -789,11 +847,15 @@ void lapic_shutdown(void) return; local_irq_save(flags); - clear_local_APIC(); - if (enabled_via_apicbase) +#ifdef CONFIG_X86_32 + if (!enabled_via_apicbase) + clear_local_APIC(); + else +#endif disable_local_APIC(); + local_irq_restore(flags); } @@ -838,6 +900,12 @@ int __init verify_local_APIC(void) */ reg0 = apic_read(APIC_ID); apic_printk(APIC_DEBUG, "Getting ID: %x\n", reg0); + apic_write(APIC_ID, reg0 ^ APIC_ID_MASK); + reg1 = apic_read(APIC_ID); + apic_printk(APIC_DEBUG, "Getting ID: %x\n", reg1); + apic_write(APIC_ID, reg0); + if (reg1 != (reg0 ^ APIC_ID_MASK)) + return 0; /* * The next two are just to see if we have sane values. @@ -863,14 +931,15 @@ void __init sync_Arb_IDs(void) */ if (modern_apic() || boot_cpu_data.x86_vendor == X86_VENDOR_AMD) return; + /* * Wait for idle. */ apic_wait_icr_idle(); apic_printk(APIC_DEBUG, "Synchronizing Arb IDs.\n"); - apic_write(APIC_ICR, - APIC_DEST_ALLINC | APIC_INT_LEVELTRIG | APIC_DM_INIT); + apic_write(APIC_ICR, APIC_DEST_ALLINC | + APIC_INT_LEVELTRIG | APIC_DM_INIT); } /* @@ -878,7 +947,7 @@ void __init sync_Arb_IDs(void) */ void __init init_bsp_APIC(void) { - unsigned long value; + unsigned int value; /* * Don't do the setup now if we have a SMP BIOS as the @@ -899,11 +968,13 @@ void __init init_bsp_APIC(void) value &= ~APIC_VECTOR_MASK; value |= APIC_SPIV_APIC_ENABLED; +#ifdef CONFIG_X86_32 /* This bit is reserved on P4/Xeon and should be cleared */ if ((boot_cpu_data.x86_vendor == X86_VENDOR_INTEL) && (boot_cpu_data.x86 == 15)) value &= ~APIC_SPIV_FOCUS_DISABLED; else +#endif value |= APIC_SPIV_FOCUS_DISABLED; value |= SPURIOUS_APIC_VECTOR; apic_write(APIC_SPIV, value); @@ -922,6 +993,16 @@ static void __cpuinit lapic_setup_esr(void) { unsigned long oldvalue, value, maxlvt; if (lapic_is_integrated() && !esr_disable) { + if (esr_disable) { + /* + * Something untraceable is creating bad interrupts on + * secondary quads ... for the moment, just leave the + * ESR disabled - we can't do anything useful with the + * errors anyway - mbligh + */ + printk(KERN_INFO "Leaving ESR disabled.\n"); + return; + } /* !82489DX */ maxlvt = lapic_get_maxlvt(); if (maxlvt > 3) /* Due to the Pentium erratum 3AP. */ @@ -942,16 +1023,7 @@ static void __cpuinit lapic_setup_esr(void) "vector: 0x%08lx after: 0x%08lx\n", oldvalue, value); } else { - if (esr_disable) - /* - * Something untraceable is creating bad interrupts on - * secondary quads ... for the moment, just leave the - * ESR disabled - we can't do anything useful with the - * errors anyway - mbligh - */ - printk(KERN_INFO "Leaving ESR disabled.\n"); - else - printk(KERN_INFO "No ESR for 82489DX.\n"); + printk(KERN_INFO "No ESR for 82489DX.\n"); } } @@ -1089,13 +1161,17 @@ void __cpuinit setup_local_APIC(void) void __cpuinit end_local_APIC_setup(void) { - unsigned long value; - lapic_setup_esr(); - /* Disable the local apic timer */ - value = apic_read(APIC_LVTT); - value |= (APIC_LVT_MASKED | LOCAL_TIMER_VECTOR); - apic_write(APIC_LVTT, value); + +#ifdef CONFIG_X86_32 + { + unsigned int value; + /* Disable the local apic timer */ + value = apic_read(APIC_LVTT); + value |= (APIC_LVT_MASKED | LOCAL_TIMER_VECTOR); + apic_write(APIC_LVTT, value); + } +#endif setup_apic_nmi_watchdog(NULL); apic_pm_activate(); @@ -1205,7 +1281,7 @@ void __init init_apic_mappings(void) * default configuration (or the MP table is broken). */ if (boot_cpu_physical_apicid == -1U) - boot_cpu_physical_apicid = GET_APIC_ID(read_apic_id()); + boot_cpu_physical_apicid = read_apic_id(); } @@ -1242,7 +1318,7 @@ int __init APIC_init_uniprocessor(void) * might be zero if read from MP tables. Get it from LAPIC. */ #ifdef CONFIG_CRASH_DUMP - boot_cpu_physical_apicid = GET_APIC_ID(read_apic_id()); + boot_cpu_physical_apicid = read_apic_id(); #endif physid_set_mask_of_physid(boot_cpu_physical_apicid, &phys_cpu_present_map); @@ -1321,59 +1397,12 @@ void smp_error_interrupt(struct pt_regs *regs) irq_exit(); } -#ifdef CONFIG_SMP -void __init smp_intr_init(void) -{ - /* - * IRQ0 must be given a fixed assignment and initialized, - * because it's used before the IO-APIC is set up. - */ - set_intr_gate(FIRST_DEVICE_VECTOR, interrupt[0]); - - /* - * The reschedule interrupt is a CPU-to-CPU reschedule-helper - * IPI, driven by wakeup. - */ - alloc_intr_gate(RESCHEDULE_VECTOR, reschedule_interrupt); - - /* IPI for invalidation */ - alloc_intr_gate(INVALIDATE_TLB_VECTOR, invalidate_interrupt); - - /* IPI for generic function call */ - alloc_intr_gate(CALL_FUNCTION_VECTOR, call_function_interrupt); - - /* IPI for single call function */ - set_intr_gate(CALL_FUNCTION_SINGLE_VECTOR, - call_function_single_interrupt); -} -#endif - -/* - * Initialize APIC interrupts - */ -void __init apic_intr_init(void) -{ -#ifdef CONFIG_SMP - smp_intr_init(); -#endif - /* self generated IPI for local APIC timer */ - alloc_intr_gate(LOCAL_TIMER_VECTOR, apic_timer_interrupt); - - /* IPI vectors for APIC spurious and error interrupts */ - alloc_intr_gate(SPURIOUS_APIC_VECTOR, spurious_interrupt); - alloc_intr_gate(ERROR_APIC_VECTOR, error_interrupt); - - /* thermal monitor LVT interrupt */ -#ifdef CONFIG_X86_MCE_P4THERMAL - alloc_intr_gate(THERMAL_APIC_VECTOR, thermal_interrupt); -#endif -} - /** * connect_bsp_APIC - attach the APIC to the interrupt system */ void __init connect_bsp_APIC(void) { +#ifdef CONFIG_X86_32 if (pic_mode) { /* * Do not trust the local APIC being empty at bootup. @@ -1388,6 +1417,7 @@ void __init connect_bsp_APIC(void) outb(0x70, 0x22); outb(0x01, 0x23); } +#endif enable_apic_mode(); } @@ -1400,6 +1430,9 @@ void __init connect_bsp_APIC(void) */ void disconnect_bsp_APIC(int virt_wire_setup) { + unsigned int value; + +#ifdef CONFIG_X86_32 if (pic_mode) { /* * Put the board back into PIC mode (has an effect only on @@ -1411,54 +1444,53 @@ void disconnect_bsp_APIC(int virt_wire_setup) "entering PIC mode.\n"); outb(0x70, 0x22); outb(0x00, 0x23); - } else { - /* Go back to Virtual Wire compatibility mode */ - unsigned long value; + return; + } +#endif - /* For the spurious interrupt use vector F, and enable it */ - value = apic_read(APIC_SPIV); - value &= ~APIC_VECTOR_MASK; - value |= APIC_SPIV_APIC_ENABLED; - value |= 0xf; - apic_write(APIC_SPIV, value); + /* Go back to Virtual Wire compatibility mode */ - if (!virt_wire_setup) { - /* - * For LVT0 make it edge triggered, active high, - * external and enabled - */ - value = apic_read(APIC_LVT0); - value &= ~(APIC_MODE_MASK | APIC_SEND_PENDING | - APIC_INPUT_POLARITY | APIC_LVT_REMOTE_IRR | - APIC_LVT_LEVEL_TRIGGER | APIC_LVT_MASKED); - value |= APIC_LVT_REMOTE_IRR | APIC_SEND_PENDING; - value = SET_APIC_DELIVERY_MODE(value, APIC_MODE_EXTINT); - apic_write(APIC_LVT0, value); - } else { - /* Disable LVT0 */ - apic_write(APIC_LVT0, APIC_LVT_MASKED); - } + /* For the spurious interrupt use vector F, and enable it */ + value = apic_read(APIC_SPIV); + value &= ~APIC_VECTOR_MASK; + value |= APIC_SPIV_APIC_ENABLED; + value |= 0xf; + apic_write(APIC_SPIV, value); + if (!virt_wire_setup) { /* - * For LVT1 make it edge triggered, active high, nmi and - * enabled + * For LVT0 make it edge triggered, active high, + * external and enabled */ - value = apic_read(APIC_LVT1); - value &= ~( - APIC_MODE_MASK | APIC_SEND_PENDING | + value = apic_read(APIC_LVT0); + value &= ~(APIC_MODE_MASK | APIC_SEND_PENDING | APIC_INPUT_POLARITY | APIC_LVT_REMOTE_IRR | APIC_LVT_LEVEL_TRIGGER | APIC_LVT_MASKED); value |= APIC_LVT_REMOTE_IRR | APIC_SEND_PENDING; - value = SET_APIC_DELIVERY_MODE(value, APIC_MODE_NMI); - apic_write(APIC_LVT1, value); + value = SET_APIC_DELIVERY_MODE(value, APIC_MODE_EXTINT); + apic_write(APIC_LVT0, value); + } else { + /* Disable LVT0 */ + apic_write(APIC_LVT0, APIC_LVT_MASKED); } + + /* + * For LVT1 make it edge triggered, active high, + * nmi and enabled + */ + value = apic_read(APIC_LVT1); + value &= ~(APIC_MODE_MASK | APIC_SEND_PENDING | + APIC_INPUT_POLARITY | APIC_LVT_REMOTE_IRR | + APIC_LVT_LEVEL_TRIGGER | APIC_LVT_MASKED); + value |= APIC_LVT_REMOTE_IRR | APIC_SEND_PENDING; + value = SET_APIC_DELIVERY_MODE(value, APIC_MODE_NMI); + apic_write(APIC_LVT1, value); } void __cpuinit generic_processor_info(int apicid, int version) { int cpu; cpumask_t tmp_map; - physid_mask_t phys_cpu; /* * Validate version @@ -1471,9 +1503,6 @@ void __cpuinit generic_processor_info(int apicid, int version) } apic_version[apicid] = version; - phys_cpu = apicid_to_cpu_present(apicid); - physids_or(phys_cpu_present_map, phys_cpu_present_map, phys_cpu); - if (num_processors >= NR_CPUS) { printk(KERN_WARNING "WARNING: NR_CPUS limit of %i reached." " Processor ignored.\n", NR_CPUS); @@ -1484,17 +1513,19 @@ void __cpuinit generic_processor_info(int apicid, int version) cpus_complement(tmp_map, cpu_present_map); cpu = first_cpu(tmp_map); - if (apicid == boot_cpu_physical_apicid) + physid_set(apicid, phys_cpu_present_map); + if (apicid == boot_cpu_physical_apicid) { /* * x86_bios_cpu_apicid is required to have processors listed * in same order as logical cpu numbers. Hence the first * entry is BSP, and so on. */ cpu = 0; - + } if (apicid > max_physical_apicid) max_physical_apicid = apicid; +#ifdef CONFIG_X86_32 /* * Would be preferable to switch to bigsmp when CONFIG_HOTPLUG_CPU=y * but we need to work other dependencies like SMP_SUSPEND etc @@ -1514,7 +1545,9 @@ void __cpuinit generic_processor_info(int apicid, int version) def_to_bigsmp = 1; } } -#ifdef CONFIG_SMP +#endif + +#if defined(CONFIG_X86_SMP) || defined(CONFIG_X86_64) /* are we being called early in kernel startup? */ if (early_per_cpu_ptr(x86_cpu_to_apicid)) { u16 *cpu_to_apicid = early_per_cpu_ptr(x86_cpu_to_apicid); @@ -1527,6 +1560,7 @@ void __cpuinit generic_processor_info(int apicid, int version) per_cpu(x86_bios_cpu_apicid, cpu) = apicid; } #endif + cpu_set(cpu, cpu_possible_map); cpu_set(cpu, cpu_present_map); } @@ -1537,6 +1571,11 @@ void __cpuinit generic_processor_info(int apicid, int version) #ifdef CONFIG_PM static struct { + /* + * 'active' is true if the local APIC was enabled by us and + * not the BIOS; this signifies that we are also responsible + * for disabling it before entering apm/acpi suspend + */ int active; /* r/w apic fields */ unsigned int apic_id; @@ -1577,7 +1616,7 @@ static int lapic_suspend(struct sys_device *dev, pm_message_t state) apic_pm_state.apic_lvterr = apic_read(APIC_LVTERR); apic_pm_state.apic_tmict = apic_read(APIC_TMICT); apic_pm_state.apic_tdcr = apic_read(APIC_TDCR); -#ifdef CONFIG_X86_MCE_P4THERMAL +#if defined(CONFIG_X86_MCE_P4THERMAL) || defined(CONFIG_X86_MCE_INTEL) if (maxlvt >= 5) apic_pm_state.apic_thmr = apic_read(APIC_LVTTHMR); #endif @@ -1601,16 +1640,23 @@ static int lapic_resume(struct sys_device *dev) local_irq_save(flags); - /* - * Make sure the APICBASE points to the right address - * - * FIXME! This will be wrong if we ever support suspend on - * SMP! We'll need to do this as part of the CPU restore! - */ - rdmsr(MSR_IA32_APICBASE, l, h); - l &= ~MSR_IA32_APICBASE_BASE; - l |= MSR_IA32_APICBASE_ENABLE | mp_lapic_addr; - wrmsr(MSR_IA32_APICBASE, l, h); +#ifdef CONFIG_X86_64 + if (x2apic) + enable_x2apic(); + else +#endif + { + /* + * Make sure the APICBASE points to the right address + * + * FIXME! This will be wrong if we ever support suspend on + * SMP! We'll need to do this as part of the CPU restore! + */ + rdmsr(MSR_IA32_APICBASE, l, h); + l &= ~MSR_IA32_APICBASE_BASE; + l |= MSR_IA32_APICBASE_ENABLE | mp_lapic_addr; + wrmsr(MSR_IA32_APICBASE, l, h); + } apic_write(APIC_LVTERR, ERROR_APIC_VECTOR | APIC_LVT_MASKED); apic_write(APIC_ID, apic_pm_state.apic_id); @@ -1620,7 +1666,7 @@ static int lapic_resume(struct sys_device *dev) apic_write(APIC_SPIV, apic_pm_state.apic_spiv); apic_write(APIC_LVT0, apic_pm_state.apic_lvt0); apic_write(APIC_LVT1, apic_pm_state.apic_lvt1); -#ifdef CONFIG_X86_MCE_P4THERMAL +#if defined(CONFIG_X86_MCE_P4THERMAL) || defined(CONFIG_X86_MCE_INTEL) if (maxlvt >= 5) apic_write(APIC_LVTTHMR, apic_pm_state.apic_thmr); #endif @@ -1634,7 +1680,9 @@ static int lapic_resume(struct sys_device *dev) apic_write(APIC_LVTERR, apic_pm_state.apic_lvterr); apic_write(APIC_ESR, 0); apic_read(APIC_ESR); + local_irq_restore(flags); + return 0; } @@ -1690,20 +1738,20 @@ static int __init parse_lapic(char *arg) } early_param("lapic", parse_lapic); -static int __init parse_nolapic(char *arg) +static int __init setup_disableapic(char *arg) { disable_apic = 1; setup_clear_cpu_cap(X86_FEATURE_APIC); return 0; } -early_param("nolapic", parse_nolapic); +early_param("disableapic", setup_disableapic); -static int __init parse_disable_lapic_timer(char *arg) +/* same as disableapic, for compatibility */ +static int __init setup_nolapic(char *arg) { - local_apic_timer_disabled = 1; - return 0; + return setup_disableapic(arg); } -early_param("nolapic_timer", parse_disable_lapic_timer); +early_param("nolapic", setup_nolapic); static int __init parse_lapic_timer_c2_ok(char *arg) { @@ -1712,15 +1760,40 @@ static int __init parse_lapic_timer_c2_ok(char *arg) } early_param("lapic_timer_c2_ok", parse_lapic_timer_c2_ok); +static int __init parse_disable_apic_timer(char *arg) +{ + disable_apic_timer = 1; + return 0; +} +early_param("noapictimer", parse_disable_apic_timer); + +static int __init parse_nolapic_timer(char *arg) +{ + disable_apic_timer = 1; + return 0; +} +early_param("nolapic_timer", parse_nolapic_timer); + static int __init apic_set_verbosity(char *arg) { - if (!arg) + if (!arg) { +#ifdef CONFIG_X86_64 + skip_ioapic_setup = 0; + ioapic_force = 1; + return 0; +#endif return -EINVAL; + } - if (strcmp(arg, "debug") == 0) + if (strcmp("debug", arg) == 0) apic_verbosity = APIC_DEBUG; - else if (strcmp(arg, "verbose") == 0) + else if (strcmp("verbose", arg) == 0) apic_verbosity = APIC_VERBOSE; + else { + printk(KERN_WARNING "APIC Verbosity level %s not recognised" + " use apic=verbose or apic=debug\n", arg); + return -EINVAL; + } return 0; } diff --git a/arch/x86/kernel/apic_64.c b/arch/x86/kernel/apic_64.c index 446c062e831..94ddb69ae15 100644 --- a/arch/x86/kernel/apic_64.c +++ b/arch/x86/kernel/apic_64.c @@ -27,6 +27,7 @@ #include <linux/clockchips.h> #include <linux/acpi_pmtmr.h> #include <linux/module.h> +#include <linux/dmar.h> #include <asm/atomic.h> #include <asm/smp.h> @@ -39,13 +40,20 @@ #include <asm/proto.h> #include <asm/timex.h> #include <asm/apic.h> +#include <asm/i8259.h> #include <mach_ipi.h> #include <mach_apic.h> +/* Disable local APIC timer from the kernel commandline or via dmi quirk */ static int disable_apic_timer __cpuinitdata; static int apic_calibrate_pmtmr __initdata; int disable_apic; +int disable_x2apic; +int x2apic; + +/* x2apic enabled before OS handover */ +int x2apic_preenabled; /* Local APIC timer works in C2 */ int local_apic_timer_c2_ok; @@ -73,6 +81,9 @@ static void lapic_timer_setup(enum clock_event_mode mode, static void lapic_timer_broadcast(cpumask_t mask); static void apic_pm_activate(void); +/* + * The local apic timer can be used for any function which is CPU local. + */ static struct clock_event_device lapic_clockevent = { .name = "lapic", .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT @@ -99,11 +110,15 @@ static inline int lapic_get_version(void) } /* - * Check, if the APIC is integrated or a seperate chip + * Check, if the APIC is integrated or a separate chip */ static inline int lapic_is_integrated(void) { +#ifdef CONFIG_X86_64 return 1; +#else + return APIC_INTEGRATED(lapic_get_version()); +#endif } /* @@ -118,13 +133,18 @@ static int modern_apic(void) return lapic_get_version() >= 0x14; } -void apic_wait_icr_idle(void) +/* + * Paravirt kernels also might be using these below ops. So we still + * use generic apic_read()/apic_write(), which might be pointing to different + * ops in PARAVIRT case. + */ +void xapic_wait_icr_idle(void) { while (apic_read(APIC_ICR) & APIC_ICR_BUSY) cpu_relax(); } -u32 safe_apic_wait_icr_idle(void) +u32 safe_xapic_wait_icr_idle(void) { u32 send_status; int timeout; @@ -140,6 +160,68 @@ u32 safe_apic_wait_icr_idle(void) return send_status; } +void xapic_icr_write(u32 low, u32 id) +{ + apic_write(APIC_ICR2, SET_APIC_DEST_FIELD(id)); + apic_write(APIC_ICR, low); +} + +u64 xapic_icr_read(void) +{ + u32 icr1, icr2; + + icr2 = apic_read(APIC_ICR2); + icr1 = apic_read(APIC_ICR); + + return icr1 | ((u64)icr2 << 32); +} + +static struct apic_ops xapic_ops = { + .read = native_apic_mem_read, + .write = native_apic_mem_write, + .icr_read = xapic_icr_read, + .icr_write = xapic_icr_write, + .wait_icr_idle = xapic_wait_icr_idle, + .safe_wait_icr_idle = safe_xapic_wait_icr_idle, +}; + +struct apic_ops __read_mostly *apic_ops = &xapic_ops; +EXPORT_SYMBOL_GPL(apic_ops); + +static void x2apic_wait_icr_idle(void) +{ + /* no need to wait for icr idle in x2apic */ + return; +} + +static u32 safe_x2apic_wait_icr_idle(void) +{ + /* no need to wait for icr idle in x2apic */ + return 0; +} + +void x2apic_icr_write(u32 low, u32 id) +{ + wrmsrl(APIC_BASE_MSR + (APIC_ICR >> 4), ((__u64) id) << 32 | low); +} + +u64 x2apic_icr_read(void) +{ + unsigned long val; + + rdmsrl(APIC_BASE_MSR + (APIC_ICR >> 4), val); + return val; +} + +static struct apic_ops x2apic_ops = { + .read = native_apic_msr_read, + .write = native_apic_msr_write, + .icr_read = x2apic_icr_read, + .icr_write = x2apic_icr_write, + .wait_icr_idle = x2apic_wait_icr_idle, + .safe_wait_icr_idle = safe_x2apic_wait_icr_idle, +}; + /** * enable_NMI_through_LVT0 - enable NMI through local vector table 0 */ @@ -149,6 +231,11 @@ void __cpuinit enable_NMI_through_LVT0(void) /* unmask and set to NMI */ v = APIC_DM_NMI; + + /* Level triggered for 82489DX (32bit mode) */ + if (!lapic_is_integrated()) + v |= APIC_LVT_LEVEL_TRIGGER; + apic_write(APIC_LVT0, v); } @@ -157,14 +244,28 @@ void __cpuinit enable_NMI_through_LVT0(void) */ int lapic_get_maxlvt(void) { - unsigned int v, maxlvt; + unsigned int v; v = apic_read(APIC_LVR); - maxlvt = GET_APIC_MAXLVT(v); - return maxlvt; + /* + * - we always have APIC integrated on 64bit mode + * - 82489DXs do not report # of LVT entries + */ + return APIC_INTEGRATED(GET_APIC_VERSION(v)) ? GET_APIC_MAXLVT(v) : 2; } /* + * Local APIC timer + */ + +/* Clock divisor */ +#ifdef CONFG_X86_64 +#define APIC_DIVISOR 1 +#else +#define APIC_DIVISOR 16 +#endif + +/* * This function sets up the local APIC timer, with a timeout of * 'clocks' APIC bus clock. During calibration we actually call * this function twice on the boot CPU, once with a bogus timeout @@ -174,7 +275,6 @@ int lapic_get_maxlvt(void) * We do reads before writes even if unnecessary, to get around the * P5 APIC double write bug. */ - static void __setup_APIC_LVTT(unsigned int clocks, int oneshot, int irqen) { unsigned int lvtt_value, tmp_value; @@ -182,6 +282,9 @@ static void __setup_APIC_LVTT(unsigned int clocks, int oneshot, int irqen) lvtt_value = LOCAL_TIMER_VECTOR; if (!oneshot) lvtt_value |= APIC_LVT_TIMER_PERIODIC; + if (!lapic_is_integrated()) + lvtt_value |= SET_APIC_TIMER_BASE(APIC_TIMER_BASE_DIV); + if (!irqen) lvtt_value |= APIC_LVT_MASKED; @@ -191,12 +294,12 @@ static void __setup_APIC_LVTT(unsigned int clocks, int oneshot, int irqen) * Divide PICLK by 16 */ tmp_value = apic_read(APIC_TDCR); - apic_write(APIC_TDCR, (tmp_value - & ~(APIC_TDR_DIV_1 | APIC_TDR_DIV_TMBASE)) - | APIC_TDR_DIV_16); + apic_write(APIC_TDCR, + (tmp_value & ~(APIC_TDR_DIV_1 | APIC_TDR_DIV_TMBASE)) | + APIC_TDR_DIV_16); if (!oneshot) - apic_write(APIC_TMICT, clocks); + apic_write(APIC_TMICT, clocks / APIC_DIVISOR); } /* @@ -204,6 +307,9 @@ static void __setup_APIC_LVTT(unsigned int clocks, int oneshot, int irqen) * * Vector mappings are hard coded. On K8 only offset 0 (APIC500) and * MCE interrupts are supported. Thus MCE offset must be set to 0. + * + * If mask=1, the LVT entry does not generate interrupts while mask=0 + * enables the vector. See also the BKDGs. */ #define APIC_EILVT_LVTOFF_MCE 0 @@ -228,6 +334,7 @@ u8 setup_APIC_eilvt_ibs(u8 vector, u8 msg_type, u8 mask) setup_APIC_eilvt(APIC_EILVT_LVTOFF_IBS, vector, msg_type, mask); return APIC_EILVT_LVTOFF_IBS; } +EXPORT_SYMBOL_GPL(setup_APIC_eilvt_ibs); /* * Program the next event, relative to now @@ -366,7 +473,7 @@ static int __init calibrate_APIC_clock(void) lapic_clockevent.min_delta_ns = clockevent_delta2ns(0xF, &lapic_clockevent); - calibration_result = result / HZ; + calibration_result = (result * APIC_DIVISOR) / HZ; /* * Do a sanity check on the APIC calibration result @@ -388,10 +495,10 @@ static int __init calibrate_APIC_clock(void) void __init setup_boot_APIC_clock(void) { /* - * The local apic timer can be disabled via the kernel commandline. - * Register the lapic timer as a dummy clock event source on SMP - * systems, so the broadcast mechanism is used. On UP systems simply - * ignore it. + * The local apic timer can be disabled via the kernel + * commandline or from the CPU detection code. Register the lapic + * timer as a dummy clock event source on SMP systems, so the + * broadcast mechanism is used. On UP systems simply ignore it. */ if (disable_apic_timer) { printk(KERN_INFO "Disabling APIC timer\n"); @@ -403,7 +510,9 @@ void __init setup_boot_APIC_clock(void) return; } - printk(KERN_INFO "Using local APIC timer interrupts.\n"); + apic_printk(APIC_VERBOSE, "Using local APIC timer interrupts.\n" + "calibrating APIC timer ...\n"); + if (calibrate_APIC_clock()) { /* No broadcast on UP ! */ if (num_possible_cpus() > 1) @@ -422,6 +531,7 @@ void __init setup_boot_APIC_clock(void) printk(KERN_WARNING "APIC timer registered as dummy," " due to nmi_watchdog=%d!\n", nmi_watchdog); + /* Setup the lapic or request the broadcast */ setup_APIC_timer(); } @@ -460,7 +570,11 @@ static void local_apic_timer_interrupt(void) /* * the NMI deadlock-detector uses this. */ +#ifdef CONFIG_X86_64 add_pda(apic_timer_irqs, 1); +#else + per_cpu(irq_stat, cpu).apic_timer_irqs++; +#endif evt->event_handler(evt); } @@ -491,6 +605,7 @@ void smp_apic_timer_interrupt(struct pt_regs *regs) irq_enter(); local_apic_timer_interrupt(); irq_exit(); + set_irq_regs(old_regs); } @@ -544,6 +659,13 @@ void clear_local_APIC(void) apic_write(APIC_LVTPC, v | APIC_LVT_MASKED); } + /* lets not touch this if we didn't frob it */ +#if defined(CONFIG_X86_MCE_P4THERMAL) || defined(X86_MCE_INTEL) + if (maxlvt >= 5) { + v = apic_read(APIC_LVTTHMR); + apic_write(APIC_LVTTHMR, v | APIC_LVT_MASKED); + } +#endif /* * Clean APIC state for other OSs: */ @@ -554,8 +676,14 @@ void clear_local_APIC(void) apic_write(APIC_LVTERR, APIC_LVT_MASKED); if (maxlvt >= 4) apic_write(APIC_LVTPC, APIC_LVT_MASKED); - apic_write(APIC_ESR, 0); - apic_read(APIC_ESR); + + /* Integrated APIC (!82489DX) ? */ + if (lapic_is_integrated()) { + if (maxlvt > 3) + /* Clear ESR due to Pentium errata 3AP and 11AP */ + apic_write(APIC_ESR, 0); + apic_read(APIC_ESR); + } } /** @@ -574,8 +702,28 @@ void disable_local_APIC(void) value = apic_read(APIC_SPIV); value &= ~APIC_SPIV_APIC_ENABLED; apic_write(APIC_SPIV, value); + +#ifdef CONFIG_X86_32 + /* + * When LAPIC was disabled by the BIOS and enabled by the kernel, + * restore the disabled state. + */ + if (enabled_via_apicbase) { + unsigned int l, h; + + rdmsr(MSR_IA32_APICBASE, l, h); + l &= ~MSR_IA32_APICBASE_ENABLE; + wrmsr(MSR_IA32_APICBASE, l, h); + } +#endif } +/* + * If Linux enabled the LAPIC against the BIOS default disable it down before + * re-entering the BIOS on shutdown. Otherwise the BIOS may get confused and + * not power-off. Additionally clear all LVT entries before disable_local_APIC + * for the case where Linux didn't enable the LAPIC. + */ void lapic_shutdown(void) { unsigned long flags; @@ -585,7 +733,13 @@ void lapic_shutdown(void) local_irq_save(flags); - disable_local_APIC(); +#ifdef CONFIG_X86_32 + if (!enabled_via_apicbase) + clear_local_APIC(); + else +#endif + disable_local_APIC(); + local_irq_restore(flags); } @@ -629,10 +783,10 @@ int __init verify_local_APIC(void) /* * The ID register is read/write in a real APIC. */ - reg0 = read_apic_id(); + reg0 = apic_read(APIC_ID); apic_printk(APIC_DEBUG, "Getting ID: %x\n", reg0); apic_write(APIC_ID, reg0 ^ APIC_ID_MASK); - reg1 = read_apic_id(); + reg1 = apic_read(APIC_ID); apic_printk(APIC_DEBUG, "Getting ID: %x\n", reg1); apic_write(APIC_ID, reg0); if (reg1 != (reg0 ^ APIC_ID_MASK)) @@ -656,8 +810,11 @@ int __init verify_local_APIC(void) */ void __init sync_Arb_IDs(void) { - /* Unsupported on P4 - see Intel Dev. Manual Vol. 3, Ch. 8.6.1 */ - if (modern_apic()) + /* + * Unsupported on P4 - see Intel Dev. Manual Vol. 3, Ch. 8.6.1 And not + * needed on AMD. + */ + if (modern_apic() || boot_cpu_data.x86_vendor == X86_VENDOR_AMD) return; /* @@ -666,8 +823,8 @@ void __init sync_Arb_IDs(void) apic_wait_icr_idle(); apic_printk(APIC_DEBUG, "Synchronizing Arb IDs.\n"); - apic_write(APIC_ICR, APIC_DEST_ALLINC | APIC_INT_LEVELTRIG - | APIC_DM_INIT); + apic_write(APIC_ICR, APIC_DEST_ALLINC | + APIC_INT_LEVELTRIG | APIC_DM_INIT); } /* @@ -684,8 +841,6 @@ void __init init_bsp_APIC(void) if (smp_found_config || !cpu_has_apic) return; - value = apic_read(APIC_LVR); - /* * Do not trust the local APIC being empty at bootup. */ @@ -697,7 +852,15 @@ void __init init_bsp_APIC(void) value = apic_read(APIC_SPIV); value &= ~APIC_VECTOR_MASK; value |= APIC_SPIV_APIC_ENABLED; - value |= APIC_SPIV_FOCUS_DISABLED; + +#ifdef CONFIG_X86_32 + /* This bit is reserved on P4/Xeon and should be cleared */ + if ((boot_cpu_data.x86_vendor == X86_VENDOR_INTEL) && + (boot_cpu_data.x86 == 15)) + value &= ~APIC_SPIV_FOCUS_DISABLED; + else +#endif + value |= APIC_SPIV_FOCUS_DISABLED; value |= SPURIOUS_APIC_VECTOR; apic_write(APIC_SPIV, value); @@ -706,9 +869,50 @@ void __init init_bsp_APIC(void) */ apic_write(APIC_LVT0, APIC_DM_EXTINT); value = APIC_DM_NMI; + if (!lapic_is_integrated()) /* 82489DX */ + value |= APIC_LVT_LEVEL_TRIGGER; apic_write(APIC_LVT1, value); } +static void __cpuinit lapic_setup_esr(void) +{ + unsigned long oldvalue, value, maxlvt; + if (lapic_is_integrated() && !esr_disable) { + if (esr_disable) { + /* + * Something untraceable is creating bad interrupts on + * secondary quads ... for the moment, just leave the + * ESR disabled - we can't do anything useful with the + * errors anyway - mbligh + */ + printk(KERN_INFO "Leaving ESR disabled.\n"); + return; + } + /* !82489DX */ + maxlvt = lapic_get_maxlvt(); + if (maxlvt > 3) /* Due to the Pentium erratum 3AP. */ + apic_write(APIC_ESR, 0); + oldvalue = apic_read(APIC_ESR); + + /* enables sending errors */ + value = ERROR_APIC_VECTOR; + apic_write(APIC_LVTERR, value); + /* + * spec says clear errors after enabling vector. + */ + if (maxlvt > 3) + apic_write(APIC_ESR, 0); + value = apic_read(APIC_ESR); + if (value != oldvalue) + apic_printk(APIC_VERBOSE, "ESR value before enabling " + "vector: 0x%08lx after: 0x%08lx\n", + oldvalue, value); + } else { + printk(KERN_INFO "No ESR for 82489DX.\n"); + } +} + + /** * setup_local_APIC - setup the local APIC */ @@ -814,25 +1018,143 @@ void __cpuinit setup_local_APIC(void) preempt_enable(); } -static void __cpuinit lapic_setup_esr(void) -{ - unsigned maxlvt = lapic_get_maxlvt(); - - apic_write(APIC_LVTERR, ERROR_APIC_VECTOR); - /* - * spec says clear errors after enabling vector. - */ - if (maxlvt > 3) - apic_write(APIC_ESR, 0); -} - void __cpuinit end_local_APIC_setup(void) { lapic_setup_esr(); + +#ifdef CONFIG_X86_32 + { + unsigned int value; + /* Disable the local apic timer */ + value = apic_read(APIC_LVTT); + value |= (APIC_LVT_MASKED | LOCAL_TIMER_VECTOR); + apic_write(APIC_LVTT, value); + } +#endif + setup_apic_nmi_watchdog(NULL); apic_pm_activate(); } +void check_x2apic(void) +{ + int msr, msr2; + + rdmsr(MSR_IA32_APICBASE, msr, msr2); + + if (msr & X2APIC_ENABLE) { + printk("x2apic enabled by BIOS, switching to x2apic ops\n"); + x2apic_preenabled = x2apic = 1; + apic_ops = &x2apic_ops; + } +} + +void enable_x2apic(void) +{ + int msr, msr2; + + rdmsr(MSR_IA32_APICBASE, msr, msr2); + if (!(msr & X2APIC_ENABLE)) { + printk("Enabling x2apic\n"); + wrmsr(MSR_IA32_APICBASE, msr | X2APIC_ENABLE, 0); + } +} + +void enable_IR_x2apic(void) +{ +#ifdef CONFIG_INTR_REMAP + int ret; + unsigned long flags; + + if (!cpu_has_x2apic) + return; + + if (!x2apic_preenabled && disable_x2apic) { + printk(KERN_INFO + "Skipped enabling x2apic and Interrupt-remapping " + "because of nox2apic\n"); + return; + } + + if (x2apic_preenabled && disable_x2apic) + panic("Bios already enabled x2apic, can't enforce nox2apic"); + + if (!x2apic_preenabled && skip_ioapic_setup) { + printk(KERN_INFO + "Skipped enabling x2apic and Interrupt-remapping " + "because of skipping io-apic setup\n"); + return; + } + + ret = dmar_table_init(); + if (ret) { + printk(KERN_INFO + "dmar_table_init() failed with %d:\n", ret); + + if (x2apic_preenabled) + panic("x2apic enabled by bios. But IR enabling failed"); + else + printk(KERN_INFO + "Not enabling x2apic,Intr-remapping\n"); + return; + } + + local_irq_save(flags); + mask_8259A(); + save_mask_IO_APIC_setup(); + + ret = enable_intr_remapping(1); + + if (ret && x2apic_preenabled) { + local_irq_restore(flags); + panic("x2apic enabled by bios. But IR enabling failed"); + } + + if (ret) + goto end; + + if (!x2apic) { + x2apic = 1; + apic_ops = &x2apic_ops; + enable_x2apic(); + } +end: + if (ret) + /* + * IR enabling failed + */ + restore_IO_APIC_setup(); + else + reinit_intr_remapped_IO_APIC(x2apic_preenabled); + + unmask_8259A(); + local_irq_restore(flags); + + if (!ret) { + if (!x2apic_preenabled) + printk(KERN_INFO + "Enabled x2apic and interrupt-remapping\n"); + else + printk(KERN_INFO + "Enabled Interrupt-remapping\n"); + } else + printk(KERN_ERR + "Failed to enable Interrupt-remapping and x2apic\n"); +#else + if (!cpu_has_x2apic) + return; + + if (x2apic_preenabled) + panic("x2apic enabled prior OS handover," + " enable CONFIG_INTR_REMAP"); + + printk(KERN_INFO "Enable CONFIG_INTR_REMAP for enabling intr-remapping " + " and x2apic\n"); +#endif + + return; +} + /* * Detect and enable local APICs on non-SMP boards. * Original code written by Keir Fraser. @@ -872,7 +1194,7 @@ void __init early_init_lapic_mapping(void) * Fetch the APIC ID of the BSP in case we have a * default configuration (or the MP table is broken). */ - boot_cpu_physical_apicid = GET_APIC_ID(read_apic_id()); + boot_cpu_physical_apicid = read_apic_id(); } /** @@ -880,6 +1202,11 @@ void __init early_init_lapic_mapping(void) */ void __init init_apic_mappings(void) { + if (x2apic) { + boot_cpu_physical_apicid = read_apic_id(); + return; + } + /* * If no local APIC can be found then set up a fake all * zeroes page to simulate the local APIC and another @@ -899,13 +1226,15 @@ void __init init_apic_mappings(void) * Fetch the APIC ID of the BSP in case we have a * default configuration (or the MP table is broken). */ - boot_cpu_physical_apicid = GET_APIC_ID(read_apic_id()); + boot_cpu_physical_apicid = read_apic_id(); } /* * This initializes the IO-APIC and APIC hardware if this is * a UP kernel. */ +int apic_version[MAX_APICS]; + int __init APIC_init_uniprocessor(void) { if (disable_apic) { @@ -918,6 +1247,9 @@ int __init APIC_init_uniprocessor(void) return -1; } + enable_IR_x2apic(); + setup_apic_routing(); + verify_local_APIC(); connect_bsp_APIC(); @@ -1004,17 +1336,57 @@ asmlinkage void smp_error_interrupt(void) } /** - * * connect_bsp_APIC - attach the APIC to the interrupt system - * */ + * connect_bsp_APIC - attach the APIC to the interrupt system + */ void __init connect_bsp_APIC(void) { +#ifdef CONFIG_X86_32 + if (pic_mode) { + /* + * Do not trust the local APIC being empty at bootup. + */ + clear_local_APIC(); + /* + * PIC mode, enable APIC mode in the IMCR, i.e. connect BSP's + * local APIC to INT and NMI lines. + */ + apic_printk(APIC_VERBOSE, "leaving PIC mode, " + "enabling APIC mode.\n"); + outb(0x70, 0x22); + outb(0x01, 0x23); + } +#endif enable_apic_mode(); } +/** + * disconnect_bsp_APIC - detach the APIC from the interrupt system + * @virt_wire_setup: indicates, whether virtual wire mode is selected + * + * Virtual wire mode is necessary to deliver legacy interrupts even when the + * APIC is disabled. + */ void disconnect_bsp_APIC(int virt_wire_setup) { + unsigned int value; + +#ifdef CONFIG_X86_32 + if (pic_mode) { + /* + * Put the board back into PIC mode (has an effect only on + * certain older boards). Note that APIC interrupts, including + * IPIs, won't work beyond this point! The only exception are + * INIT IPIs. + */ + apic_printk(APIC_VERBOSE, "disabling APIC mode, " + "entering PIC mode.\n"); + outb(0x70, 0x22); + outb(0x00, 0x23); + return; + } +#endif + /* Go back to Virtual Wire compatibility mode */ - unsigned long value; /* For the spurious interrupt use vector F, and enable it */ value = apic_read(APIC_SPIV); @@ -1040,7 +1412,10 @@ void disconnect_bsp_APIC(int virt_wire_setup) apic_write(APIC_LVT0, APIC_LVT_MASKED); } - /* For LVT1 make it edge triggered, active high, nmi and enabled */ + /* + * For LVT1 make it edge triggered, active high, + * nmi and enabled + */ value = apic_read(APIC_LVT1); value &= ~(APIC_MODE_MASK | APIC_SEND_PENDING | APIC_INPUT_POLARITY | APIC_LVT_REMOTE_IRR | @@ -1055,9 +1430,20 @@ void __cpuinit generic_processor_info(int apicid, int version) int cpu; cpumask_t tmp_map; + /* + * Validate version + */ + if (version == 0x0) { + printk(KERN_WARNING "BIOS bug, APIC version is 0 for CPU#%d! " + "fixing up to 0x10. (tell your hw vendor)\n", + version); + version = 0x10; + } + apic_version[apicid] = version; + if (num_processors >= NR_CPUS) { printk(KERN_WARNING "WARNING: NR_CPUS limit of %i reached." - " Processor ignored.\n", NR_CPUS); + " Processor ignored.\n", NR_CPUS); return; } @@ -1077,6 +1463,29 @@ void __cpuinit generic_processor_info(int apicid, int version) if (apicid > max_physical_apicid) max_physical_apicid = apicid; +#ifdef CONFIG_X86_32 + /* + * Would be preferable to switch to bigsmp when CONFIG_HOTPLUG_CPU=y + * but we need to work other dependencies like SMP_SUSPEND etc + * before this can be done without some confusion. + * if (CPU_HOTPLUG_ENABLED || num_processors > 8) + * - Ashok Raj <ashok.raj@intel.com> + */ + if (max_physical_apicid >= 8) { + switch (boot_cpu_data.x86_vendor) { + case X86_VENDOR_INTEL: + if (!APIC_XAPIC(version)) { + def_to_bigsmp = 0; + break; + } + /* If P4 and above fall through */ + case X86_VENDOR_AMD: + def_to_bigsmp = 1; + } + } +#endif + +#if defined(CONFIG_X86_SMP) || defined(CONFIG_X86_64) /* are we being called early in kernel startup? */ if (early_per_cpu_ptr(x86_cpu_to_apicid)) { u16 *cpu_to_apicid = early_per_cpu_ptr(x86_cpu_to_apicid); @@ -1088,20 +1497,28 @@ void __cpuinit generic_processor_info(int apicid, int version) per_cpu(x86_cpu_to_apicid, cpu) = apicid; per_cpu(x86_bios_cpu_apicid, cpu) = apicid; } +#endif cpu_set(cpu, cpu_possible_map); cpu_set(cpu, cpu_present_map); } +int hard_smp_processor_id(void) +{ + return read_apic_id(); +} + /* * Power management */ #ifdef CONFIG_PM static struct { - /* 'active' is true if the local APIC was enabled by us and - not the BIOS; this signifies that we are also responsible - for disabling it before entering apm/acpi suspend */ + /* + * 'active' is true if the local APIC was enabled by us and + * not the BIOS; this signifies that we are also responsible + * for disabling it before entering apm/acpi suspend + */ int active; /* r/w apic fields */ unsigned int apic_id; @@ -1129,7 +1546,7 @@ static int lapic_suspend(struct sys_device *dev, pm_message_t state) maxlvt = lapic_get_maxlvt(); - apic_pm_state.apic_id = read_apic_id(); + apic_pm_state.apic_id = apic_read(APIC_ID); apic_pm_state.apic_taskpri = apic_read(APIC_TASKPRI); apic_pm_state.apic_ldr = apic_read(APIC_LDR); apic_pm_state.apic_dfr = apic_read(APIC_DFR); @@ -1142,10 +1559,11 @@ static int lapic_suspend(struct sys_device *dev, pm_message_t state) apic_pm_state.apic_lvterr = apic_read(APIC_LVTERR); apic_pm_state.apic_tmict = apic_read(APIC_TMICT); apic_pm_state.apic_tdcr = apic_read(APIC_TDCR); -#ifdef CONFIG_X86_MCE_INTEL +#if defined(CONFIG_X86_MCE_P4THERMAL) || defined(CONFIG_X86_MCE_INTEL) if (maxlvt >= 5) apic_pm_state.apic_thmr = apic_read(APIC_LVTTHMR); #endif + local_irq_save(flags); disable_local_APIC(); local_irq_restore(flags); @@ -1164,10 +1582,25 @@ static int lapic_resume(struct sys_device *dev) maxlvt = lapic_get_maxlvt(); local_irq_save(flags); - rdmsr(MSR_IA32_APICBASE, l, h); - l &= ~MSR_IA32_APICBASE_BASE; - l |= MSR_IA32_APICBASE_ENABLE | mp_lapic_addr; - wrmsr(MSR_IA32_APICBASE, l, h); + +#ifdef CONFIG_X86_64 + if (x2apic) + enable_x2apic(); + else +#endif + { + /* + * Make sure the APICBASE points to the right address + * + * FIXME! This will be wrong if we ever support suspend on + * SMP! We'll need to do this as part of the CPU restore! + */ + rdmsr(MSR_IA32_APICBASE, l, h); + l &= ~MSR_IA32_APICBASE_BASE; + l |= MSR_IA32_APICBASE_ENABLE | mp_lapic_addr; + wrmsr(MSR_IA32_APICBASE, l, h); + } + apic_write(APIC_LVTERR, ERROR_APIC_VECTOR | APIC_LVT_MASKED); apic_write(APIC_ID, apic_pm_state.apic_id); apic_write(APIC_DFR, apic_pm_state.apic_dfr); @@ -1176,7 +1609,7 @@ static int lapic_resume(struct sys_device *dev) apic_write(APIC_SPIV, apic_pm_state.apic_spiv); apic_write(APIC_LVT0, apic_pm_state.apic_lvt0); apic_write(APIC_LVT1, apic_pm_state.apic_lvt1); -#ifdef CONFIG_X86_MCE_INTEL +#if defined(CONFIG_X86_MCE_P4THERMAL) || defined(CONFIG_X86_MCE_INTEL) if (maxlvt >= 5) apic_write(APIC_LVTTHMR, apic_pm_state.apic_thmr); #endif @@ -1190,10 +1623,17 @@ static int lapic_resume(struct sys_device *dev) apic_write(APIC_LVTERR, apic_pm_state.apic_lvterr); apic_write(APIC_ESR, 0); apic_read(APIC_ESR); + local_irq_restore(flags); + return 0; } +/* + * This device has no shutdown method - fully functioning local APICs + * are needed on every CPU up until machine_halt/restart/poweroff. + */ + static struct sysdev_class lapic_sysclass = { .name = "lapic", .resume = lapic_resume, @@ -1307,31 +1747,19 @@ __cpuinit int apic_is_clustered_box(void) return (clusters > 2); } -/* - * APIC command line parameters - */ -static int __init apic_set_verbosity(char *str) +static __init int setup_nox2apic(char *str) { - if (str == NULL) { - skip_ioapic_setup = 0; - ioapic_force = 1; - return 0; - } - if (strcmp("debug", str) == 0) - apic_verbosity = APIC_DEBUG; - else if (strcmp("verbose", str) == 0) - apic_verbosity = APIC_VERBOSE; - else { - printk(KERN_WARNING "APIC Verbosity level %s not recognised" - " use apic=verbose or apic=debug\n", str); - return -EINVAL; - } - + disable_x2apic = 1; + clear_cpu_cap(&boot_cpu_data, X86_FEATURE_X2APIC); return 0; } -early_param("apic", apic_set_verbosity); +early_param("nox2apic", setup_nox2apic); + -static __init int setup_disableapic(char *str) +/* + * APIC command line parameters + */ +static int __init setup_disableapic(char *arg) { disable_apic = 1; setup_clear_cpu_cap(X86_FEATURE_APIC); @@ -1340,9 +1768,9 @@ static __init int setup_disableapic(char *str) early_param("disableapic", setup_disableapic); /* same as disableapic, for compatibility */ -static __init int setup_nolapic(char *str) +static int __init setup_nolapic(char *arg) { - return setup_disableapic(str); + return setup_disableapic(arg); } early_param("nolapic", setup_nolapic); @@ -1353,14 +1781,19 @@ static int __init parse_lapic_timer_c2_ok(char *arg) } early_param("lapic_timer_c2_ok", parse_lapic_timer_c2_ok); -static __init int setup_noapictimer(char *str) +static int __init parse_disable_apic_timer(char *arg) { - if (str[0] != ' ' && str[0] != 0) - return 0; disable_apic_timer = 1; - return 1; + return 0; } -__setup("noapictimer", setup_noapictimer); +early_param("noapictimer", parse_disable_apic_timer); + +static int __init parse_nolapic_timer(char *arg) +{ + disable_apic_timer = 1; + return 0; +} +early_param("nolapic_timer", parse_nolapic_timer); static __init int setup_apicpmtimer(char *s) { @@ -1370,6 +1803,31 @@ static __init int setup_apicpmtimer(char *s) } __setup("apicpmtimer", setup_apicpmtimer); +static int __init apic_set_verbosity(char *arg) +{ + if (!arg) { +#ifdef CONFIG_X86_64 + skip_ioapic_setup = 0; + ioapic_force = 1; + return 0; +#endif + return -EINVAL; + } + + if (strcmp("debug", arg) == 0) + apic_verbosity = APIC_DEBUG; + else if (strcmp("verbose", arg) == 0) + apic_verbosity = APIC_VERBOSE; + else { + printk(KERN_WARNING "APIC Verbosity level %s not recognised" + " use apic=verbose or apic=debug\n", arg); + return -EINVAL; + } + + return 0; +} +early_param("apic", apic_set_verbosity); + static int __init lapic_insert_resource(void) { if (!apic_phys) diff --git a/arch/x86/kernel/cpu/Makefile b/arch/x86/kernel/cpu/Makefile index ee76eaad300..7f0b45a5d78 100644 --- a/arch/x86/kernel/cpu/Makefile +++ b/arch/x86/kernel/cpu/Makefile @@ -3,22 +3,30 @@ # obj-y := intel_cacheinfo.o addon_cpuid_features.o -obj-y += proc.o feature_names.o - -obj-$(CONFIG_X86_32) += common.o bugs.o -obj-$(CONFIG_X86_64) += common_64.o bugs_64.o -obj-$(CONFIG_X86_32) += amd.o -obj-$(CONFIG_X86_64) += amd_64.o -obj-$(CONFIG_X86_32) += cyrix.o -obj-$(CONFIG_X86_32) += centaur.o -obj-$(CONFIG_X86_64) += centaur_64.o -obj-$(CONFIG_X86_32) += transmeta.o -obj-$(CONFIG_X86_32) += intel.o -obj-$(CONFIG_X86_64) += intel_64.o -obj-$(CONFIG_X86_32) += umc.o +obj-y += proc.o capflags.o powerflags.o common.o + +obj-$(CONFIG_X86_32) += bugs.o cmpxchg.o +obj-$(CONFIG_X86_64) += bugs_64.o + +obj-$(CONFIG_CPU_SUP_INTEL) += intel.o +obj-$(CONFIG_CPU_SUP_AMD) += amd.o +obj-$(CONFIG_CPU_SUP_CYRIX_32) += cyrix.o +obj-$(CONFIG_CPU_SUP_CENTAUR_32) += centaur.o +obj-$(CONFIG_CPU_SUP_CENTAUR_64) += centaur_64.o +obj-$(CONFIG_CPU_SUP_TRANSMETA_32) += transmeta.o +obj-$(CONFIG_CPU_SUP_UMC_32) += umc.o obj-$(CONFIG_X86_MCE) += mcheck/ obj-$(CONFIG_MTRR) += mtrr/ obj-$(CONFIG_CPU_FREQ) += cpufreq/ obj-$(CONFIG_X86_LOCAL_APIC) += perfctr-watchdog.o + +quiet_cmd_mkcapflags = MKCAP $@ + cmd_mkcapflags = $(PERL) $(srctree)/$(src)/mkcapflags.pl $< $@ + +cpufeature = $(src)/../../../../include/asm-x86/cpufeature.h + +targets += capflags.c +$(obj)/capflags.c: $(cpufeature) $(src)/mkcapflags.pl FORCE + $(call if_changed,mkcapflags) diff --git a/arch/x86/kernel/cpu/addon_cpuid_features.c b/arch/x86/kernel/cpu/addon_cpuid_features.c index a6ef672adbb..0d9c993aa93 100644 --- a/arch/x86/kernel/cpu/addon_cpuid_features.c +++ b/arch/x86/kernel/cpu/addon_cpuid_features.c @@ -7,6 +7,8 @@ #include <asm/pat.h> #include <asm/processor.h> +#include <mach_apic.h> + struct cpuid_bit { u16 feature; u8 reg; @@ -48,6 +50,92 @@ void __cpuinit init_scattered_cpuid_features(struct cpuinfo_x86 *c) } } +/* leaf 0xb SMT level */ +#define SMT_LEVEL 0 + +/* leaf 0xb sub-leaf types */ +#define INVALID_TYPE 0 +#define SMT_TYPE 1 +#define CORE_TYPE 2 + +#define LEAFB_SUBTYPE(ecx) (((ecx) >> 8) & 0xff) +#define BITS_SHIFT_NEXT_LEVEL(eax) ((eax) & 0x1f) +#define LEVEL_MAX_SIBLINGS(ebx) ((ebx) & 0xffff) + +/* + * Check for extended topology enumeration cpuid leaf 0xb and if it + * exists, use it for populating initial_apicid and cpu topology + * detection. + */ +void __cpuinit detect_extended_topology(struct cpuinfo_x86 *c) +{ +#ifdef CONFIG_SMP + unsigned int eax, ebx, ecx, edx, sub_index; + unsigned int ht_mask_width, core_plus_mask_width; + unsigned int core_select_mask, core_level_siblings; + + if (c->cpuid_level < 0xb) + return; + + cpuid_count(0xb, SMT_LEVEL, &eax, &ebx, &ecx, &edx); + + /* + * check if the cpuid leaf 0xb is actually implemented. + */ + if (ebx == 0 || (LEAFB_SUBTYPE(ecx) != SMT_TYPE)) + return; + + set_cpu_cap(c, X86_FEATURE_XTOPOLOGY); + + /* + * initial apic id, which also represents 32-bit extended x2apic id. + */ + c->initial_apicid = edx; + + /* + * Populate HT related information from sub-leaf level 0. + */ + core_level_siblings = smp_num_siblings = LEVEL_MAX_SIBLINGS(ebx); + core_plus_mask_width = ht_mask_width = BITS_SHIFT_NEXT_LEVEL(eax); + + sub_index = 1; + do { + cpuid_count(0xb, sub_index, &eax, &ebx, &ecx, &edx); + + /* + * Check for the Core type in the implemented sub leaves. + */ + if (LEAFB_SUBTYPE(ecx) == CORE_TYPE) { + core_level_siblings = LEVEL_MAX_SIBLINGS(ebx); + core_plus_mask_width = BITS_SHIFT_NEXT_LEVEL(eax); + break; + } + + sub_index++; + } while (LEAFB_SUBTYPE(ecx) != INVALID_TYPE); + + core_select_mask = (~(-1 << core_plus_mask_width)) >> ht_mask_width; + +#ifdef CONFIG_X86_32 + c->cpu_core_id = phys_pkg_id(c->initial_apicid, ht_mask_width) + & core_select_mask; + c->phys_proc_id = phys_pkg_id(c->initial_apicid, core_plus_mask_width); +#else + c->cpu_core_id = phys_pkg_id(ht_mask_width) & core_select_mask; + c->phys_proc_id = phys_pkg_id(core_plus_mask_width); +#endif + c->x86_max_cores = (core_level_siblings / smp_num_siblings); + + + printk(KERN_INFO "CPU: Physical Processor ID: %d\n", + c->phys_proc_id); + if (c->x86_max_cores > 1) + printk(KERN_INFO "CPU: Processor Core ID: %d\n", + c->cpu_core_id); + return; +#endif +} + #ifdef CONFIG_X86_PAT void __cpuinit validate_pat_support(struct cpuinfo_x86 *c) { diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c index 18514ed2610..32e73520adf 100644 --- a/arch/x86/kernel/cpu/amd.c +++ b/arch/x86/kernel/cpu/amd.c @@ -1,13 +1,22 @@ #include <linux/init.h> #include <linux/bitops.h> #include <linux/mm.h> + #include <asm/io.h> #include <asm/processor.h> #include <asm/apic.h> +#ifdef CONFIG_X86_64 +# include <asm/numa_64.h> +# include <asm/mmconfig.h> +# include <asm/cacheflush.h> +#endif + #include <mach_apic.h> + #include "cpu.h" +#ifdef CONFIG_X86_32 /* * B step AMD K6 before B 9730xxxx have hardware bugs that can cause * misexecution of code under Linux. Owners of such processors should @@ -24,26 +33,273 @@ extern void vide(void); __asm__(".align 4\nvide: ret"); -static void __cpuinit early_init_amd(struct cpuinfo_x86 *c) +static void __cpuinit init_amd_k5(struct cpuinfo_x86 *c) { - if (cpuid_eax(0x80000000) >= 0x80000007) { - c->x86_power = cpuid_edx(0x80000007); - if (c->x86_power & (1<<8)) - set_cpu_cap(c, X86_FEATURE_CONSTANT_TSC); +/* + * General Systems BIOSen alias the cpu frequency registers + * of the Elan at 0x000df000. Unfortuantly, one of the Linux + * drivers subsequently pokes it, and changes the CPU speed. + * Workaround : Remove the unneeded alias. + */ +#define CBAR (0xfffc) /* Configuration Base Address (32-bit) */ +#define CBAR_ENB (0x80000000) +#define CBAR_KEY (0X000000CB) + if (c->x86_model == 9 || c->x86_model == 10) { + if (inl (CBAR) & CBAR_ENB) + outl (0 | CBAR_KEY, CBAR); } - - /* Set MTRR capability flag if appropriate */ - if (c->x86_model == 13 || c->x86_model == 9 || - (c->x86_model == 8 && c->x86_mask >= 8)) - set_cpu_cap(c, X86_FEATURE_K6_MTRR); } -static void __cpuinit init_amd(struct cpuinfo_x86 *c) + +static void __cpuinit init_amd_k6(struct cpuinfo_x86 *c) { u32 l, h; int mbytes = num_physpages >> (20-PAGE_SHIFT); - int r; + if (c->x86_model < 6) { + /* Based on AMD doc 20734R - June 2000 */ + if (c->x86_model == 0) { + clear_cpu_cap(c, X86_FEATURE_APIC); + set_cpu_cap(c, X86_FEATURE_PGE); + } + return; + } + + if (c->x86_model == 6 && c->x86_mask == 1) { + const int K6_BUG_LOOP = 1000000; + int n; + void (*f_vide)(void); + unsigned long d, d2; + + printk(KERN_INFO "AMD K6 stepping B detected - "); + + /* + * It looks like AMD fixed the 2.6.2 bug and improved indirect + * calls at the same time. + */ + + n = K6_BUG_LOOP; + f_vide = vide; + rdtscl(d); + while (n--) + f_vide(); + rdtscl(d2); + d = d2-d; + + if (d > 20*K6_BUG_LOOP) + printk("system stability may be impaired when more than 32 MB are used.\n"); + else + printk("probably OK (after B9730xxxx).\n"); + printk(KERN_INFO "Please see http://membres.lycos.fr/poulot/k6bug.html\n"); + } + + /* K6 with old style WHCR */ + if (c->x86_model < 8 || + (c->x86_model == 8 && c->x86_mask < 8)) { + /* We can only write allocate on the low 508Mb */ + if (mbytes > 508) + mbytes = 508; + + rdmsr(MSR_K6_WHCR, l, h); + if ((l&0x0000FFFF) == 0) { + unsigned long flags; + l = (1<<0)|((mbytes/4)<<1); + local_irq_save(flags); + wbinvd(); + wrmsr(MSR_K6_WHCR, l, h); + local_irq_restore(flags); + printk(KERN_INFO "Enabling old style K6 write allocation for %d Mb\n", + mbytes); + } + return; + } + + if ((c->x86_model == 8 && c->x86_mask > 7) || + c->x86_model == 9 || c->x86_model == 13) { + /* The more serious chips .. */ + + if (mbytes > 4092) + mbytes = 4092; + + rdmsr(MSR_K6_WHCR, l, h); + if ((l&0xFFFF0000) == 0) { + unsigned long flags; + l = ((mbytes>>2)<<22)|(1<<16); + local_irq_save(flags); + wbinvd(); + wrmsr(MSR_K6_WHCR, l, h); + local_irq_restore(flags); + printk(KERN_INFO "Enabling new style K6 write allocation for %d Mb\n", + mbytes); + } + + return; + } + + if (c->x86_model == 10) { + /* AMD Geode LX is model 10 */ + /* placeholder for any needed mods */ + return; + } +} + +static void __cpuinit init_amd_k7(struct cpuinfo_x86 *c) +{ + u32 l, h; + + /* + * Bit 15 of Athlon specific MSR 15, needs to be 0 + * to enable SSE on Palomino/Morgan/Barton CPU's. + * If the BIOS didn't enable it already, enable it here. + */ + if (c->x86_model >= 6 && c->x86_model <= 10) { + if (!cpu_has(c, X86_FEATURE_XMM)) { + printk(KERN_INFO "Enabling disabled K7/SSE Support.\n"); + rdmsr(MSR_K7_HWCR, l, h); + l &= ~0x00008000; + wrmsr(MSR_K7_HWCR, l, h); + set_cpu_cap(c, X86_FEATURE_XMM); + } + } + + /* + * It's been determined by AMD that Athlons since model 8 stepping 1 + * are more robust with CLK_CTL set to 200xxxxx instead of 600xxxxx + * As per AMD technical note 27212 0.2 + */ + if ((c->x86_model == 8 && c->x86_mask >= 1) || (c->x86_model > 8)) { + rdmsr(MSR_K7_CLK_CTL, l, h); + if ((l & 0xfff00000) != 0x20000000) { + printk ("CPU: CLK_CTL MSR was %x. Reprogramming to %x\n", l, + ((l & 0x000fffff)|0x20000000)); + wrmsr(MSR_K7_CLK_CTL, (l & 0x000fffff)|0x20000000, h); + } + } + + set_cpu_cap(c, X86_FEATURE_K7); +} +#endif + +#if defined(CONFIG_NUMA) && defined(CONFIG_X86_64) +static int __cpuinit nearby_node(int apicid) +{ + int i, node; + + for (i = apicid - 1; i >= 0; i--) { + node = apicid_to_node[i]; + if (node != NUMA_NO_NODE && node_online(node)) + return node; + } + for (i = apicid + 1; i < MAX_LOCAL_APIC; i++) { + node = apicid_to_node[i]; + if (node != NUMA_NO_NODE && node_online(node)) + return node; + } + return first_node(node_online_map); /* Shouldn't happen */ +} +#endif + +/* + * On a AMD dual core setup the lower bits of the APIC id distingush the cores. + * Assumes number of cores is a power of two. + */ +static void __cpuinit amd_detect_cmp(struct cpuinfo_x86 *c) +{ +#ifdef CONFIG_X86_HT + unsigned bits; + + bits = c->x86_coreid_bits; + + /* Low order bits define the core id (index of core in socket) */ + c->cpu_core_id = c->initial_apicid & ((1 << bits)-1); + /* Convert the initial APIC ID into the socket ID */ + c->phys_proc_id = c->initial_apicid >> bits; +#endif +} + +static void __cpuinit srat_detect_node(struct cpuinfo_x86 *c) +{ +#if defined(CONFIG_NUMA) && defined(CONFIG_X86_64) + int cpu = smp_processor_id(); + int node; + unsigned apicid = hard_smp_processor_id(); + + node = c->phys_proc_id; + if (apicid_to_node[apicid] != NUMA_NO_NODE) + node = apicid_to_node[apicid]; + if (!node_online(node)) { + /* Two possibilities here: + - The CPU is missing memory and no node was created. + In that case try picking one from a nearby CPU + - The APIC IDs differ from the HyperTransport node IDs + which the K8 northbridge parsing fills in. + Assume they are all increased by a constant offset, + but in the same order as the HT nodeids. + If that doesn't result in a usable node fall back to the + path for the previous case. */ + + int ht_nodeid = c->initial_apicid; + + if (ht_nodeid >= 0 && + apicid_to_node[ht_nodeid] != NUMA_NO_NODE) + node = apicid_to_node[ht_nodeid]; + /* Pick a nearby node */ + if (!node_online(node)) + node = nearby_node(apicid); + } + numa_set_node(cpu, node); + + printk(KERN_INFO "CPU %d/%x -> Node %d\n", cpu, apicid, node); +#endif +} + +static void __cpuinit early_init_amd_mc(struct cpuinfo_x86 *c) +{ +#ifdef CONFIG_X86_HT + unsigned bits, ecx; + + /* Multi core CPU? */ + if (c->extended_cpuid_level < 0x80000008) + return; + + ecx = cpuid_ecx(0x80000008); + + c->x86_max_cores = (ecx & 0xff) + 1; + + /* CPU telling us the core id bits shift? */ + bits = (ecx >> 12) & 0xF; + + /* Otherwise recompute */ + if (bits == 0) { + while ((1 << bits) < c->x86_max_cores) + bits++; + } + + c->x86_coreid_bits = bits; +#endif +} + +static void __cpuinit early_init_amd(struct cpuinfo_x86 *c) +{ + early_init_amd_mc(c); + + /* c->x86_power is 8000_0007 edx. Bit 8 is constant TSC */ + if (c->x86_power & (1<<8)) + set_cpu_cap(c, X86_FEATURE_CONSTANT_TSC); + +#ifdef CONFIG_X86_64 + set_cpu_cap(c, X86_FEATURE_SYSCALL32); +#else + /* Set MTRR capability flag if appropriate */ + if (c->x86 == 5) + if (c->x86_model == 13 || c->x86_model == 9 || + (c->x86_model == 8 && c->x86_mask >= 8)) + set_cpu_cap(c, X86_FEATURE_K6_MTRR); +#endif +} + +static void __cpuinit init_amd(struct cpuinfo_x86 *c) +{ #ifdef CONFIG_SMP unsigned long long value; @@ -54,7 +310,7 @@ static void __cpuinit init_amd(struct cpuinfo_x86 *c) * Errata 63 for SH-B3 steppings * Errata 122 for all steppings (F+ have it disabled by default) */ - if (c->x86 == 15) { + if (c->x86 == 0xf) { rdmsrl(MSR_K7_HWCR, value); value |= 1 << 6; wrmsrl(MSR_K7_HWCR, value); @@ -64,209 +320,119 @@ static void __cpuinit init_amd(struct cpuinfo_x86 *c) early_init_amd(c); /* - * FIXME: We should handle the K5 here. Set up the write - * range and also turn on MSR 83 bits 4 and 31 (write alloc, - * no bus pipeline) - */ - - /* * Bit 31 in normal CPUID used for nonstandard 3DNow ID; * 3DNow is IDd by bit 31 in extended CPUID (1*32+31) anyway */ clear_cpu_cap(c, 0*32+31); - r = get_model_name(c); +#ifdef CONFIG_X86_64 + /* On C+ stepping K8 rep microcode works well for copy/memset */ + if (c->x86 == 0xf) { + u32 level; - switch (c->x86) { - case 4: - /* - * General Systems BIOSen alias the cpu frequency registers - * of the Elan at 0x000df000. Unfortuantly, one of the Linux - * drivers subsequently pokes it, and changes the CPU speed. - * Workaround : Remove the unneeded alias. - */ -#define CBAR (0xfffc) /* Configuration Base Address (32-bit) */ -#define CBAR_ENB (0x80000000) -#define CBAR_KEY (0X000000CB) - if (c->x86_model == 9 || c->x86_model == 10) { - if (inl (CBAR) & CBAR_ENB) - outl (0 | CBAR_KEY, CBAR); - } - break; - case 5: - if (c->x86_model < 6) { - /* Based on AMD doc 20734R - June 2000 */ - if (c->x86_model == 0) { - clear_cpu_cap(c, X86_FEATURE_APIC); - set_cpu_cap(c, X86_FEATURE_PGE); - } - break; - } - - if (c->x86_model == 6 && c->x86_mask == 1) { - const int K6_BUG_LOOP = 1000000; - int n; - void (*f_vide)(void); - unsigned long d, d2; - - printk(KERN_INFO "AMD K6 stepping B detected - "); - - /* - * It looks like AMD fixed the 2.6.2 bug and improved indirect - * calls at the same time. - */ - - n = K6_BUG_LOOP; - f_vide = vide; - rdtscl(d); - while (n--) - f_vide(); - rdtscl(d2); - d = d2-d; - - if (d > 20*K6_BUG_LOOP) - printk("system stability may be impaired when more than 32 MB are used.\n"); - else - printk("probably OK (after B9730xxxx).\n"); - printk(KERN_INFO "Please see http://membres.lycos.fr/poulot/k6bug.html\n"); - } - - /* K6 with old style WHCR */ - if (c->x86_model < 8 || - (c->x86_model == 8 && c->x86_mask < 8)) { - /* We can only write allocate on the low 508Mb */ - if (mbytes > 508) - mbytes = 508; - - rdmsr(MSR_K6_WHCR, l, h); - if ((l&0x0000FFFF) == 0) { - unsigned long flags; - l = (1<<0)|((mbytes/4)<<1); - local_irq_save(flags); - wbinvd(); - wrmsr(MSR_K6_WHCR, l, h); - local_irq_restore(flags); - printk(KERN_INFO "Enabling old style K6 write allocation for %d Mb\n", - mbytes); - } - break; - } - - if ((c->x86_model == 8 && c->x86_mask > 7) || - c->x86_model == 9 || c->x86_model == 13) { - /* The more serious chips .. */ - - if (mbytes > 4092) - mbytes = 4092; - - rdmsr(MSR_K6_WHCR, l, h); - if ((l&0xFFFF0000) == 0) { - unsigned long flags; - l = ((mbytes>>2)<<22)|(1<<16); - local_irq_save(flags); - wbinvd(); - wrmsr(MSR_K6_WHCR, l, h); - local_irq_restore(flags); - printk(KERN_INFO "Enabling new style K6 write allocation for %d Mb\n", - mbytes); - } - - break; - } - - if (c->x86_model == 10) { - /* AMD Geode LX is model 10 */ - /* placeholder for any needed mods */ - break; - } - break; - case 6: /* An Athlon/Duron */ - - /* - * Bit 15 of Athlon specific MSR 15, needs to be 0 - * to enable SSE on Palomino/Morgan/Barton CPU's. - * If the BIOS didn't enable it already, enable it here. - */ - if (c->x86_model >= 6 && c->x86_model <= 10) { - if (!cpu_has(c, X86_FEATURE_XMM)) { - printk(KERN_INFO "Enabling disabled K7/SSE Support.\n"); - rdmsr(MSR_K7_HWCR, l, h); - l &= ~0x00008000; - wrmsr(MSR_K7_HWCR, l, h); - set_cpu_cap(c, X86_FEATURE_XMM); - } - } - - /* - * It's been determined by AMD that Athlons since model 8 stepping 1 - * are more robust with CLK_CTL set to 200xxxxx instead of 600xxxxx - * As per AMD technical note 27212 0.2 - */ - if ((c->x86_model == 8 && c->x86_mask >= 1) || (c->x86_model > 8)) { - rdmsr(MSR_K7_CLK_CTL, l, h); - if ((l & 0xfff00000) != 0x20000000) { - printk ("CPU: CLK_CTL MSR was %x. Reprogramming to %x\n", l, - ((l & 0x000fffff)|0x20000000)); - wrmsr(MSR_K7_CLK_CTL, (l & 0x000fffff)|0x20000000, h); - } - } - break; + level = cpuid_eax(1); + if((level >= 0x0f48 && level < 0x0f50) || level >= 0x0f58) + set_cpu_cap(c, X86_FEATURE_REP_GOOD); } + if (c->x86 == 0x10 || c->x86 == 0x11) + set_cpu_cap(c, X86_FEATURE_REP_GOOD); +#else + + /* + * FIXME: We should handle the K5 here. Set up the write + * range and also turn on MSR 83 bits 4 and 31 (write alloc, + * no bus pipeline) + */ switch (c->x86) { - case 15: - /* Use K8 tuning for Fam10h and Fam11h */ - case 0x10: - case 0x11: - set_cpu_cap(c, X86_FEATURE_K8); + case 4: + init_amd_k5(c); break; - case 6: - set_cpu_cap(c, X86_FEATURE_K7); + case 5: + init_amd_k6(c); + break; + case 6: /* An Athlon/Duron */ + init_amd_k7(c); break; } + + /* K6s reports MCEs but don't actually have all the MSRs */ + if (c->x86 < 6) + clear_cpu_cap(c, X86_FEATURE_MCE); +#endif + + /* Enable workaround for FXSAVE leak */ if (c->x86 >= 6) set_cpu_cap(c, X86_FEATURE_FXSAVE_LEAK); - display_cacheinfo(c); - - if (cpuid_eax(0x80000000) >= 0x80000008) - c->x86_max_cores = (cpuid_ecx(0x80000008) & 0xff) + 1; + if (!c->x86_model_id[0]) { + switch (c->x86) { + case 0xf: + /* Should distinguish Models here, but this is only + a fallback anyways. */ + strcpy(c->x86_model_id, "Hammer"); + break; + } + } -#ifdef CONFIG_X86_HT - /* - * On a AMD multi core setup the lower bits of the APIC id - * distinguish the cores. - */ - if (c->x86_max_cores > 1) { - int cpu = smp_processor_id(); - unsigned bits = (cpuid_ecx(0x80000008) >> 12) & 0xf; + display_cacheinfo(c); - if (bits == 0) { - while ((1 << bits) < c->x86_max_cores) - bits++; - } - c->cpu_core_id = c->phys_proc_id & ((1<<bits)-1); - c->phys_proc_id >>= bits; - printk(KERN_INFO "CPU %d(%d) -> Core %d\n", - cpu, c->x86_max_cores, c->cpu_core_id); + /* Multi core CPU? */ + if (c->extended_cpuid_level >= 0x80000008) { + amd_detect_cmp(c); + srat_detect_node(c); } + +#ifdef CONFIG_X86_32 + detect_ht(c); #endif - if (cpuid_eax(0x80000000) >= 0x80000006) { - if ((c->x86 == 0x10) && (cpuid_edx(0x80000006) & 0xf000)) + if (c->extended_cpuid_level >= 0x80000006) { + if ((c->x86 >= 0x0f) && (cpuid_edx(0x80000006) & 0xf000)) num_cache_leaves = 4; else num_cache_leaves = 3; } - /* K6s reports MCEs but don't actually have all the MSRs */ - if (c->x86 < 6) - clear_cpu_cap(c, X86_FEATURE_MCE); + if (c->x86 >= 0xf && c->x86 <= 0x11) + set_cpu_cap(c, X86_FEATURE_K8); - if (cpu_has_xmm2) + if (cpu_has_xmm2) { + /* MFENCE stops RDTSC speculation */ set_cpu_cap(c, X86_FEATURE_MFENCE_RDTSC); + } + +#ifdef CONFIG_X86_64 + if (c->x86 == 0x10) { + /* do this for boot cpu */ + if (c == &boot_cpu_data) + check_enable_amd_mmconf_dmi(); + + fam10h_check_enable_mmcfg(); + } + + if (c == &boot_cpu_data && c->x86 >= 0xf && c->x86 <= 0x11) { + unsigned long long tseg; + + /* + * Split up direct mapping around the TSEG SMM area. + * Don't do it for gbpages because there seems very little + * benefit in doing so. + */ + if (!rdmsrl_safe(MSR_K8_TSEG_ADDR, &tseg)) { + printk(KERN_DEBUG "tseg: %010llx\n", tseg); + if ((tseg>>PMD_SHIFT) < + (max_low_pfn_mapped>>(PMD_SHIFT-PAGE_SHIFT)) || + ((tseg>>PMD_SHIFT) < + (max_pfn_mapped>>(PMD_SHIFT-PAGE_SHIFT)) && + (tseg>>PMD_SHIFT) >= (1ULL<<(32 - PMD_SHIFT)))) + set_memory_4k((unsigned long)__va(tseg), 1); + } + } +#endif } +#ifdef CONFIG_X86_32 static unsigned int __cpuinit amd_size_cache(struct cpuinfo_x86 *c, unsigned int size) { /* AMD errata T13 (order #21922) */ @@ -279,10 +445,12 @@ static unsigned int __cpuinit amd_size_cache(struct cpuinfo_x86 *c, unsigned int } return size; } +#endif static struct cpu_dev amd_cpu_dev __cpuinitdata = { .c_vendor = "AMD", .c_ident = { "AuthenticAMD" }, +#ifdef CONFIG_X86_32 .c_models = { { .vendor = X86_VENDOR_AMD, .family = 4, .model_names = { @@ -295,9 +463,11 @@ static struct cpu_dev amd_cpu_dev __cpuinitdata = { } }, }, + .c_size_cache = amd_size_cache, +#endif .c_early_init = early_init_amd, .c_init = init_amd, - .c_size_cache = amd_size_cache, + .c_x86_vendor = X86_VENDOR_AMD, }; -cpu_vendor_dev_register(X86_VENDOR_AMD, &amd_cpu_dev); +cpu_dev_register(amd_cpu_dev); diff --git a/arch/x86/kernel/cpu/amd_64.c b/arch/x86/kernel/cpu/amd_64.c deleted file mode 100644 index d1692b2a41f..00000000000 --- a/arch/x86/kernel/cpu/amd_64.c +++ /dev/null @@ -1,224 +0,0 @@ -#include <linux/init.h> -#include <linux/mm.h> - -#include <asm/numa_64.h> -#include <asm/mmconfig.h> -#include <asm/cacheflush.h> - -#include <mach_apic.h> - -#include "cpu.h" - -int force_mwait __cpuinitdata; - -#ifdef CONFIG_NUMA -static int __cpuinit nearby_node(int apicid) -{ - int i, node; - - for (i = apicid - 1; i >= 0; i--) { - node = apicid_to_node[i]; - if (node != NUMA_NO_NODE && node_online(node)) - return node; - } - for (i = apicid + 1; i < MAX_LOCAL_APIC; i++) { - node = apicid_to_node[i]; - if (node != NUMA_NO_NODE && node_online(node)) - return node; - } - return first_node(node_online_map); /* Shouldn't happen */ -} -#endif - -/* - * On a AMD dual core setup the lower bits of the APIC id distingush the cores. - * Assumes number of cores is a power of two. - */ -static void __cpuinit amd_detect_cmp(struct cpuinfo_x86 *c) -{ -#ifdef CONFIG_SMP - unsigned bits; -#ifdef CONFIG_NUMA - int cpu = smp_processor_id(); - int node = 0; - unsigned apicid = hard_smp_processor_id(); -#endif - bits = c->x86_coreid_bits; - - /* Low order bits define the core id (index of core in socket) */ - c->cpu_core_id = c->initial_apicid & ((1 << bits)-1); - /* Convert the initial APIC ID into the socket ID */ - c->phys_proc_id = c->initial_apicid >> bits; - -#ifdef CONFIG_NUMA - node = c->phys_proc_id; - if (apicid_to_node[apicid] != NUMA_NO_NODE) - node = apicid_to_node[apicid]; - if (!node_online(node)) { - /* Two possibilities here: - - The CPU is missing memory and no node was created. - In that case try picking one from a nearby CPU - - The APIC IDs differ from the HyperTransport node IDs - which the K8 northbridge parsing fills in. - Assume they are all increased by a constant offset, - but in the same order as the HT nodeids. - If that doesn't result in a usable node fall back to the - path for the previous case. */ - - int ht_nodeid = c->initial_apicid; - - if (ht_nodeid >= 0 && - apicid_to_node[ht_nodeid] != NUMA_NO_NODE) - node = apicid_to_node[ht_nodeid]; - /* Pick a nearby node */ - if (!node_online(node)) - node = nearby_node(apicid); - } - numa_set_node(cpu, node); - - printk(KERN_INFO "CPU %d/%x -> Node %d\n", cpu, apicid, node); -#endif -#endif -} - -static void __cpuinit early_init_amd_mc(struct cpuinfo_x86 *c) -{ -#ifdef CONFIG_SMP - unsigned bits, ecx; - - /* Multi core CPU? */ - if (c->extended_cpuid_level < 0x80000008) - return; - - ecx = cpuid_ecx(0x80000008); - - c->x86_max_cores = (ecx & 0xff) + 1; - - /* CPU telling us the core id bits shift? */ - bits = (ecx >> 12) & 0xF; - - /* Otherwise recompute */ - if (bits == 0) { - while ((1 << bits) < c->x86_max_cores) - bits++; - } - - c->x86_coreid_bits = bits; - -#endif -} - -static void __cpuinit early_init_amd(struct cpuinfo_x86 *c) -{ - early_init_amd_mc(c); - - /* c->x86_power is 8000_0007 edx. Bit 8 is constant TSC */ - if (c->x86_power & (1<<8)) - set_cpu_cap(c, X86_FEATURE_CONSTANT_TSC); - - set_cpu_cap(c, X86_FEATURE_SYSCALL32); -} - -static void __cpuinit init_amd(struct cpuinfo_x86 *c) -{ - unsigned level; - -#ifdef CONFIG_SMP - unsigned long value; - - /* - * Disable TLB flush filter by setting HWCR.FFDIS on K8 - * bit 6 of msr C001_0015 - * - * Errata 63 for SH-B3 steppings - * Errata 122 for all steppings (F+ have it disabled by default) - */ - if (c->x86 == 0xf) { - rdmsrl(MSR_K8_HWCR, value); - value |= 1 << 6; - wrmsrl(MSR_K8_HWCR, value); - } -#endif - - /* Bit 31 in normal CPUID used for nonstandard 3DNow ID; - 3DNow is IDd by bit 31 in extended CPUID (1*32+31) anyway */ - clear_cpu_cap(c, 0*32+31); - - /* On C+ stepping K8 rep microcode works well for copy/memset */ - if (c->x86 == 0xf) { - level = cpuid_eax(1); - if((level >= 0x0f48 && level < 0x0f50) || level >= 0x0f58) - set_cpu_cap(c, X86_FEATURE_REP_GOOD); - } - if (c->x86 == 0x10 || c->x86 == 0x11) - set_cpu_cap(c, X86_FEATURE_REP_GOOD); - - /* Enable workaround for FXSAVE leak */ - if (c->x86 >= 6) - set_cpu_cap(c, X86_FEATURE_FXSAVE_LEAK); - - level = get_model_name(c); - if (!level) { - switch (c->x86) { - case 0xf: - /* Should distinguish Models here, but this is only - a fallback anyways. */ - strcpy(c->x86_model_id, "Hammer"); - break; - } - } - display_cacheinfo(c); - - /* Multi core CPU? */ - if (c->extended_cpuid_level >= 0x80000008) - amd_detect_cmp(c); - - if (c->extended_cpuid_level >= 0x80000006 && - (cpuid_edx(0x80000006) & 0xf000)) - num_cache_leaves = 4; - else - num_cache_leaves = 3; - - if (c->x86 >= 0xf && c->x86 <= 0x11) - set_cpu_cap(c, X86_FEATURE_K8); - - /* MFENCE stops RDTSC speculation */ - set_cpu_cap(c, X86_FEATURE_MFENCE_RDTSC); - - if (c->x86 == 0x10) { - /* do this for boot cpu */ - if (c == &boot_cpu_data) - check_enable_amd_mmconf_dmi(); - - fam10h_check_enable_mmcfg(); - } - - if (c == &boot_cpu_data && c->x86 >= 0xf && c->x86 <= 0x11) { - unsigned long long tseg; - - /* - * Split up direct mapping around the TSEG SMM area. - * Don't do it for gbpages because there seems very little - * benefit in doing so. - */ - if (!rdmsrl_safe(MSR_K8_TSEG_ADDR, &tseg)) { - printk(KERN_DEBUG "tseg: %010llx\n", tseg); - if ((tseg>>PMD_SHIFT) < - (max_low_pfn_mapped>>(PMD_SHIFT-PAGE_SHIFT)) || - ((tseg>>PMD_SHIFT) < - (max_pfn_mapped>>(PMD_SHIFT-PAGE_SHIFT)) && - (tseg>>PMD_SHIFT) >= (1ULL<<(32 - PMD_SHIFT)))) - set_memory_4k((unsigned long)__va(tseg), 1); - } - } -} - -static struct cpu_dev amd_cpu_dev __cpuinitdata = { - .c_vendor = "AMD", - .c_ident = { "AuthenticAMD" }, - .c_early_init = early_init_amd, - .c_init = init_amd, -}; - -cpu_vendor_dev_register(X86_VENDOR_AMD, &amd_cpu_dev); - diff --git a/arch/x86/kernel/cpu/centaur.c b/arch/x86/kernel/cpu/centaur.c index a0534c04d38..89bfdd9cacc 100644 --- a/arch/x86/kernel/cpu/centaur.c +++ b/arch/x86/kernel/cpu/centaur.c @@ -289,7 +289,6 @@ static void __cpuinit init_c3(struct cpuinfo_x86 *c) if (c->x86_model >= 6 && c->x86_model < 9) set_cpu_cap(c, X86_FEATURE_3DNOW); - get_model_name(c); display_cacheinfo(c); } @@ -475,6 +474,7 @@ static struct cpu_dev centaur_cpu_dev __cpuinitdata = { .c_early_init = early_init_centaur, .c_init = init_centaur, .c_size_cache = centaur_size_cache, + .c_x86_vendor = X86_VENDOR_CENTAUR, }; -cpu_vendor_dev_register(X86_VENDOR_CENTAUR, ¢aur_cpu_dev); +cpu_dev_register(centaur_cpu_dev); diff --git a/arch/x86/kernel/cpu/centaur_64.c b/arch/x86/kernel/cpu/centaur_64.c index 1d181c40e2e..a1625f5a1e7 100644 --- a/arch/x86/kernel/cpu/centaur_64.c +++ b/arch/x86/kernel/cpu/centaur_64.c @@ -16,9 +16,10 @@ static void __cpuinit early_init_centaur(struct cpuinfo_x86 *c) static void __cpuinit init_centaur(struct cpuinfo_x86 *c) { + early_init_centaur(c); + if (c->x86 == 0x6 && c->x86_model >= 0xf) { c->x86_cache_alignment = c->x86_clflush_size * 2; - set_cpu_cap(c, X86_FEATURE_CONSTANT_TSC); set_cpu_cap(c, X86_FEATURE_REP_GOOD); } set_cpu_cap(c, X86_FEATURE_LFENCE_RDTSC); @@ -29,7 +30,8 @@ static struct cpu_dev centaur_cpu_dev __cpuinitdata = { .c_ident = { "CentaurHauls" }, .c_early_init = early_init_centaur, .c_init = init_centaur, + .c_x86_vendor = X86_VENDOR_CENTAUR, }; -cpu_vendor_dev_register(X86_VENDOR_CENTAUR, ¢aur_cpu_dev); +cpu_dev_register(centaur_cpu_dev); diff --git a/arch/x86/kernel/cpu/cmpxchg.c b/arch/x86/kernel/cpu/cmpxchg.c new file mode 100644 index 00000000000..2056ccf572c --- /dev/null +++ b/arch/x86/kernel/cpu/cmpxchg.c @@ -0,0 +1,72 @@ +/* + * cmpxchg*() fallbacks for CPU not supporting these instructions + */ + +#include <linux/kernel.h> +#include <linux/smp.h> +#include <linux/module.h> + +#ifndef CONFIG_X86_CMPXCHG +unsigned long cmpxchg_386_u8(volatile void *ptr, u8 old, u8 new) +{ + u8 prev; + unsigned long flags; + + /* Poor man's cmpxchg for 386. Unsuitable for SMP */ + local_irq_save(flags); + prev = *(u8 *)ptr; + if (prev == old) + *(u8 *)ptr = new; + local_irq_restore(flags); + return prev; +} +EXPORT_SYMBOL(cmpxchg_386_u8); + +unsigned long cmpxchg_386_u16(volatile void *ptr, u16 old, u16 new) +{ + u16 prev; + unsigned long flags; + + /* Poor man's cmpxchg for 386. Unsuitable for SMP */ + local_irq_save(flags); + prev = *(u16 *)ptr; + if (prev == old) + *(u16 *)ptr = new; + local_irq_restore(flags); + return prev; +} +EXPORT_SYMBOL(cmpxchg_386_u16); + +unsigned long cmpxchg_386_u32(volatile void *ptr, u32 old, u32 new) +{ + u32 prev; + unsigned long flags; + + /* Poor man's cmpxchg for 386. Unsuitable for SMP */ + local_irq_save(flags); + prev = *(u32 *)ptr; + if (prev == old) + *(u32 *)ptr = new; + local_irq_restore(flags); + return prev; +} +EXPORT_SYMBOL(cmpxchg_386_u32); +#endif + +#ifndef CONFIG_X86_CMPXCHG64 +unsigned long long cmpxchg_486_u64(volatile void *ptr, u64 old, u64 new) +{ + u64 prev; + unsigned long flags; + + /* Poor man's cmpxchg8b for 386 and 486. Unsuitable for SMP */ + local_irq_save(flags); + prev = *(u64 *)ptr; + if (prev == old) + *(u64 *)ptr = new; + local_irq_restore(flags); + return prev; +} +EXPORT_SYMBOL(cmpxchg_486_u64); +#endif + diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c index 4e456bd955b..25581dcb280 100644 --- a/arch/x86/kernel/cpu/common.c +++ b/arch/x86/kernel/cpu/common.c @@ -1,28 +1,62 @@ #include <linux/init.h> +#include <linux/kernel.h> +#include <linux/sched.h> #include <linux/string.h> +#include <linux/bootmem.h> +#include <linux/bitops.h> +#include <linux/module.h> +#include <linux/kgdb.h> +#include <linux/topology.h> #include <linux/delay.h> #include <linux/smp.h> -#include <linux/module.h> #include <linux/percpu.h> -#include <linux/bootmem.h> -#include <asm/processor.h> #include <asm/i387.h> #include <asm/msr.h> #include <asm/io.h> +#include <asm/linkage.h> #include <asm/mmu_context.h> #include <asm/mtrr.h> #include <asm/mce.h> #include <asm/pat.h> #include <asm/asm.h> +#include <asm/numa.h> #ifdef CONFIG_X86_LOCAL_APIC #include <asm/mpspec.h> #include <asm/apic.h> #include <mach_apic.h> +#include <asm/genapic.h> #endif +#include <asm/pda.h> +#include <asm/pgtable.h> +#include <asm/processor.h> +#include <asm/desc.h> +#include <asm/atomic.h> +#include <asm/proto.h> +#include <asm/sections.h> +#include <asm/setup.h> + #include "cpu.h" +static struct cpu_dev *this_cpu __cpuinitdata; + +#ifdef CONFIG_X86_64 +/* We need valid kernel segments for data and code in long mode too + * IRET will check the segment types kkeil 2000/10/28 + * Also sysret mandates a special GDT layout + */ +/* The TLS descriptors are currently at a different place compared to i386. + Hopefully nobody expects them at a fixed place (Wine?) */ DEFINE_PER_CPU(struct gdt_page, gdt_page) = { .gdt = { + [GDT_ENTRY_KERNEL32_CS] = { { { 0x0000ffff, 0x00cf9b00 } } }, + [GDT_ENTRY_KERNEL_CS] = { { { 0x0000ffff, 0x00af9b00 } } }, + [GDT_ENTRY_KERNEL_DS] = { { { 0x0000ffff, 0x00cf9300 } } }, + [GDT_ENTRY_DEFAULT_USER32_CS] = { { { 0x0000ffff, 0x00cffb00 } } }, + [GDT_ENTRY_DEFAULT_USER_DS] = { { { 0x0000ffff, 0x00cff300 } } }, + [GDT_ENTRY_DEFAULT_USER_CS] = { { { 0x0000ffff, 0x00affb00 } } }, +} }; +#else +DEFINE_PER_CPU_PAGE_ALIGNED(struct gdt_page, gdt_page) = { .gdt = { [GDT_ENTRY_KERNEL_CS] = { { { 0x0000ffff, 0x00cf9a00 } } }, [GDT_ENTRY_KERNEL_DS] = { { { 0x0000ffff, 0x00cf9200 } } }, [GDT_ENTRY_DEFAULT_USER_CS] = { { { 0x0000ffff, 0x00cffa00 } } }, @@ -56,17 +90,157 @@ DEFINE_PER_CPU(struct gdt_page, gdt_page) = { .gdt = { [GDT_ENTRY_ESPFIX_SS] = { { { 0x00000000, 0x00c09200 } } }, [GDT_ENTRY_PERCPU] = { { { 0x00000000, 0x00000000 } } }, } }; +#endif EXPORT_PER_CPU_SYMBOL_GPL(gdt_page); -__u32 cleared_cpu_caps[NCAPINTS] __cpuinitdata; - +#ifdef CONFIG_X86_32 static int cachesize_override __cpuinitdata = -1; static int disable_x86_serial_nr __cpuinitdata = 1; -struct cpu_dev *cpu_devs[X86_VENDOR_NUM] = {}; +static int __init cachesize_setup(char *str) +{ + get_option(&str, &cachesize_override); + return 1; +} +__setup("cachesize=", cachesize_setup); + +static int __init x86_fxsr_setup(char *s) +{ + setup_clear_cpu_cap(X86_FEATURE_FXSR); + setup_clear_cpu_cap(X86_FEATURE_XMM); + return 1; +} +__setup("nofxsr", x86_fxsr_setup); + +static int __init x86_sep_setup(char *s) +{ + setup_clear_cpu_cap(X86_FEATURE_SEP); + return 1; +} +__setup("nosep", x86_sep_setup); + +/* Standard macro to see if a specific flag is changeable */ +static inline int flag_is_changeable_p(u32 flag) +{ + u32 f1, f2; + + /* + * Cyrix and IDT cpus allow disabling of CPUID + * so the code below may return different results + * when it is executed before and after enabling + * the CPUID. Add "volatile" to not allow gcc to + * optimize the subsequent calls to this function. + */ + asm volatile ("pushfl\n\t" + "pushfl\n\t" + "popl %0\n\t" + "movl %0,%1\n\t" + "xorl %2,%0\n\t" + "pushl %0\n\t" + "popfl\n\t" + "pushfl\n\t" + "popl %0\n\t" + "popfl\n\t" + : "=&r" (f1), "=&r" (f2) + : "ir" (flag)); + + return ((f1^f2) & flag) != 0; +} + +/* Probe for the CPUID instruction */ +static int __cpuinit have_cpuid_p(void) +{ + return flag_is_changeable_p(X86_EFLAGS_ID); +} + +static void __cpuinit squash_the_stupid_serial_number(struct cpuinfo_x86 *c) +{ + if (cpu_has(c, X86_FEATURE_PN) && disable_x86_serial_nr) { + /* Disable processor serial number */ + unsigned long lo, hi; + rdmsr(MSR_IA32_BBL_CR_CTL, lo, hi); + lo |= 0x200000; + wrmsr(MSR_IA32_BBL_CR_CTL, lo, hi); + printk(KERN_NOTICE "CPU serial number disabled.\n"); + clear_cpu_cap(c, X86_FEATURE_PN); + + /* Disabling the serial number may affect the cpuid level */ + c->cpuid_level = cpuid_eax(0); + } +} + +static int __init x86_serial_nr_setup(char *s) +{ + disable_x86_serial_nr = 0; + return 1; +} +__setup("serialnumber", x86_serial_nr_setup); +#else +static inline int flag_is_changeable_p(u32 flag) +{ + return 1; +} +/* Probe for the CPUID instruction */ +static inline int have_cpuid_p(void) +{ + return 1; +} +static inline void squash_the_stupid_serial_number(struct cpuinfo_x86 *c) +{ +} +#endif + +/* + * Naming convention should be: <Name> [(<Codename>)] + * This table only is used unless init_<vendor>() below doesn't set it; + * in particular, if CPUID levels 0x80000002..4 are supported, this isn't used + * + */ + +/* Look up CPU names by table lookup. */ +static char __cpuinit *table_lookup_model(struct cpuinfo_x86 *c) +{ + struct cpu_model_info *info; + + if (c->x86_model >= 16) + return NULL; /* Range check */ + + if (!this_cpu) + return NULL; + + info = this_cpu->c_models; + + while (info && info->family) { + if (info->family == c->x86) + return info->model_names[c->x86_model]; + info++; + } + return NULL; /* Not found */ +} + +__u32 cleared_cpu_caps[NCAPINTS] __cpuinitdata; + +/* Current gdt points %fs at the "master" per-cpu area: after this, + * it's on the real one. */ +void switch_to_new_gdt(void) +{ + struct desc_ptr gdt_descr; + + gdt_descr.address = (long)get_cpu_gdt_table(smp_processor_id()); + gdt_descr.size = GDT_SIZE - 1; + load_gdt(&gdt_descr); +#ifdef CONFIG_X86_32 + asm("mov %0, %%fs" : : "r" (__KERNEL_PERCPU) : "memory"); +#endif +} + +static struct cpu_dev *cpu_devs[X86_VENDOR_NUM] = {}; static void __cpuinit default_init(struct cpuinfo_x86 *c) { +#ifdef CONFIG_X86_64 + display_cacheinfo(c); +#else /* Not much we can do here... */ /* Check if at least it has cpuid */ if (c->cpuid_level == -1) { @@ -76,28 +250,22 @@ static void __cpuinit default_init(struct cpuinfo_x86 *c) else if (c->x86 == 3) strcpy(c->x86_model_id, "386"); } +#endif } static struct cpu_dev __cpuinitdata default_cpu = { .c_init = default_init, .c_vendor = "Unknown", + .c_x86_vendor = X86_VENDOR_UNKNOWN, }; -static struct cpu_dev *this_cpu __cpuinitdata = &default_cpu; - -static int __init cachesize_setup(char *str) -{ - get_option(&str, &cachesize_override); - return 1; -} -__setup("cachesize=", cachesize_setup); -int __cpuinit get_model_name(struct cpuinfo_x86 *c) +static void __cpuinit get_model_name(struct cpuinfo_x86 *c) { unsigned int *v; char *p, *q; - if (cpuid_eax(0x80000000) < 0x80000004) - return 0; + if (c->extended_cpuid_level < 0x80000004) + return; v = (unsigned int *) c->x86_model_id; cpuid(0x80000002, &v[0], &v[1], &v[2], &v[3]); @@ -116,30 +284,34 @@ int __cpuinit get_model_name(struct cpuinfo_x86 *c) while (q <= &c->x86_model_id[48]) *q++ = '\0'; /* Zero-pad the rest */ } - - return 1; } - void __cpuinit display_cacheinfo(struct cpuinfo_x86 *c) { - unsigned int n, dummy, ecx, edx, l2size; + unsigned int n, dummy, ebx, ecx, edx, l2size; - n = cpuid_eax(0x80000000); + n = c->extended_cpuid_level; if (n >= 0x80000005) { - cpuid(0x80000005, &dummy, &dummy, &ecx, &edx); + cpuid(0x80000005, &dummy, &ebx, &ecx, &edx); printk(KERN_INFO "CPU: L1 I Cache: %dK (%d bytes/line), D cache %dK (%d bytes/line)\n", - edx>>24, edx&0xFF, ecx>>24, ecx&0xFF); - c->x86_cache_size = (ecx>>24)+(edx>>24); + edx>>24, edx&0xFF, ecx>>24, ecx&0xFF); + c->x86_cache_size = (ecx>>24) + (edx>>24); +#ifdef CONFIG_X86_64 + /* On K8 L1 TLB is inclusive, so don't count it */ + c->x86_tlbsize = 0; +#endif } if (n < 0x80000006) /* Some chips just has a large L1. */ return; - ecx = cpuid_ecx(0x80000006); + cpuid(0x80000006, &dummy, &ebx, &ecx, &edx); l2size = ecx >> 16; +#ifdef CONFIG_X86_64 + c->x86_tlbsize += ((ebx >> 16) & 0xfff) + (ebx & 0xfff); +#else /* do processor-specific cache resizing */ if (this_cpu->c_size_cache) l2size = this_cpu->c_size_cache(c, l2size); @@ -150,116 +322,106 @@ void __cpuinit display_cacheinfo(struct cpuinfo_x86 *c) if (l2size == 0) return; /* Again, no L2 cache is possible */ +#endif c->x86_cache_size = l2size; printk(KERN_INFO "CPU: L2 Cache: %dK (%d bytes/line)\n", - l2size, ecx & 0xFF); + l2size, ecx & 0xFF); } -/* - * Naming convention should be: <Name> [(<Codename>)] - * This table only is used unless init_<vendor>() below doesn't set it; - * in particular, if CPUID levels 0x80000002..4 are supported, this isn't used - * - */ - -/* Look up CPU names by table lookup. */ -static char __cpuinit *table_lookup_model(struct cpuinfo_x86 *c) +void __cpuinit detect_ht(struct cpuinfo_x86 *c) { - struct cpu_model_info *info; +#ifdef CONFIG_X86_HT + u32 eax, ebx, ecx, edx; + int index_msb, core_bits; - if (c->x86_model >= 16) - return NULL; /* Range check */ + if (!cpu_has(c, X86_FEATURE_HT)) + return; - if (!this_cpu) - return NULL; + if (cpu_has(c, X86_FEATURE_CMP_LEGACY)) + goto out; - info = this_cpu->c_models; + if (cpu_has(c, X86_FEATURE_XTOPOLOGY)) + return; - while (info && info->family) { - if (info->family == c->x86) - return info->model_names[c->x86_model]; - info++; + cpuid(1, &eax, &ebx, &ecx, &edx); + + smp_num_siblings = (ebx & 0xff0000) >> 16; + + if (smp_num_siblings == 1) { + printk(KERN_INFO "CPU: Hyper-Threading is disabled\n"); + } else if (smp_num_siblings > 1) { + + if (smp_num_siblings > NR_CPUS) { + printk(KERN_WARNING "CPU: Unsupported number of siblings %d", + smp_num_siblings); + smp_num_siblings = 1; + return; + } + + index_msb = get_count_order(smp_num_siblings); +#ifdef CONFIG_X86_64 + c->phys_proc_id = phys_pkg_id(index_msb); +#else + c->phys_proc_id = phys_pkg_id(c->initial_apicid, index_msb); +#endif + + smp_num_siblings = smp_num_siblings / c->x86_max_cores; + + index_msb = get_count_order(smp_num_siblings); + + core_bits = get_count_order(c->x86_max_cores); + +#ifdef CONFIG_X86_64 + c->cpu_core_id = phys_pkg_id(index_msb) & + ((1 << core_bits) - 1); +#else + c->cpu_core_id = phys_pkg_id(c->initial_apicid, index_msb) & + ((1 << core_bits) - 1); +#endif } - return NULL; /* Not found */ -} +out: + if ((c->x86_max_cores * smp_num_siblings) > 1) { + printk(KERN_INFO "CPU: Physical Processor ID: %d\n", + c->phys_proc_id); + printk(KERN_INFO "CPU: Processor Core ID: %d\n", + c->cpu_core_id); + } +#endif +} -static void __cpuinit get_cpu_vendor(struct cpuinfo_x86 *c, int early) +static void __cpuinit get_cpu_vendor(struct cpuinfo_x86 *c) { char *v = c->x86_vendor_id; int i; static int printed; for (i = 0; i < X86_VENDOR_NUM; i++) { - if (cpu_devs[i]) { - if (!strcmp(v, cpu_devs[i]->c_ident[0]) || - (cpu_devs[i]->c_ident[1] && - !strcmp(v, cpu_devs[i]->c_ident[1]))) { - c->x86_vendor = i; - if (!early) - this_cpu = cpu_devs[i]; - return; - } + if (!cpu_devs[i]) + break; + + if (!strcmp(v, cpu_devs[i]->c_ident[0]) || + (cpu_devs[i]->c_ident[1] && + !strcmp(v, cpu_devs[i]->c_ident[1]))) { + this_cpu = cpu_devs[i]; + c->x86_vendor = this_cpu->c_x86_vendor; + return; } } + if (!printed) { printed++; - printk(KERN_ERR "CPU: Vendor unknown, using generic init.\n"); + printk(KERN_ERR "CPU: vendor_id '%s' unknown, using generic init.\n", v); printk(KERN_ERR "CPU: Your system may be unstable.\n"); } + c->x86_vendor = X86_VENDOR_UNKNOWN; this_cpu = &default_cpu; } - -static int __init x86_fxsr_setup(char *s) -{ - setup_clear_cpu_cap(X86_FEATURE_FXSR); - setup_clear_cpu_cap(X86_FEATURE_XMM); - return 1; -} -__setup("nofxsr", x86_fxsr_setup); - - -static int __init x86_sep_setup(char *s) -{ - setup_clear_cpu_cap(X86_FEATURE_SEP); - return 1; -} -__setup("nosep", x86_sep_setup); - - -/* Standard macro to see if a specific flag is changeable */ -static inline int flag_is_changeable_p(u32 flag) -{ - u32 f1, f2; - - asm("pushfl\n\t" - "pushfl\n\t" - "popl %0\n\t" - "movl %0,%1\n\t" - "xorl %2,%0\n\t" - "pushl %0\n\t" - "popfl\n\t" - "pushfl\n\t" - "popl %0\n\t" - "popfl\n\t" - : "=&r" (f1), "=&r" (f2) - : "ir" (flag)); - - return ((f1^f2) & flag) != 0; -} - - -/* Probe for the CPUID instruction */ -static int __cpuinit have_cpuid_p(void) -{ - return flag_is_changeable_p(X86_EFLAGS_ID); -} - -void __init cpu_detect(struct cpuinfo_x86 *c) +void __cpuinit cpu_detect(struct cpuinfo_x86 *c) { /* Get vendor name */ cpuid(0x00000000, (unsigned int *)&c->cpuid_level, @@ -268,50 +430,87 @@ void __init cpu_detect(struct cpuinfo_x86 *c) (unsigned int *)&c->x86_vendor_id[4]); c->x86 = 4; + /* Intel-defined flags: level 0x00000001 */ if (c->cpuid_level >= 0x00000001) { u32 junk, tfms, cap0, misc; cpuid(0x00000001, &tfms, &misc, &junk, &cap0); - c->x86 = (tfms >> 8) & 15; - c->x86_model = (tfms >> 4) & 15; + c->x86 = (tfms >> 8) & 0xf; + c->x86_model = (tfms >> 4) & 0xf; + c->x86_mask = tfms & 0xf; if (c->x86 == 0xf) c->x86 += (tfms >> 20) & 0xff; if (c->x86 >= 0x6) - c->x86_model += ((tfms >> 16) & 0xF) << 4; - c->x86_mask = tfms & 15; + c->x86_model += ((tfms >> 16) & 0xf) << 4; if (cap0 & (1<<19)) { - c->x86_cache_alignment = ((misc >> 8) & 0xff) * 8; c->x86_clflush_size = ((misc >> 8) & 0xff) * 8; + c->x86_cache_alignment = c->x86_clflush_size; } } } -static void __cpuinit early_get_cap(struct cpuinfo_x86 *c) + +static void __cpuinit get_cpu_cap(struct cpuinfo_x86 *c) { u32 tfms, xlvl; - unsigned int ebx; + u32 ebx; - memset(&c->x86_capability, 0, sizeof c->x86_capability); - if (have_cpuid_p()) { - /* Intel-defined flags: level 0x00000001 */ - if (c->cpuid_level >= 0x00000001) { - u32 capability, excap; - cpuid(0x00000001, &tfms, &ebx, &excap, &capability); - c->x86_capability[0] = capability; - c->x86_capability[4] = excap; - } + /* Intel-defined flags: level 0x00000001 */ + if (c->cpuid_level >= 0x00000001) { + u32 capability, excap; + cpuid(0x00000001, &tfms, &ebx, &excap, &capability); + c->x86_capability[0] = capability; + c->x86_capability[4] = excap; + } - /* AMD-defined flags: level 0x80000001 */ - xlvl = cpuid_eax(0x80000000); - if ((xlvl & 0xffff0000) == 0x80000000) { - if (xlvl >= 0x80000001) { - c->x86_capability[1] = cpuid_edx(0x80000001); - c->x86_capability[6] = cpuid_ecx(0x80000001); - } + /* AMD-defined flags: level 0x80000001 */ + xlvl = cpuid_eax(0x80000000); + c->extended_cpuid_level = xlvl; + if ((xlvl & 0xffff0000) == 0x80000000) { + if (xlvl >= 0x80000001) { + c->x86_capability[1] = cpuid_edx(0x80000001); + c->x86_capability[6] = cpuid_ecx(0x80000001); } + } + +#ifdef CONFIG_X86_64 + if (c->extended_cpuid_level >= 0x80000008) { + u32 eax = cpuid_eax(0x80000008); + c->x86_virt_bits = (eax >> 8) & 0xff; + c->x86_phys_bits = eax & 0xff; } +#endif + + if (c->extended_cpuid_level >= 0x80000007) + c->x86_power = cpuid_edx(0x80000007); } +static void __cpuinit identify_cpu_without_cpuid(struct cpuinfo_x86 *c) +{ +#ifdef CONFIG_X86_32 + int i; + + /* + * First of all, decide if this is a 486 or higher + * It's a 486 if we can modify the AC flag + */ + if (flag_is_changeable_p(X86_EFLAGS_AC)) + c->x86 = 4; + else + c->x86 = 3; + + for (i = 0; i < X86_VENDOR_NUM; i++) + if (cpu_devs[i] && cpu_devs[i]->c_identify) { + c->x86_vendor_id[0] = 0; + cpu_devs[i]->c_identify(c); + if (c->x86_vendor_id[0]) { + get_cpu_vendor(c); + break; + } + } +#endif +} + /* * Do minimum CPU detection early. * Fields really needed: vendor, cpuid_level, family, model, mask, @@ -321,25 +520,61 @@ static void __cpuinit early_get_cap(struct cpuinfo_x86 *c) * WARNING: this function is only called on the BP. Don't add code here * that is supposed to run on all CPUs. */ -static void __init early_cpu_detect(void) +static void __init early_identify_cpu(struct cpuinfo_x86 *c) { - struct cpuinfo_x86 *c = &boot_cpu_data; - - c->x86_cache_alignment = 32; +#ifdef CONFIG_X86_64 + c->x86_clflush_size = 64; +#else c->x86_clflush_size = 32; +#endif + c->x86_cache_alignment = c->x86_clflush_size; + + memset(&c->x86_capability, 0, sizeof c->x86_capability); + c->extended_cpuid_level = 0; + + if (!have_cpuid_p()) + identify_cpu_without_cpuid(c); + /* cyrix could have cpuid enabled via c_identify()*/ if (!have_cpuid_p()) return; cpu_detect(c); - get_cpu_vendor(c, 1); + get_cpu_vendor(c); + + get_cpu_cap(c); - early_get_cap(c); + if (this_cpu->c_early_init) + this_cpu->c_early_init(c); - if (c->x86_vendor != X86_VENDOR_UNKNOWN && - cpu_devs[c->x86_vendor]->c_early_init) - cpu_devs[c->x86_vendor]->c_early_init(c); + validate_pat_support(c); +} + +void __init early_cpu_init(void) +{ + struct cpu_dev **cdev; + int count = 0; + + printk("KERNEL supported cpus:\n"); + for (cdev = __x86_cpu_dev_start; cdev < __x86_cpu_dev_end; cdev++) { + struct cpu_dev *cpudev = *cdev; + unsigned int j; + + if (count >= X86_VENDOR_NUM) + break; + cpu_devs[count] = cpudev; + count++; + + for (j = 0; j < 2; j++) { + if (!cpudev->c_ident[j]) + continue; + printk(" %s %s\n", cpudev->c_vendor, + cpudev->c_ident[j]); + } + } + + early_identify_cpu(&boot_cpu_data); } /* @@ -357,86 +592,41 @@ static void __cpuinit detect_nopl(struct cpuinfo_x86 *c) static void __cpuinit generic_identify(struct cpuinfo_x86 *c) { - u32 tfms, xlvl; - unsigned int ebx; - - if (have_cpuid_p()) { - /* Get vendor name */ - cpuid(0x00000000, (unsigned int *)&c->cpuid_level, - (unsigned int *)&c->x86_vendor_id[0], - (unsigned int *)&c->x86_vendor_id[8], - (unsigned int *)&c->x86_vendor_id[4]); - - get_cpu_vendor(c, 0); - /* Initialize the standard set of capabilities */ - /* Note that the vendor-specific code below might override */ - /* Intel-defined flags: level 0x00000001 */ - if (c->cpuid_level >= 0x00000001) { - u32 capability, excap; - cpuid(0x00000001, &tfms, &ebx, &excap, &capability); - c->x86_capability[0] = capability; - c->x86_capability[4] = excap; - c->x86 = (tfms >> 8) & 15; - c->x86_model = (tfms >> 4) & 15; - if (c->x86 == 0xf) - c->x86 += (tfms >> 20) & 0xff; - if (c->x86 >= 0x6) - c->x86_model += ((tfms >> 16) & 0xF) << 4; - c->x86_mask = tfms & 15; - c->initial_apicid = (ebx >> 24) & 0xFF; -#ifdef CONFIG_X86_HT - c->apicid = phys_pkg_id(c->initial_apicid, 0); - c->phys_proc_id = c->initial_apicid; -#else - c->apicid = c->initial_apicid; -#endif - if (test_cpu_cap(c, X86_FEATURE_CLFLSH)) - c->x86_clflush_size = ((ebx >> 8) & 0xff) * 8; - } else { - /* Have CPUID level 0 only - unheard of */ - c->x86 = 4; - } + c->extended_cpuid_level = 0; - /* AMD-defined flags: level 0x80000001 */ - xlvl = cpuid_eax(0x80000000); - if ((xlvl & 0xffff0000) == 0x80000000) { - if (xlvl >= 0x80000001) { - c->x86_capability[1] = cpuid_edx(0x80000001); - c->x86_capability[6] = cpuid_ecx(0x80000001); - } - if (xlvl >= 0x80000004) - get_model_name(c); /* Default name */ - } + if (!have_cpuid_p()) + identify_cpu_without_cpuid(c); - init_scattered_cpuid_features(c); - detect_nopl(c); - } -} + /* cyrix could have cpuid enabled via c_identify()*/ + if (!have_cpuid_p()) + return; -static void __cpuinit squash_the_stupid_serial_number(struct cpuinfo_x86 *c) -{ - if (cpu_has(c, X86_FEATURE_PN) && disable_x86_serial_nr) { - /* Disable processor serial number */ - unsigned long lo, hi; - rdmsr(MSR_IA32_BBL_CR_CTL, lo, hi); - lo |= 0x200000; - wrmsr(MSR_IA32_BBL_CR_CTL, lo, hi); - printk(KERN_NOTICE "CPU serial number disabled.\n"); - clear_cpu_cap(c, X86_FEATURE_PN); + cpu_detect(c); - /* Disabling the serial number may affect the cpuid level */ - c->cpuid_level = cpuid_eax(0); - } -} + get_cpu_vendor(c); -static int __init x86_serial_nr_setup(char *s) -{ - disable_x86_serial_nr = 0; - return 1; -} -__setup("serialnumber", x86_serial_nr_setup); + get_cpu_cap(c); + + if (c->cpuid_level >= 0x00000001) { + c->initial_apicid = (cpuid_ebx(1) >> 24) & 0xFF; +#ifdef CONFIG_X86_32 +# ifdef CONFIG_X86_HT + c->apicid = phys_pkg_id(c->initial_apicid, 0); +# else + c->apicid = c->initial_apicid; +# endif +#endif +#ifdef CONFIG_X86_HT + c->phys_proc_id = c->initial_apicid; +#endif + } + get_model_name(c); /* Default name */ + + init_scattered_cpuid_features(c); + detect_nopl(c); +} /* * This does the hard work of actually picking apart the CPU stuff... @@ -448,30 +638,29 @@ static void __cpuinit identify_cpu(struct cpuinfo_x86 *c) c->loops_per_jiffy = loops_per_jiffy; c->x86_cache_size = -1; c->x86_vendor = X86_VENDOR_UNKNOWN; - c->cpuid_level = -1; /* CPUID not detected */ c->x86_model = c->x86_mask = 0; /* So far unknown... */ c->x86_vendor_id[0] = '\0'; /* Unset */ c->x86_model_id[0] = '\0'; /* Unset */ c->x86_max_cores = 1; + c->x86_coreid_bits = 0; +#ifdef CONFIG_X86_64 + c->x86_clflush_size = 64; +#else + c->cpuid_level = -1; /* CPUID not detected */ c->x86_clflush_size = 32; +#endif + c->x86_cache_alignment = c->x86_clflush_size; memset(&c->x86_capability, 0, sizeof c->x86_capability); - if (!have_cpuid_p()) { - /* - * First of all, decide if this is a 486 or higher - * It's a 486 if we can modify the AC flag - */ - if (flag_is_changeable_p(X86_EFLAGS_AC)) - c->x86 = 4; - else - c->x86 = 3; - } - generic_identify(c); if (this_cpu->c_identify) this_cpu->c_identify(c); +#ifdef CONFIG_X86_64 + c->apicid = phys_pkg_id(0); +#endif + /* * Vendor-specific initialization. In this section we * canonicalize the feature flags, meaning if there are @@ -505,6 +694,10 @@ static void __cpuinit identify_cpu(struct cpuinfo_x86 *c) c->x86, c->x86_model); } +#ifdef CONFIG_X86_64 + detect_ht(c); +#endif + /* * On SMP, boot_cpu_data holds the common feature set between * all CPUs; so make sure that we indicate which features are @@ -513,7 +706,7 @@ static void __cpuinit identify_cpu(struct cpuinfo_x86 *c) */ if (c != &boot_cpu_data) { /* AND the already accumulated flags with these */ - for (i = 0 ; i < NCAPINTS ; i++) + for (i = 0; i < NCAPINTS; i++) boot_cpu_data.x86_capability[i] &= c->x86_capability[i]; } @@ -521,72 +714,91 @@ static void __cpuinit identify_cpu(struct cpuinfo_x86 *c) for (i = 0; i < NCAPINTS; i++) c->x86_capability[i] &= ~cleared_cpu_caps[i]; +#ifdef CONFIG_X86_MCE /* Init Machine Check Exception if available. */ mcheck_init(c); +#endif select_idle_routine(c); + +#if defined(CONFIG_NUMA) && defined(CONFIG_X86_64) + numa_add_cpu(smp_processor_id()); +#endif } +#ifdef CONFIG_X86_64 +static void vgetcpu_set_mode(void) +{ + if (cpu_has(&boot_cpu_data, X86_FEATURE_RDTSCP)) + vgetcpu_mode = VGETCPU_RDTSCP; + else + vgetcpu_mode = VGETCPU_LSL; +} +#endif + void __init identify_boot_cpu(void) { identify_cpu(&boot_cpu_data); +#ifdef CONFIG_X86_32 sysenter_setup(); enable_sep_cpu(); +#else + vgetcpu_set_mode(); +#endif } void __cpuinit identify_secondary_cpu(struct cpuinfo_x86 *c) { BUG_ON(c == &boot_cpu_data); identify_cpu(c); +#ifdef CONFIG_X86_32 enable_sep_cpu(); +#endif mtrr_ap_init(); } -#ifdef CONFIG_X86_HT -void __cpuinit detect_ht(struct cpuinfo_x86 *c) -{ - u32 eax, ebx, ecx, edx; - int index_msb, core_bits; - - cpuid(1, &eax, &ebx, &ecx, &edx); - - if (!cpu_has(c, X86_FEATURE_HT) || cpu_has(c, X86_FEATURE_CMP_LEGACY)) - return; - - smp_num_siblings = (ebx & 0xff0000) >> 16; +struct msr_range { + unsigned min; + unsigned max; +}; - if (smp_num_siblings == 1) { - printk(KERN_INFO "CPU: Hyper-Threading is disabled\n"); - } else if (smp_num_siblings > 1) { +static struct msr_range msr_range_array[] __cpuinitdata = { + { 0x00000000, 0x00000418}, + { 0xc0000000, 0xc000040b}, + { 0xc0010000, 0xc0010142}, + { 0xc0011000, 0xc001103b}, +}; - if (smp_num_siblings > NR_CPUS) { - printk(KERN_WARNING "CPU: Unsupported number of the " - "siblings %d", smp_num_siblings); - smp_num_siblings = 1; - return; +static void __cpuinit print_cpu_msr(void) +{ + unsigned index; + u64 val; + int i; + unsigned index_min, index_max; + + for (i = 0; i < ARRAY_SIZE(msr_range_array); i++) { + index_min = msr_range_array[i].min; + index_max = msr_range_array[i].max; + for (index = index_min; index < index_max; index++) { + if (rdmsrl_amd_safe(index, &val)) + continue; + printk(KERN_INFO " MSR%08x: %016llx\n", index, val); } + } +} - index_msb = get_count_order(smp_num_siblings); - c->phys_proc_id = phys_pkg_id(c->initial_apicid, index_msb); - - printk(KERN_INFO "CPU: Physical Processor ID: %d\n", - c->phys_proc_id); - - smp_num_siblings = smp_num_siblings / c->x86_max_cores; - - index_msb = get_count_order(smp_num_siblings) ; - - core_bits = get_count_order(c->x86_max_cores); +static int show_msr __cpuinitdata; +static __init int setup_show_msr(char *arg) +{ + int num; - c->cpu_core_id = phys_pkg_id(c->initial_apicid, index_msb) & - ((1 << core_bits) - 1); + get_option(&arg, &num); - if (c->x86_max_cores > 1) - printk(KERN_INFO "CPU: Processor Core ID: %d\n", - c->cpu_core_id); - } + if (num > 0) + show_msr = num; + return 1; } -#endif +__setup("show_msr=", setup_show_msr); static __init int setup_noclflush(char *arg) { @@ -604,18 +816,26 @@ void __cpuinit print_cpu_info(struct cpuinfo_x86 *c) else if (c->cpuid_level >= 0) vendor = c->x86_vendor_id; - if (vendor && strncmp(c->x86_model_id, vendor, strlen(vendor))) - printk("%s ", vendor); + if (vendor && !strstr(c->x86_model_id, vendor)) + printk(KERN_CONT "%s ", vendor); - if (!c->x86_model_id[0]) - printk("%d86", c->x86); + if (c->x86_model_id[0]) + printk(KERN_CONT "%s", c->x86_model_id); else - printk("%s", c->x86_model_id); + printk(KERN_CONT "%d86", c->x86); if (c->x86_mask || c->cpuid_level >= 0) - printk(" stepping %02x\n", c->x86_mask); + printk(KERN_CONT " stepping %02x\n", c->x86_mask); else - printk("\n"); + printk(KERN_CONT "\n"); + +#ifdef CONFIG_SMP + if (c->cpu_index < show_msr) + print_cpu_msr(); +#else + if (show_msr) + print_cpu_msr(); +#endif } static __init int setup_disablecpuid(char *arg) @@ -631,19 +851,89 @@ __setup("clearcpuid=", setup_disablecpuid); cpumask_t cpu_initialized __cpuinitdata = CPU_MASK_NONE; -void __init early_cpu_init(void) +#ifdef CONFIG_X86_64 +struct x8664_pda **_cpu_pda __read_mostly; +EXPORT_SYMBOL(_cpu_pda); + +struct desc_ptr idt_descr = { 256 * 16 - 1, (unsigned long) idt_table }; + +char boot_cpu_stack[IRQSTACKSIZE] __page_aligned_bss; + +void __cpuinit pda_init(int cpu) { - struct cpu_vendor_dev *cvdev; + struct x8664_pda *pda = cpu_pda(cpu); + + /* Setup up data that may be needed in __get_free_pages early */ + loadsegment(fs, 0); + loadsegment(gs, 0); + /* Memory clobbers used to order PDA accessed */ + mb(); + wrmsrl(MSR_GS_BASE, pda); + mb(); + + pda->cpunumber = cpu; + pda->irqcount = -1; + pda->kernelstack = (unsigned long)stack_thread_info() - + PDA_STACKOFFSET + THREAD_SIZE; + pda->active_mm = &init_mm; + pda->mmu_state = 0; + + if (cpu == 0) { + /* others are initialized in smpboot.c */ + pda->pcurrent = &init_task; + pda->irqstackptr = boot_cpu_stack; + pda->irqstackptr += IRQSTACKSIZE - 64; + } else { + if (!pda->irqstackptr) { + pda->irqstackptr = (char *) + __get_free_pages(GFP_ATOMIC, IRQSTACK_ORDER); + if (!pda->irqstackptr) + panic("cannot allocate irqstack for cpu %d", + cpu); + pda->irqstackptr += IRQSTACKSIZE - 64; + } + + if (pda->nodenumber == 0 && cpu_to_node(cpu) != NUMA_NO_NODE) + pda->nodenumber = cpu_to_node(cpu); + } +} + +char boot_exception_stacks[(N_EXCEPTION_STACKS - 1) * EXCEPTION_STKSZ + + DEBUG_STKSZ] __page_aligned_bss; - for (cvdev = __x86cpuvendor_start ; - cvdev < __x86cpuvendor_end ; - cvdev++) - cpu_devs[cvdev->vendor] = cvdev->cpu_dev; +extern asmlinkage void ignore_sysret(void); - early_cpu_detect(); - validate_pat_support(&boot_cpu_data); +/* May not be marked __init: used by software suspend */ +void syscall_init(void) +{ + /* + * LSTAR and STAR live in a bit strange symbiosis. + * They both write to the same internal register. STAR allows to + * set CS/DS but only a 32bit target. LSTAR sets the 64bit rip. + */ + wrmsrl(MSR_STAR, ((u64)__USER32_CS)<<48 | ((u64)__KERNEL_CS)<<32); + wrmsrl(MSR_LSTAR, system_call); + wrmsrl(MSR_CSTAR, ignore_sysret); + +#ifdef CONFIG_IA32_EMULATION + syscall32_cpu_init(); +#endif + + /* Flags to clear on syscall */ + wrmsrl(MSR_SYSCALL_MASK, + X86_EFLAGS_TF|X86_EFLAGS_DF|X86_EFLAGS_IF|X86_EFLAGS_IOPL); } +unsigned long kernel_eflags; + +/* + * Copies of the original ist values from the tss are only accessed during + * debugging, no special alignment required. + */ +DEFINE_PER_CPU(struct orig_ist, orig_ist); + +#else + /* Make sure %fs is initialized properly in idle threads */ struct pt_regs * __cpuinit idle_regs(struct pt_regs *regs) { @@ -651,25 +941,136 @@ struct pt_regs * __cpuinit idle_regs(struct pt_regs *regs) regs->fs = __KERNEL_PERCPU; return regs; } - -/* Current gdt points %fs at the "master" per-cpu area: after this, - * it's on the real one. */ -void switch_to_new_gdt(void) -{ - struct desc_ptr gdt_descr; - - gdt_descr.address = (long)get_cpu_gdt_table(smp_processor_id()); - gdt_descr.size = GDT_SIZE - 1; - load_gdt(&gdt_descr); - asm("mov %0, %%fs" : : "r" (__KERNEL_PERCPU) : "memory"); -} +#endif /* * cpu_init() initializes state that is per-CPU. Some data is already * initialized (naturally) in the bootstrap process, such as the GDT * and IDT. We reload them nevertheless, this function acts as a * 'CPU state barrier', nothing should get across. + * A lot of state is already set up in PDA init for 64 bit */ +#ifdef CONFIG_X86_64 +void __cpuinit cpu_init(void) +{ + int cpu = stack_smp_processor_id(); + struct tss_struct *t = &per_cpu(init_tss, cpu); + struct orig_ist *orig_ist = &per_cpu(orig_ist, cpu); + unsigned long v; + char *estacks = NULL; + struct task_struct *me; + int i; + + /* CPU 0 is initialised in head64.c */ + if (cpu != 0) + pda_init(cpu); + else + estacks = boot_exception_stacks; + + me = current; + + if (cpu_test_and_set(cpu, cpu_initialized)) + panic("CPU#%d already initialized!\n", cpu); + + printk(KERN_INFO "Initializing CPU#%d\n", cpu); + + clear_in_cr4(X86_CR4_VME|X86_CR4_PVI|X86_CR4_TSD|X86_CR4_DE); + + /* + * Initialize the per-CPU GDT with the boot GDT, + * and set up the GDT descriptor: + */ + + switch_to_new_gdt(); + load_idt((const struct desc_ptr *)&idt_descr); + + memset(me->thread.tls_array, 0, GDT_ENTRY_TLS_ENTRIES * 8); + syscall_init(); + + wrmsrl(MSR_FS_BASE, 0); + wrmsrl(MSR_KERNEL_GS_BASE, 0); + barrier(); + + check_efer(); + if (cpu != 0 && x2apic) + enable_x2apic(); + + /* + * set up and load the per-CPU TSS + */ + if (!orig_ist->ist[0]) { + static const unsigned int order[N_EXCEPTION_STACKS] = { + [0 ... N_EXCEPTION_STACKS - 1] = EXCEPTION_STACK_ORDER, + [DEBUG_STACK - 1] = DEBUG_STACK_ORDER + }; + for (v = 0; v < N_EXCEPTION_STACKS; v++) { + if (cpu) { + estacks = (char *)__get_free_pages(GFP_ATOMIC, order[v]); + if (!estacks) + panic("Cannot allocate exception " + "stack %ld %d\n", v, cpu); + } + estacks += PAGE_SIZE << order[v]; + orig_ist->ist[v] = t->x86_tss.ist[v] = + (unsigned long)estacks; + } + } + + t->x86_tss.io_bitmap_base = offsetof(struct tss_struct, io_bitmap); + /* + * <= is required because the CPU will access up to + * 8 bits beyond the end of the IO permission bitmap. + */ + for (i = 0; i <= IO_BITMAP_LONGS; i++) + t->io_bitmap[i] = ~0UL; + + atomic_inc(&init_mm.mm_count); + me->active_mm = &init_mm; + if (me->mm) + BUG(); + enter_lazy_tlb(&init_mm, me); + + load_sp0(t, ¤t->thread); + set_tss_desc(cpu, t); + load_TR_desc(); + load_LDT(&init_mm.context); + +#ifdef CONFIG_KGDB + /* + * If the kgdb is connected no debug regs should be altered. This + * is only applicable when KGDB and a KGDB I/O module are built + * into the kernel and you are using early debugging with + * kgdbwait. KGDB will control the kernel HW breakpoint registers. + */ + if (kgdb_connected && arch_kgdb_ops.correct_hw_break) + arch_kgdb_ops.correct_hw_break(); + else { +#endif + /* + * Clear all 6 debug registers: + */ + + set_debugreg(0UL, 0); + set_debugreg(0UL, 1); + set_debugreg(0UL, 2); + set_debugreg(0UL, 3); + set_debugreg(0UL, 6); + set_debugreg(0UL, 7); +#ifdef CONFIG_KGDB + /* If the kgdb is connected no debug regs should be altered. */ + } +#endif + + fpu_init(); + + raw_local_save_flags(kernel_eflags); + + if (is_uv_system()) + uv_cpu_init(); +} + +#else + void __cpuinit cpu_init(void) { int cpu = smp_processor_id(); @@ -723,19 +1124,21 @@ void __cpuinit cpu_init(void) /* * Force FPU initialization: */ - current_thread_info()->status = 0; + if (cpu_has_xsave) + current_thread_info()->status = TS_XSAVE; + else + current_thread_info()->status = 0; clear_used_math(); mxcsr_feature_mask_init(); -} -#ifdef CONFIG_HOTPLUG_CPU -void __cpuinit cpu_uninit(void) -{ - int cpu = raw_smp_processor_id(); - cpu_clear(cpu, cpu_initialized); + /* + * Boot processor to setup the FP and extended state context info. + */ + if (!smp_processor_id()) + init_thread_xstate(); - /* lazy TLB state */ - per_cpu(cpu_tlbstate, cpu).state = 0; - per_cpu(cpu_tlbstate, cpu).active_mm = &init_mm; + xsave_init(); } + + #endif diff --git a/arch/x86/kernel/cpu/common_64.c b/arch/x86/kernel/cpu/common_64.c deleted file mode 100644 index 305b465889b..00000000000 --- a/arch/x86/kernel/cpu/common_64.c +++ /dev/null @@ -1,763 +0,0 @@ -#include <linux/init.h> -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/string.h> -#include <linux/bootmem.h> -#include <linux/bitops.h> -#include <linux/module.h> -#include <linux/kgdb.h> -#include <linux/topology.h> -#include <linux/delay.h> -#include <linux/smp.h> -#include <linux/percpu.h> -#include <asm/i387.h> -#include <asm/msr.h> -#include <asm/io.h> -#include <asm/linkage.h> -#include <asm/mmu_context.h> -#include <asm/mtrr.h> -#include <asm/mce.h> -#include <asm/pat.h> -#include <asm/asm.h> -#include <asm/numa.h> -#ifdef CONFIG_X86_LOCAL_APIC -#include <asm/mpspec.h> -#include <asm/apic.h> -#include <mach_apic.h> -#endif -#include <asm/pda.h> -#include <asm/pgtable.h> -#include <asm/processor.h> -#include <asm/desc.h> -#include <asm/atomic.h> -#include <asm/proto.h> -#include <asm/sections.h> -#include <asm/setup.h> -#include <asm/genapic.h> - -#include "cpu.h" - -/* We need valid kernel segments for data and code in long mode too - * IRET will check the segment types kkeil 2000/10/28 - * Also sysret mandates a special GDT layout - */ -/* The TLS descriptors are currently at a different place compared to i386. - Hopefully nobody expects them at a fixed place (Wine?) */ -DEFINE_PER_CPU(struct gdt_page, gdt_page) = { .gdt = { - [GDT_ENTRY_KERNEL32_CS] = { { { 0x0000ffff, 0x00cf9b00 } } }, - [GDT_ENTRY_KERNEL_CS] = { { { 0x0000ffff, 0x00af9b00 } } }, - [GDT_ENTRY_KERNEL_DS] = { { { 0x0000ffff, 0x00cf9300 } } }, - [GDT_ENTRY_DEFAULT_USER32_CS] = { { { 0x0000ffff, 0x00cffb00 } } }, - [GDT_ENTRY_DEFAULT_USER_DS] = { { { 0x0000ffff, 0x00cff300 } } }, - [GDT_ENTRY_DEFAULT_USER_CS] = { { { 0x0000ffff, 0x00affb00 } } }, -} }; -EXPORT_PER_CPU_SYMBOL_GPL(gdt_page); - -__u32 cleared_cpu_caps[NCAPINTS] __cpuinitdata; - -/* Current gdt points %fs at the "master" per-cpu area: after this, - * it's on the real one. */ -void switch_to_new_gdt(void) -{ - struct desc_ptr gdt_descr; - - gdt_descr.address = (long)get_cpu_gdt_table(smp_processor_id()); - gdt_descr.size = GDT_SIZE - 1; - load_gdt(&gdt_descr); -} - -struct cpu_dev *cpu_devs[X86_VENDOR_NUM] = {}; - -static void __cpuinit default_init(struct cpuinfo_x86 *c) -{ - display_cacheinfo(c); -} - -static struct cpu_dev __cpuinitdata default_cpu = { - .c_init = default_init, - .c_vendor = "Unknown", -}; -static struct cpu_dev *this_cpu __cpuinitdata = &default_cpu; - -int __cpuinit get_model_name(struct cpuinfo_x86 *c) -{ - unsigned int *v; - - if (c->extended_cpuid_level < 0x80000004) - return 0; - - v = (unsigned int *) c->x86_model_id; - cpuid(0x80000002, &v[0], &v[1], &v[2], &v[3]); - cpuid(0x80000003, &v[4], &v[5], &v[6], &v[7]); - cpuid(0x80000004, &v[8], &v[9], &v[10], &v[11]); - c->x86_model_id[48] = 0; - return 1; -} - - -void __cpuinit display_cacheinfo(struct cpuinfo_x86 *c) -{ - unsigned int n, dummy, ebx, ecx, edx; - - n = c->extended_cpuid_level; - - if (n >= 0x80000005) { - cpuid(0x80000005, &dummy, &ebx, &ecx, &edx); - printk(KERN_INFO "CPU: L1 I Cache: %dK (%d bytes/line), " - "D cache %dK (%d bytes/line)\n", - edx>>24, edx&0xFF, ecx>>24, ecx&0xFF); - c->x86_cache_size = (ecx>>24) + (edx>>24); - /* On K8 L1 TLB is inclusive, so don't count it */ - c->x86_tlbsize = 0; - } - - if (n >= 0x80000006) { - cpuid(0x80000006, &dummy, &ebx, &ecx, &edx); - ecx = cpuid_ecx(0x80000006); - c->x86_cache_size = ecx >> 16; - c->x86_tlbsize += ((ebx >> 16) & 0xfff) + (ebx & 0xfff); - - printk(KERN_INFO "CPU: L2 Cache: %dK (%d bytes/line)\n", - c->x86_cache_size, ecx & 0xFF); - } -} - -void __cpuinit detect_ht(struct cpuinfo_x86 *c) -{ -#ifdef CONFIG_SMP - u32 eax, ebx, ecx, edx; - int index_msb, core_bits; - - cpuid(1, &eax, &ebx, &ecx, &edx); - - - if (!cpu_has(c, X86_FEATURE_HT)) - return; - if (cpu_has(c, X86_FEATURE_CMP_LEGACY)) - goto out; - - smp_num_siblings = (ebx & 0xff0000) >> 16; - - if (smp_num_siblings == 1) { - printk(KERN_INFO "CPU: Hyper-Threading is disabled\n"); - } else if (smp_num_siblings > 1) { - - if (smp_num_siblings > NR_CPUS) { - printk(KERN_WARNING "CPU: Unsupported number of " - "siblings %d", smp_num_siblings); - smp_num_siblings = 1; - return; - } - - index_msb = get_count_order(smp_num_siblings); - c->phys_proc_id = phys_pkg_id(index_msb); - - smp_num_siblings = smp_num_siblings / c->x86_max_cores; - - index_msb = get_count_order(smp_num_siblings); - - core_bits = get_count_order(c->x86_max_cores); - - c->cpu_core_id = phys_pkg_id(index_msb) & - ((1 << core_bits) - 1); - } -out: - if ((c->x86_max_cores * smp_num_siblings) > 1) { - printk(KERN_INFO "CPU: Physical Processor ID: %d\n", - c->phys_proc_id); - printk(KERN_INFO "CPU: Processor Core ID: %d\n", - c->cpu_core_id); - } - -#endif -} - -static void __cpuinit get_cpu_vendor(struct cpuinfo_x86 *c) -{ - char *v = c->x86_vendor_id; - int i; - static int printed; - - for (i = 0; i < X86_VENDOR_NUM; i++) { - if (cpu_devs[i]) { - if (!strcmp(v, cpu_devs[i]->c_ident[0]) || - (cpu_devs[i]->c_ident[1] && - !strcmp(v, cpu_devs[i]->c_ident[1]))) { - c->x86_vendor = i; - this_cpu = cpu_devs[i]; - return; - } - } - } - if (!printed) { - printed++; - printk(KERN_ERR "CPU: Vendor unknown, using generic init.\n"); - printk(KERN_ERR "CPU: Your system may be unstable.\n"); - } - c->x86_vendor = X86_VENDOR_UNKNOWN; -} - -static void __init early_cpu_support_print(void) -{ - int i,j; - struct cpu_dev *cpu_devx; - - printk("KERNEL supported cpus:\n"); - for (i = 0; i < X86_VENDOR_NUM; i++) { - cpu_devx = cpu_devs[i]; - if (!cpu_devx) - continue; - for (j = 0; j < 2; j++) { - if (!cpu_devx->c_ident[j]) - continue; - printk(" %s %s\n", cpu_devx->c_vendor, - cpu_devx->c_ident[j]); - } - } -} - -/* - * The NOPL instruction is supposed to exist on all CPUs with - * family >= 6, unfortunately, that's not true in practice because - * of early VIA chips and (more importantly) broken virtualizers that - * are not easy to detect. Hence, probe for it based on first - * principles. - * - * Note: no 64-bit chip is known to lack these, but put the code here - * for consistency with 32 bits, and to make it utterly trivial to - * diagnose the problem should it ever surface. - */ -static void __cpuinit detect_nopl(struct cpuinfo_x86 *c) -{ - const u32 nopl_signature = 0x888c53b1; /* Random number */ - u32 has_nopl = nopl_signature; - - clear_cpu_cap(c, X86_FEATURE_NOPL); - if (c->x86 >= 6) { - asm volatile("\n" - "1: .byte 0x0f,0x1f,0xc0\n" /* nopl %eax */ - "2:\n" - " .section .fixup,\"ax\"\n" - "3: xor %0,%0\n" - " jmp 2b\n" - " .previous\n" - _ASM_EXTABLE(1b,3b) - : "+a" (has_nopl)); - - if (has_nopl == nopl_signature) - set_cpu_cap(c, X86_FEATURE_NOPL); - } -} - -static void __cpuinit early_identify_cpu(struct cpuinfo_x86 *c); - -void __init early_cpu_init(void) -{ - struct cpu_vendor_dev *cvdev; - - for (cvdev = __x86cpuvendor_start ; - cvdev < __x86cpuvendor_end ; - cvdev++) - cpu_devs[cvdev->vendor] = cvdev->cpu_dev; - early_cpu_support_print(); - early_identify_cpu(&boot_cpu_data); -} - -/* Do some early cpuid on the boot CPU to get some parameter that are - needed before check_bugs. Everything advanced is in identify_cpu - below. */ -static void __cpuinit early_identify_cpu(struct cpuinfo_x86 *c) -{ - u32 tfms, xlvl; - - c->loops_per_jiffy = loops_per_jiffy; - c->x86_cache_size = -1; - c->x86_vendor = X86_VENDOR_UNKNOWN; - c->x86_model = c->x86_mask = 0; /* So far unknown... */ - c->x86_vendor_id[0] = '\0'; /* Unset */ - c->x86_model_id[0] = '\0'; /* Unset */ - c->x86_clflush_size = 64; - c->x86_cache_alignment = c->x86_clflush_size; - c->x86_max_cores = 1; - c->x86_coreid_bits = 0; - c->extended_cpuid_level = 0; - memset(&c->x86_capability, 0, sizeof c->x86_capability); - - /* Get vendor name */ - cpuid(0x00000000, (unsigned int *)&c->cpuid_level, - (unsigned int *)&c->x86_vendor_id[0], - (unsigned int *)&c->x86_vendor_id[8], - (unsigned int *)&c->x86_vendor_id[4]); - - get_cpu_vendor(c); - - /* Initialize the standard set of capabilities */ - /* Note that the vendor-specific code below might override */ - - /* Intel-defined flags: level 0x00000001 */ - if (c->cpuid_level >= 0x00000001) { - __u32 misc; - cpuid(0x00000001, &tfms, &misc, &c->x86_capability[4], - &c->x86_capability[0]); - c->x86 = (tfms >> 8) & 0xf; - c->x86_model = (tfms >> 4) & 0xf; - c->x86_mask = tfms & 0xf; - if (c->x86 == 0xf) - c->x86 += (tfms >> 20) & 0xff; - if (c->x86 >= 0x6) - c->x86_model += ((tfms >> 16) & 0xF) << 4; - if (test_cpu_cap(c, X86_FEATURE_CLFLSH)) - c->x86_clflush_size = ((misc >> 8) & 0xff) * 8; - } else { - /* Have CPUID level 0 only - unheard of */ - c->x86 = 4; - } - - c->initial_apicid = (cpuid_ebx(1) >> 24) & 0xff; -#ifdef CONFIG_SMP - c->phys_proc_id = c->initial_apicid; -#endif - /* AMD-defined flags: level 0x80000001 */ - xlvl = cpuid_eax(0x80000000); - c->extended_cpuid_level = xlvl; - if ((xlvl & 0xffff0000) == 0x80000000) { - if (xlvl >= 0x80000001) { - c->x86_capability[1] = cpuid_edx(0x80000001); - c->x86_capability[6] = cpuid_ecx(0x80000001); - } - if (xlvl >= 0x80000004) - get_model_name(c); /* Default name */ - } - - /* Transmeta-defined flags: level 0x80860001 */ - xlvl = cpuid_eax(0x80860000); - if ((xlvl & 0xffff0000) == 0x80860000) { - /* Don't set x86_cpuid_level here for now to not confuse. */ - if (xlvl >= 0x80860001) - c->x86_capability[2] = cpuid_edx(0x80860001); - } - - if (c->extended_cpuid_level >= 0x80000007) - c->x86_power = cpuid_edx(0x80000007); - - if (c->extended_cpuid_level >= 0x80000008) { - u32 eax = cpuid_eax(0x80000008); - - c->x86_virt_bits = (eax >> 8) & 0xff; - c->x86_phys_bits = eax & 0xff; - } - - detect_nopl(c); - - if (c->x86_vendor != X86_VENDOR_UNKNOWN && - cpu_devs[c->x86_vendor]->c_early_init) - cpu_devs[c->x86_vendor]->c_early_init(c); - - validate_pat_support(c); -} - -/* - * This does the hard work of actually picking apart the CPU stuff... - */ -static void __cpuinit identify_cpu(struct cpuinfo_x86 *c) -{ - int i; - - early_identify_cpu(c); - - init_scattered_cpuid_features(c); - - c->apicid = phys_pkg_id(0); - - /* - * Vendor-specific initialization. In this section we - * canonicalize the feature flags, meaning if there are - * features a certain CPU supports which CPUID doesn't - * tell us, CPUID claiming incorrect flags, or other bugs, - * we handle them here. - * - * At the end of this section, c->x86_capability better - * indicate the features this CPU genuinely supports! - */ - if (this_cpu->c_init) - this_cpu->c_init(c); - - detect_ht(c); - - /* - * On SMP, boot_cpu_data holds the common feature set between - * all CPUs; so make sure that we indicate which features are - * common between the CPUs. The first time this routine gets - * executed, c == &boot_cpu_data. - */ - if (c != &boot_cpu_data) { - /* AND the already accumulated flags with these */ - for (i = 0; i < NCAPINTS; i++) - boot_cpu_data.x86_capability[i] &= c->x86_capability[i]; - } - - /* Clear all flags overriden by options */ - for (i = 0; i < NCAPINTS; i++) - c->x86_capability[i] &= ~cleared_cpu_caps[i]; - -#ifdef CONFIG_X86_MCE - mcheck_init(c); -#endif - select_idle_routine(c); - -#ifdef CONFIG_NUMA - numa_add_cpu(smp_processor_id()); -#endif - -} - -void __cpuinit identify_boot_cpu(void) -{ - identify_cpu(&boot_cpu_data); -} - -void __cpuinit identify_secondary_cpu(struct cpuinfo_x86 *c) -{ - BUG_ON(c == &boot_cpu_data); - identify_cpu(c); - mtrr_ap_init(); -} - -static __init int setup_noclflush(char *arg) -{ - setup_clear_cpu_cap(X86_FEATURE_CLFLSH); - return 1; -} -__setup("noclflush", setup_noclflush); - -struct msr_range { - unsigned min; - unsigned max; -}; - -static struct msr_range msr_range_array[] __cpuinitdata = { - { 0x00000000, 0x00000418}, - { 0xc0000000, 0xc000040b}, - { 0xc0010000, 0xc0010142}, - { 0xc0011000, 0xc001103b}, -}; - -static void __cpuinit print_cpu_msr(void) -{ - unsigned index; - u64 val; - int i; - unsigned index_min, index_max; - - for (i = 0; i < ARRAY_SIZE(msr_range_array); i++) { - index_min = msr_range_array[i].min; - index_max = msr_range_array[i].max; - for (index = index_min; index < index_max; index++) { - if (rdmsrl_amd_safe(index, &val)) - continue; - printk(KERN_INFO " MSR%08x: %016llx\n", index, val); - } - } -} - -static int show_msr __cpuinitdata; -static __init int setup_show_msr(char *arg) -{ - int num; - - get_option(&arg, &num); - - if (num > 0) - show_msr = num; - return 1; -} -__setup("show_msr=", setup_show_msr); - -void __cpuinit print_cpu_info(struct cpuinfo_x86 *c) -{ - if (c->x86_model_id[0]) - printk(KERN_CONT "%s", c->x86_model_id); - - if (c->x86_mask || c->cpuid_level >= 0) - printk(KERN_CONT " stepping %02x\n", c->x86_mask); - else - printk(KERN_CONT "\n"); - -#ifdef CONFIG_SMP - if (c->cpu_index < show_msr) - print_cpu_msr(); -#else - if (show_msr) - print_cpu_msr(); -#endif -} - -static __init int setup_disablecpuid(char *arg) -{ - int bit; - if (get_option(&arg, &bit) && bit < NCAPINTS*32) - setup_clear_cpu_cap(bit); - else - return 0; - return 1; -} -__setup("clearcpuid=", setup_disablecpuid); - -cpumask_t cpu_initialized __cpuinitdata = CPU_MASK_NONE; - -struct x8664_pda **_cpu_pda __read_mostly; -EXPORT_SYMBOL(_cpu_pda); - -struct desc_ptr idt_descr = { 256 * 16 - 1, (unsigned long) idt_table }; - -char boot_cpu_stack[IRQSTACKSIZE] __page_aligned_bss; - -unsigned long __supported_pte_mask __read_mostly = ~0UL; -EXPORT_SYMBOL_GPL(__supported_pte_mask); - -static int do_not_nx __cpuinitdata; - -/* noexec=on|off -Control non executable mappings for 64bit processes. - -on Enable(default) -off Disable -*/ -static int __init nonx_setup(char *str) -{ - if (!str) - return -EINVAL; - if (!strncmp(str, "on", 2)) { - __supported_pte_mask |= _PAGE_NX; - do_not_nx = 0; - } else if (!strncmp(str, "off", 3)) { - do_not_nx = 1; - __supported_pte_mask &= ~_PAGE_NX; - } - return 0; -} -early_param("noexec", nonx_setup); - -int force_personality32; - -/* noexec32=on|off -Control non executable heap for 32bit processes. -To control the stack too use noexec=off - -on PROT_READ does not imply PROT_EXEC for 32bit processes (default) -off PROT_READ implies PROT_EXEC -*/ -static int __init nonx32_setup(char *str) -{ - if (!strcmp(str, "on")) - force_personality32 &= ~READ_IMPLIES_EXEC; - else if (!strcmp(str, "off")) - force_personality32 |= READ_IMPLIES_EXEC; - return 1; -} -__setup("noexec32=", nonx32_setup); - -void pda_init(int cpu) -{ - struct x8664_pda *pda = cpu_pda(cpu); - - /* Setup up data that may be needed in __get_free_pages early */ - loadsegment(fs, 0); - loadsegment(gs, 0); - /* Memory clobbers used to order PDA accessed */ - mb(); - wrmsrl(MSR_GS_BASE, pda); - mb(); - - pda->cpunumber = cpu; - pda->irqcount = -1; - pda->kernelstack = (unsigned long)stack_thread_info() - - PDA_STACKOFFSET + THREAD_SIZE; - pda->active_mm = &init_mm; - pda->mmu_state = 0; - - if (cpu == 0) { - /* others are initialized in smpboot.c */ - pda->pcurrent = &init_task; - pda->irqstackptr = boot_cpu_stack; - pda->irqstackptr += IRQSTACKSIZE - 64; - } else { - if (!pda->irqstackptr) { - pda->irqstackptr = (char *) - __get_free_pages(GFP_ATOMIC, IRQSTACK_ORDER); - if (!pda->irqstackptr) - panic("cannot allocate irqstack for cpu %d", - cpu); - pda->irqstackptr += IRQSTACKSIZE - 64; - } - - if (pda->nodenumber == 0 && cpu_to_node(cpu) != NUMA_NO_NODE) - pda->nodenumber = cpu_to_node(cpu); - } -} - -char boot_exception_stacks[(N_EXCEPTION_STACKS - 1) * EXCEPTION_STKSZ + - DEBUG_STKSZ] __page_aligned_bss; - -extern asmlinkage void ignore_sysret(void); - -/* May not be marked __init: used by software suspend */ -void syscall_init(void) -{ - /* - * LSTAR and STAR live in a bit strange symbiosis. - * They both write to the same internal register. STAR allows to - * set CS/DS but only a 32bit target. LSTAR sets the 64bit rip. - */ - wrmsrl(MSR_STAR, ((u64)__USER32_CS)<<48 | ((u64)__KERNEL_CS)<<32); - wrmsrl(MSR_LSTAR, system_call); - wrmsrl(MSR_CSTAR, ignore_sysret); - -#ifdef CONFIG_IA32_EMULATION - syscall32_cpu_init(); -#endif - - /* Flags to clear on syscall */ - wrmsrl(MSR_SYSCALL_MASK, - X86_EFLAGS_TF|X86_EFLAGS_DF|X86_EFLAGS_IF|X86_EFLAGS_IOPL); -} - -void __cpuinit check_efer(void) -{ - unsigned long efer; - - rdmsrl(MSR_EFER, efer); - if (!(efer & EFER_NX) || do_not_nx) - __supported_pte_mask &= ~_PAGE_NX; -} - -unsigned long kernel_eflags; - -/* - * Copies of the original ist values from the tss are only accessed during - * debugging, no special alignment required. - */ -DEFINE_PER_CPU(struct orig_ist, orig_ist); - -/* - * cpu_init() initializes state that is per-CPU. Some data is already - * initialized (naturally) in the bootstrap process, such as the GDT - * and IDT. We reload them nevertheless, this function acts as a - * 'CPU state barrier', nothing should get across. - * A lot of state is already set up in PDA init. - */ -void __cpuinit cpu_init(void) -{ - int cpu = stack_smp_processor_id(); - struct tss_struct *t = &per_cpu(init_tss, cpu); - struct orig_ist *orig_ist = &per_cpu(orig_ist, cpu); - unsigned long v; - char *estacks = NULL; - struct task_struct *me; - int i; - - /* CPU 0 is initialised in head64.c */ - if (cpu != 0) - pda_init(cpu); - else - estacks = boot_exception_stacks; - - me = current; - - if (cpu_test_and_set(cpu, cpu_initialized)) - panic("CPU#%d already initialized!\n", cpu); - - printk(KERN_INFO "Initializing CPU#%d\n", cpu); - - clear_in_cr4(X86_CR4_VME|X86_CR4_PVI|X86_CR4_TSD|X86_CR4_DE); - - /* - * Initialize the per-CPU GDT with the boot GDT, - * and set up the GDT descriptor: - */ - - switch_to_new_gdt(); - load_idt((const struct desc_ptr *)&idt_descr); - - memset(me->thread.tls_array, 0, GDT_ENTRY_TLS_ENTRIES * 8); - syscall_init(); - - wrmsrl(MSR_FS_BASE, 0); - wrmsrl(MSR_KERNEL_GS_BASE, 0); - barrier(); - - check_efer(); - - /* - * set up and load the per-CPU TSS - */ - if (!orig_ist->ist[0]) { - static const unsigned int order[N_EXCEPTION_STACKS] = { - [0 ... N_EXCEPTION_STACKS - 1] = EXCEPTION_STACK_ORDER, - [DEBUG_STACK - 1] = DEBUG_STACK_ORDER - }; - for (v = 0; v < N_EXCEPTION_STACKS; v++) { - if (cpu) { - estacks = (char *)__get_free_pages(GFP_ATOMIC, order[v]); - if (!estacks) - panic("Cannot allocate exception " - "stack %ld %d\n", v, cpu); - } - estacks += PAGE_SIZE << order[v]; - orig_ist->ist[v] = t->x86_tss.ist[v] = - (unsigned long)estacks; - } - } - - t->x86_tss.io_bitmap_base = offsetof(struct tss_struct, io_bitmap); - /* - * <= is required because the CPU will access up to - * 8 bits beyond the end of the IO permission bitmap. - */ - for (i = 0; i <= IO_BITMAP_LONGS; i++) - t->io_bitmap[i] = ~0UL; - - atomic_inc(&init_mm.mm_count); - me->active_mm = &init_mm; - if (me->mm) - BUG(); - enter_lazy_tlb(&init_mm, me); - - load_sp0(t, ¤t->thread); - set_tss_desc(cpu, t); - load_TR_desc(); - load_LDT(&init_mm.context); - -#ifdef CONFIG_KGDB - /* - * If the kgdb is connected no debug regs should be altered. This - * is only applicable when KGDB and a KGDB I/O module are built - * into the kernel and you are using early debugging with - * kgdbwait. KGDB will control the kernel HW breakpoint registers. - */ - if (kgdb_connected && arch_kgdb_ops.correct_hw_break) - arch_kgdb_ops.correct_hw_break(); - else { -#endif - /* - * Clear all 6 debug registers: - */ - - set_debugreg(0UL, 0); - set_debugreg(0UL, 1); - set_debugreg(0UL, 2); - set_debugreg(0UL, 3); - set_debugreg(0UL, 6); - set_debugreg(0UL, 7); -#ifdef CONFIG_KGDB - /* If the kgdb is connected no debug regs should be altered. */ - } -#endif - - fpu_init(); - - raw_local_save_flags(kernel_eflags); - - if (is_uv_system()) - uv_cpu_init(); -} diff --git a/arch/x86/kernel/cpu/cpu.h b/arch/x86/kernel/cpu/cpu.h index 4d894e8565f..de4094a3921 100644 --- a/arch/x86/kernel/cpu/cpu.h +++ b/arch/x86/kernel/cpu/cpu.h @@ -21,23 +21,16 @@ struct cpu_dev { void (*c_init)(struct cpuinfo_x86 * c); void (*c_identify)(struct cpuinfo_x86 * c); unsigned int (*c_size_cache)(struct cpuinfo_x86 * c, unsigned int size); + int c_x86_vendor; }; -extern struct cpu_dev * cpu_devs [X86_VENDOR_NUM]; +#define cpu_dev_register(cpu_devX) \ + static struct cpu_dev *__cpu_dev_##cpu_devX __used \ + __attribute__((__section__(".x86_cpu_dev.init"))) = \ + &cpu_devX; -struct cpu_vendor_dev { - int vendor; - struct cpu_dev *cpu_dev; -}; - -#define cpu_vendor_dev_register(cpu_vendor_id, cpu_dev) \ - static struct cpu_vendor_dev __cpu_vendor_dev_##cpu_vendor_id __used \ - __attribute__((__section__(".x86cpuvendor.init"))) = \ - { cpu_vendor_id, cpu_dev } - -extern struct cpu_vendor_dev __x86cpuvendor_start[], __x86cpuvendor_end[]; +extern struct cpu_dev *__x86_cpu_dev_start[], *__x86_cpu_dev_end[]; -extern int get_model_name(struct cpuinfo_x86 *c); extern void display_cacheinfo(struct cpuinfo_x86 *c); #endif diff --git a/arch/x86/kernel/cpu/cyrix.c b/arch/x86/kernel/cpu/cyrix.c index 898a5a2002e..ffd0f5ed071 100644 --- a/arch/x86/kernel/cpu/cyrix.c +++ b/arch/x86/kernel/cpu/cyrix.c @@ -121,7 +121,7 @@ static void __cpuinit set_cx86_reorder(void) setCx86(CX86_CCR3, (ccr3 & 0x0f) | 0x10); /* enable MAPEN */ /* Load/Store Serialize to mem access disable (=reorder it) */ - setCx86(CX86_PCR0, getCx86(CX86_PCR0) & ~0x80); + setCx86_old(CX86_PCR0, getCx86_old(CX86_PCR0) & ~0x80); /* set load/store serialize from 1GB to 4GB */ ccr3 |= 0xe0; setCx86(CX86_CCR3, ccr3); @@ -132,11 +132,11 @@ static void __cpuinit set_cx86_memwb(void) printk(KERN_INFO "Enable Memory-Write-back mode on Cyrix/NSC processor.\n"); /* CCR2 bit 2: unlock NW bit */ - setCx86(CX86_CCR2, getCx86(CX86_CCR2) & ~0x04); + setCx86_old(CX86_CCR2, getCx86_old(CX86_CCR2) & ~0x04); /* set 'Not Write-through' */ write_cr0(read_cr0() | X86_CR0_NW); /* CCR2 bit 2: lock NW bit and set WT1 */ - setCx86(CX86_CCR2, getCx86(CX86_CCR2) | 0x14); + setCx86_old(CX86_CCR2, getCx86_old(CX86_CCR2) | 0x14); } /* @@ -150,14 +150,14 @@ static void __cpuinit geode_configure(void) local_irq_save(flags); /* Suspend on halt power saving and enable #SUSP pin */ - setCx86(CX86_CCR2, getCx86(CX86_CCR2) | 0x88); + setCx86_old(CX86_CCR2, getCx86_old(CX86_CCR2) | 0x88); ccr3 = getCx86(CX86_CCR3); setCx86(CX86_CCR3, (ccr3 & 0x0f) | 0x10); /* enable MAPEN */ /* FPU fast, DTE cache, Mem bypass */ - setCx86(CX86_CCR4, getCx86(CX86_CCR4) | 0x38); + setCx86_old(CX86_CCR4, getCx86_old(CX86_CCR4) | 0x38); setCx86(CX86_CCR3, ccr3); /* disable MAPEN */ set_cx86_memwb(); @@ -291,7 +291,7 @@ static void __cpuinit init_cyrix(struct cpuinfo_x86 *c) /* GXm supports extended cpuid levels 'ala' AMD */ if (c->cpuid_level == 2) { /* Enable cxMMX extensions (GX1 Datasheet 54) */ - setCx86(CX86_CCR7, getCx86(CX86_CCR7) | 1); + setCx86_old(CX86_CCR7, getCx86_old(CX86_CCR7) | 1); /* * GXm : 0x30 ... 0x5f GXm datasheet 51 @@ -301,7 +301,6 @@ static void __cpuinit init_cyrix(struct cpuinfo_x86 *c) */ if ((0x30 <= dir1 && dir1 <= 0x6f) || (0x80 <= dir1 && dir1 <= 0x8f)) geode_configure(); - get_model_name(c); /* get CPU marketing name */ return; } else { /* MediaGX */ Cx86_cb[2] = (dir0_lsn & 1) ? '3' : '4'; @@ -314,7 +313,7 @@ static void __cpuinit init_cyrix(struct cpuinfo_x86 *c) if (dir1 > 7) { dir0_msn++; /* M II */ /* Enable MMX extensions (App note 108) */ - setCx86(CX86_CCR7, getCx86(CX86_CCR7)|1); + setCx86_old(CX86_CCR7, getCx86_old(CX86_CCR7)|1); } else { c->coma_bug = 1; /* 6x86MX, it has the bug. */ } @@ -429,7 +428,7 @@ static void __cpuinit cyrix_identify(struct cpuinfo_x86 *c) local_irq_save(flags); ccr3 = getCx86(CX86_CCR3); setCx86(CX86_CCR3, (ccr3 & 0x0f) | 0x10); /* enable MAPEN */ - setCx86(CX86_CCR4, getCx86(CX86_CCR4) | 0x80); /* enable cpuid */ + setCx86_old(CX86_CCR4, getCx86_old(CX86_CCR4) | 0x80); /* enable cpuid */ setCx86(CX86_CCR3, ccr3); /* disable MAPEN */ local_irq_restore(flags); } @@ -442,14 +441,16 @@ static struct cpu_dev cyrix_cpu_dev __cpuinitdata = { .c_early_init = early_init_cyrix, .c_init = init_cyrix, .c_identify = cyrix_identify, + .c_x86_vendor = X86_VENDOR_CYRIX, }; -cpu_vendor_dev_register(X86_VENDOR_CYRIX, &cyrix_cpu_dev); +cpu_dev_register(cyrix_cpu_dev); static struct cpu_dev nsc_cpu_dev __cpuinitdata = { .c_vendor = "NSC", .c_ident = { "Geode by NSC" }, .c_init = init_nsc, + .c_x86_vendor = X86_VENDOR_NSC, }; -cpu_vendor_dev_register(X86_VENDOR_NSC, &nsc_cpu_dev); +cpu_dev_register(nsc_cpu_dev); diff --git a/arch/x86/kernel/cpu/feature_names.c b/arch/x86/kernel/cpu/feature_names.c deleted file mode 100644 index c9017799497..00000000000 --- a/arch/x86/kernel/cpu/feature_names.c +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Strings for the various x86 capability flags. - * - * This file must not contain any executable code. - */ - -#include <asm/cpufeature.h> - -/* - * These flag bits must match the definitions in <asm/cpufeature.h>. - * NULL means this bit is undefined or reserved; either way it doesn't - * have meaning as far as Linux is concerned. Note that it's important - * to realize there is a difference between this table and CPUID -- if - * applications want to get the raw CPUID data, they should access - * /dev/cpu/<cpu_nr>/cpuid instead. - */ -const char * const x86_cap_flags[NCAPINTS*32] = { - /* Intel-defined */ - "fpu", "vme", "de", "pse", "tsc", "msr", "pae", "mce", - "cx8", "apic", NULL, "sep", "mtrr", "pge", "mca", "cmov", - "pat", "pse36", "pn", "clflush", NULL, "dts", "acpi", "mmx", - "fxsr", "sse", "sse2", "ss", "ht", "tm", "ia64", "pbe", - - /* AMD-defined */ - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, "syscall", NULL, NULL, NULL, NULL, - NULL, NULL, NULL, "mp", "nx", NULL, "mmxext", NULL, - NULL, "fxsr_opt", "pdpe1gb", "rdtscp", NULL, "lm", - "3dnowext", "3dnow", - - /* Transmeta-defined */ - "recovery", "longrun", NULL, "lrti", NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - - /* Other (Linux-defined) */ - "cxmmx", "k6_mtrr", "cyrix_arr", "centaur_mcr", - NULL, NULL, NULL, NULL, - "constant_tsc", "up", NULL, "arch_perfmon", - "pebs", "bts", NULL, NULL, - "rep_good", NULL, NULL, NULL, - "nopl", NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - - /* Intel-defined (#2) */ - "pni", NULL, NULL, "monitor", "ds_cpl", "vmx", "smx", "est", - "tm2", "ssse3", "cid", NULL, NULL, "cx16", "xtpr", NULL, - NULL, NULL, "dca", "sse4_1", "sse4_2", NULL, NULL, "popcnt", - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - - /* VIA/Cyrix/Centaur-defined */ - NULL, NULL, "rng", "rng_en", NULL, NULL, "ace", "ace_en", - "ace2", "ace2_en", "phe", "phe_en", "pmm", "pmm_en", NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - - /* AMD-defined (#2) */ - "lahf_lm", "cmp_legacy", "svm", "extapic", - "cr8_legacy", "abm", "sse4a", "misalignsse", - "3dnowprefetch", "osvw", "ibs", "sse5", - "skinit", "wdt", NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - - /* Auxiliary (Linux-defined) */ - "ida", NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, -}; - -const char *const x86_power_flags[32] = { - "ts", /* temperature sensor */ - "fid", /* frequency id control */ - "vid", /* voltage id control */ - "ttp", /* thermal trip */ - "tm", - "stc", - "100mhzsteps", - "hwpstate", - "", /* tsc invariant mapped to constant_tsc */ - /* nothing */ -}; diff --git a/arch/x86/kernel/cpu/intel.c b/arch/x86/kernel/cpu/intel.c index f113ef4595f..99468dbd08d 100644 --- a/arch/x86/kernel/cpu/intel.c +++ b/arch/x86/kernel/cpu/intel.c @@ -15,6 +15,11 @@ #include <asm/ds.h> #include <asm/bugs.h> +#ifdef CONFIG_X86_64 +#include <asm/topology.h> +#include <asm/numa_64.h> +#endif + #include "cpu.h" #ifdef CONFIG_X86_LOCAL_APIC @@ -23,23 +28,22 @@ #include <mach_apic.h> #endif -#ifdef CONFIG_X86_INTEL_USERCOPY -/* - * Alignment at which movsl is preferred for bulk memory copies. - */ -struct movsl_mask movsl_mask __read_mostly; -#endif - static void __cpuinit early_init_intel(struct cpuinfo_x86 *c) { - /* Netburst reports 64 bytes clflush size, but does IO in 128 bytes */ - if (c->x86 == 15 && c->x86_cache_alignment == 64) - c->x86_cache_alignment = 128; if ((c->x86 == 0xf && c->x86_model >= 0x03) || (c->x86 == 0x6 && c->x86_model >= 0x0e)) set_cpu_cap(c, X86_FEATURE_CONSTANT_TSC); + +#ifdef CONFIG_X86_64 + set_cpu_cap(c, X86_FEATURE_SYSENTER32); +#else + /* Netburst reports 64 bytes clflush size, but does IO in 128 bytes */ + if (c->x86 == 15 && c->x86_cache_alignment == 64) + c->x86_cache_alignment = 128; +#endif } +#ifdef CONFIG_X86_32 /* * Early probe support logic for ppro memory erratum #50 * @@ -59,15 +63,54 @@ int __cpuinit ppro_with_ram_bug(void) return 0; } +#ifdef CONFIG_X86_F00F_BUG +static void __cpuinit trap_init_f00f_bug(void) +{ + __set_fixmap(FIX_F00F_IDT, __pa(&idt_table), PAGE_KERNEL_RO); -/* - * P4 Xeon errata 037 workaround. - * Hardware prefetcher may cause stale data to be loaded into the cache. - */ -static void __cpuinit Intel_errata_workarounds(struct cpuinfo_x86 *c) + /* + * Update the IDT descriptor and reload the IDT so that + * it uses the read-only mapped virtual address. + */ + idt_descr.address = fix_to_virt(FIX_F00F_IDT); + load_idt(&idt_descr); +} +#endif + +static void __cpuinit intel_workarounds(struct cpuinfo_x86 *c) { unsigned long lo, hi; +#ifdef CONFIG_X86_F00F_BUG + /* + * All current models of Pentium and Pentium with MMX technology CPUs + * have the F0 0F bug, which lets nonprivileged users lock up the system. + * Note that the workaround only should be initialized once... + */ + c->f00f_bug = 0; + if (!paravirt_enabled() && c->x86 == 5) { + static int f00f_workaround_enabled; + + c->f00f_bug = 1; + if (!f00f_workaround_enabled) { + trap_init_f00f_bug(); + printk(KERN_NOTICE "Intel Pentium with F0 0F bug - workaround enabled.\n"); + f00f_workaround_enabled = 1; + } + } +#endif + + /* + * SEP CPUID bug: Pentium Pro reports SEP but doesn't have it until + * model 3 mask 3 + */ + if ((c->x86<<8 | c->x86_model<<4 | c->x86_mask) < 0x633) + clear_cpu_cap(c, X86_FEATURE_SEP); + + /* + * P4 Xeon errata 037 workaround. + * Hardware prefetcher may cause stale data to be loaded into the cache. + */ if ((c->x86 == 15) && (c->x86_model == 1) && (c->x86_mask == 1)) { rdmsr(MSR_IA32_MISC_ENABLE, lo, hi); if ((lo & (1<<9)) == 0) { @@ -77,13 +120,68 @@ static void __cpuinit Intel_errata_workarounds(struct cpuinfo_x86 *c) wrmsr (MSR_IA32_MISC_ENABLE, lo, hi); } } + + /* + * See if we have a good local APIC by checking for buggy Pentia, + * i.e. all B steppings and the C2 stepping of P54C when using their + * integrated APIC (see 11AP erratum in "Pentium Processor + * Specification Update"). + */ + if (cpu_has_apic && (c->x86<<8 | c->x86_model<<4) == 0x520 && + (c->x86_mask < 0x6 || c->x86_mask == 0xb)) + set_cpu_cap(c, X86_FEATURE_11AP); + + +#ifdef CONFIG_X86_INTEL_USERCOPY + /* + * Set up the preferred alignment for movsl bulk memory moves + */ + switch (c->x86) { + case 4: /* 486: untested */ + break; + case 5: /* Old Pentia: untested */ + break; + case 6: /* PII/PIII only like movsl with 8-byte alignment */ + movsl_mask.mask = 7; + break; + case 15: /* P4 is OK down to 8-byte alignment */ + movsl_mask.mask = 7; + break; + } +#endif + +#ifdef CONFIG_X86_NUMAQ + numaq_tsc_disable(); +#endif } +#else +static void __cpuinit intel_workarounds(struct cpuinfo_x86 *c) +{ +} +#endif +static void __cpuinit srat_detect_node(void) +{ +#if defined(CONFIG_NUMA) && defined(CONFIG_X86_64) + unsigned node; + int cpu = smp_processor_id(); + int apicid = hard_smp_processor_id(); + + /* Don't do the funky fallback heuristics the AMD version employs + for now. */ + node = apicid_to_node[apicid]; + if (node == NUMA_NO_NODE || !node_online(node)) + node = first_node(node_online_map); + numa_set_node(cpu, node); + + printk(KERN_INFO "CPU %d/%x -> Node %d\n", cpu, apicid, node); +#endif +} /* * find out the number of processor cores on the die */ -static int __cpuinit num_cpu_cores(struct cpuinfo_x86 *c) +static int __cpuinit intel_num_cpu_cores(struct cpuinfo_x86 *c) { unsigned int eax, ebx, ecx, edx; @@ -98,45 +196,51 @@ static int __cpuinit num_cpu_cores(struct cpuinfo_x86 *c) return 1; } -#ifdef CONFIG_X86_F00F_BUG -static void __cpuinit trap_init_f00f_bug(void) +static void __cpuinit detect_vmx_virtcap(struct cpuinfo_x86 *c) { - __set_fixmap(FIX_F00F_IDT, __pa(&idt_table), PAGE_KERNEL_RO); - - /* - * Update the IDT descriptor and reload the IDT so that - * it uses the read-only mapped virtual address. - */ - idt_descr.address = fix_to_virt(FIX_F00F_IDT); - load_idt(&idt_descr); + /* Intel VMX MSR indicated features */ +#define X86_VMX_FEATURE_PROC_CTLS_TPR_SHADOW 0x00200000 +#define X86_VMX_FEATURE_PROC_CTLS_VNMI 0x00400000 +#define X86_VMX_FEATURE_PROC_CTLS_2ND_CTLS 0x80000000 +#define X86_VMX_FEATURE_PROC_CTLS2_VIRT_APIC 0x00000001 +#define X86_VMX_FEATURE_PROC_CTLS2_EPT 0x00000002 +#define X86_VMX_FEATURE_PROC_CTLS2_VPID 0x00000020 + + u32 vmx_msr_low, vmx_msr_high, msr_ctl, msr_ctl2; + + clear_cpu_cap(c, X86_FEATURE_TPR_SHADOW); + clear_cpu_cap(c, X86_FEATURE_VNMI); + clear_cpu_cap(c, X86_FEATURE_FLEXPRIORITY); + clear_cpu_cap(c, X86_FEATURE_EPT); + clear_cpu_cap(c, X86_FEATURE_VPID); + + rdmsr(MSR_IA32_VMX_PROCBASED_CTLS, vmx_msr_low, vmx_msr_high); + msr_ctl = vmx_msr_high | vmx_msr_low; + if (msr_ctl & X86_VMX_FEATURE_PROC_CTLS_TPR_SHADOW) + set_cpu_cap(c, X86_FEATURE_TPR_SHADOW); + if (msr_ctl & X86_VMX_FEATURE_PROC_CTLS_VNMI) + set_cpu_cap(c, X86_FEATURE_VNMI); + if (msr_ctl & X86_VMX_FEATURE_PROC_CTLS_2ND_CTLS) { + rdmsr(MSR_IA32_VMX_PROCBASED_CTLS2, + vmx_msr_low, vmx_msr_high); + msr_ctl2 = vmx_msr_high | vmx_msr_low; + if ((msr_ctl2 & X86_VMX_FEATURE_PROC_CTLS2_VIRT_APIC) && + (msr_ctl & X86_VMX_FEATURE_PROC_CTLS_TPR_SHADOW)) + set_cpu_cap(c, X86_FEATURE_FLEXPRIORITY); + if (msr_ctl2 & X86_VMX_FEATURE_PROC_CTLS2_EPT) + set_cpu_cap(c, X86_FEATURE_EPT); + if (msr_ctl2 & X86_VMX_FEATURE_PROC_CTLS2_VPID) + set_cpu_cap(c, X86_FEATURE_VPID); + } } -#endif static void __cpuinit init_intel(struct cpuinfo_x86 *c) { unsigned int l2 = 0; - char *p = NULL; early_init_intel(c); -#ifdef CONFIG_X86_F00F_BUG - /* - * All current models of Pentium and Pentium with MMX technology CPUs - * have the F0 0F bug, which lets nonprivileged users lock up the system. - * Note that the workaround only should be initialized once... - */ - c->f00f_bug = 0; - if (!paravirt_enabled() && c->x86 == 5) { - static int f00f_workaround_enabled; - - c->f00f_bug = 1; - if (!f00f_workaround_enabled) { - trap_init_f00f_bug(); - printk(KERN_NOTICE "Intel Pentium with F0 0F bug - workaround enabled.\n"); - f00f_workaround_enabled = 1; - } - } -#endif + intel_workarounds(c); l2 = init_intel_cacheinfo(c); if (c->cpuid_level > 9) { @@ -146,16 +250,32 @@ static void __cpuinit init_intel(struct cpuinfo_x86 *c) set_cpu_cap(c, X86_FEATURE_ARCH_PERFMON); } - /* SEP CPUID bug: Pentium Pro reports SEP but doesn't have it until model 3 mask 3 */ - if ((c->x86<<8 | c->x86_model<<4 | c->x86_mask) < 0x633) - clear_cpu_cap(c, X86_FEATURE_SEP); + if (cpu_has_xmm2) + set_cpu_cap(c, X86_FEATURE_LFENCE_RDTSC); + if (cpu_has_ds) { + unsigned int l1; + rdmsr(MSR_IA32_MISC_ENABLE, l1, l2); + if (!(l1 & (1<<11))) + set_cpu_cap(c, X86_FEATURE_BTS); + if (!(l1 & (1<<12))) + set_cpu_cap(c, X86_FEATURE_PEBS); + ds_init_intel(c); + } +#ifdef CONFIG_X86_64 + if (c->x86 == 15) + c->x86_cache_alignment = c->x86_clflush_size * 2; + if (c->x86 == 6) + set_cpu_cap(c, X86_FEATURE_REP_GOOD); +#else /* * Names for the Pentium II/Celeron processors * detectable only by also checking the cache size. * Dixon is NOT a Celeron. */ if (c->x86 == 6) { + char *p = NULL; + switch (c->x86_model) { case 5: if (c->x86_mask == 0) { @@ -178,71 +298,41 @@ static void __cpuinit init_intel(struct cpuinfo_x86 *c) p = "Celeron (Coppermine)"; break; } - } - - if (p) - strcpy(c->x86_model_id, p); - - c->x86_max_cores = num_cpu_cores(c); - - detect_ht(c); - /* Work around errata */ - Intel_errata_workarounds(c); - -#ifdef CONFIG_X86_INTEL_USERCOPY - /* - * Set up the preferred alignment for movsl bulk memory moves - */ - switch (c->x86) { - case 4: /* 486: untested */ - break; - case 5: /* Old Pentia: untested */ - break; - case 6: /* PII/PIII only like movsl with 8-byte alignment */ - movsl_mask.mask = 7; - break; - case 15: /* P4 is OK down to 8-byte alignment */ - movsl_mask.mask = 7; - break; + if (p) + strcpy(c->x86_model_id, p); } -#endif - if (cpu_has_xmm2) - set_cpu_cap(c, X86_FEATURE_LFENCE_RDTSC); - if (c->x86 == 15) { + if (c->x86 == 15) set_cpu_cap(c, X86_FEATURE_P4); - } if (c->x86 == 6) set_cpu_cap(c, X86_FEATURE_P3); - if (cpu_has_ds) { - unsigned int l1; - rdmsr(MSR_IA32_MISC_ENABLE, l1, l2); - if (!(l1 & (1<<11))) - set_cpu_cap(c, X86_FEATURE_BTS); - if (!(l1 & (1<<12))) - set_cpu_cap(c, X86_FEATURE_PEBS); - ds_init_intel(c); - } if (cpu_has_bts) ptrace_bts_init_intel(c); - /* - * See if we have a good local APIC by checking for buggy Pentia, - * i.e. all B steppings and the C2 stepping of P54C when using their - * integrated APIC (see 11AP erratum in "Pentium Processor - * Specification Update"). - */ - if (cpu_has_apic && (c->x86<<8 | c->x86_model<<4) == 0x520 && - (c->x86_mask < 0x6 || c->x86_mask == 0xb)) - set_cpu_cap(c, X86_FEATURE_11AP); +#endif -#ifdef CONFIG_X86_NUMAQ - numaq_tsc_disable(); + detect_extended_topology(c); + if (!cpu_has(c, X86_FEATURE_XTOPOLOGY)) { + /* + * let's use the legacy cpuid vector 0x1 and 0x4 for topology + * detection. + */ + c->x86_max_cores = intel_num_cpu_cores(c); +#ifdef CONFIG_X86_32 + detect_ht(c); #endif + } + + /* Work around errata */ + srat_detect_node(); + + if (cpu_has(c, X86_FEATURE_VMX)) + detect_vmx_virtcap(c); } +#ifdef CONFIG_X86_32 static unsigned int __cpuinit intel_size_cache(struct cpuinfo_x86 *c, unsigned int size) { /* @@ -255,10 +345,12 @@ static unsigned int __cpuinit intel_size_cache(struct cpuinfo_x86 *c, unsigned i size = 256; return size; } +#endif static struct cpu_dev intel_cpu_dev __cpuinitdata = { .c_vendor = "Intel", .c_ident = { "GenuineIntel" }, +#ifdef CONFIG_X86_32 .c_models = { { .vendor = X86_VENDOR_INTEL, .family = 4, .model_names = { @@ -308,76 +400,12 @@ static struct cpu_dev intel_cpu_dev __cpuinitdata = { } }, }, + .c_size_cache = intel_size_cache, +#endif .c_early_init = early_init_intel, .c_init = init_intel, - .c_size_cache = intel_size_cache, + .c_x86_vendor = X86_VENDOR_INTEL, }; -cpu_vendor_dev_register(X86_VENDOR_INTEL, &intel_cpu_dev); - -#ifndef CONFIG_X86_CMPXCHG -unsigned long cmpxchg_386_u8(volatile void *ptr, u8 old, u8 new) -{ - u8 prev; - unsigned long flags; - - /* Poor man's cmpxchg for 386. Unsuitable for SMP */ - local_irq_save(flags); - prev = *(u8 *)ptr; - if (prev == old) - *(u8 *)ptr = new; - local_irq_restore(flags); - return prev; -} -EXPORT_SYMBOL(cmpxchg_386_u8); - -unsigned long cmpxchg_386_u16(volatile void *ptr, u16 old, u16 new) -{ - u16 prev; - unsigned long flags; - - /* Poor man's cmpxchg for 386. Unsuitable for SMP */ - local_irq_save(flags); - prev = *(u16 *)ptr; - if (prev == old) - *(u16 *)ptr = new; - local_irq_restore(flags); - return prev; -} -EXPORT_SYMBOL(cmpxchg_386_u16); - -unsigned long cmpxchg_386_u32(volatile void *ptr, u32 old, u32 new) -{ - u32 prev; - unsigned long flags; - - /* Poor man's cmpxchg for 386. Unsuitable for SMP */ - local_irq_save(flags); - prev = *(u32 *)ptr; - if (prev == old) - *(u32 *)ptr = new; - local_irq_restore(flags); - return prev; -} -EXPORT_SYMBOL(cmpxchg_386_u32); -#endif - -#ifndef CONFIG_X86_CMPXCHG64 -unsigned long long cmpxchg_486_u64(volatile void *ptr, u64 old, u64 new) -{ - u64 prev; - unsigned long flags; - - /* Poor man's cmpxchg8b for 386 and 486. Unsuitable for SMP */ - local_irq_save(flags); - prev = *(u64 *)ptr; - if (prev == old) - *(u64 *)ptr = new; - local_irq_restore(flags); - return prev; -} -EXPORT_SYMBOL(cmpxchg_486_u64); -#endif - -/* arch_initcall(intel_cpu_init); */ +cpu_dev_register(intel_cpu_dev); diff --git a/arch/x86/kernel/cpu/intel_64.c b/arch/x86/kernel/cpu/intel_64.c deleted file mode 100644 index 1019c58d39f..00000000000 --- a/arch/x86/kernel/cpu/intel_64.c +++ /dev/null @@ -1,95 +0,0 @@ -#include <linux/init.h> -#include <linux/smp.h> -#include <asm/processor.h> -#include <asm/ptrace.h> -#include <asm/topology.h> -#include <asm/numa_64.h> - -#include "cpu.h" - -static void __cpuinit early_init_intel(struct cpuinfo_x86 *c) -{ - if ((c->x86 == 0xf && c->x86_model >= 0x03) || - (c->x86 == 0x6 && c->x86_model >= 0x0e)) - set_cpu_cap(c, X86_FEATURE_CONSTANT_TSC); - - set_cpu_cap(c, X86_FEATURE_SYSENTER32); -} - -/* - * find out the number of processor cores on the die - */ -static int __cpuinit intel_num_cpu_cores(struct cpuinfo_x86 *c) -{ - unsigned int eax, t; - - if (c->cpuid_level < 4) - return 1; - - cpuid_count(4, 0, &eax, &t, &t, &t); - - if (eax & 0x1f) - return ((eax >> 26) + 1); - else - return 1; -} - -static void __cpuinit srat_detect_node(void) -{ -#ifdef CONFIG_NUMA - unsigned node; - int cpu = smp_processor_id(); - int apicid = hard_smp_processor_id(); - - /* Don't do the funky fallback heuristics the AMD version employs - for now. */ - node = apicid_to_node[apicid]; - if (node == NUMA_NO_NODE || !node_online(node)) - node = first_node(node_online_map); - numa_set_node(cpu, node); - - printk(KERN_INFO "CPU %d/%x -> Node %d\n", cpu, apicid, node); -#endif -} - -static void __cpuinit init_intel(struct cpuinfo_x86 *c) -{ - init_intel_cacheinfo(c); - if (c->cpuid_level > 9) { - unsigned eax = cpuid_eax(10); - /* Check for version and the number of counters */ - if ((eax & 0xff) && (((eax>>8) & 0xff) > 1)) - set_cpu_cap(c, X86_FEATURE_ARCH_PERFMON); - } - - if (cpu_has_ds) { - unsigned int l1, l2; - rdmsr(MSR_IA32_MISC_ENABLE, l1, l2); - if (!(l1 & (1<<11))) - set_cpu_cap(c, X86_FEATURE_BTS); - if (!(l1 & (1<<12))) - set_cpu_cap(c, X86_FEATURE_PEBS); - } - - - if (cpu_has_bts) - ds_init_intel(c); - - if (c->x86 == 15) - c->x86_cache_alignment = c->x86_clflush_size * 2; - if (c->x86 == 6) - set_cpu_cap(c, X86_FEATURE_REP_GOOD); - set_cpu_cap(c, X86_FEATURE_LFENCE_RDTSC); - c->x86_max_cores = intel_num_cpu_cores(c); - - srat_detect_node(); -} - -static struct cpu_dev intel_cpu_dev __cpuinitdata = { - .c_vendor = "Intel", - .c_ident = { "GenuineIntel" }, - .c_early_init = early_init_intel, - .c_init = init_intel, -}; -cpu_vendor_dev_register(X86_VENDOR_INTEL, &intel_cpu_dev); - diff --git a/arch/x86/kernel/cpu/intel_cacheinfo.c b/arch/x86/kernel/cpu/intel_cacheinfo.c index 6b0a10b002f..3f46afbb1cf 100644 --- a/arch/x86/kernel/cpu/intel_cacheinfo.c +++ b/arch/x86/kernel/cpu/intel_cacheinfo.c @@ -1,8 +1,8 @@ /* - * Routines to indentify caches on Intel CPU. + * Routines to indentify caches on Intel CPU. * - * Changes: - * Venkatesh Pallipadi : Adding cache identification through cpuid(4) + * Changes: + * Venkatesh Pallipadi : Adding cache identification through cpuid(4) * Ashok Raj <ashok.raj@intel.com>: Work with CPU hotplug infrastructure. * Andi Kleen / Andreas Herrmann : CPUID4 emulation on AMD. */ @@ -13,6 +13,7 @@ #include <linux/compiler.h> #include <linux/cpu.h> #include <linux/sched.h> +#include <linux/pci.h> #include <asm/processor.h> #include <asm/smp.h> @@ -130,9 +131,18 @@ struct _cpuid4_info { union _cpuid4_leaf_ebx ebx; union _cpuid4_leaf_ecx ecx; unsigned long size; + unsigned long can_disable; cpumask_t shared_cpu_map; /* future?: only cpus/node is needed */ }; +#ifdef CONFIG_PCI +static struct pci_device_id k8_nb_id[] = { + { PCI_DEVICE(PCI_VENDOR_ID_AMD, 0x1103) }, + { PCI_DEVICE(PCI_VENDOR_ID_AMD, 0x1203) }, + {} +}; +#endif + unsigned short num_cache_leaves; /* AMD doesn't have CPUID4. Emulate it here to report the same @@ -182,9 +192,10 @@ static unsigned short assocs[] __cpuinitdata = { static unsigned char levels[] __cpuinitdata = { 1, 1, 2, 3 }; static unsigned char types[] __cpuinitdata = { 1, 2, 3, 3 }; -static void __cpuinit amd_cpuid4(int leaf, union _cpuid4_leaf_eax *eax, - union _cpuid4_leaf_ebx *ebx, - union _cpuid4_leaf_ecx *ecx) +static void __cpuinit +amd_cpuid4(int leaf, union _cpuid4_leaf_eax *eax, + union _cpuid4_leaf_ebx *ebx, + union _cpuid4_leaf_ecx *ecx) { unsigned dummy; unsigned line_size, lines_per_tag, assoc, size_in_kb; @@ -251,27 +262,40 @@ static void __cpuinit amd_cpuid4(int leaf, union _cpuid4_leaf_eax *eax, (ebx->split.ways_of_associativity + 1) - 1; } -static int __cpuinit cpuid4_cache_lookup(int index, struct _cpuid4_info *this_leaf) +static void __cpuinit +amd_check_l3_disable(int index, struct _cpuid4_info *this_leaf) +{ + if (index < 3) + return; + this_leaf->can_disable = 1; +} + +static int +__cpuinit cpuid4_cache_lookup(int index, struct _cpuid4_info *this_leaf) { union _cpuid4_leaf_eax eax; union _cpuid4_leaf_ebx ebx; union _cpuid4_leaf_ecx ecx; unsigned edx; - if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD) + if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD) { amd_cpuid4(index, &eax, &ebx, &ecx); - else - cpuid_count(4, index, &eax.full, &ebx.full, &ecx.full, &edx); + if (boot_cpu_data.x86 >= 0x10) + amd_check_l3_disable(index, this_leaf); + } else { + cpuid_count(4, index, &eax.full, &ebx.full, &ecx.full, &edx); + } + if (eax.split.type == CACHE_TYPE_NULL) return -EIO; /* better error ? */ this_leaf->eax = eax; this_leaf->ebx = ebx; this_leaf->ecx = ecx; - this_leaf->size = (ecx.split.number_of_sets + 1) * - (ebx.split.coherency_line_size + 1) * - (ebx.split.physical_line_partition + 1) * - (ebx.split.ways_of_associativity + 1); + this_leaf->size = (ecx.split.number_of_sets + 1) * + (ebx.split.coherency_line_size + 1) * + (ebx.split.physical_line_partition + 1) * + (ebx.split.ways_of_associativity + 1); return 0; } @@ -453,7 +477,7 @@ unsigned int __cpuinit init_intel_cacheinfo(struct cpuinfo_x86 *c) /* pointer to _cpuid4_info array (for each cache leaf) */ static DEFINE_PER_CPU(struct _cpuid4_info *, cpuid4_info); -#define CPUID4_INFO_IDX(x, y) (&((per_cpu(cpuid4_info, x))[y])) +#define CPUID4_INFO_IDX(x, y) (&((per_cpu(cpuid4_info, x))[y])) #ifdef CONFIG_SMP static void __cpuinit cache_shared_cpu_map_setup(unsigned int cpu, int index) @@ -490,7 +514,7 @@ static void __cpuinit cache_remove_shared_cpu_map(unsigned int cpu, int index) this_leaf = CPUID4_INFO_IDX(cpu, index); for_each_cpu_mask_nr(sibling, this_leaf->shared_cpu_map) { - sibling_leaf = CPUID4_INFO_IDX(sibling, index); + sibling_leaf = CPUID4_INFO_IDX(sibling, index); cpu_clear(cpu, sibling_leaf->shared_cpu_map); } } @@ -572,7 +596,7 @@ struct _index_kobject { /* pointer to array of kobjects for cpuX/cache/indexY */ static DEFINE_PER_CPU(struct _index_kobject *, index_kobject); -#define INDEX_KOBJECT_PTR(x, y) (&((per_cpu(index_kobject, x))[y])) +#define INDEX_KOBJECT_PTR(x, y) (&((per_cpu(index_kobject, x))[y])) #define show_one_plus(file_name, object, val) \ static ssize_t show_##file_name \ @@ -637,6 +661,99 @@ static ssize_t show_type(struct _cpuid4_info *this_leaf, char *buf) { } } +#define to_object(k) container_of(k, struct _index_kobject, kobj) +#define to_attr(a) container_of(a, struct _cache_attr, attr) + +#ifdef CONFIG_PCI +static struct pci_dev *get_k8_northbridge(int node) +{ + struct pci_dev *dev = NULL; + int i; + + for (i = 0; i <= node; i++) { + do { + dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev); + if (!dev) + break; + } while (!pci_match_id(&k8_nb_id[0], dev)); + if (!dev) + break; + } + return dev; +} +#else +static struct pci_dev *get_k8_northbridge(int node) +{ + return NULL; +} +#endif + +static ssize_t show_cache_disable(struct _cpuid4_info *this_leaf, char *buf) +{ + int node = cpu_to_node(first_cpu(this_leaf->shared_cpu_map)); + struct pci_dev *dev = NULL; + ssize_t ret = 0; + int i; + + if (!this_leaf->can_disable) + return sprintf(buf, "Feature not enabled\n"); + + dev = get_k8_northbridge(node); + if (!dev) { + printk(KERN_ERR "Attempting AMD northbridge operation on a system with no northbridge\n"); + return -EINVAL; + } + + for (i = 0; i < 2; i++) { + unsigned int reg; + + pci_read_config_dword(dev, 0x1BC + i * 4, ®); + + ret += sprintf(buf, "%sEntry: %d\n", buf, i); + ret += sprintf(buf, "%sReads: %s\tNew Entries: %s\n", + buf, + reg & 0x80000000 ? "Disabled" : "Allowed", + reg & 0x40000000 ? "Disabled" : "Allowed"); + ret += sprintf(buf, "%sSubCache: %x\tIndex: %x\n", + buf, (reg & 0x30000) >> 16, reg & 0xfff); + } + return ret; +} + +static ssize_t +store_cache_disable(struct _cpuid4_info *this_leaf, const char *buf, + size_t count) +{ + int node = cpu_to_node(first_cpu(this_leaf->shared_cpu_map)); + struct pci_dev *dev = NULL; + unsigned int ret, index, val; + + if (!this_leaf->can_disable) + return 0; + + if (strlen(buf) > 15) + return -EINVAL; + + ret = sscanf(buf, "%x %x", &index, &val); + if (ret != 2) + return -EINVAL; + if (index > 1) + return -EINVAL; + + val |= 0xc0000000; + dev = get_k8_northbridge(node); + if (!dev) { + printk(KERN_ERR "Attempting AMD northbridge operation on a system with no northbridge\n"); + return -EINVAL; + } + + pci_write_config_dword(dev, 0x1BC + index * 4, val & ~0x40000000); + wbinvd(); + pci_write_config_dword(dev, 0x1BC + index * 4, val); + + return 1; +} + struct _cache_attr { struct attribute attr; ssize_t (*show)(struct _cpuid4_info *, char *); @@ -657,6 +774,8 @@ define_one_ro(size); define_one_ro(shared_cpu_map); define_one_ro(shared_cpu_list); +static struct _cache_attr cache_disable = __ATTR(cache_disable, 0644, show_cache_disable, store_cache_disable); + static struct attribute * default_attrs[] = { &type.attr, &level.attr, @@ -667,12 +786,10 @@ static struct attribute * default_attrs[] = { &size.attr, &shared_cpu_map.attr, &shared_cpu_list.attr, + &cache_disable.attr, NULL }; -#define to_object(k) container_of(k, struct _index_kobject, kobj) -#define to_attr(a) container_of(a, struct _cache_attr, attr) - static ssize_t show(struct kobject * kobj, struct attribute * attr, char * buf) { struct _cache_attr *fattr = to_attr(attr); @@ -682,14 +799,22 @@ static ssize_t show(struct kobject * kobj, struct attribute * attr, char * buf) ret = fattr->show ? fattr->show(CPUID4_INFO_IDX(this_leaf->cpu, this_leaf->index), buf) : - 0; + 0; return ret; } static ssize_t store(struct kobject * kobj, struct attribute * attr, const char * buf, size_t count) { - return 0; + struct _cache_attr *fattr = to_attr(attr); + struct _index_kobject *this_leaf = to_object(kobj); + ssize_t ret; + + ret = fattr->store ? + fattr->store(CPUID4_INFO_IDX(this_leaf->cpu, this_leaf->index), + buf, count) : + 0; + return ret; } static struct sysfs_ops sysfs_ops = { diff --git a/arch/x86/kernel/cpu/mcheck/mce_64.c b/arch/x86/kernel/cpu/mcheck/mce_64.c index 726a5fcdf34..4b031a4ac85 100644 --- a/arch/x86/kernel/cpu/mcheck/mce_64.c +++ b/arch/x86/kernel/cpu/mcheck/mce_64.c @@ -860,7 +860,7 @@ error: return err; } -static void mce_remove_device(unsigned int cpu) +static __cpuinit void mce_remove_device(unsigned int cpu) { int i; diff --git a/arch/x86/kernel/cpu/mkcapflags.pl b/arch/x86/kernel/cpu/mkcapflags.pl new file mode 100644 index 00000000000..dfea390e160 --- /dev/null +++ b/arch/x86/kernel/cpu/mkcapflags.pl @@ -0,0 +1,32 @@ +#!/usr/bin/perl +# +# Generate the x86_cap_flags[] array from include/asm-x86/cpufeature.h +# + +($in, $out) = @ARGV; + +open(IN, "< $in\0") or die "$0: cannot open: $in: $!\n"; +open(OUT, "> $out\0") or die "$0: cannot create: $out: $!\n"; + +print OUT "#include <asm/cpufeature.h>\n\n"; +print OUT "const char * const x86_cap_flags[NCAPINTS*32] = {\n"; + +while (defined($line = <IN>)) { + if ($line =~ /^\s*\#\s*define\s+(X86_FEATURE_(\S+))\s+(.*)$/) { + $macro = $1; + $feature = $2; + $tail = $3; + if ($tail =~ /\/\*\s*\"([^"]*)\".*\*\//) { + $feature = $1; + } + + if ($feature ne '') { + printf OUT "\t%-32s = \"%s\",\n", + "[$macro]", "\L$feature"; + } + } +} +print OUT "};\n"; + +close(IN); +close(OUT); diff --git a/arch/x86/kernel/cpu/powerflags.c b/arch/x86/kernel/cpu/powerflags.c new file mode 100644 index 00000000000..5abbea297e0 --- /dev/null +++ b/arch/x86/kernel/cpu/powerflags.c @@ -0,0 +1,20 @@ +/* + * Strings for the various x86 power flags + * + * This file must not contain any executable code. + */ + +#include <asm/cpufeature.h> + +const char *const x86_power_flags[32] = { + "ts", /* temperature sensor */ + "fid", /* frequency id control */ + "vid", /* voltage id control */ + "ttp", /* thermal trip */ + "tm", + "stc", + "100mhzsteps", + "hwpstate", + "", /* tsc invariant mapped to constant_tsc */ + /* nothing */ +}; diff --git a/arch/x86/kernel/cpu/transmeta.c b/arch/x86/kernel/cpu/transmeta.c index b911a2c61b8..52b3fefbd5a 100644 --- a/arch/x86/kernel/cpu/transmeta.c +++ b/arch/x86/kernel/cpu/transmeta.c @@ -5,6 +5,18 @@ #include <asm/msr.h> #include "cpu.h" +static void __cpuinit early_init_transmeta(struct cpuinfo_x86 *c) +{ + u32 xlvl; + + /* Transmeta-defined flags: level 0x80860001 */ + xlvl = cpuid_eax(0x80860000); + if ((xlvl & 0xffff0000) == 0x80860000) { + if (xlvl >= 0x80860001) + c->x86_capability[2] = cpuid_edx(0x80860001); + } +} + static void __cpuinit init_transmeta(struct cpuinfo_x86 *c) { unsigned int cap_mask, uk, max, dummy; @@ -12,7 +24,8 @@ static void __cpuinit init_transmeta(struct cpuinfo_x86 *c) unsigned int cpu_rev, cpu_freq = 0, cpu_flags, new_cpu_rev; char cpu_info[65]; - get_model_name(c); /* Same as AMD/Cyrix */ + early_init_transmeta(c); + display_cacheinfo(c); /* Print CMS and CPU revision */ @@ -85,23 +98,12 @@ static void __cpuinit init_transmeta(struct cpuinfo_x86 *c) #endif } -static void __cpuinit transmeta_identify(struct cpuinfo_x86 *c) -{ - u32 xlvl; - - /* Transmeta-defined flags: level 0x80860001 */ - xlvl = cpuid_eax(0x80860000); - if ((xlvl & 0xffff0000) == 0x80860000) { - if (xlvl >= 0x80860001) - c->x86_capability[2] = cpuid_edx(0x80860001); - } -} - static struct cpu_dev transmeta_cpu_dev __cpuinitdata = { .c_vendor = "Transmeta", .c_ident = { "GenuineTMx86", "TransmetaCPU" }, + .c_early_init = early_init_transmeta, .c_init = init_transmeta, - .c_identify = transmeta_identify, + .c_x86_vendor = X86_VENDOR_TRANSMETA, }; -cpu_vendor_dev_register(X86_VENDOR_TRANSMETA, &transmeta_cpu_dev); +cpu_dev_register(transmeta_cpu_dev); diff --git a/arch/x86/kernel/cpu/umc.c b/arch/x86/kernel/cpu/umc.c index b1fc90989d7..e777f79e096 100644 --- a/arch/x86/kernel/cpu/umc.c +++ b/arch/x86/kernel/cpu/umc.c @@ -19,7 +19,8 @@ static struct cpu_dev umc_cpu_dev __cpuinitdata = { } }, }, + .c_x86_vendor = X86_VENDOR_UMC, }; -cpu_vendor_dev_register(X86_VENDOR_UMC, &umc_cpu_dev); +cpu_dev_register(umc_cpu_dev); diff --git a/arch/x86/kernel/doublefault_32.c b/arch/x86/kernel/doublefault_32.c index a47798b59f0..b4f14c6c09d 100644 --- a/arch/x86/kernel/doublefault_32.c +++ b/arch/x86/kernel/doublefault_32.c @@ -66,6 +66,6 @@ struct tss_struct doublefault_tss __cacheline_aligned = { .ds = __USER_DS, .fs = __KERNEL_PERCPU, - .__cr3 = __pa(swapper_pg_dir) + .__cr3 = __pa_nodebug(swapper_pg_dir), } }; diff --git a/arch/x86/kernel/dumpstack_32.c b/arch/x86/kernel/dumpstack_32.c new file mode 100644 index 00000000000..201ee359a1a --- /dev/null +++ b/arch/x86/kernel/dumpstack_32.c @@ -0,0 +1,447 @@ +/* + * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright (C) 2000, 2001, 2002 Andi Kleen, SuSE Labs + */ +#include <linux/kallsyms.h> +#include <linux/kprobes.h> +#include <linux/uaccess.h> +#include <linux/utsname.h> +#include <linux/hardirq.h> +#include <linux/kdebug.h> +#include <linux/module.h> +#include <linux/ptrace.h> +#include <linux/kexec.h> +#include <linux/bug.h> +#include <linux/nmi.h> + +#include <asm/stacktrace.h> + +#define STACKSLOTS_PER_LINE 8 +#define get_bp(bp) asm("movl %%ebp, %0" : "=r" (bp) :) + +int panic_on_unrecovered_nmi; +int kstack_depth_to_print = 3 * STACKSLOTS_PER_LINE; +static unsigned int code_bytes = 64; +static int die_counter; + +void printk_address(unsigned long address, int reliable) +{ + printk(" [<%p>] %s%pS\n", (void *) address, + reliable ? "" : "? ", (void *) address); +} + +static inline int valid_stack_ptr(struct thread_info *tinfo, + void *p, unsigned int size, void *end) +{ + void *t = tinfo; + if (end) { + if (p < end && p >= (end-THREAD_SIZE)) + return 1; + else + return 0; + } + return p > t && p < t + THREAD_SIZE - size; +} + +/* The form of the top of the frame on the stack */ +struct stack_frame { + struct stack_frame *next_frame; + unsigned long return_address; +}; + +static inline unsigned long +print_context_stack(struct thread_info *tinfo, + unsigned long *stack, unsigned long bp, + const struct stacktrace_ops *ops, void *data, + unsigned long *end) +{ + struct stack_frame *frame = (struct stack_frame *)bp; + + while (valid_stack_ptr(tinfo, stack, sizeof(*stack), end)) { + unsigned long addr; + + addr = *stack; + if (__kernel_text_address(addr)) { + if ((unsigned long) stack == bp + sizeof(long)) { + ops->address(data, addr, 1); + frame = frame->next_frame; + bp = (unsigned long) frame; + } else { + ops->address(data, addr, bp == 0); + } + } + stack++; + } + return bp; +} + +void dump_trace(struct task_struct *task, struct pt_regs *regs, + unsigned long *stack, unsigned long bp, + const struct stacktrace_ops *ops, void *data) +{ + if (!task) + task = current; + + if (!stack) { + unsigned long dummy; + stack = &dummy; + if (task && task != current) + stack = (unsigned long *)task->thread.sp; + } + +#ifdef CONFIG_FRAME_POINTER + if (!bp) { + if (task == current) { + /* Grab bp right from our regs */ + get_bp(bp); + } else { + /* bp is the last reg pushed by switch_to */ + bp = *(unsigned long *) task->thread.sp; + } + } +#endif + + for (;;) { + struct thread_info *context; + + context = (struct thread_info *) + ((unsigned long)stack & (~(THREAD_SIZE - 1))); + bp = print_context_stack(context, stack, bp, ops, data, NULL); + + stack = (unsigned long *)context->previous_esp; + if (!stack) + break; + if (ops->stack(data, "IRQ") < 0) + break; + touch_nmi_watchdog(); + } +} +EXPORT_SYMBOL(dump_trace); + +static void +print_trace_warning_symbol(void *data, char *msg, unsigned long symbol) +{ + printk(data); + print_symbol(msg, symbol); + printk("\n"); +} + +static void print_trace_warning(void *data, char *msg) +{ + printk("%s%s\n", (char *)data, msg); +} + +static int print_trace_stack(void *data, char *name) +{ + printk("%s <%s> ", (char *)data, name); + return 0; +} + +/* + * Print one address/symbol entries per line. + */ +static void print_trace_address(void *data, unsigned long addr, int reliable) +{ + touch_nmi_watchdog(); + printk(data); + printk_address(addr, reliable); +} + +static const struct stacktrace_ops print_trace_ops = { + .warning = print_trace_warning, + .warning_symbol = print_trace_warning_symbol, + .stack = print_trace_stack, + .address = print_trace_address, +}; + +static void +show_trace_log_lvl(struct task_struct *task, struct pt_regs *regs, + unsigned long *stack, unsigned long bp, char *log_lvl) +{ + printk("%sCall Trace:\n", log_lvl); + dump_trace(task, regs, stack, bp, &print_trace_ops, log_lvl); +} + +void show_trace(struct task_struct *task, struct pt_regs *regs, + unsigned long *stack, unsigned long bp) +{ + show_trace_log_lvl(task, regs, stack, bp, ""); +} + +static void +show_stack_log_lvl(struct task_struct *task, struct pt_regs *regs, + unsigned long *sp, unsigned long bp, char *log_lvl) +{ + unsigned long *stack; + int i; + + if (sp == NULL) { + if (task) + sp = (unsigned long *)task->thread.sp; + else + sp = (unsigned long *)&sp; + } + + stack = sp; + for (i = 0; i < kstack_depth_to_print; i++) { + if (kstack_end(stack)) + break; + if (i && ((i % STACKSLOTS_PER_LINE) == 0)) + printk("\n%s", log_lvl); + printk(" %08lx", *stack++); + touch_nmi_watchdog(); + } + printk("\n"); + show_trace_log_lvl(task, regs, sp, bp, log_lvl); +} + +void show_stack(struct task_struct *task, unsigned long *sp) +{ + show_stack_log_lvl(task, NULL, sp, 0, ""); +} + +/* + * The architecture-independent dump_stack generator + */ +void dump_stack(void) +{ + unsigned long bp = 0; + unsigned long stack; + +#ifdef CONFIG_FRAME_POINTER + if (!bp) + get_bp(bp); +#endif + + printk("Pid: %d, comm: %.20s %s %s %.*s\n", + current->pid, current->comm, print_tainted(), + init_utsname()->release, + (int)strcspn(init_utsname()->version, " "), + init_utsname()->version); + show_trace(NULL, NULL, &stack, bp); +} + +EXPORT_SYMBOL(dump_stack); + +void show_registers(struct pt_regs *regs) +{ + int i; + + print_modules(); + __show_regs(regs, 0); + + printk(KERN_EMERG "Process %.*s (pid: %d, ti=%p task=%p task.ti=%p)\n", + TASK_COMM_LEN, current->comm, task_pid_nr(current), + current_thread_info(), current, task_thread_info(current)); + /* + * When in-kernel, we also print out the stack and code at the + * time of the fault.. + */ + if (!user_mode_vm(regs)) { + unsigned int code_prologue = code_bytes * 43 / 64; + unsigned int code_len = code_bytes; + unsigned char c; + u8 *ip; + + printk(KERN_EMERG "Stack:\n"); + show_stack_log_lvl(NULL, regs, ®s->sp, + 0, KERN_EMERG); + + printk(KERN_EMERG "Code: "); + + ip = (u8 *)regs->ip - code_prologue; + if (ip < (u8 *)PAGE_OFFSET || probe_kernel_address(ip, c)) { + /* try starting at IP */ + ip = (u8 *)regs->ip; + code_len = code_len - code_prologue + 1; + } + for (i = 0; i < code_len; i++, ip++) { + if (ip < (u8 *)PAGE_OFFSET || + probe_kernel_address(ip, c)) { + printk(" Bad EIP value."); + break; + } + if (ip == (u8 *)regs->ip) + printk("<%02x> ", c); + else + printk("%02x ", c); + } + } + printk("\n"); +} + +int is_valid_bugaddr(unsigned long ip) +{ + unsigned short ud2; + + if (ip < PAGE_OFFSET) + return 0; + if (probe_kernel_address((unsigned short *)ip, ud2)) + return 0; + + return ud2 == 0x0b0f; +} + +static raw_spinlock_t die_lock = __RAW_SPIN_LOCK_UNLOCKED; +static int die_owner = -1; +static unsigned int die_nest_count; + +unsigned __kprobes long oops_begin(void) +{ + unsigned long flags; + + oops_enter(); + + if (die_owner != raw_smp_processor_id()) { + console_verbose(); + raw_local_irq_save(flags); + __raw_spin_lock(&die_lock); + die_owner = smp_processor_id(); + die_nest_count = 0; + bust_spinlocks(1); + } else { + raw_local_irq_save(flags); + } + die_nest_count++; + return flags; +} + +void __kprobes oops_end(unsigned long flags, struct pt_regs *regs, int signr) +{ + bust_spinlocks(0); + die_owner = -1; + add_taint(TAINT_DIE); + __raw_spin_unlock(&die_lock); + raw_local_irq_restore(flags); + + if (!regs) + return; + + if (kexec_should_crash(current)) + crash_kexec(regs); + if (in_interrupt()) + panic("Fatal exception in interrupt"); + if (panic_on_oops) + panic("Fatal exception"); + oops_exit(); + do_exit(signr); +} + +int __kprobes __die(const char *str, struct pt_regs *regs, long err) +{ + unsigned short ss; + unsigned long sp; + + printk(KERN_EMERG "%s: %04lx [#%d] ", str, err & 0xffff, ++die_counter); +#ifdef CONFIG_PREEMPT + printk("PREEMPT "); +#endif +#ifdef CONFIG_SMP + printk("SMP "); +#endif +#ifdef CONFIG_DEBUG_PAGEALLOC + printk("DEBUG_PAGEALLOC"); +#endif + printk("\n"); + if (notify_die(DIE_OOPS, str, regs, err, + current->thread.trap_no, SIGSEGV) == NOTIFY_STOP) + return 1; + + show_registers(regs); + /* Executive summary in case the oops scrolled away */ + sp = (unsigned long) (®s->sp); + savesegment(ss, ss); + if (user_mode(regs)) { + sp = regs->sp; + ss = regs->ss & 0xffff; + } + printk(KERN_EMERG "EIP: [<%08lx>] ", regs->ip); + print_symbol("%s", regs->ip); + printk(" SS:ESP %04x:%08lx\n", ss, sp); + return 0; +} + +/* + * This is gone through when something in the kernel has done something bad + * and is about to be terminated: + */ +void die(const char *str, struct pt_regs *regs, long err) +{ + unsigned long flags = oops_begin(); + + if (die_nest_count < 3) { + report_bug(regs->ip, regs); + + if (__die(str, regs, err)) + regs = NULL; + } else { + printk(KERN_EMERG "Recursive die() failure, output suppressed\n"); + } + + oops_end(flags, regs, SIGSEGV); +} + +static DEFINE_SPINLOCK(nmi_print_lock); + +void notrace __kprobes +die_nmi(char *str, struct pt_regs *regs, int do_panic) +{ + if (notify_die(DIE_NMIWATCHDOG, str, regs, 0, 2, SIGINT) == NOTIFY_STOP) + return; + + spin_lock(&nmi_print_lock); + /* + * We are in trouble anyway, lets at least try + * to get a message out: + */ + bust_spinlocks(1); + printk(KERN_EMERG "%s", str); + printk(" on CPU%d, ip %08lx, registers:\n", + smp_processor_id(), regs->ip); + show_registers(regs); + if (do_panic) + panic("Non maskable interrupt"); + console_silent(); + spin_unlock(&nmi_print_lock); + bust_spinlocks(0); + + /* + * If we are in kernel we are probably nested up pretty bad + * and might aswell get out now while we still can: + */ + if (!user_mode_vm(regs)) { + current->thread.trap_no = 2; + crash_kexec(regs); + } + + do_exit(SIGSEGV); +} + +static int __init oops_setup(char *s) +{ + if (!s) + return -EINVAL; + if (!strcmp(s, "panic")) + panic_on_oops = 1; + return 0; +} +early_param("oops", oops_setup); + +static int __init kstack_setup(char *s) +{ + if (!s) + return -EINVAL; + kstack_depth_to_print = simple_strtoul(s, NULL, 0); + return 0; +} +early_param("kstack", kstack_setup); + +static int __init code_bytes_setup(char *s) +{ + code_bytes = simple_strtoul(s, NULL, 0); + if (code_bytes > 8192) + code_bytes = 8192; + + return 1; +} +__setup("code_bytes=", code_bytes_setup); diff --git a/arch/x86/kernel/dumpstack_64.c b/arch/x86/kernel/dumpstack_64.c new file mode 100644 index 00000000000..086cc8118e3 --- /dev/null +++ b/arch/x86/kernel/dumpstack_64.c @@ -0,0 +1,573 @@ +/* + * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright (C) 2000, 2001, 2002 Andi Kleen, SuSE Labs + */ +#include <linux/kallsyms.h> +#include <linux/kprobes.h> +#include <linux/uaccess.h> +#include <linux/utsname.h> +#include <linux/hardirq.h> +#include <linux/kdebug.h> +#include <linux/module.h> +#include <linux/ptrace.h> +#include <linux/kexec.h> +#include <linux/bug.h> +#include <linux/nmi.h> + +#include <asm/stacktrace.h> + +#define STACKSLOTS_PER_LINE 4 +#define get_bp(bp) asm("movq %%rbp, %0" : "=r" (bp) :) + +int panic_on_unrecovered_nmi; +int kstack_depth_to_print = 3 * STACKSLOTS_PER_LINE; +static unsigned int code_bytes = 64; +static int die_counter; + +void printk_address(unsigned long address, int reliable) +{ + printk(" [<%p>] %s%pS\n", (void *) address, + reliable ? "" : "? ", (void *) address); +} + +static unsigned long *in_exception_stack(unsigned cpu, unsigned long stack, + unsigned *usedp, char **idp) +{ + static char ids[][8] = { + [DEBUG_STACK - 1] = "#DB", + [NMI_STACK - 1] = "NMI", + [DOUBLEFAULT_STACK - 1] = "#DF", + [STACKFAULT_STACK - 1] = "#SS", + [MCE_STACK - 1] = "#MC", +#if DEBUG_STKSZ > EXCEPTION_STKSZ + [N_EXCEPTION_STACKS ... + N_EXCEPTION_STACKS + DEBUG_STKSZ / EXCEPTION_STKSZ - 2] = "#DB[?]" +#endif + }; + unsigned k; + + /* + * Iterate over all exception stacks, and figure out whether + * 'stack' is in one of them: + */ + for (k = 0; k < N_EXCEPTION_STACKS; k++) { + unsigned long end = per_cpu(orig_ist, cpu).ist[k]; + /* + * Is 'stack' above this exception frame's end? + * If yes then skip to the next frame. + */ + if (stack >= end) + continue; + /* + * Is 'stack' above this exception frame's start address? + * If yes then we found the right frame. + */ + if (stack >= end - EXCEPTION_STKSZ) { + /* + * Make sure we only iterate through an exception + * stack once. If it comes up for the second time + * then there's something wrong going on - just + * break out and return NULL: + */ + if (*usedp & (1U << k)) + break; + *usedp |= 1U << k; + *idp = ids[k]; + return (unsigned long *)end; + } + /* + * If this is a debug stack, and if it has a larger size than + * the usual exception stacks, then 'stack' might still + * be within the lower portion of the debug stack: + */ +#if DEBUG_STKSZ > EXCEPTION_STKSZ + if (k == DEBUG_STACK - 1 && stack >= end - DEBUG_STKSZ) { + unsigned j = N_EXCEPTION_STACKS - 1; + + /* + * Black magic. A large debug stack is composed of + * multiple exception stack entries, which we + * iterate through now. Dont look: + */ + do { + ++j; + end -= EXCEPTION_STKSZ; + ids[j][4] = '1' + (j - N_EXCEPTION_STACKS); + } while (stack < end - EXCEPTION_STKSZ); + if (*usedp & (1U << j)) + break; + *usedp |= 1U << j; + *idp = ids[j]; + return (unsigned long *)end; + } +#endif + } + return NULL; +} + +/* + * x86-64 can have up to three kernel stacks: + * process stack + * interrupt stack + * severe exception (double fault, nmi, stack fault, debug, mce) hardware stack + */ + +static inline int valid_stack_ptr(struct thread_info *tinfo, + void *p, unsigned int size, void *end) +{ + void *t = tinfo; + if (end) { + if (p < end && p >= (end-THREAD_SIZE)) + return 1; + else + return 0; + } + return p > t && p < t + THREAD_SIZE - size; +} + +/* The form of the top of the frame on the stack */ +struct stack_frame { + struct stack_frame *next_frame; + unsigned long return_address; +}; + +static inline unsigned long +print_context_stack(struct thread_info *tinfo, + unsigned long *stack, unsigned long bp, + const struct stacktrace_ops *ops, void *data, + unsigned long *end) +{ + struct stack_frame *frame = (struct stack_frame *)bp; + + while (valid_stack_ptr(tinfo, stack, sizeof(*stack), end)) { + unsigned long addr; + + addr = *stack; + if (__kernel_text_address(addr)) { + if ((unsigned long) stack == bp + sizeof(long)) { + ops->address(data, addr, 1); + frame = frame->next_frame; + bp = (unsigned long) frame; + } else { + ops->address(data, addr, bp == 0); + } + } + stack++; + } + return bp; +} + +void dump_trace(struct task_struct *task, struct pt_regs *regs, + unsigned long *stack, unsigned long bp, + const struct stacktrace_ops *ops, void *data) +{ + const unsigned cpu = get_cpu(); + unsigned long *irqstack_end = (unsigned long *)cpu_pda(cpu)->irqstackptr; + unsigned used = 0; + struct thread_info *tinfo; + + if (!task) + task = current; + + if (!stack) { + unsigned long dummy; + stack = &dummy; + if (task && task != current) + stack = (unsigned long *)task->thread.sp; + } + +#ifdef CONFIG_FRAME_POINTER + if (!bp) { + if (task == current) { + /* Grab bp right from our regs */ + get_bp(bp); + } else { + /* bp is the last reg pushed by switch_to */ + bp = *(unsigned long *) task->thread.sp; + } + } +#endif + + /* + * Print function call entries in all stacks, starting at the + * current stack address. If the stacks consist of nested + * exceptions + */ + tinfo = task_thread_info(task); + for (;;) { + char *id; + unsigned long *estack_end; + estack_end = in_exception_stack(cpu, (unsigned long)stack, + &used, &id); + + if (estack_end) { + if (ops->stack(data, id) < 0) + break; + + bp = print_context_stack(tinfo, stack, bp, ops, + data, estack_end); + ops->stack(data, "<EOE>"); + /* + * We link to the next stack via the + * second-to-last pointer (index -2 to end) in the + * exception stack: + */ + stack = (unsigned long *) estack_end[-2]; + continue; + } + if (irqstack_end) { + unsigned long *irqstack; + irqstack = irqstack_end - + (IRQSTACKSIZE - 64) / sizeof(*irqstack); + + if (stack >= irqstack && stack < irqstack_end) { + if (ops->stack(data, "IRQ") < 0) + break; + bp = print_context_stack(tinfo, stack, bp, + ops, data, irqstack_end); + /* + * We link to the next stack (which would be + * the process stack normally) the last + * pointer (index -1 to end) in the IRQ stack: + */ + stack = (unsigned long *) (irqstack_end[-1]); + irqstack_end = NULL; + ops->stack(data, "EOI"); + continue; + } + } + break; + } + + /* + * This handles the process stack: + */ + bp = print_context_stack(tinfo, stack, bp, ops, data, NULL); + put_cpu(); +} +EXPORT_SYMBOL(dump_trace); + +static void +print_trace_warning_symbol(void *data, char *msg, unsigned long symbol) +{ + printk(data); + print_symbol(msg, symbol); + printk("\n"); +} + +static void print_trace_warning(void *data, char *msg) +{ + printk("%s%s\n", (char *)data, msg); +} + +static int print_trace_stack(void *data, char *name) +{ + printk("%s <%s> ", (char *)data, name); + return 0; +} + +/* + * Print one address/symbol entries per line. + */ +static void print_trace_address(void *data, unsigned long addr, int reliable) +{ + touch_nmi_watchdog(); + printk(data); + printk_address(addr, reliable); +} + +static const struct stacktrace_ops print_trace_ops = { + .warning = print_trace_warning, + .warning_symbol = print_trace_warning_symbol, + .stack = print_trace_stack, + .address = print_trace_address, +}; + +static void +show_trace_log_lvl(struct task_struct *task, struct pt_regs *regs, + unsigned long *stack, unsigned long bp, char *log_lvl) +{ + printk("%sCall Trace:\n", log_lvl); + dump_trace(task, regs, stack, bp, &print_trace_ops, log_lvl); +} + +void show_trace(struct task_struct *task, struct pt_regs *regs, + unsigned long *stack, unsigned long bp) +{ + show_trace_log_lvl(task, regs, stack, bp, ""); +} + +static void +show_stack_log_lvl(struct task_struct *task, struct pt_regs *regs, + unsigned long *sp, unsigned long bp, char *log_lvl) +{ + unsigned long *stack; + int i; + const int cpu = smp_processor_id(); + unsigned long *irqstack_end = + (unsigned long *) (cpu_pda(cpu)->irqstackptr); + unsigned long *irqstack = + (unsigned long *) (cpu_pda(cpu)->irqstackptr - IRQSTACKSIZE); + + /* + * debugging aid: "show_stack(NULL, NULL);" prints the + * back trace for this cpu. + */ + + if (sp == NULL) { + if (task) + sp = (unsigned long *)task->thread.sp; + else + sp = (unsigned long *)&sp; + } + + stack = sp; + for (i = 0; i < kstack_depth_to_print; i++) { + if (stack >= irqstack && stack <= irqstack_end) { + if (stack == irqstack_end) { + stack = (unsigned long *) (irqstack_end[-1]); + printk(" <EOI> "); + } + } else { + if (((long) stack & (THREAD_SIZE-1)) == 0) + break; + } + if (i && ((i % STACKSLOTS_PER_LINE) == 0)) + printk("\n%s", log_lvl); + printk(" %016lx", *stack++); + touch_nmi_watchdog(); + } + printk("\n"); + show_trace_log_lvl(task, regs, sp, bp, log_lvl); +} + +void show_stack(struct task_struct *task, unsigned long *sp) +{ + show_stack_log_lvl(task, NULL, sp, 0, ""); +} + +/* + * The architecture-independent dump_stack generator + */ +void dump_stack(void) +{ + unsigned long bp = 0; + unsigned long stack; + +#ifdef CONFIG_FRAME_POINTER + if (!bp) + get_bp(bp); +#endif + + printk("Pid: %d, comm: %.20s %s %s %.*s\n", + current->pid, current->comm, print_tainted(), + init_utsname()->release, + (int)strcspn(init_utsname()->version, " "), + init_utsname()->version); + show_trace(NULL, NULL, &stack, bp); +} +EXPORT_SYMBOL(dump_stack); + +void show_registers(struct pt_regs *regs) +{ + int i; + unsigned long sp; + const int cpu = smp_processor_id(); + struct task_struct *cur = cpu_pda(cpu)->pcurrent; + + sp = regs->sp; + printk("CPU %d ", cpu); + __show_regs(regs, 1); + printk("Process %s (pid: %d, threadinfo %p, task %p)\n", + cur->comm, cur->pid, task_thread_info(cur), cur); + + /* + * When in-kernel, we also print out the stack and code at the + * time of the fault.. + */ + if (!user_mode(regs)) { + unsigned int code_prologue = code_bytes * 43 / 64; + unsigned int code_len = code_bytes; + unsigned char c; + u8 *ip; + + printk(KERN_EMERG "Stack:\n"); + show_stack_log_lvl(NULL, regs, (unsigned long *)sp, + regs->bp, KERN_EMERG); + + printk(KERN_EMERG "Code: "); + + ip = (u8 *)regs->ip - code_prologue; + if (ip < (u8 *)PAGE_OFFSET || probe_kernel_address(ip, c)) { + /* try starting at IP */ + ip = (u8 *)regs->ip; + code_len = code_len - code_prologue + 1; + } + for (i = 0; i < code_len; i++, ip++) { + if (ip < (u8 *)PAGE_OFFSET || + probe_kernel_address(ip, c)) { + printk(" Bad RIP value."); + break; + } + if (ip == (u8 *)regs->ip) + printk("<%02x> ", c); + else + printk("%02x ", c); + } + } + printk("\n"); +} + +int is_valid_bugaddr(unsigned long ip) +{ + unsigned short ud2; + + if (__copy_from_user(&ud2, (const void __user *) ip, sizeof(ud2))) + return 0; + + return ud2 == 0x0b0f; +} + +static raw_spinlock_t die_lock = __RAW_SPIN_LOCK_UNLOCKED; +static int die_owner = -1; +static unsigned int die_nest_count; + +unsigned __kprobes long oops_begin(void) +{ + int cpu; + unsigned long flags; + + oops_enter(); + + /* racy, but better than risking deadlock. */ + raw_local_irq_save(flags); + cpu = smp_processor_id(); + if (!__raw_spin_trylock(&die_lock)) { + if (cpu == die_owner) + /* nested oops. should stop eventually */; + else + __raw_spin_lock(&die_lock); + } + die_nest_count++; + die_owner = cpu; + console_verbose(); + bust_spinlocks(1); + return flags; +} + +void __kprobes oops_end(unsigned long flags, struct pt_regs *regs, int signr) +{ + die_owner = -1; + bust_spinlocks(0); + die_nest_count--; + if (!die_nest_count) + /* Nest count reaches zero, release the lock. */ + __raw_spin_unlock(&die_lock); + raw_local_irq_restore(flags); + if (!regs) { + oops_exit(); + return; + } + if (in_interrupt()) + panic("Fatal exception in interrupt"); + if (panic_on_oops) + panic("Fatal exception"); + oops_exit(); + do_exit(signr); +} + +int __kprobes __die(const char *str, struct pt_regs *regs, long err) +{ + printk(KERN_EMERG "%s: %04lx [#%d] ", str, err & 0xffff, ++die_counter); +#ifdef CONFIG_PREEMPT + printk("PREEMPT "); +#endif +#ifdef CONFIG_SMP + printk("SMP "); +#endif +#ifdef CONFIG_DEBUG_PAGEALLOC + printk("DEBUG_PAGEALLOC"); +#endif + printk("\n"); + if (notify_die(DIE_OOPS, str, regs, err, + current->thread.trap_no, SIGSEGV) == NOTIFY_STOP) + return 1; + + show_registers(regs); + add_taint(TAINT_DIE); + /* Executive summary in case the oops scrolled away */ + printk(KERN_ALERT "RIP "); + printk_address(regs->ip, 1); + printk(" RSP <%016lx>\n", regs->sp); + if (kexec_should_crash(current)) + crash_kexec(regs); + return 0; +} + +void die(const char *str, struct pt_regs *regs, long err) +{ + unsigned long flags = oops_begin(); + + if (!user_mode(regs)) + report_bug(regs->ip, regs); + + if (__die(str, regs, err)) + regs = NULL; + oops_end(flags, regs, SIGSEGV); +} + +notrace __kprobes void +die_nmi(char *str, struct pt_regs *regs, int do_panic) +{ + unsigned long flags; + + if (notify_die(DIE_NMIWATCHDOG, str, regs, 0, 2, SIGINT) == NOTIFY_STOP) + return; + + flags = oops_begin(); + /* + * We are in trouble anyway, lets at least try + * to get a message out. + */ + printk(KERN_EMERG "%s", str); + printk(" on CPU%d, ip %08lx, registers:\n", + smp_processor_id(), regs->ip); + show_registers(regs); + if (kexec_should_crash(current)) + crash_kexec(regs); + if (do_panic || panic_on_oops) + panic("Non maskable interrupt"); + oops_end(flags, NULL, SIGBUS); + nmi_exit(); + local_irq_enable(); + do_exit(SIGBUS); +} + +static int __init oops_setup(char *s) +{ + if (!s) + return -EINVAL; + if (!strcmp(s, "panic")) + panic_on_oops = 1; + return 0; +} +early_param("oops", oops_setup); + +static int __init kstack_setup(char *s) +{ + if (!s) + return -EINVAL; + kstack_depth_to_print = simple_strtoul(s, NULL, 0); + return 0; +} +early_param("kstack", kstack_setup); + +static int __init code_bytes_setup(char *s) +{ + code_bytes = simple_strtoul(s, NULL, 0); + if (code_bytes > 8192) + code_bytes = 8192; + + return 1; +} +__setup("code_bytes=", code_bytes_setup); diff --git a/arch/x86/kernel/e820.c b/arch/x86/kernel/e820.c index 66e48aa2dd1..78e642feac3 100644 --- a/arch/x86/kernel/e820.c +++ b/arch/x86/kernel/e820.c @@ -148,6 +148,9 @@ void __init e820_print_map(char *who) case E820_NVS: printk(KERN_CONT "(ACPI NVS)\n"); break; + case E820_UNUSABLE: + printk("(unusable)\n"); + break; default: printk(KERN_CONT "type %u\n", e820.map[i].type); break; @@ -1260,6 +1263,7 @@ static inline const char *e820_type_to_string(int e820_type) case E820_RAM: return "System RAM"; case E820_ACPI: return "ACPI Tables"; case E820_NVS: return "ACPI Non-volatile Storage"; + case E820_UNUSABLE: return "Unusable memory"; default: return "reserved"; } } @@ -1267,6 +1271,7 @@ static inline const char *e820_type_to_string(int e820_type) /* * Mark e820 reserved areas as busy for the resource manager. */ +static struct resource __initdata *e820_res; void __init e820_reserve_resources(void) { int i; @@ -1274,6 +1279,7 @@ void __init e820_reserve_resources(void) u64 end; res = alloc_bootmem_low(sizeof(struct resource) * e820.nr_map); + e820_res = res; for (i = 0; i < e820.nr_map; i++) { end = e820.map[i].addr + e820.map[i].size - 1; #ifndef CONFIG_RESOURCES_64BIT @@ -1287,7 +1293,14 @@ void __init e820_reserve_resources(void) res->end = end; res->flags = IORESOURCE_MEM | IORESOURCE_BUSY; - insert_resource(&iomem_resource, res); + + /* + * don't register the region that could be conflicted with + * pci device BAR resource and insert them later in + * pcibios_resource_survey() + */ + if (e820.map[i].type != E820_RESERVED || res->start < (1ULL<<20)) + insert_resource(&iomem_resource, res); res++; } @@ -1299,6 +1312,19 @@ void __init e820_reserve_resources(void) } } +void __init e820_reserve_resources_late(void) +{ + int i; + struct resource *res; + + res = e820_res; + for (i = 0; i < e820.nr_map; i++) { + if (!res->parent && res->end) + reserve_region_with_split(&iomem_resource, res->start, res->end, res->name); + res++; + } +} + char *__init default_machine_specific_memory_setup(void) { char *who = "BIOS-e820"; diff --git a/arch/x86/kernel/early-quirks.c b/arch/x86/kernel/early-quirks.c index 24bb5faf5ef..733c4f8d42e 100644 --- a/arch/x86/kernel/early-quirks.c +++ b/arch/x86/kernel/early-quirks.c @@ -95,6 +95,52 @@ static void __init nvidia_bugs(int num, int slot, int func) } +static u32 ati_ixp4x0_rev(int num, int slot, int func) +{ + u32 d; + u8 b; + + b = read_pci_config_byte(num, slot, func, 0xac); + b &= ~(1<<5); + write_pci_config_byte(num, slot, func, 0xac, b); + + d = read_pci_config(num, slot, func, 0x70); + d |= 1<<8; + write_pci_config(num, slot, func, 0x70, d); + + d = read_pci_config(num, slot, func, 0x8); + d &= 0xff; + return d; +} + +static void __init ati_bugs(int num, int slot, int func) +{ +#if defined(CONFIG_ACPI) && defined (CONFIG_X86_IO_APIC) + u32 d; + u8 b; + + if (acpi_use_timer_override) + return; + + d = ati_ixp4x0_rev(num, slot, func); + if (d < 0x82) + acpi_skip_timer_override = 1; + else { + /* check for IRQ0 interrupt swap */ + outb(0x72, 0xcd6); b = inb(0xcd7); + if (!(b & 0x2)) + acpi_skip_timer_override = 1; + } + + if (acpi_skip_timer_override) { + printk(KERN_INFO "SB4X0 revision 0x%x\n", d); + printk(KERN_INFO "Ignoring ACPI timer override.\n"); + printk(KERN_INFO "If you got timer trouble " + "try acpi_use_timer_override\n"); + } +#endif +} + #ifdef CONFIG_DMAR static void __init intel_g33_dmar(int num, int slot, int func) { @@ -128,6 +174,8 @@ static struct chipset early_qrk[] __initdata = { PCI_CLASS_BRIDGE_PCI, PCI_ANY_ID, QFLAG_APPLY_ONCE, via_bugs }, { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_K8_NB, PCI_CLASS_BRIDGE_HOST, PCI_ANY_ID, 0, fix_hypertransport_config }, + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP400_SMBUS, + PCI_CLASS_SERIAL_SMBUS, PCI_ANY_ID, 0, ati_bugs }, #ifdef CONFIG_DMAR { PCI_VENDOR_ID_INTEL, 0x29c0, PCI_CLASS_BRIDGE_HOST, PCI_ANY_ID, 0, intel_g33_dmar }, diff --git a/arch/x86/kernel/early_printk.c b/arch/x86/kernel/early_printk.c index ff9e7350da5..34ad997d383 100644 --- a/arch/x86/kernel/early_printk.c +++ b/arch/x86/kernel/early_printk.c @@ -3,11 +3,19 @@ #include <linux/init.h> #include <linux/string.h> #include <linux/screen_info.h> +#include <linux/usb/ch9.h> +#include <linux/pci_regs.h> +#include <linux/pci_ids.h> +#include <linux/errno.h> #include <asm/io.h> #include <asm/processor.h> #include <asm/fcntl.h> #include <asm/setup.h> #include <xen/hvc-console.h> +#include <asm/pci-direct.h> +#include <asm/pgtable.h> +#include <asm/fixmap.h> +#include <linux/usb/ehci_def.h> /* Simple VGA output */ #define VGABASE (__ISA_IO_base + 0xb8000) @@ -78,6 +86,7 @@ static int early_serial_base = 0x3f8; /* ttyS0 */ static int early_serial_putc(unsigned char ch) { unsigned timeout = 0xffff; + while ((inb(early_serial_base + LSR) & XMTRDY) == 0 && --timeout) cpu_relax(); outb(ch, early_serial_base + TXR); @@ -111,7 +120,7 @@ static __init void early_serial_init(char *s) if (!strncmp(s, "0x", 2)) { early_serial_base = simple_strtoul(s, &e, 16); } else { - static int bases[] = { 0x3f8, 0x2f8 }; + static const int __initconst bases[] = { 0x3f8, 0x2f8 }; if (!strncmp(s, "ttyS", 4)) s += 4; @@ -151,6 +160,721 @@ static struct console early_serial_console = { .index = -1, }; +#ifdef CONFIG_EARLY_PRINTK_DBGP + +static struct ehci_caps __iomem *ehci_caps; +static struct ehci_regs __iomem *ehci_regs; +static struct ehci_dbg_port __iomem *ehci_debug; +static unsigned int dbgp_endpoint_out; + +struct ehci_dev { + u32 bus; + u32 slot; + u32 func; +}; + +static struct ehci_dev ehci_dev; + +#define USB_DEBUG_DEVNUM 127 + +#define DBGP_DATA_TOGGLE 0x8800 + +static inline u32 dbgp_pid_update(u32 x, u32 tok) +{ + return ((x ^ DBGP_DATA_TOGGLE) & 0xffff00) | (tok & 0xff); +} + +static inline u32 dbgp_len_update(u32 x, u32 len) +{ + return (x & ~0x0f) | (len & 0x0f); +} + +/* + * USB Packet IDs (PIDs) + */ + +/* token */ +#define USB_PID_OUT 0xe1 +#define USB_PID_IN 0x69 +#define USB_PID_SOF 0xa5 +#define USB_PID_SETUP 0x2d +/* handshake */ +#define USB_PID_ACK 0xd2 +#define USB_PID_NAK 0x5a +#define USB_PID_STALL 0x1e +#define USB_PID_NYET 0x96 +/* data */ +#define USB_PID_DATA0 0xc3 +#define USB_PID_DATA1 0x4b +#define USB_PID_DATA2 0x87 +#define USB_PID_MDATA 0x0f +/* Special */ +#define USB_PID_PREAMBLE 0x3c +#define USB_PID_ERR 0x3c +#define USB_PID_SPLIT 0x78 +#define USB_PID_PING 0xb4 +#define USB_PID_UNDEF_0 0xf0 + +#define USB_PID_DATA_TOGGLE 0x88 +#define DBGP_CLAIM (DBGP_OWNER | DBGP_ENABLED | DBGP_INUSE) + +#define PCI_CAP_ID_EHCI_DEBUG 0xa + +#define HUB_ROOT_RESET_TIME 50 /* times are in msec */ +#define HUB_SHORT_RESET_TIME 10 +#define HUB_LONG_RESET_TIME 200 +#define HUB_RESET_TIMEOUT 500 + +#define DBGP_MAX_PACKET 8 + +static int dbgp_wait_until_complete(void) +{ + u32 ctrl; + int loop = 0x100000; + + do { + ctrl = readl(&ehci_debug->control); + /* Stop when the transaction is finished */ + if (ctrl & DBGP_DONE) + break; + } while (--loop > 0); + + if (!loop) + return -1; + + /* + * Now that we have observed the completed transaction, + * clear the done bit. + */ + writel(ctrl | DBGP_DONE, &ehci_debug->control); + return (ctrl & DBGP_ERROR) ? -DBGP_ERRCODE(ctrl) : DBGP_LEN(ctrl); +} + +static void dbgp_mdelay(int ms) +{ + int i; + + while (ms--) { + for (i = 0; i < 1000; i++) + outb(0x1, 0x80); + } +} + +static void dbgp_breath(void) +{ + /* Sleep to give the debug port a chance to breathe */ +} + +static int dbgp_wait_until_done(unsigned ctrl) +{ + u32 pids, lpid; + int ret; + int loop = 3; + +retry: + writel(ctrl | DBGP_GO, &ehci_debug->control); + ret = dbgp_wait_until_complete(); + pids = readl(&ehci_debug->pids); + lpid = DBGP_PID_GET(pids); + + if (ret < 0) + return ret; + + /* + * If the port is getting full or it has dropped data + * start pacing ourselves, not necessary but it's friendly. + */ + if ((lpid == USB_PID_NAK) || (lpid == USB_PID_NYET)) + dbgp_breath(); + + /* If I get a NACK reissue the transmission */ + if (lpid == USB_PID_NAK) { + if (--loop > 0) + goto retry; + } + + return ret; +} + +static void dbgp_set_data(const void *buf, int size) +{ + const unsigned char *bytes = buf; + u32 lo, hi; + int i; + + lo = hi = 0; + for (i = 0; i < 4 && i < size; i++) + lo |= bytes[i] << (8*i); + for (; i < 8 && i < size; i++) + hi |= bytes[i] << (8*(i - 4)); + writel(lo, &ehci_debug->data03); + writel(hi, &ehci_debug->data47); +} + +static void dbgp_get_data(void *buf, int size) +{ + unsigned char *bytes = buf; + u32 lo, hi; + int i; + + lo = readl(&ehci_debug->data03); + hi = readl(&ehci_debug->data47); + for (i = 0; i < 4 && i < size; i++) + bytes[i] = (lo >> (8*i)) & 0xff; + for (; i < 8 && i < size; i++) + bytes[i] = (hi >> (8*(i - 4))) & 0xff; +} + +static int dbgp_bulk_write(unsigned devnum, unsigned endpoint, + const char *bytes, int size) +{ + u32 pids, addr, ctrl; + int ret; + + if (size > DBGP_MAX_PACKET) + return -1; + + addr = DBGP_EPADDR(devnum, endpoint); + + pids = readl(&ehci_debug->pids); + pids = dbgp_pid_update(pids, USB_PID_OUT); + + ctrl = readl(&ehci_debug->control); + ctrl = dbgp_len_update(ctrl, size); + ctrl |= DBGP_OUT; + ctrl |= DBGP_GO; + + dbgp_set_data(bytes, size); + writel(addr, &ehci_debug->address); + writel(pids, &ehci_debug->pids); + + ret = dbgp_wait_until_done(ctrl); + if (ret < 0) + return ret; + + return ret; +} + +static int dbgp_bulk_read(unsigned devnum, unsigned endpoint, void *data, + int size) +{ + u32 pids, addr, ctrl; + int ret; + + if (size > DBGP_MAX_PACKET) + return -1; + + addr = DBGP_EPADDR(devnum, endpoint); + + pids = readl(&ehci_debug->pids); + pids = dbgp_pid_update(pids, USB_PID_IN); + + ctrl = readl(&ehci_debug->control); + ctrl = dbgp_len_update(ctrl, size); + ctrl &= ~DBGP_OUT; + ctrl |= DBGP_GO; + + writel(addr, &ehci_debug->address); + writel(pids, &ehci_debug->pids); + ret = dbgp_wait_until_done(ctrl); + if (ret < 0) + return ret; + + if (size > ret) + size = ret; + dbgp_get_data(data, size); + return ret; +} + +static int dbgp_control_msg(unsigned devnum, int requesttype, int request, + int value, int index, void *data, int size) +{ + u32 pids, addr, ctrl; + struct usb_ctrlrequest req; + int read; + int ret; + + read = (requesttype & USB_DIR_IN) != 0; + if (size > (read ? DBGP_MAX_PACKET:0)) + return -1; + + /* Compute the control message */ + req.bRequestType = requesttype; + req.bRequest = request; + req.wValue = cpu_to_le16(value); + req.wIndex = cpu_to_le16(index); + req.wLength = cpu_to_le16(size); + + pids = DBGP_PID_SET(USB_PID_DATA0, USB_PID_SETUP); + addr = DBGP_EPADDR(devnum, 0); + + ctrl = readl(&ehci_debug->control); + ctrl = dbgp_len_update(ctrl, sizeof(req)); + ctrl |= DBGP_OUT; + ctrl |= DBGP_GO; + + /* Send the setup message */ + dbgp_set_data(&req, sizeof(req)); + writel(addr, &ehci_debug->address); + writel(pids, &ehci_debug->pids); + ret = dbgp_wait_until_done(ctrl); + if (ret < 0) + return ret; + + /* Read the result */ + return dbgp_bulk_read(devnum, 0, data, size); +} + + +/* Find a PCI capability */ +static u32 __init find_cap(u32 num, u32 slot, u32 func, int cap) +{ + u8 pos; + int bytes; + + if (!(read_pci_config_16(num, slot, func, PCI_STATUS) & + PCI_STATUS_CAP_LIST)) + return 0; + + pos = read_pci_config_byte(num, slot, func, PCI_CAPABILITY_LIST); + for (bytes = 0; bytes < 48 && pos >= 0x40; bytes++) { + u8 id; + + pos &= ~3; + id = read_pci_config_byte(num, slot, func, pos+PCI_CAP_LIST_ID); + if (id == 0xff) + break; + if (id == cap) + return pos; + + pos = read_pci_config_byte(num, slot, func, + pos+PCI_CAP_LIST_NEXT); + } + return 0; +} + +static u32 __init __find_dbgp(u32 bus, u32 slot, u32 func) +{ + u32 class; + + class = read_pci_config(bus, slot, func, PCI_CLASS_REVISION); + if ((class >> 8) != PCI_CLASS_SERIAL_USB_EHCI) + return 0; + + return find_cap(bus, slot, func, PCI_CAP_ID_EHCI_DEBUG); +} + +static u32 __init find_dbgp(int ehci_num, u32 *rbus, u32 *rslot, u32 *rfunc) +{ + u32 bus, slot, func; + + for (bus = 0; bus < 256; bus++) { + for (slot = 0; slot < 32; slot++) { + for (func = 0; func < 8; func++) { + unsigned cap; + + cap = __find_dbgp(bus, slot, func); + + if (!cap) + continue; + if (ehci_num-- != 0) + continue; + *rbus = bus; + *rslot = slot; + *rfunc = func; + return cap; + } + } + } + return 0; +} + +static int ehci_reset_port(int port) +{ + u32 portsc; + u32 delay_time, delay; + int loop; + + /* Reset the usb debug port */ + portsc = readl(&ehci_regs->port_status[port - 1]); + portsc &= ~PORT_PE; + portsc |= PORT_RESET; + writel(portsc, &ehci_regs->port_status[port - 1]); + + delay = HUB_ROOT_RESET_TIME; + for (delay_time = 0; delay_time < HUB_RESET_TIMEOUT; + delay_time += delay) { + dbgp_mdelay(delay); + + portsc = readl(&ehci_regs->port_status[port - 1]); + if (portsc & PORT_RESET) { + /* force reset to complete */ + loop = 2; + writel(portsc & ~(PORT_RWC_BITS | PORT_RESET), + &ehci_regs->port_status[port - 1]); + do { + portsc = readl(&ehci_regs->port_status[port-1]); + } while ((portsc & PORT_RESET) && (--loop > 0)); + } + + /* Device went away? */ + if (!(portsc & PORT_CONNECT)) + return -ENOTCONN; + + /* bomb out completely if something weird happend */ + if ((portsc & PORT_CSC)) + return -EINVAL; + + /* If we've finished resetting, then break out of the loop */ + if (!(portsc & PORT_RESET) && (portsc & PORT_PE)) + return 0; + } + return -EBUSY; +} + +static int ehci_wait_for_port(int port) +{ + u32 status; + int ret, reps; + + for (reps = 0; reps < 3; reps++) { + dbgp_mdelay(100); + status = readl(&ehci_regs->status); + if (status & STS_PCD) { + ret = ehci_reset_port(port); + if (ret == 0) + return 0; + } + } + return -ENOTCONN; +} + +#ifdef DBGP_DEBUG +# define dbgp_printk early_printk +#else +static inline void dbgp_printk(const char *fmt, ...) { } +#endif + +typedef void (*set_debug_port_t)(int port); + +static void default_set_debug_port(int port) +{ +} + +static set_debug_port_t set_debug_port = default_set_debug_port; + +static void nvidia_set_debug_port(int port) +{ + u32 dword; + dword = read_pci_config(ehci_dev.bus, ehci_dev.slot, ehci_dev.func, + 0x74); + dword &= ~(0x0f<<12); + dword |= ((port & 0x0f)<<12); + write_pci_config(ehci_dev.bus, ehci_dev.slot, ehci_dev.func, 0x74, + dword); + dbgp_printk("set debug port to %d\n", port); +} + +static void __init detect_set_debug_port(void) +{ + u32 vendorid; + + vendorid = read_pci_config(ehci_dev.bus, ehci_dev.slot, ehci_dev.func, + 0x00); + + if ((vendorid & 0xffff) == 0x10de) { + dbgp_printk("using nvidia set_debug_port\n"); + set_debug_port = nvidia_set_debug_port; + } +} + +static int __init ehci_setup(void) +{ + struct usb_debug_descriptor dbgp_desc; + u32 cmd, ctrl, status, portsc, hcs_params; + u32 debug_port, new_debug_port = 0, n_ports; + u32 devnum; + int ret, i; + int loop; + int port_map_tried; + int playtimes = 3; + +try_next_time: + port_map_tried = 0; + +try_next_port: + + hcs_params = readl(&ehci_caps->hcs_params); + debug_port = HCS_DEBUG_PORT(hcs_params); + n_ports = HCS_N_PORTS(hcs_params); + + dbgp_printk("debug_port: %d\n", debug_port); + dbgp_printk("n_ports: %d\n", n_ports); + + for (i = 1; i <= n_ports; i++) { + portsc = readl(&ehci_regs->port_status[i-1]); + dbgp_printk("portstatus%d: %08x\n", i, portsc); + } + + if (port_map_tried && (new_debug_port != debug_port)) { + if (--playtimes) { + set_debug_port(new_debug_port); + goto try_next_time; + } + return -1; + } + + loop = 10; + /* Reset the EHCI controller */ + cmd = readl(&ehci_regs->command); + cmd |= CMD_RESET; + writel(cmd, &ehci_regs->command); + do { + cmd = readl(&ehci_regs->command); + } while ((cmd & CMD_RESET) && (--loop > 0)); + + if (!loop) { + dbgp_printk("can not reset ehci\n"); + return -1; + } + dbgp_printk("ehci reset done\n"); + + /* Claim ownership, but do not enable yet */ + ctrl = readl(&ehci_debug->control); + ctrl |= DBGP_OWNER; + ctrl &= ~(DBGP_ENABLED | DBGP_INUSE); + writel(ctrl, &ehci_debug->control); + + /* Start the ehci running */ + cmd = readl(&ehci_regs->command); + cmd &= ~(CMD_LRESET | CMD_IAAD | CMD_PSE | CMD_ASE | CMD_RESET); + cmd |= CMD_RUN; + writel(cmd, &ehci_regs->command); + + /* Ensure everything is routed to the EHCI */ + writel(FLAG_CF, &ehci_regs->configured_flag); + + /* Wait until the controller is no longer halted */ + loop = 10; + do { + status = readl(&ehci_regs->status); + } while ((status & STS_HALT) && (--loop > 0)); + + if (!loop) { + dbgp_printk("ehci can be started\n"); + return -1; + } + dbgp_printk("ehci started\n"); + + /* Wait for a device to show up in the debug port */ + ret = ehci_wait_for_port(debug_port); + if (ret < 0) { + dbgp_printk("No device found in debug port\n"); + goto next_debug_port; + } + dbgp_printk("ehci wait for port done\n"); + + /* Enable the debug port */ + ctrl = readl(&ehci_debug->control); + ctrl |= DBGP_CLAIM; + writel(ctrl, &ehci_debug->control); + ctrl = readl(&ehci_debug->control); + if ((ctrl & DBGP_CLAIM) != DBGP_CLAIM) { + dbgp_printk("No device in debug port\n"); + writel(ctrl & ~DBGP_CLAIM, &ehci_debug->control); + goto err; + } + dbgp_printk("debug ported enabled\n"); + + /* Completely transfer the debug device to the debug controller */ + portsc = readl(&ehci_regs->port_status[debug_port - 1]); + portsc &= ~PORT_PE; + writel(portsc, &ehci_regs->port_status[debug_port - 1]); + + dbgp_mdelay(100); + + /* Find the debug device and make it device number 127 */ + for (devnum = 0; devnum <= 127; devnum++) { + ret = dbgp_control_msg(devnum, + USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE, + USB_REQ_GET_DESCRIPTOR, (USB_DT_DEBUG << 8), 0, + &dbgp_desc, sizeof(dbgp_desc)); + if (ret > 0) + break; + } + if (devnum > 127) { + dbgp_printk("Could not find attached debug device\n"); + goto err; + } + if (ret < 0) { + dbgp_printk("Attached device is not a debug device\n"); + goto err; + } + dbgp_endpoint_out = dbgp_desc.bDebugOutEndpoint; + + /* Move the device to 127 if it isn't already there */ + if (devnum != USB_DEBUG_DEVNUM) { + ret = dbgp_control_msg(devnum, + USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE, + USB_REQ_SET_ADDRESS, USB_DEBUG_DEVNUM, 0, NULL, 0); + if (ret < 0) { + dbgp_printk("Could not move attached device to %d\n", + USB_DEBUG_DEVNUM); + goto err; + } + devnum = USB_DEBUG_DEVNUM; + dbgp_printk("debug device renamed to 127\n"); + } + + /* Enable the debug interface */ + ret = dbgp_control_msg(USB_DEBUG_DEVNUM, + USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE, + USB_REQ_SET_FEATURE, USB_DEVICE_DEBUG_MODE, 0, NULL, 0); + if (ret < 0) { + dbgp_printk(" Could not enable the debug device\n"); + goto err; + } + dbgp_printk("debug interface enabled\n"); + + /* Perform a small write to get the even/odd data state in sync + */ + ret = dbgp_bulk_write(USB_DEBUG_DEVNUM, dbgp_endpoint_out, " ", 1); + if (ret < 0) { + dbgp_printk("dbgp_bulk_write failed: %d\n", ret); + goto err; + } + dbgp_printk("small write doned\n"); + + return 0; +err: + /* Things didn't work so remove my claim */ + ctrl = readl(&ehci_debug->control); + ctrl &= ~(DBGP_CLAIM | DBGP_OUT); + writel(ctrl, &ehci_debug->control); + return -1; + +next_debug_port: + port_map_tried |= (1<<(debug_port - 1)); + new_debug_port = ((debug_port-1+1)%n_ports) + 1; + if (port_map_tried != ((1<<n_ports) - 1)) { + set_debug_port(new_debug_port); + goto try_next_port; + } + if (--playtimes) { + set_debug_port(new_debug_port); + goto try_next_time; + } + + return -1; +} + +static int __init early_dbgp_init(char *s) +{ + u32 debug_port, bar, offset; + u32 bus, slot, func, cap; + void __iomem *ehci_bar; + u32 dbgp_num; + u32 bar_val; + char *e; + int ret; + u8 byte; + + if (!early_pci_allowed()) + return -1; + + dbgp_num = 0; + if (*s) + dbgp_num = simple_strtoul(s, &e, 10); + dbgp_printk("dbgp_num: %d\n", dbgp_num); + + cap = find_dbgp(dbgp_num, &bus, &slot, &func); + if (!cap) + return -1; + + dbgp_printk("Found EHCI debug port on %02x:%02x.%1x\n", bus, slot, + func); + + debug_port = read_pci_config(bus, slot, func, cap); + bar = (debug_port >> 29) & 0x7; + bar = (bar * 4) + 0xc; + offset = (debug_port >> 16) & 0xfff; + dbgp_printk("bar: %02x offset: %03x\n", bar, offset); + if (bar != PCI_BASE_ADDRESS_0) { + dbgp_printk("only debug ports on bar 1 handled.\n"); + + return -1; + } + + bar_val = read_pci_config(bus, slot, func, PCI_BASE_ADDRESS_0); + dbgp_printk("bar_val: %02x offset: %03x\n", bar_val, offset); + if (bar_val & ~PCI_BASE_ADDRESS_MEM_MASK) { + dbgp_printk("only simple 32bit mmio bars supported\n"); + + return -1; + } + + /* double check if the mem space is enabled */ + byte = read_pci_config_byte(bus, slot, func, 0x04); + if (!(byte & 0x2)) { + byte |= 0x02; + write_pci_config_byte(bus, slot, func, 0x04, byte); + dbgp_printk("mmio for ehci enabled\n"); + } + + /* + * FIXME I don't have the bar size so just guess PAGE_SIZE is more + * than enough. 1K is the biggest I have seen. + */ + set_fixmap_nocache(FIX_DBGP_BASE, bar_val & PAGE_MASK); + ehci_bar = (void __iomem *)__fix_to_virt(FIX_DBGP_BASE); + ehci_bar += bar_val & ~PAGE_MASK; + dbgp_printk("ehci_bar: %p\n", ehci_bar); + + ehci_caps = ehci_bar; + ehci_regs = ehci_bar + HC_LENGTH(readl(&ehci_caps->hc_capbase)); + ehci_debug = ehci_bar + offset; + ehci_dev.bus = bus; + ehci_dev.slot = slot; + ehci_dev.func = func; + + detect_set_debug_port(); + + ret = ehci_setup(); + if (ret < 0) { + dbgp_printk("ehci_setup failed\n"); + ehci_debug = NULL; + + return -1; + } + + return 0; +} + +static void early_dbgp_write(struct console *con, const char *str, u32 n) +{ + int chunk, ret; + + if (!ehci_debug) + return; + while (n > 0) { + chunk = n; + if (chunk > DBGP_MAX_PACKET) + chunk = DBGP_MAX_PACKET; + ret = dbgp_bulk_write(USB_DEBUG_DEVNUM, + dbgp_endpoint_out, str, chunk); + str += chunk; + n -= chunk; + } +} + +static struct console early_dbgp_console = { + .name = "earlydbg", + .write = early_dbgp_write, + .flags = CON_PRINTBUFFER, + .index = -1, +}; +#endif + /* Console interface to a host file on AMD's SimNow! */ static int simnow_fd; @@ -165,6 +889,7 @@ enum { static noinline long simnow(long cmd, long a, long b, long c) { long ret; + asm volatile("cpuid" : "=a" (ret) : "b" (a), "c" (b), "d" (c), "0" (MAGIC1), "D" (cmd + MAGIC2)); @@ -174,6 +899,7 @@ static noinline long simnow(long cmd, long a, long b, long c) static void __init simnow_init(char *str) { char *fn = "klog"; + if (*str == '=') fn = ++str; /* error ignored */ @@ -194,7 +920,7 @@ static struct console simnow_console = { /* Direct interface for emergencies */ static struct console *early_console = &early_vga_console; -static int early_console_initialized; +static int __initdata early_console_initialized; asmlinkage void early_printk(const char *fmt, ...) { @@ -208,10 +934,11 @@ asmlinkage void early_printk(const char *fmt, ...) va_end(ap); } -static int __initdata keep_early; static int __init setup_early_printk(char *buf) { + int keep_early; + if (!buf) return 0; @@ -219,8 +946,7 @@ static int __init setup_early_printk(char *buf) return 0; early_console_initialized = 1; - if (strstr(buf, "keep")) - keep_early = 1; + keep_early = (strstr(buf, "keep") != NULL); if (!strncmp(buf, "serial", 6)) { early_serial_init(buf + 6); @@ -238,6 +964,17 @@ static int __init setup_early_printk(char *buf) simnow_init(buf + 6); early_console = &simnow_console; keep_early = 1; +#ifdef CONFIG_EARLY_PRINTK_DBGP + } else if (!strncmp(buf, "dbgp", 4)) { + if (early_dbgp_init(buf+4) < 0) + return 0; + early_console = &early_dbgp_console; + /* + * usb subsys will reset ehci controller, so don't keep + * that early console + */ + keep_early = 0; +#endif #ifdef CONFIG_HVC_XEN } else if (!strncmp(buf, "xen", 3)) { early_console = &xenboot_console; @@ -251,4 +988,5 @@ static int __init setup_early_printk(char *buf) register_console(early_console); return 0; } + early_param("earlyprintk", setup_early_printk); diff --git a/arch/x86/kernel/entry_32.S b/arch/x86/kernel/entry_32.S index 109792bc7cf..b21fbfaffe3 100644 --- a/arch/x86/kernel/entry_32.S +++ b/arch/x86/kernel/entry_32.S @@ -730,6 +730,7 @@ error_code: movl $(__USER_DS), %ecx movl %ecx, %ds movl %ecx, %es + TRACE_IRQS_OFF movl %esp,%eax # pt_regs pointer call *%edi jmp ret_from_exception @@ -760,20 +761,9 @@ ENTRY(device_not_available) RING0_INT_FRAME pushl $-1 # mark this as an int CFI_ADJUST_CFA_OFFSET 4 - SAVE_ALL - GET_CR0_INTO_EAX - testl $0x4, %eax # EM (math emulation bit) - jne device_not_available_emulate - preempt_stop(CLBR_ANY) - call math_state_restore - jmp ret_from_exception -device_not_available_emulate: - pushl $0 # temporary storage for ORIG_EIP + pushl $do_device_not_available CFI_ADJUST_CFA_OFFSET 4 - call math_emulate - addl $4, %esp - CFI_ADJUST_CFA_OFFSET -4 - jmp ret_from_exception + jmp error_code CFI_ENDPROC END(device_not_available) @@ -814,6 +804,7 @@ debug_stack_correct: pushl $-1 # mark this as an int CFI_ADJUST_CFA_OFFSET 4 SAVE_ALL + TRACE_IRQS_OFF xorl %edx,%edx # error code 0 movl %esp,%eax # pt_regs pointer call do_debug @@ -858,6 +849,7 @@ nmi_stack_correct: pushl %eax CFI_ADJUST_CFA_OFFSET 4 SAVE_ALL + TRACE_IRQS_OFF xorl %edx,%edx # zero error code movl %esp,%eax # pt_regs pointer call do_nmi @@ -898,6 +890,7 @@ nmi_espfix_stack: pushl %eax CFI_ADJUST_CFA_OFFSET 4 SAVE_ALL + TRACE_IRQS_OFF FIXUP_ESPFIX_STACK # %eax == %esp xorl %edx,%edx # zero error code call do_nmi @@ -928,6 +921,7 @@ KPROBE_ENTRY(int3) pushl $-1 # mark this as an int CFI_ADJUST_CFA_OFFSET 4 SAVE_ALL + TRACE_IRQS_OFF xorl %edx,%edx # zero error code movl %esp,%eax # pt_regs pointer call do_int3 @@ -1030,7 +1024,7 @@ ENTRY(machine_check) RING0_INT_FRAME pushl $0 CFI_ADJUST_CFA_OFFSET 4 - pushl machine_check_vector + pushl $do_machine_check CFI_ADJUST_CFA_OFFSET 4 jmp error_code CFI_ENDPROC diff --git a/arch/x86/kernel/entry_64.S b/arch/x86/kernel/entry_64.S index cf3a0b2d005..1db6ce4314e 100644 --- a/arch/x86/kernel/entry_64.S +++ b/arch/x86/kernel/entry_64.S @@ -667,6 +667,13 @@ END(stub_rt_sigreturn) SAVE_ARGS leaq -ARGOFFSET(%rsp),%rdi # arg1 for handler pushq %rbp + /* + * Save rbp twice: One is for marking the stack frame, as usual, and the + * other, to fill pt_regs properly. This is because bx comes right + * before the last saved register in that structure, and not bp. If the + * base pointer were in the place bx is today, this would not be needed. + */ + movq %rbp, -8(%rsp) CFI_ADJUST_CFA_OFFSET 8 CFI_REL_OFFSET rbp, 0 movq %rsp,%rbp @@ -932,6 +939,9 @@ END(spurious_interrupt) .if \ist movq %gs:pda_data_offset, %rbp .endif + .if \irqtrace + TRACE_IRQS_OFF + .endif movq %rsp,%rdi movq ORIG_RAX(%rsp),%rsi movq $-1,ORIG_RAX(%rsp) @@ -1058,7 +1068,8 @@ KPROBE_ENTRY(error_entry) je error_kernelspace error_swapgs: SWAPGS -error_sti: +error_sti: + TRACE_IRQS_OFF movq %rdi,RDI(%rsp) CFI_REL_OFFSET rdi,RDI movq %rsp,%rdi @@ -1232,7 +1243,7 @@ ENTRY(simd_coprocessor_error) END(simd_coprocessor_error) ENTRY(device_not_available) - zeroentry math_state_restore + zeroentry do_device_not_available END(device_not_available) /* runs on exception stack */ diff --git a/arch/x86/mach-es7000/es7000plat.c b/arch/x86/kernel/es7000_32.c index 50189af14b8..f454c78fcef 100644 --- a/arch/x86/mach-es7000/es7000plat.c +++ b/arch/x86/kernel/es7000_32.c @@ -39,10 +39,94 @@ #include <asm/nmi.h> #include <asm/smp.h> #include <asm/apicdef.h> -#include "es7000.h" #include <mach_mpparse.h> /* + * ES7000 chipsets + */ + +#define NON_UNISYS 0 +#define ES7000_CLASSIC 1 +#define ES7000_ZORRO 2 + + +#define MIP_REG 1 +#define MIP_PSAI_REG 4 + +#define MIP_BUSY 1 +#define MIP_SPIN 0xf0000 +#define MIP_VALID 0x0100000000000000ULL +#define MIP_PORT(VALUE) ((VALUE >> 32) & 0xffff) + +#define MIP_RD_LO(VALUE) (VALUE & 0xffffffff) + +struct mip_reg_info { + unsigned long long mip_info; + unsigned long long delivery_info; + unsigned long long host_reg; + unsigned long long mip_reg; +}; + +struct part_info { + unsigned char type; + unsigned char length; + unsigned char part_id; + unsigned char apic_mode; + unsigned long snum; + char ptype[16]; + char sname[64]; + char pname[64]; +}; + +struct psai { + unsigned long long entry_type; + unsigned long long addr; + unsigned long long bep_addr; +}; + +struct es7000_mem_info { + unsigned char type; + unsigned char length; + unsigned char resv[6]; + unsigned long long start; + unsigned long long size; +}; + +struct es7000_oem_table { + unsigned long long hdr; + struct mip_reg_info mip; + struct part_info pif; + struct es7000_mem_info shm; + struct psai psai; +}; + +#ifdef CONFIG_ACPI + +struct oem_table { + struct acpi_table_header Header; + u32 OEMTableAddr; + u32 OEMTableSize; +}; + +extern int find_unisys_acpi_oem_table(unsigned long *oem_addr); +extern void unmap_unisys_acpi_oem_table(unsigned long oem_addr); +#endif + +struct mip_reg { + unsigned long long off_0; + unsigned long long off_8; + unsigned long long off_10; + unsigned long long off_18; + unsigned long long off_20; + unsigned long long off_28; + unsigned long long off_30; + unsigned long long off_38; +}; + +#define MIP_SW_APIC 0x1020b +#define MIP_FUNC(VALUE) (VALUE & 0xff) + +/* * ES7000 Globals */ @@ -72,7 +156,7 @@ es7000_rename_gsi(int ioapic, int gsi) base += nr_ioapic_registers[i]; } - if (!ioapic && (gsi < 16)) + if (!ioapic && (gsi < 16)) gsi += base; return gsi; } @@ -160,21 +244,38 @@ parse_unisys_oem (char *oemptr) } #ifdef CONFIG_ACPI -int __init -find_unisys_acpi_oem_table(unsigned long *oem_addr) +static unsigned long oem_addrX; +static unsigned long oem_size; +int __init find_unisys_acpi_oem_table(unsigned long *oem_addr) { struct acpi_table_header *header = NULL; int i = 0; - while (ACPI_SUCCESS(acpi_get_table("OEM1", i++, &header))) { + acpi_size tbl_size; + + while (ACPI_SUCCESS(acpi_get_table_with_size("OEM1", i++, &header, &tbl_size))) { if (!memcmp((char *) &header->oem_id, "UNISYS", 6)) { struct oem_table *t = (struct oem_table *)header; - *oem_addr = (unsigned long)__acpi_map_table(t->OEMTableAddr, - t->OEMTableSize); + + oem_addrX = t->OEMTableAddr; + oem_size = t->OEMTableSize; + early_acpi_os_unmap_memory(header, tbl_size); + + *oem_addr = (unsigned long)__acpi_map_table(oem_addrX, + oem_size); return 0; } + early_acpi_os_unmap_memory(header, tbl_size); } return -1; } + +void __init unmap_unisys_acpi_oem_table(unsigned long oem_addr) +{ + if (!oem_addr) + return; + + __acpi_unmap_table((char *)oem_addr, oem_size); +} #endif static void diff --git a/arch/x86/kernel/genapic_64.c b/arch/x86/kernel/genapic_64.c index eaff0bbb144..6c9bfc9e1e9 100644 --- a/arch/x86/kernel/genapic_64.c +++ b/arch/x86/kernel/genapic_64.c @@ -16,87 +16,63 @@ #include <linux/ctype.h> #include <linux/init.h> #include <linux/hardirq.h> +#include <linux/dmar.h> #include <asm/smp.h> #include <asm/ipi.h> #include <asm/genapic.h> -#ifdef CONFIG_ACPI -#include <acpi/acpi_bus.h> -#endif - -DEFINE_PER_CPU(int, x2apic_extra_bits); +extern struct genapic apic_flat; +extern struct genapic apic_physflat; +extern struct genapic apic_x2xpic_uv_x; +extern struct genapic apic_x2apic_phys; +extern struct genapic apic_x2apic_cluster; struct genapic __read_mostly *genapic = &apic_flat; -static enum uv_system_type uv_system_type; +static struct genapic *apic_probe[] __initdata = { + &apic_x2apic_uv_x, + &apic_x2apic_phys, + &apic_x2apic_cluster, + &apic_physflat, + NULL, +}; /* * Check the APIC IDs in bios_cpu_apicid and choose the APIC mode. */ void __init setup_apic_routing(void) { - if (uv_system_type == UV_NON_UNIQUE_APIC) - genapic = &apic_x2apic_uv_x; - else -#ifdef CONFIG_ACPI - /* - * Quirk: some x86_64 machines can only use physical APIC mode - * regardless of how many processors are present (x86_64 ES7000 - * is an example). - */ - if (acpi_gbl_FADT.header.revision > FADT2_REVISION_ID && - (acpi_gbl_FADT.flags & ACPI_FADT_APIC_PHYSICAL)) - genapic = &apic_physflat; - else -#endif - - if (max_physical_apicid < 8) - genapic = &apic_flat; - else - genapic = &apic_physflat; + if (genapic == &apic_x2apic_phys || genapic == &apic_x2apic_cluster) { + if (!intr_remapping_enabled) + genapic = &apic_flat; + } - printk(KERN_INFO "Setting APIC routing to %s\n", genapic->name); + if (genapic == &apic_flat) { + if (max_physical_apicid >= 8) + genapic = &apic_physflat; + printk(KERN_INFO "Setting APIC routing to %s\n", genapic->name); + } } /* Same for both flat and physical. */ -void send_IPI_self(int vector) +void apic_send_IPI_self(int vector) { __send_IPI_shortcut(APIC_DEST_SELF, vector, APIC_DEST_PHYSICAL); } int __init acpi_madt_oem_check(char *oem_id, char *oem_table_id) { - if (!strcmp(oem_id, "SGI")) { - if (!strcmp(oem_table_id, "UVL")) - uv_system_type = UV_LEGACY_APIC; - else if (!strcmp(oem_table_id, "UVX")) - uv_system_type = UV_X2APIC; - else if (!strcmp(oem_table_id, "UVH")) - uv_system_type = UV_NON_UNIQUE_APIC; + int i; + + for (i = 0; apic_probe[i]; ++i) { + if (apic_probe[i]->acpi_madt_oem_check(oem_id, oem_table_id)) { + genapic = apic_probe[i]; + printk(KERN_INFO "Setting APIC routing to %s.\n", + genapic->name); + return 1; + } } return 0; } - -unsigned int read_apic_id(void) -{ - unsigned int id; - - WARN_ON(preemptible() && num_online_cpus() > 1); - id = apic_read(APIC_ID); - if (uv_system_type >= UV_X2APIC) - id |= __get_cpu_var(x2apic_extra_bits); - return id; -} - -enum uv_system_type get_uv_system_type(void) -{ - return uv_system_type; -} - -int is_uv_system(void) -{ - return uv_system_type != UV_NONE; -} -EXPORT_SYMBOL_GPL(is_uv_system); diff --git a/arch/x86/kernel/genapic_flat_64.c b/arch/x86/kernel/genapic_flat_64.c index 786548a62d3..9eca5ba7a6b 100644 --- a/arch/x86/kernel/genapic_flat_64.c +++ b/arch/x86/kernel/genapic_flat_64.c @@ -15,9 +15,20 @@ #include <linux/kernel.h> #include <linux/ctype.h> #include <linux/init.h> +#include <linux/hardirq.h> #include <asm/smp.h> #include <asm/ipi.h> #include <asm/genapic.h> +#include <mach_apicdef.h> + +#ifdef CONFIG_ACPI +#include <acpi/acpi_bus.h> +#endif + +static int __init flat_acpi_madt_oem_check(char *oem_id, char *oem_table_id) +{ + return 1; +} static cpumask_t flat_target_cpus(void) { @@ -95,9 +106,33 @@ static void flat_send_IPI_all(int vector) __send_IPI_shortcut(APIC_DEST_ALLINC, vector, APIC_DEST_LOGICAL); } +static unsigned int get_apic_id(unsigned long x) +{ + unsigned int id; + + id = (((x)>>24) & 0xFFu); + return id; +} + +static unsigned long set_apic_id(unsigned int id) +{ + unsigned long x; + + x = ((id & 0xFFu)<<24); + return x; +} + +static unsigned int read_xapic_id(void) +{ + unsigned int id; + + id = get_apic_id(apic_read(APIC_ID)); + return id; +} + static int flat_apic_id_registered(void) { - return physid_isset(GET_APIC_ID(read_apic_id()), phys_cpu_present_map); + return physid_isset(read_xapic_id(), phys_cpu_present_map); } static unsigned int flat_cpu_mask_to_apicid(cpumask_t cpumask) @@ -112,6 +147,7 @@ static unsigned int phys_pkg_id(int index_msb) struct genapic apic_flat = { .name = "flat", + .acpi_madt_oem_check = flat_acpi_madt_oem_check, .int_delivery_mode = dest_LowestPrio, .int_dest_mode = (APIC_DEST_LOGICAL != 0), .target_cpus = flat_target_cpus, @@ -121,8 +157,12 @@ struct genapic apic_flat = { .send_IPI_all = flat_send_IPI_all, .send_IPI_allbutself = flat_send_IPI_allbutself, .send_IPI_mask = flat_send_IPI_mask, + .send_IPI_self = apic_send_IPI_self, .cpu_mask_to_apicid = flat_cpu_mask_to_apicid, .phys_pkg_id = phys_pkg_id, + .get_apic_id = get_apic_id, + .set_apic_id = set_apic_id, + .apic_id_mask = (0xFFu<<24), }; /* @@ -130,6 +170,21 @@ struct genapic apic_flat = { * We cannot use logical delivery in this case because the mask * overflows, so use physical mode. */ +static int __init physflat_acpi_madt_oem_check(char *oem_id, char *oem_table_id) +{ +#ifdef CONFIG_ACPI + /* + * Quirk: some x86_64 machines can only use physical APIC mode + * regardless of how many processors are present (x86_64 ES7000 + * is an example). + */ + if (acpi_gbl_FADT.header.revision > FADT2_REVISION_ID && + (acpi_gbl_FADT.flags & ACPI_FADT_APIC_PHYSICAL)) + return 1; +#endif + + return 0; +} static cpumask_t physflat_target_cpus(void) { @@ -176,6 +231,7 @@ static unsigned int physflat_cpu_mask_to_apicid(cpumask_t cpumask) struct genapic apic_physflat = { .name = "physical flat", + .acpi_madt_oem_check = physflat_acpi_madt_oem_check, .int_delivery_mode = dest_Fixed, .int_dest_mode = (APIC_DEST_PHYSICAL != 0), .target_cpus = physflat_target_cpus, @@ -185,6 +241,10 @@ struct genapic apic_physflat = { .send_IPI_all = physflat_send_IPI_all, .send_IPI_allbutself = physflat_send_IPI_allbutself, .send_IPI_mask = physflat_send_IPI_mask, + .send_IPI_self = apic_send_IPI_self, .cpu_mask_to_apicid = physflat_cpu_mask_to_apicid, .phys_pkg_id = phys_pkg_id, + .get_apic_id = get_apic_id, + .set_apic_id = set_apic_id, + .apic_id_mask = (0xFFu<<24), }; diff --git a/arch/x86/kernel/genx2apic_cluster.c b/arch/x86/kernel/genx2apic_cluster.c new file mode 100644 index 00000000000..e4bf2cc0d74 --- /dev/null +++ b/arch/x86/kernel/genx2apic_cluster.c @@ -0,0 +1,159 @@ +#include <linux/threads.h> +#include <linux/cpumask.h> +#include <linux/string.h> +#include <linux/kernel.h> +#include <linux/ctype.h> +#include <linux/init.h> +#include <linux/dmar.h> + +#include <asm/smp.h> +#include <asm/ipi.h> +#include <asm/genapic.h> + +DEFINE_PER_CPU(u32, x86_cpu_to_logical_apicid); + +static int __init x2apic_acpi_madt_oem_check(char *oem_id, char *oem_table_id) +{ + if (cpu_has_x2apic) + return 1; + + return 0; +} + +/* Start with all IRQs pointing to boot CPU. IRQ balancing will shift them. */ + +static cpumask_t x2apic_target_cpus(void) +{ + return cpumask_of_cpu(0); +} + +/* + * for now each logical cpu is in its own vector allocation domain. + */ +static cpumask_t x2apic_vector_allocation_domain(int cpu) +{ + cpumask_t domain = CPU_MASK_NONE; + cpu_set(cpu, domain); + return domain; +} + +static void __x2apic_send_IPI_dest(unsigned int apicid, int vector, + unsigned int dest) +{ + unsigned long cfg; + + cfg = __prepare_ICR(0, vector, dest); + + /* + * send the IPI. + */ + x2apic_icr_write(cfg, apicid); +} + +/* + * for now, we send the IPI's one by one in the cpumask. + * TBD: Based on the cpu mask, we can send the IPI's to the cluster group + * at once. We have 16 cpu's in a cluster. This will minimize IPI register + * writes. + */ +static void x2apic_send_IPI_mask(cpumask_t mask, int vector) +{ + unsigned long flags; + unsigned long query_cpu; + + local_irq_save(flags); + for_each_cpu_mask(query_cpu, mask) { + __x2apic_send_IPI_dest(per_cpu(x86_cpu_to_logical_apicid, query_cpu), + vector, APIC_DEST_LOGICAL); + } + local_irq_restore(flags); +} + +static void x2apic_send_IPI_allbutself(int vector) +{ + cpumask_t mask = cpu_online_map; + + cpu_clear(smp_processor_id(), mask); + + if (!cpus_empty(mask)) + x2apic_send_IPI_mask(mask, vector); +} + +static void x2apic_send_IPI_all(int vector) +{ + x2apic_send_IPI_mask(cpu_online_map, vector); +} + +static int x2apic_apic_id_registered(void) +{ + return 1; +} + +static unsigned int x2apic_cpu_mask_to_apicid(cpumask_t cpumask) +{ + int cpu; + + /* + * We're using fixed IRQ delivery, can only return one phys APIC ID. + * May as well be the first. + */ + cpu = first_cpu(cpumask); + if ((unsigned)cpu < NR_CPUS) + return per_cpu(x86_cpu_to_logical_apicid, cpu); + else + return BAD_APICID; +} + +static unsigned int get_apic_id(unsigned long x) +{ + unsigned int id; + + id = x; + return id; +} + +static unsigned long set_apic_id(unsigned int id) +{ + unsigned long x; + + x = id; + return x; +} + +static unsigned int phys_pkg_id(int index_msb) +{ + return current_cpu_data.initial_apicid >> index_msb; +} + +static void x2apic_send_IPI_self(int vector) +{ + apic_write(APIC_SELF_IPI, vector); +} + +static void init_x2apic_ldr(void) +{ + int cpu = smp_processor_id(); + + per_cpu(x86_cpu_to_logical_apicid, cpu) = apic_read(APIC_LDR); + return; +} + +struct genapic apic_x2apic_cluster = { + .name = "cluster x2apic", + .acpi_madt_oem_check = x2apic_acpi_madt_oem_check, + .int_delivery_mode = dest_LowestPrio, + .int_dest_mode = (APIC_DEST_LOGICAL != 0), + .target_cpus = x2apic_target_cpus, + .vector_allocation_domain = x2apic_vector_allocation_domain, + .apic_id_registered = x2apic_apic_id_registered, + .init_apic_ldr = init_x2apic_ldr, + .send_IPI_all = x2apic_send_IPI_all, + .send_IPI_allbutself = x2apic_send_IPI_allbutself, + .send_IPI_mask = x2apic_send_IPI_mask, + .send_IPI_self = x2apic_send_IPI_self, + .cpu_mask_to_apicid = x2apic_cpu_mask_to_apicid, + .phys_pkg_id = phys_pkg_id, + .get_apic_id = get_apic_id, + .set_apic_id = set_apic_id, + .apic_id_mask = (0xFFFFFFFFu), +}; diff --git a/arch/x86/kernel/genx2apic_phys.c b/arch/x86/kernel/genx2apic_phys.c new file mode 100644 index 00000000000..8f1343df262 --- /dev/null +++ b/arch/x86/kernel/genx2apic_phys.c @@ -0,0 +1,154 @@ +#include <linux/threads.h> +#include <linux/cpumask.h> +#include <linux/string.h> +#include <linux/kernel.h> +#include <linux/ctype.h> +#include <linux/init.h> +#include <linux/dmar.h> + +#include <asm/smp.h> +#include <asm/ipi.h> +#include <asm/genapic.h> + +static int x2apic_phys; + +static int set_x2apic_phys_mode(char *arg) +{ + x2apic_phys = 1; + return 0; +} +early_param("x2apic_phys", set_x2apic_phys_mode); + +static int __init x2apic_acpi_madt_oem_check(char *oem_id, char *oem_table_id) +{ + if (cpu_has_x2apic && x2apic_phys) + return 1; + + return 0; +} + +/* Start with all IRQs pointing to boot CPU. IRQ balancing will shift them. */ + +static cpumask_t x2apic_target_cpus(void) +{ + return cpumask_of_cpu(0); +} + +static cpumask_t x2apic_vector_allocation_domain(int cpu) +{ + cpumask_t domain = CPU_MASK_NONE; + cpu_set(cpu, domain); + return domain; +} + +static void __x2apic_send_IPI_dest(unsigned int apicid, int vector, + unsigned int dest) +{ + unsigned long cfg; + + cfg = __prepare_ICR(0, vector, dest); + + /* + * send the IPI. + */ + x2apic_icr_write(cfg, apicid); +} + +static void x2apic_send_IPI_mask(cpumask_t mask, int vector) +{ + unsigned long flags; + unsigned long query_cpu; + + local_irq_save(flags); + for_each_cpu_mask(query_cpu, mask) { + __x2apic_send_IPI_dest(per_cpu(x86_cpu_to_apicid, query_cpu), + vector, APIC_DEST_PHYSICAL); + } + local_irq_restore(flags); +} + +static void x2apic_send_IPI_allbutself(int vector) +{ + cpumask_t mask = cpu_online_map; + + cpu_clear(smp_processor_id(), mask); + + if (!cpus_empty(mask)) + x2apic_send_IPI_mask(mask, vector); +} + +static void x2apic_send_IPI_all(int vector) +{ + x2apic_send_IPI_mask(cpu_online_map, vector); +} + +static int x2apic_apic_id_registered(void) +{ + return 1; +} + +static unsigned int x2apic_cpu_mask_to_apicid(cpumask_t cpumask) +{ + int cpu; + + /* + * We're using fixed IRQ delivery, can only return one phys APIC ID. + * May as well be the first. + */ + cpu = first_cpu(cpumask); + if ((unsigned)cpu < NR_CPUS) + return per_cpu(x86_cpu_to_apicid, cpu); + else + return BAD_APICID; +} + +static unsigned int get_apic_id(unsigned long x) +{ + unsigned int id; + + id = x; + return id; +} + +static unsigned long set_apic_id(unsigned int id) +{ + unsigned long x; + + x = id; + return x; +} + +static unsigned int phys_pkg_id(int index_msb) +{ + return current_cpu_data.initial_apicid >> index_msb; +} + +void x2apic_send_IPI_self(int vector) +{ + apic_write(APIC_SELF_IPI, vector); +} + +void init_x2apic_ldr(void) +{ + return; +} + +struct genapic apic_x2apic_phys = { + .name = "physical x2apic", + .acpi_madt_oem_check = x2apic_acpi_madt_oem_check, + .int_delivery_mode = dest_Fixed, + .int_dest_mode = (APIC_DEST_PHYSICAL != 0), + .target_cpus = x2apic_target_cpus, + .vector_allocation_domain = x2apic_vector_allocation_domain, + .apic_id_registered = x2apic_apic_id_registered, + .init_apic_ldr = init_x2apic_ldr, + .send_IPI_all = x2apic_send_IPI_all, + .send_IPI_allbutself = x2apic_send_IPI_allbutself, + .send_IPI_mask = x2apic_send_IPI_mask, + .send_IPI_self = x2apic_send_IPI_self, + .cpu_mask_to_apicid = x2apic_cpu_mask_to_apicid, + .phys_pkg_id = phys_pkg_id, + .get_apic_id = get_apic_id, + .set_apic_id = set_apic_id, + .apic_id_mask = (0xFFFFFFFFu), +}; diff --git a/arch/x86/kernel/genx2apic_uv_x.c b/arch/x86/kernel/genx2apic_uv_x.c index bfa837cb16b..33581d94a90 100644 --- a/arch/x86/kernel/genx2apic_uv_x.c +++ b/arch/x86/kernel/genx2apic_uv_x.c @@ -12,12 +12,12 @@ #include <linux/threads.h> #include <linux/cpumask.h> #include <linux/string.h> -#include <linux/kernel.h> #include <linux/ctype.h> #include <linux/init.h> #include <linux/sched.h> #include <linux/bootmem.h> #include <linux/module.h> +#include <linux/hardirq.h> #include <asm/smp.h> #include <asm/ipi.h> #include <asm/genapic.h> @@ -26,6 +26,36 @@ #include <asm/uv/uv_hub.h> #include <asm/uv/bios.h> +DEFINE_PER_CPU(int, x2apic_extra_bits); + +static enum uv_system_type uv_system_type; + +static int __init uv_acpi_madt_oem_check(char *oem_id, char *oem_table_id) +{ + if (!strcmp(oem_id, "SGI")) { + if (!strcmp(oem_table_id, "UVL")) + uv_system_type = UV_LEGACY_APIC; + else if (!strcmp(oem_table_id, "UVX")) + uv_system_type = UV_X2APIC; + else if (!strcmp(oem_table_id, "UVH")) { + uv_system_type = UV_NON_UNIQUE_APIC; + return 1; + } + } + return 0; +} + +enum uv_system_type get_uv_system_type(void) +{ + return uv_system_type; +} + +int is_uv_system(void) +{ + return uv_system_type != UV_NONE; +} +EXPORT_SYMBOL_GPL(is_uv_system); + DEFINE_PER_CPU(struct uv_hub_info_s, __uv_hub_info); EXPORT_PER_CPU_SYMBOL_GPL(__uv_hub_info); @@ -84,7 +114,7 @@ static void uv_send_IPI_one(int cpu, int vector) unsigned long val, apicid, lapicid; int pnode; - apicid = per_cpu(x86_cpu_to_apicid, cpu); /* ZZZ - cache node-local ? */ + apicid = per_cpu(x86_cpu_to_apicid, cpu); lapicid = apicid & 0x3f; /* ZZZ macro needed */ pnode = uv_apicid_to_pnode(apicid); val = @@ -123,6 +153,10 @@ static int uv_apic_id_registered(void) return 1; } +static void uv_init_apic_ldr(void) +{ +} + static unsigned int uv_cpu_mask_to_apicid(cpumask_t cpumask) { int cpu; @@ -138,31 +172,59 @@ static unsigned int uv_cpu_mask_to_apicid(cpumask_t cpumask) return BAD_APICID; } +static unsigned int get_apic_id(unsigned long x) +{ + unsigned int id; + + WARN_ON(preemptible() && num_online_cpus() > 1); + id = x | __get_cpu_var(x2apic_extra_bits); + + return id; +} + +static unsigned long set_apic_id(unsigned int id) +{ + unsigned long x; + + /* maskout x2apic_extra_bits ? */ + x = id; + return x; +} + +static unsigned int uv_read_apic_id(void) +{ + + return get_apic_id(apic_read(APIC_ID)); +} + static unsigned int phys_pkg_id(int index_msb) { - return GET_APIC_ID(read_apic_id()) >> index_msb; + return uv_read_apic_id() >> index_msb; } -#ifdef ZZZ /* Needs x2apic patch */ static void uv_send_IPI_self(int vector) { apic_write(APIC_SELF_IPI, vector); } -#endif struct genapic apic_x2apic_uv_x = { .name = "UV large system", + .acpi_madt_oem_check = uv_acpi_madt_oem_check, .int_delivery_mode = dest_Fixed, .int_dest_mode = (APIC_DEST_PHYSICAL != 0), .target_cpus = uv_target_cpus, - .vector_allocation_domain = uv_vector_allocation_domain,/* Fixme ZZZ */ + .vector_allocation_domain = uv_vector_allocation_domain, .apic_id_registered = uv_apic_id_registered, + .init_apic_ldr = uv_init_apic_ldr, .send_IPI_all = uv_send_IPI_all, .send_IPI_allbutself = uv_send_IPI_allbutself, .send_IPI_mask = uv_send_IPI_mask, - /* ZZZ.send_IPI_self = uv_send_IPI_self, */ + .send_IPI_self = uv_send_IPI_self, .cpu_mask_to_apicid = uv_cpu_mask_to_apicid, - .phys_pkg_id = phys_pkg_id, /* Fixme ZZZ */ + .phys_pkg_id = phys_pkg_id, + .get_apic_id = get_apic_id, + .set_apic_id = set_apic_id, + .apic_id_mask = (0xFFFFFFFFu), }; static __cpuinit void set_x2apic_extra_bits(int pnode) @@ -222,12 +284,13 @@ static __init void map_low_mmrs(void) enum map_type {map_wb, map_uc}; -static __init void map_high(char *id, unsigned long base, int shift, enum map_type map_type) +static __init void map_high(char *id, unsigned long base, int shift, + int max_pnode, enum map_type map_type) { unsigned long bytes, paddr; paddr = base << shift; - bytes = (1UL << shift); + bytes = (1UL << shift) * (max_pnode + 1); printk(KERN_INFO "UV: Map %s_HI 0x%lx - 0x%lx\n", id, paddr, paddr + bytes); if (map_type == map_uc) @@ -243,7 +306,7 @@ static __init void map_gru_high(int max_pnode) gru.v = uv_read_local_mmr(UVH_RH_GAM_GRU_OVERLAY_CONFIG_MMR); if (gru.s.enable) - map_high("GRU", gru.s.base, shift, map_wb); + map_high("GRU", gru.s.base, shift, max_pnode, map_wb); } static __init void map_config_high(int max_pnode) @@ -253,7 +316,7 @@ static __init void map_config_high(int max_pnode) cfg.v = uv_read_local_mmr(UVH_RH_GAM_CFG_OVERLAY_CONFIG_MMR); if (cfg.s.enable) - map_high("CONFIG", cfg.s.base, shift, map_uc); + map_high("CONFIG", cfg.s.base, shift, max_pnode, map_uc); } static __init void map_mmr_high(int max_pnode) @@ -263,7 +326,7 @@ static __init void map_mmr_high(int max_pnode) mmr.v = uv_read_local_mmr(UVH_RH_GAM_MMR_OVERLAY_CONFIG_MMR); if (mmr.s.enable) - map_high("MMR", mmr.s.base, shift, map_uc); + map_high("MMR", mmr.s.base, shift, max_pnode, map_uc); } static __init void map_mmioh_high(int max_pnode) @@ -273,7 +336,7 @@ static __init void map_mmioh_high(int max_pnode) mmioh.v = uv_read_local_mmr(UVH_RH_GAM_MMIOH_OVERLAY_CONFIG_MMR); if (mmioh.s.enable) - map_high("MMIOH", mmioh.s.base, shift, map_uc); + map_high("MMIOH", mmioh.s.base, shift, max_pnode, map_uc); } static __init void uv_rtc_init(void) @@ -401,3 +464,5 @@ void __cpuinit uv_cpu_init(void) if (get_uv_system_type() == UV_NON_UNIQUE_APIC) set_x2apic_extra_bits(uv_hub_info->pnode); } + + diff --git a/arch/x86/kernel/head.c b/arch/x86/kernel/head.c index 3e66bd364a9..1dcb0f13897 100644 --- a/arch/x86/kernel/head.c +++ b/arch/x86/kernel/head.c @@ -35,6 +35,7 @@ void __init reserve_ebda_region(void) /* start of EBDA area */ ebda_addr = get_bios_ebda(); + printk(KERN_INFO "BIOS EBDA/lowmem at: %08x/%08x\n", ebda_addr, lowmem); /* Fixup: bios puts an EBDA in the top 64K segment */ /* of conventional memory, but does not adjust lowmem. */ diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c index 73deaffadd0..acf62fc233d 100644 --- a/arch/x86/kernel/hpet.c +++ b/arch/x86/kernel/hpet.c @@ -115,13 +115,17 @@ static void hpet_reserve_platform_timers(unsigned long id) hd.hd_phys_address = hpet_address; hd.hd_address = hpet; hd.hd_nirqs = nrtimers; - hd.hd_flags = HPET_DATA_PLATFORM; hpet_reserve_timer(&hd, 0); #ifdef CONFIG_HPET_EMULATE_RTC hpet_reserve_timer(&hd, 1); #endif + /* + * NOTE that hd_irq[] reflects IOAPIC input pins (LEGACY_8254 + * is wrong for i8259!) not the output IRQ. Many BIOS writers + * don't bother configuring *any* comparator interrupts. + */ hd.hd_irq[0] = HPET_LEGACY_8254; hd.hd_irq[1] = HPET_LEGACY_RTC; diff --git a/arch/x86/kernel/i387.c b/arch/x86/kernel/i387.c index eb9ddd8efb8..1f20608d4ca 100644 --- a/arch/x86/kernel/i387.c +++ b/arch/x86/kernel/i387.c @@ -21,9 +21,12 @@ # include <asm/sigcontext32.h> # include <asm/user32.h> #else -# define save_i387_ia32 save_i387 -# define restore_i387_ia32 restore_i387 +# define save_i387_xstate_ia32 save_i387_xstate +# define restore_i387_xstate_ia32 restore_i387_xstate # define _fpstate_ia32 _fpstate +# define _xstate_ia32 _xstate +# define sig_xstate_ia32_size sig_xstate_size +# define fx_sw_reserved_ia32 fx_sw_reserved # define user_i387_ia32_struct user_i387_struct # define user32_fxsr_struct user_fxsr_struct #endif @@ -36,6 +39,7 @@ static unsigned int mxcsr_feature_mask __read_mostly = 0xffffffffu; unsigned int xstate_size; +unsigned int sig_xstate_ia32_size = sizeof(struct _fpstate_ia32); static struct i387_fxsave_struct fx_scratch __cpuinitdata; void __cpuinit mxcsr_feature_mask_init(void) @@ -61,6 +65,11 @@ void __init init_thread_xstate(void) return; } + if (cpu_has_xsave) { + xsave_cntxt_init(); + return; + } + if (cpu_has_fxsr) xstate_size = sizeof(struct i387_fxsave_struct); #ifdef CONFIG_X86_32 @@ -83,9 +92,19 @@ void __cpuinit fpu_init(void) write_cr0(oldcr0 & ~(X86_CR0_TS|X86_CR0_EM)); /* clear TS and EM */ + /* + * Boot processor to setup the FP and extended state context info. + */ + if (!smp_processor_id()) + init_thread_xstate(); + xsave_init(); + mxcsr_feature_mask_init(); /* clean state in init */ - current_thread_info()->status = 0; + if (cpu_has_xsave) + current_thread_info()->status = TS_XSAVE; + else + current_thread_info()->status = 0; clear_used_math(); } #endif /* CONFIG_X86_64 */ @@ -195,6 +214,13 @@ int xfpregs_set(struct task_struct *target, const struct user_regset *regset, */ target->thread.xstate->fxsave.mxcsr &= mxcsr_feature_mask; + /* + * update the header bits in the xsave header, indicating the + * presence of FP and SSE state. + */ + if (cpu_has_xsave) + target->thread.xstate->xsave.xsave_hdr.xstate_bv |= XSTATE_FPSSE; + return ret; } @@ -395,6 +421,12 @@ int fpregs_set(struct task_struct *target, const struct user_regset *regset, if (!ret) convert_to_fxsr(target, &env); + /* + * update the header bit in the xsave header, indicating the + * presence of FP. + */ + if (cpu_has_xsave) + target->thread.xstate->xsave.xsave_hdr.xstate_bv |= XSTATE_FP; return ret; } @@ -407,7 +439,6 @@ static inline int save_i387_fsave(struct _fpstate_ia32 __user *buf) struct task_struct *tsk = current; struct i387_fsave_struct *fp = &tsk->thread.xstate->fsave; - unlazy_fpu(tsk); fp->status = fp->swd; if (__copy_to_user(buf, fp, sizeof(struct i387_fsave_struct))) return -1; @@ -421,8 +452,6 @@ static int save_i387_fxsave(struct _fpstate_ia32 __user *buf) struct user_i387_ia32_struct env; int err = 0; - unlazy_fpu(tsk); - convert_from_fxsr(&env, tsk); if (__copy_to_user(buf, &env, sizeof(env))) return -1; @@ -432,16 +461,54 @@ static int save_i387_fxsave(struct _fpstate_ia32 __user *buf) if (err) return -1; - if (__copy_to_user(&buf->_fxsr_env[0], fx, - sizeof(struct i387_fxsave_struct))) + if (__copy_to_user(&buf->_fxsr_env[0], fx, xstate_size)) + return -1; + return 1; +} + +static int save_i387_xsave(void __user *buf) +{ + struct task_struct *tsk = current; + struct _fpstate_ia32 __user *fx = buf; + int err = 0; + + /* + * For legacy compatible, we always set FP/SSE bits in the bit + * vector while saving the state to the user context. + * This will enable us capturing any changes(during sigreturn) to + * the FP/SSE bits by the legacy applications which don't touch + * xstate_bv in the xsave header. + * + * xsave aware applications can change the xstate_bv in the xsave + * header as well as change any contents in the memory layout. + * xrestore as part of sigreturn will capture all the changes. + */ + tsk->thread.xstate->xsave.xsave_hdr.xstate_bv |= XSTATE_FPSSE; + + if (save_i387_fxsave(fx) < 0) + return -1; + + err = __copy_to_user(&fx->sw_reserved, &fx_sw_reserved_ia32, + sizeof(struct _fpx_sw_bytes)); + err |= __put_user(FP_XSTATE_MAGIC2, + (__u32 __user *) (buf + sig_xstate_ia32_size + - FP_XSTATE_MAGIC2_SIZE)); + if (err) return -1; + return 1; } -int save_i387_ia32(struct _fpstate_ia32 __user *buf) +int save_i387_xstate_ia32(void __user *buf) { + struct _fpstate_ia32 __user *fp = (struct _fpstate_ia32 __user *) buf; + struct task_struct *tsk = current; + if (!used_math()) return 0; + + if (!access_ok(VERIFY_WRITE, buf, sig_xstate_ia32_size)) + return -EACCES; /* * This will cause a "finit" to be triggered by the next * attempted FPU operation by the 'current' process. @@ -451,13 +518,17 @@ int save_i387_ia32(struct _fpstate_ia32 __user *buf) if (!HAVE_HWFP) { return fpregs_soft_get(current, NULL, 0, sizeof(struct user_i387_ia32_struct), - NULL, buf) ? -1 : 1; + NULL, fp) ? -1 : 1; } + unlazy_fpu(tsk); + + if (cpu_has_xsave) + return save_i387_xsave(fp); if (cpu_has_fxsr) - return save_i387_fxsave(buf); + return save_i387_fxsave(fp); else - return save_i387_fsave(buf); + return save_i387_fsave(fp); } static inline int restore_i387_fsave(struct _fpstate_ia32 __user *buf) @@ -468,14 +539,15 @@ static inline int restore_i387_fsave(struct _fpstate_ia32 __user *buf) sizeof(struct i387_fsave_struct)); } -static int restore_i387_fxsave(struct _fpstate_ia32 __user *buf) +static int restore_i387_fxsave(struct _fpstate_ia32 __user *buf, + unsigned int size) { struct task_struct *tsk = current; struct user_i387_ia32_struct env; int err; err = __copy_from_user(&tsk->thread.xstate->fxsave, &buf->_fxsr_env[0], - sizeof(struct i387_fxsave_struct)); + size); /* mxcsr reserved bits must be masked to zero for security reasons */ tsk->thread.xstate->fxsave.mxcsr &= mxcsr_feature_mask; if (err || __copy_from_user(&env, buf, sizeof(env))) @@ -485,14 +557,69 @@ static int restore_i387_fxsave(struct _fpstate_ia32 __user *buf) return 0; } -int restore_i387_ia32(struct _fpstate_ia32 __user *buf) +static int restore_i387_xsave(void __user *buf) +{ + struct _fpx_sw_bytes fx_sw_user; + struct _fpstate_ia32 __user *fx_user = + ((struct _fpstate_ia32 __user *) buf); + struct i387_fxsave_struct __user *fx = + (struct i387_fxsave_struct __user *) &fx_user->_fxsr_env[0]; + struct xsave_hdr_struct *xsave_hdr = + ¤t->thread.xstate->xsave.xsave_hdr; + u64 mask; + int err; + + if (check_for_xstate(fx, buf, &fx_sw_user)) + goto fx_only; + + mask = fx_sw_user.xstate_bv; + + err = restore_i387_fxsave(buf, fx_sw_user.xstate_size); + + xsave_hdr->xstate_bv &= pcntxt_mask; + /* + * These bits must be zero. + */ + xsave_hdr->reserved1[0] = xsave_hdr->reserved1[1] = 0; + + /* + * Init the state that is not present in the memory layout + * and enabled by the OS. + */ + mask = ~(pcntxt_mask & ~mask); + xsave_hdr->xstate_bv &= mask; + + return err; +fx_only: + /* + * Couldn't find the extended state information in the memory + * layout. Restore the FP/SSE and init the other extended state + * enabled by the OS. + */ + xsave_hdr->xstate_bv = XSTATE_FPSSE; + return restore_i387_fxsave(buf, sizeof(struct i387_fxsave_struct)); +} + +int restore_i387_xstate_ia32(void __user *buf) { int err; struct task_struct *tsk = current; + struct _fpstate_ia32 __user *fp = (struct _fpstate_ia32 __user *) buf; if (HAVE_HWFP) clear_fpu(tsk); + if (!buf) { + if (used_math()) { + clear_fpu(tsk); + clear_used_math(); + } + + return 0; + } else + if (!access_ok(VERIFY_READ, buf, sig_xstate_ia32_size)) + return -EACCES; + if (!used_math()) { err = init_fpu(tsk); if (err) @@ -500,14 +627,17 @@ int restore_i387_ia32(struct _fpstate_ia32 __user *buf) } if (HAVE_HWFP) { - if (cpu_has_fxsr) - err = restore_i387_fxsave(buf); + if (cpu_has_xsave) + err = restore_i387_xsave(buf); + else if (cpu_has_fxsr) + err = restore_i387_fxsave(fp, sizeof(struct + i387_fxsave_struct)); else - err = restore_i387_fsave(buf); + err = restore_i387_fsave(fp); } else { err = fpregs_soft_set(current, NULL, 0, sizeof(struct user_i387_ia32_struct), - NULL, buf) != 0; + NULL, fp) != 0; } set_used_math(); diff --git a/arch/x86/kernel/i8259.c b/arch/x86/kernel/i8259.c index dc92b49d920..4b8a53d841f 100644 --- a/arch/x86/kernel/i8259.c +++ b/arch/x86/kernel/i8259.c @@ -282,6 +282,30 @@ static int __init i8259A_init_sysfs(void) device_initcall(i8259A_init_sysfs); +void mask_8259A(void) +{ + unsigned long flags; + + spin_lock_irqsave(&i8259A_lock, flags); + + outb(0xff, PIC_MASTER_IMR); /* mask all of 8259A-1 */ + outb(0xff, PIC_SLAVE_IMR); /* mask all of 8259A-2 */ + + spin_unlock_irqrestore(&i8259A_lock, flags); +} + +void unmask_8259A(void) +{ + unsigned long flags; + + spin_lock_irqsave(&i8259A_lock, flags); + + outb(cached_master_mask, PIC_MASTER_IMR); /* restore master IRQ mask */ + outb(cached_slave_mask, PIC_SLAVE_IMR); /* restore slave IRQ mask */ + + spin_unlock_irqrestore(&i8259A_lock, flags); +} + void init_8259A(int auto_eoi) { unsigned long flags; diff --git a/arch/x86/kernel/io_apic_32.c b/arch/x86/kernel/io_apic_32.c index 09cddb57bec..e710289f673 100644 --- a/arch/x86/kernel/io_apic_32.c +++ b/arch/x86/kernel/io_apic_32.c @@ -46,10 +46,13 @@ #include <asm/nmi.h> #include <asm/msidef.h> #include <asm/hypertransport.h> +#include <asm/setup.h> #include <mach_apic.h> #include <mach_apicdef.h> +#define __apicdebuginit(type) static type __init + int (*ioapic_renumber_irq)(int ioapic, int irq); atomic_t irq_mis_count; @@ -1341,7 +1344,8 @@ static void __init setup_timer_IRQ0_pin(unsigned int apic, unsigned int pin, ioapic_write_entry(apic, pin, entry); } -void __init print_IO_APIC(void) + +__apicdebuginit(void) print_IO_APIC(void) { int apic, i; union IO_APIC_reg_00 reg_00; @@ -1456,9 +1460,7 @@ void __init print_IO_APIC(void) return; } -#if 0 - -static void print_APIC_bitfield(int base) +__apicdebuginit(void) print_APIC_bitfield(int base) { unsigned int v; int i, j; @@ -1479,9 +1481,10 @@ static void print_APIC_bitfield(int base) } } -void /*__init*/ print_local_APIC(void *dummy) +__apicdebuginit(void) print_local_APIC(void *dummy) { unsigned int v, ver, maxlvt; + u64 icr; if (apic_verbosity == APIC_QUIET) return; @@ -1490,7 +1493,7 @@ void /*__init*/ print_local_APIC(void *dummy) smp_processor_id(), hard_smp_processor_id()); v = apic_read(APIC_ID); printk(KERN_INFO "... APIC ID: %08x (%01x)\n", v, - GET_APIC_ID(read_apic_id())); + GET_APIC_ID(v)); v = apic_read(APIC_LVR); printk(KERN_INFO "... APIC VERSION: %08x\n", v); ver = GET_APIC_VERSION(v); @@ -1532,10 +1535,9 @@ void /*__init*/ print_local_APIC(void *dummy) printk(KERN_DEBUG "... APIC ESR: %08x\n", v); } - v = apic_read(APIC_ICR); - printk(KERN_DEBUG "... APIC ICR: %08x\n", v); - v = apic_read(APIC_ICR2); - printk(KERN_DEBUG "... APIC ICR2: %08x\n", v); + icr = apic_icr_read(); + printk(KERN_DEBUG "... APIC ICR: %08x\n", icr); + printk(KERN_DEBUG "... APIC ICR2: %08x\n", icr >> 32); v = apic_read(APIC_LVTT); printk(KERN_DEBUG "... APIC LVTT: %08x\n", v); @@ -1563,12 +1565,12 @@ void /*__init*/ print_local_APIC(void *dummy) printk("\n"); } -void print_all_local_APICs(void) +__apicdebuginit(void) print_all_local_APICs(void) { on_each_cpu(print_local_APIC, NULL, 1); } -void /*__init*/ print_PIC(void) +__apicdebuginit(void) print_PIC(void) { unsigned int v; unsigned long flags; @@ -1600,7 +1602,17 @@ void /*__init*/ print_PIC(void) printk(KERN_DEBUG "... PIC ELCR: %04x\n", v); } -#endif /* 0 */ +__apicdebuginit(int) print_all_ICs(void) +{ + print_PIC(); + print_all_local_APICs(); + print_IO_APIC(); + + return 0; +} + +fs_initcall(print_all_ICs); + static void __init enable_IO_APIC(void) { @@ -1698,8 +1710,7 @@ void disable_IO_APIC(void) entry.dest_mode = 0; /* Physical */ entry.delivery_mode = dest_ExtINT; /* ExtInt */ entry.vector = 0; - entry.dest.physical.physical_dest = - GET_APIC_ID(read_apic_id()); + entry.dest.physical.physical_dest = read_apic_id(); /* * Add it to the IO-APIC irq-routing table: @@ -1725,10 +1736,8 @@ static void __init setup_ioapic_ids_from_mpc(void) unsigned char old_id; unsigned long flags; -#ifdef CONFIG_X86_NUMAQ - if (found_numaq) + if (x86_quirks->setup_ioapic_ids && x86_quirks->setup_ioapic_ids()) return; -#endif /* * Don't check I/O APIC IDs for xAPIC systems. They have @@ -2329,8 +2338,6 @@ void __init setup_IO_APIC(void) setup_IO_APIC_irqs(); init_IO_APIC_traps(); check_timer(); - if (!acpi_ioapic) - print_IO_APIC(); } /* diff --git a/arch/x86/kernel/io_apic_64.c b/arch/x86/kernel/io_apic_64.c index 61a83b70c18..02063ae042f 100644 --- a/arch/x86/kernel/io_apic_64.c +++ b/arch/x86/kernel/io_apic_64.c @@ -37,6 +37,7 @@ #include <acpi/acpi_bus.h> #endif #include <linux/bootmem.h> +#include <linux/dmar.h> #include <asm/idle.h> #include <asm/io.h> @@ -49,10 +50,13 @@ #include <asm/nmi.h> #include <asm/msidef.h> #include <asm/hypertransport.h> +#include <asm/irq_remapping.h> #include <mach_ipi.h> #include <mach_apic.h> +#define __apicdebuginit(type) static type __init + struct irq_cfg { cpumask_t domain; cpumask_t old_domain; @@ -87,8 +91,6 @@ int first_system_vector = 0xfe; char system_vectors[NR_VECTORS] = { [0 ... NR_VECTORS-1] = SYS_VECTOR_FREE}; -#define __apicdebuginit __init - int sis_apic_bug; /* not actually supported, dummy for compile */ static int no_timer_check; @@ -108,6 +110,9 @@ static DEFINE_SPINLOCK(vector_lock); */ int nr_ioapic_registers[MAX_IO_APICS]; +/* I/O APIC RTE contents at the OS boot up */ +struct IO_APIC_route_entry *early_ioapic_entries[MAX_IO_APICS]; + /* I/O APIC entries */ struct mp_config_ioapic mp_ioapics[MAX_IO_APICS]; int nr_ioapics; @@ -303,7 +308,12 @@ static void __target_IO_APIC_irq(unsigned int irq, unsigned int dest, u8 vector) pin = entry->pin; if (pin == -1) break; - io_apic_write(apic, 0x11 + pin*2, dest); + /* + * With interrupt-remapping, destination information comes + * from interrupt-remapping table entry. + */ + if (!irq_remapped(irq)) + io_apic_write(apic, 0x11 + pin*2, dest); reg = io_apic_read(apic, 0x10 + pin*2); reg &= ~IO_APIC_REDIR_VECTOR_MASK; reg |= vector; @@ -440,6 +450,69 @@ static void clear_IO_APIC (void) clear_IO_APIC_pin(apic, pin); } +/* + * Saves and masks all the unmasked IO-APIC RTE's + */ +int save_mask_IO_APIC_setup(void) +{ + union IO_APIC_reg_01 reg_01; + unsigned long flags; + int apic, pin; + + /* + * The number of IO-APIC IRQ registers (== #pins): + */ + for (apic = 0; apic < nr_ioapics; apic++) { + spin_lock_irqsave(&ioapic_lock, flags); + reg_01.raw = io_apic_read(apic, 1); + spin_unlock_irqrestore(&ioapic_lock, flags); + nr_ioapic_registers[apic] = reg_01.bits.entries+1; + } + + for (apic = 0; apic < nr_ioapics; apic++) { + early_ioapic_entries[apic] = + kzalloc(sizeof(struct IO_APIC_route_entry) * + nr_ioapic_registers[apic], GFP_KERNEL); + if (!early_ioapic_entries[apic]) + return -ENOMEM; + } + + for (apic = 0; apic < nr_ioapics; apic++) + for (pin = 0; pin < nr_ioapic_registers[apic]; pin++) { + struct IO_APIC_route_entry entry; + + entry = early_ioapic_entries[apic][pin] = + ioapic_read_entry(apic, pin); + if (!entry.mask) { + entry.mask = 1; + ioapic_write_entry(apic, pin, entry); + } + } + return 0; +} + +void restore_IO_APIC_setup(void) +{ + int apic, pin; + + for (apic = 0; apic < nr_ioapics; apic++) + for (pin = 0; pin < nr_ioapic_registers[apic]; pin++) + ioapic_write_entry(apic, pin, + early_ioapic_entries[apic][pin]); +} + +void reinit_intr_remapped_IO_APIC(int intr_remapping) +{ + /* + * for now plain restore of previous settings. + * TBD: In the case of OS enabling interrupt-remapping, + * IO-APIC RTE's need to be setup to point to interrupt-remapping + * table entries. for now, do a plain restore, and wait for + * the setup_IO_APIC_irqs() to do proper initialization. + */ + restore_IO_APIC_setup(); +} + int skip_ioapic_setup; int ioapic_force; @@ -839,18 +912,98 @@ void __setup_vector_irq(int cpu) } static struct irq_chip ioapic_chip; +#ifdef CONFIG_INTR_REMAP +static struct irq_chip ir_ioapic_chip; +#endif static void ioapic_register_intr(int irq, unsigned long trigger) { - if (trigger) { + if (trigger) irq_desc[irq].status |= IRQ_LEVEL; - set_irq_chip_and_handler_name(irq, &ioapic_chip, - handle_fasteoi_irq, "fasteoi"); - } else { + else irq_desc[irq].status &= ~IRQ_LEVEL; + +#ifdef CONFIG_INTR_REMAP + if (irq_remapped(irq)) { + irq_desc[irq].status |= IRQ_MOVE_PCNTXT; + if (trigger) + set_irq_chip_and_handler_name(irq, &ir_ioapic_chip, + handle_fasteoi_irq, + "fasteoi"); + else + set_irq_chip_and_handler_name(irq, &ir_ioapic_chip, + handle_edge_irq, "edge"); + return; + } +#endif + if (trigger) + set_irq_chip_and_handler_name(irq, &ioapic_chip, + handle_fasteoi_irq, + "fasteoi"); + else set_irq_chip_and_handler_name(irq, &ioapic_chip, handle_edge_irq, "edge"); +} + +static int setup_ioapic_entry(int apic, int irq, + struct IO_APIC_route_entry *entry, + unsigned int destination, int trigger, + int polarity, int vector) +{ + /* + * add it to the IO-APIC irq-routing table: + */ + memset(entry,0,sizeof(*entry)); + +#ifdef CONFIG_INTR_REMAP + if (intr_remapping_enabled) { + struct intel_iommu *iommu = map_ioapic_to_ir(apic); + struct irte irte; + struct IR_IO_APIC_route_entry *ir_entry = + (struct IR_IO_APIC_route_entry *) entry; + int index; + + if (!iommu) + panic("No mapping iommu for ioapic %d\n", apic); + + index = alloc_irte(iommu, irq, 1); + if (index < 0) + panic("Failed to allocate IRTE for ioapic %d\n", apic); + + memset(&irte, 0, sizeof(irte)); + + irte.present = 1; + irte.dst_mode = INT_DEST_MODE; + irte.trigger_mode = trigger; + irte.dlvry_mode = INT_DELIVERY_MODE; + irte.vector = vector; + irte.dest_id = IRTE_DEST(destination); + + modify_irte(irq, &irte); + + ir_entry->index2 = (index >> 15) & 0x1; + ir_entry->zero = 0; + ir_entry->format = 1; + ir_entry->index = (index & 0x7fff); + } else +#endif + { + entry->delivery_mode = INT_DELIVERY_MODE; + entry->dest_mode = INT_DEST_MODE; + entry->dest = destination; } + + entry->mask = 0; /* enable IRQ */ + entry->trigger = trigger; + entry->polarity = polarity; + entry->vector = vector; + + /* Mask level triggered irqs. + * Use IRQ_DELAYED_DISABLE for edge triggered irqs. + */ + if (trigger) + entry->mask = 1; + return 0; } static void setup_IO_APIC_irq(int apic, int pin, unsigned int irq, @@ -875,24 +1028,15 @@ static void setup_IO_APIC_irq(int apic, int pin, unsigned int irq, apic, mp_ioapics[apic].mp_apicid, pin, cfg->vector, irq, trigger, polarity); - /* - * add it to the IO-APIC irq-routing table: - */ - memset(&entry,0,sizeof(entry)); - - entry.delivery_mode = INT_DELIVERY_MODE; - entry.dest_mode = INT_DEST_MODE; - entry.dest = cpu_mask_to_apicid(mask); - entry.mask = 0; /* enable IRQ */ - entry.trigger = trigger; - entry.polarity = polarity; - entry.vector = cfg->vector; - /* Mask level triggered irqs. - * Use IRQ_DELAYED_DISABLE for edge triggered irqs. - */ - if (trigger) - entry.mask = 1; + if (setup_ioapic_entry(mp_ioapics[apic].mp_apicid, irq, &entry, + cpu_mask_to_apicid(mask), trigger, polarity, + cfg->vector)) { + printk("Failed to setup ioapic entry for ioapic %d, pin %d\n", + mp_ioapics[apic].mp_apicid, pin); + __clear_irq_vector(irq); + return; + } ioapic_register_intr(irq, trigger); if (irq < 16) @@ -944,6 +1088,9 @@ static void __init setup_timer_IRQ0_pin(unsigned int apic, unsigned int pin, { struct IO_APIC_route_entry entry; + if (intr_remapping_enabled) + return; + memset(&entry, 0, sizeof(entry)); /* @@ -970,7 +1117,8 @@ static void __init setup_timer_IRQ0_pin(unsigned int apic, unsigned int pin, ioapic_write_entry(apic, pin, entry); } -void __apicdebuginit print_IO_APIC(void) + +__apicdebuginit(void) print_IO_APIC(void) { int apic, i; union IO_APIC_reg_00 reg_00; @@ -1064,9 +1212,7 @@ void __apicdebuginit print_IO_APIC(void) return; } -#if 0 - -static __apicdebuginit void print_APIC_bitfield (int base) +__apicdebuginit(void) print_APIC_bitfield(int base) { unsigned int v; int i, j; @@ -1087,9 +1233,10 @@ static __apicdebuginit void print_APIC_bitfield (int base) } } -void __apicdebuginit print_local_APIC(void * dummy) +__apicdebuginit(void) print_local_APIC(void *dummy) { unsigned int v, ver, maxlvt; + unsigned long icr; if (apic_verbosity == APIC_QUIET) return; @@ -1097,7 +1244,7 @@ void __apicdebuginit print_local_APIC(void * dummy) printk("\n" KERN_DEBUG "printing local APIC contents on CPU#%d/%d:\n", smp_processor_id(), hard_smp_processor_id()); v = apic_read(APIC_ID); - printk(KERN_INFO "... APIC ID: %08x (%01x)\n", v, GET_APIC_ID(read_apic_id())); + printk(KERN_INFO "... APIC ID: %08x (%01x)\n", v, read_apic_id()); v = apic_read(APIC_LVR); printk(KERN_INFO "... APIC VERSION: %08x\n", v); ver = GET_APIC_VERSION(v); @@ -1133,10 +1280,9 @@ void __apicdebuginit print_local_APIC(void * dummy) v = apic_read(APIC_ESR); printk(KERN_DEBUG "... APIC ESR: %08x\n", v); - v = apic_read(APIC_ICR); - printk(KERN_DEBUG "... APIC ICR: %08x\n", v); - v = apic_read(APIC_ICR2); - printk(KERN_DEBUG "... APIC ICR2: %08x\n", v); + icr = apic_icr_read(); + printk(KERN_DEBUG "... APIC ICR: %08x\n", (u32)icr); + printk(KERN_DEBUG "... APIC ICR2: %08x\n", (u32)(icr >> 32)); v = apic_read(APIC_LVTT); printk(KERN_DEBUG "... APIC LVTT: %08x\n", v); @@ -1164,12 +1310,12 @@ void __apicdebuginit print_local_APIC(void * dummy) printk("\n"); } -void print_all_local_APICs (void) +__apicdebuginit(void) print_all_local_APICs(void) { on_each_cpu(print_local_APIC, NULL, 1); } -void __apicdebuginit print_PIC(void) +__apicdebuginit(void) print_PIC(void) { unsigned int v; unsigned long flags; @@ -1201,7 +1347,17 @@ void __apicdebuginit print_PIC(void) printk(KERN_DEBUG "... PIC ELCR: %04x\n", v); } -#endif /* 0 */ +__apicdebuginit(int) print_all_ICs(void) +{ + print_PIC(); + print_all_local_APICs(); + print_IO_APIC(); + + return 0; +} + +fs_initcall(print_all_ICs); + void __init enable_IO_APIC(void) { @@ -1291,7 +1447,7 @@ void disable_IO_APIC(void) entry.dest_mode = 0; /* Physical */ entry.delivery_mode = dest_ExtINT; /* ExtInt */ entry.vector = 0; - entry.dest = GET_APIC_ID(read_apic_id()); + entry.dest = read_apic_id(); /* * Add it to the IO-APIC irq-routing table: @@ -1397,6 +1553,147 @@ static int ioapic_retrigger_irq(unsigned int irq) */ #ifdef CONFIG_SMP + +#ifdef CONFIG_INTR_REMAP +static void ir_irq_migration(struct work_struct *work); + +static DECLARE_DELAYED_WORK(ir_migration_work, ir_irq_migration); + +/* + * Migrate the IO-APIC irq in the presence of intr-remapping. + * + * For edge triggered, irq migration is a simple atomic update(of vector + * and cpu destination) of IRTE and flush the hardware cache. + * + * For level triggered, we need to modify the io-apic RTE aswell with the update + * vector information, along with modifying IRTE with vector and destination. + * So irq migration for level triggered is little bit more complex compared to + * edge triggered migration. But the good news is, we use the same algorithm + * for level triggered migration as we have today, only difference being, + * we now initiate the irq migration from process context instead of the + * interrupt context. + * + * In future, when we do a directed EOI (combined with cpu EOI broadcast + * suppression) to the IO-APIC, level triggered irq migration will also be + * as simple as edge triggered migration and we can do the irq migration + * with a simple atomic update to IO-APIC RTE. + */ +static void migrate_ioapic_irq(int irq, cpumask_t mask) +{ + struct irq_cfg *cfg = irq_cfg + irq; + struct irq_desc *desc = irq_desc + irq; + cpumask_t tmp, cleanup_mask; + struct irte irte; + int modify_ioapic_rte = desc->status & IRQ_LEVEL; + unsigned int dest; + unsigned long flags; + + cpus_and(tmp, mask, cpu_online_map); + if (cpus_empty(tmp)) + return; + + if (get_irte(irq, &irte)) + return; + + if (assign_irq_vector(irq, mask)) + return; + + cpus_and(tmp, cfg->domain, mask); + dest = cpu_mask_to_apicid(tmp); + + if (modify_ioapic_rte) { + spin_lock_irqsave(&ioapic_lock, flags); + __target_IO_APIC_irq(irq, dest, cfg->vector); + spin_unlock_irqrestore(&ioapic_lock, flags); + } + + irte.vector = cfg->vector; + irte.dest_id = IRTE_DEST(dest); + + /* + * Modified the IRTE and flushes the Interrupt entry cache. + */ + modify_irte(irq, &irte); + + if (cfg->move_in_progress) { + cpus_and(cleanup_mask, cfg->old_domain, cpu_online_map); + cfg->move_cleanup_count = cpus_weight(cleanup_mask); + send_IPI_mask(cleanup_mask, IRQ_MOVE_CLEANUP_VECTOR); + cfg->move_in_progress = 0; + } + + irq_desc[irq].affinity = mask; +} + +static int migrate_irq_remapped_level(int irq) +{ + int ret = -1; + + mask_IO_APIC_irq(irq); + + if (io_apic_level_ack_pending(irq)) { + /* + * Interrupt in progress. Migrating irq now will change the + * vector information in the IO-APIC RTE and that will confuse + * the EOI broadcast performed by cpu. + * So, delay the irq migration to the next instance. + */ + schedule_delayed_work(&ir_migration_work, 1); + goto unmask; + } + + /* everthing is clear. we have right of way */ + migrate_ioapic_irq(irq, irq_desc[irq].pending_mask); + + ret = 0; + irq_desc[irq].status &= ~IRQ_MOVE_PENDING; + cpus_clear(irq_desc[irq].pending_mask); + +unmask: + unmask_IO_APIC_irq(irq); + return ret; +} + +static void ir_irq_migration(struct work_struct *work) +{ + int irq; + + for (irq = 0; irq < NR_IRQS; irq++) { + struct irq_desc *desc = irq_desc + irq; + if (desc->status & IRQ_MOVE_PENDING) { + unsigned long flags; + + spin_lock_irqsave(&desc->lock, flags); + if (!desc->chip->set_affinity || + !(desc->status & IRQ_MOVE_PENDING)) { + desc->status &= ~IRQ_MOVE_PENDING; + spin_unlock_irqrestore(&desc->lock, flags); + continue; + } + + desc->chip->set_affinity(irq, + irq_desc[irq].pending_mask); + spin_unlock_irqrestore(&desc->lock, flags); + } + } +} + +/* + * Migrates the IRQ destination in the process context. + */ +static void set_ir_ioapic_affinity_irq(unsigned int irq, cpumask_t mask) +{ + if (irq_desc[irq].status & IRQ_LEVEL) { + irq_desc[irq].status |= IRQ_MOVE_PENDING; + irq_desc[irq].pending_mask = mask; + migrate_irq_remapped_level(irq); + return; + } + + migrate_ioapic_irq(irq, mask); +} +#endif + asmlinkage void smp_irq_move_cleanup_interrupt(void) { unsigned vector, me; @@ -1453,6 +1750,17 @@ static void irq_complete_move(unsigned int irq) #else static inline void irq_complete_move(unsigned int irq) {} #endif +#ifdef CONFIG_INTR_REMAP +static void ack_x2apic_level(unsigned int irq) +{ + ack_x2APIC_irq(); +} + +static void ack_x2apic_edge(unsigned int irq) +{ + ack_x2APIC_irq(); +} +#endif static void ack_apic_edge(unsigned int irq) { @@ -1527,6 +1835,21 @@ static struct irq_chip ioapic_chip __read_mostly = { .retrigger = ioapic_retrigger_irq, }; +#ifdef CONFIG_INTR_REMAP +static struct irq_chip ir_ioapic_chip __read_mostly = { + .name = "IR-IO-APIC", + .startup = startup_ioapic_irq, + .mask = mask_IO_APIC_irq, + .unmask = unmask_IO_APIC_irq, + .ack = ack_x2apic_edge, + .eoi = ack_x2apic_level, +#ifdef CONFIG_SMP + .set_affinity = set_ir_ioapic_affinity_irq, +#endif + .retrigger = ioapic_retrigger_irq, +}; +#endif + static inline void init_IO_APIC_traps(void) { int irq; @@ -1712,6 +2035,8 @@ static inline void __init check_timer(void) * 8259A. */ if (pin1 == -1) { + if (intr_remapping_enabled) + panic("BIOS bug: timer not connected to IO-APIC"); pin1 = pin2; apic1 = apic2; no_pin1 = 1; @@ -1738,6 +2063,8 @@ static inline void __init check_timer(void) clear_IO_APIC_pin(0, pin1); goto out; } + if (intr_remapping_enabled) + panic("timer doesn't work through Interrupt-remapped IO-APIC"); clear_IO_APIC_pin(apic1, pin1); if (!no_pin1) apic_printk(APIC_QUIET, KERN_ERR "..MP-BIOS bug: " @@ -1854,8 +2181,6 @@ void __init setup_IO_APIC(void) setup_IO_APIC_irqs(); init_IO_APIC_traps(); check_timer(); - if (!acpi_ioapic) - print_IO_APIC(); } struct sysfs_ioapic_data { @@ -1977,6 +2302,9 @@ void destroy_irq(unsigned int irq) dynamic_irq_cleanup(irq); +#ifdef CONFIG_INTR_REMAP + free_irte(irq); +#endif spin_lock_irqsave(&vector_lock, flags); __clear_irq_vector(irq); spin_unlock_irqrestore(&vector_lock, flags); @@ -1995,11 +2323,42 @@ static int msi_compose_msg(struct pci_dev *pdev, unsigned int irq, struct msi_ms tmp = TARGET_CPUS; err = assign_irq_vector(irq, tmp); - if (!err) { - cpus_and(tmp, cfg->domain, tmp); - dest = cpu_mask_to_apicid(tmp); + if (err) + return err; + + cpus_and(tmp, cfg->domain, tmp); + dest = cpu_mask_to_apicid(tmp); + +#ifdef CONFIG_INTR_REMAP + if (irq_remapped(irq)) { + struct irte irte; + int ir_index; + u16 sub_handle; + + ir_index = map_irq_to_irte_handle(irq, &sub_handle); + BUG_ON(ir_index == -1); + + memset (&irte, 0, sizeof(irte)); + + irte.present = 1; + irte.dst_mode = INT_DEST_MODE; + irte.trigger_mode = 0; /* edge */ + irte.dlvry_mode = INT_DELIVERY_MODE; + irte.vector = cfg->vector; + irte.dest_id = IRTE_DEST(dest); + + modify_irte(irq, &irte); msg->address_hi = MSI_ADDR_BASE_HI; + msg->data = sub_handle; + msg->address_lo = MSI_ADDR_BASE_LO | MSI_ADDR_IR_EXT_INT | + MSI_ADDR_IR_SHV | + MSI_ADDR_IR_INDEX1(ir_index) | + MSI_ADDR_IR_INDEX2(ir_index); + } else +#endif + { + msg->address_hi = MSI_ADDR_BASE_HI; msg->address_lo = MSI_ADDR_BASE_LO | ((INT_DEST_MODE == 0) ? @@ -2049,6 +2408,55 @@ static void set_msi_irq_affinity(unsigned int irq, cpumask_t mask) write_msi_msg(irq, &msg); irq_desc[irq].affinity = mask; } + +#ifdef CONFIG_INTR_REMAP +/* + * Migrate the MSI irq to another cpumask. This migration is + * done in the process context using interrupt-remapping hardware. + */ +static void ir_set_msi_irq_affinity(unsigned int irq, cpumask_t mask) +{ + struct irq_cfg *cfg = irq_cfg + irq; + unsigned int dest; + cpumask_t tmp, cleanup_mask; + struct irte irte; + + cpus_and(tmp, mask, cpu_online_map); + if (cpus_empty(tmp)) + return; + + if (get_irte(irq, &irte)) + return; + + if (assign_irq_vector(irq, mask)) + return; + + cpus_and(tmp, cfg->domain, mask); + dest = cpu_mask_to_apicid(tmp); + + irte.vector = cfg->vector; + irte.dest_id = IRTE_DEST(dest); + + /* + * atomically update the IRTE with the new destination and vector. + */ + modify_irte(irq, &irte); + + /* + * After this point, all the interrupts will start arriving + * at the new destination. So, time to cleanup the previous + * vector allocation. + */ + if (cfg->move_in_progress) { + cpus_and(cleanup_mask, cfg->old_domain, cpu_online_map); + cfg->move_cleanup_count = cpus_weight(cleanup_mask); + send_IPI_mask(cleanup_mask, IRQ_MOVE_CLEANUP_VECTOR); + cfg->move_in_progress = 0; + } + + irq_desc[irq].affinity = mask; +} +#endif #endif /* CONFIG_SMP */ /* @@ -2066,26 +2474,157 @@ static struct irq_chip msi_chip = { .retrigger = ioapic_retrigger_irq, }; -int arch_setup_msi_irq(struct pci_dev *dev, struct msi_desc *desc) +#ifdef CONFIG_INTR_REMAP +static struct irq_chip msi_ir_chip = { + .name = "IR-PCI-MSI", + .unmask = unmask_msi_irq, + .mask = mask_msi_irq, + .ack = ack_x2apic_edge, +#ifdef CONFIG_SMP + .set_affinity = ir_set_msi_irq_affinity, +#endif + .retrigger = ioapic_retrigger_irq, +}; + +/* + * Map the PCI dev to the corresponding remapping hardware unit + * and allocate 'nvec' consecutive interrupt-remapping table entries + * in it. + */ +static int msi_alloc_irte(struct pci_dev *dev, int irq, int nvec) { + struct intel_iommu *iommu; + int index; + + iommu = map_dev_to_ir(dev); + if (!iommu) { + printk(KERN_ERR + "Unable to map PCI %s to iommu\n", pci_name(dev)); + return -ENOENT; + } + + index = alloc_irte(iommu, irq, nvec); + if (index < 0) { + printk(KERN_ERR + "Unable to allocate %d IRTE for PCI %s\n", nvec, + pci_name(dev)); + return -ENOSPC; + } + return index; +} +#endif + +static int setup_msi_irq(struct pci_dev *dev, struct msi_desc *desc, int irq) +{ + int ret; struct msi_msg msg; + + ret = msi_compose_msg(dev, irq, &msg); + if (ret < 0) + return ret; + + set_irq_msi(irq, desc); + write_msi_msg(irq, &msg); + +#ifdef CONFIG_INTR_REMAP + if (irq_remapped(irq)) { + struct irq_desc *desc = irq_desc + irq; + /* + * irq migration in process context + */ + desc->status |= IRQ_MOVE_PCNTXT; + set_irq_chip_and_handler_name(irq, &msi_ir_chip, handle_edge_irq, "edge"); + } else +#endif + set_irq_chip_and_handler_name(irq, &msi_chip, handle_edge_irq, "edge"); + + return 0; +} + +int arch_setup_msi_irq(struct pci_dev *dev, struct msi_desc *desc) +{ int irq, ret; + irq = create_irq(); if (irq < 0) return irq; - ret = msi_compose_msg(dev, irq, &msg); +#ifdef CONFIG_INTR_REMAP + if (!intr_remapping_enabled) + goto no_ir; + + ret = msi_alloc_irte(dev, irq, 1); + if (ret < 0) + goto error; +no_ir: +#endif + ret = setup_msi_irq(dev, desc, irq); if (ret < 0) { destroy_irq(irq); return ret; } + return 0; - set_irq_msi(irq, desc); - write_msi_msg(irq, &msg); +#ifdef CONFIG_INTR_REMAP +error: + destroy_irq(irq); + return ret; +#endif +} - set_irq_chip_and_handler_name(irq, &msi_chip, handle_edge_irq, "edge"); +int arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) +{ + int irq, ret, sub_handle; + struct msi_desc *desc; +#ifdef CONFIG_INTR_REMAP + struct intel_iommu *iommu = 0; + int index = 0; +#endif + + sub_handle = 0; + list_for_each_entry(desc, &dev->msi_list, list) { + irq = create_irq(); + if (irq < 0) + return irq; +#ifdef CONFIG_INTR_REMAP + if (!intr_remapping_enabled) + goto no_ir; + if (!sub_handle) { + /* + * allocate the consecutive block of IRTE's + * for 'nvec' + */ + index = msi_alloc_irte(dev, irq, nvec); + if (index < 0) { + ret = index; + goto error; + } + } else { + iommu = map_dev_to_ir(dev); + if (!iommu) { + ret = -ENOENT; + goto error; + } + /* + * setup the mapping between the irq and the IRTE + * base index, the sub_handle pointing to the + * appropriate interrupt remap table entry. + */ + set_irte_irq(irq, iommu, index, sub_handle); + } +no_ir: +#endif + ret = setup_msi_irq(dev, desc, irq); + if (ret < 0) + goto error; + sub_handle++; + } return 0; + +error: + destroy_irq(irq); + return ret; } void arch_teardown_msi_irq(unsigned int irq) @@ -2333,6 +2872,10 @@ void __init setup_ioapic_dest(void) setup_IO_APIC_irq(ioapic, pin, irq, irq_trigger(irq_entry), irq_polarity(irq_entry)); +#ifdef CONFIG_INTR_REMAP + else if (intr_remapping_enabled) + set_ir_ioapic_affinity_irq(irq, TARGET_CPUS); +#endif else set_ioapic_affinity_irq(irq, TARGET_CPUS); } diff --git a/arch/x86/kernel/irqinit_32.c b/arch/x86/kernel/irqinit_32.c index d66914287ee..9200a1e2752 100644 --- a/arch/x86/kernel/irqinit_32.c +++ b/arch/x86/kernel/irqinit_32.c @@ -74,6 +74,15 @@ void __init init_ISA_irqs (void) } } +/* + * IRQ2 is cascade interrupt to second interrupt controller + */ +static struct irqaction irq2 = { + .handler = no_action, + .mask = CPU_MASK_NONE, + .name = "cascade", +}; + /* Overridden in paravirt.c */ void init_IRQ(void) __attribute__((weak, alias("native_init_IRQ"))); @@ -98,6 +107,46 @@ void __init native_init_IRQ(void) set_intr_gate(vector, interrupt[i]); } +#if defined(CONFIG_X86_LOCAL_APIC) && defined(CONFIG_SMP) + /* + * IRQ0 must be given a fixed assignment and initialized, + * because it's used before the IO-APIC is set up. + */ + set_intr_gate(FIRST_DEVICE_VECTOR, interrupt[0]); + + /* + * The reschedule interrupt is a CPU-to-CPU reschedule-helper + * IPI, driven by wakeup. + */ + alloc_intr_gate(RESCHEDULE_VECTOR, reschedule_interrupt); + + /* IPI for invalidation */ + alloc_intr_gate(INVALIDATE_TLB_VECTOR, invalidate_interrupt); + + /* IPI for generic function call */ + alloc_intr_gate(CALL_FUNCTION_VECTOR, call_function_interrupt); + + /* IPI for single call function */ + set_intr_gate(CALL_FUNCTION_SINGLE_VECTOR, call_function_single_interrupt); +#endif + +#ifdef CONFIG_X86_LOCAL_APIC + /* self generated IPI for local APIC timer */ + alloc_intr_gate(LOCAL_TIMER_VECTOR, apic_timer_interrupt); + + /* IPI vectors for APIC spurious and error interrupts */ + alloc_intr_gate(SPURIOUS_APIC_VECTOR, spurious_interrupt); + alloc_intr_gate(ERROR_APIC_VECTOR, error_interrupt); +#endif + +#if defined(CONFIG_X86_LOCAL_APIC) && defined(CONFIG_X86_MCE_P4THERMAL) + /* thermal monitor LVT interrupt */ + alloc_intr_gate(THERMAL_APIC_VECTOR, thermal_interrupt); +#endif + + if (!acpi_ioapic) + setup_irq(2, &irq2); + /* setup after call gates are initialised (usually add in * the architecture specific gates) */ diff --git a/arch/x86/kernel/irqinit_64.c b/arch/x86/kernel/irqinit_64.c index 1f26fd9ec4f..5b5be9d43c2 100644 --- a/arch/x86/kernel/irqinit_64.c +++ b/arch/x86/kernel/irqinit_64.c @@ -135,7 +135,7 @@ DEFINE_PER_CPU(vector_irq_t, vector_irq) = { [IRQ15_VECTOR + 1 ... NR_VECTORS - 1] = -1 }; -static void __init init_ISA_irqs (void) +void __init init_ISA_irqs(void) { int i; @@ -164,22 +164,8 @@ static void __init init_ISA_irqs (void) void init_IRQ(void) __attribute__((weak, alias("native_init_IRQ"))); -void __init native_init_IRQ(void) +static void __init smp_intr_init(void) { - int i; - - init_ISA_irqs(); - /* - * Cover the whole vector space, no vector can escape - * us. (some of these will be overridden and become - * 'special' SMP interrupts) - */ - for (i = 0; i < (NR_VECTORS - FIRST_EXTERNAL_VECTOR); i++) { - int vector = FIRST_EXTERNAL_VECTOR + i; - if (vector != IA32_SYSCALL_VECTOR) - set_intr_gate(vector, interrupt[i]); - } - #ifdef CONFIG_SMP /* * The reschedule interrupt is a CPU-to-CPU reschedule-helper @@ -207,6 +193,12 @@ void __init native_init_IRQ(void) /* Low priority IPI to cleanup after moving an irq */ set_intr_gate(IRQ_MOVE_CLEANUP_VECTOR, irq_move_cleanup_interrupt); #endif +} + +static void __init apic_intr_init(void) +{ + smp_intr_init(); + alloc_intr_gate(THERMAL_APIC_VECTOR, thermal_interrupt); alloc_intr_gate(THRESHOLD_APIC_VECTOR, threshold_interrupt); @@ -216,6 +208,25 @@ void __init native_init_IRQ(void) /* IPI vectors for APIC spurious and error interrupts */ alloc_intr_gate(SPURIOUS_APIC_VECTOR, spurious_interrupt); alloc_intr_gate(ERROR_APIC_VECTOR, error_interrupt); +} + +void __init native_init_IRQ(void) +{ + int i; + + init_ISA_irqs(); + /* + * Cover the whole vector space, no vector can escape + * us. (some of these will be overridden and become + * 'special' SMP interrupts) + */ + for (i = 0; i < (NR_VECTORS - FIRST_EXTERNAL_VECTOR); i++) { + int vector = FIRST_EXTERNAL_VECTOR + i; + if (vector != IA32_SYSCALL_VECTOR) + set_intr_gate(vector, interrupt[i]); + } + + apic_intr_init(); if (!acpi_ioapic) setup_irq(2, &irq2); diff --git a/arch/x86/kernel/ldt.c b/arch/x86/kernel/ldt.c index 0ed5f939b90..eee32b43fee 100644 --- a/arch/x86/kernel/ldt.c +++ b/arch/x86/kernel/ldt.c @@ -52,6 +52,8 @@ static int alloc_ldt(mm_context_t *pc, int mincount, int reload) memset(newldt + oldsize * LDT_ENTRY_SIZE, 0, (mincount - oldsize) * LDT_ENTRY_SIZE); + paravirt_alloc_ldt(newldt, mincount); + #ifdef CONFIG_X86_64 /* CHECKME: Do we really need this ? */ wmb(); @@ -74,6 +76,7 @@ static int alloc_ldt(mm_context_t *pc, int mincount, int reload) #endif } if (oldsize) { + paravirt_free_ldt(oldldt, oldsize); if (oldsize * LDT_ENTRY_SIZE > PAGE_SIZE) vfree(oldldt); else @@ -85,10 +88,13 @@ static int alloc_ldt(mm_context_t *pc, int mincount, int reload) static inline int copy_ldt(mm_context_t *new, mm_context_t *old) { int err = alloc_ldt(new, old->size, 0); + int i; if (err < 0) return err; - memcpy(new->ldt, old->ldt, old->size * LDT_ENTRY_SIZE); + + for(i = 0; i < old->size; i++) + write_ldt_entry(new->ldt, i, old->ldt + i * LDT_ENTRY_SIZE); return 0; } @@ -125,6 +131,7 @@ void destroy_context(struct mm_struct *mm) if (mm == current->active_mm) clear_LDT(); #endif + paravirt_free_ldt(mm->context.ldt, mm->context.size); if (mm->context.size * LDT_ENTRY_SIZE > PAGE_SIZE) vfree(mm->context.ldt); else diff --git a/arch/x86/kernel/microcode.c b/arch/x86/kernel/microcode.c deleted file mode 100644 index 652fa5c38eb..00000000000 --- a/arch/x86/kernel/microcode.c +++ /dev/null @@ -1,853 +0,0 @@ -/* - * Intel CPU Microcode Update Driver for Linux - * - * Copyright (C) 2000-2006 Tigran Aivazian <tigran@aivazian.fsnet.co.uk> - * 2006 Shaohua Li <shaohua.li@intel.com> - * - * This driver allows to upgrade microcode on Intel processors - * belonging to IA-32 family - PentiumPro, Pentium II, - * Pentium III, Xeon, Pentium 4, etc. - * - * Reference: Section 8.11 of Volume 3a, IA-32 Intel? Architecture - * Software Developer's Manual - * Order Number 253668 or free download from: - * - * http://developer.intel.com/design/pentium4/manuals/253668.htm - * - * For more information, go to http://www.urbanmyth.org/microcode - * - * 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. - * - * 1.0 16 Feb 2000, Tigran Aivazian <tigran@sco.com> - * Initial release. - * 1.01 18 Feb 2000, Tigran Aivazian <tigran@sco.com> - * Added read() support + cleanups. - * 1.02 21 Feb 2000, Tigran Aivazian <tigran@sco.com> - * Added 'device trimming' support. open(O_WRONLY) zeroes - * and frees the saved copy of applied microcode. - * 1.03 29 Feb 2000, Tigran Aivazian <tigran@sco.com> - * Made to use devfs (/dev/cpu/microcode) + cleanups. - * 1.04 06 Jun 2000, Simon Trimmer <simon@veritas.com> - * Added misc device support (now uses both devfs and misc). - * Added MICROCODE_IOCFREE ioctl to clear memory. - * 1.05 09 Jun 2000, Simon Trimmer <simon@veritas.com> - * Messages for error cases (non Intel & no suitable microcode). - * 1.06 03 Aug 2000, Tigran Aivazian <tigran@veritas.com> - * Removed ->release(). Removed exclusive open and status bitmap. - * Added microcode_rwsem to serialize read()/write()/ioctl(). - * Removed global kernel lock usage. - * 1.07 07 Sep 2000, Tigran Aivazian <tigran@veritas.com> - * Write 0 to 0x8B msr and then cpuid before reading revision, - * so that it works even if there were no update done by the - * BIOS. Otherwise, reading from 0x8B gives junk (which happened - * to be 0 on my machine which is why it worked even when I - * disabled update by the BIOS) - * Thanks to Eric W. Biederman <ebiederman@lnxi.com> for the fix. - * 1.08 11 Dec 2000, Richard Schaal <richard.schaal@intel.com> and - * Tigran Aivazian <tigran@veritas.com> - * Intel Pentium 4 processor support and bugfixes. - * 1.09 30 Oct 2001, Tigran Aivazian <tigran@veritas.com> - * Bugfix for HT (Hyper-Threading) enabled processors - * whereby processor resources are shared by all logical processors - * in a single CPU package. - * 1.10 28 Feb 2002 Asit K Mallick <asit.k.mallick@intel.com> and - * Tigran Aivazian <tigran@veritas.com>, - * Serialize updates as required on HT processors due to speculative - * nature of implementation. - * 1.11 22 Mar 2002 Tigran Aivazian <tigran@veritas.com> - * Fix the panic when writing zero-length microcode chunk. - * 1.12 29 Sep 2003 Nitin Kamble <nitin.a.kamble@intel.com>, - * Jun Nakajima <jun.nakajima@intel.com> - * Support for the microcode updates in the new format. - * 1.13 10 Oct 2003 Tigran Aivazian <tigran@veritas.com> - * Removed ->read() method and obsoleted MICROCODE_IOCFREE ioctl - * because we no longer hold a copy of applied microcode - * in kernel memory. - * 1.14 25 Jun 2004 Tigran Aivazian <tigran@veritas.com> - * Fix sigmatch() macro to handle old CPUs with pf == 0. - * Thanks to Stuart Swales for pointing out this bug. - */ - -//#define DEBUG /* pr_debug */ -#include <linux/capability.h> -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/sched.h> -#include <linux/smp_lock.h> -#include <linux/cpumask.h> -#include <linux/module.h> -#include <linux/slab.h> -#include <linux/vmalloc.h> -#include <linux/miscdevice.h> -#include <linux/spinlock.h> -#include <linux/mm.h> -#include <linux/fs.h> -#include <linux/mutex.h> -#include <linux/cpu.h> -#include <linux/firmware.h> -#include <linux/platform_device.h> - -#include <asm/msr.h> -#include <asm/uaccess.h> -#include <asm/processor.h> - -MODULE_DESCRIPTION("Intel CPU (IA-32) Microcode Update Driver"); -MODULE_AUTHOR("Tigran Aivazian <tigran@aivazian.fsnet.co.uk>"); -MODULE_LICENSE("GPL"); - -#define MICROCODE_VERSION "1.14a" - -#define DEFAULT_UCODE_DATASIZE (2000) /* 2000 bytes */ -#define MC_HEADER_SIZE (sizeof (microcode_header_t)) /* 48 bytes */ -#define DEFAULT_UCODE_TOTALSIZE (DEFAULT_UCODE_DATASIZE + MC_HEADER_SIZE) /* 2048 bytes */ -#define EXT_HEADER_SIZE (sizeof (struct extended_sigtable)) /* 20 bytes */ -#define EXT_SIGNATURE_SIZE (sizeof (struct extended_signature)) /* 12 bytes */ -#define DWSIZE (sizeof (u32)) -#define get_totalsize(mc) \ - (((microcode_t *)mc)->hdr.totalsize ? \ - ((microcode_t *)mc)->hdr.totalsize : DEFAULT_UCODE_TOTALSIZE) -#define get_datasize(mc) \ - (((microcode_t *)mc)->hdr.datasize ? \ - ((microcode_t *)mc)->hdr.datasize : DEFAULT_UCODE_DATASIZE) - -#define sigmatch(s1, s2, p1, p2) \ - (((s1) == (s2)) && (((p1) & (p2)) || (((p1) == 0) && ((p2) == 0)))) - -#define exttable_size(et) ((et)->count * EXT_SIGNATURE_SIZE + EXT_HEADER_SIZE) - -/* serialize access to the physical write to MSR 0x79 */ -static DEFINE_SPINLOCK(microcode_update_lock); - -/* no concurrent ->write()s are allowed on /dev/cpu/microcode */ -static DEFINE_MUTEX(microcode_mutex); - -static struct ucode_cpu_info { - int valid; - unsigned int sig; - unsigned int pf; - unsigned int rev; - microcode_t *mc; -} ucode_cpu_info[NR_CPUS]; - -static void collect_cpu_info(int cpu_num) -{ - struct cpuinfo_x86 *c = &cpu_data(cpu_num); - struct ucode_cpu_info *uci = ucode_cpu_info + cpu_num; - unsigned int val[2]; - - /* We should bind the task to the CPU */ - BUG_ON(raw_smp_processor_id() != cpu_num); - uci->pf = uci->rev = 0; - uci->mc = NULL; - uci->valid = 1; - - if (c->x86_vendor != X86_VENDOR_INTEL || c->x86 < 6 || - cpu_has(c, X86_FEATURE_IA64)) { - printk(KERN_ERR "microcode: CPU%d not a capable Intel " - "processor\n", cpu_num); - uci->valid = 0; - return; - } - - uci->sig = cpuid_eax(0x00000001); - - if ((c->x86_model >= 5) || (c->x86 > 6)) { - /* get processor flags from MSR 0x17 */ - rdmsr(MSR_IA32_PLATFORM_ID, val[0], val[1]); - uci->pf = 1 << ((val[1] >> 18) & 7); - } - - wrmsr(MSR_IA32_UCODE_REV, 0, 0); - /* see notes above for revision 1.07. Apparent chip bug */ - sync_core(); - /* get the current revision from MSR 0x8B */ - rdmsr(MSR_IA32_UCODE_REV, val[0], uci->rev); - pr_debug("microcode: collect_cpu_info : sig=0x%x, pf=0x%x, rev=0x%x\n", - uci->sig, uci->pf, uci->rev); -} - -static inline int microcode_update_match(int cpu_num, - microcode_header_t *mc_header, int sig, int pf) -{ - struct ucode_cpu_info *uci = ucode_cpu_info + cpu_num; - - if (!sigmatch(sig, uci->sig, pf, uci->pf) - || mc_header->rev <= uci->rev) - return 0; - return 1; -} - -static int microcode_sanity_check(void *mc) -{ - microcode_header_t *mc_header = mc; - struct extended_sigtable *ext_header = NULL; - struct extended_signature *ext_sig; - unsigned long total_size, data_size, ext_table_size; - int sum, orig_sum, ext_sigcount = 0, i; - - total_size = get_totalsize(mc_header); - data_size = get_datasize(mc_header); - if (data_size + MC_HEADER_SIZE > total_size) { - printk(KERN_ERR "microcode: error! " - "Bad data size in microcode data file\n"); - return -EINVAL; - } - - if (mc_header->ldrver != 1 || mc_header->hdrver != 1) { - printk(KERN_ERR "microcode: error! " - "Unknown microcode update format\n"); - return -EINVAL; - } - ext_table_size = total_size - (MC_HEADER_SIZE + data_size); - if (ext_table_size) { - if ((ext_table_size < EXT_HEADER_SIZE) - || ((ext_table_size - EXT_HEADER_SIZE) % EXT_SIGNATURE_SIZE)) { - printk(KERN_ERR "microcode: error! " - "Small exttable size in microcode data file\n"); - return -EINVAL; - } - ext_header = mc + MC_HEADER_SIZE + data_size; - if (ext_table_size != exttable_size(ext_header)) { - printk(KERN_ERR "microcode: error! " - "Bad exttable size in microcode data file\n"); - return -EFAULT; - } - ext_sigcount = ext_header->count; - } - - /* check extended table checksum */ - if (ext_table_size) { - int ext_table_sum = 0; - int *ext_tablep = (int *)ext_header; - - i = ext_table_size / DWSIZE; - while (i--) - ext_table_sum += ext_tablep[i]; - if (ext_table_sum) { - printk(KERN_WARNING "microcode: aborting, " - "bad extended signature table checksum\n"); - return -EINVAL; - } - } - - /* calculate the checksum */ - orig_sum = 0; - i = (MC_HEADER_SIZE + data_size) / DWSIZE; - while (i--) - orig_sum += ((int *)mc)[i]; - if (orig_sum) { - printk(KERN_ERR "microcode: aborting, bad checksum\n"); - return -EINVAL; - } - if (!ext_table_size) - return 0; - /* check extended signature checksum */ - for (i = 0; i < ext_sigcount; i++) { - ext_sig = (void *)ext_header + EXT_HEADER_SIZE + - EXT_SIGNATURE_SIZE * i; - sum = orig_sum - - (mc_header->sig + mc_header->pf + mc_header->cksum) - + (ext_sig->sig + ext_sig->pf + ext_sig->cksum); - if (sum) { - printk(KERN_ERR "microcode: aborting, bad checksum\n"); - return -EINVAL; - } - } - return 0; -} - -/* - * return 0 - no update found - * return 1 - found update - * return < 0 - error - */ -static int get_maching_microcode(void *mc, int cpu) -{ - struct ucode_cpu_info *uci = ucode_cpu_info + cpu; - microcode_header_t *mc_header = mc; - struct extended_sigtable *ext_header; - unsigned long total_size = get_totalsize(mc_header); - int ext_sigcount, i; - struct extended_signature *ext_sig; - void *new_mc; - - if (microcode_update_match(cpu, mc_header, - mc_header->sig, mc_header->pf)) - goto find; - - if (total_size <= get_datasize(mc_header) + MC_HEADER_SIZE) - return 0; - - ext_header = mc + get_datasize(mc_header) + MC_HEADER_SIZE; - ext_sigcount = ext_header->count; - ext_sig = (void *)ext_header + EXT_HEADER_SIZE; - for (i = 0; i < ext_sigcount; i++) { - if (microcode_update_match(cpu, mc_header, - ext_sig->sig, ext_sig->pf)) - goto find; - ext_sig++; - } - return 0; -find: - pr_debug("microcode: CPU%d found a matching microcode update with" - " version 0x%x (current=0x%x)\n", cpu, mc_header->rev,uci->rev); - new_mc = vmalloc(total_size); - if (!new_mc) { - printk(KERN_ERR "microcode: error! Can not allocate memory\n"); - return -ENOMEM; - } - - /* free previous update file */ - vfree(uci->mc); - - memcpy(new_mc, mc, total_size); - uci->mc = new_mc; - return 1; -} - -static void apply_microcode(int cpu) -{ - unsigned long flags; - unsigned int val[2]; - int cpu_num = raw_smp_processor_id(); - struct ucode_cpu_info *uci = ucode_cpu_info + cpu_num; - - /* We should bind the task to the CPU */ - BUG_ON(cpu_num != cpu); - - if (uci->mc == NULL) - return; - - /* serialize access to the physical write to MSR 0x79 */ - spin_lock_irqsave(µcode_update_lock, flags); - - /* write microcode via MSR 0x79 */ - wrmsr(MSR_IA32_UCODE_WRITE, - (unsigned long) uci->mc->bits, - (unsigned long) uci->mc->bits >> 16 >> 16); - wrmsr(MSR_IA32_UCODE_REV, 0, 0); - - /* see notes above for revision 1.07. Apparent chip bug */ - sync_core(); - - /* get the current revision from MSR 0x8B */ - rdmsr(MSR_IA32_UCODE_REV, val[0], val[1]); - - spin_unlock_irqrestore(µcode_update_lock, flags); - if (val[1] != uci->mc->hdr.rev) { - printk(KERN_ERR "microcode: CPU%d update from revision " - "0x%x to 0x%x failed\n", cpu_num, uci->rev, val[1]); - return; - } - printk(KERN_INFO "microcode: CPU%d updated from revision " - "0x%x to 0x%x, date = %08x \n", - cpu_num, uci->rev, val[1], uci->mc->hdr.date); - uci->rev = val[1]; -} - -#ifdef CONFIG_MICROCODE_OLD_INTERFACE -static void __user *user_buffer; /* user area microcode data buffer */ -static unsigned int user_buffer_size; /* it's size */ - -static long get_next_ucode(void **mc, long offset) -{ - microcode_header_t mc_header; - unsigned long total_size; - - /* No more data */ - if (offset >= user_buffer_size) - return 0; - if (copy_from_user(&mc_header, user_buffer + offset, MC_HEADER_SIZE)) { - printk(KERN_ERR "microcode: error! Can not read user data\n"); - return -EFAULT; - } - total_size = get_totalsize(&mc_header); - if (offset + total_size > user_buffer_size) { - printk(KERN_ERR "microcode: error! Bad total size in microcode " - "data file\n"); - return -EINVAL; - } - *mc = vmalloc(total_size); - if (!*mc) - return -ENOMEM; - if (copy_from_user(*mc, user_buffer + offset, total_size)) { - printk(KERN_ERR "microcode: error! Can not read user data\n"); - vfree(*mc); - return -EFAULT; - } - return offset + total_size; -} - -static int do_microcode_update (void) -{ - long cursor = 0; - int error = 0; - void *new_mc = NULL; - int cpu; - cpumask_t old; - - old = current->cpus_allowed; - - while ((cursor = get_next_ucode(&new_mc, cursor)) > 0) { - error = microcode_sanity_check(new_mc); - if (error) - goto out; - /* - * It's possible the data file has multiple matching ucode, - * lets keep searching till the latest version - */ - for_each_online_cpu(cpu) { - struct ucode_cpu_info *uci = ucode_cpu_info + cpu; - - if (!uci->valid) - continue; - set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu)); - error = get_maching_microcode(new_mc, cpu); - if (error < 0) - goto out; - if (error == 1) - apply_microcode(cpu); - } - vfree(new_mc); - } -out: - if (cursor > 0) - vfree(new_mc); - if (cursor < 0) - error = cursor; - set_cpus_allowed_ptr(current, &old); - return error; -} - -static int microcode_open (struct inode *unused1, struct file *unused2) -{ - cycle_kernel_lock(); - return capable(CAP_SYS_RAWIO) ? 0 : -EPERM; -} - -static ssize_t microcode_write (struct file *file, const char __user *buf, size_t len, loff_t *ppos) -{ - ssize_t ret; - - if ((len >> PAGE_SHIFT) > num_physpages) { - printk(KERN_ERR "microcode: too much data (max %ld pages)\n", num_physpages); - return -EINVAL; - } - - get_online_cpus(); - mutex_lock(µcode_mutex); - - user_buffer = (void __user *) buf; - user_buffer_size = (int) len; - - ret = do_microcode_update(); - if (!ret) - ret = (ssize_t)len; - - mutex_unlock(µcode_mutex); - put_online_cpus(); - - return ret; -} - -static const struct file_operations microcode_fops = { - .owner = THIS_MODULE, - .write = microcode_write, - .open = microcode_open, -}; - -static struct miscdevice microcode_dev = { - .minor = MICROCODE_MINOR, - .name = "microcode", - .fops = µcode_fops, -}; - -static int __init microcode_dev_init (void) -{ - int error; - - error = misc_register(µcode_dev); - if (error) { - printk(KERN_ERR - "microcode: can't misc_register on minor=%d\n", - MICROCODE_MINOR); - return error; - } - - return 0; -} - -static void microcode_dev_exit (void) -{ - misc_deregister(µcode_dev); -} - -MODULE_ALIAS_MISCDEV(MICROCODE_MINOR); -#else -#define microcode_dev_init() 0 -#define microcode_dev_exit() do { } while(0) -#endif - -static long get_next_ucode_from_buffer(void **mc, const u8 *buf, - unsigned long size, long offset) -{ - microcode_header_t *mc_header; - unsigned long total_size; - - /* No more data */ - if (offset >= size) - return 0; - mc_header = (microcode_header_t *)(buf + offset); - total_size = get_totalsize(mc_header); - - if (offset + total_size > size) { - printk(KERN_ERR "microcode: error! Bad data in microcode data file\n"); - return -EINVAL; - } - - *mc = vmalloc(total_size); - if (!*mc) { - printk(KERN_ERR "microcode: error! Can not allocate memory\n"); - return -ENOMEM; - } - memcpy(*mc, buf + offset, total_size); - return offset + total_size; -} - -/* fake device for request_firmware */ -static struct platform_device *microcode_pdev; - -static int cpu_request_microcode(int cpu) -{ - char name[30]; - struct cpuinfo_x86 *c = &cpu_data(cpu); - const struct firmware *firmware; - const u8 *buf; - unsigned long size; - long offset = 0; - int error; - void *mc; - - /* We should bind the task to the CPU */ - BUG_ON(cpu != raw_smp_processor_id()); - sprintf(name,"intel-ucode/%02x-%02x-%02x", - c->x86, c->x86_model, c->x86_mask); - error = request_firmware(&firmware, name, µcode_pdev->dev); - if (error) { - pr_debug("microcode: data file %s load failed\n", name); - return error; - } - buf = firmware->data; - size = firmware->size; - while ((offset = get_next_ucode_from_buffer(&mc, buf, size, offset)) - > 0) { - error = microcode_sanity_check(mc); - if (error) - break; - error = get_maching_microcode(mc, cpu); - if (error < 0) - break; - /* - * It's possible the data file has multiple matching ucode, - * lets keep searching till the latest version - */ - if (error == 1) { - apply_microcode(cpu); - error = 0; - } - vfree(mc); - } - if (offset > 0) - vfree(mc); - if (offset < 0) - error = offset; - release_firmware(firmware); - - return error; -} - -static int apply_microcode_check_cpu(int cpu) -{ - struct cpuinfo_x86 *c = &cpu_data(cpu); - struct ucode_cpu_info *uci = ucode_cpu_info + cpu; - cpumask_t old; - unsigned int val[2]; - int err = 0; - - /* Check if the microcode is available */ - if (!uci->mc) - return 0; - - old = current->cpus_allowed; - set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu)); - - /* Check if the microcode we have in memory matches the CPU */ - if (c->x86_vendor != X86_VENDOR_INTEL || c->x86 < 6 || - cpu_has(c, X86_FEATURE_IA64) || uci->sig != cpuid_eax(0x00000001)) - err = -EINVAL; - - if (!err && ((c->x86_model >= 5) || (c->x86 > 6))) { - /* get processor flags from MSR 0x17 */ - rdmsr(MSR_IA32_PLATFORM_ID, val[0], val[1]); - if (uci->pf != (1 << ((val[1] >> 18) & 7))) - err = -EINVAL; - } - - if (!err) { - wrmsr(MSR_IA32_UCODE_REV, 0, 0); - /* see notes above for revision 1.07. Apparent chip bug */ - sync_core(); - /* get the current revision from MSR 0x8B */ - rdmsr(MSR_IA32_UCODE_REV, val[0], val[1]); - if (uci->rev != val[1]) - err = -EINVAL; - } - - if (!err) - apply_microcode(cpu); - else - printk(KERN_ERR "microcode: Could not apply microcode to CPU%d:" - " sig=0x%x, pf=0x%x, rev=0x%x\n", - cpu, uci->sig, uci->pf, uci->rev); - - set_cpus_allowed_ptr(current, &old); - return err; -} - -static void microcode_init_cpu(int cpu, int resume) -{ - cpumask_t old; - struct ucode_cpu_info *uci = ucode_cpu_info + cpu; - - old = current->cpus_allowed; - - set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu)); - mutex_lock(µcode_mutex); - collect_cpu_info(cpu); - if (uci->valid && system_state == SYSTEM_RUNNING && !resume) - cpu_request_microcode(cpu); - mutex_unlock(µcode_mutex); - set_cpus_allowed_ptr(current, &old); -} - -static void microcode_fini_cpu(int cpu) -{ - struct ucode_cpu_info *uci = ucode_cpu_info + cpu; - - mutex_lock(µcode_mutex); - uci->valid = 0; - vfree(uci->mc); - uci->mc = NULL; - mutex_unlock(µcode_mutex); -} - -static ssize_t reload_store(struct sys_device *dev, - struct sysdev_attribute *attr, - const char *buf, size_t sz) -{ - struct ucode_cpu_info *uci = ucode_cpu_info + dev->id; - char *end; - unsigned long val = simple_strtoul(buf, &end, 0); - int err = 0; - int cpu = dev->id; - - if (end == buf) - return -EINVAL; - if (val == 1) { - cpumask_t old = current->cpus_allowed; - - get_online_cpus(); - set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu)); - - mutex_lock(µcode_mutex); - if (uci->valid) - err = cpu_request_microcode(cpu); - mutex_unlock(µcode_mutex); - put_online_cpus(); - set_cpus_allowed_ptr(current, &old); - } - if (err) - return err; - return sz; -} - -static ssize_t version_show(struct sys_device *dev, - struct sysdev_attribute *attr, char *buf) -{ - struct ucode_cpu_info *uci = ucode_cpu_info + dev->id; - - return sprintf(buf, "0x%x\n", uci->rev); -} - -static ssize_t pf_show(struct sys_device *dev, - struct sysdev_attribute *attr, char *buf) -{ - struct ucode_cpu_info *uci = ucode_cpu_info + dev->id; - - return sprintf(buf, "0x%x\n", uci->pf); -} - -static SYSDEV_ATTR(reload, 0200, NULL, reload_store); -static SYSDEV_ATTR(version, 0400, version_show, NULL); -static SYSDEV_ATTR(processor_flags, 0400, pf_show, NULL); - -static struct attribute *mc_default_attrs[] = { - &attr_reload.attr, - &attr_version.attr, - &attr_processor_flags.attr, - NULL -}; - -static struct attribute_group mc_attr_group = { - .attrs = mc_default_attrs, - .name = "microcode", -}; - -static int __mc_sysdev_add(struct sys_device *sys_dev, int resume) -{ - int err, cpu = sys_dev->id; - struct ucode_cpu_info *uci = ucode_cpu_info + cpu; - - if (!cpu_online(cpu)) - return 0; - - pr_debug("microcode: CPU%d added\n", cpu); - memset(uci, 0, sizeof(*uci)); - - err = sysfs_create_group(&sys_dev->kobj, &mc_attr_group); - if (err) - return err; - - microcode_init_cpu(cpu, resume); - - return 0; -} - -static int mc_sysdev_add(struct sys_device *sys_dev) -{ - return __mc_sysdev_add(sys_dev, 0); -} - -static int mc_sysdev_remove(struct sys_device *sys_dev) -{ - int cpu = sys_dev->id; - - if (!cpu_online(cpu)) - return 0; - - pr_debug("microcode: CPU%d removed\n", cpu); - microcode_fini_cpu(cpu); - sysfs_remove_group(&sys_dev->kobj, &mc_attr_group); - return 0; -} - -static int mc_sysdev_resume(struct sys_device *dev) -{ - int cpu = dev->id; - - if (!cpu_online(cpu)) - return 0; - pr_debug("microcode: CPU%d resumed\n", cpu); - /* only CPU 0 will apply ucode here */ - apply_microcode(0); - return 0; -} - -static struct sysdev_driver mc_sysdev_driver = { - .add = mc_sysdev_add, - .remove = mc_sysdev_remove, - .resume = mc_sysdev_resume, -}; - -static __cpuinit int -mc_cpu_callback(struct notifier_block *nb, unsigned long action, void *hcpu) -{ - unsigned int cpu = (unsigned long)hcpu; - struct sys_device *sys_dev; - - sys_dev = get_cpu_sysdev(cpu); - switch (action) { - case CPU_UP_CANCELED_FROZEN: - /* The CPU refused to come up during a system resume */ - microcode_fini_cpu(cpu); - break; - case CPU_ONLINE: - case CPU_DOWN_FAILED: - mc_sysdev_add(sys_dev); - break; - case CPU_ONLINE_FROZEN: - /* System-wide resume is in progress, try to apply microcode */ - if (apply_microcode_check_cpu(cpu)) { - /* The application of microcode failed */ - microcode_fini_cpu(cpu); - __mc_sysdev_add(sys_dev, 1); - break; - } - case CPU_DOWN_FAILED_FROZEN: - if (sysfs_create_group(&sys_dev->kobj, &mc_attr_group)) - printk(KERN_ERR "microcode: Failed to create the sysfs " - "group for CPU%d\n", cpu); - break; - case CPU_DOWN_PREPARE: - mc_sysdev_remove(sys_dev); - break; - case CPU_DOWN_PREPARE_FROZEN: - /* Suspend is in progress, only remove the interface */ - sysfs_remove_group(&sys_dev->kobj, &mc_attr_group); - break; - } - return NOTIFY_OK; -} - -static struct notifier_block __refdata mc_cpu_notifier = { - .notifier_call = mc_cpu_callback, -}; - -static int __init microcode_init (void) -{ - int error; - - printk(KERN_INFO - "IA-32 Microcode Update Driver: v" MICROCODE_VERSION " <tigran@aivazian.fsnet.co.uk>\n"); - - error = microcode_dev_init(); - if (error) - return error; - microcode_pdev = platform_device_register_simple("microcode", -1, - NULL, 0); - if (IS_ERR(microcode_pdev)) { - microcode_dev_exit(); - return PTR_ERR(microcode_pdev); - } - - get_online_cpus(); - error = sysdev_driver_register(&cpu_sysdev_class, &mc_sysdev_driver); - put_online_cpus(); - if (error) { - microcode_dev_exit(); - platform_device_unregister(microcode_pdev); - return error; - } - - register_hotcpu_notifier(&mc_cpu_notifier); - return 0; -} - -static void __exit microcode_exit (void) -{ - microcode_dev_exit(); - - unregister_hotcpu_notifier(&mc_cpu_notifier); - - get_online_cpus(); - sysdev_driver_unregister(&cpu_sysdev_class, &mc_sysdev_driver); - put_online_cpus(); - - platform_device_unregister(microcode_pdev); -} - -module_init(microcode_init) -module_exit(microcode_exit) diff --git a/arch/x86/kernel/microcode_amd.c b/arch/x86/kernel/microcode_amd.c new file mode 100644 index 00000000000..7a1f8eeac2c --- /dev/null +++ b/arch/x86/kernel/microcode_amd.c @@ -0,0 +1,435 @@ +/* + * AMD CPU Microcode Update Driver for Linux + * Copyright (C) 2008 Advanced Micro Devices Inc. + * + * Author: Peter Oruba <peter.oruba@amd.com> + * + * Based on work by: + * Tigran Aivazian <tigran@aivazian.fsnet.co.uk> + * + * This driver allows to upgrade microcode on AMD + * family 0x10 and 0x11 processors. + * + * Licensed unter the terms of the GNU General Public + * License version 2. See file COPYING for details. +*/ + +#include <linux/capability.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/cpumask.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/vmalloc.h> +#include <linux/miscdevice.h> +#include <linux/spinlock.h> +#include <linux/mm.h> +#include <linux/fs.h> +#include <linux/mutex.h> +#include <linux/cpu.h> +#include <linux/firmware.h> +#include <linux/platform_device.h> +#include <linux/pci.h> +#include <linux/pci_ids.h> + +#include <asm/msr.h> +#include <asm/uaccess.h> +#include <asm/processor.h> +#include <asm/microcode.h> + +MODULE_DESCRIPTION("AMD Microcode Update Driver"); +MODULE_AUTHOR("Peter Oruba <peter.oruba@amd.com>"); +MODULE_LICENSE("GPL v2"); + +#define UCODE_MAGIC 0x00414d44 +#define UCODE_EQUIV_CPU_TABLE_TYPE 0x00000000 +#define UCODE_UCODE_TYPE 0x00000001 + +struct equiv_cpu_entry { + unsigned int installed_cpu; + unsigned int fixed_errata_mask; + unsigned int fixed_errata_compare; + unsigned int equiv_cpu; +}; + +struct microcode_header_amd { + unsigned int data_code; + unsigned int patch_id; + unsigned char mc_patch_data_id[2]; + unsigned char mc_patch_data_len; + unsigned char init_flag; + unsigned int mc_patch_data_checksum; + unsigned int nb_dev_id; + unsigned int sb_dev_id; + unsigned char processor_rev_id[2]; + unsigned char nb_rev_id; + unsigned char sb_rev_id; + unsigned char bios_api_rev; + unsigned char reserved1[3]; + unsigned int match_reg[8]; +}; + +struct microcode_amd { + struct microcode_header_amd hdr; + unsigned int mpb[0]; +}; + +#define UCODE_MAX_SIZE (2048) +#define DEFAULT_UCODE_DATASIZE (896) +#define MC_HEADER_SIZE (sizeof(struct microcode_header_amd)) +#define DEFAULT_UCODE_TOTALSIZE (DEFAULT_UCODE_DATASIZE + MC_HEADER_SIZE) +#define DWSIZE (sizeof(u32)) +/* For now we support a fixed ucode total size only */ +#define get_totalsize(mc) \ + ((((struct microcode_amd *)mc)->hdr.mc_patch_data_len * 28) \ + + MC_HEADER_SIZE) + +/* serialize access to the physical write */ +static DEFINE_SPINLOCK(microcode_update_lock); + +static struct equiv_cpu_entry *equiv_cpu_table; + +static int collect_cpu_info_amd(int cpu, struct cpu_signature *csig) +{ + struct cpuinfo_x86 *c = &cpu_data(cpu); + + memset(csig, 0, sizeof(*csig)); + + if (c->x86_vendor != X86_VENDOR_AMD || c->x86 < 0x10) { + printk(KERN_ERR "microcode: CPU%d not a capable AMD processor\n", + cpu); + return -1; + } + + asm volatile("movl %1, %%ecx; rdmsr" + : "=a" (csig->rev) + : "i" (0x0000008B) : "ecx"); + + printk(KERN_INFO "microcode: collect_cpu_info_amd : patch_id=0x%x\n", + csig->rev); + + return 0; +} + +static int get_matching_microcode(int cpu, void *mc, int rev) +{ + struct microcode_header_amd *mc_header = mc; + struct pci_dev *nb_pci_dev, *sb_pci_dev; + unsigned int current_cpu_id; + unsigned int equiv_cpu_id = 0x00; + unsigned int i = 0; + + BUG_ON(equiv_cpu_table == NULL); + current_cpu_id = cpuid_eax(0x00000001); + + while (equiv_cpu_table[i].installed_cpu != 0) { + if (current_cpu_id == equiv_cpu_table[i].installed_cpu) { + equiv_cpu_id = equiv_cpu_table[i].equiv_cpu; + break; + } + i++; + } + + if (!equiv_cpu_id) { + printk(KERN_ERR "microcode: CPU%d cpu_id " + "not found in equivalent cpu table \n", cpu); + return 0; + } + + if ((mc_header->processor_rev_id[0]) != (equiv_cpu_id & 0xff)) { + printk(KERN_ERR + "microcode: CPU%d patch does not match " + "(patch is %x, cpu extended is %x) \n", + cpu, mc_header->processor_rev_id[0], + (equiv_cpu_id & 0xff)); + return 0; + } + + if ((mc_header->processor_rev_id[1]) != ((equiv_cpu_id >> 16) & 0xff)) { + printk(KERN_ERR "microcode: CPU%d patch does not match " + "(patch is %x, cpu base id is %x) \n", + cpu, mc_header->processor_rev_id[1], + ((equiv_cpu_id >> 16) & 0xff)); + + return 0; + } + + /* ucode may be northbridge specific */ + if (mc_header->nb_dev_id) { + nb_pci_dev = pci_get_device(PCI_VENDOR_ID_AMD, + (mc_header->nb_dev_id & 0xff), + NULL); + if ((!nb_pci_dev) || + (mc_header->nb_rev_id != nb_pci_dev->revision)) { + printk(KERN_ERR "microcode: CPU%d NB mismatch \n", cpu); + pci_dev_put(nb_pci_dev); + return 0; + } + pci_dev_put(nb_pci_dev); + } + + /* ucode may be southbridge specific */ + if (mc_header->sb_dev_id) { + sb_pci_dev = pci_get_device(PCI_VENDOR_ID_AMD, + (mc_header->sb_dev_id & 0xff), + NULL); + if ((!sb_pci_dev) || + (mc_header->sb_rev_id != sb_pci_dev->revision)) { + printk(KERN_ERR "microcode: CPU%d SB mismatch \n", cpu); + pci_dev_put(sb_pci_dev); + return 0; + } + pci_dev_put(sb_pci_dev); + } + + if (mc_header->patch_id <= rev) + return 0; + + return 1; +} + +static void apply_microcode_amd(int cpu) +{ + unsigned long flags; + unsigned int eax, edx; + unsigned int rev; + int cpu_num = raw_smp_processor_id(); + struct ucode_cpu_info *uci = ucode_cpu_info + cpu_num; + struct microcode_amd *mc_amd = uci->mc; + unsigned long addr; + + /* We should bind the task to the CPU */ + BUG_ON(cpu_num != cpu); + + if (mc_amd == NULL) + return; + + spin_lock_irqsave(µcode_update_lock, flags); + + addr = (unsigned long)&mc_amd->hdr.data_code; + edx = (unsigned int)(((unsigned long)upper_32_bits(addr))); + eax = (unsigned int)(((unsigned long)lower_32_bits(addr))); + + asm volatile("movl %0, %%ecx; wrmsr" : + : "i" (0xc0010020), "a" (eax), "d" (edx) : "ecx"); + + /* get patch id after patching */ + asm volatile("movl %1, %%ecx; rdmsr" + : "=a" (rev) + : "i" (0x0000008B) : "ecx"); + + spin_unlock_irqrestore(µcode_update_lock, flags); + + /* check current patch id and patch's id for match */ + if (rev != mc_amd->hdr.patch_id) { + printk(KERN_ERR "microcode: CPU%d update from revision " + "0x%x to 0x%x failed\n", cpu_num, + mc_amd->hdr.patch_id, rev); + return; + } + + printk(KERN_INFO "microcode: CPU%d updated from revision " + "0x%x to 0x%x \n", + cpu_num, uci->cpu_sig.rev, mc_amd->hdr.patch_id); + + uci->cpu_sig.rev = rev; +} + +static void * get_next_ucode(u8 *buf, unsigned int size, + int (*get_ucode_data)(void *, const void *, size_t), + unsigned int *mc_size) +{ + unsigned int total_size; +#define UCODE_CONTAINER_SECTION_HDR 8 + u8 section_hdr[UCODE_CONTAINER_SECTION_HDR]; + void *mc; + + if (get_ucode_data(section_hdr, buf, UCODE_CONTAINER_SECTION_HDR)) + return NULL; + + if (section_hdr[0] != UCODE_UCODE_TYPE) { + printk(KERN_ERR "microcode: error! " + "Wrong microcode payload type field\n"); + return NULL; + } + + total_size = (unsigned long) (section_hdr[4] + (section_hdr[5] << 8)); + + printk(KERN_INFO "microcode: size %u, total_size %u\n", + size, total_size); + + if (total_size > size || total_size > UCODE_MAX_SIZE) { + printk(KERN_ERR "microcode: error! Bad data in microcode data file\n"); + return NULL; + } + + mc = vmalloc(UCODE_MAX_SIZE); + if (mc) { + memset(mc, 0, UCODE_MAX_SIZE); + if (get_ucode_data(mc, buf + UCODE_CONTAINER_SECTION_HDR, total_size)) { + vfree(mc); + mc = NULL; + } else + *mc_size = total_size + UCODE_CONTAINER_SECTION_HDR; + } +#undef UCODE_CONTAINER_SECTION_HDR + return mc; +} + + +static int install_equiv_cpu_table(u8 *buf, + int (*get_ucode_data)(void *, const void *, size_t)) +{ +#define UCODE_CONTAINER_HEADER_SIZE 12 + u8 *container_hdr[UCODE_CONTAINER_HEADER_SIZE]; + unsigned int *buf_pos = (unsigned int *)container_hdr; + unsigned long size; + + if (get_ucode_data(&container_hdr, buf, UCODE_CONTAINER_HEADER_SIZE)) + return 0; + + size = buf_pos[2]; + + if (buf_pos[1] != UCODE_EQUIV_CPU_TABLE_TYPE || !size) { + printk(KERN_ERR "microcode: error! " + "Wrong microcode equivalnet cpu table\n"); + return 0; + } + + equiv_cpu_table = (struct equiv_cpu_entry *) vmalloc(size); + if (!equiv_cpu_table) { + printk(KERN_ERR "microcode: error, can't allocate memory for equiv CPU table\n"); + return 0; + } + + buf += UCODE_CONTAINER_HEADER_SIZE; + if (get_ucode_data(equiv_cpu_table, buf, size)) { + vfree(equiv_cpu_table); + return 0; + } + + return size + UCODE_CONTAINER_HEADER_SIZE; /* add header length */ +#undef UCODE_CONTAINER_HEADER_SIZE +} + +static void free_equiv_cpu_table(void) +{ + if (equiv_cpu_table) { + vfree(equiv_cpu_table); + equiv_cpu_table = NULL; + } +} + +static int generic_load_microcode(int cpu, void *data, size_t size, + int (*get_ucode_data)(void *, const void *, size_t)) +{ + struct ucode_cpu_info *uci = ucode_cpu_info + cpu; + u8 *ucode_ptr = data, *new_mc = NULL, *mc; + int new_rev = uci->cpu_sig.rev; + unsigned int leftover; + unsigned long offset; + + offset = install_equiv_cpu_table(ucode_ptr, get_ucode_data); + if (!offset) { + printk(KERN_ERR "microcode: installing equivalent cpu table failed\n"); + return -EINVAL; + } + + ucode_ptr += offset; + leftover = size - offset; + + while (leftover) { + unsigned int uninitialized_var(mc_size); + struct microcode_header_amd *mc_header; + + mc = get_next_ucode(ucode_ptr, leftover, get_ucode_data, &mc_size); + if (!mc) + break; + + mc_header = (struct microcode_header_amd *)mc; + if (get_matching_microcode(cpu, mc, new_rev)) { + if (new_mc) + vfree(new_mc); + new_rev = mc_header->patch_id; + new_mc = mc; + } else + vfree(mc); + + ucode_ptr += mc_size; + leftover -= mc_size; + } + + if (new_mc) { + if (!leftover) { + if (uci->mc) + vfree(uci->mc); + uci->mc = new_mc; + pr_debug("microcode: CPU%d found a matching microcode update with" + " version 0x%x (current=0x%x)\n", + cpu, new_rev, uci->cpu_sig.rev); + } else + vfree(new_mc); + } + + free_equiv_cpu_table(); + + return (int)leftover; +} + +static int get_ucode_fw(void *to, const void *from, size_t n) +{ + memcpy(to, from, n); + return 0; +} + +static int request_microcode_fw(int cpu, struct device *device) +{ + const char *fw_name = "amd-ucode/microcode_amd.bin"; + const struct firmware *firmware; + int ret; + + /* We should bind the task to the CPU */ + BUG_ON(cpu != raw_smp_processor_id()); + + ret = request_firmware(&firmware, fw_name, device); + if (ret) { + printk(KERN_ERR "microcode: ucode data file %s load failed\n", fw_name); + return ret; + } + + ret = generic_load_microcode(cpu, (void*)firmware->data, firmware->size, + &get_ucode_fw); + + release_firmware(firmware); + + return ret; +} + +static int request_microcode_user(int cpu, const void __user *buf, size_t size) +{ + printk(KERN_WARNING "microcode: AMD microcode update via /dev/cpu/microcode" + "is not supported\n"); + return -1; +} + +static void microcode_fini_cpu_amd(int cpu) +{ + struct ucode_cpu_info *uci = ucode_cpu_info + cpu; + + vfree(uci->mc); + uci->mc = NULL; +} + +static struct microcode_ops microcode_amd_ops = { + .request_microcode_user = request_microcode_user, + .request_microcode_fw = request_microcode_fw, + .collect_cpu_info = collect_cpu_info_amd, + .apply_microcode = apply_microcode_amd, + .microcode_fini_cpu = microcode_fini_cpu_amd, +}; + +struct microcode_ops * __init init_amd_microcode(void) +{ + return µcode_amd_ops; +} diff --git a/arch/x86/kernel/microcode_core.c b/arch/x86/kernel/microcode_core.c new file mode 100644 index 00000000000..936d8d55f23 --- /dev/null +++ b/arch/x86/kernel/microcode_core.c @@ -0,0 +1,508 @@ +/* + * Intel CPU Microcode Update Driver for Linux + * + * Copyright (C) 2000-2006 Tigran Aivazian <tigran@aivazian.fsnet.co.uk> + * 2006 Shaohua Li <shaohua.li@intel.com> + * + * This driver allows to upgrade microcode on Intel processors + * belonging to IA-32 family - PentiumPro, Pentium II, + * Pentium III, Xeon, Pentium 4, etc. + * + * Reference: Section 8.11 of Volume 3a, IA-32 Intel? Architecture + * Software Developer's Manual + * Order Number 253668 or free download from: + * + * http://developer.intel.com/design/pentium4/manuals/253668.htm + * + * For more information, go to http://www.urbanmyth.org/microcode + * + * 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. + * + * 1.0 16 Feb 2000, Tigran Aivazian <tigran@sco.com> + * Initial release. + * 1.01 18 Feb 2000, Tigran Aivazian <tigran@sco.com> + * Added read() support + cleanups. + * 1.02 21 Feb 2000, Tigran Aivazian <tigran@sco.com> + * Added 'device trimming' support. open(O_WRONLY) zeroes + * and frees the saved copy of applied microcode. + * 1.03 29 Feb 2000, Tigran Aivazian <tigran@sco.com> + * Made to use devfs (/dev/cpu/microcode) + cleanups. + * 1.04 06 Jun 2000, Simon Trimmer <simon@veritas.com> + * Added misc device support (now uses both devfs and misc). + * Added MICROCODE_IOCFREE ioctl to clear memory. + * 1.05 09 Jun 2000, Simon Trimmer <simon@veritas.com> + * Messages for error cases (non Intel & no suitable microcode). + * 1.06 03 Aug 2000, Tigran Aivazian <tigran@veritas.com> + * Removed ->release(). Removed exclusive open and status bitmap. + * Added microcode_rwsem to serialize read()/write()/ioctl(). + * Removed global kernel lock usage. + * 1.07 07 Sep 2000, Tigran Aivazian <tigran@veritas.com> + * Write 0 to 0x8B msr and then cpuid before reading revision, + * so that it works even if there were no update done by the + * BIOS. Otherwise, reading from 0x8B gives junk (which happened + * to be 0 on my machine which is why it worked even when I + * disabled update by the BIOS) + * Thanks to Eric W. Biederman <ebiederman@lnxi.com> for the fix. + * 1.08 11 Dec 2000, Richard Schaal <richard.schaal@intel.com> and + * Tigran Aivazian <tigran@veritas.com> + * Intel Pentium 4 processor support and bugfixes. + * 1.09 30 Oct 2001, Tigran Aivazian <tigran@veritas.com> + * Bugfix for HT (Hyper-Threading) enabled processors + * whereby processor resources are shared by all logical processors + * in a single CPU package. + * 1.10 28 Feb 2002 Asit K Mallick <asit.k.mallick@intel.com> and + * Tigran Aivazian <tigran@veritas.com>, + * Serialize updates as required on HT processors due to + * speculative nature of implementation. + * 1.11 22 Mar 2002 Tigran Aivazian <tigran@veritas.com> + * Fix the panic when writing zero-length microcode chunk. + * 1.12 29 Sep 2003 Nitin Kamble <nitin.a.kamble@intel.com>, + * Jun Nakajima <jun.nakajima@intel.com> + * Support for the microcode updates in the new format. + * 1.13 10 Oct 2003 Tigran Aivazian <tigran@veritas.com> + * Removed ->read() method and obsoleted MICROCODE_IOCFREE ioctl + * because we no longer hold a copy of applied microcode + * in kernel memory. + * 1.14 25 Jun 2004 Tigran Aivazian <tigran@veritas.com> + * Fix sigmatch() macro to handle old CPUs with pf == 0. + * Thanks to Stuart Swales for pointing out this bug. + */ +#include <linux/capability.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/smp_lock.h> +#include <linux/cpumask.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/vmalloc.h> +#include <linux/miscdevice.h> +#include <linux/spinlock.h> +#include <linux/mm.h> +#include <linux/fs.h> +#include <linux/mutex.h> +#include <linux/cpu.h> +#include <linux/firmware.h> +#include <linux/platform_device.h> + +#include <asm/msr.h> +#include <asm/uaccess.h> +#include <asm/processor.h> +#include <asm/microcode.h> + +MODULE_DESCRIPTION("Microcode Update Driver"); +MODULE_AUTHOR("Tigran Aivazian <tigran@aivazian.fsnet.co.uk>"); +MODULE_LICENSE("GPL"); + +#define MICROCODE_VERSION "2.00" + +struct microcode_ops *microcode_ops; + +/* no concurrent ->write()s are allowed on /dev/cpu/microcode */ +static DEFINE_MUTEX(microcode_mutex); + +struct ucode_cpu_info ucode_cpu_info[NR_CPUS]; +EXPORT_SYMBOL_GPL(ucode_cpu_info); + +#ifdef CONFIG_MICROCODE_OLD_INTERFACE +static int do_microcode_update(const void __user *buf, size_t size) +{ + cpumask_t old; + int error = 0; + int cpu; + + old = current->cpus_allowed; + + for_each_online_cpu(cpu) { + struct ucode_cpu_info *uci = ucode_cpu_info + cpu; + + if (!uci->valid) + continue; + + set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu)); + error = microcode_ops->request_microcode_user(cpu, buf, size); + if (error < 0) + goto out; + if (!error) + microcode_ops->apply_microcode(cpu); + } +out: + set_cpus_allowed_ptr(current, &old); + return error; +} + +static int microcode_open(struct inode *unused1, struct file *unused2) +{ + cycle_kernel_lock(); + return capable(CAP_SYS_RAWIO) ? 0 : -EPERM; +} + +static ssize_t microcode_write(struct file *file, const char __user *buf, + size_t len, loff_t *ppos) +{ + ssize_t ret; + + if ((len >> PAGE_SHIFT) > num_physpages) { + printk(KERN_ERR "microcode: too much data (max %ld pages)\n", + num_physpages); + return -EINVAL; + } + + get_online_cpus(); + mutex_lock(µcode_mutex); + + ret = do_microcode_update(buf, len); + if (!ret) + ret = (ssize_t)len; + + mutex_unlock(µcode_mutex); + put_online_cpus(); + + return ret; +} + +static const struct file_operations microcode_fops = { + .owner = THIS_MODULE, + .write = microcode_write, + .open = microcode_open, +}; + +static struct miscdevice microcode_dev = { + .minor = MICROCODE_MINOR, + .name = "microcode", + .fops = µcode_fops, +}; + +static int __init microcode_dev_init(void) +{ + int error; + + error = misc_register(µcode_dev); + if (error) { + printk(KERN_ERR + "microcode: can't misc_register on minor=%d\n", + MICROCODE_MINOR); + return error; + } + + return 0; +} + +static void microcode_dev_exit(void) +{ + misc_deregister(µcode_dev); +} + +MODULE_ALIAS_MISCDEV(MICROCODE_MINOR); +#else +#define microcode_dev_init() 0 +#define microcode_dev_exit() do { } while (0) +#endif + +/* fake device for request_firmware */ +struct platform_device *microcode_pdev; + +static ssize_t reload_store(struct sys_device *dev, + struct sysdev_attribute *attr, + const char *buf, size_t sz) +{ + struct ucode_cpu_info *uci = ucode_cpu_info + dev->id; + char *end; + unsigned long val = simple_strtoul(buf, &end, 0); + int err = 0; + int cpu = dev->id; + + if (end == buf) + return -EINVAL; + if (val == 1) { + cpumask_t old = current->cpus_allowed; + + get_online_cpus(); + if (cpu_online(cpu)) { + set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu)); + mutex_lock(µcode_mutex); + if (uci->valid) { + err = microcode_ops->request_microcode_fw(cpu, + µcode_pdev->dev); + if (!err) + microcode_ops->apply_microcode(cpu); + } + mutex_unlock(µcode_mutex); + set_cpus_allowed_ptr(current, &old); + } + put_online_cpus(); + } + if (err) + return err; + return sz; +} + +static ssize_t version_show(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) +{ + struct ucode_cpu_info *uci = ucode_cpu_info + dev->id; + + return sprintf(buf, "0x%x\n", uci->cpu_sig.rev); +} + +static ssize_t pf_show(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) +{ + struct ucode_cpu_info *uci = ucode_cpu_info + dev->id; + + return sprintf(buf, "0x%x\n", uci->cpu_sig.pf); +} + +static SYSDEV_ATTR(reload, 0200, NULL, reload_store); +static SYSDEV_ATTR(version, 0400, version_show, NULL); +static SYSDEV_ATTR(processor_flags, 0400, pf_show, NULL); + +static struct attribute *mc_default_attrs[] = { + &attr_reload.attr, + &attr_version.attr, + &attr_processor_flags.attr, + NULL +}; + +static struct attribute_group mc_attr_group = { + .attrs = mc_default_attrs, + .name = "microcode", +}; + +static void microcode_fini_cpu(int cpu) +{ + struct ucode_cpu_info *uci = ucode_cpu_info + cpu; + + mutex_lock(µcode_mutex); + microcode_ops->microcode_fini_cpu(cpu); + uci->valid = 0; + mutex_unlock(µcode_mutex); +} + +static void collect_cpu_info(int cpu) +{ + struct ucode_cpu_info *uci = ucode_cpu_info + cpu; + + memset(uci, 0, sizeof(*uci)); + if (!microcode_ops->collect_cpu_info(cpu, &uci->cpu_sig)) + uci->valid = 1; +} + +static int microcode_resume_cpu(int cpu) +{ + struct ucode_cpu_info *uci = ucode_cpu_info + cpu; + struct cpu_signature nsig; + + pr_debug("microcode: CPU%d resumed\n", cpu); + + if (!uci->mc) + return 1; + + /* + * Let's verify that the 'cached' ucode does belong + * to this cpu (a bit of paranoia): + */ + if (microcode_ops->collect_cpu_info(cpu, &nsig)) { + microcode_fini_cpu(cpu); + return -1; + } + + if (memcmp(&nsig, &uci->cpu_sig, sizeof(nsig))) { + microcode_fini_cpu(cpu); + /* Should we look for a new ucode here? */ + return 1; + } + + return 0; +} + +void microcode_update_cpu(int cpu) +{ + struct ucode_cpu_info *uci = ucode_cpu_info + cpu; + int err = 0; + + /* + * Check if the system resume is in progress (uci->valid != NULL), + * otherwise just request a firmware: + */ + if (uci->valid) { + err = microcode_resume_cpu(cpu); + } else { + collect_cpu_info(cpu); + if (uci->valid && system_state == SYSTEM_RUNNING) + err = microcode_ops->request_microcode_fw(cpu, + µcode_pdev->dev); + } + if (!err) + microcode_ops->apply_microcode(cpu); +} + +static void microcode_init_cpu(int cpu) +{ + cpumask_t old = current->cpus_allowed; + + set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu)); + /* We should bind the task to the CPU */ + BUG_ON(raw_smp_processor_id() != cpu); + + mutex_lock(µcode_mutex); + microcode_update_cpu(cpu); + mutex_unlock(µcode_mutex); + + set_cpus_allowed_ptr(current, &old); +} + +static int mc_sysdev_add(struct sys_device *sys_dev) +{ + int err, cpu = sys_dev->id; + struct ucode_cpu_info *uci = ucode_cpu_info + cpu; + + if (!cpu_online(cpu)) + return 0; + + pr_debug("microcode: CPU%d added\n", cpu); + memset(uci, 0, sizeof(*uci)); + + err = sysfs_create_group(&sys_dev->kobj, &mc_attr_group); + if (err) + return err; + + microcode_init_cpu(cpu); + return 0; +} + +static int mc_sysdev_remove(struct sys_device *sys_dev) +{ + int cpu = sys_dev->id; + + if (!cpu_online(cpu)) + return 0; + + pr_debug("microcode: CPU%d removed\n", cpu); + microcode_fini_cpu(cpu); + sysfs_remove_group(&sys_dev->kobj, &mc_attr_group); + return 0; +} + +static int mc_sysdev_resume(struct sys_device *dev) +{ + int cpu = dev->id; + + if (!cpu_online(cpu)) + return 0; + + /* only CPU 0 will apply ucode here */ + microcode_update_cpu(0); + return 0; +} + +static struct sysdev_driver mc_sysdev_driver = { + .add = mc_sysdev_add, + .remove = mc_sysdev_remove, + .resume = mc_sysdev_resume, +}; + +static __cpuinit int +mc_cpu_callback(struct notifier_block *nb, unsigned long action, void *hcpu) +{ + unsigned int cpu = (unsigned long)hcpu; + struct sys_device *sys_dev; + + sys_dev = get_cpu_sysdev(cpu); + switch (action) { + case CPU_ONLINE: + case CPU_ONLINE_FROZEN: + microcode_init_cpu(cpu); + case CPU_DOWN_FAILED: + case CPU_DOWN_FAILED_FROZEN: + pr_debug("microcode: CPU%d added\n", cpu); + if (sysfs_create_group(&sys_dev->kobj, &mc_attr_group)) + printk(KERN_ERR "microcode: Failed to create the sysfs " + "group for CPU%d\n", cpu); + break; + case CPU_DOWN_PREPARE: + case CPU_DOWN_PREPARE_FROZEN: + /* Suspend is in progress, only remove the interface */ + sysfs_remove_group(&sys_dev->kobj, &mc_attr_group); + pr_debug("microcode: CPU%d removed\n", cpu); + break; + case CPU_DEAD: + case CPU_UP_CANCELED_FROZEN: + /* The CPU refused to come up during a system resume */ + microcode_fini_cpu(cpu); + break; + } + return NOTIFY_OK; +} + +static struct notifier_block __refdata mc_cpu_notifier = { + .notifier_call = mc_cpu_callback, +}; + +static int __init microcode_init(void) +{ + struct cpuinfo_x86 *c = &cpu_data(0); + int error; + + if (c->x86_vendor == X86_VENDOR_INTEL) + microcode_ops = init_intel_microcode(); + else if (c->x86_vendor == X86_VENDOR_AMD) + microcode_ops = init_amd_microcode(); + + if (!microcode_ops) { + printk(KERN_ERR "microcode: no support for this CPU vendor\n"); + return -ENODEV; + } + + error = microcode_dev_init(); + if (error) + return error; + microcode_pdev = platform_device_register_simple("microcode", -1, + NULL, 0); + if (IS_ERR(microcode_pdev)) { + microcode_dev_exit(); + return PTR_ERR(microcode_pdev); + } + + get_online_cpus(); + error = sysdev_driver_register(&cpu_sysdev_class, &mc_sysdev_driver); + put_online_cpus(); + if (error) { + microcode_dev_exit(); + platform_device_unregister(microcode_pdev); + return error; + } + + register_hotcpu_notifier(&mc_cpu_notifier); + + printk(KERN_INFO + "Microcode Update Driver: v" MICROCODE_VERSION + " <tigran@aivazian.fsnet.co.uk>" + " <peter.oruba@amd.com>\n"); + + return 0; +} + +static void __exit microcode_exit(void) +{ + microcode_dev_exit(); + + unregister_hotcpu_notifier(&mc_cpu_notifier); + + get_online_cpus(); + sysdev_driver_unregister(&cpu_sysdev_class, &mc_sysdev_driver); + put_online_cpus(); + + platform_device_unregister(microcode_pdev); + + microcode_ops = NULL; + + printk(KERN_INFO + "Microcode Update Driver: v" MICROCODE_VERSION " removed.\n"); +} + +module_init(microcode_init); +module_exit(microcode_exit); diff --git a/arch/x86/kernel/microcode_intel.c b/arch/x86/kernel/microcode_intel.c new file mode 100644 index 00000000000..622dc4a2178 --- /dev/null +++ b/arch/x86/kernel/microcode_intel.c @@ -0,0 +1,480 @@ +/* + * Intel CPU Microcode Update Driver for Linux + * + * Copyright (C) 2000-2006 Tigran Aivazian <tigran@aivazian.fsnet.co.uk> + * 2006 Shaohua Li <shaohua.li@intel.com> + * + * This driver allows to upgrade microcode on Intel processors + * belonging to IA-32 family - PentiumPro, Pentium II, + * Pentium III, Xeon, Pentium 4, etc. + * + * Reference: Section 8.11 of Volume 3a, IA-32 Intel? Architecture + * Software Developer's Manual + * Order Number 253668 or free download from: + * + * http://developer.intel.com/design/pentium4/manuals/253668.htm + * + * For more information, go to http://www.urbanmyth.org/microcode + * + * 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. + * + * 1.0 16 Feb 2000, Tigran Aivazian <tigran@sco.com> + * Initial release. + * 1.01 18 Feb 2000, Tigran Aivazian <tigran@sco.com> + * Added read() support + cleanups. + * 1.02 21 Feb 2000, Tigran Aivazian <tigran@sco.com> + * Added 'device trimming' support. open(O_WRONLY) zeroes + * and frees the saved copy of applied microcode. + * 1.03 29 Feb 2000, Tigran Aivazian <tigran@sco.com> + * Made to use devfs (/dev/cpu/microcode) + cleanups. + * 1.04 06 Jun 2000, Simon Trimmer <simon@veritas.com> + * Added misc device support (now uses both devfs and misc). + * Added MICROCODE_IOCFREE ioctl to clear memory. + * 1.05 09 Jun 2000, Simon Trimmer <simon@veritas.com> + * Messages for error cases (non Intel & no suitable microcode). + * 1.06 03 Aug 2000, Tigran Aivazian <tigran@veritas.com> + * Removed ->release(). Removed exclusive open and status bitmap. + * Added microcode_rwsem to serialize read()/write()/ioctl(). + * Removed global kernel lock usage. + * 1.07 07 Sep 2000, Tigran Aivazian <tigran@veritas.com> + * Write 0 to 0x8B msr and then cpuid before reading revision, + * so that it works even if there were no update done by the + * BIOS. Otherwise, reading from 0x8B gives junk (which happened + * to be 0 on my machine which is why it worked even when I + * disabled update by the BIOS) + * Thanks to Eric W. Biederman <ebiederman@lnxi.com> for the fix. + * 1.08 11 Dec 2000, Richard Schaal <richard.schaal@intel.com> and + * Tigran Aivazian <tigran@veritas.com> + * Intel Pentium 4 processor support and bugfixes. + * 1.09 30 Oct 2001, Tigran Aivazian <tigran@veritas.com> + * Bugfix for HT (Hyper-Threading) enabled processors + * whereby processor resources are shared by all logical processors + * in a single CPU package. + * 1.10 28 Feb 2002 Asit K Mallick <asit.k.mallick@intel.com> and + * Tigran Aivazian <tigran@veritas.com>, + * Serialize updates as required on HT processors due to + * speculative nature of implementation. + * 1.11 22 Mar 2002 Tigran Aivazian <tigran@veritas.com> + * Fix the panic when writing zero-length microcode chunk. + * 1.12 29 Sep 2003 Nitin Kamble <nitin.a.kamble@intel.com>, + * Jun Nakajima <jun.nakajima@intel.com> + * Support for the microcode updates in the new format. + * 1.13 10 Oct 2003 Tigran Aivazian <tigran@veritas.com> + * Removed ->read() method and obsoleted MICROCODE_IOCFREE ioctl + * because we no longer hold a copy of applied microcode + * in kernel memory. + * 1.14 25 Jun 2004 Tigran Aivazian <tigran@veritas.com> + * Fix sigmatch() macro to handle old CPUs with pf == 0. + * Thanks to Stuart Swales for pointing out this bug. + */ +#include <linux/capability.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/smp_lock.h> +#include <linux/cpumask.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/vmalloc.h> +#include <linux/miscdevice.h> +#include <linux/spinlock.h> +#include <linux/mm.h> +#include <linux/fs.h> +#include <linux/mutex.h> +#include <linux/cpu.h> +#include <linux/firmware.h> +#include <linux/platform_device.h> + +#include <asm/msr.h> +#include <asm/uaccess.h> +#include <asm/processor.h> +#include <asm/microcode.h> + +MODULE_DESCRIPTION("Microcode Update Driver"); +MODULE_AUTHOR("Tigran Aivazian <tigran@aivazian.fsnet.co.uk>"); +MODULE_LICENSE("GPL"); + +struct microcode_header_intel { + unsigned int hdrver; + unsigned int rev; + unsigned int date; + unsigned int sig; + unsigned int cksum; + unsigned int ldrver; + unsigned int pf; + unsigned int datasize; + unsigned int totalsize; + unsigned int reserved[3]; +}; + +struct microcode_intel { + struct microcode_header_intel hdr; + unsigned int bits[0]; +}; + +/* microcode format is extended from prescott processors */ +struct extended_signature { + unsigned int sig; + unsigned int pf; + unsigned int cksum; +}; + +struct extended_sigtable { + unsigned int count; + unsigned int cksum; + unsigned int reserved[3]; + struct extended_signature sigs[0]; +}; + +#define DEFAULT_UCODE_DATASIZE (2000) +#define MC_HEADER_SIZE (sizeof(struct microcode_header_intel)) +#define DEFAULT_UCODE_TOTALSIZE (DEFAULT_UCODE_DATASIZE + MC_HEADER_SIZE) +#define EXT_HEADER_SIZE (sizeof(struct extended_sigtable)) +#define EXT_SIGNATURE_SIZE (sizeof(struct extended_signature)) +#define DWSIZE (sizeof(u32)) +#define get_totalsize(mc) \ + (((struct microcode_intel *)mc)->hdr.totalsize ? \ + ((struct microcode_intel *)mc)->hdr.totalsize : \ + DEFAULT_UCODE_TOTALSIZE) + +#define get_datasize(mc) \ + (((struct microcode_intel *)mc)->hdr.datasize ? \ + ((struct microcode_intel *)mc)->hdr.datasize : DEFAULT_UCODE_DATASIZE) + +#define sigmatch(s1, s2, p1, p2) \ + (((s1) == (s2)) && (((p1) & (p2)) || (((p1) == 0) && ((p2) == 0)))) + +#define exttable_size(et) ((et)->count * EXT_SIGNATURE_SIZE + EXT_HEADER_SIZE) + +/* serialize access to the physical write to MSR 0x79 */ +static DEFINE_SPINLOCK(microcode_update_lock); + +static int collect_cpu_info(int cpu_num, struct cpu_signature *csig) +{ + struct cpuinfo_x86 *c = &cpu_data(cpu_num); + unsigned int val[2]; + + memset(csig, 0, sizeof(*csig)); + + if (c->x86_vendor != X86_VENDOR_INTEL || c->x86 < 6 || + cpu_has(c, X86_FEATURE_IA64)) { + printk(KERN_ERR "microcode: CPU%d not a capable Intel " + "processor\n", cpu_num); + return -1; + } + + csig->sig = cpuid_eax(0x00000001); + + if ((c->x86_model >= 5) || (c->x86 > 6)) { + /* get processor flags from MSR 0x17 */ + rdmsr(MSR_IA32_PLATFORM_ID, val[0], val[1]); + csig->pf = 1 << ((val[1] >> 18) & 7); + } + + wrmsr(MSR_IA32_UCODE_REV, 0, 0); + /* see notes above for revision 1.07. Apparent chip bug */ + sync_core(); + /* get the current revision from MSR 0x8B */ + rdmsr(MSR_IA32_UCODE_REV, val[0], csig->rev); + pr_debug("microcode: collect_cpu_info : sig=0x%x, pf=0x%x, rev=0x%x\n", + csig->sig, csig->pf, csig->rev); + + return 0; +} + +static inline int update_match_cpu(struct cpu_signature *csig, int sig, int pf) +{ + return (!sigmatch(sig, csig->sig, pf, csig->pf)) ? 0 : 1; +} + +static inline int +update_match_revision(struct microcode_header_intel *mc_header, int rev) +{ + return (mc_header->rev <= rev) ? 0 : 1; +} + +static int microcode_sanity_check(void *mc) +{ + struct microcode_header_intel *mc_header = mc; + struct extended_sigtable *ext_header = NULL; + struct extended_signature *ext_sig; + unsigned long total_size, data_size, ext_table_size; + int sum, orig_sum, ext_sigcount = 0, i; + + total_size = get_totalsize(mc_header); + data_size = get_datasize(mc_header); + if (data_size + MC_HEADER_SIZE > total_size) { + printk(KERN_ERR "microcode: error! " + "Bad data size in microcode data file\n"); + return -EINVAL; + } + + if (mc_header->ldrver != 1 || mc_header->hdrver != 1) { + printk(KERN_ERR "microcode: error! " + "Unknown microcode update format\n"); + return -EINVAL; + } + ext_table_size = total_size - (MC_HEADER_SIZE + data_size); + if (ext_table_size) { + if ((ext_table_size < EXT_HEADER_SIZE) + || ((ext_table_size - EXT_HEADER_SIZE) % EXT_SIGNATURE_SIZE)) { + printk(KERN_ERR "microcode: error! " + "Small exttable size in microcode data file\n"); + return -EINVAL; + } + ext_header = mc + MC_HEADER_SIZE + data_size; + if (ext_table_size != exttable_size(ext_header)) { + printk(KERN_ERR "microcode: error! " + "Bad exttable size in microcode data file\n"); + return -EFAULT; + } + ext_sigcount = ext_header->count; + } + + /* check extended table checksum */ + if (ext_table_size) { + int ext_table_sum = 0; + int *ext_tablep = (int *)ext_header; + + i = ext_table_size / DWSIZE; + while (i--) + ext_table_sum += ext_tablep[i]; + if (ext_table_sum) { + printk(KERN_WARNING "microcode: aborting, " + "bad extended signature table checksum\n"); + return -EINVAL; + } + } + + /* calculate the checksum */ + orig_sum = 0; + i = (MC_HEADER_SIZE + data_size) / DWSIZE; + while (i--) + orig_sum += ((int *)mc)[i]; + if (orig_sum) { + printk(KERN_ERR "microcode: aborting, bad checksum\n"); + return -EINVAL; + } + if (!ext_table_size) + return 0; + /* check extended signature checksum */ + for (i = 0; i < ext_sigcount; i++) { + ext_sig = (void *)ext_header + EXT_HEADER_SIZE + + EXT_SIGNATURE_SIZE * i; + sum = orig_sum + - (mc_header->sig + mc_header->pf + mc_header->cksum) + + (ext_sig->sig + ext_sig->pf + ext_sig->cksum); + if (sum) { + printk(KERN_ERR "microcode: aborting, bad checksum\n"); + return -EINVAL; + } + } + return 0; +} + +/* + * return 0 - no update found + * return 1 - found update + */ +static int +get_matching_microcode(struct cpu_signature *cpu_sig, void *mc, int rev) +{ + struct microcode_header_intel *mc_header = mc; + struct extended_sigtable *ext_header; + unsigned long total_size = get_totalsize(mc_header); + int ext_sigcount, i; + struct extended_signature *ext_sig; + + if (!update_match_revision(mc_header, rev)) + return 0; + + if (update_match_cpu(cpu_sig, mc_header->sig, mc_header->pf)) + return 1; + + /* Look for ext. headers: */ + if (total_size <= get_datasize(mc_header) + MC_HEADER_SIZE) + return 0; + + ext_header = mc + get_datasize(mc_header) + MC_HEADER_SIZE; + ext_sigcount = ext_header->count; + ext_sig = (void *)ext_header + EXT_HEADER_SIZE; + + for (i = 0; i < ext_sigcount; i++) { + if (update_match_cpu(cpu_sig, ext_sig->sig, ext_sig->pf)) + return 1; + ext_sig++; + } + return 0; +} + +static void apply_microcode(int cpu) +{ + unsigned long flags; + unsigned int val[2]; + int cpu_num = raw_smp_processor_id(); + struct ucode_cpu_info *uci = ucode_cpu_info + cpu; + struct microcode_intel *mc_intel = uci->mc; + + /* We should bind the task to the CPU */ + BUG_ON(cpu_num != cpu); + + if (mc_intel == NULL) + return; + + /* serialize access to the physical write to MSR 0x79 */ + spin_lock_irqsave(µcode_update_lock, flags); + + /* write microcode via MSR 0x79 */ + wrmsr(MSR_IA32_UCODE_WRITE, + (unsigned long) mc_intel->bits, + (unsigned long) mc_intel->bits >> 16 >> 16); + wrmsr(MSR_IA32_UCODE_REV, 0, 0); + + /* see notes above for revision 1.07. Apparent chip bug */ + sync_core(); + + /* get the current revision from MSR 0x8B */ + rdmsr(MSR_IA32_UCODE_REV, val[0], val[1]); + + spin_unlock_irqrestore(µcode_update_lock, flags); + if (val[1] != mc_intel->hdr.rev) { + printk(KERN_ERR "microcode: CPU%d update from revision " + "0x%x to 0x%x failed\n", cpu_num, uci->cpu_sig.rev, val[1]); + return; + } + printk(KERN_INFO "microcode: CPU%d updated from revision " + "0x%x to 0x%x, date = %04x-%02x-%02x \n", + cpu_num, uci->cpu_sig.rev, val[1], + mc_intel->hdr.date & 0xffff, + mc_intel->hdr.date >> 24, + (mc_intel->hdr.date >> 16) & 0xff); + uci->cpu_sig.rev = val[1]; +} + +static int generic_load_microcode(int cpu, void *data, size_t size, + int (*get_ucode_data)(void *, const void *, size_t)) +{ + struct ucode_cpu_info *uci = ucode_cpu_info + cpu; + u8 *ucode_ptr = data, *new_mc = NULL, *mc; + int new_rev = uci->cpu_sig.rev; + unsigned int leftover = size; + + while (leftover) { + struct microcode_header_intel mc_header; + unsigned int mc_size; + + if (get_ucode_data(&mc_header, ucode_ptr, sizeof(mc_header))) + break; + + mc_size = get_totalsize(&mc_header); + if (!mc_size || mc_size > leftover) { + printk(KERN_ERR "microcode: error!" + "Bad data in microcode data file\n"); + break; + } + + mc = vmalloc(mc_size); + if (!mc) + break; + + if (get_ucode_data(mc, ucode_ptr, mc_size) || + microcode_sanity_check(mc) < 0) { + vfree(mc); + break; + } + + if (get_matching_microcode(&uci->cpu_sig, mc, new_rev)) { + if (new_mc) + vfree(new_mc); + new_rev = mc_header.rev; + new_mc = mc; + } else + vfree(mc); + + ucode_ptr += mc_size; + leftover -= mc_size; + } + + if (new_mc) { + if (!leftover) { + if (uci->mc) + vfree(uci->mc); + uci->mc = (struct microcode_intel *)new_mc; + pr_debug("microcode: CPU%d found a matching microcode update with" + " version 0x%x (current=0x%x)\n", + cpu, new_rev, uci->cpu_sig.rev); + } else + vfree(new_mc); + } + + return (int)leftover; +} + +static int get_ucode_fw(void *to, const void *from, size_t n) +{ + memcpy(to, from, n); + return 0; +} + +static int request_microcode_fw(int cpu, struct device *device) +{ + char name[30]; + struct cpuinfo_x86 *c = &cpu_data(cpu); + const struct firmware *firmware; + int ret; + + /* We should bind the task to the CPU */ + BUG_ON(cpu != raw_smp_processor_id()); + sprintf(name, "intel-ucode/%02x-%02x-%02x", + c->x86, c->x86_model, c->x86_mask); + ret = request_firmware(&firmware, name, device); + if (ret) { + pr_debug("microcode: data file %s load failed\n", name); + return ret; + } + + ret = generic_load_microcode(cpu, (void*)firmware->data, firmware->size, + &get_ucode_fw); + + release_firmware(firmware); + + return ret; +} + +static int get_ucode_user(void *to, const void *from, size_t n) +{ + return copy_from_user(to, from, n); +} + +static int request_microcode_user(int cpu, const void __user *buf, size_t size) +{ + /* We should bind the task to the CPU */ + BUG_ON(cpu != raw_smp_processor_id()); + + return generic_load_microcode(cpu, (void*)buf, size, &get_ucode_user); +} + +static void microcode_fini_cpu(int cpu) +{ + struct ucode_cpu_info *uci = ucode_cpu_info + cpu; + + vfree(uci->mc); + uci->mc = NULL; +} + +struct microcode_ops microcode_intel_ops = { + .request_microcode_user = request_microcode_user, + .request_microcode_fw = request_microcode_fw, + .collect_cpu_info = collect_cpu_info, + .apply_microcode = apply_microcode, + .microcode_fini_cpu = microcode_fini_cpu, +}; + +struct microcode_ops * __init init_intel_microcode(void) +{ + return µcode_intel_ops; +} + diff --git a/arch/x86/kernel/mpparse.c b/arch/x86/kernel/mpparse.c index b3fb430725c..f98f4e1dba0 100644 --- a/arch/x86/kernel/mpparse.c +++ b/arch/x86/kernel/mpparse.c @@ -397,7 +397,9 @@ static int __init smp_read_mpc(struct mp_config_table *mpc, unsigned early) generic_bigsmp_probe(); #endif +#ifdef CONFIG_X86_32 setup_apic_routing(); +#endif if (!num_processors) printk(KERN_ERR "MPTABLE: no processors registered!\n"); return num_processors; diff --git a/arch/x86/kernel/numaq_32.c b/arch/x86/kernel/numaq_32.c index eecc8c18f01..4caff39078e 100644 --- a/arch/x86/kernel/numaq_32.c +++ b/arch/x86/kernel/numaq_32.c @@ -229,6 +229,12 @@ static void __init smp_read_mpc_oem(struct mp_config_oemtable *oemtable, } } +static int __init numaq_setup_ioapic_ids(void) +{ + /* so can skip it */ + return 1; +} + static struct x86_quirks numaq_x86_quirks __initdata = { .arch_pre_time_init = numaq_pre_time_init, .arch_time_init = NULL, @@ -243,6 +249,7 @@ static struct x86_quirks numaq_x86_quirks __initdata = { .mpc_oem_bus_info = mpc_oem_bus_info, .mpc_oem_pci_bus = mpc_oem_pci_bus, .smp_read_mpc_oem = smp_read_mpc_oem, + .setup_ioapic_ids = numaq_setup_ioapic_ids, }; void numaq_mps_oem_check(struct mp_config_table *mpc, char *oem, diff --git a/arch/x86/kernel/paravirt-spinlocks.c b/arch/x86/kernel/paravirt-spinlocks.c new file mode 100644 index 00000000000..0e9f1982b1d --- /dev/null +++ b/arch/x86/kernel/paravirt-spinlocks.c @@ -0,0 +1,37 @@ +/* + * Split spinlock implementation out into its own file, so it can be + * compiled in a FTRACE-compatible way. + */ +#include <linux/spinlock.h> +#include <linux/module.h> + +#include <asm/paravirt.h> + +static void default_spin_lock_flags(struct raw_spinlock *lock, unsigned long flags) +{ + __raw_spin_lock(lock); +} + +struct pv_lock_ops pv_lock_ops = { +#ifdef CONFIG_SMP + .spin_is_locked = __ticket_spin_is_locked, + .spin_is_contended = __ticket_spin_is_contended, + + .spin_lock = __ticket_spin_lock, + .spin_lock_flags = default_spin_lock_flags, + .spin_trylock = __ticket_spin_trylock, + .spin_unlock = __ticket_spin_unlock, +#endif +}; +EXPORT_SYMBOL(pv_lock_ops); + +void __init paravirt_use_bytelocks(void) +{ +#ifdef CONFIG_SMP + pv_lock_ops.spin_is_locked = __byte_spin_is_locked; + pv_lock_ops.spin_is_contended = __byte_spin_is_contended; + pv_lock_ops.spin_lock = __byte_spin_lock; + pv_lock_ops.spin_trylock = __byte_spin_trylock; + pv_lock_ops.spin_unlock = __byte_spin_unlock; +#endif +} diff --git a/arch/x86/kernel/paravirt.c b/arch/x86/kernel/paravirt.c index e2f43768723..e4c8fb60887 100644 --- a/arch/x86/kernel/paravirt.c +++ b/arch/x86/kernel/paravirt.c @@ -268,17 +268,6 @@ enum paravirt_lazy_mode paravirt_get_lazy_mode(void) return __get_cpu_var(paravirt_lazy_mode); } -void __init paravirt_use_bytelocks(void) -{ -#ifdef CONFIG_SMP - pv_lock_ops.spin_is_locked = __byte_spin_is_locked; - pv_lock_ops.spin_is_contended = __byte_spin_is_contended; - pv_lock_ops.spin_lock = __byte_spin_lock; - pv_lock_ops.spin_trylock = __byte_spin_trylock; - pv_lock_ops.spin_unlock = __byte_spin_unlock; -#endif -} - struct pv_info pv_info = { .name = "bare hardware", .paravirt_enabled = 0, @@ -349,6 +338,10 @@ struct pv_cpu_ops pv_cpu_ops = { .write_ldt_entry = native_write_ldt_entry, .write_gdt_entry = native_write_gdt_entry, .write_idt_entry = native_write_idt_entry, + + .alloc_ldt = paravirt_nop, + .free_ldt = paravirt_nop, + .load_sp0 = native_load_sp0, #if defined(CONFIG_X86_32) || defined(CONFIG_IA32_EMULATION) @@ -374,8 +367,6 @@ struct pv_cpu_ops pv_cpu_ops = { struct pv_apic_ops pv_apic_ops = { #ifdef CONFIG_X86_LOCAL_APIC - .apic_write = native_apic_write, - .apic_read = native_apic_read, .setup_boot_clock = setup_boot_APIC_clock, .setup_secondary_clock = setup_secondary_APIC_clock, .startup_ipi_hook = paravirt_nop, @@ -462,18 +453,6 @@ struct pv_mmu_ops pv_mmu_ops = { .set_fixmap = native_set_fixmap, }; -struct pv_lock_ops pv_lock_ops = { -#ifdef CONFIG_SMP - .spin_is_locked = __ticket_spin_is_locked, - .spin_is_contended = __ticket_spin_is_contended, - - .spin_lock = __ticket_spin_lock, - .spin_trylock = __ticket_spin_trylock, - .spin_unlock = __ticket_spin_unlock, -#endif -}; -EXPORT_SYMBOL(pv_lock_ops); - EXPORT_SYMBOL_GPL(pv_time_ops); EXPORT_SYMBOL (pv_cpu_ops); EXPORT_SYMBOL (pv_mmu_ops); diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c index ec7a2ba9bce..c622772744d 100644 --- a/arch/x86/kernel/process.c +++ b/arch/x86/kernel/process.c @@ -15,7 +15,6 @@ unsigned long idle_nomwait; EXPORT_SYMBOL(idle_nomwait); struct kmem_cache *task_xstate_cachep; -static int force_mwait __cpuinitdata; int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src) { diff --git a/arch/x86/kernel/process_32.c b/arch/x86/kernel/process_32.c index 205188db962..0a1302fe6d4 100644 --- a/arch/x86/kernel/process_32.c +++ b/arch/x86/kernel/process_32.c @@ -76,47 +76,12 @@ unsigned long thread_saved_pc(struct task_struct *tsk) return ((unsigned long *)tsk->thread.sp)[3]; } -#ifdef CONFIG_HOTPLUG_CPU -#include <asm/nmi.h> - -static void cpu_exit_clear(void) -{ - int cpu = raw_smp_processor_id(); - - idle_task_exit(); - - cpu_uninit(); - irq_ctx_exit(cpu); - - cpu_clear(cpu, cpu_callout_map); - cpu_clear(cpu, cpu_callin_map); - - numa_remove_cpu(cpu); - c1e_remove_cpu(cpu); -} - -/* We don't actually take CPU down, just spin without interrupts. */ -static inline void play_dead(void) -{ - /* This must be done before dead CPU ack */ - cpu_exit_clear(); - mb(); - /* Ack it */ - __get_cpu_var(cpu_state) = CPU_DEAD; - - /* - * With physical CPU hotplug, we should halt the cpu - */ - local_irq_disable(); - /* mask all interrupts, flush any and all caches, and halt */ - wbinvd_halt(); -} -#else +#ifndef CONFIG_SMP static inline void play_dead(void) { BUG(); } -#endif /* CONFIG_HOTPLUG_CPU */ +#endif /* * The idle thread. There's no useful work to be @@ -158,7 +123,7 @@ void cpu_idle(void) } } -void __show_registers(struct pt_regs *regs, int all) +void __show_regs(struct pt_regs *regs, int all) { unsigned long cr0 = 0L, cr2 = 0L, cr3 = 0L, cr4 = 0L; unsigned long d0, d1, d2, d3, d6, d7; @@ -224,7 +189,7 @@ void __show_registers(struct pt_regs *regs, int all) void show_regs(struct pt_regs *regs) { - __show_registers(regs, 1); + __show_regs(regs, 1); show_trace(NULL, regs, ®s->sp, regs->bp); } diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c index 2a8ccb9238b..cd8c0ed02b7 100644 --- a/arch/x86/kernel/process_64.c +++ b/arch/x86/kernel/process_64.c @@ -86,30 +86,12 @@ void exit_idle(void) __exit_idle(); } -#ifdef CONFIG_HOTPLUG_CPU -DECLARE_PER_CPU(int, cpu_state); - -#include <linux/nmi.h> -/* We halt the CPU with physical CPU hotplug */ -static inline void play_dead(void) -{ - idle_task_exit(); - c1e_remove_cpu(raw_smp_processor_id()); - - mb(); - /* Ack it */ - __get_cpu_var(cpu_state) = CPU_DEAD; - - local_irq_disable(); - /* mask all interrupts, flush any and all caches, and halt */ - wbinvd_halt(); -} -#else +#ifndef CONFIG_SMP static inline void play_dead(void) { BUG(); } -#endif /* CONFIG_HOTPLUG_CPU */ +#endif /* * The idle thread. There's no useful work to be @@ -154,7 +136,7 @@ void cpu_idle(void) } /* Prints also some state that isn't saved in the pt_regs */ -void __show_regs(struct pt_regs *regs) +void __show_regs(struct pt_regs *regs, int all) { unsigned long cr0 = 0L, cr2 = 0L, cr3 = 0L, cr4 = 0L, fs, gs, shadowgs; unsigned long d0, d1, d2, d3, d6, d7; @@ -193,6 +175,9 @@ void __show_regs(struct pt_regs *regs) rdmsrl(MSR_GS_BASE, gs); rdmsrl(MSR_KERNEL_GS_BASE, shadowgs); + if (!all) + return; + cr0 = read_cr0(); cr2 = read_cr2(); cr3 = read_cr3(); @@ -218,7 +203,7 @@ void __show_regs(struct pt_regs *regs) void show_regs(struct pt_regs *regs) { printk(KERN_INFO "CPU %d:", smp_processor_id()); - __show_regs(regs); + __show_regs(regs, 1); show_trace(NULL, regs, (void *)(regs + 1), regs->bp); } @@ -754,12 +739,12 @@ unsigned long get_wchan(struct task_struct *p) if (!p || p == current || p->state == TASK_RUNNING) return 0; stack = (unsigned long)task_stack_page(p); - if (p->thread.sp < stack || p->thread.sp > stack+THREAD_SIZE) + if (p->thread.sp < stack || p->thread.sp >= stack+THREAD_SIZE) return 0; fp = *(u64 *)(p->thread.sp); do { if (fp < (unsigned long)stack || - fp > (unsigned long)stack+THREAD_SIZE) + fp >= (unsigned long)stack+THREAD_SIZE) return 0; ip = *(u64 *)(fp+8); if (!in_sched_functions(ip)) diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c index e375b658efc..0a6d8c12e10 100644 --- a/arch/x86/kernel/ptrace.c +++ b/arch/x86/kernel/ptrace.c @@ -40,7 +40,9 @@ enum x86_regset { REGSET_GENERAL, REGSET_FP, REGSET_XFP, + REGSET_IOPERM64 = REGSET_XFP, REGSET_TLS, + REGSET_IOPERM32, }; /* @@ -555,6 +557,29 @@ static int ptrace_set_debugreg(struct task_struct *child, return 0; } +/* + * These access the current or another (stopped) task's io permission + * bitmap for debugging or core dump. + */ +static int ioperm_active(struct task_struct *target, + const struct user_regset *regset) +{ + return target->thread.io_bitmap_max / regset->size; +} + +static int ioperm_get(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf) +{ + if (!target->thread.io_bitmap_ptr) + return -ENXIO; + + return user_regset_copyout(&pos, &count, &kbuf, &ubuf, + target->thread.io_bitmap_ptr, + 0, IO_BITMAP_BYTES); +} + #ifdef CONFIG_X86_PTRACE_BTS /* * The configuration for a particular BTS hardware implementation. @@ -1385,6 +1410,12 @@ static const struct user_regset x86_64_regsets[] = { .size = sizeof(long), .align = sizeof(long), .active = xfpregs_active, .get = xfpregs_get, .set = xfpregs_set }, + [REGSET_IOPERM64] = { + .core_note_type = NT_386_IOPERM, + .n = IO_BITMAP_LONGS, + .size = sizeof(long), .align = sizeof(long), + .active = ioperm_active, .get = ioperm_get + }, }; static const struct user_regset_view user_x86_64_view = { @@ -1431,6 +1462,12 @@ static const struct user_regset x86_32_regsets[] = { .active = regset_tls_active, .get = regset_tls_get, .set = regset_tls_set }, + [REGSET_IOPERM32] = { + .core_note_type = NT_386_IOPERM, + .n = IO_BITMAP_BYTES / sizeof(u32), + .size = sizeof(u32), .align = sizeof(u32), + .active = ioperm_active, .get = ioperm_get + }, }; static const struct user_regset_view user_x86_32_view = { @@ -1452,7 +1489,8 @@ const struct user_regset_view *task_user_regset_view(struct task_struct *task) #endif } -void send_sigtrap(struct task_struct *tsk, struct pt_regs *regs, int error_code) +void send_sigtrap(struct task_struct *tsk, struct pt_regs *regs, + int error_code, int si_code) { struct siginfo info; @@ -1461,7 +1499,7 @@ void send_sigtrap(struct task_struct *tsk, struct pt_regs *regs, int error_code) memset(&info, 0, sizeof(info)); info.si_signo = SIGTRAP; - info.si_code = TRAP_BRKPT; + info.si_code = si_code; /* User-mode ip? */ info.si_addr = user_mode_vm(regs) ? (void __user *) regs->ip : NULL; @@ -1548,5 +1586,5 @@ asmregparm void syscall_trace_leave(struct pt_regs *regs) */ if (test_thread_flag(TIF_SINGLESTEP) && tracehook_consider_fatal_signal(current, SIGTRAP, SIG_DFL)) - send_sigtrap(current, regs, 0); + send_sigtrap(current, regs, 0, TRAP_BRKPT); } diff --git a/arch/x86/kernel/quirks.c b/arch/x86/kernel/quirks.c index d1385881810..f6a11b9b1f9 100644 --- a/arch/x86/kernel/quirks.c +++ b/arch/x86/kernel/quirks.c @@ -354,9 +354,27 @@ static void ati_force_hpet_resume(void) printk(KERN_DEBUG "Force enabled HPET at resume\n"); } +static u32 ati_ixp4x0_rev(struct pci_dev *dev) +{ + u32 d; + u8 b; + + pci_read_config_byte(dev, 0xac, &b); + b &= ~(1<<5); + pci_write_config_byte(dev, 0xac, b); + pci_read_config_dword(dev, 0x70, &d); + d |= 1<<8; + pci_write_config_dword(dev, 0x70, d); + pci_read_config_dword(dev, 0x8, &d); + d &= 0xff; + dev_printk(KERN_DEBUG, &dev->dev, "SB4X0 revision 0x%x\n", d); + return d; +} + static void ati_force_enable_hpet(struct pci_dev *dev) { - u32 uninitialized_var(val); + u32 d, val; + u8 b; if (hpet_address || force_hpet_address) return; @@ -366,14 +384,33 @@ static void ati_force_enable_hpet(struct pci_dev *dev) return; } + d = ati_ixp4x0_rev(dev); + if (d < 0x82) + return; + + /* base address */ pci_write_config_dword(dev, 0x14, 0xfed00000); pci_read_config_dword(dev, 0x14, &val); + + /* enable interrupt */ + outb(0x72, 0xcd6); b = inb(0xcd7); + b |= 0x1; + outb(0x72, 0xcd6); outb(b, 0xcd7); + outb(0x72, 0xcd6); b = inb(0xcd7); + if (!(b & 0x1)) + return; + pci_read_config_dword(dev, 0x64, &d); + d |= (1<<10); + pci_write_config_dword(dev, 0x64, d); + pci_read_config_dword(dev, 0x64, &d); + if (!(d & (1<<10))) + return; + force_hpet_address = val; force_hpet_resume_type = ATI_FORCE_HPET_RESUME; dev_printk(KERN_DEBUG, &dev->dev, "Force enabled HPET at 0x%lx\n", force_hpet_address); cached_dev = dev; - return; } DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP400_SMBUS, ati_force_enable_hpet); diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index 141efab5240..2255782e8d4 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c @@ -302,7 +302,7 @@ static void __init relocate_initrd(void) if (clen > MAX_MAP_CHUNK-slop) clen = MAX_MAP_CHUNK-slop; mapaddr = ramdisk_image & PAGE_MASK; - p = early_ioremap(mapaddr, clen+slop); + p = early_memremap(mapaddr, clen+slop); memcpy(q, p+slop, clen); early_iounmap(p, clen+slop); q += clen; @@ -379,7 +379,7 @@ static void __init parse_setup_data(void) return; pa_data = boot_params.hdr.setup_data; while (pa_data) { - data = early_ioremap(pa_data, PAGE_SIZE); + data = early_memremap(pa_data, PAGE_SIZE); switch (data->type) { case SETUP_E820_EXT: parse_e820_ext(data, pa_data); @@ -402,7 +402,7 @@ static void __init e820_reserve_setup_data(void) return; pa_data = boot_params.hdr.setup_data; while (pa_data) { - data = early_ioremap(pa_data, sizeof(*data)); + data = early_memremap(pa_data, sizeof(*data)); e820_update_range(pa_data, sizeof(*data)+data->len, E820_RAM, E820_RESERVED_KERN); found = 1; @@ -428,7 +428,7 @@ static void __init reserve_early_setup_data(void) return; pa_data = boot_params.hdr.setup_data; while (pa_data) { - data = early_ioremap(pa_data, sizeof(*data)); + data = early_memremap(pa_data, sizeof(*data)); sprintf(buf, "setup data %x", data->type); reserve_early(pa_data, pa_data+sizeof(*data)+data->len, buf); pa_data = data->next; @@ -582,6 +582,190 @@ static struct x86_quirks default_x86_quirks __initdata; struct x86_quirks *x86_quirks __initdata = &default_x86_quirks; /* + * Some BIOSes seem to corrupt the low 64k of memory during events + * like suspend/resume and unplugging an HDMI cable. Reserve all + * remaining free memory in that area and fill it with a distinct + * pattern. + */ +#ifdef CONFIG_X86_CHECK_BIOS_CORRUPTION +#define MAX_SCAN_AREAS 8 + +static int __read_mostly memory_corruption_check = -1; + +static unsigned __read_mostly corruption_check_size = 64*1024; +static unsigned __read_mostly corruption_check_period = 60; /* seconds */ + +static struct e820entry scan_areas[MAX_SCAN_AREAS]; +static int num_scan_areas; + + +static int set_corruption_check(char *arg) +{ + char *end; + + memory_corruption_check = simple_strtol(arg, &end, 10); + + return (*end == 0) ? 0 : -EINVAL; +} +early_param("memory_corruption_check", set_corruption_check); + +static int set_corruption_check_period(char *arg) +{ + char *end; + + corruption_check_period = simple_strtoul(arg, &end, 10); + + return (*end == 0) ? 0 : -EINVAL; +} +early_param("memory_corruption_check_period", set_corruption_check_period); + +static int set_corruption_check_size(char *arg) +{ + char *end; + unsigned size; + + size = memparse(arg, &end); + + if (*end == '\0') + corruption_check_size = size; + + return (size == corruption_check_size) ? 0 : -EINVAL; +} +early_param("memory_corruption_check_size", set_corruption_check_size); + + +static void __init setup_bios_corruption_check(void) +{ + u64 addr = PAGE_SIZE; /* assume first page is reserved anyway */ + + if (memory_corruption_check == -1) { + memory_corruption_check = +#ifdef CONFIG_X86_BOOTPARAM_MEMORY_CORRUPTION_CHECK + 1 +#else + 0 +#endif + ; + } + + if (corruption_check_size == 0) + memory_corruption_check = 0; + + if (!memory_corruption_check) + return; + + corruption_check_size = round_up(corruption_check_size, PAGE_SIZE); + + while(addr < corruption_check_size && num_scan_areas < MAX_SCAN_AREAS) { + u64 size; + addr = find_e820_area_size(addr, &size, PAGE_SIZE); + + if (addr == 0) + break; + + if ((addr + size) > corruption_check_size) + size = corruption_check_size - addr; + + if (size == 0) + break; + + e820_update_range(addr, size, E820_RAM, E820_RESERVED); + scan_areas[num_scan_areas].addr = addr; + scan_areas[num_scan_areas].size = size; + num_scan_areas++; + + /* Assume we've already mapped this early memory */ + memset(__va(addr), 0, size); + + addr += size; + } + + printk(KERN_INFO "Scanning %d areas for low memory corruption\n", + num_scan_areas); + update_e820(); +} + +static struct timer_list periodic_check_timer; + +void check_for_bios_corruption(void) +{ + int i; + int corruption = 0; + + if (!memory_corruption_check) + return; + + for(i = 0; i < num_scan_areas; i++) { + unsigned long *addr = __va(scan_areas[i].addr); + unsigned long size = scan_areas[i].size; + + for(; size; addr++, size -= sizeof(unsigned long)) { + if (!*addr) + continue; + printk(KERN_ERR "Corrupted low memory at %p (%lx phys) = %08lx\n", + addr, __pa(addr), *addr); + corruption = 1; + *addr = 0; + } + } + + WARN(corruption, KERN_ERR "Memory corruption detected in low memory\n"); +} + +static void periodic_check_for_corruption(unsigned long data) +{ + check_for_bios_corruption(); + mod_timer(&periodic_check_timer, round_jiffies(jiffies + corruption_check_period*HZ)); +} + +void start_periodic_check_for_corruption(void) +{ + if (!memory_corruption_check || corruption_check_period == 0) + return; + + printk(KERN_INFO "Scanning for low memory corruption every %d seconds\n", + corruption_check_period); + + init_timer(&periodic_check_timer); + periodic_check_timer.function = &periodic_check_for_corruption; + periodic_check_for_corruption(0); +} +#endif + +static int __init dmi_low_memory_corruption(const struct dmi_system_id *d) +{ + printk(KERN_NOTICE + "%s detected: BIOS may corrupt low RAM, working it around.\n", + d->ident); + + e820_update_range(0, 0x10000, E820_RAM, E820_RESERVED); + sanitize_e820_map(e820.map, ARRAY_SIZE(e820.map), &e820.nr_map); + + return 0; +} + +/* List of systems that have known low memory corruption BIOS problems */ +static struct dmi_system_id __initdata bad_bios_dmi_table[] = { +#ifdef CONFIG_X86_RESERVE_LOW_64K + { + .callback = dmi_low_memory_corruption, + .ident = "AMI BIOS", + .matches = { + DMI_MATCH(DMI_BIOS_VENDOR, "American Megatrends Inc."), + }, + }, + { + .callback = dmi_low_memory_corruption, + .ident = "Phoenix BIOS", + .matches = { + DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies, LTD"), + }, + }, +#endif + {} +}; + +/* * Determine if we were loaded by an EFI loader. If so, then we have also been * passed the efi memmap, systab, etc., so we should use these data structures * for initialization. Note, the efi init code path is determined by the @@ -715,6 +899,10 @@ void __init setup_arch(char **cmdline_p) finish_e820_parsing(); + dmi_scan_machine(); + + dmi_check_system(bad_bios_dmi_table); + #ifdef CONFIG_X86_32 probe_roms(); #endif @@ -758,6 +946,8 @@ void __init setup_arch(char **cmdline_p) #else num_physpages = max_pfn; + if (cpu_has_x2apic) + check_x2apic(); /* How many end-of-memory variables you have, grandma! */ /* need this before calling reserve_initrd */ @@ -769,6 +959,10 @@ void __init setup_arch(char **cmdline_p) high_memory = (void *)__va(max_pfn * PAGE_SIZE - 1) + 1; #endif +#ifdef CONFIG_X86_CHECK_BIOS_CORRUPTION + setup_bios_corruption_check(); +#endif + /* max_pfn_mapped is updated here */ max_low_pfn_mapped = init_memory_mapping(0, max_low_pfn<<PAGE_SHIFT); max_pfn_mapped = max_low_pfn_mapped; @@ -797,8 +991,6 @@ void __init setup_arch(char **cmdline_p) vsmp_init(); #endif - dmi_scan_machine(); - io_delay_init(); /* @@ -806,6 +998,8 @@ void __init setup_arch(char **cmdline_p) */ acpi_boot_table_init(); + early_acpi_boot_init(); + #ifdef CONFIG_ACPI_NUMA /* * Parse SRAT to discover nodes. @@ -901,3 +1095,5 @@ void __init setup_arch(char **cmdline_p) #endif #endif } + + diff --git a/arch/x86/kernel/sigframe.h b/arch/x86/kernel/sigframe.h index 8b4956e800a..cc673aa55ce 100644 --- a/arch/x86/kernel/sigframe.h +++ b/arch/x86/kernel/sigframe.h @@ -3,9 +3,18 @@ struct sigframe { char __user *pretcode; int sig; struct sigcontext sc; - struct _fpstate fpstate; + /* + * fpstate is unused. fpstate is moved/allocated after + * retcode[] below. This movement allows to have the FP state and the + * future state extensions (xsave) stay together. + * And at the same time retaining the unused fpstate, prevents changing + * the offset of extramask[] in the sigframe and thus prevent any + * legacy application accessing/modifying it. + */ + struct _fpstate fpstate_unused; unsigned long extramask[_NSIG_WORDS-1]; char retcode[8]; + /* fp state follows here */ }; struct rt_sigframe { @@ -15,14 +24,15 @@ struct rt_sigframe { void __user *puc; struct siginfo info; struct ucontext uc; - struct _fpstate fpstate; char retcode[8]; + /* fp state follows here */ }; #else struct rt_sigframe { char __user *pretcode; struct ucontext uc; struct siginfo info; + /* fp state follows here */ }; int ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, diff --git a/arch/x86/kernel/signal_32.c b/arch/x86/kernel/signal_32.c index 2a2435d3037..d6dd057d0f2 100644 --- a/arch/x86/kernel/signal_32.c +++ b/arch/x86/kernel/signal_32.c @@ -27,6 +27,7 @@ #include <asm/uaccess.h> #include <asm/i387.h> #include <asm/vdso.h> +#include <asm/syscall.h> #include <asm/syscalls.h> #include "sigframe.h" @@ -112,6 +113,27 @@ asmlinkage int sys_sigaltstack(unsigned long bx) return do_sigaltstack(uss, uoss, regs->sp); } +#define COPY(x) { \ + err |= __get_user(regs->x, &sc->x); \ +} + +#define COPY_SEG(seg) { \ + unsigned short tmp; \ + err |= __get_user(tmp, &sc->seg); \ + regs->seg = tmp; \ +} + +#define COPY_SEG_STRICT(seg) { \ + unsigned short tmp; \ + err |= __get_user(tmp, &sc->seg); \ + regs->seg = tmp | 3; \ +} + +#define GET_SEG(seg) { \ + unsigned short tmp; \ + err |= __get_user(tmp, &sc->seg); \ + loadsegment(seg, tmp); \ +} /* * Do a signal return; undo the signal stack. @@ -120,28 +142,13 @@ static int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc, unsigned long *pax) { + void __user *buf; + unsigned int tmpflags; unsigned int err = 0; /* Always make any pending restarted system calls return -EINTR */ current_thread_info()->restart_block.fn = do_no_restart_syscall; -#define COPY(x) err |= __get_user(regs->x, &sc->x) - -#define COPY_SEG(seg) \ - { unsigned short tmp; \ - err |= __get_user(tmp, &sc->seg); \ - regs->seg = tmp; } - -#define COPY_SEG_STRICT(seg) \ - { unsigned short tmp; \ - err |= __get_user(tmp, &sc->seg); \ - regs->seg = tmp|3; } - -#define GET_SEG(seg) \ - { unsigned short tmp; \ - err |= __get_user(tmp, &sc->seg); \ - loadsegment(seg, tmp); } - GET_SEG(gs); COPY_SEG(fs); COPY_SEG(es); @@ -151,38 +158,15 @@ restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc, COPY_SEG_STRICT(cs); COPY_SEG_STRICT(ss); - { - unsigned int tmpflags; - - err |= __get_user(tmpflags, &sc->flags); - regs->flags = (regs->flags & ~FIX_EFLAGS) | - (tmpflags & FIX_EFLAGS); - regs->orig_ax = -1; /* disable syscall checks */ - } - - { - struct _fpstate __user *buf; + err |= __get_user(tmpflags, &sc->flags); + regs->flags = (regs->flags & ~FIX_EFLAGS) | (tmpflags & FIX_EFLAGS); + regs->orig_ax = -1; /* disable syscall checks */ - err |= __get_user(buf, &sc->fpstate); - if (buf) { - if (!access_ok(VERIFY_READ, buf, sizeof(*buf))) - goto badframe; - err |= restore_i387(buf); - } else { - struct task_struct *me = current; - - if (used_math()) { - clear_fpu(me); - clear_used_math(); - } - } - } + err |= __get_user(buf, &sc->fpstate); + err |= restore_i387_xstate(buf); err |= __get_user(*pax, &sc->ax); return err; - -badframe: - return 1; } asmlinkage unsigned long sys_sigreturn(unsigned long __unused) @@ -228,9 +212,8 @@ badframe: return 0; } -asmlinkage int sys_rt_sigreturn(unsigned long __unused) +static long do_rt_sigreturn(struct pt_regs *regs) { - struct pt_regs *regs = (struct pt_regs *)&__unused; struct rt_sigframe __user *frame; unsigned long ax; sigset_t set; @@ -256,15 +239,22 @@ asmlinkage int sys_rt_sigreturn(unsigned long __unused) return ax; badframe: - force_sig(SIGSEGV, current); + signal_fault(regs, frame, "rt_sigreturn"); return 0; } +asmlinkage int sys_rt_sigreturn(unsigned long __unused) +{ + struct pt_regs *regs = (struct pt_regs *)&__unused; + + return do_rt_sigreturn(regs); +} + /* * Set up a signal frame. */ static int -setup_sigcontext(struct sigcontext __user *sc, struct _fpstate __user *fpstate, +setup_sigcontext(struct sigcontext __user *sc, void __user *fpstate, struct pt_regs *regs, unsigned long mask) { int tmp, err = 0; @@ -291,7 +281,7 @@ setup_sigcontext(struct sigcontext __user *sc, struct _fpstate __user *fpstate, err |= __put_user(regs->sp, &sc->sp_at_signal); err |= __put_user(regs->ss, (unsigned int __user *)&sc->ss); - tmp = save_i387(fpstate); + tmp = save_i387_xstate(fpstate); if (tmp < 0) err = 1; else @@ -308,7 +298,8 @@ setup_sigcontext(struct sigcontext __user *sc, struct _fpstate __user *fpstate, * Determine which stack to use.. */ static inline void __user * -get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size) +get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size, + void **fpstate) { unsigned long sp; @@ -334,6 +325,11 @@ get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size) sp = (unsigned long) ka->sa.sa_restorer; } + if (used_math()) { + sp = sp - sig_xstate_size; + *fpstate = (struct _fpstate *) sp; + } + sp -= frame_size; /* * Align the stack pointer according to the i386 ABI, @@ -345,38 +341,29 @@ get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size) } static int -setup_frame(int sig, struct k_sigaction *ka, sigset_t *set, - struct pt_regs *regs) +__setup_frame(int sig, struct k_sigaction *ka, sigset_t *set, + struct pt_regs *regs) { struct sigframe __user *frame; void __user *restorer; int err = 0; - int usig; + void __user *fpstate = NULL; - frame = get_sigframe(ka, regs, sizeof(*frame)); + frame = get_sigframe(ka, regs, sizeof(*frame), &fpstate); if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) - goto give_sigsegv; + return -EFAULT; - usig = current_thread_info()->exec_domain - && current_thread_info()->exec_domain->signal_invmap - && sig < 32 - ? current_thread_info()->exec_domain->signal_invmap[sig] - : sig; + if (__put_user(sig, &frame->sig)) + return -EFAULT; - err = __put_user(usig, &frame->sig); - if (err) - goto give_sigsegv; - - err = setup_sigcontext(&frame->sc, &frame->fpstate, regs, set->sig[0]); - if (err) - goto give_sigsegv; + if (setup_sigcontext(&frame->sc, fpstate, regs, set->sig[0])) + return -EFAULT; if (_NSIG_WORDS > 1) { - err = __copy_to_user(&frame->extramask, &set->sig[1], - sizeof(frame->extramask)); - if (err) - goto give_sigsegv; + if (__copy_to_user(&frame->extramask, &set->sig[1], + sizeof(frame->extramask))) + return -EFAULT; } if (current->mm->context.vdso) @@ -401,7 +388,7 @@ setup_frame(int sig, struct k_sigaction *ka, sigset_t *set, err |= __put_user(0x80cd, (short __user *)(frame->retcode+6)); if (err) - goto give_sigsegv; + return -EFAULT; /* Set up registers for signal handler */ regs->sp = (unsigned long)frame; @@ -416,50 +403,43 @@ setup_frame(int sig, struct k_sigaction *ka, sigset_t *set, regs->cs = __USER_CS; return 0; - -give_sigsegv: - force_sigsegv(sig, current); - return -EFAULT; } -static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, - sigset_t *set, struct pt_regs *regs) +static int __setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, + sigset_t *set, struct pt_regs *regs) { struct rt_sigframe __user *frame; void __user *restorer; int err = 0; - int usig; + void __user *fpstate = NULL; - frame = get_sigframe(ka, regs, sizeof(*frame)); + frame = get_sigframe(ka, regs, sizeof(*frame), &fpstate); if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) - goto give_sigsegv; + return -EFAULT; - usig = current_thread_info()->exec_domain - && current_thread_info()->exec_domain->signal_invmap - && sig < 32 - ? current_thread_info()->exec_domain->signal_invmap[sig] - : sig; - - err |= __put_user(usig, &frame->sig); + err |= __put_user(sig, &frame->sig); err |= __put_user(&frame->info, &frame->pinfo); err |= __put_user(&frame->uc, &frame->puc); err |= copy_siginfo_to_user(&frame->info, info); if (err) - goto give_sigsegv; + return -EFAULT; /* Create the ucontext. */ - err |= __put_user(0, &frame->uc.uc_flags); + if (cpu_has_xsave) + err |= __put_user(UC_FP_XSTATE, &frame->uc.uc_flags); + else + err |= __put_user(0, &frame->uc.uc_flags); err |= __put_user(0, &frame->uc.uc_link); err |= __put_user(current->sas_ss_sp, &frame->uc.uc_stack.ss_sp); err |= __put_user(sas_ss_flags(regs->sp), &frame->uc.uc_stack.ss_flags); err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size); - err |= setup_sigcontext(&frame->uc.uc_mcontext, &frame->fpstate, + err |= setup_sigcontext(&frame->uc.uc_mcontext, fpstate, regs, set->sig[0]); err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); if (err) - goto give_sigsegv; + return -EFAULT; /* Set up to return from userspace. */ restorer = VDSO32_SYMBOL(current->mm->context.vdso, rt_sigreturn); @@ -479,12 +459,12 @@ static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, err |= __put_user(0x80cd, (short __user *)(frame->retcode+5)); if (err) - goto give_sigsegv; + return -EFAULT; /* Set up registers for signal handler */ regs->sp = (unsigned long)frame; regs->ip = (unsigned long)ka->sa.sa_handler; - regs->ax = (unsigned long)usig; + regs->ax = (unsigned long)sig; regs->dx = (unsigned long)&frame->info; regs->cx = (unsigned long)&frame->uc; @@ -494,15 +474,48 @@ static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, regs->cs = __USER_CS; return 0; - -give_sigsegv: - force_sigsegv(sig, current); - return -EFAULT; } /* * OK, we're invoking a handler: */ +static int signr_convert(int sig) +{ + struct thread_info *info = current_thread_info(); + + if (info->exec_domain && info->exec_domain->signal_invmap && sig < 32) + return info->exec_domain->signal_invmap[sig]; + return sig; +} + +#define is_ia32 1 +#define ia32_setup_frame __setup_frame +#define ia32_setup_rt_frame __setup_rt_frame + +static int +setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, + sigset_t *set, struct pt_regs *regs) +{ + int usig = signr_convert(sig); + int ret; + + /* Set up the stack frame */ + if (is_ia32) { + if (ka->sa.sa_flags & SA_SIGINFO) + ret = ia32_setup_rt_frame(usig, ka, info, set, regs); + else + ret = ia32_setup_frame(usig, ka, set, regs); + } else + ret = __setup_rt_frame(sig, ka, info, set, regs); + + if (ret) { + force_sigsegv(sig, current); + return -EFAULT; + } + + return ret; +} + static int handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka, sigset_t *oldset, struct pt_regs *regs) @@ -510,9 +523,9 @@ handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka, int ret; /* Are we from a system call? */ - if ((long)regs->orig_ax >= 0) { + if (syscall_get_nr(current, regs) >= 0) { /* If so, check system call restarting.. */ - switch (regs->ax) { + switch (syscall_get_error(current, regs)) { case -ERESTART_RESTARTBLOCK: case -ERESTARTNOHAND: regs->ax = -EINTR; @@ -539,15 +552,20 @@ handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka, likely(test_and_clear_thread_flag(TIF_FORCED_TF))) regs->flags &= ~X86_EFLAGS_TF; - /* Set up the stack frame */ - if (ka->sa.sa_flags & SA_SIGINFO) - ret = setup_rt_frame(sig, ka, info, oldset, regs); - else - ret = setup_frame(sig, ka, oldset, regs); + ret = setup_rt_frame(sig, ka, info, oldset, regs); if (ret) return ret; +#ifdef CONFIG_X86_64 + /* + * This has nothing to do with segment registers, + * despite the name. This magic affects uaccess.h + * macros' behavior. Reset it to the normal setting. + */ + set_fs(USER_DS); +#endif + /* * Clear the direction flag as per the ABI for function entry. */ @@ -574,6 +592,7 @@ handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka, return 0; } +#define NR_restart_syscall __NR_restart_syscall /* * Note that 'init' is a special process: it doesn't get signals it doesn't * want to handle. Thus you cannot kill init even with a SIGKILL even by @@ -626,9 +645,9 @@ static void do_signal(struct pt_regs *regs) } /* Did we come from a system call? */ - if ((long)regs->orig_ax >= 0) { + if (syscall_get_nr(current, regs) >= 0) { /* Restart the system call - no handlers present */ - switch (regs->ax) { + switch (syscall_get_error(current, regs)) { case -ERESTARTNOHAND: case -ERESTARTSYS: case -ERESTARTNOINTR: @@ -637,7 +656,7 @@ static void do_signal(struct pt_regs *regs) break; case -ERESTART_RESTARTBLOCK: - regs->ax = __NR_restart_syscall; + regs->ax = NR_restart_syscall; regs->ip -= 2; break; } @@ -660,6 +679,12 @@ static void do_signal(struct pt_regs *regs) void do_notify_resume(struct pt_regs *regs, void *unused, __u32 thread_info_flags) { +#if defined(CONFIG_X86_64) && defined(CONFIG_X86_MCE) + /* notify userspace of pending MCEs */ + if (thread_info_flags & _TIF_MCE_NOTIFY) + mce_notify_user(); +#endif /* CONFIG_X86_64 && CONFIG_X86_MCE */ + /* deal with pending signal delivery */ if (thread_info_flags & _TIF_SIGPENDING) do_signal(regs); @@ -669,5 +694,23 @@ do_notify_resume(struct pt_regs *regs, void *unused, __u32 thread_info_flags) tracehook_notify_resume(regs); } +#ifdef CONFIG_X86_32 clear_thread_flag(TIF_IRET); +#endif /* CONFIG_X86_32 */ +} + +void signal_fault(struct pt_regs *regs, void __user *frame, char *where) +{ + struct task_struct *me = current; + + if (show_unhandled_signals && printk_ratelimit()) { + printk(KERN_INFO + "%s[%d] bad frame in %s frame:%p ip:%lx sp:%lx orax:%lx", + me->comm, me->pid, where, frame, + regs->ip, regs->sp, regs->orig_ax); + print_vma_addr(" in ", regs->ip); + printk(KERN_CONT "\n"); + } + + force_sig(SIGSEGV, me); } diff --git a/arch/x86/kernel/signal_64.c b/arch/x86/kernel/signal_64.c index 694aa888bb1..a5c9627f4db 100644 --- a/arch/x86/kernel/signal_64.c +++ b/arch/x86/kernel/signal_64.c @@ -52,67 +52,14 @@ sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss, return do_sigaltstack(uss, uoss, regs->sp); } -/* - * Signal frame handlers. - */ - -static inline int save_i387(struct _fpstate __user *buf) -{ - struct task_struct *tsk = current; - int err = 0; - - BUILD_BUG_ON(sizeof(struct user_i387_struct) != - sizeof(tsk->thread.xstate->fxsave)); - - if ((unsigned long)buf % 16) - printk("save_i387: bad fpstate %p\n", buf); - - if (!used_math()) - return 0; - clear_used_math(); /* trigger finit */ - if (task_thread_info(tsk)->status & TS_USEDFPU) { - err = save_i387_checking((struct i387_fxsave_struct __user *) - buf); - if (err) - return err; - task_thread_info(tsk)->status &= ~TS_USEDFPU; - stts(); - } else { - if (__copy_to_user(buf, &tsk->thread.xstate->fxsave, - sizeof(struct i387_fxsave_struct))) - return -1; - } - return 1; +#define COPY(x) { \ + err |= __get_user(regs->x, &sc->x); \ } -/* - * This restores directly out of user space. Exceptions are handled. - */ -static inline int restore_i387(struct _fpstate __user *buf) -{ - struct task_struct *tsk = current; - int err; - - if (!used_math()) { - err = init_fpu(tsk); - if (err) - return err; - } - - if (!(task_thread_info(current)->status & TS_USEDFPU)) { - clts(); - task_thread_info(current)->status |= TS_USEDFPU; - } - err = restore_fpu_checking((__force struct i387_fxsave_struct *)buf); - if (unlikely(err)) { - /* - * Encountered an error while doing the restore from the - * user buffer, clear the fpu state. - */ - clear_fpu(tsk); - clear_used_math(); - } - return err; +#define COPY_SEG_STRICT(seg) { \ + unsigned short tmp; \ + err |= __get_user(tmp, &sc->seg); \ + regs->seg = tmp | 3; \ } /* @@ -122,13 +69,13 @@ static int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc, unsigned long *pax) { + void __user *buf; + unsigned int tmpflags; unsigned int err = 0; /* Always make any pending restarted system calls return -EINTR */ current_thread_info()->restart_block.fn = do_no_restart_syscall; -#define COPY(x) (err |= __get_user(regs->x, &sc->x)) - COPY(di); COPY(si); COPY(bp); COPY(sp); COPY(bx); COPY(dx); COPY(cx); COPY(ip); COPY(r8); @@ -143,48 +90,24 @@ restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc, /* Kernel saves and restores only the CS segment register on signals, * which is the bare minimum needed to allow mixed 32/64-bit code. * App's signal handler can save/restore other segments if needed. */ - { - unsigned cs; - err |= __get_user(cs, &sc->cs); - regs->cs = cs | 3; /* Force into user mode */ - } + COPY_SEG_STRICT(cs); - { - unsigned int tmpflags; - err |= __get_user(tmpflags, &sc->flags); - regs->flags = (regs->flags & ~FIX_EFLAGS) | (tmpflags & FIX_EFLAGS); - regs->orig_ax = -1; /* disable syscall checks */ - } + err |= __get_user(tmpflags, &sc->flags); + regs->flags = (regs->flags & ~FIX_EFLAGS) | (tmpflags & FIX_EFLAGS); + regs->orig_ax = -1; /* disable syscall checks */ - { - struct _fpstate __user *buf; - err |= __get_user(buf, &sc->fpstate); - - if (buf) { - if (!access_ok(VERIFY_READ, buf, sizeof(*buf))) - goto badframe; - err |= restore_i387(buf); - } else { - struct task_struct *me = current; - if (used_math()) { - clear_fpu(me); - clear_used_math(); - } - } - } + err |= __get_user(buf, &sc->fpstate); + err |= restore_i387_xstate(buf); err |= __get_user(*pax, &sc->ax); return err; - -badframe: - return 1; } -asmlinkage long sys_rt_sigreturn(struct pt_regs *regs) +static long do_rt_sigreturn(struct pt_regs *regs) { struct rt_sigframe __user *frame; - sigset_t set; unsigned long ax; + sigset_t set; frame = (struct rt_sigframe __user *)(regs->sp - sizeof(long)); if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) @@ -207,10 +130,15 @@ asmlinkage long sys_rt_sigreturn(struct pt_regs *regs) return ax; badframe: - signal_fault(regs, frame, "sigreturn"); + signal_fault(regs, frame, "rt_sigreturn"); return 0; } +asmlinkage long sys_rt_sigreturn(struct pt_regs *regs) +{ + return do_rt_sigreturn(regs); +} + /* * Set up a signal frame. */ @@ -269,41 +197,40 @@ get_stack(struct k_sigaction *ka, struct pt_regs *regs, unsigned long size) sp = current->sas_ss_sp + current->sas_ss_size; } - return (void __user *)round_down(sp - size, 16); + return (void __user *)round_down(sp - size, 64); } -static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, - sigset_t *set, struct pt_regs *regs) +static int __setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, + sigset_t *set, struct pt_regs *regs) { struct rt_sigframe __user *frame; - struct _fpstate __user *fp = NULL; + void __user *fp = NULL; int err = 0; struct task_struct *me = current; if (used_math()) { - fp = get_stack(ka, regs, sizeof(struct _fpstate)); + fp = get_stack(ka, regs, sig_xstate_size); frame = (void __user *)round_down( (unsigned long)fp - sizeof(struct rt_sigframe), 16) - 8; - if (!access_ok(VERIFY_WRITE, fp, sizeof(struct _fpstate))) - goto give_sigsegv; - - if (save_i387(fp) < 0) - err |= -1; + if (save_i387_xstate(fp) < 0) + return -EFAULT; } else frame = get_stack(ka, regs, sizeof(struct rt_sigframe)) - 8; if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) - goto give_sigsegv; + return -EFAULT; if (ka->sa.sa_flags & SA_SIGINFO) { - err |= copy_siginfo_to_user(&frame->info, info); - if (err) - goto give_sigsegv; + if (copy_siginfo_to_user(&frame->info, info)) + return -EFAULT; } /* Create the ucontext. */ - err |= __put_user(0, &frame->uc.uc_flags); + if (cpu_has_xsave) + err |= __put_user(UC_FP_XSTATE, &frame->uc.uc_flags); + else + err |= __put_user(0, &frame->uc.uc_flags); err |= __put_user(0, &frame->uc.uc_link); err |= __put_user(me->sas_ss_sp, &frame->uc.uc_stack.ss_sp); err |= __put_user(sas_ss_flags(regs->sp), @@ -324,11 +251,11 @@ static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, err |= __put_user(ka->sa.sa_restorer, &frame->pretcode); } else { /* could use a vstub here */ - goto give_sigsegv; + return -EFAULT; } if (err) - goto give_sigsegv; + return -EFAULT; /* Set up registers for signal handler */ regs->di = sig; @@ -348,15 +275,45 @@ static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, regs->cs = __USER_CS; return 0; - -give_sigsegv: - force_sigsegv(sig, current); - return -EFAULT; } /* * OK, we're invoking a handler */ +static int signr_convert(int sig) +{ + return sig; +} + +#ifdef CONFIG_IA32_EMULATION +#define is_ia32 test_thread_flag(TIF_IA32) +#else +#define is_ia32 0 +#endif + +static int +setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, + sigset_t *set, struct pt_regs *regs) +{ + int usig = signr_convert(sig); + int ret; + + /* Set up the stack frame */ + if (is_ia32) { + if (ka->sa.sa_flags & SA_SIGINFO) + ret = ia32_setup_rt_frame(usig, ka, info, set, regs); + else + ret = ia32_setup_frame(usig, ka, set, regs); + } else + ret = __setup_rt_frame(sig, ka, info, set, regs); + + if (ret) { + force_sigsegv(sig, current); + return -EFAULT; + } + + return ret; +} static int handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka, @@ -394,51 +351,48 @@ handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka, likely(test_and_clear_thread_flag(TIF_FORCED_TF))) regs->flags &= ~X86_EFLAGS_TF; -#ifdef CONFIG_IA32_EMULATION - if (test_thread_flag(TIF_IA32)) { - if (ka->sa.sa_flags & SA_SIGINFO) - ret = ia32_setup_rt_frame(sig, ka, info, oldset, regs); - else - ret = ia32_setup_frame(sig, ka, oldset, regs); - } else -#endif ret = setup_rt_frame(sig, ka, info, oldset, regs); - if (ret == 0) { - /* - * This has nothing to do with segment registers, - * despite the name. This magic affects uaccess.h - * macros' behavior. Reset it to the normal setting. - */ - set_fs(USER_DS); + if (ret) + return ret; - /* - * Clear the direction flag as per the ABI for function entry. - */ - regs->flags &= ~X86_EFLAGS_DF; +#ifdef CONFIG_X86_64 + /* + * This has nothing to do with segment registers, + * despite the name. This magic affects uaccess.h + * macros' behavior. Reset it to the normal setting. + */ + set_fs(USER_DS); +#endif - /* - * Clear TF when entering the signal handler, but - * notify any tracer that was single-stepping it. - * The tracer may want to single-step inside the - * handler too. - */ - regs->flags &= ~X86_EFLAGS_TF; + /* + * Clear the direction flag as per the ABI for function entry. + */ + regs->flags &= ~X86_EFLAGS_DF; - spin_lock_irq(¤t->sighand->siglock); - sigorsets(¤t->blocked, ¤t->blocked, &ka->sa.sa_mask); - if (!(ka->sa.sa_flags & SA_NODEFER)) - sigaddset(¤t->blocked, sig); - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); + /* + * Clear TF when entering the signal handler, but + * notify any tracer that was single-stepping it. + * The tracer may want to single-step inside the + * handler too. + */ + regs->flags &= ~X86_EFLAGS_TF; - tracehook_signal_handler(sig, info, ka, regs, - test_thread_flag(TIF_SINGLESTEP)); - } + spin_lock_irq(¤t->sighand->siglock); + sigorsets(¤t->blocked, ¤t->blocked, &ka->sa.sa_mask); + if (!(ka->sa.sa_flags & SA_NODEFER)) + sigaddset(¤t->blocked, sig); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); - return ret; + tracehook_signal_handler(sig, info, ka, regs, + test_thread_flag(TIF_SINGLESTEP)); + + return 0; } +#define NR_restart_syscall \ + test_thread_flag(TIF_IA32) ? __NR_ia32_restart_syscall : __NR_restart_syscall /* * Note that 'init' is a special process: it doesn't get signals it doesn't * want to handle. Thus you cannot kill init even with a SIGKILL even by @@ -468,7 +422,8 @@ static void do_signal(struct pt_regs *regs) signr = get_signal_to_deliver(&info, &ka, regs, NULL); if (signr > 0) { - /* Re-enable any watchpoints before delivering the + /* + * Re-enable any watchpoints before delivering the * signal to user space. The processor register will * have been cleared if the watchpoint triggered * inside the kernel. @@ -476,7 +431,7 @@ static void do_signal(struct pt_regs *regs) if (current->thread.debugreg7) set_debugreg(current->thread.debugreg7, 7); - /* Whee! Actually deliver the signal. */ + /* Whee! Actually deliver the signal. */ if (handle_signal(signr, &info, &ka, oldset, regs) == 0) { /* * A signal was successfully delivered; the saved @@ -499,10 +454,9 @@ static void do_signal(struct pt_regs *regs) regs->ax = regs->orig_ax; regs->ip -= 2; break; + case -ERESTART_RESTARTBLOCK: - regs->ax = test_thread_flag(TIF_IA32) ? - __NR_ia32_restart_syscall : - __NR_restart_syscall; + regs->ax = NR_restart_syscall; regs->ip -= 2; break; } @@ -518,14 +472,18 @@ static void do_signal(struct pt_regs *regs) } } -void do_notify_resume(struct pt_regs *regs, void *unused, - __u32 thread_info_flags) +/* + * notification of userspace execution resumption + * - triggered by the TIF_WORK_MASK flags + */ +void +do_notify_resume(struct pt_regs *regs, void *unused, __u32 thread_info_flags) { -#ifdef CONFIG_X86_MCE +#if defined(CONFIG_X86_64) && defined(CONFIG_X86_MCE) /* notify userspace of pending MCEs */ if (thread_info_flags & _TIF_MCE_NOTIFY) mce_notify_user(); -#endif /* CONFIG_X86_MCE */ +#endif /* CONFIG_X86_64 && CONFIG_X86_MCE */ /* deal with pending signal delivery */ if (thread_info_flags & _TIF_SIGPENDING) @@ -535,17 +493,23 @@ void do_notify_resume(struct pt_regs *regs, void *unused, clear_thread_flag(TIF_NOTIFY_RESUME); tracehook_notify_resume(regs); } + +#ifdef CONFIG_X86_32 + clear_thread_flag(TIF_IRET); +#endif /* CONFIG_X86_32 */ } void signal_fault(struct pt_regs *regs, void __user *frame, char *where) { struct task_struct *me = current; + if (show_unhandled_signals && printk_ratelimit()) { - printk("%s[%d] bad frame in %s frame:%p ip:%lx sp:%lx orax:%lx", - me->comm, me->pid, where, frame, regs->ip, - regs->sp, regs->orig_ax); + printk(KERN_INFO + "%s[%d] bad frame in %s frame:%p ip:%lx sp:%lx orax:%lx", + me->comm, me->pid, where, frame, + regs->ip, regs->sp, regs->orig_ax); print_vma_addr(" in ", regs->ip); - printk("\n"); + printk(KERN_CONT "\n"); } force_sig(SIGSEGV, me); diff --git a/arch/x86/kernel/smp.c b/arch/x86/kernel/smp.c index 361b7a4c640..18f9b19f5f8 100644 --- a/arch/x86/kernel/smp.c +++ b/arch/x86/kernel/smp.c @@ -214,12 +214,16 @@ void smp_call_function_single_interrupt(struct pt_regs *regs) struct smp_ops smp_ops = { .smp_prepare_boot_cpu = native_smp_prepare_boot_cpu, .smp_prepare_cpus = native_smp_prepare_cpus, - .cpu_up = native_cpu_up, .smp_cpus_done = native_smp_cpus_done, .smp_send_stop = native_smp_send_stop, .smp_send_reschedule = native_smp_send_reschedule, + .cpu_up = native_cpu_up, + .cpu_die = native_cpu_die, + .cpu_disable = native_cpu_disable, + .play_dead = native_play_dead, + .send_call_func_ipi = native_send_call_func_ipi, .send_call_func_single_ipi = native_send_call_func_single_ipi, }; diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c index 4e7ccb0e2a9..8c3aca7cb34 100644 --- a/arch/x86/kernel/smpboot.c +++ b/arch/x86/kernel/smpboot.c @@ -52,6 +52,7 @@ #include <asm/desc.h> #include <asm/nmi.h> #include <asm/irq.h> +#include <asm/idle.h> #include <asm/smp.h> #include <asm/trampoline.h> #include <asm/cpu.h> @@ -123,7 +124,6 @@ EXPORT_PER_CPU_SYMBOL(cpu_info); static atomic_t init_deasserted; -static int boot_cpu_logical_apicid; /* representing cpus for which sibling maps can be computed */ static cpumask_t cpu_sibling_setup_map; @@ -165,6 +165,8 @@ static void unmap_cpu_to_node(int cpu) #endif #ifdef CONFIG_X86_32 +static int boot_cpu_logical_apicid; + u8 cpu_2_logical_apicid[NR_CPUS] __read_mostly = { [0 ... NR_CPUS-1] = BAD_APICID }; @@ -210,7 +212,7 @@ static void __cpuinit smp_callin(void) /* * (This works even if the APIC is not enabled.) */ - phys_id = GET_APIC_ID(read_apic_id()); + phys_id = read_apic_id(); cpuid = smp_processor_id(); if (cpu_isset(cpuid, cpu_callin_map)) { panic("%s: phys CPU#%d, CPU#%d already present??\n", __func__, @@ -332,14 +334,17 @@ static void __cpuinit start_secondary(void *unused) * does not change while we are assigning vectors to cpus. Holding * this lock ensures we don't half assign or remove an irq from a cpu. */ - ipi_call_lock_irq(); + ipi_call_lock(); lock_vector_lock(); __setup_vector_irq(smp_processor_id()); cpu_set(smp_processor_id(), cpu_online_map); unlock_vector_lock(); - ipi_call_unlock_irq(); + ipi_call_unlock(); per_cpu(cpu_state, smp_processor_id()) = CPU_ONLINE; + /* enable local interrupts */ + local_irq_enable(); + setup_secondary_clock(); wmb(); @@ -551,8 +556,7 @@ static inline void __inquire_remote_apic(int apicid) printk(KERN_CONT "a previous APIC delivery may have failed\n"); - apic_write(APIC_ICR2, SET_APIC_DEST_FIELD(apicid)); - apic_write(APIC_ICR, APIC_DM_REMRD | regs[i]); + apic_icr_write(APIC_DM_REMRD | regs[i], apicid); timeout = 0; do { @@ -584,11 +588,9 @@ wakeup_secondary_cpu(int logical_apicid, unsigned long start_eip) int maxlvt; /* Target chip */ - apic_write(APIC_ICR2, SET_APIC_DEST_FIELD(logical_apicid)); - /* Boot on the stack */ /* Kick the second */ - apic_write(APIC_ICR, APIC_DM_NMI | APIC_DEST_LOGICAL); + apic_icr_write(APIC_DM_NMI | APIC_DEST_LOGICAL, logical_apicid); pr_debug("Waiting for send to finish...\n"); send_status = safe_apic_wait_icr_idle(); @@ -597,10 +599,12 @@ wakeup_secondary_cpu(int logical_apicid, unsigned long start_eip) * Give the other CPU some time to accept the IPI. */ udelay(200); - maxlvt = lapic_get_maxlvt(); - if (maxlvt > 3) /* Due to the Pentium erratum 3AP. */ - apic_write(APIC_ESR, 0); - accept_status = (apic_read(APIC_ESR) & 0xEF); + if (APIC_INTEGRATED(apic_version[phys_apicid])) { + maxlvt = lapic_get_maxlvt(); + if (maxlvt > 3) /* Due to the Pentium erratum 3AP. */ + apic_write(APIC_ESR, 0); + accept_status = (apic_read(APIC_ESR) & 0xEF); + } pr_debug("NMI sent.\n"); if (send_status) @@ -641,13 +645,11 @@ wakeup_secondary_cpu(int phys_apicid, unsigned long start_eip) /* * Turn INIT on target chip */ - apic_write(APIC_ICR2, SET_APIC_DEST_FIELD(phys_apicid)); - /* * Send IPI */ - apic_write(APIC_ICR, - APIC_INT_LEVELTRIG | APIC_INT_ASSERT | APIC_DM_INIT); + apic_icr_write(APIC_INT_LEVELTRIG | APIC_INT_ASSERT | APIC_DM_INIT, + phys_apicid); pr_debug("Waiting for send to finish...\n"); send_status = safe_apic_wait_icr_idle(); @@ -657,10 +659,8 @@ wakeup_secondary_cpu(int phys_apicid, unsigned long start_eip) pr_debug("Deasserting INIT.\n"); /* Target chip */ - apic_write(APIC_ICR2, SET_APIC_DEST_FIELD(phys_apicid)); - /* Send IPI */ - apic_write(APIC_ICR, APIC_INT_LEVELTRIG | APIC_DM_INIT); + apic_icr_write(APIC_INT_LEVELTRIG | APIC_DM_INIT, phys_apicid); pr_debug("Waiting for send to finish...\n"); send_status = safe_apic_wait_icr_idle(); @@ -703,11 +703,10 @@ wakeup_secondary_cpu(int phys_apicid, unsigned long start_eip) */ /* Target chip */ - apic_write(APIC_ICR2, SET_APIC_DEST_FIELD(phys_apicid)); - /* Boot on the stack */ /* Kick the second */ - apic_write(APIC_ICR, APIC_DM_STARTUP | (start_eip >> 12)); + apic_icr_write(APIC_DM_STARTUP | (start_eip >> 12), + phys_apicid); /* * Give the other CPU some time to accept the IPI. @@ -1176,10 +1175,17 @@ void __init native_smp_prepare_cpus(unsigned int max_cpus) * Setup boot CPU information */ smp_store_cpu_info(0); /* Final full version of the data */ +#ifdef CONFIG_X86_32 boot_cpu_logical_apicid = logical_smp_processor_id(); +#endif current_thread_info()->cpu = 0; /* needed? */ set_cpu_sibling_map(0); +#ifdef CONFIG_X86_64 + enable_IR_x2apic(); + setup_apic_routing(); +#endif + if (smp_sanity_check(max_cpus) < 0) { printk(KERN_INFO "SMP disabled\n"); disable_smp(); @@ -1187,9 +1193,9 @@ void __init native_smp_prepare_cpus(unsigned int max_cpus) } preempt_disable(); - if (GET_APIC_ID(read_apic_id()) != boot_cpu_physical_apicid) { + if (read_apic_id() != boot_cpu_physical_apicid) { panic("Boot APIC ID in local APIC unexpected (%d vs %d)", - GET_APIC_ID(read_apic_id()), boot_cpu_physical_apicid); + read_apic_id(), boot_cpu_physical_apicid); /* Or can we switch back to PIC here? */ } preempt_enable(); @@ -1255,39 +1261,6 @@ void __init native_smp_cpus_done(unsigned int max_cpus) check_nmi_watchdog(); } -#ifdef CONFIG_HOTPLUG_CPU - -static void remove_siblinginfo(int cpu) -{ - int sibling; - struct cpuinfo_x86 *c = &cpu_data(cpu); - - for_each_cpu_mask_nr(sibling, per_cpu(cpu_core_map, cpu)) { - cpu_clear(cpu, per_cpu(cpu_core_map, sibling)); - /*/ - * last thread sibling in this cpu core going down - */ - if (cpus_weight(per_cpu(cpu_sibling_map, cpu)) == 1) - cpu_data(sibling).booted_cores--; - } - - for_each_cpu_mask_nr(sibling, per_cpu(cpu_sibling_map, cpu)) - cpu_clear(cpu, per_cpu(cpu_sibling_map, sibling)); - cpus_clear(per_cpu(cpu_sibling_map, cpu)); - cpus_clear(per_cpu(cpu_core_map, cpu)); - c->phys_proc_id = 0; - c->cpu_core_id = 0; - cpu_clear(cpu, cpu_sibling_setup_map); -} - -static int additional_cpus __initdata = -1; - -static __init int setup_additional_cpus(char *s) -{ - return s && get_option(&s, &additional_cpus) ? 0 : -EINVAL; -} -early_param("additional_cpus", setup_additional_cpus); - /* * cpu_possible_map should be static, it cannot change as cpu's * are onlined, or offlined. The reason is per-cpu data-structures @@ -1307,21 +1280,13 @@ early_param("additional_cpus", setup_additional_cpus); */ __init void prefill_possible_map(void) { - int i; - int possible; + int i, possible; /* no processor from mptable or madt */ if (!num_processors) num_processors = 1; - if (additional_cpus == -1) { - if (disabled_cpus > 0) - additional_cpus = disabled_cpus; - else - additional_cpus = 0; - } - - possible = num_processors + additional_cpus; + possible = num_processors + disabled_cpus; if (possible > NR_CPUS) possible = NR_CPUS; @@ -1334,6 +1299,31 @@ __init void prefill_possible_map(void) nr_cpu_ids = possible; } +#ifdef CONFIG_HOTPLUG_CPU + +static void remove_siblinginfo(int cpu) +{ + int sibling; + struct cpuinfo_x86 *c = &cpu_data(cpu); + + for_each_cpu_mask_nr(sibling, per_cpu(cpu_core_map, cpu)) { + cpu_clear(cpu, per_cpu(cpu_core_map, sibling)); + /*/ + * last thread sibling in this cpu core going down + */ + if (cpus_weight(per_cpu(cpu_sibling_map, cpu)) == 1) + cpu_data(sibling).booted_cores--; + } + + for_each_cpu_mask_nr(sibling, per_cpu(cpu_sibling_map, cpu)) + cpu_clear(cpu, per_cpu(cpu_sibling_map, sibling)); + cpus_clear(per_cpu(cpu_sibling_map, cpu)); + cpus_clear(per_cpu(cpu_core_map, cpu)); + c->phys_proc_id = 0; + c->cpu_core_id = 0; + cpu_clear(cpu, cpu_sibling_setup_map); +} + static void __ref remove_cpu_from_maps(int cpu) { cpu_clear(cpu, cpu_online_map); @@ -1344,25 +1334,9 @@ static void __ref remove_cpu_from_maps(int cpu) numa_remove_cpu(cpu); } -int __cpu_disable(void) +void cpu_disable_common(void) { int cpu = smp_processor_id(); - - /* - * Perhaps use cpufreq to drop frequency, but that could go - * into generic code. - * - * We won't take down the boot processor on i386 due to some - * interrupts only being able to be serviced by the BSP. - * Especially so if we're not using an IOAPIC -zwane - */ - if (cpu == 0) - return -EBUSY; - - if (nmi_watchdog == NMI_LOCAL_APIC) - stop_apic_nmi_watchdog(NULL); - clear_local_APIC(); - /* * HACK: * Allow any queued timer interrupts to get serviced @@ -1380,10 +1354,32 @@ int __cpu_disable(void) remove_cpu_from_maps(cpu); unlock_vector_lock(); fixup_irqs(cpu_online_map); +} + +int native_cpu_disable(void) +{ + int cpu = smp_processor_id(); + + /* + * Perhaps use cpufreq to drop frequency, but that could go + * into generic code. + * + * We won't take down the boot processor on i386 due to some + * interrupts only being able to be serviced by the BSP. + * Especially so if we're not using an IOAPIC -zwane + */ + if (cpu == 0) + return -EBUSY; + + if (nmi_watchdog == NMI_LOCAL_APIC) + stop_apic_nmi_watchdog(NULL); + clear_local_APIC(); + + cpu_disable_common(); return 0; } -void __cpu_die(unsigned int cpu) +void native_cpu_die(unsigned int cpu) { /* We don't do anything here: idle task is faking death itself. */ unsigned int i; @@ -1400,15 +1396,45 @@ void __cpu_die(unsigned int cpu) } printk(KERN_ERR "CPU %u didn't die...\n", cpu); } + +void play_dead_common(void) +{ + idle_task_exit(); + reset_lazy_tlbstate(); + irq_ctx_exit(raw_smp_processor_id()); + c1e_remove_cpu(raw_smp_processor_id()); + + mb(); + /* Ack it */ + __get_cpu_var(cpu_state) = CPU_DEAD; + + /* + * With physical CPU hotplug, we should halt the cpu + */ + local_irq_disable(); +} + +void native_play_dead(void) +{ + play_dead_common(); + wbinvd_halt(); +} + #else /* ... !CONFIG_HOTPLUG_CPU */ -int __cpu_disable(void) +int native_cpu_disable(void) { return -ENOSYS; } -void __cpu_die(unsigned int cpu) +void native_cpu_die(unsigned int cpu) { /* We said "no" in __cpu_disable */ BUG(); } + +void native_play_dead(void) +{ + BUG(); +} + #endif diff --git a/arch/x86/kernel/summit_32.c b/arch/x86/kernel/summit_32.c index d67ce5f044b..7b987852e87 100644 --- a/arch/x86/kernel/summit_32.c +++ b/arch/x86/kernel/summit_32.c @@ -30,7 +30,7 @@ #include <linux/init.h> #include <asm/io.h> #include <asm/bios_ebda.h> -#include <asm/mach-summit/mach_mpparse.h> +#include <asm/summit/mpparse.h> static struct rio_table_hdr *rio_table_hdr __initdata; static struct scal_detail *scal_devs[MAX_NUMNODES] __initdata; diff --git a/arch/x86/kernel/time_32.c b/arch/x86/kernel/time_32.c index bbecf8b6bf9..77b400f06ea 100644 --- a/arch/x86/kernel/time_32.c +++ b/arch/x86/kernel/time_32.c @@ -47,10 +47,9 @@ unsigned long profile_pc(struct pt_regs *regs) unsigned long pc = instruction_pointer(regs); #ifdef CONFIG_SMP - if (!v8086_mode(regs) && SEGMENT_IS_KERNEL_CODE(regs->cs) && - in_lock_functions(pc)) { + if (!user_mode_vm(regs) && in_lock_functions(pc)) { #ifdef CONFIG_FRAME_POINTER - return *(unsigned long *)(regs->bp + 4); + return *(unsigned long *)(regs->bp + sizeof(long)); #else unsigned long *sp = (unsigned long *)®s->sp; @@ -95,6 +94,7 @@ irqreturn_t timer_interrupt(int irq, void *dev_id) do_timer_interrupt_hook(); +#ifdef CONFIG_MCA if (MCA_bus) { /* The PS/2 uses level-triggered interrupts. You can't turn them off, nor would you want to (any attempt to @@ -108,6 +108,7 @@ irqreturn_t timer_interrupt(int irq, void *dev_id) u8 irq_v = inb_p( 0x61 ); /* read the current state */ outb_p( irq_v|0x80, 0x61 ); /* reset the IRQ */ } +#endif return IRQ_HANDLED; } diff --git a/arch/x86/kernel/time_64.c b/arch/x86/kernel/time_64.c index e3d49c553af..cb19d650c21 100644 --- a/arch/x86/kernel/time_64.c +++ b/arch/x86/kernel/time_64.c @@ -16,6 +16,7 @@ #include <linux/interrupt.h> #include <linux/module.h> #include <linux/time.h> +#include <linux/mca.h> #include <asm/i8253.h> #include <asm/hpet.h> @@ -33,23 +34,34 @@ unsigned long profile_pc(struct pt_regs *regs) /* Assume the lock function has either no stack frame or a copy of flags from PUSHF Eflags always has bits 22 and up cleared unlike kernel addresses. */ - if (!user_mode(regs) && in_lock_functions(pc)) { + if (!user_mode_vm(regs) && in_lock_functions(pc)) { +#ifdef CONFIG_FRAME_POINTER + return *(unsigned long *)(regs->bp + sizeof(long)); +#else unsigned long *sp = (unsigned long *)regs->sp; if (sp[0] >> 22) return sp[0]; if (sp[1] >> 22) return sp[1]; +#endif } return pc; } EXPORT_SYMBOL(profile_pc); -static irqreturn_t timer_event_interrupt(int irq, void *dev_id) +irqreturn_t timer_interrupt(int irq, void *dev_id) { add_pda(irq0_irqs, 1); global_clock_event->event_handler(global_clock_event); +#ifdef CONFIG_MCA + if (MCA_bus) { + u8 irq_v = inb_p(0x61); /* read the current state */ + outb_p(irq_v|0x80, 0x61); /* reset the IRQ */ + } +#endif + return IRQ_HANDLED; } @@ -100,7 +112,7 @@ unsigned long __init calibrate_cpu(void) } static struct irqaction irq0 = { - .handler = timer_event_interrupt, + .handler = timer_interrupt, .flags = IRQF_DISABLED | IRQF_IRQPOLL | IRQF_NOBALANCING, .mask = CPU_MASK_NONE, .name = "timer" @@ -111,16 +123,13 @@ void __init hpet_time_init(void) if (!hpet_enable()) setup_pit_timer(); + irq0.mask = cpumask_of_cpu(0); setup_irq(0, &irq0); } void __init time_init(void) { tsc_init(); - if (cpu_has(&boot_cpu_data, X86_FEATURE_RDTSCP)) - vgetcpu_mode = VGETCPU_RDTSCP; - else - vgetcpu_mode = VGETCPU_LSL; late_time_init = choose_time_init(); } diff --git a/arch/x86/kernel/tlb_32.c b/arch/x86/kernel/tlb_32.c index fec1ecedc9b..e00534b3353 100644 --- a/arch/x86/kernel/tlb_32.c +++ b/arch/x86/kernel/tlb_32.c @@ -241,3 +241,11 @@ void flush_tlb_all(void) on_each_cpu(do_flush_tlb_all, NULL, 1); } +void reset_lazy_tlbstate(void) +{ + int cpu = raw_smp_processor_id(); + + per_cpu(cpu_tlbstate, cpu).state = 0; + per_cpu(cpu_tlbstate, cpu).active_mm = &init_mm; +} + diff --git a/arch/x86/kernel/traps_32.c b/arch/x86/kernel/traps.c index 03df8e45e5a..e062974cce3 100644 --- a/arch/x86/kernel/traps_32.c +++ b/arch/x86/kernel/traps.c @@ -7,13 +7,11 @@ */ /* - * 'Traps.c' handles hardware traps and faults after we have saved some - * state in 'asm.s'. + * Handle hardware traps and faults. */ #include <linux/interrupt.h> #include <linux/kallsyms.h> #include <linux/spinlock.h> -#include <linux/highmem.h> #include <linux/kprobes.h> #include <linux/uaccess.h> #include <linux/utsname.h> @@ -32,6 +30,8 @@ #include <linux/bug.h> #include <linux/nmi.h> #include <linux/mm.h> +#include <linux/smp.h> +#include <linux/io.h> #ifdef CONFIG_EISA #include <linux/ioport.h> @@ -46,21 +46,31 @@ #include <linux/edac.h> #endif -#include <asm/arch_hooks.h> #include <asm/stacktrace.h> #include <asm/processor.h> #include <asm/debugreg.h> #include <asm/atomic.h> #include <asm/system.h> #include <asm/unwind.h> +#include <asm/traps.h> #include <asm/desc.h> #include <asm/i387.h> + +#include <mach_traps.h> + +#ifdef CONFIG_X86_64 +#include <asm/pgalloc.h> +#include <asm/proto.h> +#include <asm/pda.h> +#else +#include <asm/processor-flags.h> +#include <asm/arch_hooks.h> #include <asm/nmi.h> #include <asm/smp.h> #include <asm/io.h> #include <asm/traps.h> -#include "mach_traps.h" +#include "cpu/mcheck/mce.h" DECLARE_BITMAP(used_vectors, NR_VECTORS); EXPORT_SYMBOL_GPL(used_vectors); @@ -77,418 +87,104 @@ char ignore_fpu_irq; */ gate_desc idt_table[256] __attribute__((__section__(".data.idt"))) = { { { { 0, 0 } } }, }; - -int panic_on_unrecovered_nmi; -int kstack_depth_to_print = 24; -static unsigned int code_bytes = 64; -static int ignore_nmis; -static int die_counter; - -void printk_address(unsigned long address, int reliable) -{ -#ifdef CONFIG_KALLSYMS - unsigned long offset = 0; - unsigned long symsize; - const char *symname; - char *modname; - char *delim = ":"; - char namebuf[KSYM_NAME_LEN]; - char reliab[4] = ""; - - symname = kallsyms_lookup(address, &symsize, &offset, - &modname, namebuf); - if (!symname) { - printk(" [<%08lx>]\n", address); - return; - } - if (!reliable) - strcpy(reliab, "? "); - - if (!modname) - modname = delim = ""; - printk(" [<%08lx>] %s%s%s%s%s+0x%lx/0x%lx\n", - address, reliab, delim, modname, delim, symname, offset, symsize); -#else - printk(" [<%08lx>]\n", address); #endif -} - -static inline int valid_stack_ptr(struct thread_info *tinfo, - void *p, unsigned int size) -{ - void *t = tinfo; - return p > t && p <= t + THREAD_SIZE - size; -} - -/* The form of the top of the frame on the stack */ -struct stack_frame { - struct stack_frame *next_frame; - unsigned long return_address; -}; - -static inline unsigned long -print_context_stack(struct thread_info *tinfo, - unsigned long *stack, unsigned long bp, - const struct stacktrace_ops *ops, void *data) -{ - struct stack_frame *frame = (struct stack_frame *)bp; - - while (valid_stack_ptr(tinfo, stack, sizeof(*stack))) { - unsigned long addr; - - addr = *stack; - if (__kernel_text_address(addr)) { - if ((unsigned long) stack == bp + 4) { - ops->address(data, addr, 1); - frame = frame->next_frame; - bp = (unsigned long) frame; - } else { - ops->address(data, addr, bp == 0); - } - } - stack++; - } - return bp; -} - -void dump_trace(struct task_struct *task, struct pt_regs *regs, - unsigned long *stack, unsigned long bp, - const struct stacktrace_ops *ops, void *data) -{ - if (!task) - task = current; - - if (!stack) { - unsigned long dummy; - stack = &dummy; - if (task != current) - stack = (unsigned long *)task->thread.sp; - } - -#ifdef CONFIG_FRAME_POINTER - if (!bp) { - if (task == current) { - /* Grab bp right from our regs */ - asm("movl %%ebp, %0" : "=r" (bp) :); - } else { - /* bp is the last reg pushed by switch_to */ - bp = *(unsigned long *) task->thread.sp; - } - } -#endif - - for (;;) { - struct thread_info *context; - - context = (struct thread_info *) - ((unsigned long)stack & (~(THREAD_SIZE - 1))); - bp = print_context_stack(context, stack, bp, ops, data); - /* - * Should be after the line below, but somewhere - * in early boot context comes out corrupted and we - * can't reference it: - */ - if (ops->stack(data, "IRQ") < 0) - break; - stack = (unsigned long *)context->previous_esp; - if (!stack) - break; - touch_nmi_watchdog(); - } -} -EXPORT_SYMBOL(dump_trace); - -static void -print_trace_warning_symbol(void *data, char *msg, unsigned long symbol) -{ - printk(data); - print_symbol(msg, symbol); - printk("\n"); -} - -static void print_trace_warning(void *data, char *msg) -{ - printk("%s%s\n", (char *)data, msg); -} -static int print_trace_stack(void *data, char *name) -{ - return 0; -} - -/* - * Print one address/symbol entries per line. - */ -static void print_trace_address(void *data, unsigned long addr, int reliable) -{ - printk("%s [<%08lx>] ", (char *)data, addr); - if (!reliable) - printk("? "); - print_symbol("%s\n", addr); - touch_nmi_watchdog(); -} - -static const struct stacktrace_ops print_trace_ops = { - .warning = print_trace_warning, - .warning_symbol = print_trace_warning_symbol, - .stack = print_trace_stack, - .address = print_trace_address, -}; +static int ignore_nmis; -static void -show_trace_log_lvl(struct task_struct *task, struct pt_regs *regs, - unsigned long *stack, unsigned long bp, char *log_lvl) +static inline void conditional_sti(struct pt_regs *regs) { - dump_trace(task, regs, stack, bp, &print_trace_ops, log_lvl); - printk("%s =======================\n", log_lvl); + if (regs->flags & X86_EFLAGS_IF) + local_irq_enable(); } -void show_trace(struct task_struct *task, struct pt_regs *regs, - unsigned long *stack, unsigned long bp) +static inline void preempt_conditional_sti(struct pt_regs *regs) { - show_trace_log_lvl(task, regs, stack, bp, ""); + inc_preempt_count(); + if (regs->flags & X86_EFLAGS_IF) + local_irq_enable(); } -static void -show_stack_log_lvl(struct task_struct *task, struct pt_regs *regs, - unsigned long *sp, unsigned long bp, char *log_lvl) +static inline void preempt_conditional_cli(struct pt_regs *regs) { - unsigned long *stack; - int i; - - if (sp == NULL) { - if (task) - sp = (unsigned long *)task->thread.sp; - else - sp = (unsigned long *)&sp; - } - - stack = sp; - for (i = 0; i < kstack_depth_to_print; i++) { - if (kstack_end(stack)) - break; - if (i && ((i % 8) == 0)) - printk("\n%s ", log_lvl); - printk("%08lx ", *stack++); - } - printk("\n%sCall Trace:\n", log_lvl); - - show_trace_log_lvl(task, regs, sp, bp, log_lvl); + if (regs->flags & X86_EFLAGS_IF) + local_irq_disable(); + dec_preempt_count(); } -void show_stack(struct task_struct *task, unsigned long *sp) +#ifdef CONFIG_X86_32 +static inline void +die_if_kernel(const char *str, struct pt_regs *regs, long err) { - printk(" "); - show_stack_log_lvl(task, NULL, sp, 0, ""); + if (!user_mode_vm(regs)) + die(str, regs, err); } /* - * The architecture-independent dump_stack generator + * Perform the lazy TSS's I/O bitmap copy. If the TSS has an + * invalid offset set (the LAZY one) and the faulting thread has + * a valid I/O bitmap pointer, we copy the I/O bitmap in the TSS, + * we set the offset field correctly and return 1. */ -void dump_stack(void) +static int lazy_iobitmap_copy(void) { - unsigned long bp = 0; - unsigned long stack; - -#ifdef CONFIG_FRAME_POINTER - if (!bp) - asm("movl %%ebp, %0" : "=r" (bp):); -#endif - - printk("Pid: %d, comm: %.20s %s %s %.*s\n", - current->pid, current->comm, print_tainted(), - init_utsname()->release, - (int)strcspn(init_utsname()->version, " "), - init_utsname()->version); - - show_trace(current, NULL, &stack, bp); -} - -EXPORT_SYMBOL(dump_stack); - -void show_registers(struct pt_regs *regs) -{ - int i; + struct thread_struct *thread; + struct tss_struct *tss; + int cpu; - print_modules(); - __show_registers(regs, 0); + cpu = get_cpu(); + tss = &per_cpu(init_tss, cpu); + thread = ¤t->thread; - printk(KERN_EMERG "Process %.*s (pid: %d, ti=%p task=%p task.ti=%p)", - TASK_COMM_LEN, current->comm, task_pid_nr(current), - current_thread_info(), current, task_thread_info(current)); - /* - * When in-kernel, we also print out the stack and code at the - * time of the fault.. - */ - if (!user_mode_vm(regs)) { - unsigned int code_prologue = code_bytes * 43 / 64; - unsigned int code_len = code_bytes; - unsigned char c; - u8 *ip; - - printk("\n" KERN_EMERG "Stack: "); - show_stack_log_lvl(NULL, regs, ®s->sp, 0, KERN_EMERG); - - printk(KERN_EMERG "Code: "); - - ip = (u8 *)regs->ip - code_prologue; - if (ip < (u8 *)PAGE_OFFSET || probe_kernel_address(ip, c)) { - /* try starting at EIP */ - ip = (u8 *)regs->ip; - code_len = code_len - code_prologue + 1; - } - for (i = 0; i < code_len; i++, ip++) { - if (ip < (u8 *)PAGE_OFFSET || - probe_kernel_address(ip, c)) { - printk(" Bad EIP value."); - break; - } - if (ip == (u8 *)regs->ip) - printk("<%02x> ", c); - else - printk("%02x ", c); + if (tss->x86_tss.io_bitmap_base == INVALID_IO_BITMAP_OFFSET_LAZY && + thread->io_bitmap_ptr) { + memcpy(tss->io_bitmap, thread->io_bitmap_ptr, + thread->io_bitmap_max); + /* + * If the previously set map was extending to higher ports + * than the current one, pad extra space with 0xff (no access). + */ + if (thread->io_bitmap_max < tss->io_bitmap_max) { + memset((char *) tss->io_bitmap + + thread->io_bitmap_max, 0xff, + tss->io_bitmap_max - thread->io_bitmap_max); } - } - printk("\n"); -} - -int is_valid_bugaddr(unsigned long ip) -{ - unsigned short ud2; - - if (ip < PAGE_OFFSET) - return 0; - if (probe_kernel_address((unsigned short *)ip, ud2)) - return 0; - - return ud2 == 0x0b0f; -} - -static raw_spinlock_t die_lock = __RAW_SPIN_LOCK_UNLOCKED; -static int die_owner = -1; -static unsigned int die_nest_count; - -unsigned __kprobes long oops_begin(void) -{ - unsigned long flags; - - oops_enter(); - - if (die_owner != raw_smp_processor_id()) { - console_verbose(); - raw_local_irq_save(flags); - __raw_spin_lock(&die_lock); - die_owner = smp_processor_id(); - die_nest_count = 0; - bust_spinlocks(1); - } else { - raw_local_irq_save(flags); - } - die_nest_count++; - return flags; -} - -void __kprobes oops_end(unsigned long flags, struct pt_regs *regs, int signr) -{ - bust_spinlocks(0); - die_owner = -1; - add_taint(TAINT_DIE); - __raw_spin_unlock(&die_lock); - raw_local_irq_restore(flags); - - if (!regs) - return; - - if (kexec_should_crash(current)) - crash_kexec(regs); - - if (in_interrupt()) - panic("Fatal exception in interrupt"); - - if (panic_on_oops) - panic("Fatal exception"); - - oops_exit(); - do_exit(signr); -} - -int __kprobes __die(const char *str, struct pt_regs *regs, long err) -{ - unsigned short ss; - unsigned long sp; + tss->io_bitmap_max = thread->io_bitmap_max; + tss->x86_tss.io_bitmap_base = IO_BITMAP_OFFSET; + tss->io_bitmap_owner = thread; + put_cpu(); - printk(KERN_EMERG "%s: %04lx [#%d] ", str, err & 0xffff, ++die_counter); -#ifdef CONFIG_PREEMPT - printk("PREEMPT "); -#endif -#ifdef CONFIG_SMP - printk("SMP "); -#endif -#ifdef CONFIG_DEBUG_PAGEALLOC - printk("DEBUG_PAGEALLOC"); -#endif - printk("\n"); - if (notify_die(DIE_OOPS, str, regs, err, - current->thread.trap_no, SIGSEGV) == NOTIFY_STOP) return 1; - - show_registers(regs); - /* Executive summary in case the oops scrolled away */ - sp = (unsigned long) (®s->sp); - savesegment(ss, ss); - if (user_mode(regs)) { - sp = regs->sp; - ss = regs->ss & 0xffff; } - printk(KERN_EMERG "EIP: [<%08lx>] ", regs->ip); - print_symbol("%s", regs->ip); - printk(" SS:ESP %04x:%08lx\n", ss, sp); - return 0; -} - -/* - * This is gone through when something in the kernel has done something bad - * and is about to be terminated: - */ -void die(const char *str, struct pt_regs *regs, long err) -{ - unsigned long flags = oops_begin(); - - if (die_nest_count < 3) { - report_bug(regs->ip, regs); - - if (__die(str, regs, err)) - regs = NULL; - } else { - printk(KERN_EMERG "Recursive die() failure, output suppressed\n"); - } - - oops_end(flags, regs, SIGSEGV); -} + put_cpu(); -static inline void -die_if_kernel(const char *str, struct pt_regs *regs, long err) -{ - if (!user_mode_vm(regs)) - die(str, regs, err); + return 0; } +#endif static void __kprobes -do_trap(int trapnr, int signr, char *str, int vm86, struct pt_regs *regs, +do_trap(int trapnr, int signr, char *str, struct pt_regs *regs, long error_code, siginfo_t *info) { struct task_struct *tsk = current; +#ifdef CONFIG_X86_32 if (regs->flags & X86_VM_MASK) { - if (vm86) + /* + * traps 0, 1, 3, 4, and 5 should be forwarded to vm86. + * On nmi (interrupt 2), do_trap should not be called. + */ + if (trapnr < 6) goto vm86_trap; goto trap_signal; } +#endif if (!user_mode(regs)) goto kernel_trap; +#ifdef CONFIG_X86_32 trap_signal: +#endif /* * We want error_code and trap_no set for userspace faults and * kernelspace faults which result in die(), but not @@ -501,6 +197,18 @@ trap_signal: tsk->thread.error_code = error_code; tsk->thread.trap_no = trapnr; +#ifdef CONFIG_X86_64 + if (show_unhandled_signals && unhandled_signal(tsk, signr) && + printk_ratelimit()) { + printk(KERN_INFO + "%s[%d] trap %s ip:%lx sp:%lx error:%lx", + tsk->comm, tsk->pid, str, + regs->ip, regs->sp, error_code); + print_vma_addr(" in ", regs->ip); + printk("\n"); + } +#endif + if (info) force_sig_info(signr, info, tsk); else @@ -515,29 +223,29 @@ kernel_trap: } return; +#ifdef CONFIG_X86_32 vm86_trap: if (handle_vm86_trap((struct kernel_vm86_regs *) regs, error_code, trapnr)) goto trap_signal; return; +#endif } #define DO_ERROR(trapnr, signr, str, name) \ -void do_##name(struct pt_regs *regs, long error_code) \ +dotraplinkage void do_##name(struct pt_regs *regs, long error_code) \ { \ - trace_hardirqs_fixup(); \ if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr) \ == NOTIFY_STOP) \ return; \ - do_trap(trapnr, signr, str, 0, regs, error_code, NULL); \ + conditional_sti(regs); \ + do_trap(trapnr, signr, str, regs, error_code, NULL); \ } -#define DO_ERROR_INFO(trapnr, signr, str, name, sicode, siaddr, irq) \ -void do_##name(struct pt_regs *regs, long error_code) \ +#define DO_ERROR_INFO(trapnr, signr, str, name, sicode, siaddr) \ +dotraplinkage void do_##name(struct pt_regs *regs, long error_code) \ { \ siginfo_t info; \ - if (irq) \ - local_irq_enable(); \ info.si_signo = signr; \ info.si_errno = 0; \ info.si_code = sicode; \ @@ -545,90 +253,68 @@ void do_##name(struct pt_regs *regs, long error_code) \ if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr) \ == NOTIFY_STOP) \ return; \ - do_trap(trapnr, signr, str, 0, regs, error_code, &info); \ + conditional_sti(regs); \ + do_trap(trapnr, signr, str, regs, error_code, &info); \ } -#define DO_VM86_ERROR(trapnr, signr, str, name) \ -void do_##name(struct pt_regs *regs, long error_code) \ -{ \ - if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr) \ - == NOTIFY_STOP) \ - return; \ - do_trap(trapnr, signr, str, 1, regs, error_code, NULL); \ -} - -#define DO_VM86_ERROR_INFO(trapnr, signr, str, name, sicode, siaddr) \ -void do_##name(struct pt_regs *regs, long error_code) \ -{ \ - siginfo_t info; \ - info.si_signo = signr; \ - info.si_errno = 0; \ - info.si_code = sicode; \ - info.si_addr = (void __user *)siaddr; \ - trace_hardirqs_fixup(); \ - if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr) \ - == NOTIFY_STOP) \ - return; \ - do_trap(trapnr, signr, str, 1, regs, error_code, &info); \ -} - -DO_VM86_ERROR_INFO(0, SIGFPE, "divide error", divide_error, FPE_INTDIV, regs->ip) -#ifndef CONFIG_KPROBES -DO_VM86_ERROR(3, SIGTRAP, "int3", int3) -#endif -DO_VM86_ERROR(4, SIGSEGV, "overflow", overflow) -DO_VM86_ERROR(5, SIGSEGV, "bounds", bounds) -DO_ERROR_INFO(6, SIGILL, "invalid opcode", invalid_op, ILL_ILLOPN, regs->ip, 0) +DO_ERROR_INFO(0, SIGFPE, "divide error", divide_error, FPE_INTDIV, regs->ip) +DO_ERROR(4, SIGSEGV, "overflow", overflow) +DO_ERROR(5, SIGSEGV, "bounds", bounds) +DO_ERROR_INFO(6, SIGILL, "invalid opcode", invalid_op, ILL_ILLOPN, regs->ip) DO_ERROR(9, SIGFPE, "coprocessor segment overrun", coprocessor_segment_overrun) DO_ERROR(10, SIGSEGV, "invalid TSS", invalid_TSS) DO_ERROR(11, SIGBUS, "segment not present", segment_not_present) +#ifdef CONFIG_X86_32 DO_ERROR(12, SIGBUS, "stack segment", stack_segment) -DO_ERROR_INFO(17, SIGBUS, "alignment check", alignment_check, BUS_ADRALN, 0, 0) -DO_ERROR_INFO(32, SIGILL, "iret exception", iret_error, ILL_BADSTK, 0, 1) +#endif +DO_ERROR_INFO(17, SIGBUS, "alignment check", alignment_check, BUS_ADRALN, 0) + +#ifdef CONFIG_X86_64 +/* Runs on IST stack */ +dotraplinkage void do_stack_segment(struct pt_regs *regs, long error_code) +{ + if (notify_die(DIE_TRAP, "stack segment", regs, error_code, + 12, SIGBUS) == NOTIFY_STOP) + return; + preempt_conditional_sti(regs); + do_trap(12, SIGBUS, "stack segment", regs, error_code, NULL); + preempt_conditional_cli(regs); +} + +dotraplinkage void do_double_fault(struct pt_regs *regs, long error_code) +{ + static const char str[] = "double fault"; + struct task_struct *tsk = current; + + /* Return not checked because double check cannot be ignored */ + notify_die(DIE_TRAP, str, regs, error_code, 8, SIGSEGV); -void __kprobes + tsk->thread.error_code = error_code; + tsk->thread.trap_no = 8; + + /* This is always a kernel trap and never fixable (and thus must + never return). */ + for (;;) + die(str, regs, error_code); +} +#endif + +dotraplinkage void __kprobes do_general_protection(struct pt_regs *regs, long error_code) { struct task_struct *tsk; - struct thread_struct *thread; - struct tss_struct *tss; - int cpu; - cpu = get_cpu(); - tss = &per_cpu(init_tss, cpu); - thread = ¤t->thread; - - /* - * Perform the lazy TSS's I/O bitmap copy. If the TSS has an - * invalid offset set (the LAZY one) and the faulting thread has - * a valid I/O bitmap pointer, we copy the I/O bitmap in the TSS - * and we set the offset field correctly. Then we let the CPU to - * restart the faulting instruction. - */ - if (tss->x86_tss.io_bitmap_base == INVALID_IO_BITMAP_OFFSET_LAZY && - thread->io_bitmap_ptr) { - memcpy(tss->io_bitmap, thread->io_bitmap_ptr, - thread->io_bitmap_max); - /* - * If the previously set map was extending to higher ports - * than the current one, pad extra space with 0xff (no access). - */ - if (thread->io_bitmap_max < tss->io_bitmap_max) { - memset((char *) tss->io_bitmap + - thread->io_bitmap_max, 0xff, - tss->io_bitmap_max - thread->io_bitmap_max); - } - tss->io_bitmap_max = thread->io_bitmap_max; - tss->x86_tss.io_bitmap_base = IO_BITMAP_OFFSET; - tss->io_bitmap_owner = thread; - put_cpu(); + conditional_sti(regs); +#ifdef CONFIG_X86_32 + if (lazy_iobitmap_copy()) { + /* restart the faulting instruction */ return; } - put_cpu(); if (regs->flags & X86_VM_MASK) goto gp_in_vm86; +#endif tsk = current; if (!user_mode(regs)) @@ -650,10 +336,12 @@ do_general_protection(struct pt_regs *regs, long error_code) force_sig(SIGSEGV, tsk); return; +#ifdef CONFIG_X86_32 gp_in_vm86: local_irq_enable(); handle_vm86_fault((struct kernel_vm86_regs *) regs, error_code); return; +#endif gp_in_kernel: if (fixup_exception(regs)) @@ -690,7 +378,8 @@ mem_parity_error(unsigned char reason, struct pt_regs *regs) printk(KERN_EMERG "Dazed and confused, but trying to continue\n"); /* Clear and disable the memory parity error line. */ - clear_mem_error(reason); + reason = (reason & 0xf) | 4; + outb(reason, 0x61); } static notrace __kprobes void @@ -716,7 +405,8 @@ io_check_error(unsigned char reason, struct pt_regs *regs) static notrace __kprobes void unknown_nmi_error(unsigned char reason, struct pt_regs *regs) { - if (notify_die(DIE_NMIUNKNOWN, "nmi", regs, reason, 2, SIGINT) == NOTIFY_STOP) + if (notify_die(DIE_NMIUNKNOWN, "nmi", regs, reason, 2, SIGINT) == + NOTIFY_STOP) return; #ifdef CONFIG_MCA /* @@ -739,41 +429,6 @@ unknown_nmi_error(unsigned char reason, struct pt_regs *regs) printk(KERN_EMERG "Dazed and confused, but trying to continue\n"); } -static DEFINE_SPINLOCK(nmi_print_lock); - -void notrace __kprobes die_nmi(char *str, struct pt_regs *regs, int do_panic) -{ - if (notify_die(DIE_NMIWATCHDOG, str, regs, 0, 2, SIGINT) == NOTIFY_STOP) - return; - - spin_lock(&nmi_print_lock); - /* - * We are in trouble anyway, lets at least try - * to get a message out: - */ - bust_spinlocks(1); - printk(KERN_EMERG "%s", str); - printk(" on CPU%d, ip %08lx, registers:\n", - smp_processor_id(), regs->ip); - show_registers(regs); - if (do_panic) - panic("Non maskable interrupt"); - console_silent(); - spin_unlock(&nmi_print_lock); - bust_spinlocks(0); - - /* - * If we are in kernel we are probably nested up pretty bad - * and might aswell get out now while we still can: - */ - if (!user_mode_vm(regs)) { - current->thread.trap_no = 2; - crash_kexec(regs); - } - - do_exit(SIGSEGV); -} - static notrace __kprobes void default_do_nmi(struct pt_regs *regs) { unsigned char reason = 0; @@ -812,22 +467,25 @@ static notrace __kprobes void default_do_nmi(struct pt_regs *regs) mem_parity_error(reason, regs); if (reason & 0x40) io_check_error(reason, regs); +#ifdef CONFIG_X86_32 /* * Reassert NMI in case it became active meanwhile * as it's edge-triggered: */ reassert_nmi(); +#endif } -notrace __kprobes void do_nmi(struct pt_regs *regs, long error_code) +dotraplinkage notrace __kprobes void +do_nmi(struct pt_regs *regs, long error_code) { - int cpu; - nmi_enter(); - cpu = smp_processor_id(); - - ++nmi_count(cpu); +#ifdef CONFIG_X86_32 + { int cpu; cpu = smp_processor_id(); ++nmi_count(cpu); } +#else + add_pda(__nmi_count, 1); +#endif if (!ignore_nmis) default_do_nmi(regs); @@ -847,21 +505,44 @@ void restart_nmi(void) acpi_nmi_enable(); } -#ifdef CONFIG_KPROBES -void __kprobes do_int3(struct pt_regs *regs, long error_code) +/* May run on IST stack. */ +dotraplinkage void __kprobes do_int3(struct pt_regs *regs, long error_code) { - trace_hardirqs_fixup(); - +#ifdef CONFIG_KPROBES if (notify_die(DIE_INT3, "int3", regs, error_code, 3, SIGTRAP) == NOTIFY_STOP) return; - /* - * This is an interrupt gate, because kprobes wants interrupts - * disabled. Normal trap handlers don't. - */ - restore_interrupts(regs); +#else + if (notify_die(DIE_TRAP, "int3", regs, error_code, 3, SIGTRAP) + == NOTIFY_STOP) + return; +#endif + + preempt_conditional_sti(regs); + do_trap(3, SIGTRAP, "int3", regs, error_code, NULL); + preempt_conditional_cli(regs); +} - do_trap(3, SIGTRAP, "int3", 1, regs, error_code, NULL); +#ifdef CONFIG_X86_64 +/* Help handler running on IST stack to switch back to user stack + for scheduling or signal handling. The actual stack switch is done in + entry.S */ +asmlinkage __kprobes struct pt_regs *sync_regs(struct pt_regs *eregs) +{ + struct pt_regs *regs = eregs; + /* Did already sync */ + if (eregs == (struct pt_regs *)eregs->sp) + ; + /* Exception from user space */ + else if (user_mode(eregs)) + regs = task_pt_regs(current); + /* Exception from kernel and interrupts are enabled. Move to + kernel process stack. */ + else if (eregs->flags & X86_EFLAGS_IF) + regs = (struct pt_regs *)(eregs->sp -= sizeof(struct pt_regs)); + if (eregs != regs) + *regs = *eregs; + return regs; } #endif @@ -886,13 +567,14 @@ void __kprobes do_int3(struct pt_regs *regs, long error_code) * about restoring all the debug state, and ptrace doesn't have to * find every occurrence of the TF bit that could be saved away even * by user code) + * + * May run on IST stack. */ -void __kprobes do_debug(struct pt_regs *regs, long error_code) +dotraplinkage void __kprobes do_debug(struct pt_regs *regs, long error_code) { struct task_struct *tsk = current; - unsigned int condition; - - trace_hardirqs_fixup(); + unsigned long condition; + int si_code; get_debugreg(condition, 6); @@ -905,9 +587,9 @@ void __kprobes do_debug(struct pt_regs *regs, long error_code) if (notify_die(DIE_DEBUG, "debug", regs, condition, error_code, SIGTRAP) == NOTIFY_STOP) return; + /* It's safe to allow irq's after DR6 has been saved */ - if (regs->flags & X86_EFLAGS_IF) - local_irq_enable(); + preempt_conditional_sti(regs); /* Mask out spurious debug traps due to lazy DR7 setting */ if (condition & (DR_TRAP0|DR_TRAP1|DR_TRAP2|DR_TRAP3)) { @@ -915,8 +597,10 @@ void __kprobes do_debug(struct pt_regs *regs, long error_code) goto clear_dr7; } +#ifdef CONFIG_X86_32 if (regs->flags & X86_VM_MASK) goto debug_vm86; +#endif /* Save debug status register where ptrace can see it */ tsk->thread.debugreg6 = condition; @@ -926,17 +610,13 @@ void __kprobes do_debug(struct pt_regs *regs, long error_code) * kernel space (but re-enable TF when returning to user mode). */ if (condition & DR_STEP) { - /* - * We already checked v86 mode above, so we can - * check for kernel mode by just checking the CPL - * of CS. - */ if (!user_mode(regs)) goto clear_TF_reenable; } + si_code = get_si_code(condition); /* Ok, finally something we can handle */ - send_sigtrap(tsk, regs, error_code); + send_sigtrap(tsk, regs, error_code, si_code); /* * Disable additional traps. They'll be re-enabled when @@ -944,18 +624,37 @@ void __kprobes do_debug(struct pt_regs *regs, long error_code) */ clear_dr7: set_debugreg(0, 7); + preempt_conditional_cli(regs); return; +#ifdef CONFIG_X86_32 debug_vm86: handle_vm86_trap((struct kernel_vm86_regs *) regs, error_code, 1); + preempt_conditional_cli(regs); return; +#endif clear_TF_reenable: set_tsk_thread_flag(tsk, TIF_SINGLESTEP); regs->flags &= ~X86_EFLAGS_TF; + preempt_conditional_cli(regs); return; } +#ifdef CONFIG_X86_64 +static int kernel_math_error(struct pt_regs *regs, const char *str, int trapnr) +{ + if (fixup_exception(regs)) + return 1; + + notify_die(DIE_GPF, str, regs, 0, trapnr, SIGFPE); + /* Illegal floating point operation in the kernel */ + current->thread.trap_no = trapnr; + die(str, regs, 0); + return 0; +} +#endif + /* * Note that we play around with the 'TS' bit in an attempt to get * the correct behaviour even in the presence of the asynchronous @@ -992,7 +691,9 @@ void math_error(void __user *ip) swd = get_fpu_swd(task); switch (swd & ~cwd & 0x3f) { case 0x000: /* No unmasked exception */ +#ifdef CONFIG_X86_32 return; +#endif default: /* Multiple exceptions */ break; case 0x001: /* Invalid Op */ @@ -1020,9 +721,18 @@ void math_error(void __user *ip) force_sig_info(SIGFPE, &info, task); } -void do_coprocessor_error(struct pt_regs *regs, long error_code) +dotraplinkage void do_coprocessor_error(struct pt_regs *regs, long error_code) { + conditional_sti(regs); + +#ifdef CONFIG_X86_32 ignore_fpu_irq = 1; +#else + if (!user_mode(regs) && + kernel_math_error(regs, "kernel x87 math error", 16)) + return; +#endif + math_error((void __user *)regs->ip); } @@ -1074,8 +784,12 @@ static void simd_math_error(void __user *ip) force_sig_info(SIGFPE, &info, task); } -void do_simd_coprocessor_error(struct pt_regs *regs, long error_code) +dotraplinkage void +do_simd_coprocessor_error(struct pt_regs *regs, long error_code) { + conditional_sti(regs); + +#ifdef CONFIG_X86_32 if (cpu_has_xmm) { /* Handle SIMD FPU exceptions on PIII+ processors. */ ignore_fpu_irq = 1; @@ -1094,16 +808,25 @@ void do_simd_coprocessor_error(struct pt_regs *regs, long error_code) current->thread.error_code = error_code; die_if_kernel("cache flush denied", regs, error_code); force_sig(SIGSEGV, current); +#else + if (!user_mode(regs) && + kernel_math_error(regs, "kernel simd math error", 19)) + return; + simd_math_error((void __user *)regs->ip); +#endif } -void do_spurious_interrupt_bug(struct pt_regs *regs, long error_code) +dotraplinkage void +do_spurious_interrupt_bug(struct pt_regs *regs, long error_code) { + conditional_sti(regs); #if 0 /* No need to warn about this any longer. */ printk(KERN_INFO "Ignoring P6 Local APIC Spurious Interrupt Bug...\n"); #endif } +#ifdef CONFIG_X86_32 unsigned long patch_espfix_desc(unsigned long uesp, unsigned long kesp) { struct desc_struct *gdt = get_cpu_gdt_table(smp_processor_id()); @@ -1122,6 +845,15 @@ unsigned long patch_espfix_desc(unsigned long uesp, unsigned long kesp) return new_kesp; } +#else +asmlinkage void __attribute__((weak)) smp_thermal_interrupt(void) +{ +} + +asmlinkage void __attribute__((weak)) mce_threshold_interrupt(void) +{ +} +#endif /* * 'math_state_restore()' saves the current math information in the @@ -1154,14 +886,24 @@ asmlinkage void math_state_restore(void) } clts(); /* Allow maths ops (or we recurse) */ +#ifdef CONFIG_X86_32 restore_fpu(tsk); +#else + /* + * Paranoid restore. send a SIGSEGV if we fail to restore the state. + */ + if (unlikely(restore_fpu_checking(tsk))) { + stts(); + force_sig(SIGSEGV, tsk); + return; + } +#endif thread->status |= TS_USEDFPU; /* So we fnsave on switch_to() */ tsk->fpu_counter++; } EXPORT_SYMBOL_GPL(math_state_restore); #ifndef CONFIG_MATH_EMULATION - asmlinkage void math_emulate(long arg) { printk(KERN_EMERG @@ -1170,12 +912,54 @@ asmlinkage void math_emulate(long arg) force_sig(SIGFPE, current); schedule(); } - #endif /* CONFIG_MATH_EMULATION */ +dotraplinkage void __kprobes +do_device_not_available(struct pt_regs *regs, long error) +{ +#ifdef CONFIG_X86_32 + if (read_cr0() & X86_CR0_EM) { + conditional_sti(regs); + math_emulate(0); + } else { + math_state_restore(); /* interrupts still off */ + conditional_sti(regs); + } +#else + math_state_restore(); +#endif +} + +#ifdef CONFIG_X86_32 +#ifdef CONFIG_X86_MCE +dotraplinkage void __kprobes do_machine_check(struct pt_regs *regs, long error) +{ + conditional_sti(regs); + machine_check_vector(regs, error); +} +#endif + +dotraplinkage void do_iret_error(struct pt_regs *regs, long error_code) +{ + siginfo_t info; + local_irq_enable(); + + info.si_signo = SIGILL; + info.si_errno = 0; + info.si_code = ILL_BADSTK; + info.si_addr = 0; + if (notify_die(DIE_TRAP, "iret exception", + regs, error_code, 32, SIGILL) == NOTIFY_STOP) + return; + do_trap(32, SIGILL, "iret exception", regs, error_code, &info); +} +#endif + void __init trap_init(void) { +#ifdef CONFIG_X86_32 int i; +#endif #ifdef CONFIG_EISA void __iomem *p = early_ioremap(0x0FFFD9, 4); @@ -1185,29 +969,40 @@ void __init trap_init(void) early_iounmap(p, 4); #endif - set_trap_gate(0, ÷_error); - set_intr_gate(1, &debug); - set_intr_gate(2, &nmi); - set_system_intr_gate(3, &int3); /* int3 can be called from all */ - set_system_gate(4, &overflow); /* int4 can be called from all */ - set_trap_gate(5, &bounds); - set_trap_gate(6, &invalid_op); - set_trap_gate(7, &device_not_available); + set_intr_gate(0, ÷_error); + set_intr_gate_ist(1, &debug, DEBUG_STACK); + set_intr_gate_ist(2, &nmi, NMI_STACK); + /* int3 can be called from all */ + set_system_intr_gate_ist(3, &int3, DEBUG_STACK); + /* int4 can be called from all */ + set_system_intr_gate(4, &overflow); + set_intr_gate(5, &bounds); + set_intr_gate(6, &invalid_op); + set_intr_gate(7, &device_not_available); +#ifdef CONFIG_X86_32 set_task_gate(8, GDT_ENTRY_DOUBLEFAULT_TSS); - set_trap_gate(9, &coprocessor_segment_overrun); - set_trap_gate(10, &invalid_TSS); - set_trap_gate(11, &segment_not_present); - set_trap_gate(12, &stack_segment); - set_trap_gate(13, &general_protection); +#else + set_intr_gate_ist(8, &double_fault, DOUBLEFAULT_STACK); +#endif + set_intr_gate(9, &coprocessor_segment_overrun); + set_intr_gate(10, &invalid_TSS); + set_intr_gate(11, &segment_not_present); + set_intr_gate_ist(12, &stack_segment, STACKFAULT_STACK); + set_intr_gate(13, &general_protection); set_intr_gate(14, &page_fault); - set_trap_gate(15, &spurious_interrupt_bug); - set_trap_gate(16, &coprocessor_error); - set_trap_gate(17, &alignment_check); + set_intr_gate(15, &spurious_interrupt_bug); + set_intr_gate(16, &coprocessor_error); + set_intr_gate(17, &alignment_check); #ifdef CONFIG_X86_MCE - set_trap_gate(18, &machine_check); + set_intr_gate_ist(18, &machine_check, MCE_STACK); #endif - set_trap_gate(19, &simd_coprocessor_error); + set_intr_gate(19, &simd_coprocessor_error); +#ifdef CONFIG_IA32_EMULATION + set_system_intr_gate(IA32_SYSCALL_VECTOR, ia32_syscall); +#endif + +#ifdef CONFIG_X86_32 if (cpu_has_fxsr) { printk(KERN_INFO "Enabling fast FPU save and restore... "); set_in_cr4(X86_CR4_OSFXSR); @@ -1220,37 +1015,20 @@ void __init trap_init(void) printk("done.\n"); } - set_system_gate(SYSCALL_VECTOR, &system_call); + set_system_trap_gate(SYSCALL_VECTOR, &system_call); /* Reserve all the builtin and the syscall vector: */ for (i = 0; i < FIRST_EXTERNAL_VECTOR; i++) set_bit(i, used_vectors); set_bit(SYSCALL_VECTOR, used_vectors); - - init_thread_xstate(); +#endif /* * Should be a barrier for any external CPU state: */ cpu_init(); +#ifdef CONFIG_X86_32 trap_init_hook(); +#endif } - -static int __init kstack_setup(char *s) -{ - kstack_depth_to_print = simple_strtoul(s, NULL, 0); - - return 1; -} -__setup("kstack=", kstack_setup); - -static int __init code_bytes_setup(char *s) -{ - code_bytes = simple_strtoul(s, NULL, 0); - if (code_bytes > 8192) - code_bytes = 8192; - - return 1; -} -__setup("code_bytes=", code_bytes_setup); diff --git a/arch/x86/kernel/traps_64.c b/arch/x86/kernel/traps_64.c deleted file mode 100644 index 7a31f104bef..00000000000 --- a/arch/x86/kernel/traps_64.c +++ /dev/null @@ -1,1218 +0,0 @@ -/* - * Copyright (C) 1991, 1992 Linus Torvalds - * Copyright (C) 2000, 2001, 2002 Andi Kleen, SuSE Labs - * - * Pentium III FXSR, SSE support - * Gareth Hughes <gareth@valinux.com>, May 2000 - */ - -/* - * 'Traps.c' handles hardware traps and faults after we have saved some - * state in 'entry.S'. - */ -#include <linux/moduleparam.h> -#include <linux/interrupt.h> -#include <linux/kallsyms.h> -#include <linux/spinlock.h> -#include <linux/kprobes.h> -#include <linux/uaccess.h> -#include <linux/utsname.h> -#include <linux/kdebug.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/ptrace.h> -#include <linux/string.h> -#include <linux/unwind.h> -#include <linux/delay.h> -#include <linux/errno.h> -#include <linux/kexec.h> -#include <linux/sched.h> -#include <linux/timer.h> -#include <linux/init.h> -#include <linux/bug.h> -#include <linux/nmi.h> -#include <linux/mm.h> -#include <linux/smp.h> -#include <linux/io.h> - -#if defined(CONFIG_EDAC) -#include <linux/edac.h> -#endif - -#include <asm/stacktrace.h> -#include <asm/processor.h> -#include <asm/debugreg.h> -#include <asm/atomic.h> -#include <asm/system.h> -#include <asm/unwind.h> -#include <asm/desc.h> -#include <asm/i387.h> -#include <asm/pgalloc.h> -#include <asm/proto.h> -#include <asm/pda.h> -#include <asm/traps.h> - -#include <mach_traps.h> - -int panic_on_unrecovered_nmi; -int kstack_depth_to_print = 12; -static unsigned int code_bytes = 64; -static int ignore_nmis; -static int die_counter; - -static inline void conditional_sti(struct pt_regs *regs) -{ - if (regs->flags & X86_EFLAGS_IF) - local_irq_enable(); -} - -static inline void preempt_conditional_sti(struct pt_regs *regs) -{ - inc_preempt_count(); - if (regs->flags & X86_EFLAGS_IF) - local_irq_enable(); -} - -static inline void preempt_conditional_cli(struct pt_regs *regs) -{ - if (regs->flags & X86_EFLAGS_IF) - local_irq_disable(); - /* Make sure to not schedule here because we could be running - on an exception stack. */ - dec_preempt_count(); -} - -void printk_address(unsigned long address, int reliable) -{ - printk(" [<%016lx>] %s%pS\n", - address, reliable ? "" : "? ", (void *) address); -} - -static unsigned long *in_exception_stack(unsigned cpu, unsigned long stack, - unsigned *usedp, char **idp) -{ - static char ids[][8] = { - [DEBUG_STACK - 1] = "#DB", - [NMI_STACK - 1] = "NMI", - [DOUBLEFAULT_STACK - 1] = "#DF", - [STACKFAULT_STACK - 1] = "#SS", - [MCE_STACK - 1] = "#MC", -#if DEBUG_STKSZ > EXCEPTION_STKSZ - [N_EXCEPTION_STACKS ... - N_EXCEPTION_STACKS + DEBUG_STKSZ / EXCEPTION_STKSZ - 2] = "#DB[?]" -#endif - }; - unsigned k; - - /* - * Iterate over all exception stacks, and figure out whether - * 'stack' is in one of them: - */ - for (k = 0; k < N_EXCEPTION_STACKS; k++) { - unsigned long end = per_cpu(orig_ist, cpu).ist[k]; - /* - * Is 'stack' above this exception frame's end? - * If yes then skip to the next frame. - */ - if (stack >= end) - continue; - /* - * Is 'stack' above this exception frame's start address? - * If yes then we found the right frame. - */ - if (stack >= end - EXCEPTION_STKSZ) { - /* - * Make sure we only iterate through an exception - * stack once. If it comes up for the second time - * then there's something wrong going on - just - * break out and return NULL: - */ - if (*usedp & (1U << k)) - break; - *usedp |= 1U << k; - *idp = ids[k]; - return (unsigned long *)end; - } - /* - * If this is a debug stack, and if it has a larger size than - * the usual exception stacks, then 'stack' might still - * be within the lower portion of the debug stack: - */ -#if DEBUG_STKSZ > EXCEPTION_STKSZ - if (k == DEBUG_STACK - 1 && stack >= end - DEBUG_STKSZ) { - unsigned j = N_EXCEPTION_STACKS - 1; - - /* - * Black magic. A large debug stack is composed of - * multiple exception stack entries, which we - * iterate through now. Dont look: - */ - do { - ++j; - end -= EXCEPTION_STKSZ; - ids[j][4] = '1' + (j - N_EXCEPTION_STACKS); - } while (stack < end - EXCEPTION_STKSZ); - if (*usedp & (1U << j)) - break; - *usedp |= 1U << j; - *idp = ids[j]; - return (unsigned long *)end; - } -#endif - } - return NULL; -} - -/* - * x86-64 can have up to three kernel stacks: - * process stack - * interrupt stack - * severe exception (double fault, nmi, stack fault, debug, mce) hardware stack - */ - -static inline int valid_stack_ptr(struct thread_info *tinfo, - void *p, unsigned int size, void *end) -{ - void *t = tinfo; - if (end) { - if (p < end && p >= (end-THREAD_SIZE)) - return 1; - else - return 0; - } - return p > t && p < t + THREAD_SIZE - size; -} - -/* The form of the top of the frame on the stack */ -struct stack_frame { - struct stack_frame *next_frame; - unsigned long return_address; -}; - -static inline unsigned long -print_context_stack(struct thread_info *tinfo, - unsigned long *stack, unsigned long bp, - const struct stacktrace_ops *ops, void *data, - unsigned long *end) -{ - struct stack_frame *frame = (struct stack_frame *)bp; - - while (valid_stack_ptr(tinfo, stack, sizeof(*stack), end)) { - unsigned long addr; - - addr = *stack; - if (__kernel_text_address(addr)) { - if ((unsigned long) stack == bp + 8) { - ops->address(data, addr, 1); - frame = frame->next_frame; - bp = (unsigned long) frame; - } else { - ops->address(data, addr, bp == 0); - } - } - stack++; - } - return bp; -} - -void dump_trace(struct task_struct *task, struct pt_regs *regs, - unsigned long *stack, unsigned long bp, - const struct stacktrace_ops *ops, void *data) -{ - const unsigned cpu = get_cpu(); - unsigned long *irqstack_end = (unsigned long *)cpu_pda(cpu)->irqstackptr; - unsigned used = 0; - struct thread_info *tinfo; - - if (!task) - task = current; - - if (!stack) { - unsigned long dummy; - stack = &dummy; - if (task && task != current) - stack = (unsigned long *)task->thread.sp; - } - -#ifdef CONFIG_FRAME_POINTER - if (!bp) { - if (task == current) { - /* Grab bp right from our regs */ - asm("movq %%rbp, %0" : "=r" (bp) : ); - } else { - /* bp is the last reg pushed by switch_to */ - bp = *(unsigned long *) task->thread.sp; - } - } -#endif - - /* - * Print function call entries in all stacks, starting at the - * current stack address. If the stacks consist of nested - * exceptions - */ - tinfo = task_thread_info(task); - for (;;) { - char *id; - unsigned long *estack_end; - estack_end = in_exception_stack(cpu, (unsigned long)stack, - &used, &id); - - if (estack_end) { - if (ops->stack(data, id) < 0) - break; - - bp = print_context_stack(tinfo, stack, bp, ops, - data, estack_end); - ops->stack(data, "<EOE>"); - /* - * We link to the next stack via the - * second-to-last pointer (index -2 to end) in the - * exception stack: - */ - stack = (unsigned long *) estack_end[-2]; - continue; - } - if (irqstack_end) { - unsigned long *irqstack; - irqstack = irqstack_end - - (IRQSTACKSIZE - 64) / sizeof(*irqstack); - - if (stack >= irqstack && stack < irqstack_end) { - if (ops->stack(data, "IRQ") < 0) - break; - bp = print_context_stack(tinfo, stack, bp, - ops, data, irqstack_end); - /* - * We link to the next stack (which would be - * the process stack normally) the last - * pointer (index -1 to end) in the IRQ stack: - */ - stack = (unsigned long *) (irqstack_end[-1]); - irqstack_end = NULL; - ops->stack(data, "EOI"); - continue; - } - } - break; - } - - /* - * This handles the process stack: - */ - bp = print_context_stack(tinfo, stack, bp, ops, data, NULL); - put_cpu(); -} -EXPORT_SYMBOL(dump_trace); - -static void -print_trace_warning_symbol(void *data, char *msg, unsigned long symbol) -{ - print_symbol(msg, symbol); - printk("\n"); -} - -static void print_trace_warning(void *data, char *msg) -{ - printk("%s\n", msg); -} - -static int print_trace_stack(void *data, char *name) -{ - printk(" <%s> ", name); - return 0; -} - -static void print_trace_address(void *data, unsigned long addr, int reliable) -{ - touch_nmi_watchdog(); - printk_address(addr, reliable); -} - -static const struct stacktrace_ops print_trace_ops = { - .warning = print_trace_warning, - .warning_symbol = print_trace_warning_symbol, - .stack = print_trace_stack, - .address = print_trace_address, -}; - -static void -show_trace_log_lvl(struct task_struct *task, struct pt_regs *regs, - unsigned long *stack, unsigned long bp, char *log_lvl) -{ - printk("Call Trace:\n"); - dump_trace(task, regs, stack, bp, &print_trace_ops, log_lvl); -} - -void show_trace(struct task_struct *task, struct pt_regs *regs, - unsigned long *stack, unsigned long bp) -{ - show_trace_log_lvl(task, regs, stack, bp, ""); -} - -static void -show_stack_log_lvl(struct task_struct *task, struct pt_regs *regs, - unsigned long *sp, unsigned long bp, char *log_lvl) -{ - unsigned long *stack; - int i; - const int cpu = smp_processor_id(); - unsigned long *irqstack_end = - (unsigned long *) (cpu_pda(cpu)->irqstackptr); - unsigned long *irqstack = - (unsigned long *) (cpu_pda(cpu)->irqstackptr - IRQSTACKSIZE); - - /* - * debugging aid: "show_stack(NULL, NULL);" prints the - * back trace for this cpu. - */ - - if (sp == NULL) { - if (task) - sp = (unsigned long *)task->thread.sp; - else - sp = (unsigned long *)&sp; - } - - stack = sp; - for (i = 0; i < kstack_depth_to_print; i++) { - if (stack >= irqstack && stack <= irqstack_end) { - if (stack == irqstack_end) { - stack = (unsigned long *) (irqstack_end[-1]); - printk(" <EOI> "); - } - } else { - if (((long) stack & (THREAD_SIZE-1)) == 0) - break; - } - if (i && ((i % 4) == 0)) - printk("\n"); - printk(" %016lx", *stack++); - touch_nmi_watchdog(); - } - printk("\n"); - show_trace_log_lvl(task, regs, sp, bp, log_lvl); -} - -void show_stack(struct task_struct *task, unsigned long *sp) -{ - show_stack_log_lvl(task, NULL, sp, 0, ""); -} - -/* - * The architecture-independent dump_stack generator - */ -void dump_stack(void) -{ - unsigned long bp = 0; - unsigned long stack; - -#ifdef CONFIG_FRAME_POINTER - if (!bp) - asm("movq %%rbp, %0" : "=r" (bp) : ); -#endif - - printk("Pid: %d, comm: %.20s %s %s %.*s\n", - current->pid, current->comm, print_tainted(), - init_utsname()->release, - (int)strcspn(init_utsname()->version, " "), - init_utsname()->version); - show_trace(NULL, NULL, &stack, bp); -} -EXPORT_SYMBOL(dump_stack); - -void show_registers(struct pt_regs *regs) -{ - int i; - unsigned long sp; - const int cpu = smp_processor_id(); - struct task_struct *cur = cpu_pda(cpu)->pcurrent; - - sp = regs->sp; - printk("CPU %d ", cpu); - __show_regs(regs); - printk("Process %s (pid: %d, threadinfo %p, task %p)\n", - cur->comm, cur->pid, task_thread_info(cur), cur); - - /* - * When in-kernel, we also print out the stack and code at the - * time of the fault.. - */ - if (!user_mode(regs)) { - unsigned int code_prologue = code_bytes * 43 / 64; - unsigned int code_len = code_bytes; - unsigned char c; - u8 *ip; - - printk("Stack: "); - show_stack_log_lvl(NULL, regs, (unsigned long *)sp, - regs->bp, ""); - - printk(KERN_EMERG "Code: "); - - ip = (u8 *)regs->ip - code_prologue; - if (ip < (u8 *)PAGE_OFFSET || probe_kernel_address(ip, c)) { - /* try starting at RIP */ - ip = (u8 *)regs->ip; - code_len = code_len - code_prologue + 1; - } - for (i = 0; i < code_len; i++, ip++) { - if (ip < (u8 *)PAGE_OFFSET || - probe_kernel_address(ip, c)) { - printk(" Bad RIP value."); - break; - } - if (ip == (u8 *)regs->ip) - printk("<%02x> ", c); - else - printk("%02x ", c); - } - } - printk("\n"); -} - -int is_valid_bugaddr(unsigned long ip) -{ - unsigned short ud2; - - if (__copy_from_user(&ud2, (const void __user *) ip, sizeof(ud2))) - return 0; - - return ud2 == 0x0b0f; -} - -static raw_spinlock_t die_lock = __RAW_SPIN_LOCK_UNLOCKED; -static int die_owner = -1; -static unsigned int die_nest_count; - -unsigned __kprobes long oops_begin(void) -{ - int cpu; - unsigned long flags; - - oops_enter(); - - /* racy, but better than risking deadlock. */ - raw_local_irq_save(flags); - cpu = smp_processor_id(); - if (!__raw_spin_trylock(&die_lock)) { - if (cpu == die_owner) - /* nested oops. should stop eventually */; - else - __raw_spin_lock(&die_lock); - } - die_nest_count++; - die_owner = cpu; - console_verbose(); - bust_spinlocks(1); - return flags; -} - -void __kprobes oops_end(unsigned long flags, struct pt_regs *regs, int signr) -{ - die_owner = -1; - bust_spinlocks(0); - die_nest_count--; - if (!die_nest_count) - /* Nest count reaches zero, release the lock. */ - __raw_spin_unlock(&die_lock); - raw_local_irq_restore(flags); - if (!regs) { - oops_exit(); - return; - } - if (panic_on_oops) - panic("Fatal exception"); - oops_exit(); - do_exit(signr); -} - -int __kprobes __die(const char *str, struct pt_regs *regs, long err) -{ - printk(KERN_EMERG "%s: %04lx [%u] ", str, err & 0xffff, ++die_counter); -#ifdef CONFIG_PREEMPT - printk("PREEMPT "); -#endif -#ifdef CONFIG_SMP - printk("SMP "); -#endif -#ifdef CONFIG_DEBUG_PAGEALLOC - printk("DEBUG_PAGEALLOC"); -#endif - printk("\n"); - if (notify_die(DIE_OOPS, str, regs, err, - current->thread.trap_no, SIGSEGV) == NOTIFY_STOP) - return 1; - - show_registers(regs); - add_taint(TAINT_DIE); - /* Executive summary in case the oops scrolled away */ - printk(KERN_ALERT "RIP "); - printk_address(regs->ip, 1); - printk(" RSP <%016lx>\n", regs->sp); - if (kexec_should_crash(current)) - crash_kexec(regs); - return 0; -} - -void die(const char *str, struct pt_regs *regs, long err) -{ - unsigned long flags = oops_begin(); - - if (!user_mode(regs)) - report_bug(regs->ip, regs); - - if (__die(str, regs, err)) - regs = NULL; - oops_end(flags, regs, SIGSEGV); -} - -notrace __kprobes void -die_nmi(char *str, struct pt_regs *regs, int do_panic) -{ - unsigned long flags; - - if (notify_die(DIE_NMIWATCHDOG, str, regs, 0, 2, SIGINT) == NOTIFY_STOP) - return; - - flags = oops_begin(); - /* - * We are in trouble anyway, lets at least try - * to get a message out. - */ - printk(KERN_EMERG "%s", str); - printk(" on CPU%d, ip %08lx, registers:\n", - smp_processor_id(), regs->ip); - show_registers(regs); - if (kexec_should_crash(current)) - crash_kexec(regs); - if (do_panic || panic_on_oops) - panic("Non maskable interrupt"); - oops_end(flags, NULL, SIGBUS); - nmi_exit(); - local_irq_enable(); - do_exit(SIGBUS); -} - -static void __kprobes -do_trap(int trapnr, int signr, char *str, struct pt_regs *regs, - long error_code, siginfo_t *info) -{ - struct task_struct *tsk = current; - - if (!user_mode(regs)) - goto kernel_trap; - - /* - * We want error_code and trap_no set for userspace faults and - * kernelspace faults which result in die(), but not - * kernelspace faults which are fixed up. die() gives the - * process no chance to handle the signal and notice the - * kernel fault information, so that won't result in polluting - * the information about previously queued, but not yet - * delivered, faults. See also do_general_protection below. - */ - tsk->thread.error_code = error_code; - tsk->thread.trap_no = trapnr; - - if (show_unhandled_signals && unhandled_signal(tsk, signr) && - printk_ratelimit()) { - printk(KERN_INFO - "%s[%d] trap %s ip:%lx sp:%lx error:%lx", - tsk->comm, tsk->pid, str, - regs->ip, regs->sp, error_code); - print_vma_addr(" in ", regs->ip); - printk("\n"); - } - - if (info) - force_sig_info(signr, info, tsk); - else - force_sig(signr, tsk); - return; - -kernel_trap: - if (!fixup_exception(regs)) { - tsk->thread.error_code = error_code; - tsk->thread.trap_no = trapnr; - die(str, regs, error_code); - } - return; -} - -#define DO_ERROR(trapnr, signr, str, name) \ -asmlinkage void do_##name(struct pt_regs *regs, long error_code) \ -{ \ - if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr) \ - == NOTIFY_STOP) \ - return; \ - conditional_sti(regs); \ - do_trap(trapnr, signr, str, regs, error_code, NULL); \ -} - -#define DO_ERROR_INFO(trapnr, signr, str, name, sicode, siaddr) \ -asmlinkage void do_##name(struct pt_regs *regs, long error_code) \ -{ \ - siginfo_t info; \ - info.si_signo = signr; \ - info.si_errno = 0; \ - info.si_code = sicode; \ - info.si_addr = (void __user *)siaddr; \ - trace_hardirqs_fixup(); \ - if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr) \ - == NOTIFY_STOP) \ - return; \ - conditional_sti(regs); \ - do_trap(trapnr, signr, str, regs, error_code, &info); \ -} - -DO_ERROR_INFO(0, SIGFPE, "divide error", divide_error, FPE_INTDIV, regs->ip) -DO_ERROR(4, SIGSEGV, "overflow", overflow) -DO_ERROR(5, SIGSEGV, "bounds", bounds) -DO_ERROR_INFO(6, SIGILL, "invalid opcode", invalid_op, ILL_ILLOPN, regs->ip) -DO_ERROR(9, SIGFPE, "coprocessor segment overrun", coprocessor_segment_overrun) -DO_ERROR(10, SIGSEGV, "invalid TSS", invalid_TSS) -DO_ERROR(11, SIGBUS, "segment not present", segment_not_present) -DO_ERROR_INFO(17, SIGBUS, "alignment check", alignment_check, BUS_ADRALN, 0) - -/* Runs on IST stack */ -asmlinkage void do_stack_segment(struct pt_regs *regs, long error_code) -{ - if (notify_die(DIE_TRAP, "stack segment", regs, error_code, - 12, SIGBUS) == NOTIFY_STOP) - return; - preempt_conditional_sti(regs); - do_trap(12, SIGBUS, "stack segment", regs, error_code, NULL); - preempt_conditional_cli(regs); -} - -asmlinkage void do_double_fault(struct pt_regs *regs, long error_code) -{ - static const char str[] = "double fault"; - struct task_struct *tsk = current; - - /* Return not checked because double check cannot be ignored */ - notify_die(DIE_TRAP, str, regs, error_code, 8, SIGSEGV); - - tsk->thread.error_code = error_code; - tsk->thread.trap_no = 8; - - /* This is always a kernel trap and never fixable (and thus must - never return). */ - for (;;) - die(str, regs, error_code); -} - -asmlinkage void __kprobes -do_general_protection(struct pt_regs *regs, long error_code) -{ - struct task_struct *tsk; - - conditional_sti(regs); - - tsk = current; - if (!user_mode(regs)) - goto gp_in_kernel; - - tsk->thread.error_code = error_code; - tsk->thread.trap_no = 13; - - if (show_unhandled_signals && unhandled_signal(tsk, SIGSEGV) && - printk_ratelimit()) { - printk(KERN_INFO - "%s[%d] general protection ip:%lx sp:%lx error:%lx", - tsk->comm, tsk->pid, - regs->ip, regs->sp, error_code); - print_vma_addr(" in ", regs->ip); - printk("\n"); - } - - force_sig(SIGSEGV, tsk); - return; - -gp_in_kernel: - if (fixup_exception(regs)) - return; - - tsk->thread.error_code = error_code; - tsk->thread.trap_no = 13; - if (notify_die(DIE_GPF, "general protection fault", regs, - error_code, 13, SIGSEGV) == NOTIFY_STOP) - return; - die("general protection fault", regs, error_code); -} - -static notrace __kprobes void -mem_parity_error(unsigned char reason, struct pt_regs *regs) -{ - printk(KERN_EMERG "Uhhuh. NMI received for unknown reason %02x.\n", - reason); - printk(KERN_EMERG "You have some hardware problem, likely on the PCI bus.\n"); - -#if defined(CONFIG_EDAC) - if (edac_handler_set()) { - edac_atomic_assert_error(); - return; - } -#endif - - if (panic_on_unrecovered_nmi) - panic("NMI: Not continuing"); - - printk(KERN_EMERG "Dazed and confused, but trying to continue\n"); - - /* Clear and disable the memory parity error line. */ - reason = (reason & 0xf) | 4; - outb(reason, 0x61); -} - -static notrace __kprobes void -io_check_error(unsigned char reason, struct pt_regs *regs) -{ - printk("NMI: IOCK error (debug interrupt?)\n"); - show_registers(regs); - - /* Re-enable the IOCK line, wait for a few seconds */ - reason = (reason & 0xf) | 8; - outb(reason, 0x61); - mdelay(2000); - reason &= ~8; - outb(reason, 0x61); -} - -static notrace __kprobes void -unknown_nmi_error(unsigned char reason, struct pt_regs *regs) -{ - if (notify_die(DIE_NMIUNKNOWN, "nmi", regs, reason, 2, SIGINT) == - NOTIFY_STOP) - return; - printk(KERN_EMERG "Uhhuh. NMI received for unknown reason %02x.\n", - reason); - printk(KERN_EMERG "Do you have a strange power saving mode enabled?\n"); - - if (panic_on_unrecovered_nmi) - panic("NMI: Not continuing"); - - printk(KERN_EMERG "Dazed and confused, but trying to continue\n"); -} - -/* Runs on IST stack. This code must keep interrupts off all the time. - Nested NMIs are prevented by the CPU. */ -asmlinkage notrace __kprobes void default_do_nmi(struct pt_regs *regs) -{ - unsigned char reason = 0; - int cpu; - - cpu = smp_processor_id(); - - /* Only the BSP gets external NMIs from the system. */ - if (!cpu) - reason = get_nmi_reason(); - - if (!(reason & 0xc0)) { - if (notify_die(DIE_NMI_IPI, "nmi_ipi", regs, reason, 2, SIGINT) - == NOTIFY_STOP) - return; - /* - * Ok, so this is none of the documented NMI sources, - * so it must be the NMI watchdog. - */ - if (nmi_watchdog_tick(regs, reason)) - return; - if (!do_nmi_callback(regs, cpu)) - unknown_nmi_error(reason, regs); - - return; - } - if (notify_die(DIE_NMI, "nmi", regs, reason, 2, SIGINT) == NOTIFY_STOP) - return; - - /* AK: following checks seem to be broken on modern chipsets. FIXME */ - if (reason & 0x80) - mem_parity_error(reason, regs); - if (reason & 0x40) - io_check_error(reason, regs); -} - -asmlinkage notrace __kprobes void -do_nmi(struct pt_regs *regs, long error_code) -{ - nmi_enter(); - - add_pda(__nmi_count, 1); - - if (!ignore_nmis) - default_do_nmi(regs); - - nmi_exit(); -} - -void stop_nmi(void) -{ - acpi_nmi_disable(); - ignore_nmis++; -} - -void restart_nmi(void) -{ - ignore_nmis--; - acpi_nmi_enable(); -} - -/* runs on IST stack. */ -asmlinkage void __kprobes do_int3(struct pt_regs *regs, long error_code) -{ - trace_hardirqs_fixup(); - - if (notify_die(DIE_INT3, "int3", regs, error_code, 3, SIGTRAP) - == NOTIFY_STOP) - return; - - preempt_conditional_sti(regs); - do_trap(3, SIGTRAP, "int3", regs, error_code, NULL); - preempt_conditional_cli(regs); -} - -/* Help handler running on IST stack to switch back to user stack - for scheduling or signal handling. The actual stack switch is done in - entry.S */ -asmlinkage __kprobes struct pt_regs *sync_regs(struct pt_regs *eregs) -{ - struct pt_regs *regs = eregs; - /* Did already sync */ - if (eregs == (struct pt_regs *)eregs->sp) - ; - /* Exception from user space */ - else if (user_mode(eregs)) - regs = task_pt_regs(current); - /* Exception from kernel and interrupts are enabled. Move to - kernel process stack. */ - else if (eregs->flags & X86_EFLAGS_IF) - regs = (struct pt_regs *)(eregs->sp -= sizeof(struct pt_regs)); - if (eregs != regs) - *regs = *eregs; - return regs; -} - -/* runs on IST stack. */ -asmlinkage void __kprobes do_debug(struct pt_regs *regs, - unsigned long error_code) -{ - struct task_struct *tsk = current; - unsigned long condition; - siginfo_t info; - - trace_hardirqs_fixup(); - - get_debugreg(condition, 6); - - /* - * The processor cleared BTF, so don't mark that we need it set. - */ - clear_tsk_thread_flag(tsk, TIF_DEBUGCTLMSR); - tsk->thread.debugctlmsr = 0; - - if (notify_die(DIE_DEBUG, "debug", regs, condition, error_code, - SIGTRAP) == NOTIFY_STOP) - return; - - preempt_conditional_sti(regs); - - /* Mask out spurious debug traps due to lazy DR7 setting */ - if (condition & (DR_TRAP0|DR_TRAP1|DR_TRAP2|DR_TRAP3)) { - if (!tsk->thread.debugreg7) - goto clear_dr7; - } - - tsk->thread.debugreg6 = condition; - - /* - * Single-stepping through TF: make sure we ignore any events in - * kernel space (but re-enable TF when returning to user mode). - */ - if (condition & DR_STEP) { - if (!user_mode(regs)) - goto clear_TF_reenable; - } - - /* Ok, finally something we can handle */ - tsk->thread.trap_no = 1; - tsk->thread.error_code = error_code; - info.si_signo = SIGTRAP; - info.si_errno = 0; - info.si_code = TRAP_BRKPT; - info.si_addr = user_mode(regs) ? (void __user *)regs->ip : NULL; - force_sig_info(SIGTRAP, &info, tsk); - -clear_dr7: - set_debugreg(0, 7); - preempt_conditional_cli(regs); - return; - -clear_TF_reenable: - set_tsk_thread_flag(tsk, TIF_SINGLESTEP); - regs->flags &= ~X86_EFLAGS_TF; - preempt_conditional_cli(regs); - return; -} - -static int kernel_math_error(struct pt_regs *regs, const char *str, int trapnr) -{ - if (fixup_exception(regs)) - return 1; - - notify_die(DIE_GPF, str, regs, 0, trapnr, SIGFPE); - /* Illegal floating point operation in the kernel */ - current->thread.trap_no = trapnr; - die(str, regs, 0); - return 0; -} - -/* - * Note that we play around with the 'TS' bit in an attempt to get - * the correct behaviour even in the presence of the asynchronous - * IRQ13 behaviour - */ -asmlinkage void do_coprocessor_error(struct pt_regs *regs) -{ - void __user *ip = (void __user *)(regs->ip); - struct task_struct *task; - siginfo_t info; - unsigned short cwd, swd; - - conditional_sti(regs); - if (!user_mode(regs) && - kernel_math_error(regs, "kernel x87 math error", 16)) - return; - - /* - * Save the info for the exception handler and clear the error. - */ - task = current; - save_init_fpu(task); - task->thread.trap_no = 16; - task->thread.error_code = 0; - info.si_signo = SIGFPE; - info.si_errno = 0; - info.si_code = __SI_FAULT; - info.si_addr = ip; - /* - * (~cwd & swd) will mask out exceptions that are not set to unmasked - * status. 0x3f is the exception bits in these regs, 0x200 is the - * C1 reg you need in case of a stack fault, 0x040 is the stack - * fault bit. We should only be taking one exception at a time, - * so if this combination doesn't produce any single exception, - * then we have a bad program that isn't synchronizing its FPU usage - * and it will suffer the consequences since we won't be able to - * fully reproduce the context of the exception - */ - cwd = get_fpu_cwd(task); - swd = get_fpu_swd(task); - switch (swd & ~cwd & 0x3f) { - case 0x000: /* No unmasked exception */ - default: /* Multiple exceptions */ - break; - case 0x001: /* Invalid Op */ - /* - * swd & 0x240 == 0x040: Stack Underflow - * swd & 0x240 == 0x240: Stack Overflow - * User must clear the SF bit (0x40) if set - */ - info.si_code = FPE_FLTINV; - break; - case 0x002: /* Denormalize */ - case 0x010: /* Underflow */ - info.si_code = FPE_FLTUND; - break; - case 0x004: /* Zero Divide */ - info.si_code = FPE_FLTDIV; - break; - case 0x008: /* Overflow */ - info.si_code = FPE_FLTOVF; - break; - case 0x020: /* Precision */ - info.si_code = FPE_FLTRES; - break; - } - force_sig_info(SIGFPE, &info, task); -} - -asmlinkage void bad_intr(void) -{ - printk("bad interrupt"); -} - -asmlinkage void do_simd_coprocessor_error(struct pt_regs *regs) -{ - void __user *ip = (void __user *)(regs->ip); - struct task_struct *task; - siginfo_t info; - unsigned short mxcsr; - - conditional_sti(regs); - if (!user_mode(regs) && - kernel_math_error(regs, "kernel simd math error", 19)) - return; - - /* - * Save the info for the exception handler and clear the error. - */ - task = current; - save_init_fpu(task); - task->thread.trap_no = 19; - task->thread.error_code = 0; - info.si_signo = SIGFPE; - info.si_errno = 0; - info.si_code = __SI_FAULT; - info.si_addr = ip; - /* - * The SIMD FPU exceptions are handled a little differently, as there - * is only a single status/control register. Thus, to determine which - * unmasked exception was caught we must mask the exception mask bits - * at 0x1f80, and then use these to mask the exception bits at 0x3f. - */ - mxcsr = get_fpu_mxcsr(task); - switch (~((mxcsr & 0x1f80) >> 7) & (mxcsr & 0x3f)) { - case 0x000: - default: - break; - case 0x001: /* Invalid Op */ - info.si_code = FPE_FLTINV; - break; - case 0x002: /* Denormalize */ - case 0x010: /* Underflow */ - info.si_code = FPE_FLTUND; - break; - case 0x004: /* Zero Divide */ - info.si_code = FPE_FLTDIV; - break; - case 0x008: /* Overflow */ - info.si_code = FPE_FLTOVF; - break; - case 0x020: /* Precision */ - info.si_code = FPE_FLTRES; - break; - } - force_sig_info(SIGFPE, &info, task); -} - -asmlinkage void do_spurious_interrupt_bug(struct pt_regs *regs) -{ -} - -asmlinkage void __attribute__((weak)) smp_thermal_interrupt(void) -{ -} - -asmlinkage void __attribute__((weak)) mce_threshold_interrupt(void) -{ -} - -/* - * 'math_state_restore()' saves the current math information in the - * old math state array, and gets the new ones from the current task - * - * Careful.. There are problems with IBM-designed IRQ13 behaviour. - * Don't touch unless you *really* know how it works. - */ -asmlinkage void math_state_restore(void) -{ - struct task_struct *me = current; - - if (!used_math()) { - local_irq_enable(); - /* - * does a slab alloc which can sleep - */ - if (init_fpu(me)) { - /* - * ran out of memory! - */ - do_group_exit(SIGKILL); - return; - } - local_irq_disable(); - } - - clts(); /* Allow maths ops (or we recurse) */ - /* - * Paranoid restore. send a SIGSEGV if we fail to restore the state. - */ - if (unlikely(restore_fpu_checking(&me->thread.xstate->fxsave))) { - stts(); - force_sig(SIGSEGV, me); - return; - } - task_thread_info(me)->status |= TS_USEDFPU; - me->fpu_counter++; -} -EXPORT_SYMBOL_GPL(math_state_restore); - -void __init trap_init(void) -{ - set_intr_gate(0, ÷_error); - set_intr_gate_ist(1, &debug, DEBUG_STACK); - set_intr_gate_ist(2, &nmi, NMI_STACK); - /* int3 can be called from all */ - set_system_gate_ist(3, &int3, DEBUG_STACK); - /* int4 can be called from all */ - set_system_gate(4, &overflow); - set_intr_gate(5, &bounds); - set_intr_gate(6, &invalid_op); - set_intr_gate(7, &device_not_available); - set_intr_gate_ist(8, &double_fault, DOUBLEFAULT_STACK); - set_intr_gate(9, &coprocessor_segment_overrun); - set_intr_gate(10, &invalid_TSS); - set_intr_gate(11, &segment_not_present); - set_intr_gate_ist(12, &stack_segment, STACKFAULT_STACK); - set_intr_gate(13, &general_protection); - set_intr_gate(14, &page_fault); - set_intr_gate(15, &spurious_interrupt_bug); - set_intr_gate(16, &coprocessor_error); - set_intr_gate(17, &alignment_check); -#ifdef CONFIG_X86_MCE - set_intr_gate_ist(18, &machine_check, MCE_STACK); -#endif - set_intr_gate(19, &simd_coprocessor_error); - -#ifdef CONFIG_IA32_EMULATION - set_system_gate(IA32_SYSCALL_VECTOR, ia32_syscall); -#endif - /* - * initialize the per thread extended state: - */ - init_thread_xstate(); - /* - * Should be a barrier for any external CPU state: - */ - cpu_init(); -} - -static int __init oops_setup(char *s) -{ - if (!s) - return -EINVAL; - if (!strcmp(s, "panic")) - panic_on_oops = 1; - return 0; -} -early_param("oops", oops_setup); - -static int __init kstack_setup(char *s) -{ - if (!s) - return -EINVAL; - kstack_depth_to_print = simple_strtoul(s, NULL, 0); - return 0; -} -early_param("kstack", kstack_setup); - -static int __init code_bytes_setup(char *s) -{ - code_bytes = simple_strtoul(s, NULL, 0); - if (code_bytes > 8192) - code_bytes = 8192; - - return 1; -} -__setup("code_bytes=", code_bytes_setup); diff --git a/arch/x86/kernel/vmi_32.c b/arch/x86/kernel/vmi_32.c index 8c9ad02af5a..8b6c393ab9f 100644 --- a/arch/x86/kernel/vmi_32.c +++ b/arch/x86/kernel/vmi_32.c @@ -905,8 +905,8 @@ static inline int __init activate_vmi(void) #endif #ifdef CONFIG_X86_LOCAL_APIC - para_fill(pv_apic_ops.apic_read, APICRead); - para_fill(pv_apic_ops.apic_write, APICWrite); + para_fill(apic_ops->read, APICRead); + para_fill(apic_ops->write, APICWrite); #endif /* diff --git a/arch/x86/kernel/vmlinux_32.lds.S b/arch/x86/kernel/vmlinux_32.lds.S index af5bdad8460..a9b8560adbc 100644 --- a/arch/x86/kernel/vmlinux_32.lds.S +++ b/arch/x86/kernel/vmlinux_32.lds.S @@ -140,10 +140,10 @@ SECTIONS *(.con_initcall.init) __con_initcall_end = .; } - .x86cpuvendor.init : AT(ADDR(.x86cpuvendor.init) - LOAD_OFFSET) { - __x86cpuvendor_start = .; - *(.x86cpuvendor.init) - __x86cpuvendor_end = .; + .x86_cpu_dev.init : AT(ADDR(.x86_cpu_dev.init) - LOAD_OFFSET) { + __x86_cpu_dev_start = .; + *(.x86_cpu_dev.init) + __x86_cpu_dev_end = .; } SECURITY_INIT . = ALIGN(4); @@ -180,6 +180,7 @@ SECTIONS . = ALIGN(PAGE_SIZE); .data.percpu : AT(ADDR(.data.percpu) - LOAD_OFFSET) { __per_cpu_start = .; + *(.data.percpu.page_aligned) *(.data.percpu) *(.data.percpu.shared_aligned) __per_cpu_end = .; diff --git a/arch/x86/kernel/vmlinux_64.lds.S b/arch/x86/kernel/vmlinux_64.lds.S index 63e5c1a22e8..46e05447405 100644 --- a/arch/x86/kernel/vmlinux_64.lds.S +++ b/arch/x86/kernel/vmlinux_64.lds.S @@ -168,12 +168,11 @@ SECTIONS *(.con_initcall.init) } __con_initcall_end = .; - . = ALIGN(16); - __x86cpuvendor_start = .; - .x86cpuvendor.init : AT(ADDR(.x86cpuvendor.init) - LOAD_OFFSET) { - *(.x86cpuvendor.init) + __x86_cpu_dev_start = .; + .x86_cpu_dev.init : AT(ADDR(.x86_cpu_dev.init) - LOAD_OFFSET) { + *(.x86_cpu_dev.init) } - __x86cpuvendor_end = .; + __x86_cpu_dev_end = .; SECURITY_INIT . = ALIGN(8); diff --git a/arch/x86/kernel/xsave.c b/arch/x86/kernel/xsave.c new file mode 100644 index 00000000000..9abac8a9d82 --- /dev/null +++ b/arch/x86/kernel/xsave.c @@ -0,0 +1,345 @@ +/* + * xsave/xrstor support. + * + * Author: Suresh Siddha <suresh.b.siddha@intel.com> + */ +#include <linux/bootmem.h> +#include <linux/compat.h> +#include <asm/i387.h> +#ifdef CONFIG_IA32_EMULATION +#include <asm/sigcontext32.h> +#endif +#include <asm/xcr.h> + +/* + * Supported feature mask by the CPU and the kernel. + */ +u64 pcntxt_mask; + +struct _fpx_sw_bytes fx_sw_reserved; +#ifdef CONFIG_IA32_EMULATION +struct _fpx_sw_bytes fx_sw_reserved_ia32; +#endif + +/* + * Check for the presence of extended state information in the + * user fpstate pointer in the sigcontext. + */ +int check_for_xstate(struct i387_fxsave_struct __user *buf, + void __user *fpstate, + struct _fpx_sw_bytes *fx_sw_user) +{ + int min_xstate_size = sizeof(struct i387_fxsave_struct) + + sizeof(struct xsave_hdr_struct); + unsigned int magic2; + int err; + + err = __copy_from_user(fx_sw_user, &buf->sw_reserved[0], + sizeof(struct _fpx_sw_bytes)); + + if (err) + return err; + + /* + * First Magic check failed. + */ + if (fx_sw_user->magic1 != FP_XSTATE_MAGIC1) + return -1; + + /* + * Check for error scenarios. + */ + if (fx_sw_user->xstate_size < min_xstate_size || + fx_sw_user->xstate_size > xstate_size || + fx_sw_user->xstate_size > fx_sw_user->extended_size) + return -1; + + err = __get_user(magic2, (__u32 *) (((void *)fpstate) + + fx_sw_user->extended_size - + FP_XSTATE_MAGIC2_SIZE)); + /* + * Check for the presence of second magic word at the end of memory + * layout. This detects the case where the user just copied the legacy + * fpstate layout with out copying the extended state information + * in the memory layout. + */ + if (err || magic2 != FP_XSTATE_MAGIC2) + return -1; + + return 0; +} + +#ifdef CONFIG_X86_64 +/* + * Signal frame handlers. + */ + +int save_i387_xstate(void __user *buf) +{ + struct task_struct *tsk = current; + int err = 0; + + if (!access_ok(VERIFY_WRITE, buf, sig_xstate_size)) + return -EACCES; + + BUG_ON(sig_xstate_size < xstate_size); + + if ((unsigned long)buf % 64) + printk("save_i387_xstate: bad fpstate %p\n", buf); + + if (!used_math()) + return 0; + clear_used_math(); /* trigger finit */ + if (task_thread_info(tsk)->status & TS_USEDFPU) { + /* + * Start with clearing the user buffer. This will present a + * clean context for the bytes not touched by the fxsave/xsave. + */ + err = __clear_user(buf, sig_xstate_size); + if (err) + return err; + + if (task_thread_info(tsk)->status & TS_XSAVE) + err = xsave_user(buf); + else + err = fxsave_user(buf); + + if (err) + return err; + task_thread_info(tsk)->status &= ~TS_USEDFPU; + stts(); + } else { + if (__copy_to_user(buf, &tsk->thread.xstate->fxsave, + xstate_size)) + return -1; + } + + if (task_thread_info(tsk)->status & TS_XSAVE) { + struct _fpstate __user *fx = buf; + struct _xstate __user *x = buf; + u64 xstate_bv; + + err = __copy_to_user(&fx->sw_reserved, &fx_sw_reserved, + sizeof(struct _fpx_sw_bytes)); + + err |= __put_user(FP_XSTATE_MAGIC2, + (__u32 __user *) (buf + sig_xstate_size + - FP_XSTATE_MAGIC2_SIZE)); + + /* + * Read the xstate_bv which we copied (directly from the cpu or + * from the state in task struct) to the user buffers and + * set the FP/SSE bits. + */ + err |= __get_user(xstate_bv, &x->xstate_hdr.xstate_bv); + + /* + * For legacy compatible, we always set FP/SSE bits in the bit + * vector while saving the state to the user context. This will + * enable us capturing any changes(during sigreturn) to + * the FP/SSE bits by the legacy applications which don't touch + * xstate_bv in the xsave header. + * + * xsave aware apps can change the xstate_bv in the xsave + * header as well as change any contents in the memory layout. + * xrestore as part of sigreturn will capture all the changes. + */ + xstate_bv |= XSTATE_FPSSE; + + err |= __put_user(xstate_bv, &x->xstate_hdr.xstate_bv); + + if (err) + return err; + } + + return 1; +} + +/* + * Restore the extended state if present. Otherwise, restore the FP/SSE + * state. + */ +int restore_user_xstate(void __user *buf) +{ + struct _fpx_sw_bytes fx_sw_user; + u64 mask; + int err; + + if (((unsigned long)buf % 64) || + check_for_xstate(buf, buf, &fx_sw_user)) + goto fx_only; + + mask = fx_sw_user.xstate_bv; + + /* + * restore the state passed by the user. + */ + err = xrestore_user(buf, mask); + if (err) + return err; + + /* + * init the state skipped by the user. + */ + mask = pcntxt_mask & ~mask; + + xrstor_state(init_xstate_buf, mask); + + return 0; + +fx_only: + /* + * couldn't find the extended state information in the + * memory layout. Restore just the FP/SSE and init all + * the other extended state. + */ + xrstor_state(init_xstate_buf, pcntxt_mask & ~XSTATE_FPSSE); + return fxrstor_checking((__force struct i387_fxsave_struct *)buf); +} + +/* + * This restores directly out of user space. Exceptions are handled. + */ +int restore_i387_xstate(void __user *buf) +{ + struct task_struct *tsk = current; + int err = 0; + + if (!buf) { + if (used_math()) + goto clear; + return 0; + } else + if (!access_ok(VERIFY_READ, buf, sig_xstate_size)) + return -EACCES; + + if (!used_math()) { + err = init_fpu(tsk); + if (err) + return err; + } + + if (!(task_thread_info(current)->status & TS_USEDFPU)) { + clts(); + task_thread_info(current)->status |= TS_USEDFPU; + } + if (task_thread_info(tsk)->status & TS_XSAVE) + err = restore_user_xstate(buf); + else + err = fxrstor_checking((__force struct i387_fxsave_struct *) + buf); + if (unlikely(err)) { + /* + * Encountered an error while doing the restore from the + * user buffer, clear the fpu state. + */ +clear: + clear_fpu(tsk); + clear_used_math(); + } + return err; +} +#endif + +/* + * Prepare the SW reserved portion of the fxsave memory layout, indicating + * the presence of the extended state information in the memory layout + * pointed by the fpstate pointer in the sigcontext. + * This will be saved when ever the FP and extended state context is + * saved on the user stack during the signal handler delivery to the user. + */ +void prepare_fx_sw_frame(void) +{ + int size_extended = (xstate_size - sizeof(struct i387_fxsave_struct)) + + FP_XSTATE_MAGIC2_SIZE; + + sig_xstate_size = sizeof(struct _fpstate) + size_extended; + +#ifdef CONFIG_IA32_EMULATION + sig_xstate_ia32_size = sizeof(struct _fpstate_ia32) + size_extended; +#endif + + memset(&fx_sw_reserved, 0, sizeof(fx_sw_reserved)); + + fx_sw_reserved.magic1 = FP_XSTATE_MAGIC1; + fx_sw_reserved.extended_size = sig_xstate_size; + fx_sw_reserved.xstate_bv = pcntxt_mask; + fx_sw_reserved.xstate_size = xstate_size; +#ifdef CONFIG_IA32_EMULATION + memcpy(&fx_sw_reserved_ia32, &fx_sw_reserved, + sizeof(struct _fpx_sw_bytes)); + fx_sw_reserved_ia32.extended_size = sig_xstate_ia32_size; +#endif +} + +/* + * Represents init state for the supported extended state. + */ +struct xsave_struct *init_xstate_buf; + +#ifdef CONFIG_X86_64 +unsigned int sig_xstate_size = sizeof(struct _fpstate); +#endif + +/* + * Enable the extended processor state save/restore feature + */ +void __cpuinit xsave_init(void) +{ + if (!cpu_has_xsave) + return; + + set_in_cr4(X86_CR4_OSXSAVE); + + /* + * Enable all the features that the HW is capable of + * and the Linux kernel is aware of. + */ + xsetbv(XCR_XFEATURE_ENABLED_MASK, pcntxt_mask); +} + +/* + * setup the xstate image representing the init state + */ +static void __init setup_xstate_init(void) +{ + init_xstate_buf = alloc_bootmem(xstate_size); + init_xstate_buf->i387.mxcsr = MXCSR_DEFAULT; +} + +/* + * Enable and initialize the xsave feature. + */ +void __init xsave_cntxt_init(void) +{ + unsigned int eax, ebx, ecx, edx; + + cpuid_count(0xd, 0, &eax, &ebx, &ecx, &edx); + pcntxt_mask = eax + ((u64)edx << 32); + + if ((pcntxt_mask & XSTATE_FPSSE) != XSTATE_FPSSE) { + printk(KERN_ERR "FP/SSE not shown under xsave features 0x%llx\n", + pcntxt_mask); + BUG(); + } + + /* + * for now OS knows only about FP/SSE + */ + pcntxt_mask = pcntxt_mask & XCNTXT_MASK; + xsave_init(); + + /* + * Recompute the context size for enabled features + */ + cpuid_count(0xd, 0, &eax, &ebx, &ecx, &edx); + xstate_size = ebx; + + prepare_fx_sw_frame(); + + setup_xstate_init(); + + printk(KERN_INFO "xsave/xrstor: enabled xstate_bv 0x%llx, " + "cntxt size 0x%x\n", + pcntxt_mask, xstate_size); +} diff --git a/arch/x86/kvm/vmx.h b/arch/x86/kvm/vmx.h index 23e8373507a..17e25995b65 100644 --- a/arch/x86/kvm/vmx.h +++ b/arch/x86/kvm/vmx.h @@ -331,21 +331,6 @@ enum vmcs_field { #define AR_RESERVD_MASK 0xfffe0f00 -#define MSR_IA32_VMX_BASIC 0x480 -#define MSR_IA32_VMX_PINBASED_CTLS 0x481 -#define MSR_IA32_VMX_PROCBASED_CTLS 0x482 -#define MSR_IA32_VMX_EXIT_CTLS 0x483 -#define MSR_IA32_VMX_ENTRY_CTLS 0x484 -#define MSR_IA32_VMX_MISC 0x485 -#define MSR_IA32_VMX_CR0_FIXED0 0x486 -#define MSR_IA32_VMX_CR0_FIXED1 0x487 -#define MSR_IA32_VMX_CR4_FIXED0 0x488 -#define MSR_IA32_VMX_CR4_FIXED1 0x489 -#define MSR_IA32_VMX_VMCS_ENUM 0x48a -#define MSR_IA32_VMX_PROCBASED_CTLS2 0x48b -#define MSR_IA32_VMX_EPT_VPID_CAP 0x48c - -#define MSR_IA32_FEATURE_CONTROL 0x3a #define MSR_IA32_FEATURE_CONTROL_LOCKED 0x1 #define MSR_IA32_FEATURE_CONTROL_VMXON_ENABLED 0x4 diff --git a/arch/x86/lguest/boot.c b/arch/x86/lguest/boot.c index d9249a882aa..65f0b8a47be 100644 --- a/arch/x86/lguest/boot.c +++ b/arch/x86/lguest/boot.c @@ -55,6 +55,7 @@ #include <linux/lguest_launcher.h> #include <linux/virtio_console.h> #include <linux/pm.h> +#include <asm/apic.h> #include <asm/lguest.h> #include <asm/paravirt.h> #include <asm/param.h> @@ -783,14 +784,44 @@ static void lguest_wbinvd(void) * code qualifies for Advanced. It will also never interrupt anything. It * does, however, allow us to get through the Linux boot code. */ #ifdef CONFIG_X86_LOCAL_APIC -static void lguest_apic_write(unsigned long reg, u32 v) +static void lguest_apic_write(u32 reg, u32 v) { } -static u32 lguest_apic_read(unsigned long reg) +static u32 lguest_apic_read(u32 reg) { return 0; } + +static u64 lguest_apic_icr_read(void) +{ + return 0; +} + +static void lguest_apic_icr_write(u32 low, u32 id) +{ + /* Warn to see if there's any stray references */ + WARN_ON(1); +} + +static void lguest_apic_wait_icr_idle(void) +{ + return; +} + +static u32 lguest_apic_safe_wait_icr_idle(void) +{ + return 0; +} + +static struct apic_ops lguest_basic_apic_ops = { + .read = lguest_apic_read, + .write = lguest_apic_write, + .icr_read = lguest_apic_icr_read, + .icr_write = lguest_apic_icr_write, + .wait_icr_idle = lguest_apic_wait_icr_idle, + .safe_wait_icr_idle = lguest_apic_safe_wait_icr_idle, +}; #endif /* STOP! Until an interrupt comes in. */ @@ -990,8 +1021,7 @@ __init void lguest_init(void) #ifdef CONFIG_X86_LOCAL_APIC /* apic read/write intercepts */ - pv_apic_ops.apic_write = lguest_apic_write; - pv_apic_ops.apic_read = lguest_apic_read; + apic_ops = &lguest_basic_apic_ops; #endif /* time operations */ diff --git a/arch/x86/lib/Makefile b/arch/x86/lib/Makefile index aa3fa411942..55e11aa6d66 100644 --- a/arch/x86/lib/Makefile +++ b/arch/x86/lib/Makefile @@ -17,9 +17,6 @@ ifeq ($(CONFIG_X86_32),y) lib-$(CONFIG_X86_USE_3DNOW) += mmx_32.o else obj-y += io_64.o iomap_copy_64.o - - CFLAGS_csum-partial_64.o := -funroll-loops - lib-y += csum-partial_64.o csum-copy_64.o csum-wrappers_64.o lib-y += thunk_64.o clear_page_64.o copy_page_64.o lib-y += memmove_64.o memset_64.o diff --git a/arch/x86/lib/usercopy_32.c b/arch/x86/lib/usercopy_32.c index 24e60944971..9e68075544f 100644 --- a/arch/x86/lib/usercopy_32.c +++ b/arch/x86/lib/usercopy_32.c @@ -14,6 +14,13 @@ #include <asm/uaccess.h> #include <asm/mmx.h> +#ifdef CONFIG_X86_INTEL_USERCOPY +/* + * Alignment at which movsl is preferred for bulk memory copies. + */ +struct movsl_mask movsl_mask __read_mostly; +#endif + static inline int __movsl_is_ok(unsigned long a1, unsigned long a2, unsigned long n) { #ifdef CONFIG_X86_INTEL_USERCOPY diff --git a/arch/x86/mach-default/setup.c b/arch/x86/mach-default/setup.c index 3f2cf11f201..37b9ae4d44c 100644 --- a/arch/x86/mach-default/setup.c +++ b/arch/x86/mach-default/setup.c @@ -38,15 +38,6 @@ void __init pre_intr_init_hook(void) init_ISA_irqs(); } -/* - * IRQ2 is cascade interrupt to second interrupt controller - */ -static struct irqaction irq2 = { - .handler = no_action, - .mask = CPU_MASK_NONE, - .name = "cascade", -}; - /** * intr_init_hook - post gate setup interrupt initialisation * @@ -62,12 +53,6 @@ void __init intr_init_hook(void) if (x86_quirks->arch_intr_init()) return; } -#ifdef CONFIG_X86_LOCAL_APIC - apic_intr_init(); -#endif - - if (!acpi_ioapic) - setup_irq(2, &irq2); } /** diff --git a/arch/x86/mach-es7000/Makefile b/arch/x86/mach-es7000/Makefile deleted file mode 100644 index 3ef8b43b62f..00000000000 --- a/arch/x86/mach-es7000/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -# -# Makefile for the linux kernel. -# - -obj-$(CONFIG_X86_ES7000) := es7000plat.o diff --git a/arch/x86/mach-es7000/es7000.h b/arch/x86/mach-es7000/es7000.h deleted file mode 100644 index c8d5aa132fa..00000000000 --- a/arch/x86/mach-es7000/es7000.h +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Written by: Garry Forsgren, Unisys Corporation - * Natalie Protasevich, Unisys Corporation - * This file contains the code to configure and interface - * with Unisys ES7000 series hardware system manager. - * - * Copyright (c) 2003 Unisys Corporation. All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it would be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write the Free Software Foundation, Inc., 59 - * Temple Place - Suite 330, Boston MA 02111-1307, USA. - * - * Contact information: Unisys Corporation, Township Line & Union Meeting - * Roads-A, Unisys Way, Blue Bell, Pennsylvania, 19424, or: - * - * http://www.unisys.com - */ - -/* - * ES7000 chipsets - */ - -#define NON_UNISYS 0 -#define ES7000_CLASSIC 1 -#define ES7000_ZORRO 2 - - -#define MIP_REG 1 -#define MIP_PSAI_REG 4 - -#define MIP_BUSY 1 -#define MIP_SPIN 0xf0000 -#define MIP_VALID 0x0100000000000000ULL -#define MIP_PORT(VALUE) ((VALUE >> 32) & 0xffff) - -#define MIP_RD_LO(VALUE) (VALUE & 0xffffffff) - -struct mip_reg_info { - unsigned long long mip_info; - unsigned long long delivery_info; - unsigned long long host_reg; - unsigned long long mip_reg; -}; - -struct part_info { - unsigned char type; - unsigned char length; - unsigned char part_id; - unsigned char apic_mode; - unsigned long snum; - char ptype[16]; - char sname[64]; - char pname[64]; -}; - -struct psai { - unsigned long long entry_type; - unsigned long long addr; - unsigned long long bep_addr; -}; - -struct es7000_mem_info { - unsigned char type; - unsigned char length; - unsigned char resv[6]; - unsigned long long start; - unsigned long long size; -}; - -struct es7000_oem_table { - unsigned long long hdr; - struct mip_reg_info mip; - struct part_info pif; - struct es7000_mem_info shm; - struct psai psai; -}; - -#ifdef CONFIG_ACPI - -struct oem_table { - struct acpi_table_header Header; - u32 OEMTableAddr; - u32 OEMTableSize; -}; - -extern int find_unisys_acpi_oem_table(unsigned long *oem_addr); -#endif - -struct mip_reg { - unsigned long long off_0; - unsigned long long off_8; - unsigned long long off_10; - unsigned long long off_18; - unsigned long long off_20; - unsigned long long off_28; - unsigned long long off_30; - unsigned long long off_38; -}; - -#define MIP_SW_APIC 0x1020b -#define MIP_FUNC(VALUE) (VALUE & 0xff) - -extern int parse_unisys_oem (char *oemptr); -extern void setup_unisys(void); -extern int es7000_start_cpu(int cpu, unsigned long eip); -extern void es7000_sw_apic(void); diff --git a/arch/x86/mach-generic/Makefile b/arch/x86/mach-generic/Makefile index 0dbd7803a1d..6730f4e7c74 100644 --- a/arch/x86/mach-generic/Makefile +++ b/arch/x86/mach-generic/Makefile @@ -9,4 +9,3 @@ obj-$(CONFIG_X86_NUMAQ) += numaq.o obj-$(CONFIG_X86_SUMMIT) += summit.o obj-$(CONFIG_X86_BIGSMP) += bigsmp.o obj-$(CONFIG_X86_ES7000) += es7000.o -obj-$(CONFIG_X86_ES7000) += ../../x86/mach-es7000/ diff --git a/arch/x86/mach-generic/bigsmp.c b/arch/x86/mach-generic/bigsmp.c index 59d77171455..df37fc9d6a2 100644 --- a/arch/x86/mach-generic/bigsmp.c +++ b/arch/x86/mach-generic/bigsmp.c @@ -5,18 +5,17 @@ #define APIC_DEFINITION 1 #include <linux/threads.h> #include <linux/cpumask.h> -#include <asm/smp.h> #include <asm/mpspec.h> #include <asm/genapic.h> #include <asm/fixmap.h> #include <asm/apicdef.h> #include <linux/kernel.h> -#include <linux/smp.h> #include <linux/init.h> #include <linux/dmi.h> -#include <asm/mach-bigsmp/mach_apic.h> -#include <asm/mach-bigsmp/mach_apicdef.h> -#include <asm/mach-bigsmp/mach_ipi.h> +#include <asm/bigsmp/apicdef.h> +#include <linux/smp.h> +#include <asm/bigsmp/apic.h> +#include <asm/bigsmp/ipi.h> #include <asm/mach-default/mach_mpparse.h> static int dmi_bigsmp; /* can be set by dmi scanners */ diff --git a/arch/x86/mach-generic/es7000.c b/arch/x86/mach-generic/es7000.c index 4742626f08c..6513d41ea21 100644 --- a/arch/x86/mach-generic/es7000.c +++ b/arch/x86/mach-generic/es7000.c @@ -4,20 +4,19 @@ #define APIC_DEFINITION 1 #include <linux/threads.h> #include <linux/cpumask.h> -#include <asm/smp.h> #include <asm/mpspec.h> #include <asm/genapic.h> #include <asm/fixmap.h> #include <asm/apicdef.h> #include <linux/kernel.h> #include <linux/string.h> -#include <linux/smp.h> #include <linux/init.h> -#include <asm/mach-es7000/mach_apicdef.h> -#include <asm/mach-es7000/mach_apic.h> -#include <asm/mach-es7000/mach_ipi.h> -#include <asm/mach-es7000/mach_mpparse.h> -#include <asm/mach-es7000/mach_wakecpu.h> +#include <asm/es7000/apicdef.h> +#include <linux/smp.h> +#include <asm/es7000/apic.h> +#include <asm/es7000/ipi.h> +#include <asm/es7000/mpparse.h> +#include <asm/es7000/wakecpu.h> static int probe_es7000(void) { @@ -48,16 +47,26 @@ static __init int mps_oem_check(struct mp_config_table *mpc, char *oem, /* Hook from generic ACPI tables.c */ static int __init acpi_madt_oem_check(char *oem_id, char *oem_table_id) { - unsigned long oem_addr; + unsigned long oem_addr = 0; + int check_dsdt; + int ret = 0; + + /* check dsdt at first to avoid clear fix_map for oem_addr */ + check_dsdt = es7000_check_dsdt(); + if (!find_unisys_acpi_oem_table(&oem_addr)) { - if (es7000_check_dsdt()) - return parse_unisys_oem((char *)oem_addr); + if (check_dsdt) + ret = parse_unisys_oem((char *)oem_addr); else { setup_unisys(); - return 1; + ret = 1; } + /* + * we need to unmap it + */ + unmap_unisys_acpi_oem_table(oem_addr); } - return 0; + return ret; } #else static int __init acpi_madt_oem_check(char *oem_id, char *oem_table_id) diff --git a/arch/x86/mach-generic/numaq.c b/arch/x86/mach-generic/numaq.c index 8091e68764c..8cf58394975 100644 --- a/arch/x86/mach-generic/numaq.c +++ b/arch/x86/mach-generic/numaq.c @@ -4,7 +4,6 @@ #define APIC_DEFINITION 1 #include <linux/threads.h> #include <linux/cpumask.h> -#include <linux/smp.h> #include <asm/mpspec.h> #include <asm/genapic.h> #include <asm/fixmap.h> @@ -12,11 +11,12 @@ #include <linux/kernel.h> #include <linux/string.h> #include <linux/init.h> -#include <asm/mach-numaq/mach_apic.h> -#include <asm/mach-numaq/mach_apicdef.h> -#include <asm/mach-numaq/mach_ipi.h> -#include <asm/mach-numaq/mach_mpparse.h> -#include <asm/mach-numaq/mach_wakecpu.h> +#include <asm/numaq/apicdef.h> +#include <linux/smp.h> +#include <asm/numaq/apic.h> +#include <asm/numaq/ipi.h> +#include <asm/numaq/mpparse.h> +#include <asm/numaq/wakecpu.h> #include <asm/numaq.h> static int mps_oem_check(struct mp_config_table *mpc, char *oem, diff --git a/arch/x86/mach-generic/summit.c b/arch/x86/mach-generic/summit.c index a97ea0f35b1..6ad6b67a723 100644 --- a/arch/x86/mach-generic/summit.c +++ b/arch/x86/mach-generic/summit.c @@ -4,19 +4,18 @@ #define APIC_DEFINITION 1 #include <linux/threads.h> #include <linux/cpumask.h> -#include <asm/smp.h> #include <asm/mpspec.h> #include <asm/genapic.h> #include <asm/fixmap.h> #include <asm/apicdef.h> #include <linux/kernel.h> #include <linux/string.h> -#include <linux/smp.h> #include <linux/init.h> -#include <asm/mach-summit/mach_apic.h> -#include <asm/mach-summit/mach_apicdef.h> -#include <asm/mach-summit/mach_ipi.h> -#include <asm/mach-summit/mach_mpparse.h> +#include <asm/summit/apicdef.h> +#include <linux/smp.h> +#include <asm/summit/apic.h> +#include <asm/summit/ipi.h> +#include <asm/summit/mpparse.h> static int probe_summit(void) { diff --git a/arch/x86/mm/Makefile b/arch/x86/mm/Makefile index dfb932dcf13..59f89b434b4 100644 --- a/arch/x86/mm/Makefile +++ b/arch/x86/mm/Makefile @@ -13,12 +13,8 @@ obj-$(CONFIG_MMIOTRACE) += mmiotrace.o mmiotrace-y := pf_in.o mmio-mod.o obj-$(CONFIG_MMIOTRACE_TEST) += testmmiotrace.o -ifeq ($(CONFIG_X86_32),y) -obj-$(CONFIG_NUMA) += discontig_32.o -else -obj-$(CONFIG_NUMA) += numa_64.o +obj-$(CONFIG_NUMA) += numa_$(BITS).o obj-$(CONFIG_K8_NUMA) += k8topology_64.o -endif obj-$(CONFIG_ACPI_NUMA) += srat_$(BITS).o obj-$(CONFIG_MEMTEST) += memtest.o diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c index 8f92cac4e6d..3f2b8962cbd 100644 --- a/arch/x86/mm/fault.c +++ b/arch/x86/mm/fault.c @@ -592,11 +592,6 @@ void __kprobes do_page_fault(struct pt_regs *regs, unsigned long error_code) unsigned long flags; #endif - /* - * We can fault from pretty much anywhere, with unknown IRQ state. - */ - trace_hardirqs_fixup(); - tsk = current; mm = tsk->mm; prefetchw(&mm->mmap_sem); @@ -914,15 +909,15 @@ LIST_HEAD(pgd_list); void vmalloc_sync_all(void) { -#ifdef CONFIG_X86_32 - unsigned long start = VMALLOC_START & PGDIR_MASK; unsigned long address; +#ifdef CONFIG_X86_32 if (SHARED_KERNEL_PMD) return; - BUILD_BUG_ON(TASK_SIZE & ~PGDIR_MASK); - for (address = start; address >= TASK_SIZE; address += PGDIR_SIZE) { + for (address = VMALLOC_START & PMD_MASK; + address >= TASK_SIZE && address < FIXADDR_TOP; + address += PMD_SIZE) { unsigned long flags; struct page *page; @@ -935,10 +930,8 @@ void vmalloc_sync_all(void) spin_unlock_irqrestore(&pgd_lock, flags); } #else /* CONFIG_X86_64 */ - unsigned long start = VMALLOC_START & PGDIR_MASK; - unsigned long address; - - for (address = start; address <= VMALLOC_END; address += PGDIR_SIZE) { + for (address = VMALLOC_START & PGDIR_MASK; address <= VMALLOC_END; + address += PGDIR_SIZE) { const pgd_t *pgd_ref = pgd_offset_k(address); unsigned long flags; struct page *page; diff --git a/arch/x86/mm/gup.c b/arch/x86/mm/gup.c index 007bb06c750..4ba373c5b8c 100644 --- a/arch/x86/mm/gup.c +++ b/arch/x86/mm/gup.c @@ -82,7 +82,7 @@ static noinline int gup_pte_range(pmd_t pmd, unsigned long addr, pte_t pte = gup_get_pte(ptep); struct page *page; - if ((pte_val(pte) & (mask | _PAGE_SPECIAL)) != mask) { + if ((pte_flags(pte) & (mask | _PAGE_SPECIAL)) != mask) { pte_unmap(ptep); return 0; } @@ -116,10 +116,10 @@ static noinline int gup_huge_pmd(pmd_t pmd, unsigned long addr, mask = _PAGE_PRESENT|_PAGE_USER; if (write) mask |= _PAGE_RW; - if ((pte_val(pte) & mask) != mask) + if ((pte_flags(pte) & mask) != mask) return 0; /* hugepages are never "special" */ - VM_BUG_ON(pte_val(pte) & _PAGE_SPECIAL); + VM_BUG_ON(pte_flags(pte) & _PAGE_SPECIAL); VM_BUG_ON(!pfn_valid(pte_pfn(pte))); refs = 0; @@ -173,10 +173,10 @@ static noinline int gup_huge_pud(pud_t pud, unsigned long addr, mask = _PAGE_PRESENT|_PAGE_USER; if (write) mask |= _PAGE_RW; - if ((pte_val(pte) & mask) != mask) + if ((pte_flags(pte) & mask) != mask) return 0; /* hugepages are never "special" */ - VM_BUG_ON(pte_val(pte) & _PAGE_SPECIAL); + VM_BUG_ON(pte_flags(pte) & _PAGE_SPECIAL); VM_BUG_ON(!pfn_valid(pte_pfn(pte))); refs = 0; diff --git a/arch/x86/mm/init_32.c b/arch/x86/mm/init_32.c index c3789bb1930..8396868e82c 100644 --- a/arch/x86/mm/init_32.c +++ b/arch/x86/mm/init_32.c @@ -31,6 +31,7 @@ #include <linux/cpumask.h> #include <asm/asm.h> +#include <asm/bios_ebda.h> #include <asm/processor.h> #include <asm/system.h> #include <asm/uaccess.h> @@ -557,7 +558,7 @@ void zap_low_mappings(void) int nx_enabled; -pteval_t __supported_pte_mask __read_mostly = ~(_PAGE_NX | _PAGE_GLOBAL); +pteval_t __supported_pte_mask __read_mostly = ~(_PAGE_NX | _PAGE_GLOBAL | _PAGE_IOMAP); EXPORT_SYMBOL_GPL(__supported_pte_mask); #ifdef CONFIG_X86_PAE @@ -969,6 +970,8 @@ void __init mem_init(void) int codesize, reservedpages, datasize, initsize; int tmp; + start_periodic_check_for_corruption(); + #ifdef CONFIG_FLATMEM BUG_ON(!mem_map); #endif diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c index fb30486c82f..b8e461d4941 100644 --- a/arch/x86/mm/init_64.c +++ b/arch/x86/mm/init_64.c @@ -31,6 +31,7 @@ #include <linux/nmi.h> #include <asm/processor.h> +#include <asm/bios_ebda.h> #include <asm/system.h> #include <asm/uaccess.h> #include <asm/pgtable.h> @@ -88,6 +89,62 @@ early_param("gbpages", parse_direct_gbpages_on); int after_bootmem; +pteval_t __supported_pte_mask __read_mostly = ~_PAGE_IOMAP; +EXPORT_SYMBOL_GPL(__supported_pte_mask); + +static int do_not_nx __cpuinitdata; + +/* + * noexec=on|off + * Control non-executable mappings for 64-bit processes. + * + * on Enable (default) + * off Disable + */ +static int __init nonx_setup(char *str) +{ + if (!str) + return -EINVAL; + if (!strncmp(str, "on", 2)) { + __supported_pte_mask |= _PAGE_NX; + do_not_nx = 0; + } else if (!strncmp(str, "off", 3)) { + do_not_nx = 1; + __supported_pte_mask &= ~_PAGE_NX; + } + return 0; +} +early_param("noexec", nonx_setup); + +void __cpuinit check_efer(void) +{ + unsigned long efer; + + rdmsrl(MSR_EFER, efer); + if (!(efer & EFER_NX) || do_not_nx) + __supported_pte_mask &= ~_PAGE_NX; +} + +int force_personality32; + +/* + * noexec32=on|off + * Control non executable heap for 32bit processes. + * To control the stack too use noexec=off + * + * on PROT_READ does not imply PROT_EXEC for 32-bit processes (default) + * off PROT_READ implies PROT_EXEC + */ +static int __init nonx32_setup(char *str) +{ + if (!strcmp(str, "on")) + force_personality32 &= ~READ_IMPLIES_EXEC; + else if (!strcmp(str, "off")) + force_personality32 |= READ_IMPLIES_EXEC; + return 1; +} +__setup("noexec32=", nonx32_setup); + /* * NOTE: This function is marked __ref because it calls __init function * (alloc_bootmem_pages). It's safe to do it ONLY when after_bootmem == 0. @@ -139,9 +196,6 @@ set_pte_vaddr_pud(pud_t *pud_page, unsigned long vaddr, pte_t new_pte) } pte = pte_offset_kernel(pmd, vaddr); - if (!pte_none(*pte) && pte_val(new_pte) && - pte_val(*pte) != (pte_val(new_pte) & __supported_pte_mask)) - pte_ERROR(*pte); set_pte(pte, new_pte); /* @@ -256,7 +310,7 @@ static __ref void *alloc_low_page(unsigned long *phys) if (pfn >= table_top) panic("alloc_low_page: ran out of memory"); - adr = early_ioremap(pfn * PAGE_SIZE, PAGE_SIZE); + adr = early_memremap(pfn * PAGE_SIZE, PAGE_SIZE); memset(adr, 0, PAGE_SIZE); *phys = pfn * PAGE_SIZE; return adr; @@ -692,7 +746,7 @@ unsigned long __init_refok init_memory_mapping(unsigned long start, old_start = mr[i].start; memmove(&mr[i], &mr[i+1], (nr_range - 1 - i) * sizeof (struct map_range)); - mr[i].start = old_start; + mr[i--].start = old_start; nr_range--; } @@ -825,6 +879,8 @@ void __init mem_init(void) { long codesize, reservedpages, datasize, initsize; + start_periodic_check_for_corruption(); + pci_iommu_alloc(); /* clear_bss() already clear the empty_zero_page */ diff --git a/arch/x86/mm/ioremap.c b/arch/x86/mm/ioremap.c index 6ab3196d12b..e4c43ec71b2 100644 --- a/arch/x86/mm/ioremap.c +++ b/arch/x86/mm/ioremap.c @@ -24,18 +24,47 @@ #ifdef CONFIG_X86_64 +static inline int phys_addr_valid(unsigned long addr) +{ + return addr < (1UL << boot_cpu_data.x86_phys_bits); +} + unsigned long __phys_addr(unsigned long x) { - if (x >= __START_KERNEL_map) - return x - __START_KERNEL_map + phys_base; - return x - PAGE_OFFSET; + if (x >= __START_KERNEL_map) { + x -= __START_KERNEL_map; + VIRTUAL_BUG_ON(x >= KERNEL_IMAGE_SIZE); + x += phys_base; + } else { + VIRTUAL_BUG_ON(x < PAGE_OFFSET); + x -= PAGE_OFFSET; + VIRTUAL_BUG_ON(system_state == SYSTEM_BOOTING ? x > MAXMEM : + !phys_addr_valid(x)); + } + return x; } EXPORT_SYMBOL(__phys_addr); -static inline int phys_addr_valid(unsigned long addr) +bool __virt_addr_valid(unsigned long x) { - return addr < (1UL << boot_cpu_data.x86_phys_bits); + if (x >= __START_KERNEL_map) { + x -= __START_KERNEL_map; + if (x >= KERNEL_IMAGE_SIZE) + return false; + x += phys_base; + } else { + if (x < PAGE_OFFSET) + return false; + x -= PAGE_OFFSET; + if (system_state == SYSTEM_BOOTING ? + x > MAXMEM : !phys_addr_valid(x)) { + return false; + } + } + + return pfn_valid(x >> PAGE_SHIFT); } +EXPORT_SYMBOL(__virt_addr_valid); #else @@ -44,6 +73,28 @@ static inline int phys_addr_valid(unsigned long addr) return 1; } +#ifdef CONFIG_DEBUG_VIRTUAL +unsigned long __phys_addr(unsigned long x) +{ + /* VMALLOC_* aren't constants; not available at the boot time */ + VIRTUAL_BUG_ON(x < PAGE_OFFSET); + VIRTUAL_BUG_ON(system_state != SYSTEM_BOOTING && + is_vmalloc_addr((void *) x)); + return x - PAGE_OFFSET; +} +EXPORT_SYMBOL(__phys_addr); +#endif + +bool __virt_addr_valid(unsigned long x) +{ + if (x < PAGE_OFFSET) + return false; + if (system_state != SYSTEM_BOOTING && is_vmalloc_addr((void *) x)) + return false; + return pfn_valid((x - PAGE_OFFSET) >> PAGE_SHIFT); +} +EXPORT_SYMBOL(__virt_addr_valid); + #endif int page_is_ram(unsigned long pagenr) @@ -223,16 +274,16 @@ static void __iomem *__ioremap_caller(resource_size_t phys_addr, switch (prot_val) { case _PAGE_CACHE_UC: default: - prot = PAGE_KERNEL_NOCACHE; + prot = PAGE_KERNEL_IO_NOCACHE; break; case _PAGE_CACHE_UC_MINUS: - prot = PAGE_KERNEL_UC_MINUS; + prot = PAGE_KERNEL_IO_UC_MINUS; break; case _PAGE_CACHE_WC: - prot = PAGE_KERNEL_WC; + prot = PAGE_KERNEL_IO_WC; break; case _PAGE_CACHE_WB: - prot = PAGE_KERNEL; + prot = PAGE_KERNEL_IO; break; } @@ -549,12 +600,12 @@ static void __init __early_set_fixmap(enum fixed_addresses idx, } static inline void __init early_set_fixmap(enum fixed_addresses idx, - unsigned long phys) + unsigned long phys, pgprot_t prot) { if (after_paging_init) - set_fixmap(idx, phys); + __set_fixmap(idx, phys, prot); else - __early_set_fixmap(idx, phys, PAGE_KERNEL); + __early_set_fixmap(idx, phys, prot); } static inline void __init early_clear_fixmap(enum fixed_addresses idx) @@ -565,16 +616,22 @@ static inline void __init early_clear_fixmap(enum fixed_addresses idx) __early_set_fixmap(idx, 0, __pgprot(0)); } - -static int __initdata early_ioremap_nested; - +static void *prev_map[FIX_BTMAPS_SLOTS] __initdata; +static unsigned long prev_size[FIX_BTMAPS_SLOTS] __initdata; static int __init check_early_ioremap_leak(void) { - if (!early_ioremap_nested) + int count = 0; + int i; + + for (i = 0; i < FIX_BTMAPS_SLOTS; i++) + if (prev_map[i]) + count++; + + if (!count) return 0; WARN(1, KERN_WARNING "Debug warning: early ioremap leak of %d areas detected.\n", - early_ioremap_nested); + count); printk(KERN_WARNING "please boot with early_ioremap_debug and report the dmesg.\n"); @@ -582,18 +639,33 @@ static int __init check_early_ioremap_leak(void) } late_initcall(check_early_ioremap_leak); -void __init *early_ioremap(unsigned long phys_addr, unsigned long size) +static void __init *__early_ioremap(unsigned long phys_addr, unsigned long size, pgprot_t prot) { unsigned long offset, last_addr; - unsigned int nrpages, nesting; + unsigned int nrpages; enum fixed_addresses idx0, idx; + int i, slot; WARN_ON(system_state != SYSTEM_BOOTING); - nesting = early_ioremap_nested; + slot = -1; + for (i = 0; i < FIX_BTMAPS_SLOTS; i++) { + if (!prev_map[i]) { + slot = i; + break; + } + } + + if (slot < 0) { + printk(KERN_INFO "early_iomap(%08lx, %08lx) not found slot\n", + phys_addr, size); + WARN_ON(1); + return NULL; + } + if (early_ioremap_debug) { printk(KERN_INFO "early_ioremap(%08lx, %08lx) [%d] => ", - phys_addr, size, nesting); + phys_addr, size, slot); dump_stack(); } @@ -604,17 +676,13 @@ void __init *early_ioremap(unsigned long phys_addr, unsigned long size) return NULL; } - if (nesting >= FIX_BTMAPS_NESTING) { - WARN_ON(1); - return NULL; - } - early_ioremap_nested++; + prev_size[slot] = size; /* * Mappings have to be page-aligned */ offset = phys_addr & ~PAGE_MASK; phys_addr &= PAGE_MASK; - size = PAGE_ALIGN(last_addr) - phys_addr; + size = PAGE_ALIGN(last_addr + 1) - phys_addr; /* * Mappings have to fit in the FIX_BTMAP area. @@ -628,10 +696,10 @@ void __init *early_ioremap(unsigned long phys_addr, unsigned long size) /* * Ok, go for it.. */ - idx0 = FIX_BTMAP_BEGIN - NR_FIX_BTMAPS*nesting; + idx0 = FIX_BTMAP_BEGIN - NR_FIX_BTMAPS*slot; idx = idx0; while (nrpages > 0) { - early_set_fixmap(idx, phys_addr); + early_set_fixmap(idx, phys_addr, prot); phys_addr += PAGE_SIZE; --idx; --nrpages; @@ -639,7 +707,20 @@ void __init *early_ioremap(unsigned long phys_addr, unsigned long size) if (early_ioremap_debug) printk(KERN_CONT "%08lx + %08lx\n", offset, fix_to_virt(idx0)); - return (void *) (offset + fix_to_virt(idx0)); + prev_map[slot] = (void *) (offset + fix_to_virt(idx0)); + return prev_map[slot]; +} + +/* Remap an IO device */ +void __init *early_ioremap(unsigned long phys_addr, unsigned long size) +{ + return __early_ioremap(phys_addr, size, PAGE_KERNEL_IO); +} + +/* Remap memory */ +void __init *early_memremap(unsigned long phys_addr, unsigned long size) +{ + return __early_ioremap(phys_addr, size, PAGE_KERNEL); } void __init early_iounmap(void *addr, unsigned long size) @@ -648,15 +729,33 @@ void __init early_iounmap(void *addr, unsigned long size) unsigned long offset; unsigned int nrpages; enum fixed_addresses idx; - int nesting; + int i, slot; + + slot = -1; + for (i = 0; i < FIX_BTMAPS_SLOTS; i++) { + if (prev_map[i] == addr) { + slot = i; + break; + } + } - nesting = --early_ioremap_nested; - if (WARN_ON(nesting < 0)) + if (slot < 0) { + printk(KERN_INFO "early_iounmap(%p, %08lx) not found slot\n", + addr, size); + WARN_ON(1); return; + } + + if (prev_size[slot] != size) { + printk(KERN_INFO "early_iounmap(%p, %08lx) [%d] size not consistent %08lx\n", + addr, size, slot, prev_size[slot]); + WARN_ON(1); + return; + } if (early_ioremap_debug) { printk(KERN_INFO "early_iounmap(%p, %08lx) [%d]\n", addr, - size, nesting); + size, slot); dump_stack(); } @@ -668,12 +767,13 @@ void __init early_iounmap(void *addr, unsigned long size) offset = virt_addr & ~PAGE_MASK; nrpages = PAGE_ALIGN(offset + size - 1) >> PAGE_SHIFT; - idx = FIX_BTMAP_BEGIN - NR_FIX_BTMAPS*nesting; + idx = FIX_BTMAP_BEGIN - NR_FIX_BTMAPS*slot; while (nrpages > 0) { early_clear_fixmap(idx); --idx; --nrpages; } + prev_map[slot] = 0; } void __this_fixmap_does_not_exist(void) diff --git a/arch/x86/mm/discontig_32.c b/arch/x86/mm/numa_32.c index 847c164725f..847c164725f 100644 --- a/arch/x86/mm/discontig_32.c +++ b/arch/x86/mm/numa_32.c diff --git a/arch/x86/mm/srat_64.c b/arch/x86/mm/srat_64.c index 1b4763e26ea..51c0a2fc14f 100644 --- a/arch/x86/mm/srat_64.c +++ b/arch/x86/mm/srat_64.c @@ -138,7 +138,7 @@ acpi_numa_processor_affinity_init(struct acpi_srat_cpu_affinity *pa) return; } - if (is_uv_system()) + if (get_uv_system_type() >= UV_X2APIC) apic_id = (pa->apic_id << 8) | pa->local_sapic_eid; else apic_id = pa->apic_id; diff --git a/arch/x86/oprofile/Makefile b/arch/x86/oprofile/Makefile index 30f3eb36666..446902b2a6b 100644 --- a/arch/x86/oprofile/Makefile +++ b/arch/x86/oprofile/Makefile @@ -7,6 +7,6 @@ DRIVER_OBJS = $(addprefix ../../../drivers/oprofile/, \ timer_int.o ) oprofile-y := $(DRIVER_OBJS) init.o backtrace.o -oprofile-$(CONFIG_X86_LOCAL_APIC) += nmi_int.o op_model_athlon.o \ +oprofile-$(CONFIG_X86_LOCAL_APIC) += nmi_int.o op_model_amd.o \ op_model_ppro.o op_model_p4.o oprofile-$(CONFIG_X86_IO_APIC) += nmi_timer_int.o diff --git a/arch/x86/oprofile/nmi_int.c b/arch/x86/oprofile/nmi_int.c index 8a5f1614a3d..57f6c908808 100644 --- a/arch/x86/oprofile/nmi_int.c +++ b/arch/x86/oprofile/nmi_int.c @@ -1,10 +1,11 @@ /** * @file nmi_int.c * - * @remark Copyright 2002 OProfile authors + * @remark Copyright 2002-2008 OProfile authors * @remark Read the file COPYING * * @author John Levon <levon@movementarian.org> + * @author Robert Richter <robert.richter@amd.com> */ #include <linux/init.h> @@ -439,6 +440,7 @@ int __init op_nmi_init(struct oprofile_operations *ops) __u8 vendor = boot_cpu_data.x86_vendor; __u8 family = boot_cpu_data.x86; char *cpu_type; + int ret = 0; if (!cpu_has_apic) return -ENODEV; @@ -451,19 +453,23 @@ int __init op_nmi_init(struct oprofile_operations *ops) default: return -ENODEV; case 6: - model = &op_athlon_spec; + model = &op_amd_spec; cpu_type = "i386/athlon"; break; case 0xf: - model = &op_athlon_spec; + model = &op_amd_spec; /* Actually it could be i386/hammer too, but give user space an consistent name. */ cpu_type = "x86-64/hammer"; break; case 0x10: - model = &op_athlon_spec; + model = &op_amd_spec; cpu_type = "x86-64/family10"; break; + case 0x11: + model = &op_amd_spec; + cpu_type = "x86-64/family11h"; + break; } break; @@ -490,17 +496,24 @@ int __init op_nmi_init(struct oprofile_operations *ops) return -ENODEV; } - init_sysfs(); #ifdef CONFIG_SMP register_cpu_notifier(&oprofile_cpu_nb); #endif - using_nmi = 1; + /* default values, can be overwritten by model */ ops->create_files = nmi_create_files; ops->setup = nmi_setup; ops->shutdown = nmi_shutdown; ops->start = nmi_start; ops->stop = nmi_stop; ops->cpu_type = cpu_type; + + if (model->init) + ret = model->init(ops); + if (ret) + return ret; + + init_sysfs(); + using_nmi = 1; printk(KERN_INFO "oprofile: using NMI interrupt.\n"); return 0; } @@ -513,4 +526,6 @@ void op_nmi_exit(void) unregister_cpu_notifier(&oprofile_cpu_nb); #endif } + if (model->exit) + model->exit(); } diff --git a/arch/x86/oprofile/op_model_amd.c b/arch/x86/oprofile/op_model_amd.c new file mode 100644 index 00000000000..d9faf607b3a --- /dev/null +++ b/arch/x86/oprofile/op_model_amd.c @@ -0,0 +1,543 @@ +/* + * @file op_model_amd.c + * athlon / K7 / K8 / Family 10h model-specific MSR operations + * + * @remark Copyright 2002-2008 OProfile authors + * @remark Read the file COPYING + * + * @author John Levon + * @author Philippe Elie + * @author Graydon Hoare + * @author Robert Richter <robert.richter@amd.com> + * @author Barry Kasindorf +*/ + +#include <linux/oprofile.h> +#include <linux/device.h> +#include <linux/pci.h> + +#include <asm/ptrace.h> +#include <asm/msr.h> +#include <asm/nmi.h> + +#include "op_x86_model.h" +#include "op_counter.h" + +#define NUM_COUNTERS 4 +#define NUM_CONTROLS 4 + +#define CTR_IS_RESERVED(msrs, c) (msrs->counters[(c)].addr ? 1 : 0) +#define CTR_READ(l, h, msrs, c) do {rdmsr(msrs->counters[(c)].addr, (l), (h)); } while (0) +#define CTR_WRITE(l, msrs, c) do {wrmsr(msrs->counters[(c)].addr, -(unsigned int)(l), -1); } while (0) +#define CTR_OVERFLOWED(n) (!((n) & (1U<<31))) + +#define CTRL_IS_RESERVED(msrs, c) (msrs->controls[(c)].addr ? 1 : 0) +#define CTRL_READ(l, h, msrs, c) do {rdmsr(msrs->controls[(c)].addr, (l), (h)); } while (0) +#define CTRL_WRITE(l, h, msrs, c) do {wrmsr(msrs->controls[(c)].addr, (l), (h)); } while (0) +#define CTRL_SET_ACTIVE(n) (n |= (1<<22)) +#define CTRL_SET_INACTIVE(n) (n &= ~(1<<22)) +#define CTRL_CLEAR_LO(x) (x &= (1<<21)) +#define CTRL_CLEAR_HI(x) (x &= 0xfffffcf0) +#define CTRL_SET_ENABLE(val) (val |= 1<<20) +#define CTRL_SET_USR(val, u) (val |= ((u & 1) << 16)) +#define CTRL_SET_KERN(val, k) (val |= ((k & 1) << 17)) +#define CTRL_SET_UM(val, m) (val |= (m << 8)) +#define CTRL_SET_EVENT_LOW(val, e) (val |= (e & 0xff)) +#define CTRL_SET_EVENT_HIGH(val, e) (val |= ((e >> 8) & 0xf)) +#define CTRL_SET_HOST_ONLY(val, h) (val |= ((h & 1) << 9)) +#define CTRL_SET_GUEST_ONLY(val, h) (val |= ((h & 1) << 8)) + +static unsigned long reset_value[NUM_COUNTERS]; + +#ifdef CONFIG_OPROFILE_IBS + +/* IbsFetchCtl bits/masks */ +#define IBS_FETCH_HIGH_VALID_BIT (1UL << 17) /* bit 49 */ +#define IBS_FETCH_HIGH_ENABLE (1UL << 16) /* bit 48 */ +#define IBS_FETCH_LOW_MAX_CNT_MASK 0x0000FFFFUL /* MaxCnt mask */ + +/*IbsOpCtl bits */ +#define IBS_OP_LOW_VALID_BIT (1ULL<<18) /* bit 18 */ +#define IBS_OP_LOW_ENABLE (1ULL<<17) /* bit 17 */ + +/* Codes used in cpu_buffer.c */ +/* This produces duplicate code, need to be fixed */ +#define IBS_FETCH_BEGIN 3 +#define IBS_OP_BEGIN 4 + +/* The function interface needs to be fixed, something like add + data. Should then be added to linux/oprofile.h. */ +extern void oprofile_add_ibs_sample(struct pt_regs *const regs, + unsigned int * const ibs_sample, u8 code); + +struct ibs_fetch_sample { + /* MSRC001_1031 IBS Fetch Linear Address Register */ + unsigned int ibs_fetch_lin_addr_low; + unsigned int ibs_fetch_lin_addr_high; + /* MSRC001_1030 IBS Fetch Control Register */ + unsigned int ibs_fetch_ctl_low; + unsigned int ibs_fetch_ctl_high; + /* MSRC001_1032 IBS Fetch Physical Address Register */ + unsigned int ibs_fetch_phys_addr_low; + unsigned int ibs_fetch_phys_addr_high; +}; + +struct ibs_op_sample { + /* MSRC001_1034 IBS Op Logical Address Register (IbsRIP) */ + unsigned int ibs_op_rip_low; + unsigned int ibs_op_rip_high; + /* MSRC001_1035 IBS Op Data Register */ + unsigned int ibs_op_data1_low; + unsigned int ibs_op_data1_high; + /* MSRC001_1036 IBS Op Data 2 Register */ + unsigned int ibs_op_data2_low; + unsigned int ibs_op_data2_high; + /* MSRC001_1037 IBS Op Data 3 Register */ + unsigned int ibs_op_data3_low; + unsigned int ibs_op_data3_high; + /* MSRC001_1038 IBS DC Linear Address Register (IbsDcLinAd) */ + unsigned int ibs_dc_linear_low; + unsigned int ibs_dc_linear_high; + /* MSRC001_1039 IBS DC Physical Address Register (IbsDcPhysAd) */ + unsigned int ibs_dc_phys_low; + unsigned int ibs_dc_phys_high; +}; + +/* + * unitialize the APIC for the IBS interrupts if needed on AMD Family10h+ +*/ +static void clear_ibs_nmi(void); + +static int ibs_allowed; /* AMD Family10h and later */ + +struct op_ibs_config { + unsigned long op_enabled; + unsigned long fetch_enabled; + unsigned long max_cnt_fetch; + unsigned long max_cnt_op; + unsigned long rand_en; + unsigned long dispatched_ops; +}; + +static struct op_ibs_config ibs_config; + +#endif + +/* functions for op_amd_spec */ + +static void op_amd_fill_in_addresses(struct op_msrs * const msrs) +{ + int i; + + for (i = 0; i < NUM_COUNTERS; i++) { + if (reserve_perfctr_nmi(MSR_K7_PERFCTR0 + i)) + msrs->counters[i].addr = MSR_K7_PERFCTR0 + i; + else + msrs->counters[i].addr = 0; + } + + for (i = 0; i < NUM_CONTROLS; i++) { + if (reserve_evntsel_nmi(MSR_K7_EVNTSEL0 + i)) + msrs->controls[i].addr = MSR_K7_EVNTSEL0 + i; + else + msrs->controls[i].addr = 0; + } +} + + +static void op_amd_setup_ctrs(struct op_msrs const * const msrs) +{ + unsigned int low, high; + int i; + + /* clear all counters */ + for (i = 0 ; i < NUM_CONTROLS; ++i) { + if (unlikely(!CTRL_IS_RESERVED(msrs, i))) + continue; + CTRL_READ(low, high, msrs, i); + CTRL_CLEAR_LO(low); + CTRL_CLEAR_HI(high); + CTRL_WRITE(low, high, msrs, i); + } + + /* avoid a false detection of ctr overflows in NMI handler */ + for (i = 0; i < NUM_COUNTERS; ++i) { + if (unlikely(!CTR_IS_RESERVED(msrs, i))) + continue; + CTR_WRITE(1, msrs, i); + } + + /* enable active counters */ + for (i = 0; i < NUM_COUNTERS; ++i) { + if ((counter_config[i].enabled) && (CTR_IS_RESERVED(msrs, i))) { + reset_value[i] = counter_config[i].count; + + CTR_WRITE(counter_config[i].count, msrs, i); + + CTRL_READ(low, high, msrs, i); + CTRL_CLEAR_LO(low); + CTRL_CLEAR_HI(high); + CTRL_SET_ENABLE(low); + CTRL_SET_USR(low, counter_config[i].user); + CTRL_SET_KERN(low, counter_config[i].kernel); + CTRL_SET_UM(low, counter_config[i].unit_mask); + CTRL_SET_EVENT_LOW(low, counter_config[i].event); + CTRL_SET_EVENT_HIGH(high, counter_config[i].event); + CTRL_SET_HOST_ONLY(high, 0); + CTRL_SET_GUEST_ONLY(high, 0); + + CTRL_WRITE(low, high, msrs, i); + } else { + reset_value[i] = 0; + } + } +} + +#ifdef CONFIG_OPROFILE_IBS + +static inline int +op_amd_handle_ibs(struct pt_regs * const regs, + struct op_msrs const * const msrs) +{ + unsigned int low, high; + struct ibs_fetch_sample ibs_fetch; + struct ibs_op_sample ibs_op; + + if (!ibs_allowed) + return 1; + + if (ibs_config.fetch_enabled) { + rdmsr(MSR_AMD64_IBSFETCHCTL, low, high); + if (high & IBS_FETCH_HIGH_VALID_BIT) { + ibs_fetch.ibs_fetch_ctl_high = high; + ibs_fetch.ibs_fetch_ctl_low = low; + rdmsr(MSR_AMD64_IBSFETCHLINAD, low, high); + ibs_fetch.ibs_fetch_lin_addr_high = high; + ibs_fetch.ibs_fetch_lin_addr_low = low; + rdmsr(MSR_AMD64_IBSFETCHPHYSAD, low, high); + ibs_fetch.ibs_fetch_phys_addr_high = high; + ibs_fetch.ibs_fetch_phys_addr_low = low; + + oprofile_add_ibs_sample(regs, + (unsigned int *)&ibs_fetch, + IBS_FETCH_BEGIN); + + /*reenable the IRQ */ + rdmsr(MSR_AMD64_IBSFETCHCTL, low, high); + high &= ~IBS_FETCH_HIGH_VALID_BIT; + high |= IBS_FETCH_HIGH_ENABLE; + low &= IBS_FETCH_LOW_MAX_CNT_MASK; + wrmsr(MSR_AMD64_IBSFETCHCTL, low, high); + } + } + + if (ibs_config.op_enabled) { + rdmsr(MSR_AMD64_IBSOPCTL, low, high); + if (low & IBS_OP_LOW_VALID_BIT) { + rdmsr(MSR_AMD64_IBSOPRIP, low, high); + ibs_op.ibs_op_rip_low = low; + ibs_op.ibs_op_rip_high = high; + rdmsr(MSR_AMD64_IBSOPDATA, low, high); + ibs_op.ibs_op_data1_low = low; + ibs_op.ibs_op_data1_high = high; + rdmsr(MSR_AMD64_IBSOPDATA2, low, high); + ibs_op.ibs_op_data2_low = low; + ibs_op.ibs_op_data2_high = high; + rdmsr(MSR_AMD64_IBSOPDATA3, low, high); + ibs_op.ibs_op_data3_low = low; + ibs_op.ibs_op_data3_high = high; + rdmsr(MSR_AMD64_IBSDCLINAD, low, high); + ibs_op.ibs_dc_linear_low = low; + ibs_op.ibs_dc_linear_high = high; + rdmsr(MSR_AMD64_IBSDCPHYSAD, low, high); + ibs_op.ibs_dc_phys_low = low; + ibs_op.ibs_dc_phys_high = high; + + /* reenable the IRQ */ + oprofile_add_ibs_sample(regs, + (unsigned int *)&ibs_op, + IBS_OP_BEGIN); + rdmsr(MSR_AMD64_IBSOPCTL, low, high); + high = 0; + low &= ~IBS_OP_LOW_VALID_BIT; + low |= IBS_OP_LOW_ENABLE; + wrmsr(MSR_AMD64_IBSOPCTL, low, high); + } + } + + return 1; +} + +#endif + +static int op_amd_check_ctrs(struct pt_regs * const regs, + struct op_msrs const * const msrs) +{ + unsigned int low, high; + int i; + + for (i = 0 ; i < NUM_COUNTERS; ++i) { + if (!reset_value[i]) + continue; + CTR_READ(low, high, msrs, i); + if (CTR_OVERFLOWED(low)) { + oprofile_add_sample(regs, i); + CTR_WRITE(reset_value[i], msrs, i); + } + } + +#ifdef CONFIG_OPROFILE_IBS + op_amd_handle_ibs(regs, msrs); +#endif + + /* See op_model_ppro.c */ + return 1; +} + +static void op_amd_start(struct op_msrs const * const msrs) +{ + unsigned int low, high; + int i; + for (i = 0 ; i < NUM_COUNTERS ; ++i) { + if (reset_value[i]) { + CTRL_READ(low, high, msrs, i); + CTRL_SET_ACTIVE(low); + CTRL_WRITE(low, high, msrs, i); + } + } + +#ifdef CONFIG_OPROFILE_IBS + if (ibs_allowed && ibs_config.fetch_enabled) { + low = (ibs_config.max_cnt_fetch >> 4) & 0xFFFF; + high = IBS_FETCH_HIGH_ENABLE; + wrmsr(MSR_AMD64_IBSFETCHCTL, low, high); + } + + if (ibs_allowed && ibs_config.op_enabled) { + low = ((ibs_config.max_cnt_op >> 4) & 0xFFFF) + IBS_OP_LOW_ENABLE; + high = 0; + wrmsr(MSR_AMD64_IBSOPCTL, low, high); + } +#endif +} + + +static void op_amd_stop(struct op_msrs const * const msrs) +{ + unsigned int low, high; + int i; + + /* Subtle: stop on all counters to avoid race with + * setting our pm callback */ + for (i = 0 ; i < NUM_COUNTERS ; ++i) { + if (!reset_value[i]) + continue; + CTRL_READ(low, high, msrs, i); + CTRL_SET_INACTIVE(low); + CTRL_WRITE(low, high, msrs, i); + } + +#ifdef CONFIG_OPROFILE_IBS + if (ibs_allowed && ibs_config.fetch_enabled) { + low = 0; /* clear max count and enable */ + high = 0; + wrmsr(MSR_AMD64_IBSFETCHCTL, low, high); + } + + if (ibs_allowed && ibs_config.op_enabled) { + low = 0; /* clear max count and enable */ + high = 0; + wrmsr(MSR_AMD64_IBSOPCTL, low, high); + } +#endif +} + +static void op_amd_shutdown(struct op_msrs const * const msrs) +{ + int i; + + for (i = 0 ; i < NUM_COUNTERS ; ++i) { + if (CTR_IS_RESERVED(msrs, i)) + release_perfctr_nmi(MSR_K7_PERFCTR0 + i); + } + for (i = 0 ; i < NUM_CONTROLS ; ++i) { + if (CTRL_IS_RESERVED(msrs, i)) + release_evntsel_nmi(MSR_K7_EVNTSEL0 + i); + } +} + +#ifndef CONFIG_OPROFILE_IBS + +/* no IBS support */ + +static int op_amd_init(struct oprofile_operations *ops) +{ + return 0; +} + +static void op_amd_exit(void) {} + +#else + +static u8 ibs_eilvt_off; + +static inline void apic_init_ibs_nmi_per_cpu(void *arg) +{ + ibs_eilvt_off = setup_APIC_eilvt_ibs(0, APIC_EILVT_MSG_NMI, 0); +} + +static inline void apic_clear_ibs_nmi_per_cpu(void *arg) +{ + setup_APIC_eilvt_ibs(0, APIC_EILVT_MSG_FIX, 1); +} + +static int pfm_amd64_setup_eilvt(void) +{ +#define IBSCTL_LVTOFFSETVAL (1 << 8) +#define IBSCTL 0x1cc + struct pci_dev *cpu_cfg; + int nodes; + u32 value = 0; + + /* per CPU setup */ + on_each_cpu(apic_init_ibs_nmi_per_cpu, NULL, 1); + + nodes = 0; + cpu_cfg = NULL; + do { + cpu_cfg = pci_get_device(PCI_VENDOR_ID_AMD, + PCI_DEVICE_ID_AMD_10H_NB_MISC, + cpu_cfg); + if (!cpu_cfg) + break; + ++nodes; + pci_write_config_dword(cpu_cfg, IBSCTL, ibs_eilvt_off + | IBSCTL_LVTOFFSETVAL); + pci_read_config_dword(cpu_cfg, IBSCTL, &value); + if (value != (ibs_eilvt_off | IBSCTL_LVTOFFSETVAL)) { + printk(KERN_DEBUG "Failed to setup IBS LVT offset, " + "IBSCTL = 0x%08x", value); + return 1; + } + } while (1); + + if (!nodes) { + printk(KERN_DEBUG "No CPU node configured for IBS"); + return 1; + } + +#ifdef CONFIG_NUMA + /* Sanity check */ + /* Works only for 64bit with proper numa implementation. */ + if (nodes != num_possible_nodes()) { + printk(KERN_DEBUG "Failed to setup CPU node(s) for IBS, " + "found: %d, expected %d", + nodes, num_possible_nodes()); + return 1; + } +#endif + return 0; +} + +/* + * initialize the APIC for the IBS interrupts + * if available (AMD Family10h rev B0 and later) + */ +static void setup_ibs(void) +{ + ibs_allowed = boot_cpu_has(X86_FEATURE_IBS); + + if (!ibs_allowed) + return; + + if (pfm_amd64_setup_eilvt()) { + ibs_allowed = 0; + return; + } + + printk(KERN_INFO "oprofile: AMD IBS detected\n"); +} + + +/* + * unitialize the APIC for the IBS interrupts if needed on AMD Family10h + * rev B0 and later */ +static void clear_ibs_nmi(void) +{ + if (ibs_allowed) + on_each_cpu(apic_clear_ibs_nmi_per_cpu, NULL, 1); +} + +static int (*create_arch_files)(struct super_block * sb, struct dentry * root); + +static int setup_ibs_files(struct super_block * sb, struct dentry * root) +{ + char buf[12]; + struct dentry *dir; + int ret = 0; + + /* architecture specific files */ + if (create_arch_files) + ret = create_arch_files(sb, root); + + if (ret) + return ret; + + if (!ibs_allowed) + return ret; + + /* model specific files */ + + /* setup some reasonable defaults */ + ibs_config.max_cnt_fetch = 250000; + ibs_config.fetch_enabled = 0; + ibs_config.max_cnt_op = 250000; + ibs_config.op_enabled = 0; + ibs_config.dispatched_ops = 1; + snprintf(buf, sizeof(buf), "ibs_fetch"); + dir = oprofilefs_mkdir(sb, root, buf); + oprofilefs_create_ulong(sb, dir, "rand_enable", + &ibs_config.rand_en); + oprofilefs_create_ulong(sb, dir, "enable", + &ibs_config.fetch_enabled); + oprofilefs_create_ulong(sb, dir, "max_count", + &ibs_config.max_cnt_fetch); + snprintf(buf, sizeof(buf), "ibs_uops"); + dir = oprofilefs_mkdir(sb, root, buf); + oprofilefs_create_ulong(sb, dir, "enable", + &ibs_config.op_enabled); + oprofilefs_create_ulong(sb, dir, "max_count", + &ibs_config.max_cnt_op); + oprofilefs_create_ulong(sb, dir, "dispatched_ops", + &ibs_config.dispatched_ops); + + return 0; +} + +static int op_amd_init(struct oprofile_operations *ops) +{ + setup_ibs(); + create_arch_files = ops->create_files; + ops->create_files = setup_ibs_files; + return 0; +} + +static void op_amd_exit(void) +{ + clear_ibs_nmi(); +} + +#endif + +struct op_x86_model_spec const op_amd_spec = { + .init = op_amd_init, + .exit = op_amd_exit, + .num_counters = NUM_COUNTERS, + .num_controls = NUM_CONTROLS, + .fill_in_addresses = &op_amd_fill_in_addresses, + .setup_ctrs = &op_amd_setup_ctrs, + .check_ctrs = &op_amd_check_ctrs, + .start = &op_amd_start, + .stop = &op_amd_stop, + .shutdown = &op_amd_shutdown +}; diff --git a/arch/x86/oprofile/op_model_athlon.c b/arch/x86/oprofile/op_model_athlon.c deleted file mode 100644 index 3d534879a9d..00000000000 --- a/arch/x86/oprofile/op_model_athlon.c +++ /dev/null @@ -1,190 +0,0 @@ -/* - * @file op_model_athlon.h - * athlon / K7 / K8 / Family 10h model-specific MSR operations - * - * @remark Copyright 2002 OProfile authors - * @remark Read the file COPYING - * - * @author John Levon - * @author Philippe Elie - * @author Graydon Hoare - */ - -#include <linux/oprofile.h> -#include <asm/ptrace.h> -#include <asm/msr.h> -#include <asm/nmi.h> - -#include "op_x86_model.h" -#include "op_counter.h" - -#define NUM_COUNTERS 4 -#define NUM_CONTROLS 4 - -#define CTR_IS_RESERVED(msrs, c) (msrs->counters[(c)].addr ? 1 : 0) -#define CTR_READ(l, h, msrs, c) do {rdmsr(msrs->counters[(c)].addr, (l), (h)); } while (0) -#define CTR_WRITE(l, msrs, c) do {wrmsr(msrs->counters[(c)].addr, -(unsigned int)(l), -1); } while (0) -#define CTR_OVERFLOWED(n) (!((n) & (1U<<31))) - -#define CTRL_IS_RESERVED(msrs, c) (msrs->controls[(c)].addr ? 1 : 0) -#define CTRL_READ(l, h, msrs, c) do {rdmsr(msrs->controls[(c)].addr, (l), (h)); } while (0) -#define CTRL_WRITE(l, h, msrs, c) do {wrmsr(msrs->controls[(c)].addr, (l), (h)); } while (0) -#define CTRL_SET_ACTIVE(n) (n |= (1<<22)) -#define CTRL_SET_INACTIVE(n) (n &= ~(1<<22)) -#define CTRL_CLEAR_LO(x) (x &= (1<<21)) -#define CTRL_CLEAR_HI(x) (x &= 0xfffffcf0) -#define CTRL_SET_ENABLE(val) (val |= 1<<20) -#define CTRL_SET_USR(val, u) (val |= ((u & 1) << 16)) -#define CTRL_SET_KERN(val, k) (val |= ((k & 1) << 17)) -#define CTRL_SET_UM(val, m) (val |= (m << 8)) -#define CTRL_SET_EVENT_LOW(val, e) (val |= (e & 0xff)) -#define CTRL_SET_EVENT_HIGH(val, e) (val |= ((e >> 8) & 0xf)) -#define CTRL_SET_HOST_ONLY(val, h) (val |= ((h & 1) << 9)) -#define CTRL_SET_GUEST_ONLY(val, h) (val |= ((h & 1) << 8)) - -static unsigned long reset_value[NUM_COUNTERS]; - -static void athlon_fill_in_addresses(struct op_msrs * const msrs) -{ - int i; - - for (i = 0; i < NUM_COUNTERS; i++) { - if (reserve_perfctr_nmi(MSR_K7_PERFCTR0 + i)) - msrs->counters[i].addr = MSR_K7_PERFCTR0 + i; - else - msrs->counters[i].addr = 0; - } - - for (i = 0; i < NUM_CONTROLS; i++) { - if (reserve_evntsel_nmi(MSR_K7_EVNTSEL0 + i)) - msrs->controls[i].addr = MSR_K7_EVNTSEL0 + i; - else - msrs->controls[i].addr = 0; - } -} - - -static void athlon_setup_ctrs(struct op_msrs const * const msrs) -{ - unsigned int low, high; - int i; - - /* clear all counters */ - for (i = 0 ; i < NUM_CONTROLS; ++i) { - if (unlikely(!CTRL_IS_RESERVED(msrs, i))) - continue; - CTRL_READ(low, high, msrs, i); - CTRL_CLEAR_LO(low); - CTRL_CLEAR_HI(high); - CTRL_WRITE(low, high, msrs, i); - } - - /* avoid a false detection of ctr overflows in NMI handler */ - for (i = 0; i < NUM_COUNTERS; ++i) { - if (unlikely(!CTR_IS_RESERVED(msrs, i))) - continue; - CTR_WRITE(1, msrs, i); - } - - /* enable active counters */ - for (i = 0; i < NUM_COUNTERS; ++i) { - if ((counter_config[i].enabled) && (CTR_IS_RESERVED(msrs, i))) { - reset_value[i] = counter_config[i].count; - - CTR_WRITE(counter_config[i].count, msrs, i); - - CTRL_READ(low, high, msrs, i); - CTRL_CLEAR_LO(low); - CTRL_CLEAR_HI(high); - CTRL_SET_ENABLE(low); - CTRL_SET_USR(low, counter_config[i].user); - CTRL_SET_KERN(low, counter_config[i].kernel); - CTRL_SET_UM(low, counter_config[i].unit_mask); - CTRL_SET_EVENT_LOW(low, counter_config[i].event); - CTRL_SET_EVENT_HIGH(high, counter_config[i].event); - CTRL_SET_HOST_ONLY(high, 0); - CTRL_SET_GUEST_ONLY(high, 0); - - CTRL_WRITE(low, high, msrs, i); - } else { - reset_value[i] = 0; - } - } -} - - -static int athlon_check_ctrs(struct pt_regs * const regs, - struct op_msrs const * const msrs) -{ - unsigned int low, high; - int i; - - for (i = 0 ; i < NUM_COUNTERS; ++i) { - if (!reset_value[i]) - continue; - CTR_READ(low, high, msrs, i); - if (CTR_OVERFLOWED(low)) { - oprofile_add_sample(regs, i); - CTR_WRITE(reset_value[i], msrs, i); - } - } - - /* See op_model_ppro.c */ - return 1; -} - - -static void athlon_start(struct op_msrs const * const msrs) -{ - unsigned int low, high; - int i; - for (i = 0 ; i < NUM_COUNTERS ; ++i) { - if (reset_value[i]) { - CTRL_READ(low, high, msrs, i); - CTRL_SET_ACTIVE(low); - CTRL_WRITE(low, high, msrs, i); - } - } -} - - -static void athlon_stop(struct op_msrs const * const msrs) -{ - unsigned int low, high; - int i; - - /* Subtle: stop on all counters to avoid race with - * setting our pm callback */ - for (i = 0 ; i < NUM_COUNTERS ; ++i) { - if (!reset_value[i]) - continue; - CTRL_READ(low, high, msrs, i); - CTRL_SET_INACTIVE(low); - CTRL_WRITE(low, high, msrs, i); - } -} - -static void athlon_shutdown(struct op_msrs const * const msrs) -{ - int i; - - for (i = 0 ; i < NUM_COUNTERS ; ++i) { - if (CTR_IS_RESERVED(msrs, i)) - release_perfctr_nmi(MSR_K7_PERFCTR0 + i); - } - for (i = 0 ; i < NUM_CONTROLS ; ++i) { - if (CTRL_IS_RESERVED(msrs, i)) - release_evntsel_nmi(MSR_K7_EVNTSEL0 + i); - } -} - -struct op_x86_model_spec const op_athlon_spec = { - .num_counters = NUM_COUNTERS, - .num_controls = NUM_CONTROLS, - .fill_in_addresses = &athlon_fill_in_addresses, - .setup_ctrs = &athlon_setup_ctrs, - .check_ctrs = &athlon_check_ctrs, - .start = &athlon_start, - .stop = &athlon_stop, - .shutdown = &athlon_shutdown -}; diff --git a/arch/x86/oprofile/op_x86_model.h b/arch/x86/oprofile/op_x86_model.h index 45b605fa71d..05a0261ba0c 100644 --- a/arch/x86/oprofile/op_x86_model.h +++ b/arch/x86/oprofile/op_x86_model.h @@ -32,6 +32,8 @@ struct pt_regs; * various x86 CPU models' perfctr support. */ struct op_x86_model_spec { + int (*init)(struct oprofile_operations *ops); + void (*exit)(void); unsigned int const num_counters; unsigned int const num_controls; void (*fill_in_addresses)(struct op_msrs * const msrs); @@ -46,6 +48,6 @@ struct op_x86_model_spec { extern struct op_x86_model_spec const op_ppro_spec; extern struct op_x86_model_spec const op_p4_spec; extern struct op_x86_model_spec const op_p4_ht2_spec; -extern struct op_x86_model_spec const op_athlon_spec; +extern struct op_x86_model_spec const op_amd_spec; #endif /* OP_X86_MODEL_H */ diff --git a/arch/x86/pci/acpi.c b/arch/x86/pci/acpi.c index 19af06927fb..1d88d2b3977 100644 --- a/arch/x86/pci/acpi.c +++ b/arch/x86/pci/acpi.c @@ -250,10 +250,5 @@ int __init pci_acpi_init(void) acpi_pci_irq_enable(dev); } -#ifdef CONFIG_X86_IO_APIC - if (acpi_ioapic) - print_IO_APIC(); -#endif - return 0; } diff --git a/arch/x86/pci/fixup.c b/arch/x86/pci/fixup.c index 4bdaa590375..3c27a809393 100644 --- a/arch/x86/pci/fixup.c +++ b/arch/x86/pci/fixup.c @@ -511,3 +511,31 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, 0x1201, fam10h_pci_cfg_space_size); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, 0x1202, fam10h_pci_cfg_space_size); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, 0x1203, fam10h_pci_cfg_space_size); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, 0x1204, fam10h_pci_cfg_space_size); + +/* + * SB600: Disable BAR1 on device 14.0 to avoid HPET resources from + * confusing the PCI engine: + */ +static void sb600_disable_hpet_bar(struct pci_dev *dev) +{ + u8 val; + + /* + * The SB600 and SB700 both share the same device + * ID, but the PM register 0x55 does something different + * for the SB700, so make sure we are dealing with the + * SB600 before touching the bit: + */ + + pci_read_config_byte(dev, 0x08, &val); + + if (val < 0x2F) { + outb(0x55, 0xCD6); + val = inb(0xCD7); + + /* Set bit 7 in PM register 0x55 */ + outb(0x55, 0xCD6); + outb(val | 0x80, 0xCD7); + } +} +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_ATI, 0x4385, sb600_disable_hpet_bar); diff --git a/arch/x86/pci/i386.c b/arch/x86/pci/i386.c index 8791fc55e71..844df0cbbd3 100644 --- a/arch/x86/pci/i386.c +++ b/arch/x86/pci/i386.c @@ -33,6 +33,7 @@ #include <linux/bootmem.h> #include <asm/pat.h> +#include <asm/e820.h> #include "pci.h" @@ -227,6 +228,8 @@ void __init pcibios_resource_survey(void) pcibios_allocate_bus_resources(&pci_root_buses); pcibios_allocate_resources(0); pcibios_allocate_resources(1); + + e820_reserve_resources_late(); } /** diff --git a/arch/x86/pci/mmconfig-shared.c b/arch/x86/pci/mmconfig-shared.c index d9635764ce3..654a2234f8f 100644 --- a/arch/x86/pci/mmconfig-shared.c +++ b/arch/x86/pci/mmconfig-shared.c @@ -209,7 +209,7 @@ static int __init pci_mmcfg_check_hostbridge(void) return name != NULL; } -static void __init pci_mmcfg_insert_resources(unsigned long resource_flags) +static void __init pci_mmcfg_insert_resources(void) { #define PCI_MMCFG_RESOURCE_NAME_LEN 19 int i; @@ -233,7 +233,7 @@ static void __init pci_mmcfg_insert_resources(unsigned long resource_flags) cfg->pci_segment); res->start = cfg->address; res->end = res->start + (num_buses << 20) - 1; - res->flags = IORESOURCE_MEM | resource_flags; + res->flags = IORESOURCE_MEM | IORESOURCE_BUSY; insert_resource(&iomem_resource, res); names += PCI_MMCFG_RESOURCE_NAME_LEN; } @@ -434,11 +434,9 @@ static void __init __pci_mmcfg_init(int early) (pci_mmcfg_config[0].address == 0)) return; - if (pci_mmcfg_arch_init()) { - if (known_bridge) - pci_mmcfg_insert_resources(IORESOURCE_BUSY); + if (pci_mmcfg_arch_init()) pci_probe = (pci_probe & ~PCI_PROBE_MASK) | PCI_PROBE_MMCONF; - } else { + else { /* * Signal not to attempt to insert mmcfg resources because * the architecture mmcfg setup could not initialize. @@ -475,7 +473,7 @@ static int __init pci_mmcfg_late_insert_resources(void) * marked so it won't cause request errors when __request_region is * called. */ - pci_mmcfg_insert_resources(0); + pci_mmcfg_insert_resources(); return 0; } diff --git a/arch/x86/power/cpu_32.c b/arch/x86/power/cpu_32.c index d3e083dea72..274d06082f4 100644 --- a/arch/x86/power/cpu_32.c +++ b/arch/x86/power/cpu_32.c @@ -11,6 +11,7 @@ #include <linux/suspend.h> #include <asm/mtrr.h> #include <asm/mce.h> +#include <asm/xcr.h> static struct saved_context saved_context; @@ -126,6 +127,12 @@ static void __restore_processor_state(struct saved_context *ctxt) if (boot_cpu_has(X86_FEATURE_SEP)) enable_sep_cpu(); + /* + * restore XCR0 for xsave capable cpu's. + */ + if (cpu_has_xsave) + xsetbv(XCR_XFEATURE_ENABLED_MASK, pcntxt_mask); + fix_processor_context(); do_fpu_end(); mtrr_ap_init(); diff --git a/arch/x86/power/cpu_64.c b/arch/x86/power/cpu_64.c index 66bdfb591fd..e3b6cf70d62 100644 --- a/arch/x86/power/cpu_64.c +++ b/arch/x86/power/cpu_64.c @@ -14,6 +14,7 @@ #include <asm/page.h> #include <asm/pgtable.h> #include <asm/mtrr.h> +#include <asm/xcr.h> static void fix_processor_context(void); @@ -122,6 +123,12 @@ static void __restore_processor_state(struct saved_context *ctxt) wrmsrl(MSR_GS_BASE, ctxt->gs_base); wrmsrl(MSR_KERNEL_GS_BASE, ctxt->gs_kernel_base); + /* + * restore XCR0 for xsave capable cpu's. + */ + if (cpu_has_xsave) + xsetbv(XCR_XFEATURE_ENABLED_MASK, pcntxt_mask); + fix_processor_context(); do_fpu_end(); diff --git a/arch/x86/xen/Kconfig b/arch/x86/xen/Kconfig index 3815e425f47..87b9ab16642 100644 --- a/arch/x86/xen/Kconfig +++ b/arch/x86/xen/Kconfig @@ -26,5 +26,13 @@ config XEN_MAX_DOMAIN_MEMORY config XEN_SAVE_RESTORE bool - depends on PM - default y
\ No newline at end of file + depends on XEN && PM + default y + +config XEN_DEBUG_FS + bool "Enable Xen debug and tuning parameters in debugfs" + depends on XEN && DEBUG_FS + default n + help + Enable statistics output and various tuning options in debugfs. + Enabling this option may incur a significant performance overhead. diff --git a/arch/x86/xen/Makefile b/arch/x86/xen/Makefile index 59c1e539aed..313947940a1 100644 --- a/arch/x86/xen/Makefile +++ b/arch/x86/xen/Makefile @@ -1,4 +1,12 @@ -obj-y := enlighten.o setup.o multicalls.o mmu.o \ +ifdef CONFIG_FTRACE +# Do not profile debug and lowlevel utilities +CFLAGS_REMOVE_spinlock.o = -pg +CFLAGS_REMOVE_time.o = -pg +CFLAGS_REMOVE_irq.o = -pg +endif + +obj-y := enlighten.o setup.o multicalls.o mmu.o irq.o \ time.o xen-asm_$(BITS).o grant-table.o suspend.o -obj-$(CONFIG_SMP) += smp.o +obj-$(CONFIG_SMP) += smp.o spinlock.o +obj-$(CONFIG_XEN_DEBUG_FS) += debugfs.o
\ No newline at end of file diff --git a/arch/x86/xen/debugfs.c b/arch/x86/xen/debugfs.c new file mode 100644 index 00000000000..b53225d2cac --- /dev/null +++ b/arch/x86/xen/debugfs.c @@ -0,0 +1,123 @@ +#include <linux/init.h> +#include <linux/debugfs.h> +#include <linux/module.h> + +#include "debugfs.h" + +static struct dentry *d_xen_debug; + +struct dentry * __init xen_init_debugfs(void) +{ + if (!d_xen_debug) { + d_xen_debug = debugfs_create_dir("xen", NULL); + + if (!d_xen_debug) + pr_warning("Could not create 'xen' debugfs directory\n"); + } + + return d_xen_debug; +} + +struct array_data +{ + void *array; + unsigned elements; +}; + +static int u32_array_open(struct inode *inode, struct file *file) +{ + file->private_data = NULL; + return nonseekable_open(inode, file); +} + +static size_t format_array(char *buf, size_t bufsize, const char *fmt, + u32 *array, unsigned array_size) +{ + size_t ret = 0; + unsigned i; + + for(i = 0; i < array_size; i++) { + size_t len; + + len = snprintf(buf, bufsize, fmt, array[i]); + len++; /* ' ' or '\n' */ + ret += len; + + if (buf) { + buf += len; + bufsize -= len; + buf[-1] = (i == array_size-1) ? '\n' : ' '; + } + } + + ret++; /* \0 */ + if (buf) + *buf = '\0'; + + return ret; +} + +static char *format_array_alloc(const char *fmt, u32 *array, unsigned array_size) +{ + size_t len = format_array(NULL, 0, fmt, array, array_size); + char *ret; + + ret = kmalloc(len, GFP_KERNEL); + if (ret == NULL) + return NULL; + + format_array(ret, len, fmt, array, array_size); + return ret; +} + +static ssize_t u32_array_read(struct file *file, char __user *buf, size_t len, + loff_t *ppos) +{ + struct inode *inode = file->f_path.dentry->d_inode; + struct array_data *data = inode->i_private; + size_t size; + + if (*ppos == 0) { + if (file->private_data) { + kfree(file->private_data); + file->private_data = NULL; + } + + file->private_data = format_array_alloc("%u", data->array, data->elements); + } + + size = 0; + if (file->private_data) + size = strlen(file->private_data); + + return simple_read_from_buffer(buf, len, ppos, file->private_data, size); +} + +static int xen_array_release(struct inode *inode, struct file *file) +{ + kfree(file->private_data); + + return 0; +} + +static struct file_operations u32_array_fops = { + .owner = THIS_MODULE, + .open = u32_array_open, + .release= xen_array_release, + .read = u32_array_read, +}; + +struct dentry *xen_debugfs_create_u32_array(const char *name, mode_t mode, + struct dentry *parent, + u32 *array, unsigned elements) +{ + struct array_data *data = kmalloc(sizeof(*data), GFP_KERNEL); + + if (data == NULL) + return NULL; + + data->array = array; + data->elements = elements; + + return debugfs_create_file(name, mode, parent, data, &u32_array_fops); +} diff --git a/arch/x86/xen/debugfs.h b/arch/x86/xen/debugfs.h new file mode 100644 index 00000000000..e2813208483 --- /dev/null +++ b/arch/x86/xen/debugfs.h @@ -0,0 +1,10 @@ +#ifndef _XEN_DEBUGFS_H +#define _XEN_DEBUGFS_H + +struct dentry * __init xen_init_debugfs(void); + +struct dentry *xen_debugfs_create_u32_array(const char *name, mode_t mode, + struct dentry *parent, + u32 *array, unsigned elements); + +#endif /* _XEN_DEBUGFS_H */ diff --git a/arch/x86/xen/enlighten.c b/arch/x86/xen/enlighten.c index 7dcd321a050..0013a729b41 100644 --- a/arch/x86/xen/enlighten.c +++ b/arch/x86/xen/enlighten.c @@ -30,12 +30,12 @@ #include <xen/interface/xen.h> #include <xen/interface/physdev.h> #include <xen/interface/vcpu.h> -#include <xen/interface/sched.h> #include <xen/features.h> #include <xen/page.h> #include <xen/hvc-console.h> #include <asm/paravirt.h> +#include <asm/apic.h> #include <asm/page.h> #include <asm/xen/hypercall.h> #include <asm/xen/hypervisor.h> @@ -57,6 +57,9 @@ EXPORT_SYMBOL_GPL(hypercall_page); DEFINE_PER_CPU(struct vcpu_info *, xen_vcpu); DEFINE_PER_CPU(struct vcpu_info, xen_vcpu_info); +enum xen_domain_type xen_domain_type = XEN_NATIVE; +EXPORT_SYMBOL_GPL(xen_domain_type); + /* * Identity map, in addition to plain kernel map. This needs to be * large enough to allocate page table pages to allocate the rest. @@ -110,7 +113,14 @@ struct shared_info *HYPERVISOR_shared_info = (void *)&xen_dummy_shared_info; * * 0: not available, 1: available */ -static int have_vcpu_info_placement = 1; +static int have_vcpu_info_placement = +#ifdef CONFIG_X86_32 + 1 +#else + 0 +#endif + ; + static void xen_vcpu_setup(int cpu) { @@ -226,103 +236,68 @@ static unsigned long xen_get_debugreg(int reg) return HYPERVISOR_get_debugreg(reg); } -static unsigned long xen_save_fl(void) +static void xen_leave_lazy(void) { - struct vcpu_info *vcpu; - unsigned long flags; - - vcpu = x86_read_percpu(xen_vcpu); - - /* flag has opposite sense of mask */ - flags = !vcpu->evtchn_upcall_mask; - - /* convert to IF type flag - -0 -> 0x00000000 - -1 -> 0xffffffff - */ - return (-flags) & X86_EFLAGS_IF; + paravirt_leave_lazy(paravirt_get_lazy_mode()); + xen_mc_flush(); } -static void xen_restore_fl(unsigned long flags) +static unsigned long xen_store_tr(void) { - struct vcpu_info *vcpu; - - /* convert from IF type flag */ - flags = !(flags & X86_EFLAGS_IF); - - /* There's a one instruction preempt window here. We need to - make sure we're don't switch CPUs between getting the vcpu - pointer and updating the mask. */ - preempt_disable(); - vcpu = x86_read_percpu(xen_vcpu); - vcpu->evtchn_upcall_mask = flags; - preempt_enable_no_resched(); - - /* Doesn't matter if we get preempted here, because any - pending event will get dealt with anyway. */ - - if (flags == 0) { - preempt_check_resched(); - barrier(); /* unmask then check (avoid races) */ - if (unlikely(vcpu->evtchn_upcall_pending)) - force_evtchn_callback(); - } + return 0; } -static void xen_irq_disable(void) +/* + * Set the page permissions for a particular virtual address. If the + * address is a vmalloc mapping (or other non-linear mapping), then + * find the linear mapping of the page and also set its protections to + * match. + */ +static void set_aliased_prot(void *v, pgprot_t prot) { - /* There's a one instruction preempt window here. We need to - make sure we're don't switch CPUs between getting the vcpu - pointer and updating the mask. */ - preempt_disable(); - x86_read_percpu(xen_vcpu)->evtchn_upcall_mask = 1; - preempt_enable_no_resched(); -} + int level; + pte_t *ptep; + pte_t pte; + unsigned long pfn; + struct page *page; -static void xen_irq_enable(void) -{ - struct vcpu_info *vcpu; + ptep = lookup_address((unsigned long)v, &level); + BUG_ON(ptep == NULL); - /* We don't need to worry about being preempted here, since - either a) interrupts are disabled, so no preemption, or b) - the caller is confused and is trying to re-enable interrupts - on an indeterminate processor. */ + pfn = pte_pfn(*ptep); + page = pfn_to_page(pfn); - vcpu = x86_read_percpu(xen_vcpu); - vcpu->evtchn_upcall_mask = 0; + pte = pfn_pte(pfn, prot); - /* Doesn't matter if we get preempted here, because any - pending event will get dealt with anyway. */ + if (HYPERVISOR_update_va_mapping((unsigned long)v, pte, 0)) + BUG(); - barrier(); /* unmask then check (avoid races) */ - if (unlikely(vcpu->evtchn_upcall_pending)) - force_evtchn_callback(); -} + if (!PageHighMem(page)) { + void *av = __va(PFN_PHYS(pfn)); -static void xen_safe_halt(void) -{ - /* Blocking includes an implicit local_irq_enable(). */ - if (HYPERVISOR_sched_op(SCHEDOP_block, NULL) != 0) - BUG(); + if (av != v) + if (HYPERVISOR_update_va_mapping((unsigned long)av, pte, 0)) + BUG(); + } else + kmap_flush_unused(); } -static void xen_halt(void) +static void xen_alloc_ldt(struct desc_struct *ldt, unsigned entries) { - if (irqs_disabled()) - HYPERVISOR_vcpu_op(VCPUOP_down, smp_processor_id(), NULL); - else - xen_safe_halt(); -} + const unsigned entries_per_page = PAGE_SIZE / LDT_ENTRY_SIZE; + int i; -static void xen_leave_lazy(void) -{ - paravirt_leave_lazy(paravirt_get_lazy_mode()); - xen_mc_flush(); + for(i = 0; i < entries; i += entries_per_page) + set_aliased_prot(ldt + i, PAGE_KERNEL_RO); } -static unsigned long xen_store_tr(void) +static void xen_free_ldt(struct desc_struct *ldt, unsigned entries) { - return 0; + const unsigned entries_per_page = PAGE_SIZE / LDT_ENTRY_SIZE; + int i; + + for(i = 0; i < entries; i += entries_per_page) + set_aliased_prot(ldt + i, PAGE_KERNEL); } static void xen_set_ldt(const void *addr, unsigned entries) @@ -425,8 +400,7 @@ static void xen_load_gs_index(unsigned int idx) static void xen_write_ldt_entry(struct desc_struct *dt, int entrynum, const void *ptr) { - unsigned long lp = (unsigned long)&dt[entrynum]; - xmaddr_t mach_lp = virt_to_machine(lp); + xmaddr_t mach_lp = arbitrary_virt_to_machine(&dt[entrynum]); u64 entry = *(u64 *)ptr; preempt_disable(); @@ -559,7 +533,7 @@ static void xen_write_gdt_entry(struct desc_struct *dt, int entry, } static void xen_load_sp0(struct tss_struct *tss, - struct thread_struct *thread) + struct thread_struct *thread) { struct multicall_space mcs = xen_mc_entry(0); MULTI_stack_switch(mcs.mc, __KERNEL_DS, thread->sp0); @@ -580,16 +554,47 @@ static void xen_io_delay(void) } #ifdef CONFIG_X86_LOCAL_APIC -static u32 xen_apic_read(unsigned long reg) +static u32 xen_apic_read(u32 reg) { return 0; } -static void xen_apic_write(unsigned long reg, u32 val) +static void xen_apic_write(u32 reg, u32 val) { /* Warn to see if there's any stray references */ WARN_ON(1); } + +static u64 xen_apic_icr_read(void) +{ + return 0; +} + +static void xen_apic_icr_write(u32 low, u32 id) +{ + /* Warn to see if there's any stray references */ + WARN_ON(1); +} + +static void xen_apic_wait_icr_idle(void) +{ + return; +} + +static u32 xen_safe_apic_wait_icr_idle(void) +{ + return 0; +} + +static struct apic_ops xen_basic_apic_ops = { + .read = xen_apic_read, + .write = xen_apic_write, + .icr_read = xen_apic_icr_read, + .icr_write = xen_apic_icr_write, + .wait_icr_idle = xen_apic_wait_icr_idle, + .safe_wait_icr_idle = xen_safe_apic_wait_icr_idle, +}; + #endif static void xen_flush_tlb(void) @@ -803,6 +808,19 @@ static int xen_write_msr_safe(unsigned int msr, unsigned low, unsigned high) ret = -EFAULT; break; #endif + + case MSR_STAR: + case MSR_CSTAR: + case MSR_LSTAR: + case MSR_SYSCALL_MASK: + case MSR_IA32_SYSENTER_CS: + case MSR_IA32_SYSENTER_ESP: + case MSR_IA32_SYSENTER_EIP: + /* Fast syscall setup is all done in hypercalls, so + these are all ignored. Stub them out here to stop + Xen console noise. */ + break; + default: ret = native_write_msr_safe(msr, low, high); } @@ -846,8 +864,8 @@ static void xen_alloc_ptpage(struct mm_struct *mm, unsigned long pfn, unsigned l SetPagePinned(page); if (!PageHighMem(page)) { - make_lowmem_page_readonly(__va(PFN_PHYS(pfn))); - if (level == PT_PTE) + make_lowmem_page_readonly(__va(PFN_PHYS((unsigned long)pfn))); + if (level == PT_PTE && USE_SPLIT_PTLOCKS) pin_pagetable_pfn(MMUEXT_PIN_L1_TABLE, pfn); } else /* make sure there are no stray mappings of @@ -915,7 +933,7 @@ static void xen_release_ptpage(unsigned long pfn, unsigned level) if (PagePinned(page)) { if (!PageHighMem(page)) { - if (level == PT_PTE) + if (level == PT_PTE && USE_SPLIT_PTLOCKS) pin_pagetable_pfn(MMUEXT_UNPIN_TABLE, pfn); make_lowmem_page_readwrite(__va(PFN_PHYS(pfn))); } @@ -962,6 +980,7 @@ static void *xen_kmap_atomic_pte(struct page *page, enum km_type type) } #endif +#ifdef CONFIG_X86_32 static __init pte_t mask_rw_pte(pte_t *ptep, pte_t pte) { /* If there's an existing pte, then don't allow _PAGE_RW to be set */ @@ -980,6 +999,7 @@ static __init void xen_set_pte_init(pte_t *ptep, pte_t pte) xen_set_pte(ptep, pte); } +#endif static __init void xen_pagetable_setup_start(pgd_t *base) { @@ -1046,7 +1066,6 @@ void xen_setup_vcpu_info_placement(void) /* xen_vcpu_setup managed to place the vcpu_info within the percpu area for all cpus, so make use of it */ -#ifdef CONFIG_X86_32 if (have_vcpu_info_placement) { printk(KERN_INFO "Xen: using vcpu_info placement\n"); @@ -1056,7 +1075,6 @@ void xen_setup_vcpu_info_placement(void) pv_irq_ops.irq_enable = xen_irq_enable_direct; pv_mmu_ops.read_cr2 = xen_read_cr2_direct; } -#endif } static unsigned xen_patch(u8 type, u16 clobbers, void *insnbuf, @@ -1077,12 +1095,10 @@ static unsigned xen_patch(u8 type, u16 clobbers, void *insnbuf, goto patch_site switch (type) { -#ifdef CONFIG_X86_32 SITE(pv_irq_ops, irq_enable); SITE(pv_irq_ops, irq_disable); SITE(pv_irq_ops, save_fl); SITE(pv_irq_ops, restore_fl); -#endif /* CONFIG_X86_32 */ #undef SITE patch_site: @@ -1220,6 +1236,9 @@ static const struct pv_cpu_ops xen_cpu_ops __initdata = { .load_gs_index = xen_load_gs_index, #endif + .alloc_ldt = xen_alloc_ldt, + .free_ldt = xen_free_ldt, + .store_gdt = native_store_gdt, .store_idt = native_store_idt, .store_tr = xen_store_tr, @@ -1241,40 +1260,8 @@ static const struct pv_cpu_ops xen_cpu_ops __initdata = { }, }; -static void __init __xen_init_IRQ(void) -{ -#ifdef CONFIG_X86_64 - int i; - - /* Create identity vector->irq map */ - for(i = 0; i < NR_VECTORS; i++) { - int cpu; - - for_each_possible_cpu(cpu) - per_cpu(vector_irq, cpu)[i] = i; - } -#endif /* CONFIG_X86_64 */ - - xen_init_IRQ(); -} - -static const struct pv_irq_ops xen_irq_ops __initdata = { - .init_IRQ = __xen_init_IRQ, - .save_fl = xen_save_fl, - .restore_fl = xen_restore_fl, - .irq_disable = xen_irq_disable, - .irq_enable = xen_irq_enable, - .safe_halt = xen_safe_halt, - .halt = xen_halt, -#ifdef CONFIG_X86_64 - .adjust_exception_frame = xen_adjust_exception_frame, -#endif -}; - static const struct pv_apic_ops xen_apic_ops __initdata = { #ifdef CONFIG_X86_LOCAL_APIC - .apic_write = xen_apic_write, - .apic_read = xen_apic_read, .setup_boot_clock = paravirt_nop, .setup_secondary_clock = paravirt_nop, .startup_ipi_hook = paravirt_nop, @@ -1413,7 +1400,7 @@ static void __init xen_reserve_top(void) if (HYPERVISOR_xen_version(XENVER_platform_parameters, &pp) == 0) top = pp.virt_start; - reserve_top_address(-top + 2 * PAGE_SIZE); + reserve_top_address(-top); #endif /* CONFIG_X86_32 */ } @@ -1447,48 +1434,11 @@ static void *m2v(phys_addr_t maddr) return __ka(m2p(maddr)); } -#ifdef CONFIG_X86_64 -static void walk(pgd_t *pgd, unsigned long addr) -{ - unsigned l4idx = pgd_index(addr); - unsigned l3idx = pud_index(addr); - unsigned l2idx = pmd_index(addr); - unsigned l1idx = pte_index(addr); - pgd_t l4; - pud_t l3; - pmd_t l2; - pte_t l1; - - xen_raw_printk("walk %p, %lx -> %d %d %d %d\n", - pgd, addr, l4idx, l3idx, l2idx, l1idx); - - l4 = pgd[l4idx]; - xen_raw_printk(" l4: %016lx\n", l4.pgd); - xen_raw_printk(" %016lx\n", pgd_val(l4)); - - l3 = ((pud_t *)(m2v(l4.pgd)))[l3idx]; - xen_raw_printk(" l3: %016lx\n", l3.pud); - xen_raw_printk(" %016lx\n", pud_val(l3)); - - l2 = ((pmd_t *)(m2v(l3.pud)))[l2idx]; - xen_raw_printk(" l2: %016lx\n", l2.pmd); - xen_raw_printk(" %016lx\n", pmd_val(l2)); - - l1 = ((pte_t *)(m2v(l2.pmd)))[l1idx]; - xen_raw_printk(" l1: %016lx\n", l1.pte); - xen_raw_printk(" %016lx\n", pte_val(l1)); -} -#endif - static void set_page_prot(void *addr, pgprot_t prot) { unsigned long pfn = __pa(addr) >> PAGE_SHIFT; pte_t pte = pfn_pte(pfn, prot); - xen_raw_printk("addr=%p pfn=%lx mfn=%lx prot=%016llx pte=%016llx\n", - addr, pfn, get_phys_to_machine(pfn), - pgprot_val(prot), pte.pte); - if (HYPERVISOR_update_va_mapping((unsigned long)addr, pte, 0)) BUG(); } @@ -1664,6 +1614,8 @@ asmlinkage void __init xen_start_kernel(void) if (!xen_start_info) return; + xen_domain_type = XEN_PV_DOMAIN; + BUG_ON(memcmp(xen_start_info->magic, "xen-3", 5) != 0); xen_setup_features(); @@ -1673,10 +1625,18 @@ asmlinkage void __init xen_start_kernel(void) pv_init_ops = xen_init_ops; pv_time_ops = xen_time_ops; pv_cpu_ops = xen_cpu_ops; - pv_irq_ops = xen_irq_ops; pv_apic_ops = xen_apic_ops; pv_mmu_ops = xen_mmu_ops; + xen_init_irq_ops(); + +#ifdef CONFIG_X86_LOCAL_APIC + /* + * set up the basic apic ops. + */ + apic_ops = &xen_basic_apic_ops; +#endif + if (xen_feature(XENFEAT_mmu_pt_update_preserve_ad)) { pv_mmu_ops.ptep_modify_prot_start = xen_ptep_modify_prot_start; pv_mmu_ops.ptep_modify_prot_commit = xen_ptep_modify_prot_commit; @@ -1700,7 +1660,7 @@ asmlinkage void __init xen_start_kernel(void) /* Prevent unwanted bits from being set in PTEs. */ __supported_pte_mask &= ~_PAGE_GLOBAL; - if (!is_initial_xendomain()) + if (!xen_initial_domain()) __supported_pte_mask &= ~(_PAGE_PWT | _PAGE_PCD); /* Don't do the full vcpu_info placement stuff until we have a @@ -1735,7 +1695,7 @@ asmlinkage void __init xen_start_kernel(void) boot_params.hdr.ramdisk_size = xen_start_info->mod_len; boot_params.hdr.cmd_line_ptr = __pa(xen_start_info->cmd_line); - if (!is_initial_xendomain()) { + if (!xen_initial_domain()) { add_preferred_console("xenboot", 0, NULL); add_preferred_console("tty", 0, NULL); add_preferred_console("hvc", 0, NULL); @@ -1743,15 +1703,6 @@ asmlinkage void __init xen_start_kernel(void) xen_raw_console_write("about to get started...\n"); -#if 0 - xen_raw_printk("&boot_params=%p __pa(&boot_params)=%lx __va(__pa(&boot_params))=%lx\n", - &boot_params, __pa_symbol(&boot_params), - __va(__pa_symbol(&boot_params))); - - walk(pgd, &boot_params); - walk(pgd, __va(__pa(&boot_params))); -#endif - /* Start the world */ #ifdef CONFIG_X86_32 i386_start_kernel(); diff --git a/arch/x86/xen/irq.c b/arch/x86/xen/irq.c new file mode 100644 index 00000000000..28b85ab8422 --- /dev/null +++ b/arch/x86/xen/irq.c @@ -0,0 +1,143 @@ +#include <linux/hardirq.h> + +#include <xen/interface/xen.h> +#include <xen/interface/sched.h> +#include <xen/interface/vcpu.h> + +#include <asm/xen/hypercall.h> +#include <asm/xen/hypervisor.h> + +#include "xen-ops.h" + +/* + * Force a proper event-channel callback from Xen after clearing the + * callback mask. We do this in a very simple manner, by making a call + * down into Xen. The pending flag will be checked by Xen on return. + */ +void xen_force_evtchn_callback(void) +{ + (void)HYPERVISOR_xen_version(0, NULL); +} + +static void __init __xen_init_IRQ(void) +{ +#ifdef CONFIG_X86_64 + int i; + + /* Create identity vector->irq map */ + for(i = 0; i < NR_VECTORS; i++) { + int cpu; + + for_each_possible_cpu(cpu) + per_cpu(vector_irq, cpu)[i] = i; + } +#endif /* CONFIG_X86_64 */ + + xen_init_IRQ(); +} + +static unsigned long xen_save_fl(void) +{ + struct vcpu_info *vcpu; + unsigned long flags; + + vcpu = x86_read_percpu(xen_vcpu); + + /* flag has opposite sense of mask */ + flags = !vcpu->evtchn_upcall_mask; + + /* convert to IF type flag + -0 -> 0x00000000 + -1 -> 0xffffffff + */ + return (-flags) & X86_EFLAGS_IF; +} + +static void xen_restore_fl(unsigned long flags) +{ + struct vcpu_info *vcpu; + + /* convert from IF type flag */ + flags = !(flags & X86_EFLAGS_IF); + + /* There's a one instruction preempt window here. We need to + make sure we're don't switch CPUs between getting the vcpu + pointer and updating the mask. */ + preempt_disable(); + vcpu = x86_read_percpu(xen_vcpu); + vcpu->evtchn_upcall_mask = flags; + preempt_enable_no_resched(); + + /* Doesn't matter if we get preempted here, because any + pending event will get dealt with anyway. */ + + if (flags == 0) { + preempt_check_resched(); + barrier(); /* unmask then check (avoid races) */ + if (unlikely(vcpu->evtchn_upcall_pending)) + xen_force_evtchn_callback(); + } +} + +static void xen_irq_disable(void) +{ + /* There's a one instruction preempt window here. We need to + make sure we're don't switch CPUs between getting the vcpu + pointer and updating the mask. */ + preempt_disable(); + x86_read_percpu(xen_vcpu)->evtchn_upcall_mask = 1; + preempt_enable_no_resched(); +} + +static void xen_irq_enable(void) +{ + struct vcpu_info *vcpu; + + /* We don't need to worry about being preempted here, since + either a) interrupts are disabled, so no preemption, or b) + the caller is confused and is trying to re-enable interrupts + on an indeterminate processor. */ + + vcpu = x86_read_percpu(xen_vcpu); + vcpu->evtchn_upcall_mask = 0; + + /* Doesn't matter if we get preempted here, because any + pending event will get dealt with anyway. */ + + barrier(); /* unmask then check (avoid races) */ + if (unlikely(vcpu->evtchn_upcall_pending)) + xen_force_evtchn_callback(); +} + +static void xen_safe_halt(void) +{ + /* Blocking includes an implicit local_irq_enable(). */ + if (HYPERVISOR_sched_op(SCHEDOP_block, NULL) != 0) + BUG(); +} + +static void xen_halt(void) +{ + if (irqs_disabled()) + HYPERVISOR_vcpu_op(VCPUOP_down, smp_processor_id(), NULL); + else + xen_safe_halt(); +} + +static const struct pv_irq_ops xen_irq_ops __initdata = { + .init_IRQ = __xen_init_IRQ, + .save_fl = xen_save_fl, + .restore_fl = xen_restore_fl, + .irq_disable = xen_irq_disable, + .irq_enable = xen_irq_enable, + .safe_halt = xen_safe_halt, + .halt = xen_halt, +#ifdef CONFIG_X86_64 + .adjust_exception_frame = xen_adjust_exception_frame, +#endif +}; + +void __init xen_init_irq_ops() +{ + pv_irq_ops = xen_irq_ops; +} diff --git a/arch/x86/xen/mmu.c b/arch/x86/xen/mmu.c index aa37469da69..ae173f6edd8 100644 --- a/arch/x86/xen/mmu.c +++ b/arch/x86/xen/mmu.c @@ -40,6 +40,7 @@ */ #include <linux/sched.h> #include <linux/highmem.h> +#include <linux/debugfs.h> #include <linux/bug.h> #include <asm/pgtable.h> @@ -57,6 +58,61 @@ #include "multicalls.h" #include "mmu.h" +#include "debugfs.h" + +#define MMU_UPDATE_HISTO 30 + +#ifdef CONFIG_XEN_DEBUG_FS + +static struct { + u32 pgd_update; + u32 pgd_update_pinned; + u32 pgd_update_batched; + + u32 pud_update; + u32 pud_update_pinned; + u32 pud_update_batched; + + u32 pmd_update; + u32 pmd_update_pinned; + u32 pmd_update_batched; + + u32 pte_update; + u32 pte_update_pinned; + u32 pte_update_batched; + + u32 mmu_update; + u32 mmu_update_extended; + u32 mmu_update_histo[MMU_UPDATE_HISTO]; + + u32 prot_commit; + u32 prot_commit_batched; + + u32 set_pte_at; + u32 set_pte_at_batched; + u32 set_pte_at_pinned; + u32 set_pte_at_current; + u32 set_pte_at_kernel; +} mmu_stats; + +static u8 zero_stats; + +static inline void check_zero(void) +{ + if (unlikely(zero_stats)) { + memset(&mmu_stats, 0, sizeof(mmu_stats)); + zero_stats = 0; + } +} + +#define ADD_STATS(elem, val) \ + do { check_zero(); mmu_stats.elem += (val); } while(0) + +#else /* !CONFIG_XEN_DEBUG_FS */ + +#define ADD_STATS(elem, val) do { (void)(val); } while(0) + +#endif /* CONFIG_XEN_DEBUG_FS */ /* * Just beyond the highest usermode address. STACK_TOP_MAX has a @@ -229,25 +285,35 @@ void make_lowmem_page_readwrite(void *vaddr) } -static bool page_pinned(void *ptr) +static bool xen_page_pinned(void *ptr) { struct page *page = virt_to_page(ptr); return PagePinned(page); } -static void extend_mmu_update(const struct mmu_update *update) +static void xen_extend_mmu_update(const struct mmu_update *update) { struct multicall_space mcs; struct mmu_update *u; mcs = xen_mc_extend_args(__HYPERVISOR_mmu_update, sizeof(*u)); - if (mcs.mc != NULL) + if (mcs.mc != NULL) { + ADD_STATS(mmu_update_extended, 1); + ADD_STATS(mmu_update_histo[mcs.mc->args[1]], -1); + mcs.mc->args[1]++; - else { + + if (mcs.mc->args[1] < MMU_UPDATE_HISTO) + ADD_STATS(mmu_update_histo[mcs.mc->args[1]], 1); + else + ADD_STATS(mmu_update_histo[0], 1); + } else { + ADD_STATS(mmu_update, 1); mcs = __xen_mc_entry(sizeof(*u)); MULTI_mmu_update(mcs.mc, mcs.args, 1, NULL, DOMID_SELF); + ADD_STATS(mmu_update_histo[1], 1); } u = mcs.args; @@ -265,7 +331,9 @@ void xen_set_pmd_hyper(pmd_t *ptr, pmd_t val) /* ptr may be ioremapped for 64-bit pagetable setup */ u.ptr = arbitrary_virt_to_machine(ptr).maddr; u.val = pmd_val_ma(val); - extend_mmu_update(&u); + xen_extend_mmu_update(&u); + + ADD_STATS(pmd_update_batched, paravirt_get_lazy_mode() == PARAVIRT_LAZY_MMU); xen_mc_issue(PARAVIRT_LAZY_MMU); @@ -274,13 +342,17 @@ void xen_set_pmd_hyper(pmd_t *ptr, pmd_t val) void xen_set_pmd(pmd_t *ptr, pmd_t val) { + ADD_STATS(pmd_update, 1); + /* If page is not pinned, we can just update the entry directly */ - if (!page_pinned(ptr)) { + if (!xen_page_pinned(ptr)) { *ptr = val; return; } + ADD_STATS(pmd_update_pinned, 1); + xen_set_pmd_hyper(ptr, val); } @@ -300,12 +372,18 @@ void xen_set_pte_at(struct mm_struct *mm, unsigned long addr, if (mm == &init_mm) preempt_disable(); + ADD_STATS(set_pte_at, 1); +// ADD_STATS(set_pte_at_pinned, xen_page_pinned(ptep)); + ADD_STATS(set_pte_at_current, mm == current->mm); + ADD_STATS(set_pte_at_kernel, mm == &init_mm); + if (mm == current->mm || mm == &init_mm) { if (paravirt_get_lazy_mode() == PARAVIRT_LAZY_MMU) { struct multicall_space mcs; mcs = xen_mc_entry(0); MULTI_update_va_mapping(mcs.mc, addr, pteval, 0); + ADD_STATS(set_pte_at_batched, 1); xen_mc_issue(PARAVIRT_LAZY_MMU); goto out; } else @@ -334,7 +412,10 @@ void xen_ptep_modify_prot_commit(struct mm_struct *mm, unsigned long addr, u.ptr = virt_to_machine(ptep).maddr | MMU_PT_UPDATE_PRESERVE_AD; u.val = pte_val_ma(pte); - extend_mmu_update(&u); + xen_extend_mmu_update(&u); + + ADD_STATS(prot_commit, 1); + ADD_STATS(prot_commit_batched, paravirt_get_lazy_mode() == PARAVIRT_LAZY_MMU); xen_mc_issue(PARAVIRT_LAZY_MMU); } @@ -400,7 +481,9 @@ void xen_set_pud_hyper(pud_t *ptr, pud_t val) /* ptr may be ioremapped for 64-bit pagetable setup */ u.ptr = arbitrary_virt_to_machine(ptr).maddr; u.val = pud_val_ma(val); - extend_mmu_update(&u); + xen_extend_mmu_update(&u); + + ADD_STATS(pud_update_batched, paravirt_get_lazy_mode() == PARAVIRT_LAZY_MMU); xen_mc_issue(PARAVIRT_LAZY_MMU); @@ -409,18 +492,26 @@ void xen_set_pud_hyper(pud_t *ptr, pud_t val) void xen_set_pud(pud_t *ptr, pud_t val) { + ADD_STATS(pud_update, 1); + /* If page is not pinned, we can just update the entry directly */ - if (!page_pinned(ptr)) { + if (!xen_page_pinned(ptr)) { *ptr = val; return; } + ADD_STATS(pud_update_pinned, 1); + xen_set_pud_hyper(ptr, val); } void xen_set_pte(pte_t *ptep, pte_t pte) { + ADD_STATS(pte_update, 1); +// ADD_STATS(pte_update_pinned, xen_page_pinned(ptep)); + ADD_STATS(pte_update_batched, paravirt_get_lazy_mode() == PARAVIRT_LAZY_MMU); + #ifdef CONFIG_X86_PAE ptep->pte_high = pte.pte_high; smp_wmb(); @@ -490,7 +581,7 @@ static void __xen_set_pgd_hyper(pgd_t *ptr, pgd_t val) u.ptr = virt_to_machine(ptr).maddr; u.val = pgd_val_ma(val); - extend_mmu_update(&u); + xen_extend_mmu_update(&u); } /* @@ -517,17 +608,22 @@ void xen_set_pgd(pgd_t *ptr, pgd_t val) { pgd_t *user_ptr = xen_get_user_pgd(ptr); + ADD_STATS(pgd_update, 1); + /* If page is not pinned, we can just update the entry directly */ - if (!page_pinned(ptr)) { + if (!xen_page_pinned(ptr)) { *ptr = val; if (user_ptr) { - WARN_ON(page_pinned(user_ptr)); + WARN_ON(xen_page_pinned(user_ptr)); *user_ptr = val; } return; } + ADD_STATS(pgd_update_pinned, 1); + ADD_STATS(pgd_update_batched, paravirt_get_lazy_mode() == PARAVIRT_LAZY_MMU); + /* If it's pinned, then we can at least batch the kernel and user updates together. */ xen_mc_batch(); @@ -555,9 +651,12 @@ void xen_set_pgd(pgd_t *ptr, pgd_t val) * For 64-bit, we must skip the Xen hole in the middle of the address * space, just after the big x86-64 virtual hole. */ -static int pgd_walk(pgd_t *pgd, int (*func)(struct page *, enum pt_level), - unsigned long limit) +static int xen_pgd_walk(struct mm_struct *mm, + int (*func)(struct mm_struct *mm, struct page *, + enum pt_level), + unsigned long limit) { + pgd_t *pgd = mm->pgd; int flush = 0; unsigned hole_low, hole_high; unsigned pgdidx_limit, pudidx_limit, pmdidx_limit; @@ -590,8 +689,6 @@ static int pgd_walk(pgd_t *pgd, int (*func)(struct page *, enum pt_level), pmdidx_limit = 0; #endif - flush |= (*func)(virt_to_page(pgd), PT_PGD); - for (pgdidx = 0; pgdidx <= pgdidx_limit; pgdidx++) { pud_t *pud; @@ -604,7 +701,7 @@ static int pgd_walk(pgd_t *pgd, int (*func)(struct page *, enum pt_level), pud = pud_offset(&pgd[pgdidx], 0); if (PTRS_PER_PUD > 1) /* not folded */ - flush |= (*func)(virt_to_page(pud), PT_PUD); + flush |= (*func)(mm, virt_to_page(pud), PT_PUD); for (pudidx = 0; pudidx < PTRS_PER_PUD; pudidx++) { pmd_t *pmd; @@ -619,7 +716,7 @@ static int pgd_walk(pgd_t *pgd, int (*func)(struct page *, enum pt_level), pmd = pmd_offset(&pud[pudidx], 0); if (PTRS_PER_PMD > 1) /* not folded */ - flush |= (*func)(virt_to_page(pmd), PT_PMD); + flush |= (*func)(mm, virt_to_page(pmd), PT_PMD); for (pmdidx = 0; pmdidx < PTRS_PER_PMD; pmdidx++) { struct page *pte; @@ -633,28 +730,34 @@ static int pgd_walk(pgd_t *pgd, int (*func)(struct page *, enum pt_level), continue; pte = pmd_page(pmd[pmdidx]); - flush |= (*func)(pte, PT_PTE); + flush |= (*func)(mm, pte, PT_PTE); } } } + out: + /* Do the top level last, so that the callbacks can use it as + a cue to do final things like tlb flushes. */ + flush |= (*func)(mm, virt_to_page(pgd), PT_PGD); return flush; } -static spinlock_t *lock_pte(struct page *page) +/* If we're using split pte locks, then take the page's lock and + return a pointer to it. Otherwise return NULL. */ +static spinlock_t *xen_pte_lock(struct page *page, struct mm_struct *mm) { spinlock_t *ptl = NULL; -#if NR_CPUS >= CONFIG_SPLIT_PTLOCK_CPUS +#if USE_SPLIT_PTLOCKS ptl = __pte_lockptr(page); - spin_lock(ptl); + spin_lock_nest_lock(ptl, &mm->page_table_lock); #endif return ptl; } -static void do_unlock(void *v) +static void xen_pte_unlock(void *v) { spinlock_t *ptl = v; spin_unlock(ptl); @@ -672,7 +775,8 @@ static void xen_do_pin(unsigned level, unsigned long pfn) MULTI_mmuext_op(mcs.mc, op, 1, NULL, DOMID_SELF); } -static int pin_page(struct page *page, enum pt_level level) +static int xen_pin_page(struct mm_struct *mm, struct page *page, + enum pt_level level) { unsigned pgfl = TestSetPagePinned(page); int flush; @@ -691,21 +795,40 @@ static int pin_page(struct page *page, enum pt_level level) flush = 0; + /* + * We need to hold the pagetable lock between the time + * we make the pagetable RO and when we actually pin + * it. If we don't, then other users may come in and + * attempt to update the pagetable by writing it, + * which will fail because the memory is RO but not + * pinned, so Xen won't do the trap'n'emulate. + * + * If we're using split pte locks, we can't hold the + * entire pagetable's worth of locks during the + * traverse, because we may wrap the preempt count (8 + * bits). The solution is to mark RO and pin each PTE + * page while holding the lock. This means the number + * of locks we end up holding is never more than a + * batch size (~32 entries, at present). + * + * If we're not using split pte locks, we needn't pin + * the PTE pages independently, because we're + * protected by the overall pagetable lock. + */ ptl = NULL; if (level == PT_PTE) - ptl = lock_pte(page); + ptl = xen_pte_lock(page, mm); MULTI_update_va_mapping(mcs.mc, (unsigned long)pt, pfn_pte(pfn, PAGE_KERNEL_RO), level == PT_PGD ? UVMF_TLB_FLUSH : 0); - if (level == PT_PTE) + if (ptl) { xen_do_pin(MMUEXT_PIN_L1_TABLE, pfn); - if (ptl) { /* Queue a deferred unlock for when this batch is completed. */ - xen_mc_callback(do_unlock, ptl); + xen_mc_callback(xen_pte_unlock, ptl); } } @@ -715,11 +838,11 @@ static int pin_page(struct page *page, enum pt_level level) /* This is called just after a mm has been created, but it has not been used yet. We need to make sure that its pagetable is all read-only, and can be pinned. */ -void xen_pgd_pin(pgd_t *pgd) +static void __xen_pgd_pin(struct mm_struct *mm, pgd_t *pgd) { xen_mc_batch(); - if (pgd_walk(pgd, pin_page, USER_LIMIT)) { + if (xen_pgd_walk(mm, xen_pin_page, USER_LIMIT)) { /* re-enable interrupts for kmap_flush_unused */ xen_mc_issue(0); kmap_flush_unused(); @@ -733,25 +856,35 @@ void xen_pgd_pin(pgd_t *pgd) xen_do_pin(MMUEXT_PIN_L4_TABLE, PFN_DOWN(__pa(pgd))); if (user_pgd) { - pin_page(virt_to_page(user_pgd), PT_PGD); + xen_pin_page(mm, virt_to_page(user_pgd), PT_PGD); xen_do_pin(MMUEXT_PIN_L4_TABLE, PFN_DOWN(__pa(user_pgd))); } } #else /* CONFIG_X86_32 */ #ifdef CONFIG_X86_PAE /* Need to make sure unshared kernel PMD is pinnable */ - pin_page(virt_to_page(pgd_page(pgd[pgd_index(TASK_SIZE)])), PT_PMD); + xen_pin_page(mm, virt_to_page(pgd_page(pgd[pgd_index(TASK_SIZE)])), + PT_PMD); #endif xen_do_pin(MMUEXT_PIN_L3_TABLE, PFN_DOWN(__pa(pgd))); #endif /* CONFIG_X86_64 */ xen_mc_issue(0); } +static void xen_pgd_pin(struct mm_struct *mm) +{ + __xen_pgd_pin(mm, mm->pgd); +} + /* * On save, we need to pin all pagetables to make sure they get their * mfns turned into pfns. Search the list for any unpinned pgds and pin * them (unpinned pgds are not currently in use, probably because the * process is under construction or destruction). + * + * Expected to be called in stop_machine() ("equivalent to taking + * every spinlock in the system"), so the locking doesn't really + * matter all that much. */ void xen_mm_pin_all(void) { @@ -762,7 +895,7 @@ void xen_mm_pin_all(void) list_for_each_entry(page, &pgd_list, lru) { if (!PagePinned(page)) { - xen_pgd_pin((pgd_t *)page_address(page)); + __xen_pgd_pin(&init_mm, (pgd_t *)page_address(page)); SetPageSavePinned(page); } } @@ -775,7 +908,8 @@ void xen_mm_pin_all(void) * that's before we have page structures to store the bits. So do all * the book-keeping now. */ -static __init int mark_pinned(struct page *page, enum pt_level level) +static __init int xen_mark_pinned(struct mm_struct *mm, struct page *page, + enum pt_level level) { SetPagePinned(page); return 0; @@ -783,10 +917,11 @@ static __init int mark_pinned(struct page *page, enum pt_level level) void __init xen_mark_init_mm_pinned(void) { - pgd_walk(init_mm.pgd, mark_pinned, FIXADDR_TOP); + xen_pgd_walk(&init_mm, xen_mark_pinned, FIXADDR_TOP); } -static int unpin_page(struct page *page, enum pt_level level) +static int xen_unpin_page(struct mm_struct *mm, struct page *page, + enum pt_level level) { unsigned pgfl = TestClearPagePinned(page); @@ -796,10 +931,18 @@ static int unpin_page(struct page *page, enum pt_level level) spinlock_t *ptl = NULL; struct multicall_space mcs; + /* + * Do the converse to pin_page. If we're using split + * pte locks, we must be holding the lock for while + * the pte page is unpinned but still RO to prevent + * concurrent updates from seeing it in this + * partially-pinned state. + */ if (level == PT_PTE) { - ptl = lock_pte(page); + ptl = xen_pte_lock(page, mm); - xen_do_pin(MMUEXT_UNPIN_TABLE, pfn); + if (ptl) + xen_do_pin(MMUEXT_UNPIN_TABLE, pfn); } mcs = __xen_mc_entry(0); @@ -810,7 +953,7 @@ static int unpin_page(struct page *page, enum pt_level level) if (ptl) { /* unlock when batch completed */ - xen_mc_callback(do_unlock, ptl); + xen_mc_callback(xen_pte_unlock, ptl); } } @@ -818,7 +961,7 @@ static int unpin_page(struct page *page, enum pt_level level) } /* Release a pagetables pages back as normal RW */ -static void xen_pgd_unpin(pgd_t *pgd) +static void __xen_pgd_unpin(struct mm_struct *mm, pgd_t *pgd) { xen_mc_batch(); @@ -830,21 +973,27 @@ static void xen_pgd_unpin(pgd_t *pgd) if (user_pgd) { xen_do_pin(MMUEXT_UNPIN_TABLE, PFN_DOWN(__pa(user_pgd))); - unpin_page(virt_to_page(user_pgd), PT_PGD); + xen_unpin_page(mm, virt_to_page(user_pgd), PT_PGD); } } #endif #ifdef CONFIG_X86_PAE /* Need to make sure unshared kernel PMD is unpinned */ - pin_page(virt_to_page(pgd_page(pgd[pgd_index(TASK_SIZE)])), PT_PMD); + xen_unpin_page(mm, virt_to_page(pgd_page(pgd[pgd_index(TASK_SIZE)])), + PT_PMD); #endif - pgd_walk(pgd, unpin_page, USER_LIMIT); + xen_pgd_walk(mm, xen_unpin_page, USER_LIMIT); xen_mc_issue(0); } +static void xen_pgd_unpin(struct mm_struct *mm) +{ + __xen_pgd_unpin(mm, mm->pgd); +} + /* * On resume, undo any pinning done at save, so that the rest of the * kernel doesn't see any unexpected pinned pagetables. @@ -859,7 +1008,7 @@ void xen_mm_unpin_all(void) list_for_each_entry(page, &pgd_list, lru) { if (PageSavePinned(page)) { BUG_ON(!PagePinned(page)); - xen_pgd_unpin((pgd_t *)page_address(page)); + __xen_pgd_unpin(&init_mm, (pgd_t *)page_address(page)); ClearPageSavePinned(page); } } @@ -870,14 +1019,14 @@ void xen_mm_unpin_all(void) void xen_activate_mm(struct mm_struct *prev, struct mm_struct *next) { spin_lock(&next->page_table_lock); - xen_pgd_pin(next->pgd); + xen_pgd_pin(next); spin_unlock(&next->page_table_lock); } void xen_dup_mmap(struct mm_struct *oldmm, struct mm_struct *mm) { spin_lock(&mm->page_table_lock); - xen_pgd_pin(mm->pgd); + xen_pgd_pin(mm); spin_unlock(&mm->page_table_lock); } @@ -907,7 +1056,7 @@ static void drop_other_mm_ref(void *info) } } -static void drop_mm_ref(struct mm_struct *mm) +static void xen_drop_mm_ref(struct mm_struct *mm) { cpumask_t mask; unsigned cpu; @@ -937,7 +1086,7 @@ static void drop_mm_ref(struct mm_struct *mm) smp_call_function_mask(mask, drop_other_mm_ref, mm, 1); } #else -static void drop_mm_ref(struct mm_struct *mm) +static void xen_drop_mm_ref(struct mm_struct *mm) { if (current->active_mm == mm) load_cr3(swapper_pg_dir); @@ -961,14 +1110,77 @@ static void drop_mm_ref(struct mm_struct *mm) void xen_exit_mmap(struct mm_struct *mm) { get_cpu(); /* make sure we don't move around */ - drop_mm_ref(mm); + xen_drop_mm_ref(mm); put_cpu(); spin_lock(&mm->page_table_lock); /* pgd may not be pinned in the error exit path of execve */ - if (page_pinned(mm->pgd)) - xen_pgd_unpin(mm->pgd); + if (xen_page_pinned(mm->pgd)) + xen_pgd_unpin(mm); spin_unlock(&mm->page_table_lock); } + +#ifdef CONFIG_XEN_DEBUG_FS + +static struct dentry *d_mmu_debug; + +static int __init xen_mmu_debugfs(void) +{ + struct dentry *d_xen = xen_init_debugfs(); + + if (d_xen == NULL) + return -ENOMEM; + + d_mmu_debug = debugfs_create_dir("mmu", d_xen); + + debugfs_create_u8("zero_stats", 0644, d_mmu_debug, &zero_stats); + + debugfs_create_u32("pgd_update", 0444, d_mmu_debug, &mmu_stats.pgd_update); + debugfs_create_u32("pgd_update_pinned", 0444, d_mmu_debug, + &mmu_stats.pgd_update_pinned); + debugfs_create_u32("pgd_update_batched", 0444, d_mmu_debug, + &mmu_stats.pgd_update_pinned); + + debugfs_create_u32("pud_update", 0444, d_mmu_debug, &mmu_stats.pud_update); + debugfs_create_u32("pud_update_pinned", 0444, d_mmu_debug, + &mmu_stats.pud_update_pinned); + debugfs_create_u32("pud_update_batched", 0444, d_mmu_debug, + &mmu_stats.pud_update_pinned); + + debugfs_create_u32("pmd_update", 0444, d_mmu_debug, &mmu_stats.pmd_update); + debugfs_create_u32("pmd_update_pinned", 0444, d_mmu_debug, + &mmu_stats.pmd_update_pinned); + debugfs_create_u32("pmd_update_batched", 0444, d_mmu_debug, + &mmu_stats.pmd_update_pinned); + + debugfs_create_u32("pte_update", 0444, d_mmu_debug, &mmu_stats.pte_update); +// debugfs_create_u32("pte_update_pinned", 0444, d_mmu_debug, +// &mmu_stats.pte_update_pinned); + debugfs_create_u32("pte_update_batched", 0444, d_mmu_debug, + &mmu_stats.pte_update_pinned); + + debugfs_create_u32("mmu_update", 0444, d_mmu_debug, &mmu_stats.mmu_update); + debugfs_create_u32("mmu_update_extended", 0444, d_mmu_debug, + &mmu_stats.mmu_update_extended); + xen_debugfs_create_u32_array("mmu_update_histo", 0444, d_mmu_debug, + mmu_stats.mmu_update_histo, 20); + + debugfs_create_u32("set_pte_at", 0444, d_mmu_debug, &mmu_stats.set_pte_at); + debugfs_create_u32("set_pte_at_batched", 0444, d_mmu_debug, + &mmu_stats.set_pte_at_batched); + debugfs_create_u32("set_pte_at_current", 0444, d_mmu_debug, + &mmu_stats.set_pte_at_current); + debugfs_create_u32("set_pte_at_kernel", 0444, d_mmu_debug, + &mmu_stats.set_pte_at_kernel); + + debugfs_create_u32("prot_commit", 0444, d_mmu_debug, &mmu_stats.prot_commit); + debugfs_create_u32("prot_commit_batched", 0444, d_mmu_debug, + &mmu_stats.prot_commit_batched); + + return 0; +} +fs_initcall(xen_mmu_debugfs); + +#endif /* CONFIG_XEN_DEBUG_FS */ diff --git a/arch/x86/xen/mmu.h b/arch/x86/xen/mmu.h index 0f59bd03f9e..98d71659da5 100644 --- a/arch/x86/xen/mmu.h +++ b/arch/x86/xen/mmu.h @@ -18,9 +18,6 @@ void xen_activate_mm(struct mm_struct *prev, struct mm_struct *next); void xen_dup_mmap(struct mm_struct *oldmm, struct mm_struct *mm); void xen_exit_mmap(struct mm_struct *mm); -void xen_pgd_pin(pgd_t *pgd); -//void xen_pgd_unpin(pgd_t *pgd); - pteval_t xen_pte_val(pte_t); pmdval_t xen_pmd_val(pmd_t); pgdval_t xen_pgd_val(pgd_t); diff --git a/arch/x86/xen/multicalls.c b/arch/x86/xen/multicalls.c index 9efd1c6c977..8ea8a0d0b0d 100644 --- a/arch/x86/xen/multicalls.c +++ b/arch/x86/xen/multicalls.c @@ -21,16 +21,20 @@ */ #include <linux/percpu.h> #include <linux/hardirq.h> +#include <linux/debugfs.h> #include <asm/xen/hypercall.h> #include "multicalls.h" +#include "debugfs.h" + +#define MC_BATCH 32 #define MC_DEBUG 1 -#define MC_BATCH 32 #define MC_ARGS (MC_BATCH * 16) + struct mc_buffer { struct multicall_entry entries[MC_BATCH]; #if MC_DEBUG @@ -47,6 +51,76 @@ struct mc_buffer { static DEFINE_PER_CPU(struct mc_buffer, mc_buffer); DEFINE_PER_CPU(unsigned long, xen_mc_irq_flags); +/* flush reasons 0- slots, 1- args, 2- callbacks */ +enum flush_reasons +{ + FL_SLOTS, + FL_ARGS, + FL_CALLBACKS, + + FL_N_REASONS +}; + +#ifdef CONFIG_XEN_DEBUG_FS +#define NHYPERCALLS 40 /* not really */ + +static struct { + unsigned histo[MC_BATCH+1]; + + unsigned issued; + unsigned arg_total; + unsigned hypercalls; + unsigned histo_hypercalls[NHYPERCALLS]; + + unsigned flush[FL_N_REASONS]; +} mc_stats; + +static u8 zero_stats; + +static inline void check_zero(void) +{ + if (unlikely(zero_stats)) { + memset(&mc_stats, 0, sizeof(mc_stats)); + zero_stats = 0; + } +} + +static void mc_add_stats(const struct mc_buffer *mc) +{ + int i; + + check_zero(); + + mc_stats.issued++; + mc_stats.hypercalls += mc->mcidx; + mc_stats.arg_total += mc->argidx; + + mc_stats.histo[mc->mcidx]++; + for(i = 0; i < mc->mcidx; i++) { + unsigned op = mc->entries[i].op; + if (op < NHYPERCALLS) + mc_stats.histo_hypercalls[op]++; + } +} + +static void mc_stats_flush(enum flush_reasons idx) +{ + check_zero(); + + mc_stats.flush[idx]++; +} + +#else /* !CONFIG_XEN_DEBUG_FS */ + +static inline void mc_add_stats(const struct mc_buffer *mc) +{ +} + +static inline void mc_stats_flush(enum flush_reasons idx) +{ +} +#endif /* CONFIG_XEN_DEBUG_FS */ + void xen_mc_flush(void) { struct mc_buffer *b = &__get_cpu_var(mc_buffer); @@ -60,6 +134,8 @@ void xen_mc_flush(void) something in the middle */ local_irq_save(flags); + mc_add_stats(b); + if (b->mcidx) { #if MC_DEBUG memcpy(b->debug, b->entries, @@ -115,6 +191,7 @@ struct multicall_space __xen_mc_entry(size_t args) if (b->mcidx == MC_BATCH || (argidx + args) > MC_ARGS) { + mc_stats_flush(b->mcidx == MC_BATCH ? FL_SLOTS : FL_ARGS); xen_mc_flush(); argidx = roundup(b->argidx, sizeof(u64)); } @@ -158,10 +235,44 @@ void xen_mc_callback(void (*fn)(void *), void *data) struct mc_buffer *b = &__get_cpu_var(mc_buffer); struct callback *cb; - if (b->cbidx == MC_BATCH) + if (b->cbidx == MC_BATCH) { + mc_stats_flush(FL_CALLBACKS); xen_mc_flush(); + } cb = &b->callbacks[b->cbidx++]; cb->fn = fn; cb->data = data; } + +#ifdef CONFIG_XEN_DEBUG_FS + +static struct dentry *d_mc_debug; + +static int __init xen_mc_debugfs(void) +{ + struct dentry *d_xen = xen_init_debugfs(); + + if (d_xen == NULL) + return -ENOMEM; + + d_mc_debug = debugfs_create_dir("multicalls", d_xen); + + debugfs_create_u8("zero_stats", 0644, d_mc_debug, &zero_stats); + + debugfs_create_u32("batches", 0444, d_mc_debug, &mc_stats.issued); + debugfs_create_u32("hypercalls", 0444, d_mc_debug, &mc_stats.hypercalls); + debugfs_create_u32("arg_total", 0444, d_mc_debug, &mc_stats.arg_total); + + xen_debugfs_create_u32_array("batch_histo", 0444, d_mc_debug, + mc_stats.histo, MC_BATCH); + xen_debugfs_create_u32_array("hypercall_histo", 0444, d_mc_debug, + mc_stats.histo_hypercalls, NHYPERCALLS); + xen_debugfs_create_u32_array("flush_reasons", 0444, d_mc_debug, + mc_stats.flush, FL_N_REASONS); + + return 0; +} +fs_initcall(xen_mc_debugfs); + +#endif /* CONFIG_XEN_DEBUG_FS */ diff --git a/arch/x86/xen/smp.c b/arch/x86/xen/smp.c index d8faf79a0a1..d77da613b1d 100644 --- a/arch/x86/xen/smp.c +++ b/arch/x86/xen/smp.c @@ -11,11 +11,8 @@ * useful topology information for the kernel to make use of. As a * result, all CPUs are treated as if they're single-core and * single-threaded. - * - * This does not handle HOTPLUG_CPU yet. */ #include <linux/sched.h> -#include <linux/kernel_stat.h> #include <linux/err.h> #include <linux/smp.h> @@ -36,8 +33,6 @@ #include "xen-ops.h" #include "mmu.h" -static void __cpuinit xen_init_lock_cpu(int cpu); - cpumask_t xen_cpu_initialized_map; static DEFINE_PER_CPU(int, resched_irq); @@ -64,11 +59,12 @@ static irqreturn_t xen_reschedule_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -static __cpuinit void cpu_bringup_and_idle(void) +static __cpuinit void cpu_bringup(void) { int cpu = smp_processor_id(); cpu_init(); + touch_softlockup_watchdog(); preempt_disable(); xen_enable_sysenter(); @@ -89,6 +85,11 @@ static __cpuinit void cpu_bringup_and_idle(void) local_irq_enable(); wmb(); /* make sure everything is out */ +} + +static __cpuinit void cpu_bringup_and_idle(void) +{ + cpu_bringup(); cpu_idle(); } @@ -212,8 +213,6 @@ static void __init xen_smp_prepare_cpus(unsigned int max_cpus) cpu_set(cpu, cpu_present_map); } - - //init_xenbus_allowed_cpumask(); } static __cpuinit int @@ -281,12 +280,6 @@ static int __cpuinit xen_cpu_up(unsigned int cpu) struct task_struct *idle = idle_task(cpu); int rc; -#if 0 - rc = cpu_up_check(cpu); - if (rc) - return rc; -#endif - #ifdef CONFIG_X86_64 /* Allocate node local memory for AP pdas */ WARN_ON(cpu == 0); @@ -339,6 +332,60 @@ static void xen_smp_cpus_done(unsigned int max_cpus) { } +#ifdef CONFIG_HOTPLUG_CPU +static int xen_cpu_disable(void) +{ + unsigned int cpu = smp_processor_id(); + if (cpu == 0) + return -EBUSY; + + cpu_disable_common(); + + load_cr3(swapper_pg_dir); + return 0; +} + +static void xen_cpu_die(unsigned int cpu) +{ + while (HYPERVISOR_vcpu_op(VCPUOP_is_up, cpu, NULL)) { + current->state = TASK_UNINTERRUPTIBLE; + schedule_timeout(HZ/10); + } + unbind_from_irqhandler(per_cpu(resched_irq, cpu), NULL); + unbind_from_irqhandler(per_cpu(callfunc_irq, cpu), NULL); + unbind_from_irqhandler(per_cpu(debug_irq, cpu), NULL); + unbind_from_irqhandler(per_cpu(callfuncsingle_irq, cpu), NULL); + xen_uninit_lock_cpu(cpu); + xen_teardown_timer(cpu); + + if (num_online_cpus() == 1) + alternatives_smp_switch(0); +} + +static void xen_play_dead(void) +{ + play_dead_common(); + HYPERVISOR_vcpu_op(VCPUOP_down, smp_processor_id(), NULL); + cpu_bringup(); +} + +#else /* !CONFIG_HOTPLUG_CPU */ +static int xen_cpu_disable(void) +{ + return -ENOSYS; +} + +static void xen_cpu_die(unsigned int cpu) +{ + BUG(); +} + +static void xen_play_dead(void) +{ + BUG(); +} + +#endif static void stop_self(void *v) { int cpu = smp_processor_id(); @@ -419,176 +466,16 @@ static irqreturn_t xen_call_function_single_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -struct xen_spinlock { - unsigned char lock; /* 0 -> free; 1 -> locked */ - unsigned short spinners; /* count of waiting cpus */ -}; - -static int xen_spin_is_locked(struct raw_spinlock *lock) -{ - struct xen_spinlock *xl = (struct xen_spinlock *)lock; - - return xl->lock != 0; -} - -static int xen_spin_is_contended(struct raw_spinlock *lock) -{ - struct xen_spinlock *xl = (struct xen_spinlock *)lock; - - /* Not strictly true; this is only the count of contended - lock-takers entering the slow path. */ - return xl->spinners != 0; -} - -static int xen_spin_trylock(struct raw_spinlock *lock) -{ - struct xen_spinlock *xl = (struct xen_spinlock *)lock; - u8 old = 1; - - asm("xchgb %b0,%1" - : "+q" (old), "+m" (xl->lock) : : "memory"); - - return old == 0; -} - -static DEFINE_PER_CPU(int, lock_kicker_irq) = -1; -static DEFINE_PER_CPU(struct xen_spinlock *, lock_spinners); - -static inline void spinning_lock(struct xen_spinlock *xl) -{ - __get_cpu_var(lock_spinners) = xl; - wmb(); /* set lock of interest before count */ - asm(LOCK_PREFIX " incw %0" - : "+m" (xl->spinners) : : "memory"); -} - -static inline void unspinning_lock(struct xen_spinlock *xl) -{ - asm(LOCK_PREFIX " decw %0" - : "+m" (xl->spinners) : : "memory"); - wmb(); /* decrement count before clearing lock */ - __get_cpu_var(lock_spinners) = NULL; -} - -static noinline int xen_spin_lock_slow(struct raw_spinlock *lock) -{ - struct xen_spinlock *xl = (struct xen_spinlock *)lock; - int irq = __get_cpu_var(lock_kicker_irq); - int ret; - - /* If kicker interrupts not initialized yet, just spin */ - if (irq == -1) - return 0; - - /* announce we're spinning */ - spinning_lock(xl); - - /* clear pending */ - xen_clear_irq_pending(irq); - - /* check again make sure it didn't become free while - we weren't looking */ - ret = xen_spin_trylock(lock); - if (ret) - goto out; - - /* block until irq becomes pending */ - xen_poll_irq(irq); - kstat_this_cpu.irqs[irq]++; - -out: - unspinning_lock(xl); - return ret; -} - -static void xen_spin_lock(struct raw_spinlock *lock) -{ - struct xen_spinlock *xl = (struct xen_spinlock *)lock; - int timeout; - u8 oldval; - - do { - timeout = 1 << 10; - - asm("1: xchgb %1,%0\n" - " testb %1,%1\n" - " jz 3f\n" - "2: rep;nop\n" - " cmpb $0,%0\n" - " je 1b\n" - " dec %2\n" - " jnz 2b\n" - "3:\n" - : "+m" (xl->lock), "=q" (oldval), "+r" (timeout) - : "1" (1) - : "memory"); - - } while (unlikely(oldval != 0 && !xen_spin_lock_slow(lock))); -} - -static noinline void xen_spin_unlock_slow(struct xen_spinlock *xl) -{ - int cpu; - - for_each_online_cpu(cpu) { - /* XXX should mix up next cpu selection */ - if (per_cpu(lock_spinners, cpu) == xl) { - xen_send_IPI_one(cpu, XEN_SPIN_UNLOCK_VECTOR); - break; - } - } -} - -static void xen_spin_unlock(struct raw_spinlock *lock) -{ - struct xen_spinlock *xl = (struct xen_spinlock *)lock; - - smp_wmb(); /* make sure no writes get moved after unlock */ - xl->lock = 0; /* release lock */ - - /* make sure unlock happens before kick */ - barrier(); - - if (unlikely(xl->spinners)) - xen_spin_unlock_slow(xl); -} - -static __cpuinit void xen_init_lock_cpu(int cpu) -{ - int irq; - const char *name; - - name = kasprintf(GFP_KERNEL, "spinlock%d", cpu); - irq = bind_ipi_to_irqhandler(XEN_SPIN_UNLOCK_VECTOR, - cpu, - xen_reschedule_interrupt, - IRQF_DISABLED|IRQF_PERCPU|IRQF_NOBALANCING, - name, - NULL); - - if (irq >= 0) { - disable_irq(irq); /* make sure it's never delivered */ - per_cpu(lock_kicker_irq, cpu) = irq; - } - - printk("cpu %d spinlock event irq %d\n", cpu, irq); -} - -static void __init xen_init_spinlocks(void) -{ - pv_lock_ops.spin_is_locked = xen_spin_is_locked; - pv_lock_ops.spin_is_contended = xen_spin_is_contended; - pv_lock_ops.spin_lock = xen_spin_lock; - pv_lock_ops.spin_trylock = xen_spin_trylock; - pv_lock_ops.spin_unlock = xen_spin_unlock; -} - static const struct smp_ops xen_smp_ops __initdata = { .smp_prepare_boot_cpu = xen_smp_prepare_boot_cpu, .smp_prepare_cpus = xen_smp_prepare_cpus, - .cpu_up = xen_cpu_up, .smp_cpus_done = xen_smp_cpus_done, + .cpu_up = xen_cpu_up, + .cpu_die = xen_cpu_die, + .cpu_disable = xen_cpu_disable, + .play_dead = xen_play_dead, + .smp_send_stop = xen_smp_send_stop, .smp_send_reschedule = xen_smp_send_reschedule, diff --git a/arch/x86/xen/spinlock.c b/arch/x86/xen/spinlock.c new file mode 100644 index 00000000000..dd71e3a021c --- /dev/null +++ b/arch/x86/xen/spinlock.c @@ -0,0 +1,428 @@ +/* + * Split spinlock implementation out into its own file, so it can be + * compiled in a FTRACE-compatible way. + */ +#include <linux/kernel_stat.h> +#include <linux/spinlock.h> +#include <linux/debugfs.h> +#include <linux/log2.h> + +#include <asm/paravirt.h> + +#include <xen/interface/xen.h> +#include <xen/events.h> + +#include "xen-ops.h" +#include "debugfs.h" + +#ifdef CONFIG_XEN_DEBUG_FS +static struct xen_spinlock_stats +{ + u64 taken; + u32 taken_slow; + u32 taken_slow_nested; + u32 taken_slow_pickup; + u32 taken_slow_spurious; + u32 taken_slow_irqenable; + + u64 released; + u32 released_slow; + u32 released_slow_kicked; + +#define HISTO_BUCKETS 30 + u32 histo_spin_total[HISTO_BUCKETS+1]; + u32 histo_spin_spinning[HISTO_BUCKETS+1]; + u32 histo_spin_blocked[HISTO_BUCKETS+1]; + + u64 time_total; + u64 time_spinning; + u64 time_blocked; +} spinlock_stats; + +static u8 zero_stats; + +static unsigned lock_timeout = 1 << 10; +#define TIMEOUT lock_timeout + +static inline void check_zero(void) +{ + if (unlikely(zero_stats)) { + memset(&spinlock_stats, 0, sizeof(spinlock_stats)); + zero_stats = 0; + } +} + +#define ADD_STATS(elem, val) \ + do { check_zero(); spinlock_stats.elem += (val); } while(0) + +static inline u64 spin_time_start(void) +{ + return xen_clocksource_read(); +} + +static void __spin_time_accum(u64 delta, u32 *array) +{ + unsigned index = ilog2(delta); + + check_zero(); + + if (index < HISTO_BUCKETS) + array[index]++; + else + array[HISTO_BUCKETS]++; +} + +static inline void spin_time_accum_spinning(u64 start) +{ + u32 delta = xen_clocksource_read() - start; + + __spin_time_accum(delta, spinlock_stats.histo_spin_spinning); + spinlock_stats.time_spinning += delta; +} + +static inline void spin_time_accum_total(u64 start) +{ + u32 delta = xen_clocksource_read() - start; + + __spin_time_accum(delta, spinlock_stats.histo_spin_total); + spinlock_stats.time_total += delta; +} + +static inline void spin_time_accum_blocked(u64 start) +{ + u32 delta = xen_clocksource_read() - start; + + __spin_time_accum(delta, spinlock_stats.histo_spin_blocked); + spinlock_stats.time_blocked += delta; +} +#else /* !CONFIG_XEN_DEBUG_FS */ +#define TIMEOUT (1 << 10) +#define ADD_STATS(elem, val) do { (void)(val); } while(0) + +static inline u64 spin_time_start(void) +{ + return 0; +} + +static inline void spin_time_accum_total(u64 start) +{ +} +static inline void spin_time_accum_spinning(u64 start) +{ +} +static inline void spin_time_accum_blocked(u64 start) +{ +} +#endif /* CONFIG_XEN_DEBUG_FS */ + +struct xen_spinlock { + unsigned char lock; /* 0 -> free; 1 -> locked */ + unsigned short spinners; /* count of waiting cpus */ +}; + +static int xen_spin_is_locked(struct raw_spinlock *lock) +{ + struct xen_spinlock *xl = (struct xen_spinlock *)lock; + + return xl->lock != 0; +} + +static int xen_spin_is_contended(struct raw_spinlock *lock) +{ + struct xen_spinlock *xl = (struct xen_spinlock *)lock; + + /* Not strictly true; this is only the count of contended + lock-takers entering the slow path. */ + return xl->spinners != 0; +} + +static int xen_spin_trylock(struct raw_spinlock *lock) +{ + struct xen_spinlock *xl = (struct xen_spinlock *)lock; + u8 old = 1; + + asm("xchgb %b0,%1" + : "+q" (old), "+m" (xl->lock) : : "memory"); + + return old == 0; +} + +static DEFINE_PER_CPU(int, lock_kicker_irq) = -1; +static DEFINE_PER_CPU(struct xen_spinlock *, lock_spinners); + +/* + * Mark a cpu as interested in a lock. Returns the CPU's previous + * lock of interest, in case we got preempted by an interrupt. + */ +static inline struct xen_spinlock *spinning_lock(struct xen_spinlock *xl) +{ + struct xen_spinlock *prev; + + prev = __get_cpu_var(lock_spinners); + __get_cpu_var(lock_spinners) = xl; + + wmb(); /* set lock of interest before count */ + + asm(LOCK_PREFIX " incw %0" + : "+m" (xl->spinners) : : "memory"); + + return prev; +} + +/* + * Mark a cpu as no longer interested in a lock. Restores previous + * lock of interest (NULL for none). + */ +static inline void unspinning_lock(struct xen_spinlock *xl, struct xen_spinlock *prev) +{ + asm(LOCK_PREFIX " decw %0" + : "+m" (xl->spinners) : : "memory"); + wmb(); /* decrement count before restoring lock */ + __get_cpu_var(lock_spinners) = prev; +} + +static noinline int xen_spin_lock_slow(struct raw_spinlock *lock, bool irq_enable) +{ + struct xen_spinlock *xl = (struct xen_spinlock *)lock; + struct xen_spinlock *prev; + int irq = __get_cpu_var(lock_kicker_irq); + int ret; + unsigned long flags; + u64 start; + + /* If kicker interrupts not initialized yet, just spin */ + if (irq == -1) + return 0; + + start = spin_time_start(); + + /* announce we're spinning */ + prev = spinning_lock(xl); + + flags = __raw_local_save_flags(); + if (irq_enable) { + ADD_STATS(taken_slow_irqenable, 1); + raw_local_irq_enable(); + } + + ADD_STATS(taken_slow, 1); + ADD_STATS(taken_slow_nested, prev != NULL); + + do { + /* clear pending */ + xen_clear_irq_pending(irq); + + /* check again make sure it didn't become free while + we weren't looking */ + ret = xen_spin_trylock(lock); + if (ret) { + ADD_STATS(taken_slow_pickup, 1); + + /* + * If we interrupted another spinlock while it + * was blocking, make sure it doesn't block + * without rechecking the lock. + */ + if (prev != NULL) + xen_set_irq_pending(irq); + goto out; + } + + /* + * Block until irq becomes pending. If we're + * interrupted at this point (after the trylock but + * before entering the block), then the nested lock + * handler guarantees that the irq will be left + * pending if there's any chance the lock became free; + * xen_poll_irq() returns immediately if the irq is + * pending. + */ + xen_poll_irq(irq); + ADD_STATS(taken_slow_spurious, !xen_test_irq_pending(irq)); + } while (!xen_test_irq_pending(irq)); /* check for spurious wakeups */ + + kstat_this_cpu.irqs[irq]++; + +out: + raw_local_irq_restore(flags); + unspinning_lock(xl, prev); + spin_time_accum_blocked(start); + + return ret; +} + +static inline void __xen_spin_lock(struct raw_spinlock *lock, bool irq_enable) +{ + struct xen_spinlock *xl = (struct xen_spinlock *)lock; + unsigned timeout; + u8 oldval; + u64 start_spin; + + ADD_STATS(taken, 1); + + start_spin = spin_time_start(); + + do { + u64 start_spin_fast = spin_time_start(); + + timeout = TIMEOUT; + + asm("1: xchgb %1,%0\n" + " testb %1,%1\n" + " jz 3f\n" + "2: rep;nop\n" + " cmpb $0,%0\n" + " je 1b\n" + " dec %2\n" + " jnz 2b\n" + "3:\n" + : "+m" (xl->lock), "=q" (oldval), "+r" (timeout) + : "1" (1) + : "memory"); + + spin_time_accum_spinning(start_spin_fast); + + } while (unlikely(oldval != 0 && + (TIMEOUT == ~0 || !xen_spin_lock_slow(lock, irq_enable)))); + + spin_time_accum_total(start_spin); +} + +static void xen_spin_lock(struct raw_spinlock *lock) +{ + __xen_spin_lock(lock, false); +} + +static void xen_spin_lock_flags(struct raw_spinlock *lock, unsigned long flags) +{ + __xen_spin_lock(lock, !raw_irqs_disabled_flags(flags)); +} + +static noinline void xen_spin_unlock_slow(struct xen_spinlock *xl) +{ + int cpu; + + ADD_STATS(released_slow, 1); + + for_each_online_cpu(cpu) { + /* XXX should mix up next cpu selection */ + if (per_cpu(lock_spinners, cpu) == xl) { + ADD_STATS(released_slow_kicked, 1); + xen_send_IPI_one(cpu, XEN_SPIN_UNLOCK_VECTOR); + break; + } + } +} + +static void xen_spin_unlock(struct raw_spinlock *lock) +{ + struct xen_spinlock *xl = (struct xen_spinlock *)lock; + + ADD_STATS(released, 1); + + smp_wmb(); /* make sure no writes get moved after unlock */ + xl->lock = 0; /* release lock */ + + /* make sure unlock happens before kick */ + barrier(); + + if (unlikely(xl->spinners)) + xen_spin_unlock_slow(xl); +} + +static irqreturn_t dummy_handler(int irq, void *dev_id) +{ + BUG(); + return IRQ_HANDLED; +} + +void __cpuinit xen_init_lock_cpu(int cpu) +{ + int irq; + const char *name; + + name = kasprintf(GFP_KERNEL, "spinlock%d", cpu); + irq = bind_ipi_to_irqhandler(XEN_SPIN_UNLOCK_VECTOR, + cpu, + dummy_handler, + IRQF_DISABLED|IRQF_PERCPU|IRQF_NOBALANCING, + name, + NULL); + + if (irq >= 0) { + disable_irq(irq); /* make sure it's never delivered */ + per_cpu(lock_kicker_irq, cpu) = irq; + } + + printk("cpu %d spinlock event irq %d\n", cpu, irq); +} + +void xen_uninit_lock_cpu(int cpu) +{ + unbind_from_irqhandler(per_cpu(lock_kicker_irq, cpu), NULL); +} + +void __init xen_init_spinlocks(void) +{ + pv_lock_ops.spin_is_locked = xen_spin_is_locked; + pv_lock_ops.spin_is_contended = xen_spin_is_contended; + pv_lock_ops.spin_lock = xen_spin_lock; + pv_lock_ops.spin_lock_flags = xen_spin_lock_flags; + pv_lock_ops.spin_trylock = xen_spin_trylock; + pv_lock_ops.spin_unlock = xen_spin_unlock; +} + +#ifdef CONFIG_XEN_DEBUG_FS + +static struct dentry *d_spin_debug; + +static int __init xen_spinlock_debugfs(void) +{ + struct dentry *d_xen = xen_init_debugfs(); + + if (d_xen == NULL) + return -ENOMEM; + + d_spin_debug = debugfs_create_dir("spinlocks", d_xen); + + debugfs_create_u8("zero_stats", 0644, d_spin_debug, &zero_stats); + + debugfs_create_u32("timeout", 0644, d_spin_debug, &lock_timeout); + + debugfs_create_u64("taken", 0444, d_spin_debug, &spinlock_stats.taken); + debugfs_create_u32("taken_slow", 0444, d_spin_debug, + &spinlock_stats.taken_slow); + debugfs_create_u32("taken_slow_nested", 0444, d_spin_debug, + &spinlock_stats.taken_slow_nested); + debugfs_create_u32("taken_slow_pickup", 0444, d_spin_debug, + &spinlock_stats.taken_slow_pickup); + debugfs_create_u32("taken_slow_spurious", 0444, d_spin_debug, + &spinlock_stats.taken_slow_spurious); + debugfs_create_u32("taken_slow_irqenable", 0444, d_spin_debug, + &spinlock_stats.taken_slow_irqenable); + + debugfs_create_u64("released", 0444, d_spin_debug, &spinlock_stats.released); + debugfs_create_u32("released_slow", 0444, d_spin_debug, + &spinlock_stats.released_slow); + debugfs_create_u32("released_slow_kicked", 0444, d_spin_debug, + &spinlock_stats.released_slow_kicked); + + debugfs_create_u64("time_spinning", 0444, d_spin_debug, + &spinlock_stats.time_spinning); + debugfs_create_u64("time_blocked", 0444, d_spin_debug, + &spinlock_stats.time_blocked); + debugfs_create_u64("time_total", 0444, d_spin_debug, + &spinlock_stats.time_total); + + xen_debugfs_create_u32_array("histo_total", 0444, d_spin_debug, + spinlock_stats.histo_spin_total, HISTO_BUCKETS + 1); + xen_debugfs_create_u32_array("histo_spinning", 0444, d_spin_debug, + spinlock_stats.histo_spin_spinning, HISTO_BUCKETS + 1); + xen_debugfs_create_u32_array("histo_blocked", 0444, d_spin_debug, + spinlock_stats.histo_spin_blocked, HISTO_BUCKETS + 1); + + return 0; +} +fs_initcall(xen_spinlock_debugfs); + +#endif /* CONFIG_XEN_DEBUG_FS */ diff --git a/arch/x86/xen/time.c b/arch/x86/xen/time.c index 685b77470fc..004ba86326a 100644 --- a/arch/x86/xen/time.c +++ b/arch/x86/xen/time.c @@ -30,8 +30,6 @@ #define TIMER_SLOP 100000 #define NS_PER_TICK (1000000000LL / HZ) -static cycle_t xen_clocksource_read(void); - /* runstate info updated by Xen */ static DEFINE_PER_CPU(struct vcpu_runstate_info, runstate); @@ -213,7 +211,7 @@ unsigned long xen_tsc_khz(void) return xen_khz; } -static cycle_t xen_clocksource_read(void) +cycle_t xen_clocksource_read(void) { struct pvclock_vcpu_time_info *src; cycle_t ret; @@ -452,6 +450,14 @@ void xen_setup_timer(int cpu) setup_runstate_info(cpu); } +void xen_teardown_timer(int cpu) +{ + struct clock_event_device *evt; + BUG_ON(cpu == 0); + evt = &per_cpu(xen_clock_events, cpu); + unbind_from_irqhandler(evt->irq, NULL); +} + void xen_setup_cpu_clockevents(void) { BUG_ON(preemptible()); diff --git a/arch/x86/xen/xen-asm_32.S b/arch/x86/xen/xen-asm_32.S index 2497a30f41d..42786f59d9c 100644 --- a/arch/x86/xen/xen-asm_32.S +++ b/arch/x86/xen/xen-asm_32.S @@ -298,7 +298,7 @@ check_events: push %eax push %ecx push %edx - call force_evtchn_callback + call xen_force_evtchn_callback pop %edx pop %ecx pop %eax diff --git a/arch/x86/xen/xen-asm_64.S b/arch/x86/xen/xen-asm_64.S index 7f58304fafb..05794c566e8 100644 --- a/arch/x86/xen/xen-asm_64.S +++ b/arch/x86/xen/xen-asm_64.S @@ -26,8 +26,15 @@ /* Pseudo-flag used for virtual NMI, which we don't implement yet */ #define XEN_EFLAGS_NMI 0x80000000 -#if 0 -#include <asm/percpu.h> +#if 1 +/* + x86-64 does not yet support direct access to percpu variables + via a segment override, so we just need to make sure this code + never gets used + */ +#define BUG ud2a +#define PER_CPU_VAR(var, off) 0xdeadbeef +#endif /* Enable events. This clears the event mask and tests the pending @@ -35,6 +42,8 @@ events, then enter the hypervisor to get them handled. */ ENTRY(xen_irq_enable_direct) + BUG + /* Unmask events */ movb $0, PER_CPU_VAR(xen_vcpu_info, XEN_vcpu_info_mask) @@ -58,6 +67,8 @@ ENDPATCH(xen_irq_enable_direct) non-zero. */ ENTRY(xen_irq_disable_direct) + BUG + movb $1, PER_CPU_VAR(xen_vcpu_info, XEN_vcpu_info_mask) ENDPATCH(xen_irq_disable_direct) ret @@ -74,6 +85,8 @@ ENDPATCH(xen_irq_disable_direct) Xen and x86 use opposite senses (mask vs enable). */ ENTRY(xen_save_fl_direct) + BUG + testb $0xff, PER_CPU_VAR(xen_vcpu_info, XEN_vcpu_info_mask) setz %ah addb %ah,%ah @@ -91,6 +104,8 @@ ENDPATCH(xen_save_fl_direct) if so. */ ENTRY(xen_restore_fl_direct) + BUG + testb $X86_EFLAGS_IF>>8, %ah setz PER_CPU_VAR(xen_vcpu_info, XEN_vcpu_info_mask) /* Preempt here doesn't matter because that will deal with @@ -122,7 +137,7 @@ check_events: push %r9 push %r10 push %r11 - call force_evtchn_callback + call xen_force_evtchn_callback pop %r11 pop %r10 pop %r9 @@ -133,7 +148,6 @@ check_events: pop %rcx pop %rax ret -#endif ENTRY(xen_adjust_exception_frame) mov 8+0(%rsp),%rcx diff --git a/arch/x86/xen/xen-ops.h b/arch/x86/xen/xen-ops.h index dd3c23152a2..d7422dc2a55 100644 --- a/arch/x86/xen/xen-ops.h +++ b/arch/x86/xen/xen-ops.h @@ -2,6 +2,7 @@ #define XEN_OPS_H #include <linux/init.h> +#include <linux/clocksource.h> #include <linux/irqreturn.h> #include <xen/xen-ops.h> @@ -31,7 +32,10 @@ void xen_vcpu_restore(void); void __init xen_build_dynamic_phys_to_machine(void); +void xen_init_irq_ops(void); void xen_setup_timer(int cpu); +void xen_teardown_timer(int cpu); +cycle_t xen_clocksource_read(void); void xen_setup_cpu_clockevents(void); unsigned long xen_tsc_khz(void); void __init xen_time_init(void); @@ -50,6 +54,10 @@ void __init xen_setup_vcpu_info_placement(void); #ifdef CONFIG_SMP void xen_smp_init(void); +void __init xen_init_spinlocks(void); +__cpuinit void xen_init_lock_cpu(int cpu); +void xen_uninit_lock_cpu(int cpu); + extern cpumask_t xen_cpu_initialized_map; #else static inline void xen_smp_init(void) {} |